Clean up check for dependency_info.
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac.mm
blob085e9f13fb7a6d956e147545346aaeef9eac65ef
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/input/web_input_event_builders_mac.h"
40 #include "content/browser/renderer_host/render_view_host_impl.h"
41 #include "content/browser/renderer_host/render_widget_helper.h"
42 #include "content/browser/renderer_host/render_widget_host_delegate.h"
43 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
44 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
45 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
46 #include "content/browser/renderer_host/render_widget_resize_helper_mac.h"
47 #import "content/browser/renderer_host/text_input_client_mac.h"
48 #include "content/common/accessibility_messages.h"
49 #include "content/common/edit_command.h"
50 #include "content/common/gpu/gpu_messages.h"
51 #include "content/common/input_messages.h"
52 #include "content/common/view_messages.h"
53 #include "content/common/webplugin_geometry.h"
54 #include "content/public/browser/browser_context.h"
55 #include "content/public/browser/browser_plugin_guest_manager.h"
56 #include "content/public/browser/browser_thread.h"
57 #include "content/public/browser/native_web_keyboard_event.h"
58 #include "content/public/browser/notification_service.h"
59 #include "content/public/browser/notification_types.h"
60 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
61 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
62 #include "content/public/browser/web_contents.h"
63 #include "skia/ext/platform_canvas.h"
64 #include "skia/ext/skia_utils_mac.h"
65 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
66 #include "third_party/WebKit/public/web/WebInputEvent.h"
67 #import "third_party/mozilla/ComplexTextInputPanel.h"
68 #include "ui/accelerated_widget_mac/io_surface_layer.h"
69 #include "ui/accelerated_widget_mac/surface_handle_types.h"
70 #include "ui/base/cocoa/animation_utils.h"
71 #import "ui/base/cocoa/fullscreen_window_manager.h"
72 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
73 #include "ui/base/layout.h"
74 #include "ui/compositor/compositor.h"
75 #include "ui/compositor/layer.h"
76 #include "ui/events/keycodes/keyboard_codes.h"
77 #include "ui/gfx/color_profile.h"
78 #include "ui/gfx/display.h"
79 #include "ui/gfx/geometry/dip_util.h"
80 #include "ui/gfx/geometry/point.h"
81 #include "ui/gfx/geometry/rect_conversions.h"
82 #include "ui/gfx/geometry/size_conversions.h"
83 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
84 #include "ui/gfx/screen.h"
85 #include "ui/gl/gl_switches.h"
87 using content::BrowserAccessibility;
88 using content::BrowserAccessibilityManager;
89 using content::EditCommand;
90 using content::FrameTreeNode;
91 using content::NativeWebKeyboardEvent;
92 using content::RenderFrameHost;
93 using content::RenderViewHost;
94 using content::RenderViewHostImpl;
95 using content::RenderWidgetHostImpl;
96 using content::RenderWidgetHostViewMac;
97 using content::RenderWidgetHostViewMacEditCommandHelper;
98 using content::TextInputClientMac;
99 using content::WebContents;
100 using content::WebGestureEventBuilder;
101 using content::WebMouseEventBuilder;
102 using content::WebMouseWheelEventBuilder;
103 using blink::WebInputEvent;
104 using blink::WebMouseEvent;
105 using blink::WebMouseWheelEvent;
106 using blink::WebGestureEvent;
108 namespace {
110 // Whether a keyboard event has been reserved by OSX.
111 BOOL EventIsReservedBySystem(NSEvent* event) {
112   content::SystemHotkeyHelperMac* helper =
113       content::SystemHotkeyHelperMac::GetInstance();
114   return helper->map()->IsEventReserved(event);
117 RenderWidgetHostViewMac* GetRenderWidgetHostViewToUse(
118     RenderWidgetHostViewMac* render_widget_host_view) {
119   WebContents* web_contents = render_widget_host_view->GetWebContents();
120   if (!web_contents)
121     return render_widget_host_view;
122   content::BrowserPluginGuestManager* guest_manager =
123       web_contents->GetBrowserContext()->GetGuestManager();
124   if (!guest_manager)
125     return render_widget_host_view;
126   content::WebContents* guest =
127       guest_manager->GetFullPageGuest(web_contents);
128   if (!guest)
129     return render_widget_host_view;
130   return static_cast<RenderWidgetHostViewMac*>(
131       guest->GetRenderWidgetHostView());
134 }  // namespace
136 // These are not documented, so use only after checking -respondsToSelector:.
137 @interface NSApplication (UndocumentedSpeechMethods)
138 - (void)speakString:(NSString*)string;
139 - (void)stopSpeaking:(id)sender;
140 - (BOOL)isSpeaking;
141 @end
143 // This method will return YES for OS X versions 10.7.3 and later, and NO
144 // otherwise.
145 // Used to prevent a crash when building with the 10.7 SDK and accessing the
146 // notification below. See: http://crbug.com/260595.
147 static BOOL SupportsBackingPropertiesChangedNotification() {
148   // windowDidChangeBackingProperties: method has been added to the
149   // NSWindowDelegate protocol in 10.7.3, at the same time as the
150   // NSWindowDidChangeBackingPropertiesNotification notification was added.
151   // If the protocol contains this method description, the notification should
152   // be supported as well.
153   Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
154   struct objc_method_description methodDescription =
155       protocol_getMethodDescription(
156           windowDelegateProtocol,
157           @selector(windowDidChangeBackingProperties:),
158           NO,
159           YES);
161   // If the protocol does not contain the method, the returned method
162   // description is {NULL, NULL}
163   return methodDescription.name != NULL || methodDescription.types != NULL;
166 // Private methods:
167 @interface RenderWidgetHostViewCocoa ()
168 @property(nonatomic, assign) NSRange selectedRange;
169 @property(nonatomic, assign) NSRange markedRange;
171 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
172 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
173 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
174                    consumed:(BOOL)consumed;
176 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
177 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
178 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
179 - (void)checkForPluginImeCancellation;
180 - (void)updateScreenProperties;
181 - (void)setResponderDelegate:
182         (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
183 @end
185 // A window subclass that allows the fullscreen window to become main and gain
186 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
187 // handled by the browser.
188 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
189 @end
191 @implementation PepperFlashFullscreenWindow
193 - (BOOL)canBecomeKeyWindow {
194   return YES;
197 - (BOOL)canBecomeMainWindow {
198   return YES;
201 @end
203 @interface RenderWidgetPopupWindow : NSWindow {
204    // The event tap that allows monitoring of all events, to properly close with
205    // a click outside the bounds of the window.
206   id clickEventTap_;
208 @end
210 @implementation RenderWidgetPopupWindow
212 - (id)initWithContentRect:(NSRect)contentRect
213                 styleMask:(NSUInteger)windowStyle
214                   backing:(NSBackingStoreType)bufferingType
215                     defer:(BOOL)deferCreation {
216   if (self = [super initWithContentRect:contentRect
217                               styleMask:windowStyle
218                                 backing:bufferingType
219                                   defer:deferCreation]) {
220     [self setOpaque:NO];
221     [self setBackgroundColor:[NSColor clearColor]];
222     [self startObservingClicks];
223   }
224   return self;
227 - (void)close {
228   [self stopObservingClicks];
229   [super close];
232 // Gets called when the menubar is clicked.
233 // Needed because the local event monitor doesn't see the click on the menubar.
234 - (void)beganTracking:(NSNotification*)notification {
235   [self close];
238 // Install the callback.
239 - (void)startObservingClicks {
240   clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
241       handler:^NSEvent* (NSEvent* event) {
242           if ([event window] == self)
243             return event;
244           NSEventType eventType = [event type];
245           if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
246             [self close];
247           return event;
248   }];
250   NSNotificationCenter* notificationCenter =
251       [NSNotificationCenter defaultCenter];
252   [notificationCenter addObserver:self
253          selector:@selector(beganTracking:)
254              name:NSMenuDidBeginTrackingNotification
255            object:[NSApp mainMenu]];
258 // Remove the callback.
259 - (void)stopObservingClicks {
260   if (!clickEventTap_)
261     return;
263   [NSEvent removeMonitor:clickEventTap_];
264    clickEventTap_ = nil;
266   NSNotificationCenter* notificationCenter =
267       [NSNotificationCenter defaultCenter];
268   [notificationCenter removeObserver:self
269                 name:NSMenuDidBeginTrackingNotification
270               object:[NSApp mainMenu]];
273 @end
275 namespace {
277 // Maximum number of characters we allow in a tooltip.
278 const size_t kMaxTooltipLength = 1024;
280 // TODO(suzhe): Upstream this function.
281 blink::WebColor WebColorFromNSColor(NSColor *color) {
282   CGFloat r, g, b, a;
283   [color getRed:&r green:&g blue:&b alpha:&a];
285   return
286       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
287       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
288       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
289       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
292 // Extract underline information from an attributed string. Mostly copied from
293 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
294 void ExtractUnderlines(
295     NSAttributedString* string,
296     std::vector<blink::WebCompositionUnderline>* underlines) {
297   int length = [[string string] length];
298   int i = 0;
299   while (i < length) {
300     NSRange range;
301     NSDictionary* attrs = [string attributesAtIndex:i
302                               longestEffectiveRange:&range
303                                             inRange:NSMakeRange(i, length - i)];
304     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
305       blink::WebColor color = SK_ColorBLACK;
306       if (NSColor *colorAttr =
307           [attrs objectForKey:NSUnderlineColorAttributeName]) {
308         color = WebColorFromNSColor(
309             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
310       }
311       underlines->push_back(
312           blink::WebCompositionUnderline(range.location,
313                                          NSMaxRange(range),
314                                          color,
315                                          [style intValue] > 1,
316                                          SK_ColorTRANSPARENT));
317     }
318     i = range.location + range.length;
319   }
322 // EnablePasswordInput() and DisablePasswordInput() are copied from
323 // enableSecureTextInput() and disableSecureTextInput() functions in
324 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
325 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
326 // here, because they are already called in webkit and they are system wide
327 // functions.
328 void EnablePasswordInput() {
329   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
330   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
331                          sizeof(CFArrayRef), &inputSources);
332   CFRelease(inputSources);
335 void DisablePasswordInput() {
336   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
339 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
340 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
341 // value when screen info changes.
342 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
343 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
344 // is necessary.
345 bool g_screen_info_up_to_date = false;
347 float FlipYFromRectToScreen(float y, float rect_height) {
348   TRACE_EVENT0("browser", "FlipYFromRectToScreen");
349   static CGFloat screen_zero_height = 0;
350   if (!g_screen_info_up_to_date) {
351     if ([[NSScreen screens] count] > 0) {
352       screen_zero_height =
353           [[[NSScreen screens] objectAtIndex:0] frame].size.height;
354       g_screen_info_up_to_date = true;
355     } else {
356       return y;
357     }
358   }
359   return screen_zero_height - y - rect_height;
362 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
363 // left of the primary screen (Carbon coordinates), and stuffs it into a
364 // gfx::Rect.
365 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
366   gfx::Rect new_rect(NSRectToCGRect(rect));
367   new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
368   return new_rect;
371 // Returns the window that visually contains the given view. This is different
372 // from [view window] in the case of tab dragging, where the view's owning
373 // window is a floating panel attached to the actual browser window that the tab
374 // is visually part of.
375 NSWindow* ApparentWindowForView(NSView* view) {
376   // TODO(shess): In case of !window, the view has been removed from
377   // the view hierarchy because the tab isn't main.  Could retrieve
378   // the information from the main tab for our window.
379   NSWindow* enclosing_window = [view window];
381   // See if this is a tab drag window. The width check is to distinguish that
382   // case from extension popup windows.
383   NSWindow* ancestor_window = [enclosing_window parentWindow];
384   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
385                           NSWidth([ancestor_window frame]))) {
386     enclosing_window = ancestor_window;
387   }
389   return enclosing_window;
392 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
393   gfx::Display display =
394       gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
396   NSScreen* screen = [NSScreen deepestScreen];
398   blink::WebScreenInfo results;
400   results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
401   results.depth = NSBitsPerPixelFromDepth([screen depth]);
402   results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
403   results.isMonochrome =
404       [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
405   results.rect = display.bounds();
406   results.availableRect = display.work_area();
407   results.orientationAngle = display.RotationAsDegree();
408   results.orientationType =
409       content::RenderWidgetHostViewBase::GetOrientationTypeForDesktop(display);
411   return results;
414 }  // namespace
416 namespace content {
418 ////////////////////////////////////////////////////////////////////////////////
419 // DelegatedFrameHost, public:
421 ui::Layer* RenderWidgetHostViewMac::DelegatedFrameHostGetLayer() const {
422   return root_layer_.get();
425 bool RenderWidgetHostViewMac::DelegatedFrameHostIsVisible() const {
426   return !render_widget_host_->is_hidden();
429 gfx::Size RenderWidgetHostViewMac::DelegatedFrameHostDesiredSizeInDIP() const {
430   return GetViewBounds().size();
433 bool RenderWidgetHostViewMac::DelegatedFrameCanCreateResizeLock() const {
434   // Mac uses the RenderWidgetResizeHelper instead of a resize lock.
435   return false;
438 scoped_ptr<ResizeLock>
439 RenderWidgetHostViewMac::DelegatedFrameHostCreateResizeLock(
440     bool defer_compositor_lock) {
441   NOTREACHED();
442   return scoped_ptr<ResizeLock>();
445 void RenderWidgetHostViewMac::DelegatedFrameHostResizeLockWasReleased() {
446   NOTREACHED();
449 void RenderWidgetHostViewMac::DelegatedFrameHostSendCompositorSwapAck(
450     int output_surface_id,
451     const cc::CompositorFrameAck& ack) {
452   render_widget_host_->Send(new ViewMsg_SwapCompositorFrameAck(
453       render_widget_host_->GetRoutingID(), output_surface_id, ack));
456 void RenderWidgetHostViewMac::DelegatedFrameHostSendReclaimCompositorResources(
457     int output_surface_id,
458     const cc::CompositorFrameAck& ack) {
459   render_widget_host_->Send(new ViewMsg_ReclaimCompositorResources(
460       render_widget_host_->GetRoutingID(), output_surface_id, ack));
463 void RenderWidgetHostViewMac::DelegatedFrameHostOnLostCompositorResources() {
464   render_widget_host_->ScheduleComposite();
467 void RenderWidgetHostViewMac::DelegatedFrameHostUpdateVSyncParameters(
468     const base::TimeTicks& timebase,
469     const base::TimeDelta& interval) {
470   render_widget_host_->UpdateVSyncParameters(timebase, interval);
473 ////////////////////////////////////////////////////////////////////////////////
474 // AcceleratedWidgetMacNSView, public:
476 NSView* RenderWidgetHostViewMac::AcceleratedWidgetGetNSView() const {
477   return cocoa_view_;
480 bool RenderWidgetHostViewMac::AcceleratedWidgetShouldIgnoreBackpressure()
481     const {
482   // If vsync is disabled, then always draw and ack frames immediately.
483   static bool is_vsync_disabled =
484       base::CommandLine::ForCurrentProcess()->HasSwitch(
485           switches::kDisableGpuVsync);
486   if (is_vsync_disabled)
487     return true;
489   // If the window is occluded, then this frame's display call may be severely
490   // throttled. This is a good thing, unless tab capture may be active, because
491   // the broadcast will be inappropriately throttled.
492   // http://crbug.com/350410
494   // If tab capture isn't active then only ack frames when we draw them.
495   if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber())
496     return false;
498   NSWindow* window = [cocoa_view_ window];
499   // If the view isn't even in the heirarchy then frames will never be drawn,
500   // so ack them immediately.
501   if (!window)
502     return true;
504   // Check the window occlusion API.
505   if ([window respondsToSelector:@selector(occlusionState)]) {
506     if ([window occlusionState] & NSWindowOcclusionStateVisible) {
507       // If the window is visible then it is safe to wait until frames are
508       // drawn to ack them.
509       return false;
510     } else {
511       // If the window is occluded then frames may never be drawn, so ack them
512       // immediately.
513       return true;
514     }
515   }
517   // If the window occlusion API is not present then ack frames when we draw
518   // them.
519   return false;
522 void RenderWidgetHostViewMac::AcceleratedWidgetGetVSyncParameters(
523     base::TimeTicks* timebase, base::TimeDelta* interval) const {
524   if (display_link_ &&
525       display_link_->GetVSyncParameters(timebase, interval))
526     return;
527   *timebase = base::TimeTicks();
528   *interval = base::TimeDelta();
531 void RenderWidgetHostViewMac::AcceleratedWidgetSwapCompleted(
532     const std::vector<ui::LatencyInfo>& all_latency_info) {
533   if (!render_widget_host_)
534     return;
535   base::TimeTicks swap_time = base::TimeTicks::Now();
537   for (auto latency_info : all_latency_info) {
538     latency_info.AddLatencyNumberWithTimestamp(
539         ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, 0, swap_time, 1);
540     latency_info.AddLatencyNumberWithTimestamp(
541         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0,
542         swap_time, 1);
543     render_widget_host_->FrameSwapped(latency_info);
544   }
546   if (display_link_)
547     display_link_->NotifyCurrentTime(swap_time);
550 void RenderWidgetHostViewMac::AcceleratedWidgetHitError() {
551   // Request a new frame be drawn.
552   browser_compositor_->compositor()->ScheduleFullRedraw();
555 ///////////////////////////////////////////////////////////////////////////////
556 // RenderWidgetHostViewBase, public:
558 // static
559 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
560     blink::WebScreenInfo* results) {
561   *results = GetWebScreenInfo(NULL);
564 ///////////////////////////////////////////////////////////////////////////////
565 // RenderWidgetHostViewMac, public:
567 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget,
568                                                  bool is_guest_view_hack)
569     : render_widget_host_(RenderWidgetHostImpl::From(widget)),
570       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
571       can_compose_inline_(true),
572       browser_compositor_state_(BrowserCompositorDestroyed),
573       browser_compositor_placeholder_(new BrowserCompositorMacPlaceholder),
574       page_at_minimum_scale_(true),
575       is_loading_(false),
576       allow_pause_for_resize_or_repaint_(true),
577       is_guest_view_hack_(is_guest_view_hack),
578       fullscreen_parent_host_view_(NULL),
579       weak_factory_(this) {
580   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
581   // goes away.  Since we autorelease it, our caller must put
582   // |GetNativeView()| into the view hierarchy right after calling us.
583   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
584                   initWithRenderWidgetHostViewMac:this] autorelease];
586   // Paint this view host with |background_color_| when there is no content
587   // ready to draw.
588   background_layer_.reset([[CALayer alloc] init]);
589   // Set the default color to be white. This is the wrong thing to do, but many
590   // UI components expect this view to be opaque.
591   [background_layer_ setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
592   [cocoa_view_ setLayer:background_layer_];
593   [cocoa_view_ setWantsLayer:YES];
595   if (IsDelegatedRendererEnabled()) {
596     root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
597     delegated_frame_host_.reset(new DelegatedFrameHost(this));
598   }
600   gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this);
602   if (!is_guest_view_hack_)
603     render_widget_host_->SetView(this);
605   // Let the page-level input event router know about our surface ID
606   // namespace for surface-based hit testing.
607   if (UseSurfacesEnabled() && render_widget_host_->delegate() &&
608       render_widget_host_->delegate()->GetInputEventRouter()) {
609     render_widget_host_->delegate()
610         ->GetInputEventRouter()
611         ->AddSurfaceIdNamespaceOwner(GetSurfaceIdNamespace(), this);
612   }
615 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
616   gfx::Screen::GetScreenFor(cocoa_view_)->RemoveObserver(this);
618   // This is being called from |cocoa_view_|'s destructor, so invalidate the
619   // pointer.
620   cocoa_view_ = nil;
622   UnlockMouse();
624   if (UseSurfacesEnabled() && render_widget_host_ &&
625       render_widget_host_->delegate() &&
626       render_widget_host_->delegate()->GetInputEventRouter()) {
627     render_widget_host_->delegate()
628         ->GetInputEventRouter()
629         ->RemoveSurfaceIdNamespaceOwner(GetSurfaceIdNamespace());
630   }
632   // Ensure that the browser compositor is destroyed in a safe order.
633   ShutdownBrowserCompositor();
635   // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
636   // RenderWidgetHost does we need to tell it not to hold a stale pointer to
637   // us.
638   if (render_widget_host_) {
639     // If this is a RenderWidgetHostViewGuest's platform_view_, we're not the
640     // RWH's view, the RenderWidgetHostViewGuest is. So don't reset the RWH's
641     // view, the RenderWidgetHostViewGuest will do it.
642     if (!is_guest_view_hack_)
643       render_widget_host_->SetView(NULL);
644   }
647 void RenderWidgetHostViewMac::SetDelegate(
648     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
649   [cocoa_view_ setResponderDelegate:delegate];
652 void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
653   allow_pause_for_resize_or_repaint_ = allow;
656 ///////////////////////////////////////////////////////////////////////////////
657 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
659 void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
660   TRACE_EVENT0("browser",
661                "RenderWidgetHostViewMac::EnsureBrowserCompositorView");
663   // Create the view, to transition from Destroyed -> Suspended.
664   if (browser_compositor_state_ == BrowserCompositorDestroyed) {
665     browser_compositor_ = BrowserCompositorMac::Create();
666     browser_compositor_->compositor()->SetRootLayer(root_layer_.get());
667     browser_compositor_->compositor()->SetHostHasTransparentBackground(
668         !GetBackgroundOpaque());
669     browser_compositor_->accelerated_widget_mac()->SetNSView(this);
670     browser_compositor_state_ = BrowserCompositorSuspended;
671   }
673   // Show the DelegatedFrameHost to transition from Suspended -> Active.
674   if (browser_compositor_state_ == BrowserCompositorSuspended) {
675     delegated_frame_host_->SetCompositor(browser_compositor_->compositor());
676     delegated_frame_host_->WasShown(ui::LatencyInfo());
677     // Unsuspend the browser compositor after showing the delegated frame host.
678     // If there is not a saved delegated frame, then the delegated frame host
679     // will keep the compositor locked until a delegated frame is swapped.
680     float scale_factor = ViewScaleFactor();
681     browser_compositor_->compositor()->SetScaleAndSize(
682         scale_factor,
683         gfx::ConvertSizeToPixel(scale_factor, GetViewBounds().size()));
684     browser_compositor_->Unsuspend();
685     browser_compositor_state_ = BrowserCompositorActive;
686   }
689 void RenderWidgetHostViewMac::SuspendBrowserCompositorView() {
690   TRACE_EVENT0("browser",
691                "RenderWidgetHostViewMac::SuspendBrowserCompositorView");
693   // Hide the DelegatedFrameHost to transition from Active -> Suspended.
694   if (browser_compositor_state_ == BrowserCompositorActive) {
695     // Ensure that any changes made to the ui::Compositor do not result in new
696     // frames being produced.
697     browser_compositor_->Suspend();
698     // Marking the DelegatedFrameHost as removed from the window hierarchy is
699     // necessary to remove all connections to its old ui::Compositor.
700     delegated_frame_host_->WasHidden();
701     delegated_frame_host_->ResetCompositor();
702     browser_compositor_state_ = BrowserCompositorSuspended;
703   }
706 void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
707   TRACE_EVENT0("browser",
708                "RenderWidgetHostViewMac::DestroyBrowserCompositorView");
710   // Transition from Active -> Suspended if need be.
711   SuspendBrowserCompositorView();
713   // Destroy the BrowserCompositorView to transition Suspended -> Destroyed.
714   if (browser_compositor_state_ == BrowserCompositorSuspended) {
715     browser_compositor_->accelerated_widget_mac()->ResetNSView();
716     browser_compositor_->compositor()->SetScaleAndSize(1.0, gfx::Size(0, 0));
717     browser_compositor_->compositor()->SetRootLayer(nullptr);
718     BrowserCompositorMac::Recycle(browser_compositor_.Pass());
719     browser_compositor_state_ = BrowserCompositorDestroyed;
720   }
723 void RenderWidgetHostViewMac::DestroySuspendedBrowserCompositorViewIfNeeded() {
724   if (browser_compositor_state_ != BrowserCompositorSuspended)
725     return;
727   // If this view is in a window that is visible, keep around the suspended
728   // BrowserCompositorView in case |cocoa_view_| is suddenly revealed (so that
729   // we don't flash white).
730   NSWindow* window = [cocoa_view_ window];
731   if (window)
732     return;
734   // This should only be reached if |render_widget_host_| is hidden, destroyed,
735   // or in the process of being destroyed.
736   DestroyBrowserCompositorView();
739 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
740   bool handled = true;
741   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
742     IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
743     IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
744     IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted,
745         OnGetRenderedTextCompleted)
746     IPC_MESSAGE_UNHANDLED(handled = false)
747   IPC_END_MESSAGE_MAP()
748   return handled;
751 void RenderWidgetHostViewMac::InitAsChild(
752     gfx::NativeView parent_view) {
755 void RenderWidgetHostViewMac::InitAsPopup(
756     RenderWidgetHostView* parent_host_view,
757     const gfx::Rect& pos) {
758   bool activatable = popup_type_ == blink::WebPopupTypeNone;
759   [cocoa_view_ setCloseOnDeactivate:YES];
760   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
762   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
763   origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
765   popup_window_.reset([[RenderWidgetPopupWindow alloc]
766       initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
767                                      pos.width(), pos.height())
768                 styleMask:NSBorderlessWindowMask
769                   backing:NSBackingStoreBuffered
770                     defer:NO]);
771   [popup_window_ setLevel:NSPopUpMenuWindowLevel];
772   [popup_window_ setReleasedWhenClosed:NO];
773   [popup_window_ makeKeyAndOrderFront:nil];
774   [[popup_window_ contentView] addSubview:cocoa_view_];
775   [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
776   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
777   [[NSNotificationCenter defaultCenter]
778       addObserver:cocoa_view_
779          selector:@selector(popupWindowWillClose:)
780              name:NSWindowWillCloseNotification
781            object:popup_window_];
784 // This function creates the fullscreen window and hides the dock and menubar if
785 // necessary. Note, this codepath is only used for pepper flash when
786 // pp::FlashFullScreen::SetFullscreen() is called. If
787 // pp::FullScreen::SetFullscreen() is called then the entire browser window
788 // will enter fullscreen instead.
789 void RenderWidgetHostViewMac::InitAsFullscreen(
790     RenderWidgetHostView* reference_host_view) {
791   fullscreen_parent_host_view_ =
792       static_cast<RenderWidgetHostViewMac*>(reference_host_view);
793   NSWindow* parent_window = nil;
794   if (reference_host_view)
795     parent_window = [reference_host_view->GetNativeView() window];
796   NSScreen* screen = [parent_window screen];
797   if (!screen)
798     screen = [NSScreen mainScreen];
800   pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
801       initWithContentRect:[screen frame]
802                 styleMask:NSBorderlessWindowMask
803                   backing:NSBackingStoreBuffered
804                     defer:NO]);
805   [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
806   [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
807   [cocoa_view_ setCanBeKeyView:YES];
808   [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
809   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
810   // If the pepper fullscreen window isn't opaque then there are performance
811   // issues when it's on the discrete GPU and the Chrome window is being drawn
812   // to. http://crbug.com/171911
813   [pepper_fullscreen_window_ setOpaque:YES];
815   // Note that this forms a reference cycle between the fullscreen window and
816   // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
817   // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
818   // This cycle is normally broken when -keyEvent: receives an <esc> key, which
819   // explicitly calls Shutdown on the render_widget_host_, which calls
820   // Destroy() on RWHVMac, which drops the reference to
821   // pepper_fullscreen_window_.
822   [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
824   // Note that this keeps another reference to pepper_fullscreen_window_.
825   fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
826       initWithWindow:pepper_fullscreen_window_.get()
827        desiredScreen:screen]);
828   [fullscreen_window_manager_ enterFullscreenMode];
829   [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
832 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
833   // See comment in InitAsFullscreen(): There is a reference cycle between
834   // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
835   // Tests that test pepper fullscreen mode without sending an <esc> event
836   // need to call this method to break the reference cycle.
837   [fullscreen_window_manager_ exitFullscreenMode];
838   fullscreen_window_manager_.reset();
839   [pepper_fullscreen_window_ close];
840   pepper_fullscreen_window_.reset();
843 int RenderWidgetHostViewMac::window_number() const {
844   NSWindow* window = [cocoa_view_ window];
845   if (!window)
846     return -1;
847   return [window windowNumber];
850 float RenderWidgetHostViewMac::ViewScaleFactor() const {
851   return ui::GetScaleFactorForNativeView(cocoa_view_);
854 void RenderWidgetHostViewMac::UpdateDisplayLink() {
855   static bool is_vsync_disabled =
856       base::CommandLine::ForCurrentProcess()->HasSwitch(
857           switches::kDisableGpuVsync);
858   if (is_vsync_disabled)
859     return;
861   NSScreen* screen = [[cocoa_view_ window] screen];
862   NSDictionary* screen_description = [screen deviceDescription];
863   NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
864   CGDirectDisplayID display_id = [screen_number unsignedIntValue];
866   display_link_ = ui::DisplayLinkMac::GetForDisplay(display_id);
867   if (!display_link_.get()) {
868     // Note that on some headless systems, the display link will fail to be
869     // created, so this should not be a fatal error.
870     LOG(ERROR) << "Failed to create display link.";
871   }
874 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
875   if (!render_widget_host_ || !display_link_.get())
876     return;
878   if (!display_link_->GetVSyncParameters(&vsync_timebase_, &vsync_interval_)) {
879     vsync_timebase_ = base::TimeTicks();
880     vsync_interval_ = base::TimeDelta();
881     return;
882   }
884   if (browser_compositor_) {
885     browser_compositor_->compositor()->vsync_manager()->UpdateVSyncParameters(
886         vsync_timebase_, vsync_interval_);
887   }
890 void RenderWidgetHostViewMac::SpeakText(const std::string& text) {
891   [NSApp speakString:base::SysUTF8ToNSString(text)];
894 void RenderWidgetHostViewMac::UpdateBackingStoreProperties() {
895   if (!render_widget_host_)
896     return;
897   render_widget_host_->NotifyScreenInfoChanged();
900 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
901   return render_widget_host_;
904 void RenderWidgetHostViewMac::Show() {
905   ScopedCAActionDisabler disabler;
906   [cocoa_view_ setHidden:NO];
907   if (!render_widget_host_->is_hidden())
908     return;
910   // Re-create the browser compositor. If the DelegatedFrameHost has a cached
911   // frame from the last time it was visible, then it will immediately be
912   // drawn. If not, then the compositor will remain locked until a new delegated
913   // frame is swapped.
914   EnsureBrowserCompositorView();
916   WasUnOccluded();
918   // If there is not a frame being currently drawn, kick one, so that the below
919   // pause will have a frame to wait on.
920   render_widget_host_->ScheduleComposite();
921   PauseForPendingResizeOrRepaintsAndDraw();
924 void RenderWidgetHostViewMac::Hide() {
925   ScopedCAActionDisabler disabler;
926   [cocoa_view_ setHidden:YES];
927   WasOccluded();
928   DestroySuspendedBrowserCompositorViewIfNeeded();
931 void RenderWidgetHostViewMac::WasUnOccluded() {
932   if (!render_widget_host_->is_hidden())
933     return;
935   ui::LatencyInfo renderer_latency_info;
936   renderer_latency_info.AddLatencyNumber(
937       ui::TAB_SHOW_COMPONENT,
938       render_widget_host_->GetLatencyComponentId(),
939       0);
940   render_widget_host_->WasShown(renderer_latency_info);
943 void RenderWidgetHostViewMac::WasOccluded() {
944   if (render_widget_host_->is_hidden())
945     return;
947   // Note that the following call to WasHidden() can trigger thumbnail
948   // generation on behalf of the NTP, and that cannot succeed if the browser
949   // compositor view has been suspended. Therefore these two statements must
950   // occur in this specific order. However, because thumbnail generation is
951   // asychronous, that operation won't run before SuspendBrowserCompositorView()
952   // completes. As a result you won't get a thumbnail for the page unless you
953   // happen to switch back to it. See http://crbug.com/530707 .
954   render_widget_host_->WasHidden();
955   SuspendBrowserCompositorView();
958 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
959   gfx::Rect rect = GetViewBounds();
960   rect.set_size(size);
961   SetBounds(rect);
964 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
965   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
966   // TODO(thakis): fix, http://crbug.com/73362
967   if (render_widget_host_->is_hidden())
968     return;
970   // During the initial creation of the RenderWidgetHostView in
971   // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
972   // an empty size. In the Windows code flow, it is not ignored because
973   // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
974   // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
975   // flags to keep things sized properly. On the other hand, if the size is not
976   // empty then this is a valid request for a pop-up.
977   if (rect.size().IsEmpty())
978     return;
980   // Ignore the position of |rect| for non-popup rwhvs. This is because
981   // background tabs do not have a window, but the window is required for the
982   // coordinate conversions. Popups are always for a visible tab.
983   //
984   // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
985   // valid for resizing to be requested (e.g., during tab capture, to size the
986   // view to screen-capture resolution). In this case, simply treat the view as
987   // relative to the screen.
988   BOOL isRelativeToScreen = IsPopup() ||
989       ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
990   if (isRelativeToScreen) {
991     // The position of |rect| is screen coordinate system and we have to
992     // consider Cocoa coordinate system is upside-down and also multi-screen.
993     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
994     NSSize size = NSMakeSize(rect.width(), rect.height());
995     size = [cocoa_view_ convertSize:size toView:nil];
996     origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
997     NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
998                               size.width, size.height);
999     if (IsPopup())
1000       [popup_window_ setFrame:frame display:YES];
1001     else
1002       [cocoa_view_ setFrame:frame];
1003   } else {
1004     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
1005     gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
1006     rect2.set_width(rect.width());
1007     rect2.set_height(rect.height());
1008     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
1009   }
1012 gfx::Vector2dF RenderWidgetHostViewMac::GetLastScrollOffset() const {
1013   return last_scroll_offset_;
1016 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
1017   return cocoa_view_;
1020 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
1021   return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
1024 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
1025   return cocoa_view_;
1028 void RenderWidgetHostViewMac::MovePluginWindows(
1029     const std::vector<WebPluginGeometry>& moves) {
1030   // Must be overridden, but unused on this platform. Core Animation
1031   // plugins are drawn by the GPU process (through the compositor),
1032   // and Core Graphics plugins are drawn by the renderer process.
1033   DCHECK_CURRENTLY_ON(BrowserThread::UI);
1036 void RenderWidgetHostViewMac::Focus() {
1037   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
1040 bool RenderWidgetHostViewMac::HasFocus() const {
1041   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
1044 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
1045   if (delegated_frame_host_)
1046     return delegated_frame_host_->CanCopyToBitmap();
1047   return false;
1050 bool RenderWidgetHostViewMac::IsShowing() {
1051   return ![cocoa_view_ isHidden];
1054 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
1055   NSRect bounds = [cocoa_view_ bounds];
1056   // TODO(shess): In case of !window, the view has been removed from
1057   // the view hierarchy because the tab isn't main.  Could retrieve
1058   // the information from the main tab for our window.
1059   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1060   if (!enclosing_window)
1061     return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
1063   bounds = [cocoa_view_ convertRect:bounds toView:nil];
1064   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
1065   return FlipNSRectToRectScreen(bounds);
1068 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
1069   WebCursor web_cursor = cursor;
1070   [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
1073 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
1074   is_loading_ = is_loading;
1075   // If we ever decide to show the waiting cursor while the page is loading
1076   // like Chrome does on Windows, call |UpdateCursor()| here.
1079 void RenderWidgetHostViewMac::TextInputStateChanged(
1080 const ViewHostMsg_TextInputState_Params& params) {
1081   if (text_input_type_ != params.type
1082       || can_compose_inline_ != params.can_compose_inline) {
1083     text_input_type_ = params.type;
1084     can_compose_inline_ = params.can_compose_inline;
1085     if (HasFocus()) {
1086       SetTextInputActive(true);
1088       // Let AppKit cache the new input context to make IMEs happy.
1089       // See http://crbug.com/73039.
1090       [NSApp updateWindows];
1092 #ifndef __LP64__
1093       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
1094 #endif
1095     }
1096   }
1099 void RenderWidgetHostViewMac::ImeCancelComposition() {
1100   [cocoa_view_ cancelComposition];
1103 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
1104     const gfx::Range& range,
1105     const std::vector<gfx::Rect>& character_bounds) {
1106   // The RangeChanged message is only sent with valid values. The current
1107   // caret position (start == end) will be sent if there is no IME range.
1108   [cocoa_view_ setMarkedRange:range.ToNSRange()];
1109   composition_range_ = range;
1110   composition_bounds_ = character_bounds;
1113 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
1114                                                 int error_code) {
1115   Destroy();
1118 void RenderWidgetHostViewMac::RenderWidgetHostGone() {
1119   // Clear SurfaceID namespace ownership before we shutdown the
1120   // compositor.
1121   if (UseSurfacesEnabled() && render_widget_host_ &&
1122       render_widget_host_->delegate() &&
1123       render_widget_host_->delegate()->GetInputEventRouter()) {
1124     render_widget_host_->delegate()
1125         ->GetInputEventRouter()
1126         ->RemoveSurfaceIdNamespaceOwner(GetSurfaceIdNamespace());
1127   }
1129   // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never
1130   // called on the view.
1131   // http://crbug.com/404828
1132   ShutdownBrowserCompositor();
1135 void RenderWidgetHostViewMac::Destroy() {
1136   [[NSNotificationCenter defaultCenter]
1137       removeObserver:cocoa_view_
1138                 name:NSWindowWillCloseNotification
1139               object:popup_window_];
1141   // We've been told to destroy.
1142   [cocoa_view_ retain];
1143   [cocoa_view_ removeFromSuperview];
1144   [cocoa_view_ autorelease];
1146   [popup_window_ close];
1147   popup_window_.autorelease();
1149   [fullscreen_window_manager_ exitFullscreenMode];
1150   fullscreen_window_manager_.reset();
1151   [pepper_fullscreen_window_ close];
1153   // This can be called as part of processing the window's responder
1154   // chain, for instance |-performKeyEquivalent:|.  In that case the
1155   // object needs to survive until the stack unwinds.
1156   pepper_fullscreen_window_.autorelease();
1158   // Clear SurfaceID namespace ownership before we shutdown the
1159   // compositor.
1160   if (UseSurfacesEnabled() && render_widget_host_ &&
1161       render_widget_host_->delegate() &&
1162       render_widget_host_->delegate()->GetInputEventRouter()) {
1163     render_widget_host_->delegate()
1164         ->GetInputEventRouter()
1165         ->RemoveSurfaceIdNamespaceOwner(GetSurfaceIdNamespace());
1166   }
1168   // Delete the delegated frame state, which will reach back into
1169   // render_widget_host_.
1170   ShutdownBrowserCompositor();
1172   // We get this call just before |render_widget_host_| deletes
1173   // itself.  But we are owned by |cocoa_view_|, which may be retained
1174   // by some other code.  Examples are WebContentsViewMac's
1175   // |latent_focus_view_| and TabWindowController's
1176   // |cachedContentView_|.
1177   render_widget_host_ = NULL;
1180 // Called from the renderer to tell us what the tooltip text should be. It
1181 // calls us frequently so we need to cache the value to prevent doing a lot
1182 // of repeat work.
1183 void RenderWidgetHostViewMac::SetTooltipText(
1184     const base::string16& tooltip_text) {
1185   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1186     tooltip_text_ = tooltip_text;
1188     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1189     // Windows; we're just trying to be polite. Don't persist the trimmed
1190     // string, as then the comparison above will always fail and we'll try to
1191     // set it again every single time the mouse moves.
1192     base::string16 display_text = tooltip_text_;
1193     if (tooltip_text_.length() > kMaxTooltipLength)
1194       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1196     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1197     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1198   }
1201 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1202   return [NSApp respondsToSelector:@selector(speakString:)] &&
1203          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1206 void RenderWidgetHostViewMac::SpeakSelection() {
1207   if (![NSApp respondsToSelector:@selector(speakString:)])
1208     return;
1210   if (selected_text_.empty() && render_widget_host_) {
1211     // If there's no selection, speak all text. Send an asynchronous IPC
1212     // request for fetching all the text for a webcontent.
1213     // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1214     render_widget_host_->Send(new ViewMsg_GetRenderedText(
1215         render_widget_host_->GetRoutingID()));
1216     return;
1217   }
1219   SpeakText(selected_text_);
1222 bool RenderWidgetHostViewMac::IsSpeaking() const {
1223   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1224          [NSApp isSpeaking];
1227 void RenderWidgetHostViewMac::StopSpeaking() {
1228   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1229     [NSApp stopSpeaking:cocoa_view_];
1233 // RenderWidgetHostViewCocoa uses the stored selection text,
1234 // which implements NSServicesRequests protocol.
1236 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1237                                                size_t offset,
1238                                                const gfx::Range& range) {
1239   if (range.is_empty() || text.empty()) {
1240     selected_text_.clear();
1241   } else {
1242     size_t pos = range.GetMin() - offset;
1243     size_t n = range.length();
1245     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1246     if (pos >= text.length()) {
1247       DCHECK(false) << "The text can not cover range.";
1248       return;
1249     }
1250     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1251   }
1253   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1254   // Updates markedRange when there is no marked text so that retrieving
1255   // markedRange immediately after calling setMarkdText: returns the current
1256   // caret position.
1257   if (![cocoa_view_ hasMarkedText]) {
1258     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1259   }
1261   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1264 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1265     const ViewHostMsg_SelectionBounds_Params& params) {
1266   if (params.anchor_rect == params.focus_rect)
1267     caret_rect_ = params.anchor_rect;
1270 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1271   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1273   // Create a fake mouse event to inform the render widget that the mouse
1274   // left or entered.
1275   NSWindow* window = [cocoa_view_ window];
1276   // TODO(asvitkine): If the location outside of the event stream doesn't
1277   // correspond to the current event (due to delayed event processing), then
1278   // this may result in a cursor flicker if there are later mouse move events
1279   // in the pipeline. Find a way to use the mouse location from the event that
1280   // dismissed the context menu.
1281   NSPoint location = [window mouseLocationOutsideOfEventStream];
1282   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1283                                       location:location
1284                                  modifierFlags:0
1285                                      timestamp:0
1286                                   windowNumber:window_number()
1287                                        context:nil
1288                                    eventNumber:0
1289                                     clickCount:0
1290                                       pressure:0];
1291   WebMouseEvent web_event = WebMouseEventBuilder::Build(event, cocoa_view_);
1292   if (showing)
1293     web_event.type = WebInputEvent::MouseLeave;
1294   ForwardMouseEvent(web_event);
1297 bool RenderWidgetHostViewMac::IsPopup() const {
1298   return popup_type_ != blink::WebPopupTypeNone;
1301 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1302     const gfx::Rect& src_subrect,
1303     const gfx::Size& dst_size,
1304     ReadbackRequestCallback& callback,
1305     const SkColorType preferred_color_type) {
1306   if (delegated_frame_host_) {
1307     delegated_frame_host_->CopyFromCompositingSurface(
1308         src_subrect, dst_size, callback, preferred_color_type);
1309   }
1312 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1313       const gfx::Rect& src_subrect,
1314       const scoped_refptr<media::VideoFrame>& target,
1315       const base::Callback<void(bool)>& callback) {
1316   if (delegated_frame_host_) {
1317     delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1318         src_subrect, target, callback);
1319   }
1322 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1323   if (delegated_frame_host_)
1324     return delegated_frame_host_->CanCopyToVideoFrame();
1325   return false;
1328 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1329   return !!delegated_frame_host_;
1332 void RenderWidgetHostViewMac::BeginFrameSubscription(
1333     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1334   if (delegated_frame_host_)
1335     delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1338 void RenderWidgetHostViewMac::EndFrameSubscription() {
1339   if (delegated_frame_host_)
1340     delegated_frame_host_->EndFrameSubscription();
1343 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1344   if (render_widget_host_)
1345     render_widget_host_->ForwardMouseEvent(event);
1347   if (event.type == WebInputEvent::MouseLeave) {
1348     [cocoa_view_ setToolTipAtMousePoint:nil];
1349     tooltip_text_.clear();
1350   }
1353 void RenderWidgetHostViewMac::KillSelf() {
1354   if (!weak_factory_.HasWeakPtrs()) {
1355     [cocoa_view_ setHidden:YES];
1356     base::MessageLoop::current()->PostTask(FROM_HERE,
1357         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1358                    weak_factory_.GetWeakPtr()));
1359   }
1362 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1363     const NativeWebKeyboardEvent& event) {
1364   // Check WebInputEvent type since multiple types of events can be sent into
1365   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1366   // necessary to avoid double processing.
1367   // Also check the native type, since NSFlagsChanged is considered a key event
1368   // for WebKit purposes, but isn't considered a key event by the OS.
1369   if (event.type == WebInputEvent::RawKeyDown &&
1370       [event.os_event type] == NSKeyDown)
1371     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1372   return false;
1375 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1376     const base::string16& text, int plugin_id) {
1377   if (render_widget_host_) {
1378     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1379         render_widget_host_->GetRoutingID(), text, plugin_id));
1380   }
1383 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1384     const std::vector<gfx::Rect>& bounds,
1385     const gfx::Range& range,
1386     size_t* line_break_point) {
1387   DCHECK(line_break_point);
1388   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1389     return false;
1391   // We can't check line breaking completely from only rectangle array. Thus we
1392   // assume the line breaking as the next character's y offset is larger than
1393   // a threshold. Currently the threshold is determined as minimum y offset plus
1394   // 75% of maximum height.
1395   // TODO(nona): Check the threshold is reliable or not.
1396   // TODO(nona): Bidi support.
1397   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1398   int max_height = 0;
1399   int min_y_offset = kint32max;
1400   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1401     max_height = std::max(max_height, bounds[idx].height());
1402     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1403   }
1404   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1405   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1406     if (bounds[idx].y() > line_break_threshold) {
1407       *line_break_point = idx;
1408       return true;
1409     }
1410   }
1411   return false;
1414 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1415     const gfx::Range& range,
1416     gfx::Range* actual_range) {
1417   DCHECK(actual_range);
1418   DCHECK(!composition_bounds_.empty());
1419   DCHECK(range.start() <= composition_bounds_.size());
1420   DCHECK(range.end() <= composition_bounds_.size());
1422   if (range.is_empty()) {
1423     *actual_range = range;
1424     if (range.start() == composition_bounds_.size()) {
1425       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1426                        composition_bounds_[range.start() - 1].y(),
1427                        0,
1428                        composition_bounds_[range.start() - 1].height());
1429     } else {
1430       return gfx::Rect(composition_bounds_[range.start()].x(),
1431                        composition_bounds_[range.start()].y(),
1432                        0,
1433                        composition_bounds_[range.start()].height());
1434     }
1435   }
1437   size_t end_idx;
1438   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1439     end_idx = range.end();
1440   }
1441   *actual_range = gfx::Range(range.start(), end_idx);
1442   gfx::Rect rect = composition_bounds_[range.start()];
1443   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1444     rect.Union(composition_bounds_[i]);
1445   }
1446   return rect;
1449 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1450     const gfx::Range& request_range) {
1451   if (composition_range_.is_empty())
1452     return gfx::Range::InvalidRange();
1454   if (request_range.is_reversed())
1455     return gfx::Range::InvalidRange();
1457   if (request_range.start() < composition_range_.start() ||
1458       request_range.start() > composition_range_.end() ||
1459       request_range.end() > composition_range_.end()) {
1460     return gfx::Range::InvalidRange();
1461   }
1463   return gfx::Range(
1464       request_range.start() - composition_range_.start(),
1465       request_range.end() - composition_range_.start());
1468 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1469   if (!render_widget_host_->IsRenderView())
1470     return NULL;
1472   return WebContents::FromRenderViewHost(
1473       RenderViewHost::From(render_widget_host_));
1476 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1477     NSRange range,
1478     NSRect* rect,
1479     NSRange* actual_range) {
1480   DCHECK(rect);
1481   // This exists to make IMEs more responsive, see http://crbug.com/115920
1482   TRACE_EVENT0("browser",
1483                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1485   // If requested range is same as caret location, we can just return it.
1486   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1487     if (actual_range)
1488       *actual_range = range;
1489     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1490     return true;
1491   }
1493   const gfx::Range request_range_in_composition =
1494       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1495   if (request_range_in_composition == gfx::Range::InvalidRange())
1496     return false;
1498   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1499   // ImeCompositionRangeChanged will be sent with empty vector.
1500   if (composition_bounds_.empty())
1501     return false;
1502   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1504   gfx::Range ui_actual_range;
1505   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1506                                request_range_in_composition,
1507                                &ui_actual_range).ToCGRect());
1508   if (actual_range) {
1509     *actual_range = gfx::Range(
1510         composition_range_.start() + ui_actual_range.start(),
1511         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1512   }
1513   return true;
1516 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1517       const gfx::Size& desired_size) {
1518   if (browser_compositor_) {
1519     return browser_compositor_->accelerated_widget_mac()->HasFrameOfSize(
1520         desired_size);
1521   }
1522   return false;
1525 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1526     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1527   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1529   last_scroll_offset_ = frame->metadata.root_scroll_offset;
1531   page_at_minimum_scale_ = frame->metadata.page_scale_factor ==
1532                            frame->metadata.min_page_scale_factor;
1534   if (frame->delegated_frame_data) {
1535     float scale_factor = frame->metadata.device_scale_factor;
1537     // Compute the frame size based on the root render pass rect size.
1538     cc::RenderPass* root_pass =
1539         frame->delegated_frame_data->render_pass_list.back();
1540     gfx::Size pixel_size = root_pass->output_rect.size();
1541     gfx::Size dip_size = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
1543     root_layer_->SetBounds(gfx::Rect(dip_size));
1544     if (!render_widget_host_->is_hidden()) {
1545       EnsureBrowserCompositorView();
1546       browser_compositor_->compositor()->SetScaleAndSize(
1547           scale_factor, pixel_size);
1548     }
1550     SendVSyncParametersToRenderer();
1552     delegated_frame_host_->SwapDelegatedFrame(
1553         output_surface_id,
1554         frame->delegated_frame_data.Pass(),
1555         frame->metadata.device_scale_factor,
1556         frame->metadata.latency_info,
1557         &frame->metadata.satisfies_sequences);
1558   } else {
1559     DLOG(ERROR) << "Received unexpected frame type.";
1560     bad_message::ReceivedBadMessage(render_widget_host_->GetProcess(),
1561                                     bad_message::RWHVM_UNEXPECTED_FRAME_TYPE);
1562   }
1565 void RenderWidgetHostViewMac::ClearCompositorFrame() {
1566   if (delegated_frame_host_)
1567     delegated_frame_host_->ClearDelegatedFrame();
1570 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1571   *results = GetWebScreenInfo(GetNativeView());
1574 bool RenderWidgetHostViewMac::GetScreenColorProfile(
1575     std::vector<char>* color_profile) {
1576   DCHECK(color_profile->empty());
1577   NSWindow* window = GetWebContents()->GetTopLevelNativeWindow();
1578   return gfx::GetDisplayColorProfile(window, color_profile);
1581 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1582   // TODO(shess): In case of !window, the view has been removed from
1583   // the view hierarchy because the tab isn't main.  Could retrieve
1584   // the information from the main tab for our window.
1585   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1586   if (!enclosing_window)
1587     return gfx::Rect();
1589   NSRect bounds = [enclosing_window frame];
1590   return FlipNSRectToRectScreen(bounds);
1593 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1594   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1595   // completely on Mac OS.
1596   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NULL_TRANSPORT);
1599 bool RenderWidgetHostViewMac::LockMouse() {
1600   if (mouse_locked_)
1601     return true;
1603   mouse_locked_ = true;
1605   // Lock position of mouse cursor and hide it.
1606   CGAssociateMouseAndMouseCursorPosition(NO);
1607   [NSCursor hide];
1609   // Clear the tooltip window.
1610   SetTooltipText(base::string16());
1612   return true;
1615 void RenderWidgetHostViewMac::UnlockMouse() {
1616   if (!mouse_locked_)
1617     return;
1618   mouse_locked_ = false;
1620   // Unlock position of mouse cursor and unhide it.
1621   CGAssociateMouseAndMouseCursorPosition(YES);
1622   [NSCursor unhide];
1624   if (render_widget_host_)
1625     render_widget_host_->LostMouseLock();
1628 void RenderWidgetHostViewMac::WheelEventAck(
1629     const blink::WebMouseWheelEvent& event,
1630     InputEventAckState ack_result) {
1631   bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1632   // Only record a wheel event as unhandled if JavaScript handlers got a chance
1633   // to see it (no-op wheel events are ignored by the event dispatcher)
1634   if (event.deltaX || event.deltaY)
1635     [cocoa_view_ processedWheelEvent:event consumed:consumed];
1638 uint32_t RenderWidgetHostViewMac::GetSurfaceIdNamespace() {
1639   if (delegated_frame_host_)
1640     return delegated_frame_host_->GetSurfaceIdNamespace();
1642   return 0;
1645 uint32_t RenderWidgetHostViewMac::SurfaceIdNamespaceAtPoint(
1646     const gfx::Point& point,
1647     gfx::Point* transformed_point) {
1648   cc::SurfaceId id =
1649       delegated_frame_host_->SurfaceIdAtPoint(point, transformed_point);
1650   // It is possible that the renderer has not yet produced a surface, in which
1651   // case we return our current namespace.
1652   if (id.is_null())
1653     return GetSurfaceIdNamespace();
1654   return cc::SurfaceIdAllocator::NamespaceForId(id);
1657 void RenderWidgetHostViewMac::ProcessMouseEvent(
1658     const blink::WebMouseEvent& event) {
1659   render_widget_host_->ForwardMouseEvent(event);
1661 void RenderWidgetHostViewMac::ProcessMouseWheelEvent(
1662     const blink::WebMouseWheelEvent& event) {
1663   render_widget_host_->ForwardWheelEvent(event);
1666 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1667   if (render_widget_host_)
1668     return render_widget_host_->Send(message);
1669   delete message;
1670   return false;
1673 void RenderWidgetHostViewMac::ShutdownHost() {
1674   weak_factory_.InvalidateWeakPtrs();
1675   render_widget_host_->Shutdown();
1676   // Do not touch any members at this point, |this| has been deleted.
1679 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1680   DestroyBrowserCompositorView();
1681   delegated_frame_host_.reset();
1682   root_layer_.reset();
1683   browser_compositor_placeholder_.reset();
1686 void RenderWidgetHostViewMac::SetActive(bool active) {
1687   if (render_widget_host_) {
1688     render_widget_host_->SetActive(active);
1689     if (active) {
1690       if (HasFocus())
1691         render_widget_host_->Focus();
1692     } else {
1693       render_widget_host_->Blur();
1694     }
1695   }
1696   if (HasFocus())
1697     SetTextInputActive(active);
1698   if (!active) {
1699     [cocoa_view_ setPluginImeActive:NO];
1700     UnlockMouse();
1701   }
1704 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1705   if (render_widget_host_) {
1706     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1707         render_widget_host_->GetRoutingID(), visible));
1708   }
1711 void RenderWidgetHostViewMac::WindowFrameChanged() {
1712   if (render_widget_host_) {
1713     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1714         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1715         GetViewBounds()));
1716   }
1719 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1720   RenderWidgetHostViewMacDictionaryHelper helper(this);
1721   helper.ShowDefinitionForSelection();
1724 void RenderWidgetHostViewMac::SetBackgroundColor(SkColor color) {
1725   RenderWidgetHostViewBase::SetBackgroundColor(color);
1726   bool opaque = GetBackgroundOpaque();
1728   if (render_widget_host_)
1729     render_widget_host_->SetBackgroundOpaque(opaque);
1731   [cocoa_view_ setOpaque:opaque];
1732   if (browser_compositor_state_ != BrowserCompositorDestroyed)
1733     browser_compositor_->compositor()->SetHostHasTransparentBackground(!opaque);
1736 BrowserAccessibilityManager*
1737     RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1738         BrowserAccessibilityDelegate* delegate) {
1739   return new BrowserAccessibilityManagerMac(
1740       cocoa_view_,
1741       BrowserAccessibilityManagerMac::GetEmptyDocument(),
1742       delegate);
1745 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1746     const gfx::Rect& bounds) {
1747   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1748   NSSize size = NSMakeSize(bounds.width(), bounds.height());
1749   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1750   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1751   NSPoint originInScreen =
1752       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1753   originInScreen.y = originInScreen.y - size.height;
1754   return gfx::Point(originInScreen.x, originInScreen.y);
1757 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1758   if (active) {
1759     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1760       EnablePasswordInput();
1761     else
1762       DisablePasswordInput();
1763   } else {
1764     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1765       DisablePasswordInput();
1766   }
1769 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1770                                                    int plugin_id) {
1771   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1774 void RenderWidgetHostViewMac::OnStartPluginIme() {
1775   [cocoa_view_ setPluginImeActive:YES];
1778 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1779     const std::string& text) {
1780   SpeakText(text);
1783 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1784   if (!render_widget_host_ || render_widget_host_->is_hidden())
1785     return;
1787   // Pausing for one view prevents others from receiving frames.
1788   // This may lead to large delays, causing overlaps. See crbug.com/352020.
1789   if (!allow_pause_for_resize_or_repaint_)
1790     return;
1792   // Wait for a frame of the right size to come in.
1793   if (browser_compositor_)
1794     browser_compositor_->accelerated_widget_mac()->BeginPumpingFrames();
1795   render_widget_host_->PauseForPendingResizeOrRepaints();
1796   if (browser_compositor_)
1797     browser_compositor_->accelerated_widget_mac()->EndPumpingFrames();
1800 ////////////////////////////////////////////////////////////////////////////////
1801 // gfx::DisplayObserver, public:
1803 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1806 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1809 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1810     const gfx::Display& display, uint32_t metrics) {
1811   gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1812   if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1813     return;
1815   UpdateScreenInfo(cocoa_view_);
1818 }  // namespace content
1820 // RenderWidgetHostViewCocoa ---------------------------------------------------
1822 @implementation RenderWidgetHostViewCocoa
1823 @synthesize selectedRange = selectedRange_;
1824 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1825 @synthesize markedRange = markedRange_;
1827 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1828   self = [super initWithFrame:NSZeroRect];
1829   if (self) {
1830     self.acceptsTouchEvents = YES;
1831     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1832     editCommand_helper_->AddEditingSelectorsToClass([self class]);
1834     renderWidgetHostView_.reset(r);
1835     canBeKeyView_ = YES;
1836     opaque_ = YES;
1837     focusedPluginIdentifier_ = -1;
1838     pinchHasReachedZoomThreshold_ = false;
1840     // OpenGL support:
1841     if ([self respondsToSelector:
1842         @selector(setWantsBestResolutionOpenGLSurface:)]) {
1843       [self setWantsBestResolutionOpenGLSurface:YES];
1844     }
1845     [[NSNotificationCenter defaultCenter]
1846         addObserver:self
1847            selector:@selector(didChangeScreenParameters:)
1848                name:NSApplicationDidChangeScreenParametersNotification
1849              object:nil];
1850   }
1851   return self;
1854 - (void)dealloc {
1855   if (responderDelegate_ &&
1856       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1857     [responderDelegate_ viewGone:self];
1858   responderDelegate_.reset();
1860   [[NSNotificationCenter defaultCenter] removeObserver:self];
1862   [super dealloc];
1865 - (void)didChangeScreenParameters:(NSNotification*)notify {
1866   g_screen_info_up_to_date = false;
1869 - (void)setResponderDelegate:
1870             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1871   DCHECK(!responderDelegate_);
1872   responderDelegate_.reset([delegate retain]);
1875 - (void)resetCursorRects {
1876   if (currentCursor_) {
1877     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1878     [currentCursor_ setOnMouseEntered:YES];
1879   }
1882 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1883                    consumed:(BOOL)consumed {
1884   [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1887 - (BOOL)respondsToSelector:(SEL)selector {
1888   // Trickiness: this doesn't mean "does this object's superclass respond to
1889   // this selector" but rather "does the -respondsToSelector impl from the
1890   // superclass say that this class responds to the selector".
1891   if ([super respondsToSelector:selector])
1892     return YES;
1894   if (responderDelegate_)
1895     return [responderDelegate_ respondsToSelector:selector];
1897   return NO;
1900 - (id)forwardingTargetForSelector:(SEL)selector {
1901   if ([responderDelegate_ respondsToSelector:selector])
1902     return responderDelegate_.get();
1904   return [super forwardingTargetForSelector:selector];
1907 - (void)setCanBeKeyView:(BOOL)can {
1908   canBeKeyView_ = can;
1911 - (BOOL)acceptsMouseEventsWhenInactive {
1912   // Some types of windows (balloons, always-on-top panels) want to accept mouse
1913   // clicks w/o the first click being treated as 'activation'. Same applies to
1914   // mouse move events.
1915   return [[self window] level] > NSNormalWindowLevel;
1918 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1919   return [self acceptsMouseEventsWhenInactive];
1922 - (void)setCloseOnDeactivate:(BOOL)b {
1923   closeOnDeactivate_ = b;
1926 - (void)setOpaque:(BOOL)opaque {
1927   opaque_ = opaque;
1930 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1931   NSWindow* window = [self window];
1932   // If this is a background window, don't handle mouse movement events. This
1933   // is the expected behavior on the Mac as evidenced by other applications.
1934   if ([theEvent type] == NSMouseMoved &&
1935       ![self acceptsMouseEventsWhenInactive] &&
1936       ![window isKeyWindow]) {
1937     return YES;
1938   }
1940   // Use hitTest to check whether the mouse is over a nonWebContentView - in
1941   // which case the mouse event should not be handled by the render host.
1942   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1943   NSView* contentView = [window contentView];
1944   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1945   // Traverse the superview hierarchy as the hitTest will return the frontmost
1946   // view, such as an NSTextView, while nonWebContentView may be specified by
1947   // its parent view.
1948   while (view) {
1949     if ([view respondsToSelector:nonWebContentViewSelector] &&
1950         [view performSelector:nonWebContentViewSelector]) {
1951       // The cursor is over a nonWebContentView - ignore this mouse event.
1952       return YES;
1953     }
1954     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1955         !hasOpenMouseDown_) {
1956       // The cursor is over an overlapping render widget. This check is done by
1957       // both views so the one that's returned by -hitTest: will end up
1958       // processing the event.
1959       // Note that while dragging, we only get events for the render view where
1960       // drag started, even if mouse is  actually over another view or outside
1961       // the window. Cocoa does this for us. We should handle these events and
1962       // not ignore (since there is no other render view to handle them). Thus
1963       // the |!hasOpenMouseDown_| check above.
1964       return YES;
1965     }
1966     view = [view superview];
1967   }
1968   return NO;
1971 - (void)mouseEvent:(NSEvent*)theEvent {
1972   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1973   if (responderDelegate_ &&
1974       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1975     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1976     if (handled)
1977       return;
1978   }
1980   if ([self shouldIgnoreMouseEvent:theEvent]) {
1981     // If this is the first such event, send a mouse exit to the host view.
1982     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1983       WebMouseEvent exitEvent = WebMouseEventBuilder::Build(theEvent, self);
1984       exitEvent.type = WebInputEvent::MouseLeave;
1985       exitEvent.button = WebMouseEvent::ButtonNone;
1986       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1987     }
1988     mouseEventWasIgnored_ = YES;
1989     return;
1990   }
1992   if (mouseEventWasIgnored_) {
1993     // If this is the first mouse event after a previous event that was ignored
1994     // due to the hitTest, send a mouse enter event to the host view.
1995     if (renderWidgetHostView_->render_widget_host_) {
1996       WebMouseEvent enterEvent = WebMouseEventBuilder::Build(theEvent, self);
1997       enterEvent.type = WebInputEvent::MouseMove;
1998       enterEvent.button = WebMouseEvent::ButtonNone;
1999       if (renderWidgetHostView_->render_widget_host_->delegate() &&
2000           renderWidgetHostView_->render_widget_host_->delegate()
2001               ->GetInputEventRouter()) {
2002         renderWidgetHostView_->render_widget_host_->delegate()
2003             ->GetInputEventRouter()
2004             ->RouteMouseEvent(renderWidgetHostView_.get(), &enterEvent);
2005       } else {
2006         renderWidgetHostView_->ForwardMouseEvent(enterEvent);
2007       }
2008     }
2009   }
2010   mouseEventWasIgnored_ = NO;
2012   // Don't cancel child popups; killing them on a mouse click would prevent the
2013   // user from positioning the insertion point in the text field spawning the
2014   // popup. A click outside the text field would cause the text field to drop
2015   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
2016   // the popup anyway, so we're OK.
2018   NSEventType type = [theEvent type];
2019   if (type == NSLeftMouseDown)
2020     hasOpenMouseDown_ = YES;
2021   else if (type == NSLeftMouseUp)
2022     hasOpenMouseDown_ = NO;
2024   // TODO(suzhe): We should send mouse events to the input method first if it
2025   // wants to handle them. But it won't work without implementing method
2026   // - (NSUInteger)characterIndexForPoint:.
2027   // See: http://code.google.com/p/chromium/issues/detail?id=47141
2028   // Instead of sending mouse events to the input method first, we now just
2029   // simply confirm all ongoing composition here.
2030   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
2031       type == NSOtherMouseDown) {
2032     [self confirmComposition];
2033   }
2035   WebMouseEvent event = WebMouseEventBuilder::Build(theEvent, self);
2036   if (renderWidgetHostView_->render_widget_host_->delegate() &&
2037       renderWidgetHostView_->render_widget_host_->delegate()
2038           ->GetInputEventRouter()) {
2039     renderWidgetHostView_->render_widget_host_->delegate()
2040         ->GetInputEventRouter()
2041         ->RouteMouseEvent(renderWidgetHostView_.get(), &event);
2042   } else {
2043     renderWidgetHostView_->ForwardMouseEvent(event);
2044   }
2047 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
2048   // |performKeyEquivalent:| is sent to all views of a window, not only down the
2049   // responder chain (cf. "Handling Key Equivalents" in
2050   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
2051   // ). We only want to handle key equivalents if we're first responder.
2052   if ([[self window] firstResponder] != self)
2053     return NO;
2055   // If the event is reserved by the system, then do not pass it to web content.
2056   if (EventIsReservedBySystem(theEvent))
2057     return NO;
2059   // If we return |NO| from this function, cocoa will send the key event to
2060   // the menu and only if the menu does not process the event to |keyDown:|. We
2061   // want to send the event to a renderer _before_ sending it to the menu, so
2062   // we need to return |YES| for all events that might be swallowed by the menu.
2063   // We do not return |YES| for every keypress because we don't get |keyDown:|
2064   // events for keys that we handle this way.
2065   NSUInteger modifierFlags = [theEvent modifierFlags];
2066   if ((modifierFlags & NSCommandKeyMask) == 0) {
2067     // Make sure the menu does not contain key equivalents that don't
2068     // contain cmd.
2069     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
2070     return NO;
2071   }
2073   // Command key combinations are sent via performKeyEquivalent rather than
2074   // keyDown:. We just forward this on and if WebCore doesn't want to handle
2075   // it, we let the WebContentsView figure out how to reinject it.
2076   [self keyEvent:theEvent wasKeyEquivalent:YES];
2077   return YES;
2080 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
2081   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
2082   // returned NO. If this function returns |YES|, Cocoa sends the event to
2083   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
2084   // to us instead of doing key view loop control, ctrl-left/right get handled
2085   // correctly, etc.
2086   // (However, there are still some keys that Cocoa swallows, e.g. the key
2087   // equivalent that Cocoa uses for toggling the input language. In this case,
2088   // that's actually a good thing, though -- see http://crbug.com/26115 .)
2089   return YES;
2092 - (EventHandled)keyEvent:(NSEvent*)theEvent {
2093   if (responderDelegate_ &&
2094       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2095     BOOL handled = [responderDelegate_ handleEvent:theEvent];
2096     if (handled)
2097       return kEventHandled;
2098   }
2100   [self keyEvent:theEvent wasKeyEquivalent:NO];
2101   return kEventHandled;
2104 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
2105   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
2107   // If the user changes the system hotkey mapping after Chrome has been
2108   // launched, then it is possible that a formerly reserved system hotkey is no
2109   // longer reserved. The hotkey would have skipped the renderer, but would
2110   // also have not been handled by the system. If this is the case, immediately
2111   // return.
2112   // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
2113   // api to monitor changes to system hotkeys. This logic will have to be
2114   // updated.
2115   // http://crbug.com/383558.
2116   if (EventIsReservedBySystem(theEvent))
2117     return;
2119   DCHECK([theEvent type] != NSKeyDown ||
2120          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
2122   if ([theEvent type] == NSFlagsChanged) {
2123     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
2124     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
2125     int keyCode = [theEvent keyCode];
2126     if (!keyCode || keyCode == 10 || keyCode == 63)
2127       return;
2128   }
2130   // Don't cancel child popups; the key events are probably what's triggering
2131   // the popup in the first place.
2133   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
2134   DCHECK(widgetHost);
2136   NativeWebKeyboardEvent event(theEvent);
2138   // Force fullscreen windows to close on Escape so they won't keep the keyboard
2139   // grabbed or be stuck onscreen if the renderer is hanging.
2140   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
2141       event.windowsKeyCode == ui::VKEY_ESCAPE &&
2142       renderWidgetHostView_->pepper_fullscreen_window()) {
2143     RenderWidgetHostViewMac* parent =
2144         renderWidgetHostView_->fullscreen_parent_host_view();
2145     if (parent)
2146       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
2147     widgetHost->Shutdown();
2148     return;
2149   }
2151   // Suppress the escape key up event if necessary.
2152   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
2153     if (event.type == NativeWebKeyboardEvent::KeyUp)
2154       suppressNextEscapeKeyUp_ = NO;
2155     return;
2156   }
2158   // Do not forward key up events unless preceded by a matching key down,
2159   // otherwise we might get an event from releasing the return key in the
2160   // omnibox (http://crbug.com/338736).
2161   if ([theEvent type] == NSKeyUp) {
2162     auto numErased = keyDownCodes_.erase([theEvent keyCode]);
2163     if (numErased < 1)
2164       return;
2165   }
2167   // We only handle key down events and just simply forward other events.
2168   if ([theEvent type] != NSKeyDown) {
2169     widgetHost->ForwardKeyboardEvent(event);
2171     // Possibly autohide the cursor.
2172     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2173       [NSCursor setHiddenUntilMouseMoves:YES];
2175     return;
2176   }
2178   keyDownCodes_.insert([theEvent keyCode]);
2180   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2182   // Records the current marked text state, so that we can know if the marked
2183   // text was deleted or not after handling the key down event.
2184   BOOL oldHasMarkedText = hasMarkedText_;
2186   // This method should not be called recursively.
2187   DCHECK(!handlingKeyDown_);
2189   // Tells insertText: and doCommandBySelector: that we are handling a key
2190   // down event.
2191   handlingKeyDown_ = YES;
2193   // These variables might be set when handling the keyboard event.
2194   // Clear them here so that we can know whether they have changed afterwards.
2195   textToBeInserted_.clear();
2196   markedText_.clear();
2197   markedTextSelectedRange_ = NSMakeRange(NSNotFound, 0);
2198   underlines_.clear();
2199   unmarkTextCalled_ = NO;
2200   hasEditCommands_ = NO;
2201   editCommands_.clear();
2203   // Before doing anything with a key down, check to see if plugin IME has been
2204   // cancelled, since the plugin host needs to be informed of that before
2205   // receiving the keydown.
2206   if ([theEvent type] == NSKeyDown)
2207     [self checkForPluginImeCancellation];
2209   // Sends key down events to input method first, then we can decide what should
2210   // be done according to input method's feedback.
2211   // If a plugin is active, bypass this step since events are forwarded directly
2212   // to the plugin IME.
2213   if (focusedPluginIdentifier_ == -1)
2214     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2216   handlingKeyDown_ = NO;
2218   // Indicates if we should send the key event and corresponding editor commands
2219   // after processing the input method result.
2220   BOOL delayEventUntilAfterImeCompostion = NO;
2222   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2223   // while an input method is composing or inserting a text.
2224   // Gmail checks this code in its onkeydown handler to stop auto-completing
2225   // e-mail addresses while composing a CJK text.
2226   // If the text to be inserted has only one character, then we don't need this
2227   // trick, because we'll send the text as a key press event instead.
2228   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2229     NativeWebKeyboardEvent fakeEvent = event;
2230     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2231     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2232     fakeEvent.skip_in_browser = true;
2233     widgetHost->ForwardKeyboardEvent(fakeEvent);
2234     // If this key event was handled by the input method, but
2235     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2236     // enqueued edit commands, then in order to let webkit handle them
2237     // correctly, we need to send the real key event and corresponding edit
2238     // commands after processing the input method result.
2239     // We shouldn't do this if a new marked text was set by the input method,
2240     // otherwise the new marked text might be cancelled by webkit.
2241     if (hasEditCommands_ && !hasMarkedText_)
2242       delayEventUntilAfterImeCompostion = YES;
2243   } else {
2244     if (!editCommands_.empty()) {
2245       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2246           widgetHost->GetRoutingID(), editCommands_));
2247     }
2248     widgetHost->ForwardKeyboardEvent(event);
2249   }
2251   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2252   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2253   // be set to NULL. So we check it here and return immediately if it's NULL.
2254   if (!renderWidgetHostView_->render_widget_host_)
2255     return;
2257   // Then send keypress and/or composition related events.
2258   // If there was a marked text or the text to be inserted is longer than 1
2259   // character, then we send the text by calling ConfirmComposition().
2260   // Otherwise, if the text to be inserted only contains 1 character, then we
2261   // can just send a keypress event which is fabricated by changing the type of
2262   // the keydown event, so that we can retain all necessary informations, such
2263   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2264   // prevent the browser from handling it again.
2265   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2266   // handle BMP characters here, as we can always insert non-BMP characters as
2267   // text.
2268   BOOL textInserted = NO;
2269   if (textToBeInserted_.length() >
2270       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2271     widgetHost->ImeConfirmComposition(
2272         textToBeInserted_, gfx::Range::InvalidRange(), false);
2273     textInserted = YES;
2274   }
2276   // Updates or cancels the composition. If some text has been inserted, then
2277   // we don't need to cancel the composition explicitly.
2278   if (hasMarkedText_ && markedText_.length()) {
2279     // Sends the updated marked text to the renderer so it can update the
2280     // composition node in WebKit.
2281     // When marked text is available, |markedTextSelectedRange_| will be the
2282     // range being selected inside the marked text.
2283     widgetHost->ImeSetComposition(markedText_, underlines_,
2284                                   markedTextSelectedRange_.location,
2285                                   NSMaxRange(markedTextSelectedRange_));
2286   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2287     if (unmarkTextCalled_) {
2288       widgetHost->ImeConfirmComposition(
2289           base::string16(), gfx::Range::InvalidRange(), false);
2290     } else {
2291       widgetHost->ImeCancelComposition();
2292     }
2293   }
2295   // If the key event was handled by the input method but it also generated some
2296   // edit commands, then we need to send the real key event and corresponding
2297   // edit commands here. This usually occurs when the input method wants to
2298   // finish current composition session but still wants the application to
2299   // handle the key event. See http://crbug.com/48161 for reference.
2300   if (delayEventUntilAfterImeCompostion) {
2301     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2302     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2303     // So before sending the real key down event, we need to send a fake key up
2304     // event to balance it.
2305     NativeWebKeyboardEvent fakeEvent = event;
2306     fakeEvent.type = blink::WebInputEvent::KeyUp;
2307     fakeEvent.skip_in_browser = true;
2308     widgetHost->ForwardKeyboardEvent(fakeEvent);
2309     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2310     // a key event with |skip_in_browser| == true won't be handled by browser,
2311     // thus it won't destroy the widget.
2313     if (!editCommands_.empty()) {
2314       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2315           widgetHost->GetRoutingID(), editCommands_));
2316     }
2317     widgetHost->ForwardKeyboardEvent(event);
2319     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2320     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2321     // be set to NULL. So we check it here and return immediately if it's NULL.
2322     if (!renderWidgetHostView_->render_widget_host_)
2323       return;
2324   }
2326   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2327   // Only send a corresponding key press event if there is no marked text.
2328   if (!hasMarkedText_) {
2329     if (!textInserted && textToBeInserted_.length() == 1) {
2330       // If a single character was inserted, then we just send it as a keypress
2331       // event.
2332       event.type = blink::WebInputEvent::Char;
2333       event.text[0] = textToBeInserted_[0];
2334       event.text[1] = 0;
2335       event.skip_in_browser = true;
2336       widgetHost->ForwardKeyboardEvent(event);
2337     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2338                [[theEvent characters] length] > 0 &&
2339                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2340                 (hasEditCommands_ && editCommands_.empty()))) {
2341       // We don't get insertText: calls if ctrl or cmd is down, or the key event
2342       // generates an insert command. So synthesize a keypress event for these
2343       // cases, unless the key event generated any other command.
2344       event.type = blink::WebInputEvent::Char;
2345       event.skip_in_browser = true;
2346       widgetHost->ForwardKeyboardEvent(event);
2347     }
2348   }
2350   // Possibly autohide the cursor.
2351   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2352     [NSCursor setHiddenUntilMouseMoves:YES];
2355 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2356   DCHECK(base::mac::IsOSLionOrLater());
2358   if ([event phase] != NSEventPhaseEnded &&
2359       [event phase] != NSEventPhaseCancelled) {
2360     return;
2361   }
2363   if (renderWidgetHostView_->render_widget_host_) {
2364     // History-swiping is not possible if the logic reaches this point.
2365     // Allow rubber-banding in both directions.
2366     bool canRubberbandLeft = true;
2367     bool canRubberbandRight = true;
2368     WebMouseWheelEvent webEvent = WebMouseWheelEventBuilder::Build(
2369         event, self, canRubberbandLeft, canRubberbandRight);
2370     webEvent.railsMode = mouseWheelFilter_.UpdateRailsMode(webEvent);
2371     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2372   }
2374   if (endWheelMonitor_) {
2375     [NSEvent removeMonitor:endWheelMonitor_];
2376     endWheelMonitor_ = nil;
2377   }
2380 - (void)beginGestureWithEvent:(NSEvent*)event {
2381   [responderDelegate_ beginGestureWithEvent:event];
2382   gestureBeginEvent_.reset(
2383       new WebGestureEvent(WebGestureEventBuilder::Build(event, self)));
2385   // If the page is at the minimum zoom level, require a threshold be reached
2386   // before the pinch has an effect.
2387   if (renderWidgetHostView_->page_at_minimum_scale_) {
2388     pinchHasReachedZoomThreshold_ = false;
2389     pinchUnusedAmount_ = 1;
2390   }
2393 - (void)endGestureWithEvent:(NSEvent*)event {
2394   [responderDelegate_ endGestureWithEvent:event];
2395   gestureBeginEvent_.reset();
2397   if (!renderWidgetHostView_->render_widget_host_)
2398     return;
2400   if (gestureBeginPinchSent_) {
2401     WebGestureEvent endEvent(WebGestureEventBuilder::Build(event, self));
2402     endEvent.type = WebInputEvent::GesturePinchEnd;
2403     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(endEvent);
2404     gestureBeginPinchSent_ = NO;
2405   }
2408 - (void)touchesMovedWithEvent:(NSEvent*)event {
2409   [responderDelegate_ touchesMovedWithEvent:event];
2412 - (void)touchesBeganWithEvent:(NSEvent*)event {
2413   [responderDelegate_ touchesBeganWithEvent:event];
2416 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2417   [responderDelegate_ touchesCancelledWithEvent:event];
2420 - (void)touchesEndedWithEvent:(NSEvent*)event {
2421   [responderDelegate_ touchesEndedWithEvent:event];
2424 - (void)smartMagnifyWithEvent:(NSEvent*)event {
2425   const WebGestureEvent& smartMagnifyEvent =
2426       WebGestureEventBuilder::Build(event, self);
2427   if (renderWidgetHostView_ && renderWidgetHostView_->render_widget_host_) {
2428     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(
2429         smartMagnifyEvent);
2430   }
2433 // This is invoked only on 10.8 or newer when the user taps a word using
2434 // three fingers.
2435 - (void)quickLookWithEvent:(NSEvent*)event {
2436   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2437   TextInputClientMac::GetInstance()->GetStringAtPoint(
2438       renderWidgetHostView_->render_widget_host_,
2439       gfx::Point(point.x, NSHeight([self frame]) - point.y),
2440       ^(NSAttributedString* string, NSPoint baselinePoint) {
2441           if (string && [string length] > 0) {
2442             dispatch_async(dispatch_get_main_queue(), ^{
2443                 [self showDefinitionForAttributedString:string
2444                                                 atPoint:baselinePoint];
2445             });
2446           }
2447       }
2448   );
2451 // This method handles 2 different types of hardware events.
2452 // (Apple does not distinguish between them).
2453 //  a. Scrolling the middle wheel of a mouse.
2454 //  b. Swiping on the track pad.
2456 // This method is responsible for 2 types of behavior:
2457 //  a. Scrolling the content of window.
2458 //  b. Navigating forwards/backwards in history.
2460 // This is a brief description of the logic:
2461 //  1. If the content can be scrolled, scroll the content.
2462 //     (This requires a roundtrip to blink to determine whether the content
2463 //      can be scrolled.)
2464 //     Once this logic is triggered, the navigate logic cannot be triggered
2465 //     until the gesture finishes.
2466 //  2. If the user is making a horizontal swipe, start the navigate
2467 //     forward/backwards UI.
2468 //     Once this logic is triggered, the user can either cancel or complete
2469 //     the gesture. If the user completes the gesture, all remaining touches
2470 //     are swallowed, and not allowed to scroll the content. If the user
2471 //     cancels the gesture, all remaining touches are forwarded to the content
2472 //     scroll logic. The user cannot trigger the navigation logic again.
2473 - (void)scrollWheel:(NSEvent*)event {
2474   if (responderDelegate_ &&
2475       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2476     BOOL handled = [responderDelegate_ handleEvent:event];
2477     if (handled)
2478       return;
2479   }
2481   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2482   // the event is received even when the mouse cursor is no longer over the view
2483   // when the scrolling ends (e.g. if the tab was switched). This is necessary
2484   // for ending rubber-banding in such cases.
2485   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2486       !endWheelMonitor_) {
2487     endWheelMonitor_ =
2488       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2489       handler:^(NSEvent* blockEvent) {
2490           [self shortCircuitScrollWheelEvent:blockEvent];
2491           return blockEvent;
2492       }];
2493   }
2495   // This is responsible for content scrolling!
2496   if (renderWidgetHostView_->render_widget_host_) {
2497     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2498     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2499     WebMouseWheelEvent webEvent = WebMouseWheelEventBuilder::Build(
2500         event, self, canRubberbandLeft, canRubberbandRight);
2501     webEvent.railsMode = mouseWheelFilter_.UpdateRailsMode(webEvent);
2502     if (renderWidgetHostView_->render_widget_host_->delegate() &&
2503         renderWidgetHostView_->render_widget_host_->delegate()
2504             ->GetInputEventRouter()) {
2505       renderWidgetHostView_->render_widget_host_->delegate()
2506           ->GetInputEventRouter()
2507           ->RouteMouseWheelEvent(renderWidgetHostView_.get(), &webEvent);
2508     } else {
2509       renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2510     }
2511   }
2514 // Called repeatedly during a pinch gesture, with incremental change values.
2515 - (void)magnifyWithEvent:(NSEvent*)event {
2516   if (!renderWidgetHostView_->render_widget_host_)
2517     return;
2519   // If, due to nesting of multiple gestures (e.g, from multiple touch
2520   // devices), the beginning of the gesture has been lost, skip the remainder
2521   // of the gesture.
2522   if (!gestureBeginEvent_)
2523     return;
2525   if (!pinchHasReachedZoomThreshold_) {
2526       pinchUnusedAmount_ *= (1 + [event magnification]);
2527       if (pinchUnusedAmount_ < 0.667 || pinchUnusedAmount_ > 1.5)
2528           pinchHasReachedZoomThreshold_ = true;
2529   }
2531   // Send a GesturePinchBegin event if none has been sent yet.
2532   if (!gestureBeginPinchSent_) {
2533     WebGestureEvent beginEvent(*gestureBeginEvent_);
2534     beginEvent.type = WebInputEvent::GesturePinchBegin;
2535     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(beginEvent);
2536     gestureBeginPinchSent_ = YES;
2537   }
2539   // Send a GesturePinchUpdate event.
2540   WebGestureEvent updateEvent = WebGestureEventBuilder::Build(event, self);
2541   updateEvent.data.pinchUpdate.zoomDisabled = !pinchHasReachedZoomThreshold_;
2542   renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(updateEvent);
2545 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2546   NSWindow* oldWindow = [self window];
2548   NSNotificationCenter* notificationCenter =
2549       [NSNotificationCenter defaultCenter];
2551   // Backing property notifications crash on 10.6 when building with the 10.7
2552   // SDK, see http://crbug.com/260595.
2553   static BOOL supportsBackingPropertiesNotification =
2554       SupportsBackingPropertiesChangedNotification();
2556   if (oldWindow) {
2557     if (supportsBackingPropertiesNotification) {
2558       [notificationCenter
2559           removeObserver:self
2560                     name:NSWindowDidChangeBackingPropertiesNotification
2561                   object:oldWindow];
2562     }
2563     [notificationCenter
2564         removeObserver:self
2565                   name:NSWindowDidMoveNotification
2566                 object:oldWindow];
2567     [notificationCenter
2568         removeObserver:self
2569                   name:NSWindowDidEndLiveResizeNotification
2570                 object:oldWindow];
2571   }
2572   if (newWindow) {
2573     if (supportsBackingPropertiesNotification) {
2574       [notificationCenter
2575           addObserver:self
2576              selector:@selector(windowDidChangeBackingProperties:)
2577                  name:NSWindowDidChangeBackingPropertiesNotification
2578                object:newWindow];
2579     }
2580     [notificationCenter
2581         addObserver:self
2582            selector:@selector(windowChangedGlobalFrame:)
2583                name:NSWindowDidMoveNotification
2584              object:newWindow];
2585     [notificationCenter
2586         addObserver:self
2587            selector:@selector(windowChangedGlobalFrame:)
2588                name:NSWindowDidEndLiveResizeNotification
2589              object:newWindow];
2590   }
2593 - (void)updateScreenProperties{
2594   renderWidgetHostView_->UpdateBackingStoreProperties();
2595   renderWidgetHostView_->UpdateDisplayLink();
2598 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2599 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2600   // Background tabs check if their screen scale factor, color profile, and
2601   // vsync properties changed when they are added to a window.
2603   // Allocating a CGLayerRef with the current scale factor immediately from
2604   // this handler doesn't work. Schedule the backing store update on the
2605   // next runloop cycle, then things are read for CGLayerRef allocations to
2606   // work.
2607   [self performSelector:@selector(updateScreenProperties)
2608              withObject:nil
2609              afterDelay:0];
2612 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2613   renderWidgetHostView_->UpdateScreenInfo(
2614       renderWidgetHostView_->GetNativeView());
2617 - (void)setFrameSize:(NSSize)newSize {
2618   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2620   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2621   // -setFrame: isn't neccessary.
2622   [super setFrameSize:newSize];
2624   if (!renderWidgetHostView_->render_widget_host_)
2625     return;
2627   renderWidgetHostView_->render_widget_host_->SendScreenRects();
2628   renderWidgetHostView_->render_widget_host_->WasResized();
2629   if (renderWidgetHostView_->delegated_frame_host_)
2630     renderWidgetHostView_->delegated_frame_host_->WasResized();
2632   // Wait for the frame that WasResize might have requested. If the view is
2633   // being made visible at a new size, then this call will have no effect
2634   // because the view widget is still hidden, and the pause call in WasShown
2635   // will have this effect for us.
2636   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2639 - (BOOL)canBecomeKeyView {
2640   if (!renderWidgetHostView_->render_widget_host_)
2641     return NO;
2643   return canBeKeyView_;
2646 - (BOOL)acceptsFirstResponder {
2647   if (!renderWidgetHostView_->render_widget_host_)
2648     return NO;
2650   return canBeKeyView_;
2653 - (BOOL)becomeFirstResponder {
2654   if (!renderWidgetHostView_->render_widget_host_)
2655     return NO;
2657   renderWidgetHostView_->render_widget_host_->Focus();
2658   renderWidgetHostView_->SetTextInputActive(true);
2660   // Cancel any onging composition text which was left before we lost focus.
2661   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2662   // somehow that method won't be called when switching among different tabs.
2663   // See http://crbug.com/47209
2664   [self cancelComposition];
2666   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2667       [[self window] keyViewSelectionDirection]];
2668   NSDictionary* userInfo =
2669       [NSDictionary dictionaryWithObject:direction
2670                                   forKey:kSelectionDirection];
2671   [[NSNotificationCenter defaultCenter]
2672       postNotificationName:kViewDidBecomeFirstResponder
2673                     object:self
2674                   userInfo:userInfo];
2676   return YES;
2679 - (BOOL)resignFirstResponder {
2680   renderWidgetHostView_->SetTextInputActive(false);
2681   if (!renderWidgetHostView_->render_widget_host_)
2682     return YES;
2684   if (closeOnDeactivate_)
2685     renderWidgetHostView_->KillSelf();
2687   renderWidgetHostView_->render_widget_host_->Blur();
2689   // We should cancel any onging composition whenever RWH's Blur() method gets
2690   // called, because in this case, webkit will confirm the ongoing composition
2691   // internally.
2692   [self cancelComposition];
2694   return YES;
2697 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2698   if (responderDelegate_ &&
2699       [responderDelegate_
2700           respondsToSelector:@selector(validateUserInterfaceItem:
2701                                                      isValidItem:)]) {
2702     BOOL valid;
2703     BOOL known =
2704         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2705     if (known)
2706       return valid;
2707   }
2709   SEL action = [item action];
2711   if (action == @selector(stopSpeaking:)) {
2712     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2713            renderWidgetHostView_->IsSpeaking();
2714   }
2715   if (action == @selector(startSpeaking:)) {
2716     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2717            renderWidgetHostView_->SupportsSpeech();
2718   }
2720   // For now, these actions are always enabled for render view,
2721   // this is sub-optimal.
2722   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2723   if (action == @selector(undo:) ||
2724       action == @selector(redo:) ||
2725       action == @selector(cut:) ||
2726       action == @selector(copy:) ||
2727       action == @selector(copyToFindPboard:) ||
2728       action == @selector(paste:) ||
2729       action == @selector(pasteAndMatchStyle:)) {
2730     return renderWidgetHostView_->render_widget_host_->IsRenderView();
2731   }
2733   return editCommand_helper_->IsMenuItemEnabled(action, self);
2736 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2737   return renderWidgetHostView_.get();
2740 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2741 // move) for the given event. Customize here to be more selective about which
2742 // key presses to autohide on.
2743 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2744   return ([event type] == NSKeyDown &&
2745              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2748 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2749                                          index:(NSUInteger)index
2750                                       maxCount:(NSUInteger)maxCount {
2751   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2752   NSUInteger totalLength = [fullArray count];
2753   if (index >= totalLength)
2754     return nil;
2755   NSUInteger length = MIN(totalLength - index, maxCount);
2756   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2759 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2760   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2761   return [fullArray count];
2764 - (id)accessibilityAttributeValue:(NSString *)attribute {
2765   BrowserAccessibilityManager* manager =
2766       renderWidgetHostView_->render_widget_host_
2767           ->GetRootBrowserAccessibilityManager();
2769   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2770   // BrowserAccessibilityManager. Children includes all subviews in addition to
2771   // contents. Currently we do not have subviews besides the document view.
2772   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2773           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2774       manager) {
2775     return [NSArray arrayWithObjects:manager->
2776         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2777   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2778     return NSAccessibilityScrollAreaRole;
2779   }
2780   id ret = [super accessibilityAttributeValue:attribute];
2781   return ret;
2784 - (NSArray*)accessibilityAttributeNames {
2785   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2786   [ret addObject:NSAccessibilityContentsAttribute];
2787   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2788   return ret;
2791 - (id)accessibilityHitTest:(NSPoint)point {
2792   BrowserAccessibilityManager* manager =
2793       renderWidgetHostView_->render_widget_host_
2794           ->GetRootBrowserAccessibilityManager();
2795   if (!manager)
2796     return self;
2797   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2798   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2799   localPoint.y = NSHeight([self bounds]) - localPoint.y;
2800   BrowserAccessibilityCocoa* root =
2801       manager->GetRoot()->ToBrowserAccessibilityCocoa();
2802   id obj = [root accessibilityHitTest:localPoint];
2803   return obj;
2806 - (BOOL)accessibilityIsIgnored {
2807   BrowserAccessibilityManager* manager =
2808       renderWidgetHostView_->render_widget_host_
2809           ->GetRootBrowserAccessibilityManager();
2810   return !manager;
2813 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2814   BrowserAccessibilityManager* manager =
2815       renderWidgetHostView_->render_widget_host_
2816           ->GetRootBrowserAccessibilityManager();
2817   // Only child is root.
2818   if (manager &&
2819       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2820     return 0;
2821   } else {
2822     return NSNotFound;
2823   }
2826 - (id)accessibilityFocusedUIElement {
2827   BrowserAccessibilityManager* manager =
2828       renderWidgetHostView_->render_widget_host_
2829           ->GetRootBrowserAccessibilityManager();
2830   if (manager) {
2831     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2832     DCHECK(focused_item);
2833     if (focused_item) {
2834       BrowserAccessibilityCocoa* focused_item_cocoa =
2835           focused_item->ToBrowserAccessibilityCocoa();
2836       DCHECK(focused_item_cocoa);
2837       if (focused_item_cocoa)
2838         return focused_item_cocoa;
2839     }
2840   }
2841   return [super accessibilityFocusedUIElement];
2844 // Below is our NSTextInputClient implementation.
2846 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2847 // functions to process this event.
2849 // [WebHTMLView keyDown] ->
2850 //     EventHandler::keyEvent() ->
2851 //     ...
2852 //     [WebEditorClient handleKeyboardEvent] ->
2853 //     [WebHTMLView _interceptEditingKeyEvent] ->
2854 //     [NSResponder interpretKeyEvents] ->
2855 //     [WebHTMLView insertText] ->
2856 //     Editor::insertText()
2858 // Unfortunately, it is hard for Chromium to use this implementation because
2859 // it causes key-typing jank.
2860 // RenderWidgetHostViewMac is running in a browser process. On the other
2861 // hand, Editor and EventHandler are running in a renderer process.
2862 // So, if we used this implementation, a NSKeyDown event is dispatched to
2863 // the following functions of Chromium.
2865 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2866 //     |Sync IPC (KeyDown)| (*1) ->
2867 //     EventHandler::keyEvent() (renderer) ->
2868 //     ...
2869 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
2870 //     |Sync IPC| (*2) ->
2871 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2872 //     [self interpretKeyEvents] ->
2873 //     [RenderWidgetHostViewMac insertText] (browser) ->
2874 //     |Async IPC| ->
2875 //     Editor::insertText() (renderer)
2877 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2878 // result of EventHandler::keyEvent().
2879 // (*2) we need to wait until this call finishes since WebEditorClient uses
2880 // the result of [WebHTMLView _interceptEditingKeyEvent].
2882 // This needs many sync IPC messages sent between a browser and a renderer for
2883 // each key event, which would probably result in key-typing jank.
2884 // To avoid this problem, this implementation processes key events (and input
2885 // method events) totally in a browser process and sends asynchronous input
2886 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2887 // renderer process.
2889 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2890 //     |Async IPC (RawKeyDown)| ->
2891 //     [self interpretKeyEvents] ->
2892 //     [RenderWidgetHostViewMac insertText] (browser) ->
2893 //     |Async IPC (Char)| ->
2894 //     Editor::insertText() (renderer)
2896 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2897 // make any key-typing jank. --hbono 7/23/09
2899 extern "C" {
2900 extern NSString *NSTextInputReplacementRangeAttributeName;
2903 - (NSArray *)validAttributesForMarkedText {
2904   // This code is just copied from WebKit except renaming variables.
2905   if (!validAttributesForMarkedText_) {
2906     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2907         NSUnderlineStyleAttributeName,
2908         NSUnderlineColorAttributeName,
2909         NSMarkedClauseSegmentAttributeName,
2910         NSTextInputReplacementRangeAttributeName,
2911         nil]);
2912   }
2913   return validAttributesForMarkedText_.get();
2916 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2917   DCHECK([self window]);
2918   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2919   // coordinates (upper left origin). Scroll offsets will be taken care of in
2920   // the renderer.
2921   thePoint = [[self window] convertScreenToBase:thePoint];
2922   thePoint = [self convertPoint:thePoint fromView:nil];
2923   thePoint.y = NSHeight([self frame]) - thePoint.y;
2925   NSUInteger index =
2926       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2927           renderWidgetHostView_->render_widget_host_,
2928           gfx::Point(thePoint.x, thePoint.y));
2929   return index;
2932 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2933                              actualRange:(NSRangePointer)actualRange {
2934   NSRect rect;
2935   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2936           theRange,
2937           &rect,
2938           actualRange)) {
2939     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2940         renderWidgetHostView_->render_widget_host_, theRange);
2942     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2943     if (actualRange)
2944       *actualRange = theRange;
2945   }
2947   // The returned rectangle is in WebKit coordinates (upper left origin), so
2948   // flip the coordinate system.
2949   NSRect viewFrame = [self frame];
2950   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2951   return rect;
2954 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2955                          actualRange:(NSRangePointer)actualRange {
2956   NSRect rect = [self firstViewRectForCharacterRange:theRange
2957                                          actualRange:actualRange];
2959   // Convert into screen coordinates for return.
2960   rect = [self convertRect:rect toView:nil];
2961   rect.origin = [[self window] convertBaseToScreen:rect.origin];
2962   return rect;
2965 - (NSRange)markedRange {
2966   // An input method calls this method to check if an application really has
2967   // a text being composed when hasMarkedText call returns true.
2968   // Returns the range saved in the setMarkedText method so the input method
2969   // calls the setMarkedText method and we can update the composition node
2970   // there. (When this method returns an empty range, the input method doesn't
2971   // call the setMarkedText method.)
2972   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2975 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2976     actualRange:(NSRangePointer)actualRange {
2977   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2978   if (actualRange)
2979     *actualRange = range;
2980   NSAttributedString* str =
2981       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2982           renderWidgetHostView_->render_widget_host_, range);
2983   return str;
2986 - (NSInteger)conversationIdentifier {
2987   return reinterpret_cast<NSInteger>(self);
2990 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2991 // nil when the caret is in non-editable content or password box to avoid
2992 // making input methods do their work.
2993 - (NSTextInputContext *)inputContext {
2994   if (focusedPluginIdentifier_ != -1)
2995     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2997   switch(renderWidgetHostView_->text_input_type_) {
2998     case ui::TEXT_INPUT_TYPE_NONE:
2999     case ui::TEXT_INPUT_TYPE_PASSWORD:
3000       return nil;
3001     default:
3002       return [super inputContext];
3003   }
3006 - (BOOL)hasMarkedText {
3007   // An input method calls this function to figure out whether or not an
3008   // application is really composing a text. If it is composing, it calls
3009   // the markedRange method, and maybe calls the setMarkedText method.
3010   // It seems an input method usually calls this function when it is about to
3011   // cancel an ongoing composition. If an application has a non-empty marked
3012   // range, it calls the setMarkedText method to delete the range.
3013   return hasMarkedText_;
3016 - (void)unmarkText {
3017   // Delete the composition node of the renderer and finish an ongoing
3018   // composition.
3019   // It seems an input method calls the setMarkedText method and set an empty
3020   // text when it cancels an ongoing composition, i.e. I have never seen an
3021   // input method calls this method.
3022   hasMarkedText_ = NO;
3023   markedText_.clear();
3024   markedTextSelectedRange_ = NSMakeRange(NSNotFound, 0);
3025   underlines_.clear();
3027   // If we are handling a key down event, then ConfirmComposition() will be
3028   // called in keyEvent: method.
3029   if (!handlingKeyDown_) {
3030     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3031         base::string16(), gfx::Range::InvalidRange(), false);
3032   } else {
3033     unmarkTextCalled_ = YES;
3034   }
3037 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
3038                               replacementRange:(NSRange)replacementRange {
3039   // An input method updates the composition string.
3040   // We send the given text and range to the renderer so it can update the
3041   // composition node of WebKit.
3042   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3043   // the full web content.
3044   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3045   NSString* im_text = isAttributedString ? [string string] : string;
3046   int length = [im_text length];
3048   // |markedRange_| will get set on a callback from ImeSetComposition().
3049   markedTextSelectedRange_ = newSelRange;
3050   markedText_ = base::SysNSStringToUTF16(im_text);
3051   hasMarkedText_ = (length > 0);
3053   underlines_.clear();
3054   if (isAttributedString) {
3055     ExtractUnderlines(string, &underlines_);
3056   } else {
3057     // Use a thin black underline by default.
3058     underlines_.push_back(blink::WebCompositionUnderline(
3059         0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
3060   }
3062   // If we are handling a key down event, then SetComposition() will be
3063   // called in keyEvent: method.
3064   // Input methods of Mac use setMarkedText calls with an empty text to cancel
3065   // an ongoing composition. So, we should check whether or not the given text
3066   // is empty to update the input method state. (Our input method backend can
3067   // automatically cancels an ongoing composition when we send an empty text.
3068   // So, it is OK to send an empty text to the renderer.)
3069   if (!handlingKeyDown_) {
3070     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3071         markedText_, underlines_,
3072         newSelRange.location, NSMaxRange(newSelRange));
3073   }
3076 - (void)doCommandBySelector:(SEL)selector {
3077   // An input method calls this function to dispatch an editing command to be
3078   // handled by this view.
3079   if (selector == @selector(noop:))
3080     return;
3082   std::string command(
3083       [RenderWidgetHostViewMacEditCommandHelper::
3084           CommandNameForSelector(selector) UTF8String]);
3086   // If this method is called when handling a key down event, then we need to
3087   // handle the command in the key event handler. Otherwise we can just handle
3088   // it here.
3089   if (handlingKeyDown_) {
3090     hasEditCommands_ = YES;
3091     // We ignore commands that insert characters, because this was causing
3092     // strange behavior (e.g. tab always inserted a tab rather than moving to
3093     // the next field on the page).
3094     if (!base::StartsWith(command, "insert",
3095                           base::CompareCase::INSENSITIVE_ASCII))
3096       editCommands_.push_back(EditCommand(command, ""));
3097   } else {
3098     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3099     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3100                                               command, ""));
3101   }
3104 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3105   // An input method has characters to be inserted.
3106   // Same as Linux, Mac calls this method not only:
3107   // * when an input method finishs composing text, but also;
3108   // * when we type an ASCII character (without using input methods).
3109   // When we aren't using input methods, we should send the given character as
3110   // a Char event so it is dispatched to an onkeypress() event handler of
3111   // JavaScript.
3112   // On the other hand, when we are using input methods, we should send the
3113   // given characters as an input method event and prevent the characters from
3114   // being dispatched to onkeypress() event handlers.
3115   // Text inserting might be initiated by other source instead of keyboard
3116   // events, such as the Characters dialog. In this case the text should be
3117   // sent as an input method event as well.
3118   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3119   // the full web content.
3120   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3121   NSString* im_text = isAttributedString ? [string string] : string;
3122   if (handlingKeyDown_) {
3123     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3124   } else {
3125     gfx::Range replacement_range(replacementRange);
3126     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3127         base::SysNSStringToUTF16(im_text), replacement_range, false);
3128   }
3130   // Inserting text will delete all marked text automatically.
3131   hasMarkedText_ = NO;
3134 - (void)insertText:(id)string {
3135   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3138 - (void)viewDidMoveToWindow {
3139   if ([self window]) {
3140     [self updateScreenProperties];
3141   } else {
3142     // If the RenderWidgetHostViewCocoa is being removed from its window, tear
3143     // down its browser compositor resources, if needed.
3144     renderWidgetHostView_->DestroySuspendedBrowserCompositorViewIfNeeded();
3145   }
3147   if (canBeKeyView_) {
3148     NSWindow* newWindow = [self window];
3149     // Pointer comparison only, since we don't know if lastWindow_ is still
3150     // valid.
3151     if (newWindow) {
3152       // If we move into a new window, refresh the frame information. We
3153       // don't need to do it if it was the same window as it used to be in,
3154       // since that case is covered by WasShown(). We only want to do this for
3155       // real browser views, not popups.
3156       if (newWindow != lastWindow_) {
3157         lastWindow_ = newWindow;
3158         renderWidgetHostView_->WindowFrameChanged();
3159       }
3160     }
3161   }
3163   // If we switch windows (or are removed from the view hierarchy), cancel any
3164   // open mouse-downs.
3165   if (hasOpenMouseDown_) {
3166     WebMouseEvent event;
3167     event.type = WebInputEvent::MouseUp;
3168     event.button = WebMouseEvent::ButtonLeft;
3169     renderWidgetHostView_->ForwardMouseEvent(event);
3171     hasOpenMouseDown_ = NO;
3172   }
3175 - (void)undo:(id)sender {
3176   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3177   if (web_contents)
3178     web_contents->Undo();
3181 - (void)redo:(id)sender {
3182   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3183   if (web_contents)
3184     web_contents->Redo();
3187 - (void)cut:(id)sender {
3188   content::RenderWidgetHostDelegate* render_widget_host_delegate =
3189       renderWidgetHostView_->render_widget_host_->delegate();
3190   if (render_widget_host_delegate)
3191     render_widget_host_delegate->Cut();
3194 - (void)copy:(id)sender {
3195   content::RenderWidgetHostDelegate* render_widget_host_delegate =
3196       renderWidgetHostView_->render_widget_host_->delegate();
3197   if (render_widget_host_delegate)
3198     render_widget_host_delegate->Copy();
3201 - (void)copyToFindPboard:(id)sender {
3202   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3203   if (web_contents)
3204     web_contents->CopyToFindPboard();
3207 - (void)paste:(id)sender {
3208   content::RenderWidgetHostDelegate* render_widget_host_delegate =
3209       renderWidgetHostView_->render_widget_host_->delegate();
3210   if (render_widget_host_delegate)
3211     render_widget_host_delegate->Paste();
3214 - (void)pasteAndMatchStyle:(id)sender {
3215   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3216   if (web_contents)
3217     web_contents->PasteAndMatchStyle();
3220 - (void)selectAll:(id)sender {
3221   // editCommand_helper_ adds implementations for most NSResponder methods
3222   // dynamically. But the renderer side only sends selection results back to
3223   // the browser if they were triggered by a keyboard event or went through
3224   // one of the Select methods on RWH. Since selectAll: is called from the
3225   // menu handler, neither is true.
3226   // Explicitly call SelectAll() here to make sure the renderer returns
3227   // selection results.
3228   content::RenderWidgetHostDelegate* render_widget_host_delegate =
3229       renderWidgetHostView_->render_widget_host_->delegate();
3230   if (render_widget_host_delegate)
3231     render_widget_host_delegate->SelectAll();
3234 - (void)startSpeaking:(id)sender {
3235   GetRenderWidgetHostViewToUse(renderWidgetHostView_.get())->SpeakSelection();
3238 - (void)stopSpeaking:(id)sender {
3239   GetRenderWidgetHostViewToUse(renderWidgetHostView_.get())->StopSpeaking();
3242 - (void)cancelComposition {
3243   if (!hasMarkedText_)
3244     return;
3246   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3247   // doesn't call any NSTextInput functions, such as setMarkedText or
3248   // insertText. So, we need to send an IPC message to a renderer so it can
3249   // delete the composition node.
3250   // TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
3251   // NSTextInputContext. http://www.crbug.com/479010.
3252 #pragma clang diagnostic push
3253 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3254   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3255   [currentInputManager markedTextAbandoned:self];
3256 #pragma clang diagnostic pop
3258   hasMarkedText_ = NO;
3259   // Should not call [self unmarkText] here, because it'll send unnecessary
3260   // cancel composition IPC message to the renderer.
3263 - (void)confirmComposition {
3264   if (!hasMarkedText_)
3265     return;
3267   if (renderWidgetHostView_->render_widget_host_)
3268     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3269         base::string16(), gfx::Range::InvalidRange(), false);
3271   [self cancelComposition];
3274 - (void)setPluginImeActive:(BOOL)active {
3275   if (active == pluginImeActive_)
3276     return;
3278   pluginImeActive_ = active;
3279   if (!active) {
3280     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3281     renderWidgetHostView_->PluginImeCompositionCompleted(
3282         base::string16(), focusedPluginIdentifier_);
3283   }
3286 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3287   if (focused)
3288     focusedPluginIdentifier_ = pluginId;
3289   else if (focusedPluginIdentifier_ == pluginId)
3290     focusedPluginIdentifier_ = -1;
3292   // Whenever plugin focus changes, plugin IME resets.
3293   [self setPluginImeActive:NO];
3296 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3297   if (!pluginImeActive_)
3298     return false;
3300   ComplexTextInputPanel* inputPanel =
3301       [ComplexTextInputPanel sharedComplexTextInputPanel];
3302   NSString* composited_string = nil;
3303   BOOL handled = [inputPanel interpretKeyEvent:event
3304                                         string:&composited_string];
3305   if (composited_string) {
3306     renderWidgetHostView_->PluginImeCompositionCompleted(
3307         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3308     pluginImeActive_ = NO;
3309   }
3310   return handled;
3313 - (void)checkForPluginImeCancellation {
3314   if (pluginImeActive_ &&
3315       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3316     renderWidgetHostView_->PluginImeCompositionCompleted(
3317         base::string16(), focusedPluginIdentifier_);
3318     pluginImeActive_ = NO;
3319   }
3322 // Overriding a NSResponder method to support application services.
3324 - (id)validRequestorForSendType:(NSString*)sendType
3325                      returnType:(NSString*)returnType {
3326   id requestor = nil;
3327   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3328   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3329   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3330   BOOL takesText =
3331       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3333   if (sendTypeIsString && hasText && !returnType) {
3334     requestor = self;
3335   } else if (!sendType && returnTypeIsString && takesText) {
3336     requestor = self;
3337   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3338     requestor = self;
3339   } else {
3340     requestor = [super validRequestorForSendType:sendType
3341                                       returnType:returnType];
3342   }
3343   return requestor;
3346 - (void)viewWillStartLiveResize {
3347   [super viewWillStartLiveResize];
3348   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3349   if (widget)
3350     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3353 - (void)viewDidEndLiveResize {
3354   [super viewDidEndLiveResize];
3355   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3356   if (widget)
3357     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3360 - (void)updateCursor:(NSCursor*)cursor {
3361   if (currentCursor_ == cursor)
3362     return;
3364   currentCursor_.reset([cursor retain]);
3365   [[self window] invalidateCursorRectsForView:self];
3368 - (void)popupWindowWillClose:(NSNotification *)notification {
3369   renderWidgetHostView_->KillSelf();
3372 @end
3375 // Supporting application services
3377 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3379 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3380                              types:(NSArray*)types {
3381   const std::string& str = renderWidgetHostView_->selected_text();
3382   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3384   base::scoped_nsobject<NSString> text(
3385       [[NSString alloc] initWithUTF8String:str.c_str()]);
3386   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3387   [pboard declareTypes:toDeclare owner:nil];
3388   return [pboard setString:text forType:NSStringPboardType];
3391 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3392   NSString *string = [pboard stringForType:NSStringPboardType];
3393   if (!string) return NO;
3395   // If the user is currently using an IME, confirm the IME input,
3396   // and then insert the text from the service, the same as TextEdit and Safari.
3397   [self confirmComposition];
3398   [self insertText:string];
3399   return YES;
3402 - (BOOL)isOpaque {
3403   return opaque_;
3406 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3407 // regions that are not draggable. (See ControlRegionView in
3408 // native_app_window_cocoa.mm). This requires the render host view to be
3409 // draggable by default.
3410 - (BOOL)mouseDownCanMoveWindow {
3411   return YES;
3414 @end