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