Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac.mm
blob4ebbd6291d3290e77da750fb3e2c1fffb6137e85
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/resize_lock.h"
34 #include "content/browser/frame_host/frame_tree.h"
35 #include "content/browser/frame_host/frame_tree_node.h"
36 #include "content/browser/frame_host/render_frame_host_impl.h"
37 #include "content/browser/gpu/compositor_util.h"
38 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
39 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
40 #include "content/browser/renderer_host/compositing_iosurface_mac.h"
41 #include "content/browser/renderer_host/render_widget_helper.h"
42 #include "content/browser/renderer_host/render_view_host_impl.h"
43 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
44 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
45 #import "content/browser/renderer_host/software_layer_mac.h"
46 #import "content/browser/renderer_host/text_input_client_mac.h"
47 #include "content/common/accessibility_messages.h"
48 #include "content/common/edit_command.h"
49 #include "content/common/gpu/gpu_messages.h"
50 #include "content/common/gpu/surface_handle_types_mac.h"
51 #include "content/common/input_messages.h"
52 #include "content/common/view_messages.h"
53 #include "content/common/webplugin_geometry.h"
54 #include "content/public/browser/browser_thread.h"
55 #include "content/public/browser/native_web_keyboard_event.h"
56 #include "content/public/browser/notification_service.h"
57 #include "content/public/browser/notification_types.h"
58 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
59 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
60 #include "content/public/browser/user_metrics.h"
61 #include "content/public/browser/web_contents.h"
62 #include "skia/ext/platform_canvas.h"
63 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
64 #include "third_party/WebKit/public/web/WebInputEvent.h"
65 #include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
66 #import "third_party/mozilla/ComplexTextInputPanel.h"
67 #include "ui/base/cocoa/animation_utils.h"
68 #import "ui/base/cocoa/fullscreen_window_manager.h"
69 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
70 #include "ui/events/keycodes/keyboard_codes.h"
71 #include "ui/base/layout.h"
72 #include "ui/compositor/compositor.h"
73 #include "ui/compositor/layer.h"
74 #include "ui/gfx/display.h"
75 #include "ui/gfx/frame_time.h"
76 #include "ui/gfx/point.h"
77 #include "ui/gfx/rect_conversions.h"
78 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
79 #include "ui/gfx/screen.h"
80 #include "ui/gfx/size_conversions.h"
81 #include "ui/gl/gl_switches.h"
83 using content::BrowserAccessibility;
84 using content::BrowserAccessibilityManager;
85 using content::EditCommand;
86 using content::FrameTreeNode;
87 using content::NativeWebKeyboardEvent;
88 using content::RenderFrameHost;
89 using content::RenderViewHost;
90 using content::RenderViewHostImpl;
91 using content::RenderWidgetHostImpl;
92 using content::RenderWidgetHostViewMac;
93 using content::RenderWidgetHostViewMacEditCommandHelper;
94 using content::TextInputClientMac;
95 using content::WebContents;
96 using blink::WebInputEvent;
97 using blink::WebInputEventFactory;
98 using blink::WebMouseEvent;
99 using blink::WebMouseWheelEvent;
100 using blink::WebGestureEvent;
102 namespace {
104 // Whether a keyboard event has been reserved by OSX.
105 BOOL EventIsReservedBySystem(NSEvent* event) {
106   content::SystemHotkeyHelperMac* helper =
107       content::SystemHotkeyHelperMac::GetInstance();
108   return helper->map()->IsEventReserved(event);
111 }  // namespace
113 // These are not documented, so use only after checking -respondsToSelector:.
114 @interface NSApplication (UndocumentedSpeechMethods)
115 - (void)speakString:(NSString*)string;
116 - (void)stopSpeaking:(id)sender;
117 - (BOOL)isSpeaking;
118 @end
120 // Declare things that are part of the 10.7 SDK.
121 #if !defined(MAC_OS_X_VERSION_10_7) || \
122     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
124 static NSString* const NSWindowDidChangeBackingPropertiesNotification =
125     @"NSWindowDidChangeBackingPropertiesNotification";
127 #endif  // 10.7
129 // This method will return YES for OS X versions 10.7.3 and later, and NO
130 // otherwise.
131 // Used to prevent a crash when building with the 10.7 SDK and accessing the
132 // notification below. See: http://crbug.com/260595.
133 static BOOL SupportsBackingPropertiesChangedNotification() {
134   // windowDidChangeBackingProperties: method has been added to the
135   // NSWindowDelegate protocol in 10.7.3, at the same time as the
136   // NSWindowDidChangeBackingPropertiesNotification notification was added.
137   // If the protocol contains this method description, the notification should
138   // be supported as well.
139   Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
140   struct objc_method_description methodDescription =
141       protocol_getMethodDescription(
142           windowDelegateProtocol,
143           @selector(windowDidChangeBackingProperties:),
144           NO,
145           YES);
147   // If the protocol does not contain the method, the returned method
148   // description is {NULL, NULL}
149   return methodDescription.name != NULL || methodDescription.types != NULL;
152 // Private methods:
153 @interface RenderWidgetHostViewCocoa ()
154 @property(nonatomic, assign) NSRange selectedRange;
155 @property(nonatomic, assign) NSRange markedRange;
157 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
158 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
159 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
160                    consumed:(BOOL)consumed;
162 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
163 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
164 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
165 - (void)checkForPluginImeCancellation;
166 - (void)updateScreenProperties;
167 - (void)setResponderDelegate:
168         (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
169 @end
171 // A window subclass that allows the fullscreen window to become main and gain
172 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
173 // handled by the browser.
174 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
175 @end
177 @implementation PepperFlashFullscreenWindow
179 - (BOOL)canBecomeKeyWindow {
180   return YES;
183 - (BOOL)canBecomeMainWindow {
184   return YES;
187 @end
189 @interface RenderWidgetPopupWindow : NSWindow {
190    // The event tap that allows monitoring of all events, to properly close with
191    // a click outside the bounds of the window.
192   id clickEventTap_;
194 @end
196 @implementation RenderWidgetPopupWindow
198 - (id)initWithContentRect:(NSRect)contentRect
199                 styleMask:(NSUInteger)windowStyle
200                   backing:(NSBackingStoreType)bufferingType
201                     defer:(BOOL)deferCreation {
202   if (self = [super initWithContentRect:contentRect
203                               styleMask:windowStyle
204                                 backing:bufferingType
205                                   defer:deferCreation]) {
206     [self setOpaque:NO];
207     [self setBackgroundColor:[NSColor clearColor]];
208     [self startObservingClicks];
209   }
210   return self;
213 - (void)close {
214   [self stopObservingClicks];
215   [super close];
218 // Gets called when the menubar is clicked.
219 // Needed because the local event monitor doesn't see the click on the menubar.
220 - (void)beganTracking:(NSNotification*)notification {
221   [self close];
224 // Install the callback.
225 - (void)startObservingClicks {
226   clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
227       handler:^NSEvent* (NSEvent* event) {
228           if ([event window] == self)
229             return event;
230           NSEventType eventType = [event type];
231           if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
232             [self close];
233           return event;
234   }];
236   NSNotificationCenter* notificationCenter =
237       [NSNotificationCenter defaultCenter];
238   [notificationCenter addObserver:self
239          selector:@selector(beganTracking:)
240              name:NSMenuDidBeginTrackingNotification
241            object:[NSApp mainMenu]];
244 // Remove the callback.
245 - (void)stopObservingClicks {
246   if (!clickEventTap_)
247     return;
249   [NSEvent removeMonitor:clickEventTap_];
250    clickEventTap_ = nil;
252   NSNotificationCenter* notificationCenter =
253       [NSNotificationCenter defaultCenter];
254   [notificationCenter removeObserver:self
255                 name:NSMenuDidBeginTrackingNotification
256               object:[NSApp mainMenu]];
259 @end
261 namespace {
263 // Maximum number of characters we allow in a tooltip.
264 const size_t kMaxTooltipLength = 1024;
266 // TODO(suzhe): Upstream this function.
267 blink::WebColor WebColorFromNSColor(NSColor *color) {
268   CGFloat r, g, b, a;
269   [color getRed:&r green:&g blue:&b alpha:&a];
271   return
272       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
273       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
274       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
275       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
278 // Extract underline information from an attributed string. Mostly copied from
279 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
280 void ExtractUnderlines(
281     NSAttributedString* string,
282     std::vector<blink::WebCompositionUnderline>* underlines) {
283   int length = [[string string] length];
284   int i = 0;
285   while (i < length) {
286     NSRange range;
287     NSDictionary* attrs = [string attributesAtIndex:i
288                               longestEffectiveRange:&range
289                                             inRange:NSMakeRange(i, length - i)];
290     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
291       blink::WebColor color = SK_ColorBLACK;
292       if (NSColor *colorAttr =
293           [attrs objectForKey:NSUnderlineColorAttributeName]) {
294         color = WebColorFromNSColor(
295             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
296       }
297       underlines->push_back(
298           blink::WebCompositionUnderline(range.location,
299                                          NSMaxRange(range),
300                                          color,
301                                          [style intValue] > 1,
302                                          SK_ColorTRANSPARENT));
303     }
304     i = range.location + range.length;
305   }
308 // EnablePasswordInput() and DisablePasswordInput() are copied from
309 // enableSecureTextInput() and disableSecureTextInput() functions in
310 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
311 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
312 // here, because they are already called in webkit and they are system wide
313 // functions.
314 void EnablePasswordInput() {
315   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
316   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
317                          sizeof(CFArrayRef), &inputSources);
318   CFRelease(inputSources);
321 void DisablePasswordInput() {
322   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
325 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
326 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
327 // value when screen info changes.
328 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
329 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
330 // is necessary.
331 bool g_screen_info_up_to_date = false;
333 float FlipYFromRectToScreen(float y, float rect_height) {
334   TRACE_EVENT0("browser", "FlipYFromRectToScreen");
335   static CGFloat screen_zero_height = 0;
336   if (!g_screen_info_up_to_date) {
337     if ([[NSScreen screens] count] > 0) {
338       screen_zero_height =
339           [[[NSScreen screens] objectAtIndex:0] frame].size.height;
340       g_screen_info_up_to_date = true;
341     } else {
342       return y;
343     }
344   }
345   return screen_zero_height - y - rect_height;
348 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
349 // left of the primary screen (Carbon coordinates), and stuffs it into a
350 // gfx::Rect.
351 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
352   gfx::Rect new_rect(NSRectToCGRect(rect));
353   new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
354   return new_rect;
357 // Returns the window that visually contains the given view. This is different
358 // from [view window] in the case of tab dragging, where the view's owning
359 // window is a floating panel attached to the actual browser window that the tab
360 // is visually part of.
361 NSWindow* ApparentWindowForView(NSView* view) {
362   // TODO(shess): In case of !window, the view has been removed from
363   // the view hierarchy because the tab isn't main.  Could retrieve
364   // the information from the main tab for our window.
365   NSWindow* enclosing_window = [view window];
367   // See if this is a tab drag window. The width check is to distinguish that
368   // case from extension popup windows.
369   NSWindow* ancestor_window = [enclosing_window parentWindow];
370   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
371                           NSWidth([ancestor_window frame]))) {
372     enclosing_window = ancestor_window;
373   }
375   return enclosing_window;
378 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
379   gfx::Display display =
380       gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
382   NSScreen* screen = [NSScreen deepestScreen];
384   blink::WebScreenInfo results;
386   results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
387   results.depth = NSBitsPerPixelFromDepth([screen depth]);
388   results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
389   results.isMonochrome =
390       [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
391   results.rect = display.bounds();
392   results.availableRect = display.work_area();
393   results.orientationAngle = display.RotationAsDegree();
394   results.orientationType =
395       content::RenderWidgetHostViewBase::GetOrientationTypeForDesktop(display);
397   return results;
400 }  // namespace
402 namespace content {
404 ////////////////////////////////////////////////////////////////////////////////
405 // DelegatedFrameHost, public:
407 ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
408   if (browser_compositor_view_)
409     return browser_compositor_view_->GetCompositor();
410   return NULL;
413 ui::Layer* RenderWidgetHostViewMac::GetLayer() {
414   return root_layer_.get();
417 RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
418   return render_widget_host_;
421 bool RenderWidgetHostViewMac::IsVisible() {
422   return !render_widget_host_->is_hidden();
425 gfx::Size RenderWidgetHostViewMac::DesiredFrameSize() {
426   return GetViewBounds().size();
429 float RenderWidgetHostViewMac::CurrentDeviceScaleFactor() {
430   return ViewScaleFactor();
433 gfx::Size RenderWidgetHostViewMac::ConvertViewSizeToPixel(
434     const gfx::Size& size) {
435   return gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size),
436                                              ViewScaleFactor())).size();
439 scoped_ptr<ResizeLock> RenderWidgetHostViewMac::CreateResizeLock(
440     bool defer_compositor_lock) {
441   NOTREACHED();
442   ResizeLock* lock = NULL;
443   return scoped_ptr<ResizeLock>(lock);
446 DelegatedFrameHost* RenderWidgetHostViewMac::GetDelegatedFrameHost() const {
447   return delegated_frame_host_.get();
450 ////////////////////////////////////////////////////////////////////////////////
451 // BrowserCompositorViewMacClient, public:
453 bool RenderWidgetHostViewMac::BrowserCompositorViewShouldAckImmediately()
454     const {
455   // The logic for delegated and non-delegated rendering is the same.
456   return AcceleratedLayerShouldAckImmediately();
459 void RenderWidgetHostViewMac::BrowserCompositorViewFrameSwapped(
460     const std::vector<ui::LatencyInfo>& all_latency_info) {
461   if (!render_widget_host_)
462     return;
463   for (auto latency_info : all_latency_info) {
464     latency_info.AddLatencyNumber(
465         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
466     render_widget_host_->FrameSwapped(latency_info);
467   }
470 NSView* RenderWidgetHostViewMac::BrowserCompositorSuperview() {
471   return cocoa_view_;
474 ui::Layer* RenderWidgetHostViewMac::BrowserCompositorRootLayer() {
475   return root_layer_.get();
478 ///////////////////////////////////////////////////////////////////////////////
479 // RenderWidgetHostViewBase, public:
481 // static
482 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
483     blink::WebScreenInfo* results) {
484   *results = GetWebScreenInfo(NULL);
487 ///////////////////////////////////////////////////////////////////////////////
488 // RenderWidgetHostViewMac, public:
490 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
491     : render_widget_host_(RenderWidgetHostImpl::From(widget)),
492       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
493       can_compose_inline_(true),
494       browser_compositor_view_placeholder_(
495           new BrowserCompositorViewPlaceholderMac),
496       is_loading_(false),
497       allow_pause_for_resize_or_repaint_(true),
498       weak_factory_(this),
499       fullscreen_parent_host_view_(NULL) {
500   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
501   // goes away.  Since we autorelease it, our caller must put
502   // |GetNativeView()| into the view hierarchy right after calling us.
503   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
504                   initWithRenderWidgetHostViewMac:this] autorelease];
506   // Make this view host a solid white layer when there is no content ready to
507   // draw.
508   background_layer_.reset([[CALayer alloc] init]);
509   [background_layer_
510       setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
511   [cocoa_view_ setLayer:background_layer_];
512   [cocoa_view_ setWantsLayer:YES];
514   if (IsDelegatedRendererEnabled()) {
515     root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
516     delegated_frame_host_.reset(new DelegatedFrameHost(this));
517   }
519   gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this);
521   render_widget_host_->SetView(this);
524 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
525   gfx::Screen::GetScreenFor(cocoa_view_)->RemoveObserver(this);
527   // This is being called from |cocoa_view_|'s destructor, so invalidate the
528   // pointer.
529   cocoa_view_ = nil;
531   UnlockMouse();
533   // Ensure that the browser compositor is destroyed in a safe order.
534   ShutdownBrowserCompositor();
536   // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
537   // RenderWidgetHost does we need to tell it not to hold a stale pointer to
538   // us.
539   if (render_widget_host_)
540     render_widget_host_->SetView(NULL);
543 void RenderWidgetHostViewMac::SetDelegate(
544     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
545   [cocoa_view_ setResponderDelegate:delegate];
548 void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
549   allow_pause_for_resize_or_repaint_ = allow;
552 ///////////////////////////////////////////////////////////////////////////////
553 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
555 void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
556   if (browser_compositor_view_)
557     return;
559   TRACE_EVENT0("browser",
560                "RenderWidgetHostViewMac::EnsureBrowserCompositorView");
562   browser_compositor_view_.reset(new BrowserCompositorViewMac(this));
563   delegated_frame_host_->AddedToWindow();
564   delegated_frame_host_->WasShown(ui::LatencyInfo());
567 void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
568   TRACE_EVENT0("browser",
569                "RenderWidgetHostViewMac::DestroyBrowserCompositorView");
570   if (!browser_compositor_view_)
571     return;
573   // Marking the DelegatedFrameHost as removed from the window hierarchy is
574   // necessary to remove all connections to its old ui::Compositor.
575   delegated_frame_host_->WasHidden();
576   delegated_frame_host_->RemovingFromWindow();
577   browser_compositor_view_.reset();
580 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
581   bool handled = true;
582   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
583     IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
584     IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
585     IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted,
586         OnGetRenderedTextCompleted)
587     IPC_MESSAGE_UNHANDLED(handled = false)
588   IPC_END_MESSAGE_MAP()
589   return handled;
592 void RenderWidgetHostViewMac::InitAsChild(
593     gfx::NativeView parent_view) {
596 void RenderWidgetHostViewMac::InitAsPopup(
597     RenderWidgetHostView* parent_host_view,
598     const gfx::Rect& pos) {
599   bool activatable = popup_type_ == blink::WebPopupTypeNone;
600   [cocoa_view_ setCloseOnDeactivate:YES];
601   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
603   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
604   origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
606   popup_window_.reset([[RenderWidgetPopupWindow alloc]
607       initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
608                                      pos.width(), pos.height())
609                 styleMask:NSBorderlessWindowMask
610                   backing:NSBackingStoreBuffered
611                     defer:NO]);
612   [popup_window_ setLevel:NSPopUpMenuWindowLevel];
613   [popup_window_ setReleasedWhenClosed:NO];
614   [popup_window_ makeKeyAndOrderFront:nil];
615   [[popup_window_ contentView] addSubview:cocoa_view_];
616   [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
617   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
618   [[NSNotificationCenter defaultCenter]
619       addObserver:cocoa_view_
620          selector:@selector(popupWindowWillClose:)
621              name:NSWindowWillCloseNotification
622            object:popup_window_];
625 // This function creates the fullscreen window and hides the dock and menubar if
626 // necessary. Note, this codepath is only used for pepper flash when
627 // pp::FlashFullScreen::SetFullscreen() is called. If
628 // pp::FullScreen::SetFullscreen() is called then the entire browser window
629 // will enter fullscreen instead.
630 void RenderWidgetHostViewMac::InitAsFullscreen(
631     RenderWidgetHostView* reference_host_view) {
632   fullscreen_parent_host_view_ =
633       static_cast<RenderWidgetHostViewMac*>(reference_host_view);
634   NSWindow* parent_window = nil;
635   if (reference_host_view)
636     parent_window = [reference_host_view->GetNativeView() window];
637   NSScreen* screen = [parent_window screen];
638   if (!screen)
639     screen = [NSScreen mainScreen];
641   pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
642       initWithContentRect:[screen frame]
643                 styleMask:NSBorderlessWindowMask
644                   backing:NSBackingStoreBuffered
645                     defer:NO]);
646   [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
647   [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
648   [cocoa_view_ setCanBeKeyView:YES];
649   [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
650   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
651   // If the pepper fullscreen window isn't opaque then there are performance
652   // issues when it's on the discrete GPU and the Chrome window is being drawn
653   // to. http://crbug.com/171911
654   [pepper_fullscreen_window_ setOpaque:YES];
656   // Note that this forms a reference cycle between the fullscreen window and
657   // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
658   // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
659   // This cycle is normally broken when -keyEvent: receives an <esc> key, which
660   // explicitly calls Shutdown on the render_widget_host_, which calls
661   // Destroy() on RWHVMac, which drops the reference to
662   // pepper_fullscreen_window_.
663   [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
665   // Note that this keeps another reference to pepper_fullscreen_window_.
666   fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
667       initWithWindow:pepper_fullscreen_window_.get()
668        desiredScreen:screen]);
669   [fullscreen_window_manager_ enterFullscreenMode];
670   [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
673 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
674   // See comment in InitAsFullscreen(): There is a reference cycle between
675   // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
676   // Tests that test pepper fullscreen mode without sending an <esc> event
677   // need to call this method to break the reference cycle.
678   [fullscreen_window_manager_ exitFullscreenMode];
679   fullscreen_window_manager_.reset();
680   [pepper_fullscreen_window_ close];
681   pepper_fullscreen_window_.reset();
684 int RenderWidgetHostViewMac::window_number() const {
685   NSWindow* window = [cocoa_view_ window];
686   if (!window)
687     return -1;
688   return [window windowNumber];
691 float RenderWidgetHostViewMac::ViewScaleFactor() const {
692   return ui::GetScaleFactorForNativeView(cocoa_view_);
695 void RenderWidgetHostViewMac::UpdateDisplayLink() {
696   static bool is_vsync_disabled =
697       base::CommandLine::ForCurrentProcess()->HasSwitch(
698           switches::kDisableGpuVsync);
699   if (is_vsync_disabled)
700     return;
702   NSScreen* screen = [[cocoa_view_ window] screen];
703   NSDictionary* screen_description = [screen deviceDescription];
704   NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
705   CGDirectDisplayID display_id = [screen_number unsignedIntValue];
707   display_link_ = DisplayLinkMac::GetForDisplay(display_id);
708   if (!display_link_) {
709     // Note that on some headless systems, the display link will fail to be
710     // created, so this should not be a fatal error.
711     LOG(ERROR) << "Failed to create display link.";
712   }
715 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
716   if (!render_widget_host_ || !display_link_)
717     return;
719   if (!display_link_->GetVSyncParameters(&vsync_timebase_, &vsync_interval_)) {
720     vsync_timebase_ = base::TimeTicks();
721     vsync_interval_ = base::TimeDelta();
722     return;
723   }
725   render_widget_host_->UpdateVSyncParameters(vsync_timebase_, vsync_interval_);
728 void RenderWidgetHostViewMac::SpeakText(const std::string& text) {
729   [NSApp speakString:base::SysUTF8ToNSString(text)];
732 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
733   if (!render_widget_host_)
734     return;
735   render_widget_host_->NotifyScreenInfoChanged();
738 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
739   return render_widget_host_;
742 void RenderWidgetHostViewMac::WasShown() {
743   if (!render_widget_host_->is_hidden())
744     return;
746   ui::LatencyInfo renderer_latency_info;
747   renderer_latency_info.AddLatencyNumber(
748       ui::TAB_SHOW_COMPONENT,
749       render_widget_host_->GetLatencyComponentId(),
750       0);
751   render_widget_host_->WasShown(renderer_latency_info);
753   // If there is not a frame being currently drawn, kick one, so that the below
754   // pause will have a frame to wait on.
755   render_widget_host_->ScheduleComposite();
756   PauseForPendingResizeOrRepaintsAndDraw();
759 void RenderWidgetHostViewMac::WasHidden() {
760   if (render_widget_host_->is_hidden())
761     return;
763   DestroyBrowserCompositorView();
765   // If we have a renderer, then inform it that we are being hidden so it can
766   // reduce its resource utilization.
767   render_widget_host_->WasHidden();
770 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
771   gfx::Rect rect = GetViewBounds();
772   rect.set_size(size);
773   SetBounds(rect);
776 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
777   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
778   // TODO(thakis): fix, http://crbug.com/73362
779   if (render_widget_host_->is_hidden())
780     return;
782   // During the initial creation of the RenderWidgetHostView in
783   // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
784   // an empty size. In the Windows code flow, it is not ignored because
785   // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
786   // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
787   // flags to keep things sized properly. On the other hand, if the size is not
788   // empty then this is a valid request for a pop-up.
789   if (rect.size().IsEmpty())
790     return;
792   // Ignore the position of |rect| for non-popup rwhvs. This is because
793   // background tabs do not have a window, but the window is required for the
794   // coordinate conversions. Popups are always for a visible tab.
795   //
796   // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
797   // valid for resizing to be requested (e.g., during tab capture, to size the
798   // view to screen-capture resolution). In this case, simply treat the view as
799   // relative to the screen.
800   BOOL isRelativeToScreen = IsPopup() ||
801       ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
802   if (isRelativeToScreen) {
803     // The position of |rect| is screen coordinate system and we have to
804     // consider Cocoa coordinate system is upside-down and also multi-screen.
805     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
806     NSSize size = NSMakeSize(rect.width(), rect.height());
807     size = [cocoa_view_ convertSize:size toView:nil];
808     origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
809     NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
810                               size.width, size.height);
811     if (IsPopup())
812       [popup_window_ setFrame:frame display:YES];
813     else
814       [cocoa_view_ setFrame:frame];
815   } else {
816     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
817     gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
818     rect2.set_width(rect.width());
819     rect2.set_height(rect.height());
820     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
821   }
824 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
825   return cocoa_view_;
828 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
829   return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
832 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
833   NOTIMPLEMENTED();
834   return static_cast<gfx::NativeViewAccessible>(NULL);
837 void RenderWidgetHostViewMac::MovePluginWindows(
838     const std::vector<WebPluginGeometry>& moves) {
839   // Must be overridden, but unused on this platform. Core Animation
840   // plugins are drawn by the GPU process (through the compositor),
841   // and Core Graphics plugins are drawn by the renderer process.
842   DCHECK_CURRENTLY_ON(BrowserThread::UI);
845 void RenderWidgetHostViewMac::Focus() {
846   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
849 void RenderWidgetHostViewMac::Blur() {
850   UnlockMouse();
851   [[cocoa_view_ window] makeFirstResponder:nil];
854 bool RenderWidgetHostViewMac::HasFocus() const {
855   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
858 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
859   if (delegated_frame_host_)
860     return delegated_frame_host_->CanCopyToBitmap();
861   return false;
864 void RenderWidgetHostViewMac::Show() {
865   [cocoa_view_ setHidden:NO];
867   WasShown();
870 void RenderWidgetHostViewMac::Hide() {
871   [cocoa_view_ setHidden:YES];
873   WasHidden();
876 bool RenderWidgetHostViewMac::IsShowing() {
877   return ![cocoa_view_ isHidden];
880 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
881   NSRect bounds = [cocoa_view_ bounds];
882   // TODO(shess): In case of !window, the view has been removed from
883   // the view hierarchy because the tab isn't main.  Could retrieve
884   // the information from the main tab for our window.
885   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
886   if (!enclosing_window)
887     return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
889   bounds = [cocoa_view_ convertRect:bounds toView:nil];
890   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
891   return FlipNSRectToRectScreen(bounds);
894 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
895   WebCursor web_cursor = cursor;
896   [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
899 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
900   is_loading_ = is_loading;
901   // If we ever decide to show the waiting cursor while the page is loading
902   // like Chrome does on Windows, call |UpdateCursor()| here.
905 void RenderWidgetHostViewMac::TextInputStateChanged(
906     const ViewHostMsg_TextInputState_Params& params) {
907   if (text_input_type_ != params.type ||
908       can_compose_inline_ != params.can_compose_inline) {
909     text_input_type_ = params.type;
910     can_compose_inline_ = params.can_compose_inline;
911     if (HasFocus()) {
912       SetTextInputActive(true);
914       // Let AppKit cache the new input context to make IMEs happy.
915       // See http://crbug.com/73039.
916       [NSApp updateWindows];
918 #ifndef __LP64__
919       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
920 #endif
921     }
922   }
925 void RenderWidgetHostViewMac::ImeCancelComposition() {
926   [cocoa_view_ cancelComposition];
929 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
930     const gfx::Range& range,
931     const std::vector<gfx::Rect>& character_bounds) {
932   // The RangeChanged message is only sent with valid values. The current
933   // caret position (start == end) will be sent if there is no IME range.
934   [cocoa_view_ setMarkedRange:range.ToNSRange()];
935   composition_range_ = range;
936   composition_bounds_ = character_bounds;
939 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
940                                                 int error_code) {
941   Destroy();
944 void RenderWidgetHostViewMac::Destroy() {
945   [[NSNotificationCenter defaultCenter]
946       removeObserver:cocoa_view_
947                 name:NSWindowWillCloseNotification
948               object:popup_window_];
950   // We've been told to destroy.
951   [cocoa_view_ retain];
952   [cocoa_view_ removeFromSuperview];
953   [cocoa_view_ autorelease];
955   [popup_window_ close];
956   popup_window_.autorelease();
958   [fullscreen_window_manager_ exitFullscreenMode];
959   fullscreen_window_manager_.reset();
960   [pepper_fullscreen_window_ close];
962   // This can be called as part of processing the window's responder
963   // chain, for instance |-performKeyEquivalent:|.  In that case the
964   // object needs to survive until the stack unwinds.
965   pepper_fullscreen_window_.autorelease();
967   // Delete the delegated frame state, which will reach back into
968   // render_widget_host_.
969   ShutdownBrowserCompositor();
971   // We get this call just before |render_widget_host_| deletes
972   // itself.  But we are owned by |cocoa_view_|, which may be retained
973   // by some other code.  Examples are WebContentsViewMac's
974   // |latent_focus_view_| and TabWindowController's
975   // |cachedContentView_|.
976   render_widget_host_ = NULL;
979 // Called from the renderer to tell us what the tooltip text should be. It
980 // calls us frequently so we need to cache the value to prevent doing a lot
981 // of repeat work.
982 void RenderWidgetHostViewMac::SetTooltipText(
983     const base::string16& tooltip_text) {
984   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
985     tooltip_text_ = tooltip_text;
987     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
988     // Windows; we're just trying to be polite. Don't persist the trimmed
989     // string, as then the comparison above will always fail and we'll try to
990     // set it again every single time the mouse moves.
991     base::string16 display_text = tooltip_text_;
992     if (tooltip_text_.length() > kMaxTooltipLength)
993       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
995     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
996     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
997   }
1000 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1001   return [NSApp respondsToSelector:@selector(speakString:)] &&
1002          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1005 void RenderWidgetHostViewMac::SpeakSelection() {
1006   if (![NSApp respondsToSelector:@selector(speakString:)])
1007     return;
1009   if (selected_text_.empty() && render_widget_host_) {
1010     // If there's no selection, speak all text. Send an asynchronous IPC
1011     // request for fetching all the text for a webcontent.
1012     // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1013     render_widget_host_->Send(new ViewMsg_GetRenderedText(
1014         render_widget_host_->GetRoutingID()));
1015     return;
1016   }
1018   SpeakText(selected_text_);
1021 bool RenderWidgetHostViewMac::IsSpeaking() const {
1022   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1023          [NSApp isSpeaking];
1026 void RenderWidgetHostViewMac::StopSpeaking() {
1027   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1028     [NSApp stopSpeaking:cocoa_view_];
1032 // RenderWidgetHostViewCocoa uses the stored selection text,
1033 // which implements NSServicesRequests protocol.
1035 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1036                                                size_t offset,
1037                                                const gfx::Range& range) {
1038   if (range.is_empty() || text.empty()) {
1039     selected_text_.clear();
1040   } else {
1041     size_t pos = range.GetMin() - offset;
1042     size_t n = range.length();
1044     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1045     if (pos >= text.length()) {
1046       DCHECK(false) << "The text can not cover range.";
1047       return;
1048     }
1049     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1050   }
1052   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1053   // Updates markedRange when there is no marked text so that retrieving
1054   // markedRange immediately after calling setMarkdText: returns the current
1055   // caret position.
1056   if (![cocoa_view_ hasMarkedText]) {
1057     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1058   }
1060   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1063 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1064     const ViewHostMsg_SelectionBounds_Params& params) {
1065   if (params.anchor_rect == params.focus_rect)
1066     caret_rect_ = params.anchor_rect;
1069 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1070   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1072   // Create a fake mouse event to inform the render widget that the mouse
1073   // left or entered.
1074   NSWindow* window = [cocoa_view_ window];
1075   // TODO(asvitkine): If the location outside of the event stream doesn't
1076   // correspond to the current event (due to delayed event processing), then
1077   // this may result in a cursor flicker if there are later mouse move events
1078   // in the pipeline. Find a way to use the mouse location from the event that
1079   // dismissed the context menu.
1080   NSPoint location = [window mouseLocationOutsideOfEventStream];
1081   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1082                                       location:location
1083                                  modifierFlags:0
1084                                      timestamp:0
1085                                   windowNumber:window_number()
1086                                        context:nil
1087                                    eventNumber:0
1088                                     clickCount:0
1089                                       pressure:0];
1090   WebMouseEvent web_event =
1091       WebInputEventFactory::mouseEvent(event, cocoa_view_);
1092   if (showing)
1093     web_event.type = WebInputEvent::MouseLeave;
1094   ForwardMouseEvent(web_event);
1097 bool RenderWidgetHostViewMac::IsPopup() const {
1098   return popup_type_ != blink::WebPopupTypeNone;
1101 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1102     const gfx::Rect& src_subrect,
1103     const gfx::Size& dst_size,
1104     const base::Callback<void(bool, const SkBitmap&)>& callback,
1105     const SkColorType color_type) {
1106   if (delegated_frame_host_) {
1107     delegated_frame_host_->CopyFromCompositingSurface(
1108         src_subrect, dst_size, callback, color_type);
1109   }
1112 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1113       const gfx::Rect& src_subrect,
1114       const scoped_refptr<media::VideoFrame>& target,
1115       const base::Callback<void(bool)>& callback) {
1116   if (delegated_frame_host_) {
1117     delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1118         src_subrect, target, callback);
1119   }
1122 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1123   if (delegated_frame_host_)
1124     return delegated_frame_host_->CanCopyToVideoFrame();
1125   return false;
1128 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1129   if (delegated_frame_host_)
1130     return delegated_frame_host_->CanSubscribeFrame();
1131   return false;
1134 void RenderWidgetHostViewMac::BeginFrameSubscription(
1135     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1136   if (delegated_frame_host_)
1137     delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1140 void RenderWidgetHostViewMac::EndFrameSubscription() {
1141   if (delegated_frame_host_)
1142     delegated_frame_host_->EndFrameSubscription();
1145 // Sets whether or not to accept first responder status.
1146 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
1147   [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
1150 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1151   if (render_widget_host_)
1152     render_widget_host_->ForwardMouseEvent(event);
1154   if (event.type == WebInputEvent::MouseLeave) {
1155     [cocoa_view_ setToolTipAtMousePoint:nil];
1156     tooltip_text_.clear();
1157   }
1160 void RenderWidgetHostViewMac::KillSelf() {
1161   if (!weak_factory_.HasWeakPtrs()) {
1162     [cocoa_view_ setHidden:YES];
1163     base::MessageLoop::current()->PostTask(FROM_HERE,
1164         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1165                    weak_factory_.GetWeakPtr()));
1166   }
1169 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1170     const NativeWebKeyboardEvent& event) {
1171   // Check WebInputEvent type since multiple types of events can be sent into
1172   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1173   // necessary to avoid double processing.
1174   // Also check the native type, since NSFlagsChanged is considered a key event
1175   // for WebKit purposes, but isn't considered a key event by the OS.
1176   if (event.type == WebInputEvent::RawKeyDown &&
1177       [event.os_event type] == NSKeyDown)
1178     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1179   return false;
1182 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1183     const base::string16& text, int plugin_id) {
1184   if (render_widget_host_) {
1185     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1186         render_widget_host_->GetRoutingID(), text, plugin_id));
1187   }
1190 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1191     const std::vector<gfx::Rect>& bounds,
1192     const gfx::Range& range,
1193     size_t* line_break_point) {
1194   DCHECK(line_break_point);
1195   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1196     return false;
1198   // We can't check line breaking completely from only rectangle array. Thus we
1199   // assume the line breaking as the next character's y offset is larger than
1200   // a threshold. Currently the threshold is determined as minimum y offset plus
1201   // 75% of maximum height.
1202   // TODO(nona): Check the threshold is reliable or not.
1203   // TODO(nona): Bidi support.
1204   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1205   int max_height = 0;
1206   int min_y_offset = kint32max;
1207   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1208     max_height = std::max(max_height, bounds[idx].height());
1209     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1210   }
1211   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1212   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1213     if (bounds[idx].y() > line_break_threshold) {
1214       *line_break_point = idx;
1215       return true;
1216     }
1217   }
1218   return false;
1221 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1222     const gfx::Range& range,
1223     gfx::Range* actual_range) {
1224   DCHECK(actual_range);
1225   DCHECK(!composition_bounds_.empty());
1226   DCHECK(range.start() <= composition_bounds_.size());
1227   DCHECK(range.end() <= composition_bounds_.size());
1229   if (range.is_empty()) {
1230     *actual_range = range;
1231     if (range.start() == composition_bounds_.size()) {
1232       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1233                        composition_bounds_[range.start() - 1].y(),
1234                        0,
1235                        composition_bounds_[range.start() - 1].height());
1236     } else {
1237       return gfx::Rect(composition_bounds_[range.start()].x(),
1238                        composition_bounds_[range.start()].y(),
1239                        0,
1240                        composition_bounds_[range.start()].height());
1241     }
1242   }
1244   size_t end_idx;
1245   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1246     end_idx = range.end();
1247   }
1248   *actual_range = gfx::Range(range.start(), end_idx);
1249   gfx::Rect rect = composition_bounds_[range.start()];
1250   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1251     rect.Union(composition_bounds_[i]);
1252   }
1253   return rect;
1256 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1257     const gfx::Range& request_range) {
1258   if (composition_range_.is_empty())
1259     return gfx::Range::InvalidRange();
1261   if (request_range.is_reversed())
1262     return gfx::Range::InvalidRange();
1264   if (request_range.start() < composition_range_.start() ||
1265       request_range.start() > composition_range_.end() ||
1266       request_range.end() > composition_range_.end()) {
1267     return gfx::Range::InvalidRange();
1268   }
1270   return gfx::Range(
1271       request_range.start() - composition_range_.start(),
1272       request_range.end() - composition_range_.start());
1275 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1276   if (!render_widget_host_->IsRenderView())
1277     return NULL;
1279   return WebContents::FromRenderViewHost(
1280       RenderViewHost::From(render_widget_host_));
1283 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1284     NSRange range,
1285     NSRect* rect,
1286     NSRange* actual_range) {
1287   DCHECK(rect);
1288   // This exists to make IMEs more responsive, see http://crbug.com/115920
1289   TRACE_EVENT0("browser",
1290                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1292   // If requested range is same as caret location, we can just return it.
1293   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1294     if (actual_range)
1295       *actual_range = range;
1296     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1297     return true;
1298   }
1300   const gfx::Range request_range_in_composition =
1301       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1302   if (request_range_in_composition == gfx::Range::InvalidRange())
1303     return false;
1305   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1306   // ImeCompositionRangeChanged will be sent with empty vector.
1307   if (composition_bounds_.empty())
1308     return false;
1309   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1311   gfx::Range ui_actual_range;
1312   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1313                                request_range_in_composition,
1314                                &ui_actual_range).ToCGRect());
1315   if (actual_range) {
1316     *actual_range = gfx::Range(
1317         composition_range_.start() + ui_actual_range.start(),
1318         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1319   }
1320   return true;
1323 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
1324     const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1325     int gpu_host_id) {
1328 void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
1329     const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1330     int gpu_host_id) {
1333 void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
1336 void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
1339 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1340       const gfx::Size& desired_size) {
1341   if (browser_compositor_view_)
1342     return browser_compositor_view_->HasFrameOfSize(desired_size);
1343   return false;
1346 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1347     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1348   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1350   if (frame->delegated_frame_data) {
1351     float scale_factor = frame->metadata.device_scale_factor;
1353     // Compute the frame size based on the root render pass rect size.
1354     cc::RenderPass* root_pass =
1355         frame->delegated_frame_data->render_pass_list.back();
1356     gfx::Size pixel_size = root_pass->output_rect.size();
1357     gfx::Size dip_size =
1358         ConvertSizeToDIP(scale_factor, pixel_size);
1360     root_layer_->SetBounds(gfx::Rect(dip_size));
1361     if (!render_widget_host_->is_hidden()) {
1362       EnsureBrowserCompositorView();
1363       browser_compositor_view_->GetCompositor()->SetScaleAndSize(
1364           scale_factor, pixel_size);
1365     }
1367     SendVSyncParametersToRenderer();
1369     delegated_frame_host_->SwapDelegatedFrame(
1370         output_surface_id,
1371         frame->delegated_frame_data.Pass(),
1372         frame->metadata.device_scale_factor,
1373         frame->metadata.latency_info);
1374   } else {
1375     DLOG(ERROR) << "Received unexpected frame type.";
1376     RecordAction(
1377         base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1378     render_widget_host_->GetProcess()->ReceivedBadMessage();
1379   }
1382 void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
1383                                                             int route_id) {
1386 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1387   *results = GetWebScreenInfo(GetNativeView());
1390 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1391   // TODO(shess): In case of !window, the view has been removed from
1392   // the view hierarchy because the tab isn't main.  Could retrieve
1393   // the information from the main tab for our window.
1394   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1395   if (!enclosing_window)
1396     return gfx::Rect();
1398   NSRect bounds = [enclosing_window frame];
1399   return FlipNSRectToRectScreen(bounds);
1402 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1403   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1404   // completely on Mac OS.
1405   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
1408 bool RenderWidgetHostViewMac::LockMouse() {
1409   if (mouse_locked_)
1410     return true;
1412   mouse_locked_ = true;
1414   // Lock position of mouse cursor and hide it.
1415   CGAssociateMouseAndMouseCursorPosition(NO);
1416   [NSCursor hide];
1418   // Clear the tooltip window.
1419   SetTooltipText(base::string16());
1421   return true;
1424 void RenderWidgetHostViewMac::UnlockMouse() {
1425   if (!mouse_locked_)
1426     return;
1427   mouse_locked_ = false;
1429   // Unlock position of mouse cursor and unhide it.
1430   CGAssociateMouseAndMouseCursorPosition(YES);
1431   [NSCursor unhide];
1433   if (render_widget_host_)
1434     render_widget_host_->LostMouseLock();
1437 void RenderWidgetHostViewMac::WheelEventAck(
1438     const blink::WebMouseWheelEvent& event,
1439     InputEventAckState ack_result) {
1440   bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1441   // Only record a wheel event as unhandled if JavaScript handlers got a chance
1442   // to see it (no-op wheel events are ignored by the event dispatcher)
1443   if (event.deltaX || event.deltaY)
1444     [cocoa_view_ processedWheelEvent:event consumed:consumed];
1447 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1448   if (render_widget_host_)
1449     return render_widget_host_->Send(message);
1450   delete message;
1451   return false;
1454 void RenderWidgetHostViewMac::ShutdownHost() {
1455   weak_factory_.InvalidateWeakPtrs();
1456   render_widget_host_->Shutdown();
1457   // Do not touch any members at this point, |this| has been deleted.
1460 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1461   DestroyBrowserCompositorView();
1462   delegated_frame_host_.reset();
1463   root_layer_.reset();
1464   browser_compositor_view_placeholder_.reset();
1467 void RenderWidgetHostViewMac::SetActive(bool active) {
1468   if (render_widget_host_) {
1469     render_widget_host_->SetActive(active);
1470     if (active) {
1471       if (HasFocus())
1472         render_widget_host_->Focus();
1473     } else {
1474       render_widget_host_->Blur();
1475     }
1476   }
1477   if (HasFocus())
1478     SetTextInputActive(active);
1479   if (!active) {
1480     [cocoa_view_ setPluginImeActive:NO];
1481     UnlockMouse();
1482   }
1485 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1486   if (render_widget_host_) {
1487     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1488         render_widget_host_->GetRoutingID(), visible));
1489   }
1492 void RenderWidgetHostViewMac::WindowFrameChanged() {
1493   if (render_widget_host_) {
1494     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1495         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1496         GetViewBounds()));
1497   }
1500 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1501   RenderWidgetHostViewMacDictionaryHelper helper(this);
1502   helper.ShowDefinitionForSelection();
1505 void RenderWidgetHostViewMac::SetBackgroundOpaque(bool opaque) {
1506   RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
1507   if (render_widget_host_)
1508     render_widget_host_->SetBackgroundOpaque(opaque);
1511 BrowserAccessibilityManager*
1512     RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1513         BrowserAccessibilityDelegate* delegate) {
1514   return new BrowserAccessibilityManagerMac(
1515       cocoa_view_,
1516       BrowserAccessibilityManagerMac::GetEmptyDocument(),
1517       delegate);
1520 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1521     const gfx::Rect& bounds) {
1522   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1523   NSSize size = NSMakeSize(bounds.width(), bounds.height());
1524   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1525   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1526   NSPoint originInScreen =
1527       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1528   originInScreen.y = originInScreen.y - size.height;
1529   return gfx::Point(originInScreen.x, originInScreen.y);
1532 void RenderWidgetHostViewMac::AccessibilityShowMenu(const gfx::Point& point) {
1533   NSPoint location = NSMakePoint(point.x(), point.y());
1534   location = [[cocoa_view_ window] convertScreenToBase:location];
1535   NSEvent* fakeRightClick = [NSEvent
1536                           mouseEventWithType:NSRightMouseDown
1537                                     location:location
1538                                modifierFlags:0
1539                                    timestamp:0
1540                                 windowNumber:[[cocoa_view_ window] windowNumber]
1541                                      context:[NSGraphicsContext currentContext]
1542                                  eventNumber:0
1543                                   clickCount:1
1544                                     pressure:0];
1546   [cocoa_view_ mouseEvent:fakeRightClick];
1549 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1550   if (active) {
1551     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1552       EnablePasswordInput();
1553     else
1554       DisablePasswordInput();
1555   } else {
1556     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1557       DisablePasswordInput();
1558   }
1561 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1562                                                    int plugin_id) {
1563   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1566 void RenderWidgetHostViewMac::OnStartPluginIme() {
1567   [cocoa_view_ setPluginImeActive:YES];
1570 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1571     const std::string& text) {
1572   SpeakText(text);
1575 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1576   if (!render_widget_host_ || render_widget_host_->is_hidden())
1577     return;
1579   // Pausing for one view prevents others from receiving frames.
1580   // This may lead to large delays, causing overlaps. See crbug.com/352020.
1581   if (!allow_pause_for_resize_or_repaint_)
1582     return;
1584   // Wait for a frame of the right size to come in.
1585   if (browser_compositor_view_)
1586     browser_compositor_view_->BeginPumpingFrames();
1587   render_widget_host_->PauseForPendingResizeOrRepaints();
1588   if (browser_compositor_view_)
1589     browser_compositor_view_->EndPumpingFrames();
1592 SkColorType RenderWidgetHostViewMac::PreferredReadbackFormat() {
1593   return kN32_SkColorType;
1596 ////////////////////////////////////////////////////////////////////////////////
1597 // CompositingIOSurfaceLayerClient, public:
1599 bool RenderWidgetHostViewMac::AcceleratedLayerShouldAckImmediately() const {
1600   // If vsync is disabled, then always draw and ack frames immediately.
1601   static bool is_vsync_disabled =
1602       base::CommandLine::ForCurrentProcess()->HasSwitch(
1603           switches::kDisableGpuVsync);
1604   if (is_vsync_disabled)
1605     return true;
1607   // If the window is occluded, then this frame's display call may be severely
1608   // throttled. This is a good thing, unless tab capture may be active, because
1609   // the broadcast will be inappropriately throttled.
1610   // http://crbug.com/350410
1612   // If tab capture isn't active then only ack frames when we draw them.
1613   if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber())
1614     return false;
1616   NSWindow* window = [cocoa_view_ window];
1617   // If the view isn't even in the heirarchy then frames will never be drawn,
1618   // so ack them immediately.
1619   if (!window)
1620     return true;
1622   // Check the window occlusion API.
1623   if ([window respondsToSelector:@selector(occlusionState)]) {
1624     if ([window occlusionState] & NSWindowOcclusionStateVisible) {
1625       // If the window is visible then it is safe to wait until frames are
1626       // drawn to ack them.
1627       return false;
1628     } else {
1629       // If the window is occluded then frames may never be drawn, so ack them
1630       // immediately.
1631       return true;
1632     }
1633   }
1635   // If the window occlusion API is not present then ack frames when we draw
1636   // them.
1637   return false;
1640 void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame() {
1643 void RenderWidgetHostViewMac::AcceleratedLayerHitError() {
1646 ////////////////////////////////////////////////////////////////////////////////
1647 // gfx::DisplayObserver, public:
1649 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1652 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1655 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1656     const gfx::Display& display, uint32_t metrics) {
1657   gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1658   if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1659     return;
1661   UpdateScreenInfo(cocoa_view_);
1664 }  // namespace content
1666 // RenderWidgetHostViewCocoa ---------------------------------------------------
1668 @implementation RenderWidgetHostViewCocoa
1669 @synthesize selectedRange = selectedRange_;
1670 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1671 @synthesize markedRange = markedRange_;
1673 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1674   self = [super initWithFrame:NSZeroRect];
1675   if (self) {
1676     self.acceptsTouchEvents = YES;
1677     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1678     editCommand_helper_->AddEditingSelectorsToClass([self class]);
1680     renderWidgetHostView_.reset(r);
1681     canBeKeyView_ = YES;
1682     focusedPluginIdentifier_ = -1;
1684     // OpenGL support:
1685     if ([self respondsToSelector:
1686         @selector(setWantsBestResolutionOpenGLSurface:)]) {
1687       [self setWantsBestResolutionOpenGLSurface:YES];
1688     }
1689     [[NSNotificationCenter defaultCenter]
1690         addObserver:self
1691            selector:@selector(didChangeScreenParameters:)
1692                name:NSApplicationDidChangeScreenParametersNotification
1693              object:nil];
1694   }
1695   return self;
1698 - (void)dealloc {
1699   // Unbind the GL context from this view. If this is not done before super's
1700   // dealloc is called then the GL context will crash when it reaches into
1701   // the view in its destructor.
1702   // http://crbug.com/255608
1703   if (renderWidgetHostView_)
1704     renderWidgetHostView_->AcceleratedSurfaceRelease();
1706   if (responderDelegate_ &&
1707       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1708     [responderDelegate_ viewGone:self];
1709   responderDelegate_.reset();
1711   [[NSNotificationCenter defaultCenter] removeObserver:self];
1713   [super dealloc];
1716 - (void)didChangeScreenParameters:(NSNotification*)notify {
1717   g_screen_info_up_to_date = false;
1720 - (void)setResponderDelegate:
1721             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1722   DCHECK(!responderDelegate_);
1723   responderDelegate_.reset([delegate retain]);
1726 - (void)resetCursorRects {
1727   if (currentCursor_) {
1728     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1729     [currentCursor_ setOnMouseEntered:YES];
1730   }
1733 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1734                    consumed:(BOOL)consumed {
1735   [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1738 - (BOOL)respondsToSelector:(SEL)selector {
1739   // Trickiness: this doesn't mean "does this object's superclass respond to
1740   // this selector" but rather "does the -respondsToSelector impl from the
1741   // superclass say that this class responds to the selector".
1742   if ([super respondsToSelector:selector])
1743     return YES;
1745   if (responderDelegate_)
1746     return [responderDelegate_ respondsToSelector:selector];
1748   return NO;
1751 - (id)forwardingTargetForSelector:(SEL)selector {
1752   if ([responderDelegate_ respondsToSelector:selector])
1753     return responderDelegate_.get();
1755   return [super forwardingTargetForSelector:selector];
1758 - (void)setCanBeKeyView:(BOOL)can {
1759   canBeKeyView_ = can;
1762 - (BOOL)acceptsMouseEventsWhenInactive {
1763   // Some types of windows (balloons, always-on-top panels) want to accept mouse
1764   // clicks w/o the first click being treated as 'activation'. Same applies to
1765   // mouse move events.
1766   return [[self window] level] > NSNormalWindowLevel;
1769 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1770   return [self acceptsMouseEventsWhenInactive];
1773 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
1774   takesFocusOnlyOnMouseDown_ = b;
1777 - (void)setCloseOnDeactivate:(BOOL)b {
1778   closeOnDeactivate_ = b;
1781 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1782   NSWindow* window = [self window];
1783   // If this is a background window, don't handle mouse movement events. This
1784   // is the expected behavior on the Mac as evidenced by other applications.
1785   if ([theEvent type] == NSMouseMoved &&
1786       ![self acceptsMouseEventsWhenInactive] &&
1787       ![window isKeyWindow]) {
1788     return YES;
1789   }
1791   // Use hitTest to check whether the mouse is over a nonWebContentView - in
1792   // which case the mouse event should not be handled by the render host.
1793   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1794   NSView* contentView = [window contentView];
1795   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1796   // Traverse the superview hierarchy as the hitTest will return the frontmost
1797   // view, such as an NSTextView, while nonWebContentView may be specified by
1798   // its parent view.
1799   while (view) {
1800     if ([view respondsToSelector:nonWebContentViewSelector] &&
1801         [view performSelector:nonWebContentViewSelector]) {
1802       // The cursor is over a nonWebContentView - ignore this mouse event.
1803       return YES;
1804     }
1805     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1806         !hasOpenMouseDown_) {
1807       // The cursor is over an overlapping render widget. This check is done by
1808       // both views so the one that's returned by -hitTest: will end up
1809       // processing the event.
1810       // Note that while dragging, we only get events for the render view where
1811       // drag started, even if mouse is  actually over another view or outside
1812       // the window. Cocoa does this for us. We should handle these events and
1813       // not ignore (since there is no other render view to handle them). Thus
1814       // the |!hasOpenMouseDown_| check above.
1815       return YES;
1816     }
1817     view = [view superview];
1818   }
1819   return NO;
1822 - (void)mouseEvent:(NSEvent*)theEvent {
1823   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1824   if (responderDelegate_ &&
1825       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1826     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1827     if (handled)
1828       return;
1829   }
1831   if ([self shouldIgnoreMouseEvent:theEvent]) {
1832     // If this is the first such event, send a mouse exit to the host view.
1833     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1834       WebMouseEvent exitEvent =
1835           WebInputEventFactory::mouseEvent(theEvent, self);
1836       exitEvent.type = WebInputEvent::MouseLeave;
1837       exitEvent.button = WebMouseEvent::ButtonNone;
1838       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1839     }
1840     mouseEventWasIgnored_ = YES;
1841     return;
1842   }
1844   if (mouseEventWasIgnored_) {
1845     // If this is the first mouse event after a previous event that was ignored
1846     // due to the hitTest, send a mouse enter event to the host view.
1847     if (renderWidgetHostView_->render_widget_host_) {
1848       WebMouseEvent enterEvent =
1849           WebInputEventFactory::mouseEvent(theEvent, self);
1850       enterEvent.type = WebInputEvent::MouseMove;
1851       enterEvent.button = WebMouseEvent::ButtonNone;
1852       renderWidgetHostView_->ForwardMouseEvent(enterEvent);
1853     }
1854   }
1855   mouseEventWasIgnored_ = NO;
1857   // TODO(rohitrao): Probably need to handle other mouse down events here.
1858   if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
1859     if (renderWidgetHostView_->render_widget_host_)
1860       renderWidgetHostView_->render_widget_host_->OnPointerEventActivate();
1862     // Manually take focus after the click but before forwarding it to the
1863     // renderer.
1864     [[self window] makeFirstResponder:self];
1865   }
1867   // Don't cancel child popups; killing them on a mouse click would prevent the
1868   // user from positioning the insertion point in the text field spawning the
1869   // popup. A click outside the text field would cause the text field to drop
1870   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1871   // the popup anyway, so we're OK.
1873   NSEventType type = [theEvent type];
1874   if (type == NSLeftMouseDown)
1875     hasOpenMouseDown_ = YES;
1876   else if (type == NSLeftMouseUp)
1877     hasOpenMouseDown_ = NO;
1879   // TODO(suzhe): We should send mouse events to the input method first if it
1880   // wants to handle them. But it won't work without implementing method
1881   // - (NSUInteger)characterIndexForPoint:.
1882   // See: http://code.google.com/p/chromium/issues/detail?id=47141
1883   // Instead of sending mouse events to the input method first, we now just
1884   // simply confirm all ongoing composition here.
1885   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1886       type == NSOtherMouseDown) {
1887     [self confirmComposition];
1888   }
1890   const WebMouseEvent event =
1891       WebInputEventFactory::mouseEvent(theEvent, self);
1892   renderWidgetHostView_->ForwardMouseEvent(event);
1895 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1896   // |performKeyEquivalent:| is sent to all views of a window, not only down the
1897   // responder chain (cf. "Handling Key Equivalents" in
1898   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1899   // ). We only want to handle key equivalents if we're first responder.
1900   if ([[self window] firstResponder] != self)
1901     return NO;
1903   // If the event is reserved by the system, then do not pass it to web content.
1904   if (EventIsReservedBySystem(theEvent))
1905     return NO;
1907   // If we return |NO| from this function, cocoa will send the key event to
1908   // the menu and only if the menu does not process the event to |keyDown:|. We
1909   // want to send the event to a renderer _before_ sending it to the menu, so
1910   // we need to return |YES| for all events that might be swallowed by the menu.
1911   // We do not return |YES| for every keypress because we don't get |keyDown:|
1912   // events for keys that we handle this way.
1913   NSUInteger modifierFlags = [theEvent modifierFlags];
1914   if ((modifierFlags & NSCommandKeyMask) == 0) {
1915     // Make sure the menu does not contain key equivalents that don't
1916     // contain cmd.
1917     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1918     return NO;
1919   }
1921   // Command key combinations are sent via performKeyEquivalent rather than
1922   // keyDown:. We just forward this on and if WebCore doesn't want to handle
1923   // it, we let the WebContentsView figure out how to reinject it.
1924   [self keyEvent:theEvent wasKeyEquivalent:YES];
1925   return YES;
1928 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1929   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1930   // returned NO. If this function returns |YES|, Cocoa sends the event to
1931   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1932   // to us instead of doing key view loop control, ctrl-left/right get handled
1933   // correctly, etc.
1934   // (However, there are still some keys that Cocoa swallows, e.g. the key
1935   // equivalent that Cocoa uses for toggling the input language. In this case,
1936   // that's actually a good thing, though -- see http://crbug.com/26115 .)
1937   return YES;
1940 - (EventHandled)keyEvent:(NSEvent*)theEvent {
1941   if (responderDelegate_ &&
1942       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1943     BOOL handled = [responderDelegate_ handleEvent:theEvent];
1944     if (handled)
1945       return kEventHandled;
1946   }
1948   [self keyEvent:theEvent wasKeyEquivalent:NO];
1949   return kEventHandled;
1952 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
1953   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
1955   // If the user changes the system hotkey mapping after Chrome has been
1956   // launched, then it is possible that a formerly reserved system hotkey is no
1957   // longer reserved. The hotkey would have skipped the renderer, but would
1958   // also have not been handled by the system. If this is the case, immediately
1959   // return.
1960   // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
1961   // api to monitor changes to system hotkeys. This logic will have to be
1962   // updated.
1963   // http://crbug.com/383558.
1964   if (EventIsReservedBySystem(theEvent))
1965     return;
1967   DCHECK([theEvent type] != NSKeyDown ||
1968          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
1970   if ([theEvent type] == NSFlagsChanged) {
1971     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
1972     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
1973     int keyCode = [theEvent keyCode];
1974     if (!keyCode || keyCode == 10 || keyCode == 63)
1975       return;
1976   }
1978   // Don't cancel child popups; the key events are probably what's triggering
1979   // the popup in the first place.
1981   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
1982   DCHECK(widgetHost);
1984   NativeWebKeyboardEvent event(theEvent);
1986   // Force fullscreen windows to close on Escape so they won't keep the keyboard
1987   // grabbed or be stuck onscreen if the renderer is hanging.
1988   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
1989       event.windowsKeyCode == ui::VKEY_ESCAPE &&
1990       renderWidgetHostView_->pepper_fullscreen_window()) {
1991     RenderWidgetHostViewMac* parent =
1992         renderWidgetHostView_->fullscreen_parent_host_view();
1993     if (parent)
1994       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
1995     widgetHost->Shutdown();
1996     return;
1997   }
1999   // Suppress the escape key up event if necessary.
2000   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
2001     if (event.type == NativeWebKeyboardEvent::KeyUp)
2002       suppressNextEscapeKeyUp_ = NO;
2003     return;
2004   }
2006   // We only handle key down events and just simply forward other events.
2007   if ([theEvent type] != NSKeyDown) {
2008     widgetHost->ForwardKeyboardEvent(event);
2010     // Possibly autohide the cursor.
2011     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2012       [NSCursor setHiddenUntilMouseMoves:YES];
2014     return;
2015   }
2017   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2019   // Records the current marked text state, so that we can know if the marked
2020   // text was deleted or not after handling the key down event.
2021   BOOL oldHasMarkedText = hasMarkedText_;
2023   // This method should not be called recursively.
2024   DCHECK(!handlingKeyDown_);
2026   // Tells insertText: and doCommandBySelector: that we are handling a key
2027   // down event.
2028   handlingKeyDown_ = YES;
2030   // These variables might be set when handling the keyboard event.
2031   // Clear them here so that we can know whether they have changed afterwards.
2032   textToBeInserted_.clear();
2033   markedText_.clear();
2034   underlines_.clear();
2035   unmarkTextCalled_ = NO;
2036   hasEditCommands_ = NO;
2037   editCommands_.clear();
2039   // Before doing anything with a key down, check to see if plugin IME has been
2040   // cancelled, since the plugin host needs to be informed of that before
2041   // receiving the keydown.
2042   if ([theEvent type] == NSKeyDown)
2043     [self checkForPluginImeCancellation];
2045   // Sends key down events to input method first, then we can decide what should
2046   // be done according to input method's feedback.
2047   // If a plugin is active, bypass this step since events are forwarded directly
2048   // to the plugin IME.
2049   if (focusedPluginIdentifier_ == -1)
2050     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2052   handlingKeyDown_ = NO;
2054   // Indicates if we should send the key event and corresponding editor commands
2055   // after processing the input method result.
2056   BOOL delayEventUntilAfterImeCompostion = NO;
2058   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2059   // while an input method is composing or inserting a text.
2060   // Gmail checks this code in its onkeydown handler to stop auto-completing
2061   // e-mail addresses while composing a CJK text.
2062   // If the text to be inserted has only one character, then we don't need this
2063   // trick, because we'll send the text as a key press event instead.
2064   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2065     NativeWebKeyboardEvent fakeEvent = event;
2066     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2067     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2068     fakeEvent.skip_in_browser = true;
2069     widgetHost->ForwardKeyboardEvent(fakeEvent);
2070     // If this key event was handled by the input method, but
2071     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2072     // enqueued edit commands, then in order to let webkit handle them
2073     // correctly, we need to send the real key event and corresponding edit
2074     // commands after processing the input method result.
2075     // We shouldn't do this if a new marked text was set by the input method,
2076     // otherwise the new marked text might be cancelled by webkit.
2077     if (hasEditCommands_ && !hasMarkedText_)
2078       delayEventUntilAfterImeCompostion = YES;
2079   } else {
2080     if (!editCommands_.empty()) {
2081       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2082           widgetHost->GetRoutingID(), editCommands_));
2083     }
2084     widgetHost->ForwardKeyboardEvent(event);
2085   }
2087   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2088   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2089   // be set to NULL. So we check it here and return immediately if it's NULL.
2090   if (!renderWidgetHostView_->render_widget_host_)
2091     return;
2093   // Then send keypress and/or composition related events.
2094   // If there was a marked text or the text to be inserted is longer than 1
2095   // character, then we send the text by calling ConfirmComposition().
2096   // Otherwise, if the text to be inserted only contains 1 character, then we
2097   // can just send a keypress event which is fabricated by changing the type of
2098   // the keydown event, so that we can retain all necessary informations, such
2099   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2100   // prevent the browser from handling it again.
2101   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2102   // handle BMP characters here, as we can always insert non-BMP characters as
2103   // text.
2104   BOOL textInserted = NO;
2105   if (textToBeInserted_.length() >
2106       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2107     widgetHost->ImeConfirmComposition(
2108         textToBeInserted_, gfx::Range::InvalidRange(), false);
2109     textInserted = YES;
2110   }
2112   // Updates or cancels the composition. If some text has been inserted, then
2113   // we don't need to cancel the composition explicitly.
2114   if (hasMarkedText_ && markedText_.length()) {
2115     // Sends the updated marked text to the renderer so it can update the
2116     // composition node in WebKit.
2117     // When marked text is available, |selectedRange_| will be the range being
2118     // selected inside the marked text.
2119     widgetHost->ImeSetComposition(markedText_, underlines_,
2120                                   selectedRange_.location,
2121                                   NSMaxRange(selectedRange_));
2122   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2123     if (unmarkTextCalled_) {
2124       widgetHost->ImeConfirmComposition(
2125           base::string16(), gfx::Range::InvalidRange(), false);
2126     } else {
2127       widgetHost->ImeCancelComposition();
2128     }
2129   }
2131   // If the key event was handled by the input method but it also generated some
2132   // edit commands, then we need to send the real key event and corresponding
2133   // edit commands here. This usually occurs when the input method wants to
2134   // finish current composition session but still wants the application to
2135   // handle the key event. See http://crbug.com/48161 for reference.
2136   if (delayEventUntilAfterImeCompostion) {
2137     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2138     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2139     // So before sending the real key down event, we need to send a fake key up
2140     // event to balance it.
2141     NativeWebKeyboardEvent fakeEvent = event;
2142     fakeEvent.type = blink::WebInputEvent::KeyUp;
2143     fakeEvent.skip_in_browser = true;
2144     widgetHost->ForwardKeyboardEvent(fakeEvent);
2145     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2146     // a key event with |skip_in_browser| == true won't be handled by browser,
2147     // thus it won't destroy the widget.
2149     if (!editCommands_.empty()) {
2150       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2151           widgetHost->GetRoutingID(), editCommands_));
2152     }
2153     widgetHost->ForwardKeyboardEvent(event);
2155     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2156     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2157     // be set to NULL. So we check it here and return immediately if it's NULL.
2158     if (!renderWidgetHostView_->render_widget_host_)
2159       return;
2160   }
2162   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2163   // Only send a corresponding key press event if there is no marked text.
2164   if (!hasMarkedText_) {
2165     if (!textInserted && textToBeInserted_.length() == 1) {
2166       // If a single character was inserted, then we just send it as a keypress
2167       // event.
2168       event.type = blink::WebInputEvent::Char;
2169       event.text[0] = textToBeInserted_[0];
2170       event.text[1] = 0;
2171       event.skip_in_browser = true;
2172       widgetHost->ForwardKeyboardEvent(event);
2173     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2174                [[theEvent characters] length] > 0 &&
2175                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2176                 (hasEditCommands_ && editCommands_.empty()))) {
2177       // We don't get insertText: calls if ctrl or cmd is down, or the key event
2178       // generates an insert command. So synthesize a keypress event for these
2179       // cases, unless the key event generated any other command.
2180       event.type = blink::WebInputEvent::Char;
2181       event.skip_in_browser = true;
2182       widgetHost->ForwardKeyboardEvent(event);
2183     }
2184   }
2186   // Possibly autohide the cursor.
2187   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2188     [NSCursor setHiddenUntilMouseMoves:YES];
2191 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2192   DCHECK(base::mac::IsOSLionOrLater());
2194   if ([event phase] != NSEventPhaseEnded &&
2195       [event phase] != NSEventPhaseCancelled) {
2196     return;
2197   }
2199   if (renderWidgetHostView_->render_widget_host_) {
2200     // History-swiping is not possible if the logic reaches this point.
2201     // Allow rubber-banding in both directions.
2202     bool canRubberbandLeft = true;
2203     bool canRubberbandRight = true;
2204     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2205         event, self, canRubberbandLeft, canRubberbandRight);
2206     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2207   }
2209   if (endWheelMonitor_) {
2210     [NSEvent removeMonitor:endWheelMonitor_];
2211     endWheelMonitor_ = nil;
2212   }
2215 - (void)beginGestureWithEvent:(NSEvent*)event {
2216   [responderDelegate_ beginGestureWithEvent:event];
2218 - (void)endGestureWithEvent:(NSEvent*)event {
2219   [responderDelegate_ endGestureWithEvent:event];
2221 - (void)touchesMovedWithEvent:(NSEvent*)event {
2222   [responderDelegate_ touchesMovedWithEvent:event];
2224 - (void)touchesBeganWithEvent:(NSEvent*)event {
2225   [responderDelegate_ touchesBeganWithEvent:event];
2227 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2228   [responderDelegate_ touchesCancelledWithEvent:event];
2230 - (void)touchesEndedWithEvent:(NSEvent*)event {
2231   [responderDelegate_ touchesEndedWithEvent:event];
2234 // This is invoked only on 10.8 or newer when the user taps a word using
2235 // three fingers.
2236 - (void)quickLookWithEvent:(NSEvent*)event {
2237   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2238   TextInputClientMac::GetInstance()->GetStringAtPoint(
2239       renderWidgetHostView_->render_widget_host_,
2240       gfx::Point(point.x, NSHeight([self frame]) - point.y),
2241       ^(NSAttributedString* string, NSPoint baselinePoint) {
2242           if (string && [string length] > 0) {
2243             dispatch_async(dispatch_get_main_queue(), ^{
2244                 [self showDefinitionForAttributedString:string
2245                                                 atPoint:baselinePoint];
2246             });
2247           }
2248       }
2249   );
2252 // This method handles 2 different types of hardware events.
2253 // (Apple does not distinguish between them).
2254 //  a. Scrolling the middle wheel of a mouse.
2255 //  b. Swiping on the track pad.
2257 // This method is responsible for 2 types of behavior:
2258 //  a. Scrolling the content of window.
2259 //  b. Navigating forwards/backwards in history.
2261 // This is a brief description of the logic:
2262 //  1. If the content can be scrolled, scroll the content.
2263 //     (This requires a roundtrip to blink to determine whether the content
2264 //      can be scrolled.)
2265 //     Once this logic is triggered, the navigate logic cannot be triggered
2266 //     until the gesture finishes.
2267 //  2. If the user is making a horizontal swipe, start the navigate
2268 //     forward/backwards UI.
2269 //     Once this logic is triggered, the user can either cancel or complete
2270 //     the gesture. If the user completes the gesture, all remaining touches
2271 //     are swallowed, and not allowed to scroll the content. If the user
2272 //     cancels the gesture, all remaining touches are forwarded to the content
2273 //     scroll logic. The user cannot trigger the navigation logic again.
2274 - (void)scrollWheel:(NSEvent*)event {
2275   if (responderDelegate_ &&
2276       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2277     BOOL handled = [responderDelegate_ handleEvent:event];
2278     if (handled)
2279       return;
2280   }
2282   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2283   // the event is received even when the mouse cursor is no longer over the view
2284   // when the scrolling ends (e.g. if the tab was switched). This is necessary
2285   // for ending rubber-banding in such cases.
2286   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2287       !endWheelMonitor_) {
2288     endWheelMonitor_ =
2289       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2290       handler:^(NSEvent* blockEvent) {
2291           [self shortCircuitScrollWheelEvent:blockEvent];
2292           return blockEvent;
2293       }];
2294   }
2296   // This is responsible for content scrolling!
2297   if (renderWidgetHostView_->render_widget_host_) {
2298     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2299     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2300     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2301         event, self, canRubberbandLeft, canRubberbandRight);
2302     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2303   }
2306 // Called repeatedly during a pinch gesture, with incremental change values.
2307 - (void)magnifyWithEvent:(NSEvent*)event {
2308   if (renderWidgetHostView_->render_widget_host_) {
2309     // Send a GesturePinchUpdate event.
2310     // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
2311     // GestureSrollBegin/End) as is done for touchscreen.  Keeping track of when
2312     // a pinch is active would take a little more work here, and we don't need
2313     // it for anything yet.
2314     const WebGestureEvent& webEvent =
2315         WebInputEventFactory::gestureEvent(event, self);
2316     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
2317   }
2320 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2321   NSWindow* oldWindow = [self window];
2323   NSNotificationCenter* notificationCenter =
2324       [NSNotificationCenter defaultCenter];
2326   // Backing property notifications crash on 10.6 when building with the 10.7
2327   // SDK, see http://crbug.com/260595.
2328   static BOOL supportsBackingPropertiesNotification =
2329       SupportsBackingPropertiesChangedNotification();
2331   if (oldWindow) {
2332     if (supportsBackingPropertiesNotification) {
2333       [notificationCenter
2334           removeObserver:self
2335                     name:NSWindowDidChangeBackingPropertiesNotification
2336                   object:oldWindow];
2337     }
2338     [notificationCenter
2339         removeObserver:self
2340                   name:NSWindowDidMoveNotification
2341                 object:oldWindow];
2342     [notificationCenter
2343         removeObserver:self
2344                   name:NSWindowDidEndLiveResizeNotification
2345                 object:oldWindow];
2346   }
2347   if (newWindow) {
2348     if (supportsBackingPropertiesNotification) {
2349       [notificationCenter
2350           addObserver:self
2351              selector:@selector(windowDidChangeBackingProperties:)
2352                  name:NSWindowDidChangeBackingPropertiesNotification
2353                object:newWindow];
2354     }
2355     [notificationCenter
2356         addObserver:self
2357            selector:@selector(windowChangedGlobalFrame:)
2358                name:NSWindowDidMoveNotification
2359              object:newWindow];
2360     [notificationCenter
2361         addObserver:self
2362            selector:@selector(windowChangedGlobalFrame:)
2363                name:NSWindowDidEndLiveResizeNotification
2364              object:newWindow];
2365   }
2368 - (void)updateScreenProperties{
2369   renderWidgetHostView_->UpdateBackingStoreScaleFactor();
2370   renderWidgetHostView_->UpdateDisplayLink();
2373 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2374 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2375   // Background tabs check if their scale factor or vsync properties changed
2376   // when they are added to a window.
2378   // Allocating a CGLayerRef with the current scale factor immediately from
2379   // this handler doesn't work. Schedule the backing store update on the
2380   // next runloop cycle, then things are read for CGLayerRef allocations to
2381   // work.
2382   [self performSelector:@selector(updateScreenProperties)
2383              withObject:nil
2384              afterDelay:0];
2387 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2388   renderWidgetHostView_->UpdateScreenInfo(
2389       renderWidgetHostView_->GetNativeView());
2392 - (void)setFrameSize:(NSSize)newSize {
2393   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2395   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2396   // -setFrame: isn't neccessary.
2397   [super setFrameSize:newSize];
2399   if (!renderWidgetHostView_->render_widget_host_)
2400     return;
2402   renderWidgetHostView_->render_widget_host_->SendScreenRects();
2403   renderWidgetHostView_->render_widget_host_->WasResized();
2404   if (renderWidgetHostView_->delegated_frame_host_)
2405     renderWidgetHostView_->delegated_frame_host_->WasResized();
2407   // Wait for the frame that WasResize might have requested. If the view is
2408   // being made visible at a new size, then this call will have no effect
2409   // because the view widget is still hidden, and the pause call in WasShown
2410   // will have this effect for us.
2411   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2414 - (BOOL)canBecomeKeyView {
2415   if (!renderWidgetHostView_->render_widget_host_)
2416     return NO;
2418   return canBeKeyView_;
2421 - (BOOL)acceptsFirstResponder {
2422   if (!renderWidgetHostView_->render_widget_host_)
2423     return NO;
2425   return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
2428 - (BOOL)becomeFirstResponder {
2429   if (!renderWidgetHostView_->render_widget_host_)
2430     return NO;
2432   renderWidgetHostView_->render_widget_host_->Focus();
2433   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
2434   renderWidgetHostView_->SetTextInputActive(true);
2436   // Cancel any onging composition text which was left before we lost focus.
2437   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2438   // somehow that method won't be called when switching among different tabs.
2439   // See http://crbug.com/47209
2440   [self cancelComposition];
2442   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2443       [[self window] keyViewSelectionDirection]];
2444   NSDictionary* userInfo =
2445       [NSDictionary dictionaryWithObject:direction
2446                                   forKey:kSelectionDirection];
2447   [[NSNotificationCenter defaultCenter]
2448       postNotificationName:kViewDidBecomeFirstResponder
2449                     object:self
2450                   userInfo:userInfo];
2452   return YES;
2455 - (BOOL)resignFirstResponder {
2456   renderWidgetHostView_->SetTextInputActive(false);
2457   if (!renderWidgetHostView_->render_widget_host_)
2458     return YES;
2460   if (closeOnDeactivate_)
2461     renderWidgetHostView_->KillSelf();
2463   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
2464   renderWidgetHostView_->render_widget_host_->Blur();
2466   // We should cancel any onging composition whenever RWH's Blur() method gets
2467   // called, because in this case, webkit will confirm the ongoing composition
2468   // internally.
2469   [self cancelComposition];
2471   return YES;
2474 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2475   if (responderDelegate_ &&
2476       [responderDelegate_
2477           respondsToSelector:@selector(validateUserInterfaceItem:
2478                                                      isValidItem:)]) {
2479     BOOL valid;
2480     BOOL known =
2481         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2482     if (known)
2483       return valid;
2484   }
2486   SEL action = [item action];
2488   if (action == @selector(stopSpeaking:)) {
2489     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2490            renderWidgetHostView_->IsSpeaking();
2491   }
2492   if (action == @selector(startSpeaking:)) {
2493     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2494            renderWidgetHostView_->SupportsSpeech();
2495   }
2497   // For now, these actions are always enabled for render view,
2498   // this is sub-optimal.
2499   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2500   if (action == @selector(undo:) ||
2501       action == @selector(redo:) ||
2502       action == @selector(cut:) ||
2503       action == @selector(copy:) ||
2504       action == @selector(copyToFindPboard:) ||
2505       action == @selector(paste:) ||
2506       action == @selector(pasteAndMatchStyle:)) {
2507     return renderWidgetHostView_->render_widget_host_->IsRenderView();
2508   }
2510   return editCommand_helper_->IsMenuItemEnabled(action, self);
2513 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2514   return renderWidgetHostView_.get();
2517 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2518 // move) for the given event. Customize here to be more selective about which
2519 // key presses to autohide on.
2520 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2521   return ([event type] == NSKeyDown &&
2522              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2525 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2526                                          index:(NSUInteger)index
2527                                       maxCount:(NSUInteger)maxCount {
2528   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2529   NSUInteger totalLength = [fullArray count];
2530   if (index >= totalLength)
2531     return nil;
2532   NSUInteger length = MIN(totalLength - index, maxCount);
2533   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2536 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2537   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2538   return [fullArray count];
2541 - (id)accessibilityAttributeValue:(NSString *)attribute {
2542   BrowserAccessibilityManager* manager =
2543       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2545   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2546   // BrowserAccessibilityManager. Children includes all subviews in addition to
2547   // contents. Currently we do not have subviews besides the document view.
2548   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2549           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2550       manager) {
2551     return [NSArray arrayWithObjects:manager->
2552         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2553   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2554     return NSAccessibilityScrollAreaRole;
2555   }
2556   id ret = [super accessibilityAttributeValue:attribute];
2557   return ret;
2560 - (NSArray*)accessibilityAttributeNames {
2561   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2562   [ret addObject:NSAccessibilityContentsAttribute];
2563   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2564   return ret;
2567 - (id)accessibilityHitTest:(NSPoint)point {
2568   BrowserAccessibilityManager* manager =
2569       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2570   if (!manager)
2571     return self;
2572   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2573   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2574   localPoint.y = NSHeight([self bounds]) - localPoint.y;
2575   BrowserAccessibilityCocoa* root =
2576       manager->GetRoot()->ToBrowserAccessibilityCocoa();
2577   id obj = [root accessibilityHitTest:localPoint];
2578   return obj;
2581 - (BOOL)accessibilityIsIgnored {
2582   BrowserAccessibilityManager* manager =
2583       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2584   return !manager;
2587 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2588   BrowserAccessibilityManager* manager =
2589       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2590   // Only child is root.
2591   if (manager &&
2592       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2593     return 0;
2594   } else {
2595     return NSNotFound;
2596   }
2599 - (id)accessibilityFocusedUIElement {
2600   BrowserAccessibilityManager* manager =
2601       renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2602   if (manager) {
2603     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2604     DCHECK(focused_item);
2605     if (focused_item) {
2606       BrowserAccessibilityCocoa* focused_item_cocoa =
2607           focused_item->ToBrowserAccessibilityCocoa();
2608       DCHECK(focused_item_cocoa);
2609       if (focused_item_cocoa)
2610         return focused_item_cocoa;
2611     }
2612   }
2613   return [super accessibilityFocusedUIElement];
2616 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
2617 // with minor modifications for code style and commenting.
2619 //  The 'public' interface is -setToolTipAtMousePoint:. This differs from
2620 // -setToolTip: in that the updated tooltip takes effect immediately,
2621 //  without the user's having to move the mouse out of and back into the view.
2623 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
2624 // the view, which in turn requires overriding some internal tracking-rect
2625 // methods (to keep track of its owner & userdata, which need to be filled out
2626 // in the fake events.) --snej 7/6/09
2630  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
2631  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
2633  * Redistribution and use in source and binary forms, with or without
2634  * modification, are permitted provided that the following conditions
2635  * are met:
2637  * 1.  Redistributions of source code must retain the above copyright
2638  *     notice, this list of conditions and the following disclaimer.
2639  * 2.  Redistributions in binary form must reproduce the above copyright
2640  *     notice, this list of conditions and the following disclaimer in the
2641  *     documentation and/or other materials provided with the distribution.
2642  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
2643  *     its contributors may be used to endorse or promote products derived
2644  *     from this software without specific prior written permission.
2646  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
2647  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2648  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2649  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2650  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2651  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2652  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2653  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2654  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2655  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2656  */
2658 // Any non-zero value will do, but using something recognizable might help us
2659 // debug some day.
2660 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
2662 // Override of a public NSView method, replacing the inherited functionality.
2663 // See above for rationale.
2664 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
2665                                owner:(id)owner
2666                             userData:(void *)data
2667                         assumeInside:(BOOL)assumeInside {
2668   DCHECK(trackingRectOwner_ == nil);
2669   trackingRectOwner_ = owner;
2670   trackingRectUserData_ = data;
2671   return kTrackingRectTag;
2674 // Override of (apparently) a private NSView method(!) See above for rationale.
2675 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
2676                                 owner:(id)owner
2677                              userData:(void *)data
2678                          assumeInside:(BOOL)assumeInside
2679                        useTrackingNum:(int)tag {
2680   DCHECK(tag == 0 || tag == kTrackingRectTag);
2681   DCHECK(trackingRectOwner_ == nil);
2682   trackingRectOwner_ = owner;
2683   trackingRectUserData_ = data;
2684   return kTrackingRectTag;
2687 // Override of (apparently) a private NSView method(!) See above for rationale.
2688 - (void)_addTrackingRects:(NSRect *)rects
2689                     owner:(id)owner
2690              userDataList:(void **)userDataList
2691          assumeInsideList:(BOOL *)assumeInsideList
2692              trackingNums:(NSTrackingRectTag *)trackingNums
2693                     count:(int)count {
2694   DCHECK(count == 1);
2695   DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
2696   DCHECK(trackingRectOwner_ == nil);
2697   trackingRectOwner_ = owner;
2698   trackingRectUserData_ = userDataList[0];
2699   trackingNums[0] = kTrackingRectTag;
2702 // Override of a public NSView method, replacing the inherited functionality.
2703 // See above for rationale.
2704 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
2705   if (tag == 0)
2706     return;
2708   if (tag == kTrackingRectTag) {
2709     trackingRectOwner_ = nil;
2710     return;
2711   }
2713   if (tag == lastToolTipTag_) {
2714     [super removeTrackingRect:tag];
2715     lastToolTipTag_ = 0;
2716     return;
2717   }
2719   // If any other tracking rect is being removed, we don't know how it was
2720   // created and it's possible there's a leak involved (see Radar 3500217).
2721   NOTREACHED();
2724 // Override of (apparently) a private NSView method(!)
2725 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
2726   for (int i = 0; i < count; ++i) {
2727     int tag = tags[i];
2728     if (tag == 0)
2729       continue;
2730     DCHECK(tag == kTrackingRectTag);
2731     trackingRectOwner_ = nil;
2732   }
2735 // Sends a fake NSMouseExited event to the view for its current tracking rect.
2736 - (void)_sendToolTipMouseExited {
2737   // Nothing matters except window, trackingNumber, and userData.
2738   int windowNumber = [[self window] windowNumber];
2739   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2740                                               location:NSZeroPoint
2741                                          modifierFlags:0
2742                                              timestamp:0
2743                                           windowNumber:windowNumber
2744                                                context:NULL
2745                                            eventNumber:0
2746                                         trackingNumber:kTrackingRectTag
2747                                               userData:trackingRectUserData_];
2748   [trackingRectOwner_ mouseExited:fakeEvent];
2751 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
2752 - (void)_sendToolTipMouseEntered {
2753   // Nothing matters except window, trackingNumber, and userData.
2754   int windowNumber = [[self window] windowNumber];
2755   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2756                                               location:NSZeroPoint
2757                                          modifierFlags:0
2758                                              timestamp:0
2759                                           windowNumber:windowNumber
2760                                                context:NULL
2761                                            eventNumber:0
2762                                         trackingNumber:kTrackingRectTag
2763                                               userData:trackingRectUserData_];
2764   [trackingRectOwner_ mouseEntered:fakeEvent];
2767 // Sets the view's current tooltip, to be displayed at the current mouse
2768 // location. (This does not make the tooltip appear -- as usual, it only
2769 // appears after a delay.) Pass null to remove the tooltip.
2770 - (void)setToolTipAtMousePoint:(NSString *)string {
2771   NSString *toolTip = [string length] == 0 ? nil : string;
2772   if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
2773       (!toolTip && !toolTip_)) {
2774     return;
2775   }
2777   if (toolTip_) {
2778     [self _sendToolTipMouseExited];
2779   }
2781   toolTip_.reset([toolTip copy]);
2783   if (toolTip) {
2784     // See radar 3500217 for why we remove all tooltips
2785     // rather than just the single one we created.
2786     [self removeAllToolTips];
2787     NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2788     lastToolTipTag_ = [self addToolTipRect:wideOpenRect
2789                                      owner:self
2790                                   userData:NULL];
2791     [self _sendToolTipMouseEntered];
2792   }
2795 // NSView calls this to get the text when displaying the tooltip.
2796 - (NSString *)view:(NSView *)view
2797   stringForToolTip:(NSToolTipTag)tag
2798              point:(NSPoint)point
2799           userData:(void *)data {
2800   return [[toolTip_ copy] autorelease];
2803 // Below is our NSTextInputClient implementation.
2805 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2806 // functions to process this event.
2808 // [WebHTMLView keyDown] ->
2809 //     EventHandler::keyEvent() ->
2810 //     ...
2811 //     [WebEditorClient handleKeyboardEvent] ->
2812 //     [WebHTMLView _interceptEditingKeyEvent] ->
2813 //     [NSResponder interpretKeyEvents] ->
2814 //     [WebHTMLView insertText] ->
2815 //     Editor::insertText()
2817 // Unfortunately, it is hard for Chromium to use this implementation because
2818 // it causes key-typing jank.
2819 // RenderWidgetHostViewMac is running in a browser process. On the other
2820 // hand, Editor and EventHandler are running in a renderer process.
2821 // So, if we used this implementation, a NSKeyDown event is dispatched to
2822 // the following functions of Chromium.
2824 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2825 //     |Sync IPC (KeyDown)| (*1) ->
2826 //     EventHandler::keyEvent() (renderer) ->
2827 //     ...
2828 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
2829 //     |Sync IPC| (*2) ->
2830 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2831 //     [self interpretKeyEvents] ->
2832 //     [RenderWidgetHostViewMac insertText] (browser) ->
2833 //     |Async IPC| ->
2834 //     Editor::insertText() (renderer)
2836 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2837 // result of EventHandler::keyEvent().
2838 // (*2) we need to wait until this call finishes since WebEditorClient uses
2839 // the result of [WebHTMLView _interceptEditingKeyEvent].
2841 // This needs many sync IPC messages sent between a browser and a renderer for
2842 // each key event, which would probably result in key-typing jank.
2843 // To avoid this problem, this implementation processes key events (and input
2844 // method events) totally in a browser process and sends asynchronous input
2845 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2846 // renderer process.
2848 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2849 //     |Async IPC (RawKeyDown)| ->
2850 //     [self interpretKeyEvents] ->
2851 //     [RenderWidgetHostViewMac insertText] (browser) ->
2852 //     |Async IPC (Char)| ->
2853 //     Editor::insertText() (renderer)
2855 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2856 // make any key-typing jank. --hbono 7/23/09
2858 extern "C" {
2859 extern NSString *NSTextInputReplacementRangeAttributeName;
2862 - (NSArray *)validAttributesForMarkedText {
2863   // This code is just copied from WebKit except renaming variables.
2864   if (!validAttributesForMarkedText_) {
2865     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2866         NSUnderlineStyleAttributeName,
2867         NSUnderlineColorAttributeName,
2868         NSMarkedClauseSegmentAttributeName,
2869         NSTextInputReplacementRangeAttributeName,
2870         nil]);
2871   }
2872   return validAttributesForMarkedText_.get();
2875 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2876   DCHECK([self window]);
2877   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2878   // coordinates (upper left origin). Scroll offsets will be taken care of in
2879   // the renderer.
2880   thePoint = [[self window] convertScreenToBase:thePoint];
2881   thePoint = [self convertPoint:thePoint fromView:nil];
2882   thePoint.y = NSHeight([self frame]) - thePoint.y;
2884   NSUInteger index =
2885       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2886           renderWidgetHostView_->render_widget_host_,
2887           gfx::Point(thePoint.x, thePoint.y));
2888   return index;
2891 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2892                              actualRange:(NSRangePointer)actualRange {
2893   NSRect rect;
2894   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2895           theRange,
2896           &rect,
2897           actualRange)) {
2898     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2899         renderWidgetHostView_->render_widget_host_, theRange);
2901     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2902     if (actualRange)
2903       *actualRange = theRange;
2904   }
2906   // The returned rectangle is in WebKit coordinates (upper left origin), so
2907   // flip the coordinate system.
2908   NSRect viewFrame = [self frame];
2909   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2910   return rect;
2913 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2914                          actualRange:(NSRangePointer)actualRange {
2915   NSRect rect = [self firstViewRectForCharacterRange:theRange
2916                                          actualRange:actualRange];
2918   // Convert into screen coordinates for return.
2919   rect = [self convertRect:rect toView:nil];
2920   rect.origin = [[self window] convertBaseToScreen:rect.origin];
2921   return rect;
2924 - (NSRange)markedRange {
2925   // An input method calls this method to check if an application really has
2926   // a text being composed when hasMarkedText call returns true.
2927   // Returns the range saved in the setMarkedText method so the input method
2928   // calls the setMarkedText method and we can update the composition node
2929   // there. (When this method returns an empty range, the input method doesn't
2930   // call the setMarkedText method.)
2931   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2934 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2935     actualRange:(NSRangePointer)actualRange {
2936   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2937   if (actualRange)
2938     *actualRange = range;
2939   NSAttributedString* str =
2940       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2941           renderWidgetHostView_->render_widget_host_, range);
2942   return str;
2945 - (NSInteger)conversationIdentifier {
2946   return reinterpret_cast<NSInteger>(self);
2949 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2950 // nil when the caret is in non-editable content or password box to avoid
2951 // making input methods do their work.
2952 - (NSTextInputContext *)inputContext {
2953   if (focusedPluginIdentifier_ != -1)
2954     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2956   switch(renderWidgetHostView_->text_input_type_) {
2957     case ui::TEXT_INPUT_TYPE_NONE:
2958     case ui::TEXT_INPUT_TYPE_PASSWORD:
2959       return nil;
2960     default:
2961       return [super inputContext];
2962   }
2965 - (BOOL)hasMarkedText {
2966   // An input method calls this function to figure out whether or not an
2967   // application is really composing a text. If it is composing, it calls
2968   // the markedRange method, and maybe calls the setMarkedText method.
2969   // It seems an input method usually calls this function when it is about to
2970   // cancel an ongoing composition. If an application has a non-empty marked
2971   // range, it calls the setMarkedText method to delete the range.
2972   return hasMarkedText_;
2975 - (void)unmarkText {
2976   // Delete the composition node of the renderer and finish an ongoing
2977   // composition.
2978   // It seems an input method calls the setMarkedText method and set an empty
2979   // text when it cancels an ongoing composition, i.e. I have never seen an
2980   // input method calls this method.
2981   hasMarkedText_ = NO;
2982   markedText_.clear();
2983   underlines_.clear();
2985   // If we are handling a key down event, then ConfirmComposition() will be
2986   // called in keyEvent: method.
2987   if (!handlingKeyDown_) {
2988     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2989         base::string16(), gfx::Range::InvalidRange(), false);
2990   } else {
2991     unmarkTextCalled_ = YES;
2992   }
2995 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
2996                               replacementRange:(NSRange)replacementRange {
2997   // An input method updates the composition string.
2998   // We send the given text and range to the renderer so it can update the
2999   // composition node of WebKit.
3000   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3001   // the full web content.
3002   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3003   NSString* im_text = isAttributedString ? [string string] : string;
3004   int length = [im_text length];
3006   // |markedRange_| will get set on a callback from ImeSetComposition().
3007   selectedRange_ = newSelRange;
3008   markedText_ = base::SysNSStringToUTF16(im_text);
3009   hasMarkedText_ = (length > 0);
3011   underlines_.clear();
3012   if (isAttributedString) {
3013     ExtractUnderlines(string, &underlines_);
3014   } else {
3015     // Use a thin black underline by default.
3016     underlines_.push_back(blink::WebCompositionUnderline(
3017         0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
3018   }
3020   // If we are handling a key down event, then SetComposition() will be
3021   // called in keyEvent: method.
3022   // Input methods of Mac use setMarkedText calls with an empty text to cancel
3023   // an ongoing composition. So, we should check whether or not the given text
3024   // is empty to update the input method state. (Our input method backend can
3025   // automatically cancels an ongoing composition when we send an empty text.
3026   // So, it is OK to send an empty text to the renderer.)
3027   if (!handlingKeyDown_) {
3028     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3029         markedText_, underlines_,
3030         newSelRange.location, NSMaxRange(newSelRange));
3031   }
3034 - (void)doCommandBySelector:(SEL)selector {
3035   // An input method calls this function to dispatch an editing command to be
3036   // handled by this view.
3037   if (selector == @selector(noop:))
3038     return;
3040   std::string command(
3041       [RenderWidgetHostViewMacEditCommandHelper::
3042           CommandNameForSelector(selector) UTF8String]);
3044   // If this method is called when handling a key down event, then we need to
3045   // handle the command in the key event handler. Otherwise we can just handle
3046   // it here.
3047   if (handlingKeyDown_) {
3048     hasEditCommands_ = YES;
3049     // We ignore commands that insert characters, because this was causing
3050     // strange behavior (e.g. tab always inserted a tab rather than moving to
3051     // the next field on the page).
3052     if (!StartsWithASCII(command, "insert", false))
3053       editCommands_.push_back(EditCommand(command, ""));
3054   } else {
3055     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3056     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3057                                               command, ""));
3058   }
3061 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3062   // An input method has characters to be inserted.
3063   // Same as Linux, Mac calls this method not only:
3064   // * when an input method finishs composing text, but also;
3065   // * when we type an ASCII character (without using input methods).
3066   // When we aren't using input methods, we should send the given character as
3067   // a Char event so it is dispatched to an onkeypress() event handler of
3068   // JavaScript.
3069   // On the other hand, when we are using input methods, we should send the
3070   // given characters as an input method event and prevent the characters from
3071   // being dispatched to onkeypress() event handlers.
3072   // Text inserting might be initiated by other source instead of keyboard
3073   // events, such as the Characters dialog. In this case the text should be
3074   // sent as an input method event as well.
3075   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3076   // the full web content.
3077   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3078   NSString* im_text = isAttributedString ? [string string] : string;
3079   if (handlingKeyDown_) {
3080     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3081   } else {
3082     gfx::Range replacement_range(replacementRange);
3083     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3084         base::SysNSStringToUTF16(im_text), replacement_range, false);
3085   }
3087   // Inserting text will delete all marked text automatically.
3088   hasMarkedText_ = NO;
3091 - (void)insertText:(id)string {
3092   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3095 - (void)viewDidMoveToWindow {
3096   if ([self window])
3097     [self updateScreenProperties];
3099   if (canBeKeyView_) {
3100     NSWindow* newWindow = [self window];
3101     // Pointer comparison only, since we don't know if lastWindow_ is still
3102     // valid.
3103     if (newWindow) {
3104       // If we move into a new window, refresh the frame information. We
3105       // don't need to do it if it was the same window as it used to be in,
3106       // since that case is covered by WasShown(). We only want to do this for
3107       // real browser views, not popups.
3108       if (newWindow != lastWindow_) {
3109         lastWindow_ = newWindow;
3110         renderWidgetHostView_->WindowFrameChanged();
3111       }
3112     }
3113   }
3115   // If we switch windows (or are removed from the view hierarchy), cancel any
3116   // open mouse-downs.
3117   if (hasOpenMouseDown_) {
3118     WebMouseEvent event;
3119     event.type = WebInputEvent::MouseUp;
3120     event.button = WebMouseEvent::ButtonLeft;
3121     renderWidgetHostView_->ForwardMouseEvent(event);
3123     hasOpenMouseDown_ = NO;
3124   }
3127 - (void)undo:(id)sender {
3128   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3129   if (web_contents)
3130     web_contents->Undo();
3133 - (void)redo:(id)sender {
3134   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3135   if (web_contents)
3136     web_contents->Redo();
3139 - (void)cut:(id)sender {
3140   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3141   if (web_contents)
3142     web_contents->Cut();
3145 - (void)copy:(id)sender {
3146   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3147   if (web_contents)
3148     web_contents->Copy();
3151 - (void)copyToFindPboard:(id)sender {
3152   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3153   if (web_contents)
3154     web_contents->CopyToFindPboard();
3157 - (void)paste:(id)sender {
3158   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3159   if (web_contents)
3160     web_contents->Paste();
3163 - (void)pasteAndMatchStyle:(id)sender {
3164   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3165   if (web_contents)
3166     web_contents->PasteAndMatchStyle();
3169 - (void)selectAll:(id)sender {
3170   // editCommand_helper_ adds implementations for most NSResponder methods
3171   // dynamically. But the renderer side only sends selection results back to
3172   // the browser if they were triggered by a keyboard event or went through
3173   // one of the Select methods on RWH. Since selectAll: is called from the
3174   // menu handler, neither is true.
3175   // Explicitly call SelectAll() here to make sure the renderer returns
3176   // selection results.
3177   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3178   if (web_contents)
3179     web_contents->SelectAll();
3182 - (void)startSpeaking:(id)sender {
3183   renderWidgetHostView_->SpeakSelection();
3186 - (void)stopSpeaking:(id)sender {
3187   renderWidgetHostView_->StopSpeaking();
3190 - (void)cancelComposition {
3191   if (!hasMarkedText_)
3192     return;
3194   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3195   // doesn't call any NSTextInput functions, such as setMarkedText or
3196   // insertText. So, we need to send an IPC message to a renderer so it can
3197   // delete the composition node.
3198   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3199   [currentInputManager markedTextAbandoned:self];
3201   hasMarkedText_ = NO;
3202   // Should not call [self unmarkText] here, because it'll send unnecessary
3203   // cancel composition IPC message to the renderer.
3206 - (void)confirmComposition {
3207   if (!hasMarkedText_)
3208     return;
3210   if (renderWidgetHostView_->render_widget_host_)
3211     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3212         base::string16(), gfx::Range::InvalidRange(), false);
3214   [self cancelComposition];
3217 - (void)setPluginImeActive:(BOOL)active {
3218   if (active == pluginImeActive_)
3219     return;
3221   pluginImeActive_ = active;
3222   if (!active) {
3223     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3224     renderWidgetHostView_->PluginImeCompositionCompleted(
3225         base::string16(), focusedPluginIdentifier_);
3226   }
3229 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3230   if (focused)
3231     focusedPluginIdentifier_ = pluginId;
3232   else if (focusedPluginIdentifier_ == pluginId)
3233     focusedPluginIdentifier_ = -1;
3235   // Whenever plugin focus changes, plugin IME resets.
3236   [self setPluginImeActive:NO];
3239 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3240   if (!pluginImeActive_)
3241     return false;
3243   ComplexTextInputPanel* inputPanel =
3244       [ComplexTextInputPanel sharedComplexTextInputPanel];
3245   NSString* composited_string = nil;
3246   BOOL handled = [inputPanel interpretKeyEvent:event
3247                                         string:&composited_string];
3248   if (composited_string) {
3249     renderWidgetHostView_->PluginImeCompositionCompleted(
3250         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3251     pluginImeActive_ = NO;
3252   }
3253   return handled;
3256 - (void)checkForPluginImeCancellation {
3257   if (pluginImeActive_ &&
3258       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3259     renderWidgetHostView_->PluginImeCompositionCompleted(
3260         base::string16(), focusedPluginIdentifier_);
3261     pluginImeActive_ = NO;
3262   }
3265 // Overriding a NSResponder method to support application services.
3267 - (id)validRequestorForSendType:(NSString*)sendType
3268                      returnType:(NSString*)returnType {
3269   id requestor = nil;
3270   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3271   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3272   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3273   BOOL takesText =
3274       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3276   if (sendTypeIsString && hasText && !returnType) {
3277     requestor = self;
3278   } else if (!sendType && returnTypeIsString && takesText) {
3279     requestor = self;
3280   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3281     requestor = self;
3282   } else {
3283     requestor = [super validRequestorForSendType:sendType
3284                                       returnType:returnType];
3285   }
3286   return requestor;
3289 - (void)viewWillStartLiveResize {
3290   [super viewWillStartLiveResize];
3291   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3292   if (widget)
3293     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3296 - (void)viewDidEndLiveResize {
3297   [super viewDidEndLiveResize];
3298   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3299   if (widget)
3300     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3303 - (void)updateCursor:(NSCursor*)cursor {
3304   if (currentCursor_ == cursor)
3305     return;
3307   currentCursor_.reset([cursor retain]);
3308   [[self window] invalidateCursorRectsForView:self];
3311 - (void)popupWindowWillClose:(NSNotification *)notification {
3312   renderWidgetHostView_->KillSelf();
3315 @end
3318 // Supporting application services
3320 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3322 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3323                              types:(NSArray*)types {
3324   const std::string& str = renderWidgetHostView_->selected_text();
3325   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3327   base::scoped_nsobject<NSString> text(
3328       [[NSString alloc] initWithUTF8String:str.c_str()]);
3329   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3330   [pboard declareTypes:toDeclare owner:nil];
3331   return [pboard setString:text forType:NSStringPboardType];
3334 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3335   NSString *string = [pboard stringForType:NSStringPboardType];
3336   if (!string) return NO;
3338   // If the user is currently using an IME, confirm the IME input,
3339   // and then insert the text from the service, the same as TextEdit and Safari.
3340   [self confirmComposition];
3341   [self insertText:string];
3342   return YES;
3345 - (BOOL)isOpaque {
3346   return YES;
3349 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3350 // regions that are not draggable. (See ControlRegionView in
3351 // native_app_window_cocoa.mm). This requires the render host view to be
3352 // draggable by default.
3353 - (BOOL)mouseDownCanMoveWindow {
3354   return YES;
3357 @end