chrome/browser/extensions: Remove use of MessageLoopProxy and deprecated MessageLoop...
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac.mm
blobd06da9df1ca68137b32292b54abbf5ed34ea4c7f
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 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
42 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
43 #include "content/browser/renderer_host/render_widget_resize_helper.h"
44 #import "content/browser/renderer_host/text_input_client_mac.h"
45 #include "content/common/accessibility_messages.h"
46 #include "content/common/edit_command.h"
47 #include "content/common/gpu/gpu_messages.h"
48 #include "content/common/input_messages.h"
49 #include "content/common/view_messages.h"
50 #include "content/common/webplugin_geometry.h"
51 #include "content/public/browser/browser_context.h"
52 #include "content/public/browser/browser_plugin_guest_manager.h"
53 #include "content/public/browser/browser_thread.h"
54 #include "content/public/browser/native_web_keyboard_event.h"
55 #include "content/public/browser/notification_service.h"
56 #include "content/public/browser/notification_types.h"
57 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
58 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
59 #include "content/public/browser/web_contents.h"
60 #include "skia/ext/platform_canvas.h"
61 #include "skia/ext/skia_utils_mac.h"
62 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
63 #include "third_party/WebKit/public/web/WebInputEvent.h"
64 #include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
65 #import "third_party/mozilla/ComplexTextInputPanel.h"
66 #include "ui/accelerated_widget_mac/io_surface_layer.h"
67 #include "ui/accelerated_widget_mac/surface_handle_types.h"
68 #include "ui/base/cocoa/animation_utils.h"
69 #import "ui/base/cocoa/fullscreen_window_manager.h"
70 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
71 #include "ui/base/layout.h"
72 #include "ui/compositor/compositor.h"
73 #include "ui/compositor/layer.h"
74 #include "ui/events/keycodes/keyboard_codes.h"
75 #include "ui/gfx/display.h"
76 #include "ui/gfx/geometry/dip_util.h"
77 #include "ui/gfx/geometry/point.h"
78 #include "ui/gfx/geometry/rect_conversions.h"
79 #include "ui/gfx/geometry/size_conversions.h"
80 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
81 #include "ui/gfx/screen.h"
82 #include "ui/gl/gl_switches.h"
84 using content::BrowserAccessibility;
85 using content::BrowserAccessibilityManager;
86 using content::EditCommand;
87 using content::FrameTreeNode;
88 using content::NativeWebKeyboardEvent;
89 using content::RenderFrameHost;
90 using content::RenderViewHost;
91 using content::RenderViewHostImpl;
92 using content::RenderWidgetHostImpl;
93 using content::RenderWidgetHostViewMac;
94 using content::RenderWidgetHostViewMacEditCommandHelper;
95 using content::TextInputClientMac;
96 using content::WebContents;
97 using blink::WebInputEvent;
98 using blink::WebInputEventFactory;
99 using blink::WebMouseEvent;
100 using blink::WebMouseWheelEvent;
101 using blink::WebGestureEvent;
103 namespace {
105 // Whether a keyboard event has been reserved by OSX.
106 BOOL EventIsReservedBySystem(NSEvent* event) {
107   content::SystemHotkeyHelperMac* helper =
108       content::SystemHotkeyHelperMac::GetInstance();
109   return helper->map()->IsEventReserved(event);
112 RenderWidgetHostViewMac* GetRenderWidgetHostViewToUse(
113     RenderWidgetHostViewMac* render_widget_host_view) {
114   WebContents* web_contents = render_widget_host_view->GetWebContents();
115   if (!web_contents)
116     return render_widget_host_view;
117   content::BrowserPluginGuestManager* guest_manager =
118       web_contents->GetBrowserContext()->GetGuestManager();
119   if (!guest_manager)
120     return render_widget_host_view;
121   content::WebContents* guest =
122       guest_manager->GetFullPageGuest(web_contents);
123   if (!guest)
124     return render_widget_host_view;
125   return static_cast<RenderWidgetHostViewMac*>(
126       guest->GetRenderWidgetHostView());
129 }  // namespace
131 // These are not documented, so use only after checking -respondsToSelector:.
132 @interface NSApplication (UndocumentedSpeechMethods)
133 - (void)speakString:(NSString*)string;
134 - (void)stopSpeaking:(id)sender;
135 - (BOOL)isSpeaking;
136 @end
138 // This method will return YES for OS X versions 10.7.3 and later, and NO
139 // otherwise.
140 // Used to prevent a crash when building with the 10.7 SDK and accessing the
141 // notification below. See: http://crbug.com/260595.
142 static BOOL SupportsBackingPropertiesChangedNotification() {
143   // windowDidChangeBackingProperties: method has been added to the
144   // NSWindowDelegate protocol in 10.7.3, at the same time as the
145   // NSWindowDidChangeBackingPropertiesNotification notification was added.
146   // If the protocol contains this method description, the notification should
147   // be supported as well.
148   Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
149   struct objc_method_description methodDescription =
150       protocol_getMethodDescription(
151           windowDelegateProtocol,
152           @selector(windowDidChangeBackingProperties:),
153           NO,
154           YES);
156   // If the protocol does not contain the method, the returned method
157   // description is {NULL, NULL}
158   return methodDescription.name != NULL || methodDescription.types != NULL;
161 // Private methods:
162 @interface RenderWidgetHostViewCocoa ()
163 @property(nonatomic, assign) NSRange selectedRange;
164 @property(nonatomic, assign) NSRange markedRange;
166 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
167 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
168 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
169                    consumed:(BOOL)consumed;
171 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
172 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
173 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
174 - (void)checkForPluginImeCancellation;
175 - (void)updateScreenProperties;
176 - (void)setResponderDelegate:
177         (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
178 @end
180 // A window subclass that allows the fullscreen window to become main and gain
181 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
182 // handled by the browser.
183 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
184 @end
186 @implementation PepperFlashFullscreenWindow
188 - (BOOL)canBecomeKeyWindow {
189   return YES;
192 - (BOOL)canBecomeMainWindow {
193   return YES;
196 @end
198 @interface RenderWidgetPopupWindow : NSWindow {
199    // The event tap that allows monitoring of all events, to properly close with
200    // a click outside the bounds of the window.
201   id clickEventTap_;
203 @end
205 @implementation RenderWidgetPopupWindow
207 - (id)initWithContentRect:(NSRect)contentRect
208                 styleMask:(NSUInteger)windowStyle
209                   backing:(NSBackingStoreType)bufferingType
210                     defer:(BOOL)deferCreation {
211   if (self = [super initWithContentRect:contentRect
212                               styleMask:windowStyle
213                                 backing:bufferingType
214                                   defer:deferCreation]) {
215     [self setOpaque:NO];
216     [self setBackgroundColor:[NSColor clearColor]];
217     [self startObservingClicks];
218   }
219   return self;
222 - (void)close {
223   [self stopObservingClicks];
224   [super close];
227 // Gets called when the menubar is clicked.
228 // Needed because the local event monitor doesn't see the click on the menubar.
229 - (void)beganTracking:(NSNotification*)notification {
230   [self close];
233 // Install the callback.
234 - (void)startObservingClicks {
235   clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
236       handler:^NSEvent* (NSEvent* event) {
237           if ([event window] == self)
238             return event;
239           NSEventType eventType = [event type];
240           if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
241             [self close];
242           return event;
243   }];
245   NSNotificationCenter* notificationCenter =
246       [NSNotificationCenter defaultCenter];
247   [notificationCenter addObserver:self
248          selector:@selector(beganTracking:)
249              name:NSMenuDidBeginTrackingNotification
250            object:[NSApp mainMenu]];
253 // Remove the callback.
254 - (void)stopObservingClicks {
255   if (!clickEventTap_)
256     return;
258   [NSEvent removeMonitor:clickEventTap_];
259    clickEventTap_ = nil;
261   NSNotificationCenter* notificationCenter =
262       [NSNotificationCenter defaultCenter];
263   [notificationCenter removeObserver:self
264                 name:NSMenuDidBeginTrackingNotification
265               object:[NSApp mainMenu]];
268 @end
270 namespace {
272 // Maximum number of characters we allow in a tooltip.
273 const size_t kMaxTooltipLength = 1024;
275 // TODO(suzhe): Upstream this function.
276 blink::WebColor WebColorFromNSColor(NSColor *color) {
277   CGFloat r, g, b, a;
278   [color getRed:&r green:&g blue:&b alpha:&a];
280   return
281       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
282       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
283       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
284       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
287 // Extract underline information from an attributed string. Mostly copied from
288 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
289 void ExtractUnderlines(
290     NSAttributedString* string,
291     std::vector<blink::WebCompositionUnderline>* underlines) {
292   int length = [[string string] length];
293   int i = 0;
294   while (i < length) {
295     NSRange range;
296     NSDictionary* attrs = [string attributesAtIndex:i
297                               longestEffectiveRange:&range
298                                             inRange:NSMakeRange(i, length - i)];
299     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
300       blink::WebColor color = SK_ColorBLACK;
301       if (NSColor *colorAttr =
302           [attrs objectForKey:NSUnderlineColorAttributeName]) {
303         color = WebColorFromNSColor(
304             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
305       }
306       underlines->push_back(
307           blink::WebCompositionUnderline(range.location,
308                                          NSMaxRange(range),
309                                          color,
310                                          [style intValue] > 1,
311                                          SK_ColorTRANSPARENT));
312     }
313     i = range.location + range.length;
314   }
317 // EnablePasswordInput() and DisablePasswordInput() are copied from
318 // enableSecureTextInput() and disableSecureTextInput() functions in
319 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
320 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
321 // here, because they are already called in webkit and they are system wide
322 // functions.
323 void EnablePasswordInput() {
324   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
325   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
326                          sizeof(CFArrayRef), &inputSources);
327   CFRelease(inputSources);
330 void DisablePasswordInput() {
331   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
334 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
335 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
336 // value when screen info changes.
337 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
338 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
339 // is necessary.
340 bool g_screen_info_up_to_date = false;
342 float FlipYFromRectToScreen(float y, float rect_height) {
343   TRACE_EVENT0("browser", "FlipYFromRectToScreen");
344   static CGFloat screen_zero_height = 0;
345   if (!g_screen_info_up_to_date) {
346     if ([[NSScreen screens] count] > 0) {
347       screen_zero_height =
348           [[[NSScreen screens] objectAtIndex:0] frame].size.height;
349       g_screen_info_up_to_date = true;
350     } else {
351       return y;
352     }
353   }
354   return screen_zero_height - y - rect_height;
357 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
358 // left of the primary screen (Carbon coordinates), and stuffs it into a
359 // gfx::Rect.
360 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
361   gfx::Rect new_rect(NSRectToCGRect(rect));
362   new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
363   return new_rect;
366 // Returns the window that visually contains the given view. This is different
367 // from [view window] in the case of tab dragging, where the view's owning
368 // window is a floating panel attached to the actual browser window that the tab
369 // is visually part of.
370 NSWindow* ApparentWindowForView(NSView* view) {
371   // TODO(shess): In case of !window, the view has been removed from
372   // the view hierarchy because the tab isn't main.  Could retrieve
373   // the information from the main tab for our window.
374   NSWindow* enclosing_window = [view window];
376   // See if this is a tab drag window. The width check is to distinguish that
377   // case from extension popup windows.
378   NSWindow* ancestor_window = [enclosing_window parentWindow];
379   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
380                           NSWidth([ancestor_window frame]))) {
381     enclosing_window = ancestor_window;
382   }
384   return enclosing_window;
387 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
388   gfx::Display display =
389       gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
391   NSScreen* screen = [NSScreen deepestScreen];
393   blink::WebScreenInfo results;
395   results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
396   results.depth = NSBitsPerPixelFromDepth([screen depth]);
397   results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
398   results.isMonochrome =
399       [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
400   results.rect = display.bounds();
401   results.availableRect = display.work_area();
402   results.orientationAngle = display.RotationAsDegree();
403   results.orientationType =
404       content::RenderWidgetHostViewBase::GetOrientationTypeForDesktop(display);
406   return results;
409 }  // namespace
411 namespace content {
413 ////////////////////////////////////////////////////////////////////////////////
414 // DelegatedFrameHost, public:
416 ui::Layer* RenderWidgetHostViewMac::DelegatedFrameHostGetLayer() const {
417   return root_layer_.get();
420 bool RenderWidgetHostViewMac::DelegatedFrameHostIsVisible() const {
421   return !render_widget_host_->is_hidden();
424 gfx::Size RenderWidgetHostViewMac::DelegatedFrameHostDesiredSizeInDIP() const {
425   return GetViewBounds().size();
428 bool RenderWidgetHostViewMac::DelegatedFrameCanCreateResizeLock() const {
429   // Mac uses the RenderWidgetResizeHelper instead of a resize lock.
430   return false;
433 scoped_ptr<ResizeLock>
434 RenderWidgetHostViewMac::DelegatedFrameHostCreateResizeLock(
435     bool defer_compositor_lock) {
436   NOTREACHED();
437   return scoped_ptr<ResizeLock>();
440 void RenderWidgetHostViewMac::DelegatedFrameHostResizeLockWasReleased() {
441   NOTREACHED();
444 void RenderWidgetHostViewMac::DelegatedFrameHostSendCompositorSwapAck(
445     int output_surface_id,
446     const cc::CompositorFrameAck& ack) {
447   render_widget_host_->Send(new ViewMsg_SwapCompositorFrameAck(
448       render_widget_host_->GetRoutingID(), output_surface_id, ack));
451 void RenderWidgetHostViewMac::DelegatedFrameHostSendReclaimCompositorResources(
452     int output_surface_id,
453     const cc::CompositorFrameAck& ack) {
454   render_widget_host_->Send(new ViewMsg_ReclaimCompositorResources(
455       render_widget_host_->GetRoutingID(), output_surface_id, ack));
458 void RenderWidgetHostViewMac::DelegatedFrameHostOnLostCompositorResources() {
459   render_widget_host_->ScheduleComposite();
462 void RenderWidgetHostViewMac::DelegatedFrameHostUpdateVSyncParameters(
463     const base::TimeTicks& timebase,
464     const base::TimeDelta& interval) {
465   render_widget_host_->UpdateVSyncParameters(timebase, interval);
468 ////////////////////////////////////////////////////////////////////////////////
469 // AcceleratedWidgetMacNSView, public:
471 NSView* RenderWidgetHostViewMac::AcceleratedWidgetGetNSView() const {
472   return cocoa_view_;
475 bool RenderWidgetHostViewMac::AcceleratedWidgetShouldIgnoreBackpressure()
476     const {
477   // If vsync is disabled, then always draw and ack frames immediately.
478   static bool is_vsync_disabled =
479       base::CommandLine::ForCurrentProcess()->HasSwitch(
480           switches::kDisableGpuVsync);
481   if (is_vsync_disabled)
482     return true;
484   // If the window is occluded, then this frame's display call may be severely
485   // throttled. This is a good thing, unless tab capture may be active, because
486   // the broadcast will be inappropriately throttled.
487   // http://crbug.com/350410
489   // If tab capture isn't active then only ack frames when we draw them.
490   if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber())
491     return false;
493   NSWindow* window = [cocoa_view_ window];
494   // If the view isn't even in the heirarchy then frames will never be drawn,
495   // so ack them immediately.
496   if (!window)
497     return true;
499   // Check the window occlusion API.
500   if ([window respondsToSelector:@selector(occlusionState)]) {
501     if ([window occlusionState] & NSWindowOcclusionStateVisible) {
502       // If the window is visible then it is safe to wait until frames are
503       // drawn to ack them.
504       return false;
505     } else {
506       // If the window is occluded then frames may never be drawn, so ack them
507       // immediately.
508       return true;
509     }
510   }
512   // If the window occlusion API is not present then ack frames when we draw
513   // them.
514   return false;
517 void RenderWidgetHostViewMac::AcceleratedWidgetSwapCompleted(
518     const std::vector<ui::LatencyInfo>& all_latency_info) {
519   if (!render_widget_host_)
520     return;
521   base::TimeTicks swap_time = base::TimeTicks::Now();
522   for (auto latency_info : all_latency_info) {
523     latency_info.AddLatencyNumberWithTimestamp(
524         ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, 0, swap_time, 1);
525     latency_info.AddLatencyNumberWithTimestamp(
526         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0,
527         swap_time, 1);
528     render_widget_host_->FrameSwapped(latency_info);
529   }
532 void RenderWidgetHostViewMac::AcceleratedWidgetHitError() {
533   // Request a new frame be drawn.
534   browser_compositor_->compositor()->ScheduleFullRedraw();
537 ///////////////////////////////////////////////////////////////////////////////
538 // RenderWidgetHostViewBase, public:
540 // static
541 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
542     blink::WebScreenInfo* results) {
543   *results = GetWebScreenInfo(NULL);
546 ///////////////////////////////////////////////////////////////////////////////
547 // RenderWidgetHostViewMac, public:
549 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget,
550                                                  bool is_guest_view_hack)
551     : render_widget_host_(RenderWidgetHostImpl::From(widget)),
552       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
553       can_compose_inline_(true),
554       browser_compositor_state_(BrowserCompositorDestroyed),
555       browser_compositor_placeholder_(new BrowserCompositorMacPlaceholder),
556       page_at_minimum_scale_(true),
557       is_loading_(false),
558       allow_pause_for_resize_or_repaint_(true),
559       is_guest_view_hack_(is_guest_view_hack),
560       fullscreen_parent_host_view_(NULL),
561       weak_factory_(this) {
562   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
563   // goes away.  Since we autorelease it, our caller must put
564   // |GetNativeView()| into the view hierarchy right after calling us.
565   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
566                   initWithRenderWidgetHostViewMac:this] autorelease];
568   // Paint this view host with |background_color_| when there is no content
569   // ready to draw.
570   background_layer_.reset([[CALayer alloc] init]);
571   // Set the default color to be white. This is the wrong thing to do, but many
572   // UI components expect this view to be opaque.
573   [background_layer_ setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
574   [cocoa_view_ setLayer:background_layer_];
575   [cocoa_view_ setWantsLayer:YES];
577   if (IsDelegatedRendererEnabled()) {
578     root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
579     delegated_frame_host_.reset(new DelegatedFrameHost(this));
580   }
582   gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this);
584   if (!is_guest_view_hack_)
585     render_widget_host_->SetView(this);
588 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
589   gfx::Screen::GetScreenFor(cocoa_view_)->RemoveObserver(this);
591   // This is being called from |cocoa_view_|'s destructor, so invalidate the
592   // pointer.
593   cocoa_view_ = nil;
595   UnlockMouse();
597   // Ensure that the browser compositor is destroyed in a safe order.
598   ShutdownBrowserCompositor();
600   // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
601   // RenderWidgetHost does we need to tell it not to hold a stale pointer to
602   // us.
603   if (render_widget_host_) {
604     // If this is a RenderWidgetHostViewGuest's platform_view_, we're not the
605     // RWH's view, the RenderWidgetHostViewGuest is. So don't reset the RWH's
606     // view, the RenderWidgetHostViewGuest will do it.
607     if (!is_guest_view_hack_)
608       render_widget_host_->SetView(NULL);
609   }
612 void RenderWidgetHostViewMac::SetDelegate(
613     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
614   [cocoa_view_ setResponderDelegate:delegate];
617 void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
618   allow_pause_for_resize_or_repaint_ = allow;
621 ///////////////////////////////////////////////////////////////////////////////
622 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
624 void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
625   TRACE_EVENT0("browser",
626                "RenderWidgetHostViewMac::EnsureBrowserCompositorView");
628   // Create the view, to transition from Destroyed -> Suspended.
629   if (browser_compositor_state_ == BrowserCompositorDestroyed) {
630     browser_compositor_ = BrowserCompositorMac::Create();
631     browser_compositor_->compositor()->SetRootLayer(root_layer_.get());
632     browser_compositor_->compositor()->SetHostHasTransparentBackground(
633         !GetBackgroundOpaque());
634     browser_compositor_->accelerated_widget_mac()->SetNSView(this);
635     browser_compositor_state_ = BrowserCompositorSuspended;
636   }
638   // Show the DelegatedFrameHost to transition from Suspended -> Active.
639   if (browser_compositor_state_ == BrowserCompositorSuspended) {
640     delegated_frame_host_->SetCompositor(browser_compositor_->compositor());
641     delegated_frame_host_->WasShown(ui::LatencyInfo());
642     // Unsuspend the browser compositor after showing the delegated frame host.
643     // If there is not a saved delegated frame, then the delegated frame host
644     // will keep the compositor locked until a delegated frame is swapped.
645     float scale_factor = ViewScaleFactor();
646     browser_compositor_->compositor()->SetScaleAndSize(
647         scale_factor,
648         gfx::ConvertSizeToPixel(scale_factor, GetViewBounds().size()));
649     browser_compositor_->Unsuspend();
650     browser_compositor_state_ = BrowserCompositorActive;
651   }
654 void RenderWidgetHostViewMac::SuspendBrowserCompositorView() {
655   TRACE_EVENT0("browser",
656                "RenderWidgetHostViewMac::SuspendBrowserCompositorView");
658   // Hide the DelegatedFrameHost to transition from Active -> Suspended.
659   if (browser_compositor_state_ == BrowserCompositorActive) {
660     // Ensure that any changes made to the ui::Compositor do not result in new
661     // frames being produced.
662     browser_compositor_->Suspend();
663     // Marking the DelegatedFrameHost as removed from the window hierarchy is
664     // necessary to remove all connections to its old ui::Compositor.
665     delegated_frame_host_->WasHidden();
666     delegated_frame_host_->ResetCompositor();
667     browser_compositor_state_ = BrowserCompositorSuspended;
668   }
671 void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
672   TRACE_EVENT0("browser",
673                "RenderWidgetHostViewMac::DestroyBrowserCompositorView");
675   // Transition from Active -> Suspended if need be.
676   SuspendBrowserCompositorView();
678   // Destroy the BrowserCompositorView to transition Suspended -> Destroyed.
679   if (browser_compositor_state_ == BrowserCompositorSuspended) {
680     browser_compositor_->accelerated_widget_mac()->ResetNSView();
681     browser_compositor_->compositor()->SetScaleAndSize(1.0, gfx::Size(0, 0));
682     browser_compositor_->compositor()->SetRootLayer(nullptr);
683     BrowserCompositorMac::Recycle(browser_compositor_.Pass());
684     browser_compositor_state_ = BrowserCompositorDestroyed;
685   }
688 void RenderWidgetHostViewMac::DestroySuspendedBrowserCompositorViewIfNeeded() {
689   if (browser_compositor_state_ != BrowserCompositorSuspended)
690     return;
692   // If this view is in a window that is visible, keep around the suspended
693   // BrowserCompositorView in case |cocoa_view_| is suddenly revealed (so that
694   // we don't flash white).
695   NSWindow* window = [cocoa_view_ window];
696   if (window)
697     return;
699   // This should only be reached if |render_widget_host_| is hidden, destroyed,
700   // or in the process of being destroyed.
701   DestroyBrowserCompositorView();
704 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
705   bool handled = true;
706   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
707     IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
708     IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
709     IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted,
710         OnGetRenderedTextCompleted)
711     IPC_MESSAGE_UNHANDLED(handled = false)
712   IPC_END_MESSAGE_MAP()
713   return handled;
716 void RenderWidgetHostViewMac::InitAsChild(
717     gfx::NativeView parent_view) {
720 void RenderWidgetHostViewMac::InitAsPopup(
721     RenderWidgetHostView* parent_host_view,
722     const gfx::Rect& pos) {
723   bool activatable = popup_type_ == blink::WebPopupTypeNone;
724   [cocoa_view_ setCloseOnDeactivate:YES];
725   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
727   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
728   origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
730   popup_window_.reset([[RenderWidgetPopupWindow alloc]
731       initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
732                                      pos.width(), pos.height())
733                 styleMask:NSBorderlessWindowMask
734                   backing:NSBackingStoreBuffered
735                     defer:NO]);
736   [popup_window_ setLevel:NSPopUpMenuWindowLevel];
737   [popup_window_ setReleasedWhenClosed:NO];
738   [popup_window_ makeKeyAndOrderFront:nil];
739   [[popup_window_ contentView] addSubview:cocoa_view_];
740   [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
741   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
742   [[NSNotificationCenter defaultCenter]
743       addObserver:cocoa_view_
744          selector:@selector(popupWindowWillClose:)
745              name:NSWindowWillCloseNotification
746            object:popup_window_];
749 // This function creates the fullscreen window and hides the dock and menubar if
750 // necessary. Note, this codepath is only used for pepper flash when
751 // pp::FlashFullScreen::SetFullscreen() is called. If
752 // pp::FullScreen::SetFullscreen() is called then the entire browser window
753 // will enter fullscreen instead.
754 void RenderWidgetHostViewMac::InitAsFullscreen(
755     RenderWidgetHostView* reference_host_view) {
756   fullscreen_parent_host_view_ =
757       static_cast<RenderWidgetHostViewMac*>(reference_host_view);
758   NSWindow* parent_window = nil;
759   if (reference_host_view)
760     parent_window = [reference_host_view->GetNativeView() window];
761   NSScreen* screen = [parent_window screen];
762   if (!screen)
763     screen = [NSScreen mainScreen];
765   pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
766       initWithContentRect:[screen frame]
767                 styleMask:NSBorderlessWindowMask
768                   backing:NSBackingStoreBuffered
769                     defer:NO]);
770   [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
771   [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
772   [cocoa_view_ setCanBeKeyView:YES];
773   [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
774   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
775   // If the pepper fullscreen window isn't opaque then there are performance
776   // issues when it's on the discrete GPU and the Chrome window is being drawn
777   // to. http://crbug.com/171911
778   [pepper_fullscreen_window_ setOpaque:YES];
780   // Note that this forms a reference cycle between the fullscreen window and
781   // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
782   // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
783   // This cycle is normally broken when -keyEvent: receives an <esc> key, which
784   // explicitly calls Shutdown on the render_widget_host_, which calls
785   // Destroy() on RWHVMac, which drops the reference to
786   // pepper_fullscreen_window_.
787   [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
789   // Note that this keeps another reference to pepper_fullscreen_window_.
790   fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
791       initWithWindow:pepper_fullscreen_window_.get()
792        desiredScreen:screen]);
793   [fullscreen_window_manager_ enterFullscreenMode];
794   [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
797 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
798   // See comment in InitAsFullscreen(): There is a reference cycle between
799   // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
800   // Tests that test pepper fullscreen mode without sending an <esc> event
801   // need to call this method to break the reference cycle.
802   [fullscreen_window_manager_ exitFullscreenMode];
803   fullscreen_window_manager_.reset();
804   [pepper_fullscreen_window_ close];
805   pepper_fullscreen_window_.reset();
808 int RenderWidgetHostViewMac::window_number() const {
809   NSWindow* window = [cocoa_view_ window];
810   if (!window)
811     return -1;
812   return [window windowNumber];
815 float RenderWidgetHostViewMac::ViewScaleFactor() const {
816   return ui::GetScaleFactorForNativeView(cocoa_view_);
819 void RenderWidgetHostViewMac::UpdateDisplayLink() {
820   static bool is_vsync_disabled =
821       base::CommandLine::ForCurrentProcess()->HasSwitch(
822           switches::kDisableGpuVsync);
823   if (is_vsync_disabled)
824     return;
826   NSScreen* screen = [[cocoa_view_ window] screen];
827   NSDictionary* screen_description = [screen deviceDescription];
828   NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
829   CGDirectDisplayID display_id = [screen_number unsignedIntValue];
831   display_link_ = ui::DisplayLinkMac::GetForDisplay(display_id);
832   if (!display_link_.get()) {
833     // Note that on some headless systems, the display link will fail to be
834     // created, so this should not be a fatal error.
835     LOG(ERROR) << "Failed to create display link.";
836   }
839 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
840   if (!render_widget_host_ || !display_link_.get())
841     return;
843   if (!display_link_->GetVSyncParameters(&vsync_timebase_, &vsync_interval_)) {
844     vsync_timebase_ = base::TimeTicks();
845     vsync_interval_ = base::TimeDelta();
846     return;
847   }
849   if (browser_compositor_) {
850     browser_compositor_->compositor()->vsync_manager()->UpdateVSyncParameters(
851         vsync_timebase_, vsync_interval_);
852   }
855 void RenderWidgetHostViewMac::SpeakText(const std::string& text) {
856   [NSApp speakString:base::SysUTF8ToNSString(text)];
859 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
860   if (!render_widget_host_)
861     return;
862   render_widget_host_->NotifyScreenInfoChanged();
865 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
866   return render_widget_host_;
869 void RenderWidgetHostViewMac::Show() {
870   ScopedCAActionDisabler disabler;
871   [cocoa_view_ setHidden:NO];
872   if (!render_widget_host_->is_hidden())
873     return;
875   // Re-create the browser compositor. If the DelegatedFrameHost has a cached
876   // frame from the last time it was visible, then it will immediately be
877   // drawn. If not, then the compositor will remain locked until a new delegated
878   // frame is swapped.
879   EnsureBrowserCompositorView();
881   WasUnOccluded();
883   // If there is not a frame being currently drawn, kick one, so that the below
884   // pause will have a frame to wait on.
885   render_widget_host_->ScheduleComposite();
886   PauseForPendingResizeOrRepaintsAndDraw();
889 void RenderWidgetHostViewMac::Hide() {
890   ScopedCAActionDisabler disabler;
891   [cocoa_view_ setHidden:YES];
892   WasOccluded();
893   DestroySuspendedBrowserCompositorViewIfNeeded();
896 void RenderWidgetHostViewMac::WasUnOccluded() {
897   if (!render_widget_host_->is_hidden())
898     return;
900   ui::LatencyInfo renderer_latency_info;
901   renderer_latency_info.AddLatencyNumber(
902       ui::TAB_SHOW_COMPONENT,
903       render_widget_host_->GetLatencyComponentId(),
904       0);
905   render_widget_host_->WasShown(renderer_latency_info);
908 void RenderWidgetHostViewMac::WasOccluded() {
909   if (render_widget_host_->is_hidden())
910     return;
912   render_widget_host_->WasHidden();
913   SuspendBrowserCompositorView();
916 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
917   gfx::Rect rect = GetViewBounds();
918   rect.set_size(size);
919   SetBounds(rect);
922 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
923   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
924   // TODO(thakis): fix, http://crbug.com/73362
925   if (render_widget_host_->is_hidden())
926     return;
928   // During the initial creation of the RenderWidgetHostView in
929   // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
930   // an empty size. In the Windows code flow, it is not ignored because
931   // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
932   // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
933   // flags to keep things sized properly. On the other hand, if the size is not
934   // empty then this is a valid request for a pop-up.
935   if (rect.size().IsEmpty())
936     return;
938   // Ignore the position of |rect| for non-popup rwhvs. This is because
939   // background tabs do not have a window, but the window is required for the
940   // coordinate conversions. Popups are always for a visible tab.
941   //
942   // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
943   // valid for resizing to be requested (e.g., during tab capture, to size the
944   // view to screen-capture resolution). In this case, simply treat the view as
945   // relative to the screen.
946   BOOL isRelativeToScreen = IsPopup() ||
947       ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
948   if (isRelativeToScreen) {
949     // The position of |rect| is screen coordinate system and we have to
950     // consider Cocoa coordinate system is upside-down and also multi-screen.
951     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
952     NSSize size = NSMakeSize(rect.width(), rect.height());
953     size = [cocoa_view_ convertSize:size toView:nil];
954     origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
955     NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
956                               size.width, size.height);
957     if (IsPopup())
958       [popup_window_ setFrame:frame display:YES];
959     else
960       [cocoa_view_ setFrame:frame];
961   } else {
962     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
963     gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
964     rect2.set_width(rect.width());
965     rect2.set_height(rect.height());
966     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
967   }
970 gfx::Vector2dF RenderWidgetHostViewMac::GetLastScrollOffset() const {
971   return last_scroll_offset_;
974 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
975   return cocoa_view_;
978 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
979   return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
982 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
983   return cocoa_view_;
986 void RenderWidgetHostViewMac::MovePluginWindows(
987     const std::vector<WebPluginGeometry>& moves) {
988   // Must be overridden, but unused on this platform. Core Animation
989   // plugins are drawn by the GPU process (through the compositor),
990   // and Core Graphics plugins are drawn by the renderer process.
991   DCHECK_CURRENTLY_ON(BrowserThread::UI);
994 void RenderWidgetHostViewMac::Focus() {
995   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
998 bool RenderWidgetHostViewMac::HasFocus() const {
999   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
1002 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
1003   if (delegated_frame_host_)
1004     return delegated_frame_host_->CanCopyToBitmap();
1005   return false;
1008 bool RenderWidgetHostViewMac::IsShowing() {
1009   return ![cocoa_view_ isHidden];
1012 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
1013   NSRect bounds = [cocoa_view_ bounds];
1014   // TODO(shess): In case of !window, the view has been removed from
1015   // the view hierarchy because the tab isn't main.  Could retrieve
1016   // the information from the main tab for our window.
1017   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1018   if (!enclosing_window)
1019     return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
1021   bounds = [cocoa_view_ convertRect:bounds toView:nil];
1022   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
1023   return FlipNSRectToRectScreen(bounds);
1026 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
1027   WebCursor web_cursor = cursor;
1028   [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
1031 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
1032   is_loading_ = is_loading;
1033   // If we ever decide to show the waiting cursor while the page is loading
1034   // like Chrome does on Windows, call |UpdateCursor()| here.
1037 void RenderWidgetHostViewMac::TextInputTypeChanged(
1038     ui::TextInputType type,
1039     ui::TextInputMode input_mode,
1040     bool can_compose_inline,
1041     int flags) {
1042   if (text_input_type_ != type
1043       || can_compose_inline_ != can_compose_inline) {
1044     text_input_type_ = type;
1045     can_compose_inline_ = can_compose_inline;
1046     if (HasFocus()) {
1047       SetTextInputActive(true);
1049       // Let AppKit cache the new input context to make IMEs happy.
1050       // See http://crbug.com/73039.
1051       [NSApp updateWindows];
1053 #ifndef __LP64__
1054       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
1055 #endif
1056     }
1057   }
1060 void RenderWidgetHostViewMac::ImeCancelComposition() {
1061   [cocoa_view_ cancelComposition];
1064 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
1065     const gfx::Range& range,
1066     const std::vector<gfx::Rect>& character_bounds) {
1067   // The RangeChanged message is only sent with valid values. The current
1068   // caret position (start == end) will be sent if there is no IME range.
1069   [cocoa_view_ setMarkedRange:range.ToNSRange()];
1070   composition_range_ = range;
1071   composition_bounds_ = character_bounds;
1074 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
1075                                                 int error_code) {
1076   Destroy();
1079 void RenderWidgetHostViewMac::RenderWidgetHostGone() {
1080   // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never
1081   // called on the view.
1082   // http://crbug.com/404828
1083   ShutdownBrowserCompositor();
1086 void RenderWidgetHostViewMac::Destroy() {
1087   [[NSNotificationCenter defaultCenter]
1088       removeObserver:cocoa_view_
1089                 name:NSWindowWillCloseNotification
1090               object:popup_window_];
1092   // We've been told to destroy.
1093   [cocoa_view_ retain];
1094   [cocoa_view_ removeFromSuperview];
1095   [cocoa_view_ autorelease];
1097   [popup_window_ close];
1098   popup_window_.autorelease();
1100   [fullscreen_window_manager_ exitFullscreenMode];
1101   fullscreen_window_manager_.reset();
1102   [pepper_fullscreen_window_ close];
1104   // This can be called as part of processing the window's responder
1105   // chain, for instance |-performKeyEquivalent:|.  In that case the
1106   // object needs to survive until the stack unwinds.
1107   pepper_fullscreen_window_.autorelease();
1109   // Delete the delegated frame state, which will reach back into
1110   // render_widget_host_.
1111   ShutdownBrowserCompositor();
1113   // We get this call just before |render_widget_host_| deletes
1114   // itself.  But we are owned by |cocoa_view_|, which may be retained
1115   // by some other code.  Examples are WebContentsViewMac's
1116   // |latent_focus_view_| and TabWindowController's
1117   // |cachedContentView_|.
1118   render_widget_host_ = NULL;
1121 // Called from the renderer to tell us what the tooltip text should be. It
1122 // calls us frequently so we need to cache the value to prevent doing a lot
1123 // of repeat work.
1124 void RenderWidgetHostViewMac::SetTooltipText(
1125     const base::string16& tooltip_text) {
1126   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1127     tooltip_text_ = tooltip_text;
1129     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1130     // Windows; we're just trying to be polite. Don't persist the trimmed
1131     // string, as then the comparison above will always fail and we'll try to
1132     // set it again every single time the mouse moves.
1133     base::string16 display_text = tooltip_text_;
1134     if (tooltip_text_.length() > kMaxTooltipLength)
1135       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1137     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1138     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1139   }
1142 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1143   return [NSApp respondsToSelector:@selector(speakString:)] &&
1144          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1147 void RenderWidgetHostViewMac::SpeakSelection() {
1148   if (![NSApp respondsToSelector:@selector(speakString:)])
1149     return;
1151   if (selected_text_.empty() && render_widget_host_) {
1152     // If there's no selection, speak all text. Send an asynchronous IPC
1153     // request for fetching all the text for a webcontent.
1154     // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1155     render_widget_host_->Send(new ViewMsg_GetRenderedText(
1156         render_widget_host_->GetRoutingID()));
1157     return;
1158   }
1160   SpeakText(selected_text_);
1163 bool RenderWidgetHostViewMac::IsSpeaking() const {
1164   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1165          [NSApp isSpeaking];
1168 void RenderWidgetHostViewMac::StopSpeaking() {
1169   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1170     [NSApp stopSpeaking:cocoa_view_];
1174 // RenderWidgetHostViewCocoa uses the stored selection text,
1175 // which implements NSServicesRequests protocol.
1177 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1178                                                size_t offset,
1179                                                const gfx::Range& range) {
1180   if (range.is_empty() || text.empty()) {
1181     selected_text_.clear();
1182   } else {
1183     size_t pos = range.GetMin() - offset;
1184     size_t n = range.length();
1186     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1187     if (pos >= text.length()) {
1188       DCHECK(false) << "The text can not cover range.";
1189       return;
1190     }
1191     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1192   }
1194   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1195   // Updates markedRange when there is no marked text so that retrieving
1196   // markedRange immediately after calling setMarkdText: returns the current
1197   // caret position.
1198   if (![cocoa_view_ hasMarkedText]) {
1199     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1200   }
1202   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1205 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1206     const ViewHostMsg_SelectionBounds_Params& params) {
1207   if (params.anchor_rect == params.focus_rect)
1208     caret_rect_ = params.anchor_rect;
1211 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1212   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1214   // Create a fake mouse event to inform the render widget that the mouse
1215   // left or entered.
1216   NSWindow* window = [cocoa_view_ window];
1217   // TODO(asvitkine): If the location outside of the event stream doesn't
1218   // correspond to the current event (due to delayed event processing), then
1219   // this may result in a cursor flicker if there are later mouse move events
1220   // in the pipeline. Find a way to use the mouse location from the event that
1221   // dismissed the context menu.
1222   NSPoint location = [window mouseLocationOutsideOfEventStream];
1223   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1224                                       location:location
1225                                  modifierFlags:0
1226                                      timestamp:0
1227                                   windowNumber:window_number()
1228                                        context:nil
1229                                    eventNumber:0
1230                                     clickCount:0
1231                                       pressure:0];
1232   WebMouseEvent web_event =
1233       WebInputEventFactory::mouseEvent(event, cocoa_view_);
1234   if (showing)
1235     web_event.type = WebInputEvent::MouseLeave;
1236   ForwardMouseEvent(web_event);
1239 bool RenderWidgetHostViewMac::IsPopup() const {
1240   return popup_type_ != blink::WebPopupTypeNone;
1243 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1244     const gfx::Rect& src_subrect,
1245     const gfx::Size& dst_size,
1246     ReadbackRequestCallback& callback,
1247     const SkColorType preferred_color_type) {
1248   if (delegated_frame_host_) {
1249     delegated_frame_host_->CopyFromCompositingSurface(
1250         src_subrect, dst_size, callback, preferred_color_type);
1251   }
1254 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1255       const gfx::Rect& src_subrect,
1256       const scoped_refptr<media::VideoFrame>& target,
1257       const base::Callback<void(bool)>& callback) {
1258   if (delegated_frame_host_) {
1259     delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1260         src_subrect, target, callback);
1261   }
1264 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1265   if (delegated_frame_host_)
1266     return delegated_frame_host_->CanCopyToVideoFrame();
1267   return false;
1270 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1271   if (delegated_frame_host_)
1272     return delegated_frame_host_->CanSubscribeFrame();
1273   return false;
1276 void RenderWidgetHostViewMac::BeginFrameSubscription(
1277     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1278   if (delegated_frame_host_)
1279     delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1282 void RenderWidgetHostViewMac::EndFrameSubscription() {
1283   if (delegated_frame_host_)
1284     delegated_frame_host_->EndFrameSubscription();
1287 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1288   if (render_widget_host_)
1289     render_widget_host_->ForwardMouseEvent(event);
1291   if (event.type == WebInputEvent::MouseLeave) {
1292     [cocoa_view_ setToolTipAtMousePoint:nil];
1293     tooltip_text_.clear();
1294   }
1297 void RenderWidgetHostViewMac::KillSelf() {
1298   if (!weak_factory_.HasWeakPtrs()) {
1299     [cocoa_view_ setHidden:YES];
1300     base::MessageLoop::current()->PostTask(FROM_HERE,
1301         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1302                    weak_factory_.GetWeakPtr()));
1303   }
1306 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1307     const NativeWebKeyboardEvent& event) {
1308   // Check WebInputEvent type since multiple types of events can be sent into
1309   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1310   // necessary to avoid double processing.
1311   // Also check the native type, since NSFlagsChanged is considered a key event
1312   // for WebKit purposes, but isn't considered a key event by the OS.
1313   if (event.type == WebInputEvent::RawKeyDown &&
1314       [event.os_event type] == NSKeyDown)
1315     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1316   return false;
1319 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1320     const base::string16& text, int plugin_id) {
1321   if (render_widget_host_) {
1322     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1323         render_widget_host_->GetRoutingID(), text, plugin_id));
1324   }
1327 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1328     const std::vector<gfx::Rect>& bounds,
1329     const gfx::Range& range,
1330     size_t* line_break_point) {
1331   DCHECK(line_break_point);
1332   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1333     return false;
1335   // We can't check line breaking completely from only rectangle array. Thus we
1336   // assume the line breaking as the next character's y offset is larger than
1337   // a threshold. Currently the threshold is determined as minimum y offset plus
1338   // 75% of maximum height.
1339   // TODO(nona): Check the threshold is reliable or not.
1340   // TODO(nona): Bidi support.
1341   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1342   int max_height = 0;
1343   int min_y_offset = kint32max;
1344   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1345     max_height = std::max(max_height, bounds[idx].height());
1346     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1347   }
1348   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1349   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1350     if (bounds[idx].y() > line_break_threshold) {
1351       *line_break_point = idx;
1352       return true;
1353     }
1354   }
1355   return false;
1358 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1359     const gfx::Range& range,
1360     gfx::Range* actual_range) {
1361   DCHECK(actual_range);
1362   DCHECK(!composition_bounds_.empty());
1363   DCHECK(range.start() <= composition_bounds_.size());
1364   DCHECK(range.end() <= composition_bounds_.size());
1366   if (range.is_empty()) {
1367     *actual_range = range;
1368     if (range.start() == composition_bounds_.size()) {
1369       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1370                        composition_bounds_[range.start() - 1].y(),
1371                        0,
1372                        composition_bounds_[range.start() - 1].height());
1373     } else {
1374       return gfx::Rect(composition_bounds_[range.start()].x(),
1375                        composition_bounds_[range.start()].y(),
1376                        0,
1377                        composition_bounds_[range.start()].height());
1378     }
1379   }
1381   size_t end_idx;
1382   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1383     end_idx = range.end();
1384   }
1385   *actual_range = gfx::Range(range.start(), end_idx);
1386   gfx::Rect rect = composition_bounds_[range.start()];
1387   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1388     rect.Union(composition_bounds_[i]);
1389   }
1390   return rect;
1393 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1394     const gfx::Range& request_range) {
1395   if (composition_range_.is_empty())
1396     return gfx::Range::InvalidRange();
1398   if (request_range.is_reversed())
1399     return gfx::Range::InvalidRange();
1401   if (request_range.start() < composition_range_.start() ||
1402       request_range.start() > composition_range_.end() ||
1403       request_range.end() > composition_range_.end()) {
1404     return gfx::Range::InvalidRange();
1405   }
1407   return gfx::Range(
1408       request_range.start() - composition_range_.start(),
1409       request_range.end() - composition_range_.start());
1412 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1413   if (!render_widget_host_->IsRenderView())
1414     return NULL;
1416   return WebContents::FromRenderViewHost(
1417       RenderViewHost::From(render_widget_host_));
1420 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1421     NSRange range,
1422     NSRect* rect,
1423     NSRange* actual_range) {
1424   DCHECK(rect);
1425   // This exists to make IMEs more responsive, see http://crbug.com/115920
1426   TRACE_EVENT0("browser",
1427                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1429   // If requested range is same as caret location, we can just return it.
1430   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1431     if (actual_range)
1432       *actual_range = range;
1433     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1434     return true;
1435   }
1437   const gfx::Range request_range_in_composition =
1438       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1439   if (request_range_in_composition == gfx::Range::InvalidRange())
1440     return false;
1442   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1443   // ImeCompositionRangeChanged will be sent with empty vector.
1444   if (composition_bounds_.empty())
1445     return false;
1446   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1448   gfx::Range ui_actual_range;
1449   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1450                                request_range_in_composition,
1451                                &ui_actual_range).ToCGRect());
1452   if (actual_range) {
1453     *actual_range = gfx::Range(
1454         composition_range_.start() + ui_actual_range.start(),
1455         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1456   }
1457   return true;
1460 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1461       const gfx::Size& desired_size) {
1462   if (browser_compositor_) {
1463     return browser_compositor_->accelerated_widget_mac()->HasFrameOfSize(
1464         desired_size);
1465   }
1466   return false;
1469 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1470     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1471   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1473   last_scroll_offset_ = frame->metadata.root_scroll_offset;
1475   page_at_minimum_scale_ = frame->metadata.page_scale_factor ==
1476                            frame->metadata.min_page_scale_factor;
1478   if (frame->delegated_frame_data) {
1479     float scale_factor = frame->metadata.device_scale_factor;
1481     // Compute the frame size based on the root render pass rect size.
1482     cc::RenderPass* root_pass =
1483         frame->delegated_frame_data->render_pass_list.back();
1484     gfx::Size pixel_size = root_pass->output_rect.size();
1485     gfx::Size dip_size = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
1487     root_layer_->SetBounds(gfx::Rect(dip_size));
1488     if (!render_widget_host_->is_hidden()) {
1489       EnsureBrowserCompositorView();
1490       browser_compositor_->compositor()->SetScaleAndSize(
1491           scale_factor, pixel_size);
1492     }
1494     SendVSyncParametersToRenderer();
1496     delegated_frame_host_->SwapDelegatedFrame(
1497         output_surface_id,
1498         frame->delegated_frame_data.Pass(),
1499         frame->metadata.device_scale_factor,
1500         frame->metadata.latency_info);
1501   } else {
1502     DLOG(ERROR) << "Received unexpected frame type.";
1503     bad_message::ReceivedBadMessage(render_widget_host_->GetProcess(),
1504                                     bad_message::RWHVM_UNEXPECTED_FRAME_TYPE);
1505   }
1508 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1509   *results = GetWebScreenInfo(GetNativeView());
1512 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1513   // TODO(shess): In case of !window, the view has been removed from
1514   // the view hierarchy because the tab isn't main.  Could retrieve
1515   // the information from the main tab for our window.
1516   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1517   if (!enclosing_window)
1518     return gfx::Rect();
1520   NSRect bounds = [enclosing_window frame];
1521   return FlipNSRectToRectScreen(bounds);
1524 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1525   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1526   // completely on Mac OS.
1527   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NULL_TRANSPORT);
1530 bool RenderWidgetHostViewMac::LockMouse() {
1531   if (mouse_locked_)
1532     return true;
1534   mouse_locked_ = true;
1536   // Lock position of mouse cursor and hide it.
1537   CGAssociateMouseAndMouseCursorPosition(NO);
1538   [NSCursor hide];
1540   // Clear the tooltip window.
1541   SetTooltipText(base::string16());
1543   return true;
1546 void RenderWidgetHostViewMac::UnlockMouse() {
1547   if (!mouse_locked_)
1548     return;
1549   mouse_locked_ = false;
1551   // Unlock position of mouse cursor and unhide it.
1552   CGAssociateMouseAndMouseCursorPosition(YES);
1553   [NSCursor unhide];
1555   if (render_widget_host_)
1556     render_widget_host_->LostMouseLock();
1559 void RenderWidgetHostViewMac::WheelEventAck(
1560     const blink::WebMouseWheelEvent& event,
1561     InputEventAckState ack_result) {
1562   bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1563   // Only record a wheel event as unhandled if JavaScript handlers got a chance
1564   // to see it (no-op wheel events are ignored by the event dispatcher)
1565   if (event.deltaX || event.deltaY)
1566     [cocoa_view_ processedWheelEvent:event consumed:consumed];
1569 uint32_t RenderWidgetHostViewMac::GetSurfaceIdNamespace() {
1570   if (delegated_frame_host_)
1571     return delegated_frame_host_->GetSurfaceIdNamespace();
1573   return 0;
1576 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1577   if (render_widget_host_)
1578     return render_widget_host_->Send(message);
1579   delete message;
1580   return false;
1583 void RenderWidgetHostViewMac::ShutdownHost() {
1584   weak_factory_.InvalidateWeakPtrs();
1585   render_widget_host_->Shutdown();
1586   // Do not touch any members at this point, |this| has been deleted.
1589 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1590   DestroyBrowserCompositorView();
1591   delegated_frame_host_.reset();
1592   root_layer_.reset();
1593   browser_compositor_placeholder_.reset();
1596 void RenderWidgetHostViewMac::SetActive(bool active) {
1597   if (render_widget_host_) {
1598     render_widget_host_->SetActive(active);
1599     if (active) {
1600       if (HasFocus())
1601         render_widget_host_->Focus();
1602     } else {
1603       render_widget_host_->Blur();
1604     }
1605   }
1606   if (HasFocus())
1607     SetTextInputActive(active);
1608   if (!active) {
1609     [cocoa_view_ setPluginImeActive:NO];
1610     UnlockMouse();
1611   }
1614 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1615   if (render_widget_host_) {
1616     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1617         render_widget_host_->GetRoutingID(), visible));
1618   }
1621 void RenderWidgetHostViewMac::WindowFrameChanged() {
1622   if (render_widget_host_) {
1623     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1624         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1625         GetViewBounds()));
1626   }
1629 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1630   RenderWidgetHostViewMacDictionaryHelper helper(this);
1631   helper.ShowDefinitionForSelection();
1634 void RenderWidgetHostViewMac::SetBackgroundColor(SkColor color) {
1635   RenderWidgetHostViewBase::SetBackgroundColor(color);
1636   bool opaque = GetBackgroundOpaque();
1638   if (render_widget_host_)
1639     render_widget_host_->SetBackgroundOpaque(opaque);
1641   [cocoa_view_ setOpaque:opaque];
1642   if (browser_compositor_state_ != BrowserCompositorDestroyed)
1643     browser_compositor_->compositor()->SetHostHasTransparentBackground(!opaque);
1646 BrowserAccessibilityManager*
1647     RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1648         BrowserAccessibilityDelegate* delegate) {
1649   return new BrowserAccessibilityManagerMac(
1650       cocoa_view_,
1651       BrowserAccessibilityManagerMac::GetEmptyDocument(),
1652       delegate);
1655 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1656     const gfx::Rect& bounds) {
1657   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1658   NSSize size = NSMakeSize(bounds.width(), bounds.height());
1659   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1660   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1661   NSPoint originInScreen =
1662       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1663   originInScreen.y = originInScreen.y - size.height;
1664   return gfx::Point(originInScreen.x, originInScreen.y);
1667 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1668   if (active) {
1669     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1670       EnablePasswordInput();
1671     else
1672       DisablePasswordInput();
1673   } else {
1674     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1675       DisablePasswordInput();
1676   }
1679 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1680                                                    int plugin_id) {
1681   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1684 void RenderWidgetHostViewMac::OnStartPluginIme() {
1685   [cocoa_view_ setPluginImeActive:YES];
1688 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1689     const std::string& text) {
1690   SpeakText(text);
1693 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1694   if (!render_widget_host_ || render_widget_host_->is_hidden())
1695     return;
1697   // Pausing for one view prevents others from receiving frames.
1698   // This may lead to large delays, causing overlaps. See crbug.com/352020.
1699   if (!allow_pause_for_resize_or_repaint_)
1700     return;
1702   // Wait for a frame of the right size to come in.
1703   if (browser_compositor_)
1704     browser_compositor_->accelerated_widget_mac()->BeginPumpingFrames();
1705   render_widget_host_->PauseForPendingResizeOrRepaints();
1706   if (browser_compositor_)
1707     browser_compositor_->accelerated_widget_mac()->EndPumpingFrames();
1710 ////////////////////////////////////////////////////////////////////////////////
1711 // gfx::DisplayObserver, public:
1713 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1716 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1719 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1720     const gfx::Display& display, uint32_t metrics) {
1721   gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1722   if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1723     return;
1725   UpdateScreenInfo(cocoa_view_);
1728 }  // namespace content
1730 // RenderWidgetHostViewCocoa ---------------------------------------------------
1732 @implementation RenderWidgetHostViewCocoa
1733 @synthesize selectedRange = selectedRange_;
1734 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1735 @synthesize markedRange = markedRange_;
1737 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1738   self = [super initWithFrame:NSZeroRect];
1739   if (self) {
1740     self.acceptsTouchEvents = YES;
1741     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1742     editCommand_helper_->AddEditingSelectorsToClass([self class]);
1744     renderWidgetHostView_.reset(r);
1745     canBeKeyView_ = YES;
1746     opaque_ = YES;
1747     focusedPluginIdentifier_ = -1;
1748     pinchHasReachedZoomThreshold_ = false;
1750     // OpenGL support:
1751     if ([self respondsToSelector:
1752         @selector(setWantsBestResolutionOpenGLSurface:)]) {
1753       [self setWantsBestResolutionOpenGLSurface:YES];
1754     }
1755     [[NSNotificationCenter defaultCenter]
1756         addObserver:self
1757            selector:@selector(didChangeScreenParameters:)
1758                name:NSApplicationDidChangeScreenParametersNotification
1759              object:nil];
1760   }
1761   return self;
1764 - (void)dealloc {
1765   if (responderDelegate_ &&
1766       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1767     [responderDelegate_ viewGone:self];
1768   responderDelegate_.reset();
1770   [[NSNotificationCenter defaultCenter] removeObserver:self];
1772   [super dealloc];
1775 - (void)didChangeScreenParameters:(NSNotification*)notify {
1776   g_screen_info_up_to_date = false;
1779 - (void)setResponderDelegate:
1780             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1781   DCHECK(!responderDelegate_);
1782   responderDelegate_.reset([delegate retain]);
1785 - (void)resetCursorRects {
1786   if (currentCursor_) {
1787     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1788     [currentCursor_ setOnMouseEntered:YES];
1789   }
1792 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1793                    consumed:(BOOL)consumed {
1794   [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1797 - (BOOL)respondsToSelector:(SEL)selector {
1798   // Trickiness: this doesn't mean "does this object's superclass respond to
1799   // this selector" but rather "does the -respondsToSelector impl from the
1800   // superclass say that this class responds to the selector".
1801   if ([super respondsToSelector:selector])
1802     return YES;
1804   if (responderDelegate_)
1805     return [responderDelegate_ respondsToSelector:selector];
1807   return NO;
1810 - (id)forwardingTargetForSelector:(SEL)selector {
1811   if ([responderDelegate_ respondsToSelector:selector])
1812     return responderDelegate_.get();
1814   return [super forwardingTargetForSelector:selector];
1817 - (void)setCanBeKeyView:(BOOL)can {
1818   canBeKeyView_ = can;
1821 - (BOOL)acceptsMouseEventsWhenInactive {
1822   // Some types of windows (balloons, always-on-top panels) want to accept mouse
1823   // clicks w/o the first click being treated as 'activation'. Same applies to
1824   // mouse move events.
1825   return [[self window] level] > NSNormalWindowLevel;
1828 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1829   return [self acceptsMouseEventsWhenInactive];
1832 - (void)setCloseOnDeactivate:(BOOL)b {
1833   closeOnDeactivate_ = b;
1836 - (void)setOpaque:(BOOL)opaque {
1837   opaque_ = opaque;
1840 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1841   NSWindow* window = [self window];
1842   // If this is a background window, don't handle mouse movement events. This
1843   // is the expected behavior on the Mac as evidenced by other applications.
1844   if ([theEvent type] == NSMouseMoved &&
1845       ![self acceptsMouseEventsWhenInactive] &&
1846       ![window isKeyWindow]) {
1847     return YES;
1848   }
1850   // Use hitTest to check whether the mouse is over a nonWebContentView - in
1851   // which case the mouse event should not be handled by the render host.
1852   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1853   NSView* contentView = [window contentView];
1854   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1855   // Traverse the superview hierarchy as the hitTest will return the frontmost
1856   // view, such as an NSTextView, while nonWebContentView may be specified by
1857   // its parent view.
1858   while (view) {
1859     if ([view respondsToSelector:nonWebContentViewSelector] &&
1860         [view performSelector:nonWebContentViewSelector]) {
1861       // The cursor is over a nonWebContentView - ignore this mouse event.
1862       return YES;
1863     }
1864     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1865         !hasOpenMouseDown_) {
1866       // The cursor is over an overlapping render widget. This check is done by
1867       // both views so the one that's returned by -hitTest: will end up
1868       // processing the event.
1869       // Note that while dragging, we only get events for the render view where
1870       // drag started, even if mouse is  actually over another view or outside
1871       // the window. Cocoa does this for us. We should handle these events and
1872       // not ignore (since there is no other render view to handle them). Thus
1873       // the |!hasOpenMouseDown_| check above.
1874       return YES;
1875     }
1876     view = [view superview];
1877   }
1878   return NO;
1881 - (void)mouseEvent:(NSEvent*)theEvent {
1882   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1883   if (responderDelegate_ &&
1884       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1885     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1886     if (handled)
1887       return;
1888   }
1890   if ([self shouldIgnoreMouseEvent:theEvent]) {
1891     // If this is the first such event, send a mouse exit to the host view.
1892     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1893       WebMouseEvent exitEvent =
1894           WebInputEventFactory::mouseEvent(theEvent, self);
1895       exitEvent.type = WebInputEvent::MouseLeave;
1896       exitEvent.button = WebMouseEvent::ButtonNone;
1897       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1898     }
1899     mouseEventWasIgnored_ = YES;
1900     return;
1901   }
1903   if (mouseEventWasIgnored_) {
1904     // If this is the first mouse event after a previous event that was ignored
1905     // due to the hitTest, send a mouse enter event to the host view.
1906     if (renderWidgetHostView_->render_widget_host_) {
1907       WebMouseEvent enterEvent =
1908           WebInputEventFactory::mouseEvent(theEvent, self);
1909       enterEvent.type = WebInputEvent::MouseMove;
1910       enterEvent.button = WebMouseEvent::ButtonNone;
1911       renderWidgetHostView_->ForwardMouseEvent(enterEvent);
1912     }
1913   }
1914   mouseEventWasIgnored_ = NO;
1916   // Don't cancel child popups; killing them on a mouse click would prevent the
1917   // user from positioning the insertion point in the text field spawning the
1918   // popup. A click outside the text field would cause the text field to drop
1919   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1920   // the popup anyway, so we're OK.
1922   NSEventType type = [theEvent type];
1923   if (type == NSLeftMouseDown)
1924     hasOpenMouseDown_ = YES;
1925   else if (type == NSLeftMouseUp)
1926     hasOpenMouseDown_ = NO;
1928   // TODO(suzhe): We should send mouse events to the input method first if it
1929   // wants to handle them. But it won't work without implementing method
1930   // - (NSUInteger)characterIndexForPoint:.
1931   // See: http://code.google.com/p/chromium/issues/detail?id=47141
1932   // Instead of sending mouse events to the input method first, we now just
1933   // simply confirm all ongoing composition here.
1934   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1935       type == NSOtherMouseDown) {
1936     [self confirmComposition];
1937   }
1939   const WebMouseEvent event =
1940       WebInputEventFactory::mouseEvent(theEvent, self);
1941   renderWidgetHostView_->ForwardMouseEvent(event);
1944 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1945   // |performKeyEquivalent:| is sent to all views of a window, not only down the
1946   // responder chain (cf. "Handling Key Equivalents" in
1947   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1948   // ). We only want to handle key equivalents if we're first responder.
1949   if ([[self window] firstResponder] != self)
1950     return NO;
1952   // If the event is reserved by the system, then do not pass it to web content.
1953   if (EventIsReservedBySystem(theEvent))
1954     return NO;
1956   // If we return |NO| from this function, cocoa will send the key event to
1957   // the menu and only if the menu does not process the event to |keyDown:|. We
1958   // want to send the event to a renderer _before_ sending it to the menu, so
1959   // we need to return |YES| for all events that might be swallowed by the menu.
1960   // We do not return |YES| for every keypress because we don't get |keyDown:|
1961   // events for keys that we handle this way.
1962   NSUInteger modifierFlags = [theEvent modifierFlags];
1963   if ((modifierFlags & NSCommandKeyMask) == 0) {
1964     // Make sure the menu does not contain key equivalents that don't
1965     // contain cmd.
1966     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1967     return NO;
1968   }
1970   // Command key combinations are sent via performKeyEquivalent rather than
1971   // keyDown:. We just forward this on and if WebCore doesn't want to handle
1972   // it, we let the WebContentsView figure out how to reinject it.
1973   [self keyEvent:theEvent wasKeyEquivalent:YES];
1974   return YES;
1977 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1978   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1979   // returned NO. If this function returns |YES|, Cocoa sends the event to
1980   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1981   // to us instead of doing key view loop control, ctrl-left/right get handled
1982   // correctly, etc.
1983   // (However, there are still some keys that Cocoa swallows, e.g. the key
1984   // equivalent that Cocoa uses for toggling the input language. In this case,
1985   // that's actually a good thing, though -- see http://crbug.com/26115 .)
1986   return YES;
1989 - (EventHandled)keyEvent:(NSEvent*)theEvent {
1990   if (responderDelegate_ &&
1991       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1992     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1993     if (handled)
1994       return kEventHandled;
1995   }
1997   [self keyEvent:theEvent wasKeyEquivalent:NO];
1998   return kEventHandled;
2001 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
2002   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
2004   // If the user changes the system hotkey mapping after Chrome has been
2005   // launched, then it is possible that a formerly reserved system hotkey is no
2006   // longer reserved. The hotkey would have skipped the renderer, but would
2007   // also have not been handled by the system. If this is the case, immediately
2008   // return.
2009   // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
2010   // api to monitor changes to system hotkeys. This logic will have to be
2011   // updated.
2012   // http://crbug.com/383558.
2013   if (EventIsReservedBySystem(theEvent))
2014     return;
2016   DCHECK([theEvent type] != NSKeyDown ||
2017          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
2019   if ([theEvent type] == NSFlagsChanged) {
2020     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
2021     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
2022     int keyCode = [theEvent keyCode];
2023     if (!keyCode || keyCode == 10 || keyCode == 63)
2024       return;
2025   }
2027   // Don't cancel child popups; the key events are probably what's triggering
2028   // the popup in the first place.
2030   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
2031   DCHECK(widgetHost);
2033   NativeWebKeyboardEvent event(theEvent);
2035   // Force fullscreen windows to close on Escape so they won't keep the keyboard
2036   // grabbed or be stuck onscreen if the renderer is hanging.
2037   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
2038       event.windowsKeyCode == ui::VKEY_ESCAPE &&
2039       renderWidgetHostView_->pepper_fullscreen_window()) {
2040     RenderWidgetHostViewMac* parent =
2041         renderWidgetHostView_->fullscreen_parent_host_view();
2042     if (parent)
2043       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
2044     widgetHost->Shutdown();
2045     return;
2046   }
2048   // Suppress the escape key up event if necessary.
2049   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
2050     if (event.type == NativeWebKeyboardEvent::KeyUp)
2051       suppressNextEscapeKeyUp_ = NO;
2052     return;
2053   }
2055   // Do not forward key up events unless preceded by a matching key down,
2056   // otherwise we might get an event from releasing the return key in the
2057   // omnibox (http://crbug.com/338736).
2058   if ([theEvent type] == NSKeyUp) {
2059     auto numErased = keyDownCodes_.erase([theEvent keyCode]);
2060     if (numErased < 1)
2061       return;
2062   }
2064   // We only handle key down events and just simply forward other events.
2065   if ([theEvent type] != NSKeyDown) {
2066     widgetHost->ForwardKeyboardEvent(event);
2068     // Possibly autohide the cursor.
2069     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2070       [NSCursor setHiddenUntilMouseMoves:YES];
2072     return;
2073   }
2075   keyDownCodes_.insert([theEvent keyCode]);
2077   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2079   // Records the current marked text state, so that we can know if the marked
2080   // text was deleted or not after handling the key down event.
2081   BOOL oldHasMarkedText = hasMarkedText_;
2083   // This method should not be called recursively.
2084   DCHECK(!handlingKeyDown_);
2086   // Tells insertText: and doCommandBySelector: that we are handling a key
2087   // down event.
2088   handlingKeyDown_ = YES;
2090   // These variables might be set when handling the keyboard event.
2091   // Clear them here so that we can know whether they have changed afterwards.
2092   textToBeInserted_.clear();
2093   markedText_.clear();
2094   underlines_.clear();
2095   unmarkTextCalled_ = NO;
2096   hasEditCommands_ = NO;
2097   editCommands_.clear();
2099   // Before doing anything with a key down, check to see if plugin IME has been
2100   // cancelled, since the plugin host needs to be informed of that before
2101   // receiving the keydown.
2102   if ([theEvent type] == NSKeyDown)
2103     [self checkForPluginImeCancellation];
2105   // Sends key down events to input method first, then we can decide what should
2106   // be done according to input method's feedback.
2107   // If a plugin is active, bypass this step since events are forwarded directly
2108   // to the plugin IME.
2109   if (focusedPluginIdentifier_ == -1)
2110     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2112   handlingKeyDown_ = NO;
2114   // Indicates if we should send the key event and corresponding editor commands
2115   // after processing the input method result.
2116   BOOL delayEventUntilAfterImeCompostion = NO;
2118   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2119   // while an input method is composing or inserting a text.
2120   // Gmail checks this code in its onkeydown handler to stop auto-completing
2121   // e-mail addresses while composing a CJK text.
2122   // If the text to be inserted has only one character, then we don't need this
2123   // trick, because we'll send the text as a key press event instead.
2124   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2125     NativeWebKeyboardEvent fakeEvent = event;
2126     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2127     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2128     fakeEvent.skip_in_browser = true;
2129     widgetHost->ForwardKeyboardEvent(fakeEvent);
2130     // If this key event was handled by the input method, but
2131     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2132     // enqueued edit commands, then in order to let webkit handle them
2133     // correctly, we need to send the real key event and corresponding edit
2134     // commands after processing the input method result.
2135     // We shouldn't do this if a new marked text was set by the input method,
2136     // otherwise the new marked text might be cancelled by webkit.
2137     if (hasEditCommands_ && !hasMarkedText_)
2138       delayEventUntilAfterImeCompostion = YES;
2139   } else {
2140     if (!editCommands_.empty()) {
2141       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2142           widgetHost->GetRoutingID(), editCommands_));
2143     }
2144     widgetHost->ForwardKeyboardEvent(event);
2145   }
2147   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2148   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2149   // be set to NULL. So we check it here and return immediately if it's NULL.
2150   if (!renderWidgetHostView_->render_widget_host_)
2151     return;
2153   // Then send keypress and/or composition related events.
2154   // If there was a marked text or the text to be inserted is longer than 1
2155   // character, then we send the text by calling ConfirmComposition().
2156   // Otherwise, if the text to be inserted only contains 1 character, then we
2157   // can just send a keypress event which is fabricated by changing the type of
2158   // the keydown event, so that we can retain all necessary informations, such
2159   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2160   // prevent the browser from handling it again.
2161   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2162   // handle BMP characters here, as we can always insert non-BMP characters as
2163   // text.
2164   BOOL textInserted = NO;
2165   if (textToBeInserted_.length() >
2166       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2167     widgetHost->ImeConfirmComposition(
2168         textToBeInserted_, gfx::Range::InvalidRange(), false);
2169     textInserted = YES;
2170   }
2172   // Updates or cancels the composition. If some text has been inserted, then
2173   // we don't need to cancel the composition explicitly.
2174   if (hasMarkedText_ && markedText_.length()) {
2175     // Sends the updated marked text to the renderer so it can update the
2176     // composition node in WebKit.
2177     // When marked text is available, |selectedRange_| will be the range being
2178     // selected inside the marked text.
2179     widgetHost->ImeSetComposition(markedText_, underlines_,
2180                                   selectedRange_.location,
2181                                   NSMaxRange(selectedRange_));
2182   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2183     if (unmarkTextCalled_) {
2184       widgetHost->ImeConfirmComposition(
2185           base::string16(), gfx::Range::InvalidRange(), false);
2186     } else {
2187       widgetHost->ImeCancelComposition();
2188     }
2189   }
2191   // If the key event was handled by the input method but it also generated some
2192   // edit commands, then we need to send the real key event and corresponding
2193   // edit commands here. This usually occurs when the input method wants to
2194   // finish current composition session but still wants the application to
2195   // handle the key event. See http://crbug.com/48161 for reference.
2196   if (delayEventUntilAfterImeCompostion) {
2197     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2198     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2199     // So before sending the real key down event, we need to send a fake key up
2200     // event to balance it.
2201     NativeWebKeyboardEvent fakeEvent = event;
2202     fakeEvent.type = blink::WebInputEvent::KeyUp;
2203     fakeEvent.skip_in_browser = true;
2204     widgetHost->ForwardKeyboardEvent(fakeEvent);
2205     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2206     // a key event with |skip_in_browser| == true won't be handled by browser,
2207     // thus it won't destroy the widget.
2209     if (!editCommands_.empty()) {
2210       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2211           widgetHost->GetRoutingID(), editCommands_));
2212     }
2213     widgetHost->ForwardKeyboardEvent(event);
2215     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2216     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2217     // be set to NULL. So we check it here and return immediately if it's NULL.
2218     if (!renderWidgetHostView_->render_widget_host_)
2219       return;
2220   }
2222   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2223   // Only send a corresponding key press event if there is no marked text.
2224   if (!hasMarkedText_) {
2225     if (!textInserted && textToBeInserted_.length() == 1) {
2226       // If a single character was inserted, then we just send it as a keypress
2227       // event.
2228       event.type = blink::WebInputEvent::Char;
2229       event.text[0] = textToBeInserted_[0];
2230       event.text[1] = 0;
2231       event.skip_in_browser = true;
2232       widgetHost->ForwardKeyboardEvent(event);
2233     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2234                [[theEvent characters] length] > 0 &&
2235                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2236                 (hasEditCommands_ && editCommands_.empty()))) {
2237       // We don't get insertText: calls if ctrl or cmd is down, or the key event
2238       // generates an insert command. So synthesize a keypress event for these
2239       // cases, unless the key event generated any other command.
2240       event.type = blink::WebInputEvent::Char;
2241       event.skip_in_browser = true;
2242       widgetHost->ForwardKeyboardEvent(event);
2243     }
2244   }
2246   // Possibly autohide the cursor.
2247   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2248     [NSCursor setHiddenUntilMouseMoves:YES];
2251 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2252   DCHECK(base::mac::IsOSLionOrLater());
2254   if ([event phase] != NSEventPhaseEnded &&
2255       [event phase] != NSEventPhaseCancelled) {
2256     return;
2257   }
2259   if (renderWidgetHostView_->render_widget_host_) {
2260     // History-swiping is not possible if the logic reaches this point.
2261     // Allow rubber-banding in both directions.
2262     bool canRubberbandLeft = true;
2263     bool canRubberbandRight = true;
2264     WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2265         event, self, canRubberbandLeft, canRubberbandRight);
2266     webEvent.railsMode = mouseWheelFilter_.UpdateRailsMode(webEvent);
2267     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2268   }
2270   if (endWheelMonitor_) {
2271     [NSEvent removeMonitor:endWheelMonitor_];
2272     endWheelMonitor_ = nil;
2273   }
2276 - (void)beginGestureWithEvent:(NSEvent*)event {
2277   [responderDelegate_ beginGestureWithEvent:event];
2278   gestureBeginEvent_.reset(
2279       new WebGestureEvent(WebInputEventFactory::gestureEvent(event, self)));
2281   // If the page is at the minimum zoom level, require a threshold be reached
2282   // before the pinch has an effect.
2283   if (renderWidgetHostView_->page_at_minimum_scale_) {
2284     pinchHasReachedZoomThreshold_ = false;
2285     pinchUnusedAmount_ = 1;
2286   }
2289 - (void)endGestureWithEvent:(NSEvent*)event {
2290   [responderDelegate_ endGestureWithEvent:event];
2291   gestureBeginEvent_.reset();
2293   if (!renderWidgetHostView_->render_widget_host_)
2294     return;
2296   if (gestureBeginPinchSent_) {
2297     WebGestureEvent endEvent(WebInputEventFactory::gestureEvent(event, self));
2298     endEvent.type = WebInputEvent::GesturePinchEnd;
2299     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(endEvent);
2300     gestureBeginPinchSent_ = NO;
2301   }
2304 - (void)touchesMovedWithEvent:(NSEvent*)event {
2305   [responderDelegate_ touchesMovedWithEvent:event];
2308 - (void)touchesBeganWithEvent:(NSEvent*)event {
2309   [responderDelegate_ touchesBeganWithEvent:event];
2312 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2313   [responderDelegate_ touchesCancelledWithEvent:event];
2316 - (void)touchesEndedWithEvent:(NSEvent*)event {
2317   [responderDelegate_ touchesEndedWithEvent:event];
2320 - (void)smartMagnifyWithEvent:(NSEvent*)event {
2321   const WebGestureEvent& smartMagnifyEvent =
2322       WebInputEventFactory::gestureEvent(event, self);
2323   if (renderWidgetHostView_ && renderWidgetHostView_->render_widget_host_) {
2324     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(
2325         smartMagnifyEvent);
2326   }
2329 // This is invoked only on 10.8 or newer when the user taps a word using
2330 // three fingers.
2331 - (void)quickLookWithEvent:(NSEvent*)event {
2332   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2333   TextInputClientMac::GetInstance()->GetStringAtPoint(
2334       renderWidgetHostView_->render_widget_host_,
2335       gfx::Point(point.x, NSHeight([self frame]) - point.y),
2336       ^(NSAttributedString* string, NSPoint baselinePoint) {
2337           if (string && [string length] > 0) {
2338             dispatch_async(dispatch_get_main_queue(), ^{
2339                 [self showDefinitionForAttributedString:string
2340                                                 atPoint:baselinePoint];
2341             });
2342           }
2343       }
2344   );
2347 // This method handles 2 different types of hardware events.
2348 // (Apple does not distinguish between them).
2349 //  a. Scrolling the middle wheel of a mouse.
2350 //  b. Swiping on the track pad.
2352 // This method is responsible for 2 types of behavior:
2353 //  a. Scrolling the content of window.
2354 //  b. Navigating forwards/backwards in history.
2356 // This is a brief description of the logic:
2357 //  1. If the content can be scrolled, scroll the content.
2358 //     (This requires a roundtrip to blink to determine whether the content
2359 //      can be scrolled.)
2360 //     Once this logic is triggered, the navigate logic cannot be triggered
2361 //     until the gesture finishes.
2362 //  2. If the user is making a horizontal swipe, start the navigate
2363 //     forward/backwards UI.
2364 //     Once this logic is triggered, the user can either cancel or complete
2365 //     the gesture. If the user completes the gesture, all remaining touches
2366 //     are swallowed, and not allowed to scroll the content. If the user
2367 //     cancels the gesture, all remaining touches are forwarded to the content
2368 //     scroll logic. The user cannot trigger the navigation logic again.
2369 - (void)scrollWheel:(NSEvent*)event {
2370   if (responderDelegate_ &&
2371       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2372     BOOL handled = [responderDelegate_ handleEvent:event];
2373     if (handled)
2374       return;
2375   }
2377   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2378   // the event is received even when the mouse cursor is no longer over the view
2379   // when the scrolling ends (e.g. if the tab was switched). This is necessary
2380   // for ending rubber-banding in such cases.
2381   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2382       !endWheelMonitor_) {
2383     endWheelMonitor_ =
2384       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2385       handler:^(NSEvent* blockEvent) {
2386           [self shortCircuitScrollWheelEvent:blockEvent];
2387           return blockEvent;
2388       }];
2389   }
2391   // This is responsible for content scrolling!
2392   if (renderWidgetHostView_->render_widget_host_) {
2393     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2394     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2395     WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2396         event, self, canRubberbandLeft, canRubberbandRight);
2397     webEvent.railsMode = mouseWheelFilter_.UpdateRailsMode(webEvent);
2398     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2399   }
2402 // Called repeatedly during a pinch gesture, with incremental change values.
2403 - (void)magnifyWithEvent:(NSEvent*)event {
2404   if (!renderWidgetHostView_->render_widget_host_)
2405     return;
2407   // If, due to nesting of multiple gestures (e.g, from multiple touch
2408   // devices), the beginning of the gesture has been lost, skip the remainder
2409   // of the gesture.
2410   if (!gestureBeginEvent_)
2411     return;
2413   if (!pinchHasReachedZoomThreshold_) {
2414       pinchUnusedAmount_ *= (1 + [event magnification]);
2415       if (pinchUnusedAmount_ < 0.667 || pinchUnusedAmount_ > 1.5)
2416           pinchHasReachedZoomThreshold_ = true;
2417   }
2419   // Send a GesturePinchBegin event if none has been sent yet.
2420   if (!gestureBeginPinchSent_) {
2421     WebGestureEvent beginEvent(*gestureBeginEvent_);
2422     beginEvent.type = WebInputEvent::GesturePinchBegin;
2423     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(beginEvent);
2424     gestureBeginPinchSent_ = YES;
2425   }
2427   // Send a GesturePinchUpdate event.
2428   WebGestureEvent updateEvent =
2429       WebInputEventFactory::gestureEvent(event, self);
2430   updateEvent.data.pinchUpdate.zoomDisabled = !pinchHasReachedZoomThreshold_;
2431   renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(updateEvent);
2434 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2435   NSWindow* oldWindow = [self window];
2437   NSNotificationCenter* notificationCenter =
2438       [NSNotificationCenter defaultCenter];
2440   // Backing property notifications crash on 10.6 when building with the 10.7
2441   // SDK, see http://crbug.com/260595.
2442   static BOOL supportsBackingPropertiesNotification =
2443       SupportsBackingPropertiesChangedNotification();
2445   if (oldWindow) {
2446     if (supportsBackingPropertiesNotification) {
2447       [notificationCenter
2448           removeObserver:self
2449                     name:NSWindowDidChangeBackingPropertiesNotification
2450                   object:oldWindow];
2451     }
2452     [notificationCenter
2453         removeObserver:self
2454                   name:NSWindowDidMoveNotification
2455                 object:oldWindow];
2456     [notificationCenter
2457         removeObserver:self
2458                   name:NSWindowDidEndLiveResizeNotification
2459                 object:oldWindow];
2460   }
2461   if (newWindow) {
2462     if (supportsBackingPropertiesNotification) {
2463       [notificationCenter
2464           addObserver:self
2465              selector:@selector(windowDidChangeBackingProperties:)
2466                  name:NSWindowDidChangeBackingPropertiesNotification
2467                object:newWindow];
2468     }
2469     [notificationCenter
2470         addObserver:self
2471            selector:@selector(windowChangedGlobalFrame:)
2472                name:NSWindowDidMoveNotification
2473              object:newWindow];
2474     [notificationCenter
2475         addObserver:self
2476            selector:@selector(windowChangedGlobalFrame:)
2477                name:NSWindowDidEndLiveResizeNotification
2478              object:newWindow];
2479   }
2482 - (void)updateScreenProperties{
2483   renderWidgetHostView_->UpdateBackingStoreScaleFactor();
2484   renderWidgetHostView_->UpdateDisplayLink();
2487 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2488 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2489   // Background tabs check if their scale factor or vsync properties changed
2490   // when they are added to a window.
2492   // Allocating a CGLayerRef with the current scale factor immediately from
2493   // this handler doesn't work. Schedule the backing store update on the
2494   // next runloop cycle, then things are read for CGLayerRef allocations to
2495   // work.
2496   [self performSelector:@selector(updateScreenProperties)
2497              withObject:nil
2498              afterDelay:0];
2501 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2502   renderWidgetHostView_->UpdateScreenInfo(
2503       renderWidgetHostView_->GetNativeView());
2506 - (void)setFrameSize:(NSSize)newSize {
2507   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2509   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2510   // -setFrame: isn't neccessary.
2511   [super setFrameSize:newSize];
2513   if (!renderWidgetHostView_->render_widget_host_)
2514     return;
2516   renderWidgetHostView_->render_widget_host_->SendScreenRects();
2517   renderWidgetHostView_->render_widget_host_->WasResized();
2518   if (renderWidgetHostView_->delegated_frame_host_)
2519     renderWidgetHostView_->delegated_frame_host_->WasResized();
2521   // Wait for the frame that WasResize might have requested. If the view is
2522   // being made visible at a new size, then this call will have no effect
2523   // because the view widget is still hidden, and the pause call in WasShown
2524   // will have this effect for us.
2525   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2528 - (BOOL)canBecomeKeyView {
2529   if (!renderWidgetHostView_->render_widget_host_)
2530     return NO;
2532   return canBeKeyView_;
2535 - (BOOL)acceptsFirstResponder {
2536   if (!renderWidgetHostView_->render_widget_host_)
2537     return NO;
2539   return canBeKeyView_;
2542 - (BOOL)becomeFirstResponder {
2543   if (!renderWidgetHostView_->render_widget_host_)
2544     return NO;
2546   renderWidgetHostView_->render_widget_host_->Focus();
2547   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
2548   renderWidgetHostView_->SetTextInputActive(true);
2550   // Cancel any onging composition text which was left before we lost focus.
2551   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2552   // somehow that method won't be called when switching among different tabs.
2553   // See http://crbug.com/47209
2554   [self cancelComposition];
2556   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2557       [[self window] keyViewSelectionDirection]];
2558   NSDictionary* userInfo =
2559       [NSDictionary dictionaryWithObject:direction
2560                                   forKey:kSelectionDirection];
2561   [[NSNotificationCenter defaultCenter]
2562       postNotificationName:kViewDidBecomeFirstResponder
2563                     object:self
2564                   userInfo:userInfo];
2566   return YES;
2569 - (BOOL)resignFirstResponder {
2570   renderWidgetHostView_->SetTextInputActive(false);
2571   if (!renderWidgetHostView_->render_widget_host_)
2572     return YES;
2574   if (closeOnDeactivate_)
2575     renderWidgetHostView_->KillSelf();
2577   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
2578   renderWidgetHostView_->render_widget_host_->Blur();
2580   // We should cancel any onging composition whenever RWH's Blur() method gets
2581   // called, because in this case, webkit will confirm the ongoing composition
2582   // internally.
2583   [self cancelComposition];
2585   return YES;
2588 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2589   if (responderDelegate_ &&
2590       [responderDelegate_
2591           respondsToSelector:@selector(validateUserInterfaceItem:
2592                                                      isValidItem:)]) {
2593     BOOL valid;
2594     BOOL known =
2595         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2596     if (known)
2597       return valid;
2598   }
2600   SEL action = [item action];
2602   if (action == @selector(stopSpeaking:)) {
2603     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2604            renderWidgetHostView_->IsSpeaking();
2605   }
2606   if (action == @selector(startSpeaking:)) {
2607     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2608            renderWidgetHostView_->SupportsSpeech();
2609   }
2611   // For now, these actions are always enabled for render view,
2612   // this is sub-optimal.
2613   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2614   if (action == @selector(undo:) ||
2615       action == @selector(redo:) ||
2616       action == @selector(cut:) ||
2617       action == @selector(copy:) ||
2618       action == @selector(copyToFindPboard:) ||
2619       action == @selector(paste:) ||
2620       action == @selector(pasteAndMatchStyle:)) {
2621     return renderWidgetHostView_->render_widget_host_->IsRenderView();
2622   }
2624   return editCommand_helper_->IsMenuItemEnabled(action, self);
2627 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2628   return renderWidgetHostView_.get();
2631 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2632 // move) for the given event. Customize here to be more selective about which
2633 // key presses to autohide on.
2634 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2635   return ([event type] == NSKeyDown &&
2636              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2639 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2640                                          index:(NSUInteger)index
2641                                       maxCount:(NSUInteger)maxCount {
2642   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2643   NSUInteger totalLength = [fullArray count];
2644   if (index >= totalLength)
2645     return nil;
2646   NSUInteger length = MIN(totalLength - index, maxCount);
2647   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2650 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2651   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2652   return [fullArray count];
2655 - (id)accessibilityAttributeValue:(NSString *)attribute {
2656   BrowserAccessibilityManager* manager =
2657       renderWidgetHostView_->render_widget_host_
2658           ->GetRootBrowserAccessibilityManager();
2660   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2661   // BrowserAccessibilityManager. Children includes all subviews in addition to
2662   // contents. Currently we do not have subviews besides the document view.
2663   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2664           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2665       manager) {
2666     return [NSArray arrayWithObjects:manager->
2667         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2668   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2669     return NSAccessibilityScrollAreaRole;
2670   }
2671   id ret = [super accessibilityAttributeValue:attribute];
2672   return ret;
2675 - (NSArray*)accessibilityAttributeNames {
2676   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2677   [ret addObject:NSAccessibilityContentsAttribute];
2678   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2679   return ret;
2682 - (id)accessibilityHitTest:(NSPoint)point {
2683   BrowserAccessibilityManager* manager =
2684       renderWidgetHostView_->render_widget_host_
2685           ->GetRootBrowserAccessibilityManager();
2686   if (!manager)
2687     return self;
2688   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2689   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2690   localPoint.y = NSHeight([self bounds]) - localPoint.y;
2691   BrowserAccessibilityCocoa* root =
2692       manager->GetRoot()->ToBrowserAccessibilityCocoa();
2693   id obj = [root accessibilityHitTest:localPoint];
2694   return obj;
2697 - (BOOL)accessibilityIsIgnored {
2698   BrowserAccessibilityManager* manager =
2699       renderWidgetHostView_->render_widget_host_
2700           ->GetRootBrowserAccessibilityManager();
2701   return !manager;
2704 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2705   BrowserAccessibilityManager* manager =
2706       renderWidgetHostView_->render_widget_host_
2707           ->GetRootBrowserAccessibilityManager();
2708   // Only child is root.
2709   if (manager &&
2710       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2711     return 0;
2712   } else {
2713     return NSNotFound;
2714   }
2717 - (id)accessibilityFocusedUIElement {
2718   BrowserAccessibilityManager* manager =
2719       renderWidgetHostView_->render_widget_host_
2720           ->GetRootBrowserAccessibilityManager();
2721   if (manager) {
2722     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2723     DCHECK(focused_item);
2724     if (focused_item) {
2725       BrowserAccessibilityCocoa* focused_item_cocoa =
2726           focused_item->ToBrowserAccessibilityCocoa();
2727       DCHECK(focused_item_cocoa);
2728       if (focused_item_cocoa)
2729         return focused_item_cocoa;
2730     }
2731   }
2732   return [super accessibilityFocusedUIElement];
2735 // Below is our NSTextInputClient implementation.
2737 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2738 // functions to process this event.
2740 // [WebHTMLView keyDown] ->
2741 //     EventHandler::keyEvent() ->
2742 //     ...
2743 //     [WebEditorClient handleKeyboardEvent] ->
2744 //     [WebHTMLView _interceptEditingKeyEvent] ->
2745 //     [NSResponder interpretKeyEvents] ->
2746 //     [WebHTMLView insertText] ->
2747 //     Editor::insertText()
2749 // Unfortunately, it is hard for Chromium to use this implementation because
2750 // it causes key-typing jank.
2751 // RenderWidgetHostViewMac is running in a browser process. On the other
2752 // hand, Editor and EventHandler are running in a renderer process.
2753 // So, if we used this implementation, a NSKeyDown event is dispatched to
2754 // the following functions of Chromium.
2756 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2757 //     |Sync IPC (KeyDown)| (*1) ->
2758 //     EventHandler::keyEvent() (renderer) ->
2759 //     ...
2760 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
2761 //     |Sync IPC| (*2) ->
2762 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2763 //     [self interpretKeyEvents] ->
2764 //     [RenderWidgetHostViewMac insertText] (browser) ->
2765 //     |Async IPC| ->
2766 //     Editor::insertText() (renderer)
2768 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2769 // result of EventHandler::keyEvent().
2770 // (*2) we need to wait until this call finishes since WebEditorClient uses
2771 // the result of [WebHTMLView _interceptEditingKeyEvent].
2773 // This needs many sync IPC messages sent between a browser and a renderer for
2774 // each key event, which would probably result in key-typing jank.
2775 // To avoid this problem, this implementation processes key events (and input
2776 // method events) totally in a browser process and sends asynchronous input
2777 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2778 // renderer process.
2780 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2781 //     |Async IPC (RawKeyDown)| ->
2782 //     [self interpretKeyEvents] ->
2783 //     [RenderWidgetHostViewMac insertText] (browser) ->
2784 //     |Async IPC (Char)| ->
2785 //     Editor::insertText() (renderer)
2787 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2788 // make any key-typing jank. --hbono 7/23/09
2790 extern "C" {
2791 extern NSString *NSTextInputReplacementRangeAttributeName;
2794 - (NSArray *)validAttributesForMarkedText {
2795   // This code is just copied from WebKit except renaming variables.
2796   if (!validAttributesForMarkedText_) {
2797     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2798         NSUnderlineStyleAttributeName,
2799         NSUnderlineColorAttributeName,
2800         NSMarkedClauseSegmentAttributeName,
2801         NSTextInputReplacementRangeAttributeName,
2802         nil]);
2803   }
2804   return validAttributesForMarkedText_.get();
2807 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2808   DCHECK([self window]);
2809   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2810   // coordinates (upper left origin). Scroll offsets will be taken care of in
2811   // the renderer.
2812   thePoint = [[self window] convertScreenToBase:thePoint];
2813   thePoint = [self convertPoint:thePoint fromView:nil];
2814   thePoint.y = NSHeight([self frame]) - thePoint.y;
2816   NSUInteger index =
2817       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2818           renderWidgetHostView_->render_widget_host_,
2819           gfx::Point(thePoint.x, thePoint.y));
2820   return index;
2823 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2824                              actualRange:(NSRangePointer)actualRange {
2825   NSRect rect;
2826   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2827           theRange,
2828           &rect,
2829           actualRange)) {
2830     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2831         renderWidgetHostView_->render_widget_host_, theRange);
2833     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2834     if (actualRange)
2835       *actualRange = theRange;
2836   }
2838   // The returned rectangle is in WebKit coordinates (upper left origin), so
2839   // flip the coordinate system.
2840   NSRect viewFrame = [self frame];
2841   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2842   return rect;
2845 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2846                          actualRange:(NSRangePointer)actualRange {
2847   NSRect rect = [self firstViewRectForCharacterRange:theRange
2848                                          actualRange:actualRange];
2850   // Convert into screen coordinates for return.
2851   rect = [self convertRect:rect toView:nil];
2852   rect.origin = [[self window] convertBaseToScreen:rect.origin];
2853   return rect;
2856 - (NSRange)markedRange {
2857   // An input method calls this method to check if an application really has
2858   // a text being composed when hasMarkedText call returns true.
2859   // Returns the range saved in the setMarkedText method so the input method
2860   // calls the setMarkedText method and we can update the composition node
2861   // there. (When this method returns an empty range, the input method doesn't
2862   // call the setMarkedText method.)
2863   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2866 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2867     actualRange:(NSRangePointer)actualRange {
2868   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2869   if (actualRange)
2870     *actualRange = range;
2871   NSAttributedString* str =
2872       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2873           renderWidgetHostView_->render_widget_host_, range);
2874   return str;
2877 - (NSInteger)conversationIdentifier {
2878   return reinterpret_cast<NSInteger>(self);
2881 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2882 // nil when the caret is in non-editable content or password box to avoid
2883 // making input methods do their work.
2884 - (NSTextInputContext *)inputContext {
2885   if (focusedPluginIdentifier_ != -1)
2886     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2888   switch(renderWidgetHostView_->text_input_type_) {
2889     case ui::TEXT_INPUT_TYPE_NONE:
2890     case ui::TEXT_INPUT_TYPE_PASSWORD:
2891       return nil;
2892     default:
2893       return [super inputContext];
2894   }
2897 - (BOOL)hasMarkedText {
2898   // An input method calls this function to figure out whether or not an
2899   // application is really composing a text. If it is composing, it calls
2900   // the markedRange method, and maybe calls the setMarkedText method.
2901   // It seems an input method usually calls this function when it is about to
2902   // cancel an ongoing composition. If an application has a non-empty marked
2903   // range, it calls the setMarkedText method to delete the range.
2904   return hasMarkedText_;
2907 - (void)unmarkText {
2908   // Delete the composition node of the renderer and finish an ongoing
2909   // composition.
2910   // It seems an input method calls the setMarkedText method and set an empty
2911   // text when it cancels an ongoing composition, i.e. I have never seen an
2912   // input method calls this method.
2913   hasMarkedText_ = NO;
2914   markedText_.clear();
2915   underlines_.clear();
2917   // If we are handling a key down event, then ConfirmComposition() will be
2918   // called in keyEvent: method.
2919   if (!handlingKeyDown_) {
2920     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2921         base::string16(), gfx::Range::InvalidRange(), false);
2922   } else {
2923     unmarkTextCalled_ = YES;
2924   }
2927 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
2928                               replacementRange:(NSRange)replacementRange {
2929   // An input method updates the composition string.
2930   // We send the given text and range to the renderer so it can update the
2931   // composition node of WebKit.
2932   // TODO(suzhe): It's hard for us to support replacementRange without accessing
2933   // the full web content.
2934   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
2935   NSString* im_text = isAttributedString ? [string string] : string;
2936   int length = [im_text length];
2938   // |markedRange_| will get set on a callback from ImeSetComposition().
2939   selectedRange_ = newSelRange;
2940   markedText_ = base::SysNSStringToUTF16(im_text);
2941   hasMarkedText_ = (length > 0);
2943   underlines_.clear();
2944   if (isAttributedString) {
2945     ExtractUnderlines(string, &underlines_);
2946   } else {
2947     // Use a thin black underline by default.
2948     underlines_.push_back(blink::WebCompositionUnderline(
2949         0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
2950   }
2952   // If we are handling a key down event, then SetComposition() will be
2953   // called in keyEvent: method.
2954   // Input methods of Mac use setMarkedText calls with an empty text to cancel
2955   // an ongoing composition. So, we should check whether or not the given text
2956   // is empty to update the input method state. (Our input method backend can
2957   // automatically cancels an ongoing composition when we send an empty text.
2958   // So, it is OK to send an empty text to the renderer.)
2959   if (!handlingKeyDown_) {
2960     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
2961         markedText_, underlines_,
2962         newSelRange.location, NSMaxRange(newSelRange));
2963   }
2966 - (void)doCommandBySelector:(SEL)selector {
2967   // An input method calls this function to dispatch an editing command to be
2968   // handled by this view.
2969   if (selector == @selector(noop:))
2970     return;
2972   std::string command(
2973       [RenderWidgetHostViewMacEditCommandHelper::
2974           CommandNameForSelector(selector) UTF8String]);
2976   // If this method is called when handling a key down event, then we need to
2977   // handle the command in the key event handler. Otherwise we can just handle
2978   // it here.
2979   if (handlingKeyDown_) {
2980     hasEditCommands_ = YES;
2981     // We ignore commands that insert characters, because this was causing
2982     // strange behavior (e.g. tab always inserted a tab rather than moving to
2983     // the next field on the page).
2984     if (!StartsWithASCII(command, "insert", false))
2985       editCommands_.push_back(EditCommand(command, ""));
2986   } else {
2987     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
2988     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
2989                                               command, ""));
2990   }
2993 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
2994   // An input method has characters to be inserted.
2995   // Same as Linux, Mac calls this method not only:
2996   // * when an input method finishs composing text, but also;
2997   // * when we type an ASCII character (without using input methods).
2998   // When we aren't using input methods, we should send the given character as
2999   // a Char event so it is dispatched to an onkeypress() event handler of
3000   // JavaScript.
3001   // On the other hand, when we are using input methods, we should send the
3002   // given characters as an input method event and prevent the characters from
3003   // being dispatched to onkeypress() event handlers.
3004   // Text inserting might be initiated by other source instead of keyboard
3005   // events, such as the Characters dialog. In this case the text should be
3006   // sent as an input method event as well.
3007   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3008   // the full web content.
3009   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3010   NSString* im_text = isAttributedString ? [string string] : string;
3011   if (handlingKeyDown_) {
3012     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3013   } else {
3014     gfx::Range replacement_range(replacementRange);
3015     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3016         base::SysNSStringToUTF16(im_text), replacement_range, false);
3017   }
3019   // Inserting text will delete all marked text automatically.
3020   hasMarkedText_ = NO;
3023 - (void)insertText:(id)string {
3024   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3027 - (void)viewDidMoveToWindow {
3028   if ([self window]) {
3029     [self updateScreenProperties];
3030   } else {
3031     // If the RenderWidgetHostViewCocoa is being removed from its window, tear
3032     // down its browser compositor resources, if needed.
3033     renderWidgetHostView_->DestroySuspendedBrowserCompositorViewIfNeeded();
3034   }
3036   if (canBeKeyView_) {
3037     NSWindow* newWindow = [self window];
3038     // Pointer comparison only, since we don't know if lastWindow_ is still
3039     // valid.
3040     if (newWindow) {
3041       // If we move into a new window, refresh the frame information. We
3042       // don't need to do it if it was the same window as it used to be in,
3043       // since that case is covered by WasShown(). We only want to do this for
3044       // real browser views, not popups.
3045       if (newWindow != lastWindow_) {
3046         lastWindow_ = newWindow;
3047         renderWidgetHostView_->WindowFrameChanged();
3048       }
3049     }
3050   }
3052   // If we switch windows (or are removed from the view hierarchy), cancel any
3053   // open mouse-downs.
3054   if (hasOpenMouseDown_) {
3055     WebMouseEvent event;
3056     event.type = WebInputEvent::MouseUp;
3057     event.button = WebMouseEvent::ButtonLeft;
3058     renderWidgetHostView_->ForwardMouseEvent(event);
3060     hasOpenMouseDown_ = NO;
3061   }
3064 - (void)undo:(id)sender {
3065   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3066   if (web_contents)
3067     web_contents->Undo();
3070 - (void)redo:(id)sender {
3071   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3072   if (web_contents)
3073     web_contents->Redo();
3076 - (void)cut:(id)sender {
3077   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3078   if (web_contents)
3079     web_contents->Cut();
3082 - (void)copy:(id)sender {
3083   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3084   if (web_contents)
3085     web_contents->Copy();
3088 - (void)copyToFindPboard:(id)sender {
3089   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3090   if (web_contents)
3091     web_contents->CopyToFindPboard();
3094 - (void)paste:(id)sender {
3095   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3096   if (web_contents)
3097     web_contents->Paste();
3100 - (void)pasteAndMatchStyle:(id)sender {
3101   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3102   if (web_contents)
3103     web_contents->PasteAndMatchStyle();
3106 - (void)selectAll:(id)sender {
3107   // editCommand_helper_ adds implementations for most NSResponder methods
3108   // dynamically. But the renderer side only sends selection results back to
3109   // the browser if they were triggered by a keyboard event or went through
3110   // one of the Select methods on RWH. Since selectAll: is called from the
3111   // menu handler, neither is true.
3112   // Explicitly call SelectAll() here to make sure the renderer returns
3113   // selection results.
3114   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3115   if (web_contents)
3116     web_contents->SelectAll();
3119 - (void)startSpeaking:(id)sender {
3120   GetRenderWidgetHostViewToUse(renderWidgetHostView_.get())->SpeakSelection();
3123 - (void)stopSpeaking:(id)sender {
3124   GetRenderWidgetHostViewToUse(renderWidgetHostView_.get())->StopSpeaking();
3127 - (void)cancelComposition {
3128   if (!hasMarkedText_)
3129     return;
3131   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3132   // doesn't call any NSTextInput functions, such as setMarkedText or
3133   // insertText. So, we need to send an IPC message to a renderer so it can
3134   // delete the composition node.
3135   // TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
3136   // NSTextInputContext. http://www.crbug.com/479010.
3137 #pragma clang diagnostic push
3138 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3139   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3140   [currentInputManager markedTextAbandoned:self];
3141 #pragma clang diagnostic pop
3143   hasMarkedText_ = NO;
3144   // Should not call [self unmarkText] here, because it'll send unnecessary
3145   // cancel composition IPC message to the renderer.
3148 - (void)confirmComposition {
3149   if (!hasMarkedText_)
3150     return;
3152   if (renderWidgetHostView_->render_widget_host_)
3153     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3154         base::string16(), gfx::Range::InvalidRange(), false);
3156   [self cancelComposition];
3159 - (void)setPluginImeActive:(BOOL)active {
3160   if (active == pluginImeActive_)
3161     return;
3163   pluginImeActive_ = active;
3164   if (!active) {
3165     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3166     renderWidgetHostView_->PluginImeCompositionCompleted(
3167         base::string16(), focusedPluginIdentifier_);
3168   }
3171 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3172   if (focused)
3173     focusedPluginIdentifier_ = pluginId;
3174   else if (focusedPluginIdentifier_ == pluginId)
3175     focusedPluginIdentifier_ = -1;
3177   // Whenever plugin focus changes, plugin IME resets.
3178   [self setPluginImeActive:NO];
3181 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3182   if (!pluginImeActive_)
3183     return false;
3185   ComplexTextInputPanel* inputPanel =
3186       [ComplexTextInputPanel sharedComplexTextInputPanel];
3187   NSString* composited_string = nil;
3188   BOOL handled = [inputPanel interpretKeyEvent:event
3189                                         string:&composited_string];
3190   if (composited_string) {
3191     renderWidgetHostView_->PluginImeCompositionCompleted(
3192         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3193     pluginImeActive_ = NO;
3194   }
3195   return handled;
3198 - (void)checkForPluginImeCancellation {
3199   if (pluginImeActive_ &&
3200       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3201     renderWidgetHostView_->PluginImeCompositionCompleted(
3202         base::string16(), focusedPluginIdentifier_);
3203     pluginImeActive_ = NO;
3204   }
3207 // Overriding a NSResponder method to support application services.
3209 - (id)validRequestorForSendType:(NSString*)sendType
3210                      returnType:(NSString*)returnType {
3211   id requestor = nil;
3212   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3213   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3214   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3215   BOOL takesText =
3216       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3218   if (sendTypeIsString && hasText && !returnType) {
3219     requestor = self;
3220   } else if (!sendType && returnTypeIsString && takesText) {
3221     requestor = self;
3222   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3223     requestor = self;
3224   } else {
3225     requestor = [super validRequestorForSendType:sendType
3226                                       returnType:returnType];
3227   }
3228   return requestor;
3231 - (void)viewWillStartLiveResize {
3232   [super viewWillStartLiveResize];
3233   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3234   if (widget)
3235     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3238 - (void)viewDidEndLiveResize {
3239   [super viewDidEndLiveResize];
3240   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3241   if (widget)
3242     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3245 - (void)updateCursor:(NSCursor*)cursor {
3246   if (currentCursor_ == cursor)
3247     return;
3249   currentCursor_.reset([cursor retain]);
3250   [[self window] invalidateCursorRectsForView:self];
3253 - (void)popupWindowWillClose:(NSNotification *)notification {
3254   renderWidgetHostView_->KillSelf();
3257 @end
3260 // Supporting application services
3262 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3264 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3265                              types:(NSArray*)types {
3266   const std::string& str = renderWidgetHostView_->selected_text();
3267   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3269   base::scoped_nsobject<NSString> text(
3270       [[NSString alloc] initWithUTF8String:str.c_str()]);
3271   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3272   [pboard declareTypes:toDeclare owner:nil];
3273   return [pboard setString:text forType:NSStringPboardType];
3276 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3277   NSString *string = [pboard stringForType:NSStringPboardType];
3278   if (!string) return NO;
3280   // If the user is currently using an IME, confirm the IME input,
3281   // and then insert the text from the service, the same as TextEdit and Safari.
3282   [self confirmComposition];
3283   [self insertText:string];
3284   return YES;
3287 - (BOOL)isOpaque {
3288   return opaque_;
3291 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3292 // regions that are not draggable. (See ControlRegionView in
3293 // native_app_window_cocoa.mm). This requires the render host view to be
3294 // draggable by default.
3295 - (BOOL)mouseDownCanMoveWindow {
3296   return YES;
3299 @end