Use a separate NSView to draw browser composited content
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac.mm
blob8a6574ff933867c40ee22891a06b7b79f0388215
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 <QuartzCore/QuartzCore.h>
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/command_line.h"
14 #include "base/debug/crash_logging.h"
15 #include "base/debug/trace_event.h"
16 #include "base/logging.h"
17 #include "base/mac/mac_util.h"
18 #include "base/mac/scoped_cftyperef.h"
19 #import "base/mac/scoped_nsobject.h"
20 #include "base/mac/sdk_forward_declarations.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/metrics/histogram.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/sys_string_conversions.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/sys_info.h"
28 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
29 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
30 #include "content/browser/compositor/browser_compositor_view_mac.h"
31 #include "content/browser/compositor/resize_lock.h"
32 #include "content/browser/frame_host/frame_tree.h"
33 #include "content/browser/frame_host/frame_tree_node.h"
34 #include "content/browser/frame_host/render_frame_host_impl.h"
35 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
36 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
37 #include "content/browser/renderer_host/compositing_iosurface_mac.h"
38 #include "content/browser/renderer_host/render_widget_helper.h"
39 #include "content/browser/renderer_host/render_view_host_impl.h"
40 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
41 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
42 #import "content/browser/renderer_host/software_layer_mac.h"
43 #import "content/browser/renderer_host/text_input_client_mac.h"
44 #include "content/common/accessibility_messages.h"
45 #include "content/common/edit_command.h"
46 #include "content/common/gpu/gpu_messages.h"
47 #include "content/common/input_messages.h"
48 #include "content/common/view_messages.h"
49 #include "content/common/webplugin_geometry.h"
50 #include "content/public/browser/browser_thread.h"
51 #include "content/public/browser/native_web_keyboard_event.h"
52 #include "content/public/browser/notification_service.h"
53 #include "content/public/browser/notification_types.h"
54 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
55 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
56 #include "content/public/browser/user_metrics.h"
57 #include "content/public/browser/web_contents.h"
58 #include "skia/ext/platform_canvas.h"
59 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
60 #include "third_party/WebKit/public/web/WebInputEvent.h"
61 #include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
62 #import "third_party/mozilla/ComplexTextInputPanel.h"
63 #include "ui/base/cocoa/animation_utils.h"
64 #import "ui/base/cocoa/fullscreen_window_manager.h"
65 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
66 #include "ui/events/keycodes/keyboard_codes.h"
67 #include "ui/base/layout.h"
68 #include "ui/compositor/compositor.h"
69 #include "ui/compositor/layer.h"
70 #include "ui/gfx/display.h"
71 #include "ui/gfx/point.h"
72 #include "ui/gfx/rect_conversions.h"
73 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
74 #include "ui/gfx/screen.h"
75 #include "ui/gfx/size_conversions.h"
76 #include "ui/gl/gl_switches.h"
77 #include "ui/gl/io_surface_support_mac.h"
79 using content::BrowserAccessibility;
80 using content::BrowserAccessibilityManager;
81 using content::EditCommand;
82 using content::FrameTreeNode;
83 using content::NativeWebKeyboardEvent;
84 using content::RenderFrameHost;
85 using content::RenderViewHost;
86 using content::RenderViewHostImpl;
87 using content::RenderWidgetHostImpl;
88 using content::RenderWidgetHostViewMac;
89 using content::RenderWidgetHostViewMacEditCommandHelper;
90 using content::TextInputClientMac;
91 using content::WebContents;
92 using blink::WebInputEvent;
93 using blink::WebInputEventFactory;
94 using blink::WebMouseEvent;
95 using blink::WebMouseWheelEvent;
96 using blink::WebGestureEvent;
98 // These are not documented, so use only after checking -respondsToSelector:.
99 @interface NSApplication (UndocumentedSpeechMethods)
100 - (void)speakString:(NSString*)string;
101 - (void)stopSpeaking:(id)sender;
102 - (BOOL)isSpeaking;
103 @end
105 // Declare things that are part of the 10.7 SDK.
106 #if !defined(MAC_OS_X_VERSION_10_7) || \
107     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
108 @interface NSView (NSOpenGLSurfaceResolutionLionAPI)
109 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
110 @end
112 static NSString* const NSWindowDidChangeBackingPropertiesNotification =
113     @"NSWindowDidChangeBackingPropertiesNotification";
115 #endif  // 10.7
117 // Declare things that are part of the 10.9 SDK.
118 #if !defined(MAC_OS_X_VERSION_10_9) || \
119     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
120 enum {
121    NSWindowOcclusionStateVisible = 1UL << 1,
123 typedef NSUInteger NSWindowOcclusionState;
125 @interface NSWindow (MavericksAPI)
126 - (NSWindowOcclusionState)occlusionState;
127 @end
129 #endif  // 10.9
131 // This method will return YES for OS X versions 10.7.3 and later, and NO
132 // otherwise.
133 // Used to prevent a crash when building with the 10.7 SDK and accessing the
134 // notification below. See: http://crbug.com/260595.
135 static BOOL SupportsBackingPropertiesChangedNotification() {
136   // windowDidChangeBackingProperties: method has been added to the
137   // NSWindowDelegate protocol in 10.7.3, at the same time as the
138   // NSWindowDidChangeBackingPropertiesNotification notification was added.
139   // If the protocol contains this method description, the notification should
140   // be supported as well.
141   Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
142   struct objc_method_description methodDescription =
143       protocol_getMethodDescription(
144           windowDelegateProtocol,
145           @selector(windowDidChangeBackingProperties:),
146           NO,
147           YES);
149   // If the protocol does not contain the method, the returned method
150   // description is {NULL, NULL}
151   return methodDescription.name != NULL || methodDescription.types != NULL;
154 // Private methods:
155 @interface RenderWidgetHostViewCocoa ()
156 @property(nonatomic, assign) NSRange selectedRange;
157 @property(nonatomic, assign) NSRange markedRange;
159 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
160 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
161 - (void)gotUnhandledWheelEvent;
162 - (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right;
163 - (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar;
164 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
165 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
166 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
167 - (void)drawWithDirtyRect:(CGRect)dirtyRect
168                 inContext:(CGContextRef)context;
169 - (void)checkForPluginImeCancellation;
170 - (void)updateScreenProperties;
171 - (void)setResponderDelegate:
172         (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
173 @end
175 // A window subclass that allows the fullscreen window to become main and gain
176 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
177 // handled by the browser.
178 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
179 @end
181 @implementation PepperFlashFullscreenWindow
183 - (BOOL)canBecomeKeyWindow {
184   return YES;
187 - (BOOL)canBecomeMainWindow {
188   return YES;
191 @end
193 @interface RenderWidgetPopupWindow : NSWindow {
194    // The event tap that allows monitoring of all events, to properly close with
195    // a click outside the bounds of the window.
196   id clickEventTap_;
198 @end
200 @implementation RenderWidgetPopupWindow
202 - (id)initWithContentRect:(NSRect)contentRect
203                 styleMask:(NSUInteger)windowStyle
204                   backing:(NSBackingStoreType)bufferingType
205                     defer:(BOOL)deferCreation {
206   if (self = [super initWithContentRect:contentRect
207                               styleMask:windowStyle
208                                 backing:bufferingType
209                                   defer:deferCreation]) {
210     DCHECK_EQ(content::CORE_ANIMATION_DISABLED,
211               content::GetCoreAnimationStatus());
212     [self setOpaque:NO];
213     [self setBackgroundColor:[NSColor clearColor]];
214     [self startObservingClicks];
215   }
216   return self;
219 - (void)close {
220   [self stopObservingClicks];
221   [super close];
224 // Gets called when the menubar is clicked.
225 // Needed because the local event monitor doesn't see the click on the menubar.
226 - (void)beganTracking:(NSNotification*)notification {
227   [self close];
230 // Install the callback.
231 - (void)startObservingClicks {
232   clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
233       handler:^NSEvent* (NSEvent* event) {
234           if ([event window] == self)
235             return event;
236           NSEventType eventType = [event type];
237           if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
238             [self close];
239           return event;
240   }];
242   NSNotificationCenter* notificationCenter =
243       [NSNotificationCenter defaultCenter];
244   [notificationCenter addObserver:self
245          selector:@selector(beganTracking:)
246              name:NSMenuDidBeginTrackingNotification
247            object:[NSApp mainMenu]];
250 // Remove the callback.
251 - (void)stopObservingClicks {
252   if (!clickEventTap_)
253     return;
255   [NSEvent removeMonitor:clickEventTap_];
256    clickEventTap_ = nil;
258   NSNotificationCenter* notificationCenter =
259       [NSNotificationCenter defaultCenter];
260   [notificationCenter removeObserver:self
261                 name:NSMenuDidBeginTrackingNotification
262               object:[NSApp mainMenu]];
265 @end
267 namespace {
269 // Maximum number of characters we allow in a tooltip.
270 const size_t kMaxTooltipLength = 1024;
272 // TODO(suzhe): Upstream this function.
273 blink::WebColor WebColorFromNSColor(NSColor *color) {
274   CGFloat r, g, b, a;
275   [color getRed:&r green:&g blue:&b alpha:&a];
277   return
278       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
279       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
280       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
281       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
284 // Extract underline information from an attributed string. Mostly copied from
285 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
286 void ExtractUnderlines(
287     NSAttributedString* string,
288     std::vector<blink::WebCompositionUnderline>* underlines) {
289   int length = [[string string] length];
290   int i = 0;
291   while (i < length) {
292     NSRange range;
293     NSDictionary* attrs = [string attributesAtIndex:i
294                               longestEffectiveRange:&range
295                                             inRange:NSMakeRange(i, length - i)];
296     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
297       blink::WebColor color = SK_ColorBLACK;
298       if (NSColor *colorAttr =
299           [attrs objectForKey:NSUnderlineColorAttributeName]) {
300         color = WebColorFromNSColor(
301             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
302       }
303       underlines->push_back(blink::WebCompositionUnderline(
304           range.location, NSMaxRange(range), color, [style intValue] > 1));
305     }
306     i = range.location + range.length;
307   }
310 // EnablePasswordInput() and DisablePasswordInput() are copied from
311 // enableSecureTextInput() and disableSecureTextInput() functions in
312 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
313 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
314 // here, because they are already called in webkit and they are system wide
315 // functions.
316 void EnablePasswordInput() {
317   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
318   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
319                          sizeof(CFArrayRef), &inputSources);
320   CFRelease(inputSources);
323 void DisablePasswordInput() {
324   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
327 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
328 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
329 // value when screen info changes.
330 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
331 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
332 // is necessary.
333 bool g_screen_info_up_to_date = false;
335 float FlipYFromRectToScreen(float y, float rect_height) {
336   TRACE_EVENT0("browser", "FlipYFromRectToScreen");
337   static CGFloat screen_zero_height = 0;
338   if (!g_screen_info_up_to_date) {
339     if ([[NSScreen screens] count] > 0) {
340       screen_zero_height =
341           [[[NSScreen screens] objectAtIndex:0] frame].size.height;
342       g_screen_info_up_to_date = true;
343     } else {
344       return y;
345     }
346   }
347   return screen_zero_height - y - rect_height;
350 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
351 // left of the primary screen (Carbon coordinates), and stuffs it into a
352 // gfx::Rect.
353 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
354   gfx::Rect new_rect(NSRectToCGRect(rect));
355   new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
356   return new_rect;
359 // Returns the window that visually contains the given view. This is different
360 // from [view window] in the case of tab dragging, where the view's owning
361 // window is a floating panel attached to the actual browser window that the tab
362 // is visually part of.
363 NSWindow* ApparentWindowForView(NSView* view) {
364   // TODO(shess): In case of !window, the view has been removed from
365   // the view hierarchy because the tab isn't main.  Could retrieve
366   // the information from the main tab for our window.
367   NSWindow* enclosing_window = [view window];
369   // See if this is a tab drag window. The width check is to distinguish that
370   // case from extension popup windows.
371   NSWindow* ancestor_window = [enclosing_window parentWindow];
372   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
373                           NSWidth([ancestor_window frame]))) {
374     enclosing_window = ancestor_window;
375   }
377   return enclosing_window;
380 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
381   gfx::Display display =
382       gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
384   NSScreen* screen = [NSScreen deepestScreen];
386   blink::WebScreenInfo results;
388   results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
389   results.depth = NSBitsPerPixelFromDepth([screen depth]);
390   results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
391   results.isMonochrome =
392       [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
393   results.rect = display.bounds();
394   results.availableRect = display.work_area();
395   results.orientationAngle = display.RotationAsDegree();
397   return results;
400 void RemoveLayerFromSuperlayer(
401     base::scoped_nsobject<CompositingIOSurfaceLayer> layer) {
402   // Disable the fade-out animation as the layer is removed.
403   ScopedCAActionDisabler disabler;
404   [layer removeFromSuperlayer];
407 }  // namespace
409 namespace content {
411 ////////////////////////////////////////////////////////////////////////////////
412 // DelegatedFrameHost, public:
414 ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
415   return [browser_compositor_view_ compositor];
418 ui::Layer* RenderWidgetHostViewMac::GetLayer() {
419   return root_layer_.get();
422 RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
423   return render_widget_host_;
426 void RenderWidgetHostViewMac::SchedulePaintInRect(
427     const gfx::Rect& damage_rect_in_dip) {
428   [browser_compositor_view_ compositor]->ScheduleFullRedraw();
431 bool RenderWidgetHostViewMac::IsVisible() {
432   return !render_widget_host_->is_hidden();
435 gfx::Size RenderWidgetHostViewMac::DesiredFrameSize() {
436   return GetViewBounds().size();
439 float RenderWidgetHostViewMac::CurrentDeviceScaleFactor() {
440   return ViewScaleFactor();
443 gfx::Size RenderWidgetHostViewMac::ConvertViewSizeToPixel(
444     const gfx::Size& size) {
445   return gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size),
446                                              ViewScaleFactor())).size();
449 scoped_ptr<ResizeLock> RenderWidgetHostViewMac::CreateResizeLock(
450     bool defer_compositor_lock) {
451   NOTREACHED();
452   ResizeLock* lock = NULL;
453   return scoped_ptr<ResizeLock>(lock);
456 DelegatedFrameHost* RenderWidgetHostViewMac::GetDelegatedFrameHost() const {
457   return delegated_frame_host_.get();
460 ///////////////////////////////////////////////////////////////////////////////
461 // RenderWidgetHostViewBase, public:
463 // static
464 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
465     blink::WebScreenInfo* results) {
466   *results = GetWebScreenInfo(NULL);
469 ///////////////////////////////////////////////////////////////////////////////
470 // RenderWidgetHostViewMac, public:
472 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
473     : render_widget_host_(RenderWidgetHostImpl::From(widget)),
474       last_frame_was_accelerated_(false),
475       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
476       can_compose_inline_(true),
477       allow_overlapping_views_(false),
478       use_core_animation_(false),
479       pending_latency_info_delay_(0),
480       pending_latency_info_delay_weak_ptr_factory_(this),
481       backing_store_scale_factor_(1),
482       is_loading_(false),
483       weak_factory_(this),
484       fullscreen_parent_host_view_(NULL),
485       underlay_view_has_drawn_(false),
486       overlay_view_weak_factory_(this),
487       software_frame_weak_ptr_factory_(this) {
488   software_frame_manager_.reset(new SoftwareFrameManager(
489       software_frame_weak_ptr_factory_.GetWeakPtr()));
490   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
491   // goes away.  Since we autorelease it, our caller must put
492   // |GetNativeView()| into the view hierarchy right after calling us.
493   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
494                   initWithRenderWidgetHostViewMac:this] autorelease];
496   if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED) {
497     use_core_animation_ = true;
498     background_layer_.reset([[CALayer alloc] init]);
499     [background_layer_
500         setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
501     [cocoa_view_ setLayer:background_layer_];
502     [cocoa_view_ setWantsLayer:YES];
503   }
505   render_widget_host_->SetView(this);
508 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
509   // This is being called from |cocoa_view_|'s destructor, so invalidate the
510   // pointer.
511   cocoa_view_ = nil;
513   // Delete the delegated frame state.
514   delegated_frame_host_.reset();
515   root_layer_.reset();
517   UnlockMouse();
519   // Make sure that the layer doesn't reach into the now-invalid object.
520   DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
521   DestroySoftwareLayer();
523   // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
524   // RenderWidgetHost does we need to tell it not to hold a stale pointer to
525   // us.
526   if (render_widget_host_)
527     render_widget_host_->SetView(NULL);
530 void RenderWidgetHostViewMac::SetDelegate(
531     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
532   [cocoa_view_ setResponderDelegate:delegate];
535 void RenderWidgetHostViewMac::SetAllowOverlappingViews(bool overlapping) {
536   if (allow_overlapping_views_ == overlapping)
537     return;
538   allow_overlapping_views_ = overlapping;
539   [cocoa_view_ setNeedsDisplay:YES];
540   [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
543 ///////////////////////////////////////////////////////////////////////////////
544 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
546 bool RenderWidgetHostViewMac::EnsureCompositedIOSurface() {
547   // If the context or the IOSurface's context has had an error, re-build
548   // everything from scratch.
549   if (compositing_iosurface_context_ &&
550       compositing_iosurface_context_->HasBeenPoisoned()) {
551     LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
552                << "context was poisoned";
553     return false;
554   }
555   if (compositing_iosurface_ &&
556       compositing_iosurface_->HasBeenPoisoned()) {
557     LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
558                << "surface was poisoned";
559     return false;
560   }
562   int current_window_number = use_core_animation_ ?
563       CompositingIOSurfaceContext::kOffscreenContextWindowNumber :
564       window_number();
565   bool new_surface_needed = !compositing_iosurface_;
566   bool new_context_needed =
567     !compositing_iosurface_context_ ||
568         (compositing_iosurface_context_ &&
569             compositing_iosurface_context_->window_number() !=
570                 current_window_number);
572   if (!new_surface_needed && !new_context_needed)
573     return true;
575   // Create the GL context and shaders.
576   if (new_context_needed) {
577     scoped_refptr<CompositingIOSurfaceContext> new_context =
578         CompositingIOSurfaceContext::Get(current_window_number);
579     // Un-bind the GL context from this view before binding the new GL
580     // context. Having two GL contexts bound to a view will result in
581     // crashes and corruption.
582     // http://crbug.com/230883
583     ClearBoundContextDrawable();
584     if (!new_context) {
585       LOG(ERROR) << "Failed to create CompositingIOSurfaceContext";
586       return false;
587     }
588     compositing_iosurface_context_ = new_context;
589   }
591   // Create the IOSurface texture.
592   if (new_surface_needed) {
593     compositing_iosurface_ = CompositingIOSurfaceMac::Create();
594     if (!compositing_iosurface_) {
595       LOG(ERROR) << "Failed to create CompositingIOSurface";
596       return false;
597     }
598   }
600   return true;
603 void RenderWidgetHostViewMac::EnsureSoftwareLayer() {
604   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::EnsureSoftwareLayer");
605   if (software_layer_ || !use_core_animation_)
606     return;
608   software_layer_.reset([[SoftwareLayer alloc] init]);
609   DCHECK(software_layer_);
611   // Disable the fade-in animation as the layer is added.
612   ScopedCAActionDisabler disabler;
613   [background_layer_ addSublayer:software_layer_];
616 void RenderWidgetHostViewMac::DestroySoftwareLayer() {
617   if (!software_layer_)
618     return;
620   // Disable the fade-out animation as the layer is removed.
621   ScopedCAActionDisabler disabler;
622   [software_layer_ removeFromSuperlayer];
623   software_layer_.reset();
626 void RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer() {
627   TRACE_EVENT0("browser",
628                "RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer");
629   DCHECK(compositing_iosurface_context_);
630   if (compositing_iosurface_layer_ || !use_core_animation_)
631     return;
633   compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc]
634       initWithIOSurface:compositing_iosurface_
635              withClient:this]);
636   DCHECK(compositing_iosurface_layer_);
638   // Disable the fade-in animation as the layer is added.
639   ScopedCAActionDisabler disabler;
640   [background_layer_ addSublayer:compositing_iosurface_layer_];
643 void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceLayer(
644     DestroyCompositedIOSurfaceLayerBehavior destroy_layer_behavior) {
645   if (!compositing_iosurface_layer_)
646     return;
648   if (destroy_layer_behavior == kRemoveLayerFromHierarchy) {
649     // Disable the fade-out animation as the layer is removed.
650     ScopedCAActionDisabler disabler;
651     [compositing_iosurface_layer_ removeFromSuperlayer];
652   }
653   [compositing_iosurface_layer_ resetClient];
654   compositing_iosurface_layer_.reset();
657 void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer(
658     DestroyContextBehavior destroy_context_behavior) {
659   // Any pending frames will not be displayed, so ack them now.
660   SendPendingSwapAck();
662   DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
663   compositing_iosurface_ = NULL;
665   switch (destroy_context_behavior) {
666     case kLeaveContextBoundToView:
667       break;
668     case kDestroyContext:
669       ClearBoundContextDrawable();
670       compositing_iosurface_context_ = NULL;
671       break;
672     default:
673       NOTREACHED();
674       break;
675   }
678 void RenderWidgetHostViewMac::ClearBoundContextDrawable() {
679   if (use_core_animation_)
680     return;
682   if (compositing_iosurface_context_ &&
683       cocoa_view_ &&
684       [[compositing_iosurface_context_->nsgl_context() view]
685           isEqual:cocoa_view_]) {
686     // Disable screen updates because removing the GL context from below can
687     // cause flashes.
688     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
689     [compositing_iosurface_context_->nsgl_context() clearDrawable];
690   }
693 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
694   bool handled = true;
695   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
696     IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
697     IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
698     IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeScrollbarsForMainFrame,
699                         OnDidChangeScrollbarsForMainFrame)
700     IPC_MESSAGE_UNHANDLED(handled = false)
701   IPC_END_MESSAGE_MAP()
702   return handled;
705 void RenderWidgetHostViewMac::InitAsChild(
706     gfx::NativeView parent_view) {
709 void RenderWidgetHostViewMac::InitAsPopup(
710     RenderWidgetHostView* parent_host_view,
711     const gfx::Rect& pos) {
712   bool activatable = popup_type_ == blink::WebPopupTypeNone;
713   [cocoa_view_ setCloseOnDeactivate:YES];
714   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
716   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
717   origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
719   popup_window_.reset([[RenderWidgetPopupWindow alloc]
720       initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
721                                      pos.width(), pos.height())
722                 styleMask:NSBorderlessWindowMask
723                   backing:NSBackingStoreBuffered
724                     defer:NO]);
725   [popup_window_ setLevel:NSPopUpMenuWindowLevel];
726   [popup_window_ setReleasedWhenClosed:NO];
727   [popup_window_ makeKeyAndOrderFront:nil];
728   [[popup_window_ contentView] addSubview:cocoa_view_];
729   [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
730   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
731   [[NSNotificationCenter defaultCenter]
732       addObserver:cocoa_view_
733          selector:@selector(popupWindowWillClose:)
734              name:NSWindowWillCloseNotification
735            object:popup_window_];
738 // This function creates the fullscreen window and hides the dock and menubar if
739 // necessary. Note, this codepath is only used for pepper flash when
740 // pp::FlashFullScreen::SetFullscreen() is called. If
741 // pp::FullScreen::SetFullscreen() is called then the entire browser window
742 // will enter fullscreen instead.
743 void RenderWidgetHostViewMac::InitAsFullscreen(
744     RenderWidgetHostView* reference_host_view) {
745   fullscreen_parent_host_view_ =
746       static_cast<RenderWidgetHostViewMac*>(reference_host_view);
747   NSWindow* parent_window = nil;
748   if (reference_host_view)
749     parent_window = [reference_host_view->GetNativeView() window];
750   NSScreen* screen = [parent_window screen];
751   if (!screen)
752     screen = [NSScreen mainScreen];
754   pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
755       initWithContentRect:[screen frame]
756                 styleMask:NSBorderlessWindowMask
757                   backing:NSBackingStoreBuffered
758                     defer:NO]);
759   [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
760   [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
761   [cocoa_view_ setCanBeKeyView:YES];
762   [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
763   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
764   // If the pepper fullscreen window isn't opaque then there are performance
765   // issues when it's on the discrete GPU and the Chrome window is being drawn
766   // to. http://crbug.com/171911
767   [pepper_fullscreen_window_ setOpaque:YES];
769   // Note that this forms a reference cycle between the fullscreen window and
770   // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
771   // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
772   // This cycle is normally broken when -keyEvent: receives an <esc> key, which
773   // explicitly calls Shutdown on the render_widget_host_, which calls
774   // Destroy() on RWHVMac, which drops the reference to
775   // pepper_fullscreen_window_.
776   [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
778   // Note that this keeps another reference to pepper_fullscreen_window_.
779   fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
780       initWithWindow:pepper_fullscreen_window_.get()
781        desiredScreen:screen]);
782   [fullscreen_window_manager_ enterFullscreenMode];
783   [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
786 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
787   // See comment in InitAsFullscreen(): There is a reference cycle between
788   // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
789   // Tests that test pepper fullscreen mode without sending an <esc> event
790   // need to call this method to break the reference cycle.
791   [fullscreen_window_manager_ exitFullscreenMode];
792   fullscreen_window_manager_.reset();
793   [pepper_fullscreen_window_ close];
794   pepper_fullscreen_window_.reset();
797 int RenderWidgetHostViewMac::window_number() const {
798   NSWindow* window = [cocoa_view_ window];
799   if (!window)
800     return -1;
801   return [window windowNumber];
804 float RenderWidgetHostViewMac::ViewScaleFactor() const {
805   return ui::GetScaleFactorForNativeView(cocoa_view_);
808 void RenderWidgetHostViewMac::UpdateDisplayLink() {
809   static bool is_vsync_disabled =
810       CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
811   if (is_vsync_disabled)
812     return;
814   NSScreen* screen = [[cocoa_view_ window] screen];
815   NSDictionary* screen_description = [screen deviceDescription];
816   NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
817   CGDirectDisplayID display_id = [screen_number unsignedIntValue];
819   display_link_ = DisplayLinkMac::GetForDisplay(display_id);
820   if (!display_link_) {
821     // Note that on some headless systems, the display link will fail to be
822     // created, so this should not be a fatal error.
823     LOG(ERROR) << "Failed to create display link.";
824   }
827 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
828   if (!render_widget_host_ || !display_link_)
829     return;
831   base::TimeTicks timebase;
832   base::TimeDelta interval;
833   if (!display_link_->GetVSyncParameters(&timebase, &interval))
834     return;
836   render_widget_host_->UpdateVSyncParameters(timebase, interval);
839 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
840   if (!render_widget_host_)
841     return;
843   float new_scale_factor = ui::GetScaleFactorForNativeView(cocoa_view_);
844   if (new_scale_factor == backing_store_scale_factor_)
845     return;
846   backing_store_scale_factor_ = new_scale_factor;
848   render_widget_host_->NotifyScreenInfoChanged();
851 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
852   return render_widget_host_;
855 void RenderWidgetHostViewMac::WasShown() {
856   if (!render_widget_host_->is_hidden())
857     return;
859   if (web_contents_switch_paint_time_.is_null())
860     web_contents_switch_paint_time_ = base::TimeTicks::Now();
861   render_widget_host_->WasShown();
862   software_frame_manager_->SetVisibility(true);
863   if (delegated_frame_host_)
864     delegated_frame_host_->WasShown();
866   // Call setNeedsDisplay before pausing for new frames to come in -- if any
867   // do, and are drawn, then the needsDisplay bit will be cleared.
868   [compositing_iosurface_layer_ setNeedsDisplay];
869   PauseForPendingResizeOrRepaintsAndDraw();
871   // We're messing with the window, so do this to ensure no flashes.
872   if (!use_core_animation_)
873     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
876 void RenderWidgetHostViewMac::WasHidden() {
877   if (render_widget_host_->is_hidden())
878     return;
880   // Any pending frames will not be displayed until this is shown again. Ack
881   // them now.
882   SendPendingSwapAck();
884   // If we have a renderer, then inform it that we are being hidden so it can
885   // reduce its resource utilization.
886   render_widget_host_->WasHidden();
887   software_frame_manager_->SetVisibility(false);
888   if (delegated_frame_host_)
889     delegated_frame_host_->WasHidden();
891   // There can be a transparent flash as this view is removed and the next is
892   // added, because of OSX windowing races between displaying the contents of
893   // the NSView and its corresponding OpenGL context.
894   // disableScreenUpdatesUntilFlush prevents the transparent flash by avoiding
895   // screen updates until the next tab draws.
896   if (!use_core_animation_)
897     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
899   web_contents_switch_paint_time_ = base::TimeTicks();
902 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
903   gfx::Rect rect = GetViewBounds();
904   rect.set_size(size);
905   SetBounds(rect);
908 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
909   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
910   // TODO(thakis): fix, http://crbug.com/73362
911   if (render_widget_host_->is_hidden())
912     return;
914   // During the initial creation of the RenderWidgetHostView in
915   // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
916   // an empty size. In the Windows code flow, it is not ignored because
917   // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
918   // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
919   // flags to keep things sized properly. On the other hand, if the size is not
920   // empty then this is a valid request for a pop-up.
921   if (rect.size().IsEmpty())
922     return;
924   // Ignore the position of |rect| for non-popup rwhvs. This is because
925   // background tabs do not have a window, but the window is required for the
926   // coordinate conversions. Popups are always for a visible tab.
927   //
928   // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
929   // valid for resizing to be requested (e.g., during tab capture, to size the
930   // view to screen-capture resolution). In this case, simply treat the view as
931   // relative to the screen.
932   BOOL isRelativeToScreen = IsPopup() ||
933       ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
934   if (isRelativeToScreen) {
935     // The position of |rect| is screen coordinate system and we have to
936     // consider Cocoa coordinate system is upside-down and also multi-screen.
937     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
938     NSSize size = NSMakeSize(rect.width(), rect.height());
939     size = [cocoa_view_ convertSize:size toView:nil];
940     origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
941     NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
942                               size.width, size.height);
943     if (IsPopup())
944       [popup_window_ setFrame:frame display:YES];
945     else
946       [cocoa_view_ setFrame:frame];
947   } else {
948     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
949     gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
950     rect2.set_width(rect.width());
951     rect2.set_height(rect.height());
952     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
953   }
956 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
957   return cocoa_view_;
960 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
961   return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
964 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
965   NOTIMPLEMENTED();
966   return static_cast<gfx::NativeViewAccessible>(NULL);
969 void RenderWidgetHostViewMac::MovePluginWindows(
970     const std::vector<WebPluginGeometry>& moves) {
971   // Must be overridden, but unused on this platform. Core Animation
972   // plugins are drawn by the GPU process (through the compositor),
973   // and Core Graphics plugins are drawn by the renderer process.
974   DCHECK_CURRENTLY_ON(BrowserThread::UI);
977 void RenderWidgetHostViewMac::Focus() {
978   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
981 void RenderWidgetHostViewMac::Blur() {
982   UnlockMouse();
983   [[cocoa_view_ window] makeFirstResponder:nil];
986 bool RenderWidgetHostViewMac::HasFocus() const {
987   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
990 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
991   if (delegated_frame_host_)
992     return delegated_frame_host_->CanCopyToBitmap();
994   return software_frame_manager_->HasCurrentFrame() ||
995          (compositing_iosurface_ && compositing_iosurface_->HasIOSurface());
998 void RenderWidgetHostViewMac::Show() {
999   [cocoa_view_ setHidden:NO];
1001   WasShown();
1004 void RenderWidgetHostViewMac::Hide() {
1005   // We're messing with the window, so do this to ensure no flashes.
1006   if (!use_core_animation_)
1007     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
1009   [cocoa_view_ setHidden:YES];
1011   WasHidden();
1014 bool RenderWidgetHostViewMac::IsShowing() {
1015   return ![cocoa_view_ isHidden];
1018 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
1019   NSRect bounds = [cocoa_view_ bounds];
1020   // TODO(shess): In case of !window, the view has been removed from
1021   // the view hierarchy because the tab isn't main.  Could retrieve
1022   // the information from the main tab for our window.
1023   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1024   if (!enclosing_window)
1025     return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
1027   bounds = [cocoa_view_ convertRect:bounds toView:nil];
1028   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
1029   return FlipNSRectToRectScreen(bounds);
1032 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
1033   WebCursor web_cursor = cursor;
1034   [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
1037 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
1038   is_loading_ = is_loading;
1039   // If we ever decide to show the waiting cursor while the page is loading
1040   // like Chrome does on Windows, call |UpdateCursor()| here.
1043 void RenderWidgetHostViewMac::TextInputTypeChanged(
1044     ui::TextInputType type,
1045     ui::TextInputMode input_mode,
1046     bool can_compose_inline) {
1047   if (text_input_type_ != type
1048       || can_compose_inline_ != can_compose_inline) {
1049     text_input_type_ = type;
1050     can_compose_inline_ = can_compose_inline;
1051     if (HasFocus()) {
1052       SetTextInputActive(true);
1054       // Let AppKit cache the new input context to make IMEs happy.
1055       // See http://crbug.com/73039.
1056       [NSApp updateWindows];
1058 #ifndef __LP64__
1059       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
1060 #endif
1061     }
1062   }
1065 void RenderWidgetHostViewMac::ImeCancelComposition() {
1066   [cocoa_view_ cancelComposition];
1069 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
1070     const gfx::Range& range,
1071     const std::vector<gfx::Rect>& character_bounds) {
1072   // The RangeChanged message is only sent with valid values. The current
1073   // caret position (start == end) will be sent if there is no IME range.
1074   [cocoa_view_ setMarkedRange:range.ToNSRange()];
1075   composition_range_ = range;
1076   composition_bounds_ = character_bounds;
1079 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
1080                                                 int error_code) {
1081   Destroy();
1084 void RenderWidgetHostViewMac::Destroy() {
1085   [[NSNotificationCenter defaultCenter]
1086       removeObserver:cocoa_view_
1087                 name:NSWindowWillCloseNotification
1088               object:popup_window_];
1090   // We've been told to destroy.
1091   [cocoa_view_ retain];
1092   [cocoa_view_ removeFromSuperview];
1093   [cocoa_view_ autorelease];
1095   [popup_window_ close];
1096   popup_window_.autorelease();
1098   [fullscreen_window_manager_ exitFullscreenMode];
1099   fullscreen_window_manager_.reset();
1100   [pepper_fullscreen_window_ close];
1102   // This can be called as part of processing the window's responder
1103   // chain, for instance |-performKeyEquivalent:|.  In that case the
1104   // object needs to survive until the stack unwinds.
1105   pepper_fullscreen_window_.autorelease();
1107   // We get this call just before |render_widget_host_| deletes
1108   // itself.  But we are owned by |cocoa_view_|, which may be retained
1109   // by some other code.  Examples are WebContentsViewMac's
1110   // |latent_focus_view_| and TabWindowController's
1111   // |cachedContentView_|.
1112   render_widget_host_ = NULL;
1115 // Called from the renderer to tell us what the tooltip text should be. It
1116 // calls us frequently so we need to cache the value to prevent doing a lot
1117 // of repeat work.
1118 void RenderWidgetHostViewMac::SetTooltipText(
1119     const base::string16& tooltip_text) {
1120   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1121     tooltip_text_ = tooltip_text;
1123     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1124     // Windows; we're just trying to be polite. Don't persist the trimmed
1125     // string, as then the comparison above will always fail and we'll try to
1126     // set it again every single time the mouse moves.
1127     base::string16 display_text = tooltip_text_;
1128     if (tooltip_text_.length() > kMaxTooltipLength)
1129       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1131     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1132     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1133   }
1136 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1137   return [NSApp respondsToSelector:@selector(speakString:)] &&
1138          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1141 void RenderWidgetHostViewMac::SpeakSelection() {
1142   if ([NSApp respondsToSelector:@selector(speakString:)])
1143     [NSApp speakString:base::SysUTF8ToNSString(selected_text_)];
1146 bool RenderWidgetHostViewMac::IsSpeaking() const {
1147   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1148          [NSApp isSpeaking];
1151 void RenderWidgetHostViewMac::StopSpeaking() {
1152   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1153     [NSApp stopSpeaking:cocoa_view_];
1157 // RenderWidgetHostViewCocoa uses the stored selection text,
1158 // which implements NSServicesRequests protocol.
1160 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1161                                                size_t offset,
1162                                                const gfx::Range& range) {
1163   if (range.is_empty() || text.empty()) {
1164     selected_text_.clear();
1165   } else {
1166     size_t pos = range.GetMin() - offset;
1167     size_t n = range.length();
1169     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1170     if (pos >= text.length()) {
1171       DCHECK(false) << "The text can not cover range.";
1172       return;
1173     }
1174     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1175   }
1177   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1178   // Updates markedRange when there is no marked text so that retrieving
1179   // markedRange immediately after calling setMarkdText: returns the current
1180   // caret position.
1181   if (![cocoa_view_ hasMarkedText]) {
1182     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1183   }
1185   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1188 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1189     const ViewHostMsg_SelectionBounds_Params& params) {
1190   if (params.anchor_rect == params.focus_rect)
1191     caret_rect_ = params.anchor_rect;
1194 void RenderWidgetHostViewMac::ScrollOffsetChanged() {
1197 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1198   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1200   // Create a fake mouse event to inform the render widget that the mouse
1201   // left or entered.
1202   NSWindow* window = [cocoa_view_ window];
1203   // TODO(asvitkine): If the location outside of the event stream doesn't
1204   // correspond to the current event (due to delayed event processing), then
1205   // this may result in a cursor flicker if there are later mouse move events
1206   // in the pipeline. Find a way to use the mouse location from the event that
1207   // dismissed the context menu.
1208   NSPoint location = [window mouseLocationOutsideOfEventStream];
1209   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1210                                       location:location
1211                                  modifierFlags:0
1212                                      timestamp:0
1213                                   windowNumber:window_number()
1214                                        context:nil
1215                                    eventNumber:0
1216                                     clickCount:0
1217                                       pressure:0];
1218   WebMouseEvent web_event =
1219       WebInputEventFactory::mouseEvent(event, cocoa_view_);
1220   if (showing)
1221     web_event.type = WebInputEvent::MouseLeave;
1222   ForwardMouseEvent(web_event);
1225 bool RenderWidgetHostViewMac::IsPopup() const {
1226   return popup_type_ != blink::WebPopupTypeNone;
1229 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1230     const gfx::Rect& src_subrect,
1231     const gfx::Size& dst_size,
1232     const base::Callback<void(bool, const SkBitmap&)>& callback,
1233     const SkBitmap::Config config) {
1234   if (delegated_frame_host_) {
1235     delegated_frame_host_->CopyFromCompositingSurface(
1236         src_subrect, dst_size, callback, config);
1237     return;
1238   }
1240   if (config != SkBitmap::kARGB_8888_Config) {
1241     NOTIMPLEMENTED();
1242     callback.Run(false, SkBitmap());
1243   }
1244   base::ScopedClosureRunner scoped_callback_runner(
1245       base::Bind(callback, false, SkBitmap()));
1246   float scale = ui::GetScaleFactorForNativeView(cocoa_view_);
1247   gfx::Size dst_pixel_size = gfx::ToFlooredSize(
1248       gfx::ScaleSize(dst_size, scale));
1249   if (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) {
1250     ignore_result(scoped_callback_runner.Release());
1251     compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect),
1252                                    dst_pixel_size,
1253                                    callback);
1254   } else if (software_frame_manager_->HasCurrentFrame()) {
1255     gfx::Rect src_pixel_rect = gfx::ToEnclosingRect(gfx::ScaleRect(
1256         src_subrect,
1257         software_frame_manager_->GetCurrentFrameDeviceScaleFactor()));
1258     SkBitmap source_bitmap;
1259     source_bitmap.setConfig(
1260         SkBitmap::kARGB_8888_Config,
1261         software_frame_manager_->GetCurrentFrameSizeInPixels().width(),
1262         software_frame_manager_->GetCurrentFrameSizeInPixels().height(),
1263         0,
1264         kOpaque_SkAlphaType);
1265     source_bitmap.setPixels(software_frame_manager_->GetCurrentFramePixels());
1267     SkBitmap target_bitmap;
1268     target_bitmap.setConfig(
1269         SkBitmap::kARGB_8888_Config,
1270         dst_pixel_size.width(),
1271         dst_pixel_size.height(),
1272         0,
1273         kOpaque_SkAlphaType);
1274     if (!target_bitmap.allocPixels())
1275       return;
1277     SkCanvas target_canvas(target_bitmap);
1278     SkRect src_pixel_skrect = SkRect::MakeXYWH(
1279         src_pixel_rect.x(), src_pixel_rect.y(),
1280         src_pixel_rect.width(), src_pixel_rect.height());
1281     target_canvas.drawBitmapRectToRect(
1282         source_bitmap,
1283         &src_pixel_skrect,
1284         SkRect::MakeXYWH(0, 0, dst_pixel_size.width(), dst_pixel_size.height()),
1285         NULL,
1286         SkCanvas::kNone_DrawBitmapRectFlag);
1288     ignore_result(scoped_callback_runner.Release());
1289     callback.Run(true, target_bitmap);
1290   } else {
1291     callback.Run(false, SkBitmap());
1292   }
1295 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1296       const gfx::Rect& src_subrect,
1297       const scoped_refptr<media::VideoFrame>& target,
1298       const base::Callback<void(bool)>& callback) {
1299   if (delegated_frame_host_) {
1300     delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1301         src_subrect, target, callback);
1302     return;
1303   }
1305   base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
1306   if (!compositing_iosurface_ || !compositing_iosurface_->HasIOSurface())
1307     return;
1309   if (!target.get()) {
1310     NOTREACHED();
1311     return;
1312   }
1314   if (target->format() != media::VideoFrame::YV12 &&
1315       target->format() != media::VideoFrame::I420) {
1316     NOTREACHED();
1317     return;
1318   }
1320   if (src_subrect.IsEmpty())
1321     return;
1323   ignore_result(scoped_callback_runner.Release());
1324   compositing_iosurface_->CopyToVideoFrame(
1325       GetScaledOpenGLPixelRect(src_subrect),
1326       target,
1327       callback);
1330 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1331   if (delegated_frame_host_)
1332     return delegated_frame_host_->CanCopyToVideoFrame();
1334   return (!software_frame_manager_->HasCurrentFrame() &&
1335           compositing_iosurface_ &&
1336           compositing_iosurface_->HasIOSurface());
1339 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1340   if (delegated_frame_host_)
1341     return delegated_frame_host_->CanSubscribeFrame();
1343   return !software_frame_manager_->HasCurrentFrame();
1346 void RenderWidgetHostViewMac::BeginFrameSubscription(
1347     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1348   if (delegated_frame_host_) {
1349     delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1350     return;
1351   }
1352   frame_subscriber_ = subscriber.Pass();
1355 void RenderWidgetHostViewMac::EndFrameSubscription() {
1356   if (delegated_frame_host_) {
1357     delegated_frame_host_->EndFrameSubscription();
1358     return;
1359   }
1361   frame_subscriber_.reset();
1364 // Sets whether or not to accept first responder status.
1365 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
1366   [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
1369 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1370   if (render_widget_host_)
1371     render_widget_host_->ForwardMouseEvent(event);
1373   if (event.type == WebInputEvent::MouseLeave) {
1374     [cocoa_view_ setToolTipAtMousePoint:nil];
1375     tooltip_text_.clear();
1376   }
1379 void RenderWidgetHostViewMac::KillSelf() {
1380   if (!weak_factory_.HasWeakPtrs()) {
1381     [cocoa_view_ setHidden:YES];
1382     base::MessageLoop::current()->PostTask(FROM_HERE,
1383         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1384                    weak_factory_.GetWeakPtr()));
1385   }
1388 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1389     const NativeWebKeyboardEvent& event) {
1390   // Check WebInputEvent type since multiple types of events can be sent into
1391   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1392   // necessary to avoid double processing.
1393   // Also check the native type, since NSFlagsChanged is considered a key event
1394   // for WebKit purposes, but isn't considered a key event by the OS.
1395   if (event.type == WebInputEvent::RawKeyDown &&
1396       [event.os_event type] == NSKeyDown)
1397     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1398   return false;
1401 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1402     const base::string16& text, int plugin_id) {
1403   if (render_widget_host_) {
1404     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1405         render_widget_host_->GetRoutingID(), text, plugin_id));
1406   }
1409 void RenderWidgetHostViewMac::CompositorSwapBuffers(
1410     uint64 surface_handle,
1411     const gfx::Size& size,
1412     float surface_scale_factor,
1413     const std::vector<ui::LatencyInfo>& latency_info) {
1414   // Ensure that the frame be acked unless it is explicitly passed to a
1415   // display function.
1416   base::ScopedClosureRunner scoped_ack(
1417       base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
1418                  weak_factory_.GetWeakPtr()));
1420   if (render_widget_host_->is_hidden())
1421     return;
1423   // Ensure that if this function exits before the frame is set up (but not
1424   // necessarily drawn) then it is treated as an error.
1425   base::ScopedClosureRunner scoped_error(
1426       base::Bind(&RenderWidgetHostViewMac::GotAcceleratedCompositingError,
1427                  weak_factory_.GetWeakPtr()));
1429   AddPendingLatencyInfo(latency_info);
1431   // If compositing_iosurface_ exists and has been poisoned, destroy it
1432   // and allow EnsureCompositedIOSurface to recreate it below. Keep a
1433   // reference to the destroyed layer around until after the below call
1434   // to LayoutLayers, to avoid flickers.
1435   base::ScopedClosureRunner scoped_layer_remover;
1436   if (compositing_iosurface_context_ &&
1437       compositing_iosurface_context_->HasBeenPoisoned()) {
1438     scoped_layer_remover.Reset(
1439         base::Bind(RemoveLayerFromSuperlayer, compositing_iosurface_layer_));
1440     DestroyCompositedIOSurfaceLayer(kLeaveLayerInHierarchy);
1441     DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1442   }
1444   // Ensure compositing_iosurface_ and compositing_iosurface_context_ be
1445   // allocated.
1446   if (!EnsureCompositedIOSurface()) {
1447     LOG(ERROR) << "Failed EnsureCompositingIOSurface";
1448     return;
1449   }
1451   // Make the context current and update the IOSurface with the handle
1452   // passed in by the swap command.
1453   {
1454     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
1455         compositing_iosurface_context_->cgl_context());
1456     if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent(
1457             compositing_iosurface_context_, surface_handle, size,
1458             surface_scale_factor)) {
1459       LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
1460       return;
1461     }
1462   }
1464   // Grab video frames now that the IOSurface has been set up. Note that this
1465   // will be done in an offscreen context, so it is necessary to re-set the
1466   // current context afterward.
1467   bool frame_was_captured = false;
1468   if (frame_subscriber_) {
1469     const base::TimeTicks present_time = base::TimeTicks::Now();
1470     scoped_refptr<media::VideoFrame> frame;
1471     RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
1472     if (frame_subscriber_->ShouldCaptureFrame(present_time,
1473                                               &frame, &callback)) {
1474       // Flush the context that updated the IOSurface, to ensure that the
1475       // context that does the copy picks up the correct version.
1476       {
1477         gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
1478             compositing_iosurface_context_->cgl_context());
1479         glFlush();
1480       }
1481       compositing_iosurface_->CopyToVideoFrame(
1482           gfx::Rect(size), frame,
1483           base::Bind(callback, present_time));
1484       frame_was_captured = true;
1485     }
1486   }
1488   // At this point the surface, its context, and its layer have been set up, so
1489   // don't generate an error (one may be generated when drawing).
1490   ignore_result(scoped_error.Release());
1492   GotAcceleratedFrame();
1494   gfx::Size window_size(NSSizeToCGSize([cocoa_view_ frame].size));
1495   if (window_size.IsEmpty()) {
1496     // setNeedsDisplay will never display and we'll never ack if the window is
1497     // empty, so ack now and don't bother calling setNeedsDisplay below.
1498     return;
1499   }
1500   if (window_number() <= 0) {
1501     // It's normal for a backgrounded tab that is being captured to have no
1502     // window but not be hidden. Immediately ack the frame, and don't try to
1503     // draw it.
1504     if (frame_was_captured)
1505       return;
1507     // If this frame was not captured, there is likely some sort of bug. Ack
1508     // the frame and hope for the best. Because the IOSurface and layer are
1509     // populated, it will likely be displayed when the view is added to a
1510     // window's hierarchy.
1512     // TODO(shess) If the view does not have a window, or the window
1513     // does not have backing, the IOSurface will log "invalid drawable"
1514     // in -setView:.  It is not clear how this code is reached with such
1515     // a case, so record some info into breakpad (some subset of
1516     // browsers are likely to crash later for unrelated reasons).
1517     // http://crbug.com/148882
1518     const char* const kCrashKey = "rwhvm_window";
1519     NSWindow* window = [cocoa_view_ window];
1520     if (!window) {
1521       base::debug::SetCrashKeyValue(kCrashKey, "Missing window");
1522     } else {
1523       std::string value =
1524           base::StringPrintf("window %s delegate %s controller %s",
1525               object_getClassName(window),
1526               object_getClassName([window delegate]),
1527               object_getClassName([window windowController]));
1528       base::debug::SetCrashKeyValue(kCrashKey, value);
1529     }
1530     return;
1531   }
1533   // If the window is occluded, then this frame's display call may be severely
1534   // throttled. This is a good thing, unless tab capture may be active,
1535   // because the broadcast will be inappropriately throttled.
1536   // http://crbug.com/350410
1537   NSWindow* window = [cocoa_view_ window];
1538   if (window && [window respondsToSelector:@selector(occlusionState)]) {
1539     bool window_is_occluded =
1540         !([window occlusionState] & NSWindowOcclusionStateVisible);
1541     // Note that we aggressively ack even if this particular frame is not being
1542     // captured.
1543     if (window_is_occluded && frame_subscriber_)
1544       scoped_ack.Reset();
1545   }
1547   // If we reach here, then the frame will be displayed by a future draw
1548   // call, so don't make the callback.
1549   ignore_result(scoped_ack.Release());
1550   if (use_core_animation_) {
1551     DCHECK(compositing_iosurface_layer_);
1552     [compositing_iosurface_layer_ gotNewFrame];
1553   } else {
1554     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
1555         compositing_iosurface_context_->cgl_context());
1556     DrawIOSurfaceWithoutCoreAnimation();
1557   }
1559   // Try to finish previous copy requests after draw to get better pipelining.
1560   if (compositing_iosurface_)
1561     compositing_iosurface_->CheckIfAllCopiesAreFinished(false);
1563   // The IOSurface's size may have changed, so re-layout the layers to take
1564   // this into account. This may force an immediate draw.
1565   LayoutLayers();
1568 void RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
1569   CHECK(!use_core_animation_);
1570   CHECK(compositing_iosurface_);
1572   // If there is a pending frame, it should be acked by the end of this
1573   // function. Note that the ack should happen only after all drawing is
1574   // complete, so that the ack happens after any blocking due to vsync.
1575   base::ScopedClosureRunner scoped_ack(
1576       base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
1577                  weak_factory_.GetWeakPtr()));
1579   GLint old_gl_surface_order = 0;
1580   GLint new_gl_surface_order = allow_overlapping_views_ ? -1 : 1;
1581   [compositing_iosurface_context_->nsgl_context()
1582       getValues:&old_gl_surface_order
1583       forParameter:NSOpenGLCPSurfaceOrder];
1584   if (old_gl_surface_order != new_gl_surface_order) {
1585     [compositing_iosurface_context_->nsgl_context()
1586         setValues:&new_gl_surface_order
1587         forParameter:NSOpenGLCPSurfaceOrder];
1588   }
1590   // Instead of drawing, request that underlay view redraws.
1591   if (underlay_view_ &&
1592       underlay_view_->compositing_iosurface_ &&
1593       underlay_view_has_drawn_) {
1594     [underlay_view_->cocoa_view() setNeedsDisplayInRect:NSMakeRect(0, 0, 1, 1)];
1595     return;
1596   }
1598   bool has_overlay = overlay_view_ && overlay_view_->compositing_iosurface_;
1599   if (has_overlay) {
1600     // Un-bind the overlay view's OpenGL context, since its content will be
1601     // drawn by this context. Not doing this can result in corruption.
1602     // http://crbug.com/330701
1603     overlay_view_->ClearBoundContextDrawable();
1604   }
1605   [compositing_iosurface_context_->nsgl_context() setView:cocoa_view_];
1607   gfx::Rect view_rect(NSRectToCGRect([cocoa_view_ frame]));
1608   if (!compositing_iosurface_->DrawIOSurface(
1609           compositing_iosurface_context_, view_rect,
1610           ViewScaleFactor(), !has_overlay)) {
1611     GotAcceleratedCompositingError();
1612     return;
1613   }
1615   if (has_overlay) {
1616     overlay_view_->underlay_view_has_drawn_ = true;
1617     gfx::Rect overlay_view_rect(
1618         NSRectToCGRect([overlay_view_->cocoa_view() frame]));
1619     overlay_view_rect.set_x(overlay_view_offset_.x());
1620     overlay_view_rect.set_y(view_rect.height() -
1621                             overlay_view_rect.height() -
1622                             overlay_view_offset_.y());
1623     if (!overlay_view_->compositing_iosurface_->DrawIOSurface(
1624             compositing_iosurface_context_, overlay_view_rect,
1625             overlay_view_->ViewScaleFactor(), true)) {
1626       GotAcceleratedCompositingError();
1627       return;
1628     }
1629   }
1631   SendPendingLatencyInfoToHost();
1634 void RenderWidgetHostViewMac::GotAcceleratedCompositingError() {
1635   LOG(ERROR) << "Encountered accelerated compositing error";
1636   base::MessageLoop::current()->PostTask(
1637       FROM_HERE,
1638       base::Bind(&RenderWidgetHostViewMac::DestroyCompositingStateOnError,
1639                  weak_factory_.GetWeakPtr()));
1642 void RenderWidgetHostViewMac::DestroyCompositingStateOnError() {
1643   // This should be called with a clean stack. Make sure that no context is
1644   // current.
1645   DCHECK(!CGLGetCurrentContext());
1647   // The existing GL contexts may be in a bad state, so don't re-use any of the
1648   // existing ones anymore, rather, allocate new ones.
1649   if (compositing_iosurface_context_)
1650     compositing_iosurface_context_->PoisonContextAndSharegroup();
1652   DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1654   // Request that a new frame be generated and dirty the view.
1655   if (render_widget_host_)
1656     render_widget_host_->ScheduleComposite();
1657   [cocoa_view_ setNeedsDisplay:YES];
1659   // Mark the last frame as not accelerated (so that the window is prepared for
1660   // an underlay next time an accelerated frame comes in).
1661   last_frame_was_accelerated_ = false;
1663   // TODO(ccameron): It may be a good idea to request that the renderer recreate
1664   // its GL context as well, and fall back to software if this happens
1665   // repeatedly.
1668 void RenderWidgetHostViewMac::SetOverlayView(
1669     RenderWidgetHostViewMac* overlay, const gfx::Point& offset) {
1670   if (overlay_view_)
1671     overlay_view_->underlay_view_.reset();
1673   overlay_view_ = overlay->overlay_view_weak_factory_.GetWeakPtr();
1674   overlay_view_->underlay_view_ = overlay_view_weak_factory_.GetWeakPtr();
1675   if (use_core_animation_)
1676     return;
1678   overlay_view_offset_ = offset;
1679   overlay_view_->underlay_view_has_drawn_ = false;
1681   [cocoa_view_ setNeedsDisplay:YES];
1682   [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
1685 void RenderWidgetHostViewMac::RemoveOverlayView() {
1686   if (overlay_view_) {
1687     overlay_view_->underlay_view_.reset();
1688     overlay_view_.reset();
1689   }
1690   if (use_core_animation_)
1691     return;
1693   [cocoa_view_ setNeedsDisplay:YES];
1694   [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
1697 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1698     const std::vector<gfx::Rect>& bounds,
1699     const gfx::Range& range,
1700     size_t* line_break_point) {
1701   DCHECK(line_break_point);
1702   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1703     return false;
1705   // We can't check line breaking completely from only rectangle array. Thus we
1706   // assume the line breaking as the next character's y offset is larger than
1707   // a threshold. Currently the threshold is determined as minimum y offset plus
1708   // 75% of maximum height.
1709   // TODO(nona): Check the threshold is reliable or not.
1710   // TODO(nona): Bidi support.
1711   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1712   int max_height = 0;
1713   int min_y_offset = kint32max;
1714   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1715     max_height = std::max(max_height, bounds[idx].height());
1716     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1717   }
1718   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1719   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1720     if (bounds[idx].y() > line_break_threshold) {
1721       *line_break_point = idx;
1722       return true;
1723     }
1724   }
1725   return false;
1728 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1729     const gfx::Range& range,
1730     gfx::Range* actual_range) {
1731   DCHECK(actual_range);
1732   DCHECK(!composition_bounds_.empty());
1733   DCHECK(range.start() <= composition_bounds_.size());
1734   DCHECK(range.end() <= composition_bounds_.size());
1736   if (range.is_empty()) {
1737     *actual_range = range;
1738     if (range.start() == composition_bounds_.size()) {
1739       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1740                        composition_bounds_[range.start() - 1].y(),
1741                        0,
1742                        composition_bounds_[range.start() - 1].height());
1743     } else {
1744       return gfx::Rect(composition_bounds_[range.start()].x(),
1745                        composition_bounds_[range.start()].y(),
1746                        0,
1747                        composition_bounds_[range.start()].height());
1748     }
1749   }
1751   size_t end_idx;
1752   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1753     end_idx = range.end();
1754   }
1755   *actual_range = gfx::Range(range.start(), end_idx);
1756   gfx::Rect rect = composition_bounds_[range.start()];
1757   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1758     rect.Union(composition_bounds_[i]);
1759   }
1760   return rect;
1763 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1764     const gfx::Range& request_range) {
1765   if (composition_range_.is_empty())
1766     return gfx::Range::InvalidRange();
1768   if (request_range.is_reversed())
1769     return gfx::Range::InvalidRange();
1771   if (request_range.start() < composition_range_.start() ||
1772       request_range.start() > composition_range_.end() ||
1773       request_range.end() > composition_range_.end()) {
1774     return gfx::Range::InvalidRange();
1775   }
1777   return gfx::Range(
1778       request_range.start() - composition_range_.start(),
1779       request_range.end() - composition_range_.start());
1782 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1783   if (!render_widget_host_->IsRenderView())
1784     return NULL;
1786   return WebContents::FromRenderViewHost(
1787       RenderViewHost::From(render_widget_host_));
1790 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1791     NSRange range,
1792     NSRect* rect,
1793     NSRange* actual_range) {
1794   DCHECK(rect);
1795   // This exists to make IMEs more responsive, see http://crbug.com/115920
1796   TRACE_EVENT0("browser",
1797                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1799   // If requested range is same as caret location, we can just return it.
1800   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1801     if (actual_range)
1802       *actual_range = range;
1803     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1804     return true;
1805   }
1807   const gfx::Range request_range_in_composition =
1808       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1809   if (request_range_in_composition == gfx::Range::InvalidRange())
1810     return false;
1812   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1813   // ImeCompositionRangeChanged will be sent with empty vector.
1814   if (composition_bounds_.empty())
1815     return false;
1816   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1818   gfx::Range ui_actual_range;
1819   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1820                                request_range_in_composition,
1821                                &ui_actual_range).ToCGRect());
1822   if (actual_range) {
1823     *actual_range = gfx::Range(
1824         composition_range_.start() + ui_actual_range.start(),
1825         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1826   }
1827   return true;
1830 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
1831     const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1832     int gpu_host_id) {
1833   TRACE_EVENT0("browser",
1834       "RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped");
1835   DCHECK_CURRENTLY_ON(BrowserThread::UI);
1837   AddPendingSwapAck(params.route_id,
1838                     gpu_host_id,
1839                     compositing_iosurface_ ?
1840                         compositing_iosurface_->GetRendererID() : 0);
1841   CompositorSwapBuffers(params.surface_handle,
1842                         params.size,
1843                         params.scale_factor,
1844                         params.latency_info);
1847 void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
1848     const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1849     int gpu_host_id) {
1850   TRACE_EVENT0("browser",
1851       "RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer");
1852   DCHECK_CURRENTLY_ON(BrowserThread::UI);
1854   AddPendingSwapAck(params.route_id,
1855                     gpu_host_id,
1856                     compositing_iosurface_ ?
1857                         compositing_iosurface_->GetRendererID() : 0);
1858   CompositorSwapBuffers(params.surface_handle,
1859                         params.surface_size,
1860                         params.surface_scale_factor,
1861                         params.latency_info);
1864 void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
1865   if (compositing_iosurface_)
1866     compositing_iosurface_->UnrefIOSurface();
1869 void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
1870   DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1873 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1874       const gfx::Size& desired_size) {
1875   if (last_frame_was_accelerated_) {
1876     return compositing_iosurface_ &&
1877            compositing_iosurface_->HasIOSurface() &&
1878            (desired_size.IsEmpty() ||
1879                compositing_iosurface_->dip_io_surface_size() == desired_size);
1880   } else {
1881     return (software_frame_manager_->HasCurrentFrame() &&
1882            (desired_size.IsEmpty() ||
1883                software_frame_manager_->GetCurrentFrameSizeInDIP() ==
1884                    desired_size));
1885   }
1886   return false;
1889 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1890     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1891   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1893   if (frame->delegated_frame_data) {
1894     if (!browser_compositor_view_) {
1895       browser_compositor_view_.reset(
1896           [[BrowserCompositorViewMac alloc] initWithSuperview:cocoa_view_]);
1897       root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
1898       delegated_frame_host_.reset(new DelegatedFrameHost(this));
1899     }
1901     // TODO(ccameron): Having the root layer set while swapping the frame will
1902     // result in frames not appearing. Fix this.
1903     [browser_compositor_view_ compositor]->SetRootLayer(NULL);
1904     delegated_frame_host_->SwapDelegatedFrame(
1905         output_surface_id,
1906         frame->delegated_frame_data.Pass(),
1907         frame->metadata.device_scale_factor,
1908         frame->metadata.latency_info);
1909     [browser_compositor_view_ compositor]->SetRootLayer(root_layer_.get());
1911     // Update the compositor and root layer size and scale factor to match
1912     // the frame just received.
1913     float scale_factor = frame->metadata.device_scale_factor;
1914     gfx::Size dip_size = ToCeiledSize(frame->metadata.viewport_size);
1915     gfx::Size pixel_size = ConvertSizeToPixel(
1916         scale_factor, dip_size);
1917     [browser_compositor_view_ compositor]->SetScaleAndSize(
1918         scale_factor, pixel_size);
1919     root_layer_->SetBounds(gfx::Rect(dip_size));
1920   } else if (frame->software_frame_data) {
1921     if (!software_frame_manager_->SwapToNewFrame(
1922             output_surface_id,
1923             frame->software_frame_data.get(),
1924             frame->metadata.device_scale_factor,
1925             render_widget_host_->GetProcess()->GetHandle())) {
1926       render_widget_host_->GetProcess()->ReceivedBadMessage();
1927       return;
1928     }
1930     // Add latency info to report when the frame finishes drawing.
1931     AddPendingLatencyInfo(frame->metadata.latency_info);
1933     if (use_core_animation_) {
1934       const void* pixels = software_frame_manager_->GetCurrentFramePixels();
1935       gfx::Size size_in_pixels =
1936           software_frame_manager_->GetCurrentFrameSizeInPixels();
1938       EnsureSoftwareLayer();
1939       [software_layer_ setContentsToData:pixels
1940                             withRowBytes:4 * size_in_pixels.width()
1941                            withPixelSize:size_in_pixels
1942                          withScaleFactor:frame->metadata.device_scale_factor];
1944       // Send latency information to the host immediately, as there will be no
1945       // subsequent draw call in which to do so.
1946       SendPendingLatencyInfoToHost();
1947     }
1949     GotSoftwareFrame();
1951     cc::CompositorFrameAck ack;
1952     RenderWidgetHostImpl::SendSwapCompositorFrameAck(
1953         render_widget_host_->GetRoutingID(),
1954         software_frame_manager_->GetCurrentFrameOutputSurfaceId(),
1955         render_widget_host_->GetProcess()->GetID(),
1956         ack);
1957     software_frame_manager_->SwapToNewFrameComplete(
1958         !render_widget_host_->is_hidden());
1960     // Notify observers, tab capture observers in particular, that a new
1961     // software frame has come in.
1962     NotificationService::current()->Notify(
1963         NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
1964         Source<RenderWidgetHost>(render_widget_host_),
1965         NotificationService::NoDetails());
1966   } else {
1967     DLOG(ERROR) << "Received unexpected frame type.";
1968     RecordAction(
1969         base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1970     render_widget_host_->GetProcess()->ReceivedBadMessage();
1971   }
1974 void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
1975                                                             int route_id) {
1978 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1979   *results = GetWebScreenInfo(GetNativeView());
1982 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1983   // TODO(shess): In case of !window, the view has been removed from
1984   // the view hierarchy because the tab isn't main.  Could retrieve
1985   // the information from the main tab for our window.
1986   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1987   if (!enclosing_window)
1988     return gfx::Rect();
1990   NSRect bounds = [enclosing_window frame];
1991   return FlipNSRectToRectScreen(bounds);
1994 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1995   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1996   // completely on Mac OS.
1997   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
2000 void RenderWidgetHostViewMac::SetScrollOffsetPinning(
2001     bool is_pinned_to_left, bool is_pinned_to_right) {
2002   [cocoa_view_ scrollOffsetPinnedToLeft:is_pinned_to_left
2003                                 toRight:is_pinned_to_right];
2006 bool RenderWidgetHostViewMac::LockMouse() {
2007   if (mouse_locked_)
2008     return true;
2010   mouse_locked_ = true;
2012   // Lock position of mouse cursor and hide it.
2013   CGAssociateMouseAndMouseCursorPosition(NO);
2014   [NSCursor hide];
2016   // Clear the tooltip window.
2017   SetTooltipText(base::string16());
2019   return true;
2022 void RenderWidgetHostViewMac::UnlockMouse() {
2023   if (!mouse_locked_)
2024     return;
2025   mouse_locked_ = false;
2027   // Unlock position of mouse cursor and unhide it.
2028   CGAssociateMouseAndMouseCursorPosition(YES);
2029   [NSCursor unhide];
2031   if (render_widget_host_)
2032     render_widget_host_->LostMouseLock();
2035 void RenderWidgetHostViewMac::UnhandledWheelEvent(
2036     const blink::WebMouseWheelEvent& event) {
2037   // Only record a wheel event as unhandled if JavaScript handlers got a chance
2038   // to see it (no-op wheel events are ignored by the event dispatcher)
2039   if (event.deltaX || event.deltaY)
2040     [cocoa_view_ gotUnhandledWheelEvent];
2043 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
2044   if (render_widget_host_)
2045     return render_widget_host_->Send(message);
2046   delete message;
2047   return false;
2050 void RenderWidgetHostViewMac::SoftwareFrameWasFreed(
2051     uint32 output_surface_id, unsigned frame_id) {
2052   if (!render_widget_host_)
2053     return;
2054   cc::CompositorFrameAck ack;
2055   ack.last_software_frame_id = frame_id;
2056   RenderWidgetHostImpl::SendReclaimCompositorResources(
2057       render_widget_host_->GetRoutingID(),
2058       output_surface_id,
2059       render_widget_host_->GetProcess()->GetID(),
2060       ack);
2063 void RenderWidgetHostViewMac::ReleaseReferencesToSoftwareFrame() {
2064   DestroySoftwareLayer();
2067 void RenderWidgetHostViewMac::ShutdownHost() {
2068   weak_factory_.InvalidateWeakPtrs();
2069   render_widget_host_->Shutdown();
2070   // Do not touch any members at this point, |this| has been deleted.
2073 void RenderWidgetHostViewMac::GotAcceleratedFrame() {
2074   EnsureCompositedIOSurfaceLayer();
2075   SendVSyncParametersToRenderer();
2076   if (!last_frame_was_accelerated_) {
2077     last_frame_was_accelerated_ = true;
2079     if (!use_core_animation_) {
2080       // Need to wipe the software view with transparency to expose the GL
2081       // underlay. Invalidate the whole window to do that.
2082       [cocoa_view_ setNeedsDisplay:YES];
2083     }
2085     // Delete software backingstore and layer.
2086     software_frame_manager_->DiscardCurrentFrame();
2087     DestroySoftwareLayer();
2088   }
2091 void RenderWidgetHostViewMac::GotSoftwareFrame() {
2092   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::GotSoftwareFrame");
2094   if (!render_widget_host_)
2095     return;
2097   EnsureSoftwareLayer();
2098   LayoutLayers();
2099   SendVSyncParametersToRenderer();
2101   // Draw the contents of the frame immediately. It is critical that this
2102   // happen before the frame be acked, otherwise the new frame will likely be
2103   // ready before the drawing is complete, thrashing the browser main thread.
2104   if (use_core_animation_) {
2105     [software_layer_ displayIfNeeded];
2106   } else {
2107     [cocoa_view_ setNeedsDisplay:YES];
2108     [cocoa_view_ displayIfNeeded];
2109   }
2111   if (last_frame_was_accelerated_) {
2112     last_frame_was_accelerated_ = false;
2114     // If overlapping views are allowed, then don't unbind the context
2115     // from the view (that is, don't call clearDrawble -- just delete the
2116     // texture and IOSurface). Rather, let it sit behind the software frame
2117     // that will be put up in front. This will prevent transparent
2118     // flashes.
2119     // http://crbug.com/154531
2120     // Also note that it is necessary that clearDrawable be called if
2121     // overlapping views are not allowed, e.g, for content shell.
2122     // http://crbug.com/178408
2123     // Disable screen updates so that the changes of flashes is minimized.
2124     // http://crbug.com/279472
2125     if (!use_core_animation_)
2126       [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
2127     if (allow_overlapping_views_)
2128       DestroyCompositedIOSurfaceAndLayer(kLeaveContextBoundToView);
2129     else
2130       DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
2131   }
2134 void RenderWidgetHostViewMac::SetActive(bool active) {
2135   if (render_widget_host_) {
2136     render_widget_host_->SetActive(active);
2137     if (active) {
2138       if (HasFocus())
2139         render_widget_host_->Focus();
2140     } else {
2141       render_widget_host_->Blur();
2142     }
2143   }
2144   if (HasFocus())
2145     SetTextInputActive(active);
2146   if (!active) {
2147     [cocoa_view_ setPluginImeActive:NO];
2148     UnlockMouse();
2149   }
2152 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
2153   if (render_widget_host_) {
2154     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
2155         render_widget_host_->GetRoutingID(), visible));
2156   }
2159 void RenderWidgetHostViewMac::WindowFrameChanged() {
2160   if (render_widget_host_) {
2161     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
2162         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
2163         GetViewBounds()));
2164   }
2166   if (compositing_iosurface_ && !use_core_animation_) {
2167     // This will migrate the context to the appropriate window.
2168     if (!EnsureCompositedIOSurface())
2169       GotAcceleratedCompositingError();
2170   }
2173 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
2174   RenderWidgetHostViewMacDictionaryHelper helper(this);
2175   helper.ShowDefinitionForSelection();
2178 void RenderWidgetHostViewMac::SetBackgroundOpaque(bool opaque) {
2179   RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
2180   if (render_widget_host_)
2181     render_widget_host_->SetBackgroundOpaque(opaque);
2184 void RenderWidgetHostViewMac::CreateBrowserAccessibilityManagerIfNeeded() {
2185   if (!GetBrowserAccessibilityManager()) {
2186     SetBrowserAccessibilityManager(
2187         new BrowserAccessibilityManagerMac(
2188             cocoa_view_,
2189             BrowserAccessibilityManagerMac::GetEmptyDocument(),
2190             render_widget_host_));
2191   }
2194 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
2195     const gfx::Rect& bounds) {
2196   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
2197   NSSize size = NSMakeSize(bounds.width(), bounds.height());
2198   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
2199   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
2200   NSPoint originInScreen =
2201       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
2202   originInScreen.y = originInScreen.y - size.height;
2203   return gfx::Point(originInScreen.x, originInScreen.y);
2206 void RenderWidgetHostViewMac::OnAccessibilitySetFocus(int accObjId) {
2207   // Immediately set the focused item even though we have not officially set
2208   // focus on it as VoiceOver expects to get the focused item after this
2209   // method returns.
2210   BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
2211   if (manager)
2212     manager->SetFocus(manager->GetFromID(accObjId), false);
2215 void RenderWidgetHostViewMac::AccessibilityShowMenu(int accObjId) {
2216   BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
2217   if (!manager)
2218     return;
2219   BrowserAccessibilityCocoa* obj =
2220       manager->GetFromID(accObjId)->ToBrowserAccessibilityCocoa();
2222   // Performs a right click copying WebKit's
2223   // accessibilityPerformShowMenuAction.
2224   NSPoint objOrigin = [obj origin];
2225   NSSize size = [[obj size] sizeValue];
2226   gfx::Point origin = AccessibilityOriginInScreen(
2227       gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
2228   NSPoint location = NSMakePoint(origin.x(), origin.y());
2229   location = [[cocoa_view_ window] convertScreenToBase:location];
2230   location.x += size.width/2;
2231   location.y += size.height/2;
2233   NSEvent* fakeRightClick = [NSEvent
2234                           mouseEventWithType:NSRightMouseDown
2235                                     location:location
2236                                modifierFlags:0
2237                                    timestamp:0
2238                                 windowNumber:[[cocoa_view_ window] windowNumber]
2239                                      context:[NSGraphicsContext currentContext]
2240                                  eventNumber:0
2241                                   clickCount:1
2242                                     pressure:0];
2244   [cocoa_view_ mouseEvent:fakeRightClick];
2249 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
2250   if (active) {
2251     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
2252       EnablePasswordInput();
2253     else
2254       DisablePasswordInput();
2255   } else {
2256     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
2257       DisablePasswordInput();
2258   }
2261 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
2262                                                    int plugin_id) {
2263   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
2266 void RenderWidgetHostViewMac::OnStartPluginIme() {
2267   [cocoa_view_ setPluginImeActive:YES];
2270 void RenderWidgetHostViewMac::OnDidChangeScrollbarsForMainFrame(
2271     bool has_horizontal_scrollbar, bool has_vertical_scrollbar) {
2272   [cocoa_view_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
2275 gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect(
2276     const gfx::Rect& rect) {
2277   gfx::Rect src_gl_subrect = rect;
2278   src_gl_subrect.set_y(GetViewBounds().height() - rect.bottom());
2280   return gfx::ToEnclosingRect(gfx::ScaleRect(src_gl_subrect,
2281                                              ViewScaleFactor()));
2284 void RenderWidgetHostViewMac::AddPendingLatencyInfo(
2285     const std::vector<ui::LatencyInfo>& latency_info) {
2286   // If a screenshot is being taken when using CoreAnimation, send a few extra
2287   // calls to setNeedsDisplay and wait for their resulting display calls,
2288   // before reporting that the frame has reached the screen.
2289   if (use_core_animation_) {
2290     bool should_defer = false;
2291     for (size_t i = 0; i < latency_info.size(); i++) {
2292       if (latency_info[i].FindLatency(
2293               ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
2294               render_widget_host_->GetLatencyComponentId(),
2295               NULL)) {
2296         should_defer = true;
2297       }
2298     }
2299     if (should_defer) {
2300       // Multiple pending screenshot requests will work, but if every frame
2301       // requests a screenshot, then the delay will never expire. Assert this
2302       // here to avoid this.
2303       CHECK_EQ(pending_latency_info_delay_, 0u);
2304       // Wait a fixed number of frames (calls to CALayer::display) before
2305       // claiming that the screenshot has reached the screen. This number
2306       // comes from taking the first number where tests didn't fail (six),
2307       // and doubling it.
2308       const uint32 kScreenshotLatencyDelayInFrames = 12;
2309       pending_latency_info_delay_ = kScreenshotLatencyDelayInFrames;
2310       TickPendingLatencyInfoDelay();
2311     }
2312   }
2314   for (size_t i = 0; i < latency_info.size(); i++) {
2315     pending_latency_info_.push_back(latency_info[i]);
2316   }
2319 void RenderWidgetHostViewMac::SendPendingLatencyInfoToHost() {
2320   if (pending_latency_info_delay_) {
2321     pending_latency_info_delay_ -= 1;
2322     return;
2323   }
2324   pending_latency_info_delay_weak_ptr_factory_.InvalidateWeakPtrs();
2326   for (size_t i = 0; i < pending_latency_info_.size(); i++) {
2327     pending_latency_info_[i].AddLatencyNumber(
2328         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
2329     render_widget_host_->FrameSwapped(pending_latency_info_[i]);
2330   }
2331   pending_latency_info_.clear();
2334 void RenderWidgetHostViewMac::TickPendingLatencyInfoDelay() {
2335   if (compositing_iosurface_layer_) {
2336     // Keep calling gotNewFrame in a loop until enough display calls come in.
2337     // Each call will be separated by about a vsync.
2338     base::MessageLoop::current()->PostTask(
2339         FROM_HERE,
2340         base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
2341                    pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()));
2342     [compositing_iosurface_layer_ gotNewFrame];
2343   }
2344   if (software_layer_) {
2345     // In software mode there is not an explicit setNeedsDisplay/display loop,
2346     // so just wait a pretend-vsync at 60 Hz.
2347     base::MessageLoop::current()->PostDelayedTask(
2348         FROM_HERE,
2349         base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
2350                    pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()),
2351         base::TimeDelta::FromMilliseconds(1000/60));
2352     SendPendingLatencyInfoToHost();
2353   }
2356 void RenderWidgetHostViewMac::AddPendingSwapAck(
2357     int32 route_id, int gpu_host_id, int32 renderer_id) {
2358   // Note that multiple un-acked swaps can come in the event of a GPU process
2359   // loss. Drop the old acks.
2360   pending_swap_ack_.reset(new PendingSwapAck(
2361       route_id, gpu_host_id, renderer_id));
2363   // A trace value of 2 indicates that there is a pending swap ack. See
2364   // CompositingIOSurfaceLayer's canDrawInCGLContext for other value meanings.
2365   TRACE_COUNTER_ID1("browser", "PendingSwapAck",
2366                     compositing_iosurface_layer_.get(), 2);
2369 void RenderWidgetHostViewMac::SendPendingSwapAck() {
2370   if (!pending_swap_ack_)
2371     return;
2373   AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
2374   ack_params.sync_point = 0;
2375   ack_params.renderer_id = pending_swap_ack_->renderer_id;
2376   RenderWidgetHostImpl::AcknowledgeBufferPresent(pending_swap_ack_->route_id,
2377                                                  pending_swap_ack_->gpu_host_id,
2378                                                  ack_params);
2379   pending_swap_ack_.reset();
2380   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0);
2383 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
2384   if (!render_widget_host_ || render_widget_host_->is_hidden())
2385     return;
2387   // Pausing for the overlay/underlay view prevents the other one from receiving
2388   // frames. This may lead to large delays, causing overlaps.
2389   // See crbug.com/352020.
2390   if (underlay_view_ || overlay_view_)
2391     return;
2393   // Ensure that all frames are acked before waiting for a frame to come in.
2394   // Note that we will draw a frame at the end of this function, so it is safe
2395   // to ack a never-drawn frame here.
2396   SendPendingSwapAck();
2398   // Wait for a frame of the right size to come in.
2399   render_widget_host_->PauseForPendingResizeOrRepaints();
2401   // Immediately draw any frames that haven't been drawn yet. This is necessary
2402   // to keep the window and the window's contents in sync.
2403   [cocoa_view_ displayIfNeeded];
2404   [software_layer_ displayIfNeeded];
2405   [compositing_iosurface_layer_ displayIfNeeded];
2408 void RenderWidgetHostViewMac::LayoutLayers() {
2409   if (!use_core_animation_)
2410     return;
2412   if (browser_compositor_view_) {
2413     [browser_compositor_view_ layoutLayers];
2414     return;
2415   }
2417   // Disable animation of the layer's resizing or change in contents scale.
2418   ScopedCAActionDisabler disabler;
2420   CGRect new_background_frame = NSRectToCGRect([cocoa_view() bounds]);
2422   // Dynamically calling setContentsScale on a CAOpenGLLayer for which
2423   // setAsynchronous is dynamically toggled can result in flashes of corrupt
2424   // content. Work around this by replacing the entire layer when the scale
2425   // factor changes.
2426   if (compositing_iosurface_ &&
2427       [compositing_iosurface_layer_
2428           respondsToSelector:(@selector(contentsScale))]) {
2429     if (compositing_iosurface_->scale_factor() !=
2430         [compositing_iosurface_layer_ contentsScale]) {
2431       DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
2432       EnsureCompositedIOSurfaceLayer();
2433     }
2434   }
2435   if (compositing_iosurface_ &&
2436       compositing_iosurface_->HasIOSurface() &&
2437       compositing_iosurface_layer_) {
2438     CGRect layer_bounds = CGRectMake(
2439       0,
2440       0,
2441       compositing_iosurface_->dip_io_surface_size().width(),
2442       compositing_iosurface_->dip_io_surface_size().height());
2443     CGPoint layer_position = CGPointMake(
2444       0,
2445       CGRectGetHeight(new_background_frame) - CGRectGetHeight(layer_bounds));
2446     bool bounds_changed = !CGRectEqualToRect(
2447         layer_bounds, [compositing_iosurface_layer_ bounds]);
2448     [compositing_iosurface_layer_ setPosition:layer_position];
2449     [compositing_iosurface_layer_ setBounds:layer_bounds];
2451     // If the bounds changed, then draw the frame immediately, to ensure that
2452     // content displayed is in sync with the window size.
2453     if (bounds_changed) {
2454       // Also, sometimes, especially when infobars are being removed, the
2455       // setNeedsDisplay calls are dropped on the floor, and stale content is
2456       // displayed. Calling displayIfNeeded will ensure that the right size
2457       // frame is drawn to the screen.
2458       // http://crbug.com/350817
2459       [compositing_iosurface_layer_ setNeedsDisplay];
2460       [compositing_iosurface_layer_ displayIfNeeded];
2461     }
2462   }
2464   // Changing the software layer's bounds and position doesn't always result
2465   // in the layer being anchored to the top-left. Set the layer's frame
2466   // explicitly, since this is more reliable in practice.
2467   if (software_layer_) {
2468     bool frame_changed = !CGRectEqualToRect(
2469         new_background_frame, [software_layer_ frame]);
2470     if (frame_changed) {
2471       [software_layer_ setFrame:new_background_frame];
2472     }
2473   }
2476 SkBitmap::Config RenderWidgetHostViewMac::PreferredReadbackFormat() {
2477   return SkBitmap::kARGB_8888_Config;
2480 ////////////////////////////////////////////////////////////////////////////////
2481 // CompositingIOSurfaceLayerClient, public:
2483 void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame(bool succeeded) {
2484   SendPendingLatencyInfoToHost();
2485   SendPendingSwapAck();
2486   if (!succeeded)
2487     GotAcceleratedCompositingError();
2490 bool RenderWidgetHostViewMac::AcceleratedLayerHasNotAckedPendingFrame() const {
2491   return pending_swap_ack_;
2494 }  // namespace content
2496 // RenderWidgetHostViewCocoa ---------------------------------------------------
2498 @implementation RenderWidgetHostViewCocoa
2499 @synthesize selectedRange = selectedRange_;
2500 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
2501 @synthesize markedRange = markedRange_;
2503 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
2504   self = [super initWithFrame:NSZeroRect];
2505   if (self) {
2506     self.acceptsTouchEvents = YES;
2507     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
2508     editCommand_helper_->AddEditingSelectorsToClass([self class]);
2510     renderWidgetHostView_.reset(r);
2511     canBeKeyView_ = YES;
2512     focusedPluginIdentifier_ = -1;
2513     renderWidgetHostView_->backing_store_scale_factor_ =
2514         ui::GetScaleFactorForNativeView(self);
2516     // OpenGL support:
2517     if ([self respondsToSelector:
2518         @selector(setWantsBestResolutionOpenGLSurface:)]) {
2519       [self setWantsBestResolutionOpenGLSurface:YES];
2520     }
2521     handlingGlobalFrameDidChange_ = NO;
2522     [[NSNotificationCenter defaultCenter]
2523         addObserver:self
2524            selector:@selector(globalFrameDidChange:)
2525                name:NSViewGlobalFrameDidChangeNotification
2526              object:self];
2527     [[NSNotificationCenter defaultCenter]
2528         addObserver:self
2529            selector:@selector(didChangeScreenParameters:)
2530                name:NSApplicationDidChangeScreenParametersNotification
2531              object:nil];
2532   }
2533   return self;
2536 - (void)dealloc {
2537   // Unbind the GL context from this view. If this is not done before super's
2538   // dealloc is called then the GL context will crash when it reaches into
2539   // the view in its destructor.
2540   // http://crbug.com/255608
2541   if (renderWidgetHostView_)
2542     renderWidgetHostView_->AcceleratedSurfaceRelease();
2544   if (responderDelegate_ &&
2545       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
2546     [responderDelegate_ viewGone:self];
2547   responderDelegate_.reset();
2549   [[NSNotificationCenter defaultCenter] removeObserver:self];
2551   [super dealloc];
2554 - (void)didChangeScreenParameters:(NSNotification*)notify {
2555   g_screen_info_up_to_date = false;
2558 - (void)setResponderDelegate:
2559             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
2560   DCHECK(!responderDelegate_);
2561   responderDelegate_.reset([delegate retain]);
2564 - (void)resetCursorRects {
2565   if (currentCursor_) {
2566     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
2567     [currentCursor_ setOnMouseEntered:YES];
2568   }
2571 - (void)gotUnhandledWheelEvent {
2572   if (responderDelegate_ &&
2573       [responderDelegate_
2574           respondsToSelector:@selector(gotUnhandledWheelEvent)]) {
2575     [responderDelegate_ gotUnhandledWheelEvent];
2576   }
2579 - (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right {
2580   if (responderDelegate_ &&
2581       [responderDelegate_
2582           respondsToSelector:@selector(scrollOffsetPinnedToLeft:toRight:)]) {
2583     [responderDelegate_ scrollOffsetPinnedToLeft:left toRight:right];
2584   }
2587 - (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar {
2588   if (responderDelegate_ &&
2589       [responderDelegate_
2590           respondsToSelector:@selector(setHasHorizontalScrollbar:)]) {
2591     [responderDelegate_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
2592   }
2595 - (BOOL)respondsToSelector:(SEL)selector {
2596   // Trickiness: this doesn't mean "does this object's superclass respond to
2597   // this selector" but rather "does the -respondsToSelector impl from the
2598   // superclass say that this class responds to the selector".
2599   if ([super respondsToSelector:selector])
2600     return YES;
2602   if (responderDelegate_)
2603     return [responderDelegate_ respondsToSelector:selector];
2605   return NO;
2608 - (id)forwardingTargetForSelector:(SEL)selector {
2609   if ([responderDelegate_ respondsToSelector:selector])
2610     return responderDelegate_.get();
2612   return [super forwardingTargetForSelector:selector];
2615 - (void)setCanBeKeyView:(BOOL)can {
2616   canBeKeyView_ = can;
2619 - (BOOL)acceptsMouseEventsWhenInactive {
2620   // Some types of windows (balloons, always-on-top panels) want to accept mouse
2621   // clicks w/o the first click being treated as 'activation'. Same applies to
2622   // mouse move events.
2623   return [[self window] level] > NSNormalWindowLevel;
2626 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
2627   return [self acceptsMouseEventsWhenInactive];
2630 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
2631   takesFocusOnlyOnMouseDown_ = b;
2634 - (void)setCloseOnDeactivate:(BOOL)b {
2635   closeOnDeactivate_ = b;
2638 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
2639   NSWindow* window = [self window];
2640   // If this is a background window, don't handle mouse movement events. This
2641   // is the expected behavior on the Mac as evidenced by other applications.
2642   if ([theEvent type] == NSMouseMoved &&
2643       ![self acceptsMouseEventsWhenInactive] &&
2644       ![window isKeyWindow]) {
2645     return YES;
2646   }
2648   // Use hitTest to check whether the mouse is over a nonWebContentView - in
2649   // which case the mouse event should not be handled by the render host.
2650   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
2651   NSView* contentView = [window contentView];
2652   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
2653   // Traverse the superview hierarchy as the hitTest will return the frontmost
2654   // view, such as an NSTextView, while nonWebContentView may be specified by
2655   // its parent view.
2656   while (view) {
2657     if ([view respondsToSelector:nonWebContentViewSelector] &&
2658         [view performSelector:nonWebContentViewSelector]) {
2659       // The cursor is over a nonWebContentView - ignore this mouse event.
2660       return YES;
2661     }
2662     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
2663         !hasOpenMouseDown_) {
2664       // The cursor is over an overlapping render widget. This check is done by
2665       // both views so the one that's returned by -hitTest: will end up
2666       // processing the event.
2667       // Note that while dragging, we only get events for the render view where
2668       // drag started, even if mouse is  actually over another view or outside
2669       // the window. Cocoa does this for us. We should handle these events and
2670       // not ignore (since there is no other render view to handle them). Thus
2671       // the |!hasOpenMouseDown_| check above.
2672       return YES;
2673     }
2674     view = [view superview];
2675   }
2676   return NO;
2679 - (void)mouseEvent:(NSEvent*)theEvent {
2680   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
2681   if (responderDelegate_ &&
2682       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2683     BOOL handled = [responderDelegate_ handleEvent:theEvent];
2684     if (handled)
2685       return;
2686   }
2688   if ([self shouldIgnoreMouseEvent:theEvent]) {
2689     // If this is the first such event, send a mouse exit to the host view.
2690     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
2691       WebMouseEvent exitEvent =
2692           WebInputEventFactory::mouseEvent(theEvent, self);
2693       exitEvent.type = WebInputEvent::MouseLeave;
2694       exitEvent.button = WebMouseEvent::ButtonNone;
2695       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
2696     }
2697     mouseEventWasIgnored_ = YES;
2698     return;
2699   }
2701   if (mouseEventWasIgnored_) {
2702     // If this is the first mouse event after a previous event that was ignored
2703     // due to the hitTest, send a mouse enter event to the host view.
2704     if (renderWidgetHostView_->render_widget_host_) {
2705       WebMouseEvent enterEvent =
2706           WebInputEventFactory::mouseEvent(theEvent, self);
2707       enterEvent.type = WebInputEvent::MouseMove;
2708       enterEvent.button = WebMouseEvent::ButtonNone;
2709       renderWidgetHostView_->ForwardMouseEvent(enterEvent);
2710     }
2711   }
2712   mouseEventWasIgnored_ = NO;
2714   // TODO(rohitrao): Probably need to handle other mouse down events here.
2715   if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
2716     if (renderWidgetHostView_->render_widget_host_)
2717       renderWidgetHostView_->render_widget_host_->OnPointerEventActivate();
2719     // Manually take focus after the click but before forwarding it to the
2720     // renderer.
2721     [[self window] makeFirstResponder:self];
2722   }
2724   // Don't cancel child popups; killing them on a mouse click would prevent the
2725   // user from positioning the insertion point in the text field spawning the
2726   // popup. A click outside the text field would cause the text field to drop
2727   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
2728   // the popup anyway, so we're OK.
2730   NSEventType type = [theEvent type];
2731   if (type == NSLeftMouseDown)
2732     hasOpenMouseDown_ = YES;
2733   else if (type == NSLeftMouseUp)
2734     hasOpenMouseDown_ = NO;
2736   // TODO(suzhe): We should send mouse events to the input method first if it
2737   // wants to handle them. But it won't work without implementing method
2738   // - (NSUInteger)characterIndexForPoint:.
2739   // See: http://code.google.com/p/chromium/issues/detail?id=47141
2740   // Instead of sending mouse events to the input method first, we now just
2741   // simply confirm all ongoing composition here.
2742   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
2743       type == NSOtherMouseDown) {
2744     [self confirmComposition];
2745   }
2747   const WebMouseEvent event =
2748       WebInputEventFactory::mouseEvent(theEvent, self);
2749   renderWidgetHostView_->ForwardMouseEvent(event);
2752 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
2753   // |performKeyEquivalent:| is sent to all views of a window, not only down the
2754   // responder chain (cf. "Handling Key Equivalents" in
2755   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
2756   // ). We only want to handle key equivalents if we're first responder.
2757   if ([[self window] firstResponder] != self)
2758     return NO;
2760   // If we return |NO| from this function, cocoa will send the key event to
2761   // the menu and only if the menu does not process the event to |keyDown:|. We
2762   // want to send the event to a renderer _before_ sending it to the menu, so
2763   // we need to return |YES| for all events that might be swallowed by the menu.
2764   // We do not return |YES| for every keypress because we don't get |keyDown:|
2765   // events for keys that we handle this way.
2766   NSUInteger modifierFlags = [theEvent modifierFlags];
2767   if ((modifierFlags & NSCommandKeyMask) == 0) {
2768     // Make sure the menu does not contain key equivalents that don't
2769     // contain cmd.
2770     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
2771     return NO;
2772   }
2774   // Command key combinations are sent via performKeyEquivalent rather than
2775   // keyDown:. We just forward this on and if WebCore doesn't want to handle
2776   // it, we let the WebContentsView figure out how to reinject it.
2777   [self keyEvent:theEvent wasKeyEquivalent:YES];
2778   return YES;
2781 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
2782   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
2783   // returned NO. If this function returns |YES|, Cocoa sends the event to
2784   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
2785   // to us instead of doing key view loop control, ctrl-left/right get handled
2786   // correctly, etc.
2787   // (However, there are still some keys that Cocoa swallows, e.g. the key
2788   // equivalent that Cocoa uses for toggling the input language. In this case,
2789   // that's actually a good thing, though -- see http://crbug.com/26115 .)
2790   return YES;
2793 - (EventHandled)keyEvent:(NSEvent*)theEvent {
2794   if (responderDelegate_ &&
2795       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2796     BOOL handled = [responderDelegate_ handleEvent:theEvent];
2797     if (handled)
2798       return kEventHandled;
2799   }
2801   [self keyEvent:theEvent wasKeyEquivalent:NO];
2802   return kEventHandled;
2805 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
2806   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
2807   DCHECK([theEvent type] != NSKeyDown ||
2808          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
2810   if ([theEvent type] == NSFlagsChanged) {
2811     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
2812     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
2813     int keyCode = [theEvent keyCode];
2814     if (!keyCode || keyCode == 10 || keyCode == 63)
2815       return;
2816   }
2818   // Don't cancel child popups; the key events are probably what's triggering
2819   // the popup in the first place.
2821   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
2822   DCHECK(widgetHost);
2824   NativeWebKeyboardEvent event(theEvent);
2826   // Force fullscreen windows to close on Escape so they won't keep the keyboard
2827   // grabbed or be stuck onscreen if the renderer is hanging.
2828   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
2829       event.windowsKeyCode == ui::VKEY_ESCAPE &&
2830       renderWidgetHostView_->pepper_fullscreen_window()) {
2831     RenderWidgetHostViewMac* parent =
2832         renderWidgetHostView_->fullscreen_parent_host_view();
2833     if (parent)
2834       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
2835     widgetHost->Shutdown();
2836     return;
2837   }
2839   // Suppress the escape key up event if necessary.
2840   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
2841     if (event.type == NativeWebKeyboardEvent::KeyUp)
2842       suppressNextEscapeKeyUp_ = NO;
2843     return;
2844   }
2846   // We only handle key down events and just simply forward other events.
2847   if ([theEvent type] != NSKeyDown) {
2848     widgetHost->ForwardKeyboardEvent(event);
2850     // Possibly autohide the cursor.
2851     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2852       [NSCursor setHiddenUntilMouseMoves:YES];
2854     return;
2855   }
2857   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2859   // Records the current marked text state, so that we can know if the marked
2860   // text was deleted or not after handling the key down event.
2861   BOOL oldHasMarkedText = hasMarkedText_;
2863   // This method should not be called recursively.
2864   DCHECK(!handlingKeyDown_);
2866   // Tells insertText: and doCommandBySelector: that we are handling a key
2867   // down event.
2868   handlingKeyDown_ = YES;
2870   // These variables might be set when handling the keyboard event.
2871   // Clear them here so that we can know whether they have changed afterwards.
2872   textToBeInserted_.clear();
2873   markedText_.clear();
2874   underlines_.clear();
2875   unmarkTextCalled_ = NO;
2876   hasEditCommands_ = NO;
2877   editCommands_.clear();
2879   // Before doing anything with a key down, check to see if plugin IME has been
2880   // cancelled, since the plugin host needs to be informed of that before
2881   // receiving the keydown.
2882   if ([theEvent type] == NSKeyDown)
2883     [self checkForPluginImeCancellation];
2885   // Sends key down events to input method first, then we can decide what should
2886   // be done according to input method's feedback.
2887   // If a plugin is active, bypass this step since events are forwarded directly
2888   // to the plugin IME.
2889   if (focusedPluginIdentifier_ == -1)
2890     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2892   handlingKeyDown_ = NO;
2894   // Indicates if we should send the key event and corresponding editor commands
2895   // after processing the input method result.
2896   BOOL delayEventUntilAfterImeCompostion = NO;
2898   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2899   // while an input method is composing or inserting a text.
2900   // Gmail checks this code in its onkeydown handler to stop auto-completing
2901   // e-mail addresses while composing a CJK text.
2902   // If the text to be inserted has only one character, then we don't need this
2903   // trick, because we'll send the text as a key press event instead.
2904   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2905     NativeWebKeyboardEvent fakeEvent = event;
2906     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2907     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2908     fakeEvent.skip_in_browser = true;
2909     widgetHost->ForwardKeyboardEvent(fakeEvent);
2910     // If this key event was handled by the input method, but
2911     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2912     // enqueued edit commands, then in order to let webkit handle them
2913     // correctly, we need to send the real key event and corresponding edit
2914     // commands after processing the input method result.
2915     // We shouldn't do this if a new marked text was set by the input method,
2916     // otherwise the new marked text might be cancelled by webkit.
2917     if (hasEditCommands_ && !hasMarkedText_)
2918       delayEventUntilAfterImeCompostion = YES;
2919   } else {
2920     if (!editCommands_.empty()) {
2921       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2922           widgetHost->GetRoutingID(), editCommands_));
2923     }
2924     widgetHost->ForwardKeyboardEvent(event);
2925   }
2927   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2928   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2929   // be set to NULL. So we check it here and return immediately if it's NULL.
2930   if (!renderWidgetHostView_->render_widget_host_)
2931     return;
2933   // Then send keypress and/or composition related events.
2934   // If there was a marked text or the text to be inserted is longer than 1
2935   // character, then we send the text by calling ConfirmComposition().
2936   // Otherwise, if the text to be inserted only contains 1 character, then we
2937   // can just send a keypress event which is fabricated by changing the type of
2938   // the keydown event, so that we can retain all necessary informations, such
2939   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2940   // prevent the browser from handling it again.
2941   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2942   // handle BMP characters here, as we can always insert non-BMP characters as
2943   // text.
2944   BOOL textInserted = NO;
2945   if (textToBeInserted_.length() >
2946       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2947     widgetHost->ImeConfirmComposition(
2948         textToBeInserted_, gfx::Range::InvalidRange(), false);
2949     textInserted = YES;
2950   }
2952   // Updates or cancels the composition. If some text has been inserted, then
2953   // we don't need to cancel the composition explicitly.
2954   if (hasMarkedText_ && markedText_.length()) {
2955     // Sends the updated marked text to the renderer so it can update the
2956     // composition node in WebKit.
2957     // When marked text is available, |selectedRange_| will be the range being
2958     // selected inside the marked text.
2959     widgetHost->ImeSetComposition(markedText_, underlines_,
2960                                   selectedRange_.location,
2961                                   NSMaxRange(selectedRange_));
2962   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2963     if (unmarkTextCalled_) {
2964       widgetHost->ImeConfirmComposition(
2965           base::string16(), gfx::Range::InvalidRange(), false);
2966     } else {
2967       widgetHost->ImeCancelComposition();
2968     }
2969   }
2971   // If the key event was handled by the input method but it also generated some
2972   // edit commands, then we need to send the real key event and corresponding
2973   // edit commands here. This usually occurs when the input method wants to
2974   // finish current composition session but still wants the application to
2975   // handle the key event. See http://crbug.com/48161 for reference.
2976   if (delayEventUntilAfterImeCompostion) {
2977     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2978     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2979     // So before sending the real key down event, we need to send a fake key up
2980     // event to balance it.
2981     NativeWebKeyboardEvent fakeEvent = event;
2982     fakeEvent.type = blink::WebInputEvent::KeyUp;
2983     fakeEvent.skip_in_browser = true;
2984     widgetHost->ForwardKeyboardEvent(fakeEvent);
2985     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2986     // a key event with |skip_in_browser| == true won't be handled by browser,
2987     // thus it won't destroy the widget.
2989     if (!editCommands_.empty()) {
2990       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2991           widgetHost->GetRoutingID(), editCommands_));
2992     }
2993     widgetHost->ForwardKeyboardEvent(event);
2995     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2996     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2997     // be set to NULL. So we check it here and return immediately if it's NULL.
2998     if (!renderWidgetHostView_->render_widget_host_)
2999       return;
3000   }
3002   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
3003   // Only send a corresponding key press event if there is no marked text.
3004   if (!hasMarkedText_) {
3005     if (!textInserted && textToBeInserted_.length() == 1) {
3006       // If a single character was inserted, then we just send it as a keypress
3007       // event.
3008       event.type = blink::WebInputEvent::Char;
3009       event.text[0] = textToBeInserted_[0];
3010       event.text[1] = 0;
3011       event.skip_in_browser = true;
3012       widgetHost->ForwardKeyboardEvent(event);
3013     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
3014                [[theEvent characters] length] > 0 &&
3015                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
3016                 (hasEditCommands_ && editCommands_.empty()))) {
3017       // We don't get insertText: calls if ctrl or cmd is down, or the key event
3018       // generates an insert command. So synthesize a keypress event for these
3019       // cases, unless the key event generated any other command.
3020       event.type = blink::WebInputEvent::Char;
3021       event.skip_in_browser = true;
3022       widgetHost->ForwardKeyboardEvent(event);
3023     }
3024   }
3026   // Possibly autohide the cursor.
3027   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
3028     [NSCursor setHiddenUntilMouseMoves:YES];
3031 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
3032   DCHECK(base::mac::IsOSLionOrLater());
3034   if ([event phase] != NSEventPhaseEnded &&
3035       [event phase] != NSEventPhaseCancelled) {
3036     return;
3037   }
3039   if (renderWidgetHostView_->render_widget_host_) {
3040     // History-swiping is not possible if the logic reaches this point.
3041     // Allow rubber-banding in both directions.
3042     bool canRubberbandLeft = true;
3043     bool canRubberbandRight = true;
3044     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
3045         event, self, canRubberbandLeft, canRubberbandRight);
3046     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
3047   }
3049   if (endWheelMonitor_) {
3050     [NSEvent removeMonitor:endWheelMonitor_];
3051     endWheelMonitor_ = nil;
3052   }
3055 - (void)beginGestureWithEvent:(NSEvent*)event {
3056   [responderDelegate_ beginGestureWithEvent:event];
3058 - (void)endGestureWithEvent:(NSEvent*)event {
3059   [responderDelegate_ endGestureWithEvent:event];
3061 - (void)touchesMovedWithEvent:(NSEvent*)event {
3062   [responderDelegate_ touchesMovedWithEvent:event];
3064 - (void)touchesBeganWithEvent:(NSEvent*)event {
3065   [responderDelegate_ touchesBeganWithEvent:event];
3067 - (void)touchesCancelledWithEvent:(NSEvent*)event {
3068   [responderDelegate_ touchesCancelledWithEvent:event];
3070 - (void)touchesEndedWithEvent:(NSEvent*)event {
3071   [responderDelegate_ touchesEndedWithEvent:event];
3074 // This is invoked only on 10.8 or newer when the user taps a word using
3075 // three fingers.
3076 - (void)quickLookWithEvent:(NSEvent*)event {
3077   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3078   TextInputClientMac::GetInstance()->GetStringAtPoint(
3079       renderWidgetHostView_->render_widget_host_,
3080       gfx::Point(point.x, NSHeight([self frame]) - point.y),
3081       ^(NSAttributedString* string, NSPoint baselinePoint) {
3082           if (string && [string length] > 0) {
3083             dispatch_async(dispatch_get_main_queue(), ^{
3084                 [self showDefinitionForAttributedString:string
3085                                                 atPoint:baselinePoint];
3086             });
3087           }
3088       }
3089   );
3092 // This method handles 2 different types of hardware events.
3093 // (Apple does not distinguish between them).
3094 //  a. Scrolling the middle wheel of a mouse.
3095 //  b. Swiping on the track pad.
3097 // This method is responsible for 2 types of behavior:
3098 //  a. Scrolling the content of window.
3099 //  b. Navigating forwards/backwards in history.
3101 // This is a brief description of the logic:
3102 //  1. If the content can be scrolled, scroll the content.
3103 //     (This requires a roundtrip to blink to determine whether the content
3104 //      can be scrolled.)
3105 //     Once this logic is triggered, the navigate logic cannot be triggered
3106 //     until the gesture finishes.
3107 //  2. If the user is making a horizontal swipe, start the navigate
3108 //     forward/backwards UI.
3109 //     Once this logic is triggered, the user can either cancel or complete
3110 //     the gesture. If the user completes the gesture, all remaining touches
3111 //     are swallowed, and not allowed to scroll the content. If the user
3112 //     cancels the gesture, all remaining touches are forwarded to the content
3113 //     scroll logic. The user cannot trigger the navigation logic again.
3114 - (void)scrollWheel:(NSEvent*)event {
3115   if (responderDelegate_ &&
3116       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
3117     BOOL handled = [responderDelegate_ handleEvent:event];
3118     if (handled)
3119       return;
3120   }
3122   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
3123   // the event is received even when the mouse cursor is no longer over the view
3124   // when the scrolling ends (e.g. if the tab was switched). This is necessary
3125   // for ending rubber-banding in such cases.
3126   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
3127       !endWheelMonitor_) {
3128     endWheelMonitor_ =
3129       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
3130       handler:^(NSEvent* blockEvent) {
3131           [self shortCircuitScrollWheelEvent:blockEvent];
3132           return blockEvent;
3133       }];
3134   }
3136   // This is responsible for content scrolling!
3137   if (renderWidgetHostView_->render_widget_host_) {
3138     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
3139     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
3140     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
3141         event, self, canRubberbandLeft, canRubberbandRight);
3142     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
3143   }
3146 // Called repeatedly during a pinch gesture, with incremental change values.
3147 - (void)magnifyWithEvent:(NSEvent*)event {
3148   if (renderWidgetHostView_->render_widget_host_) {
3149     // Send a GesturePinchUpdate event.
3150     // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
3151     // GestureSrollBegin/End) as is done for touchscreen.  Keeping track of when
3152     // a pinch is active would take a little more work here, and we don't need
3153     // it for anything yet.
3154     const WebGestureEvent& webEvent =
3155         WebInputEventFactory::gestureEvent(event, self);
3156     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
3157   }
3160 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
3161   NSWindow* oldWindow = [self window];
3163   // We're messing with the window, so do this to ensure no flashes. This one
3164   // prevents a flash when the current tab is closed.
3165   if (!renderWidgetHostView_->use_core_animation_)
3166     [oldWindow disableScreenUpdatesUntilFlush];
3168   NSNotificationCenter* notificationCenter =
3169       [NSNotificationCenter defaultCenter];
3171   // Backing property notifications crash on 10.6 when building with the 10.7
3172   // SDK, see http://crbug.com/260595.
3173   static BOOL supportsBackingPropertiesNotification =
3174       SupportsBackingPropertiesChangedNotification();
3176   if (oldWindow) {
3177     if (supportsBackingPropertiesNotification) {
3178       [notificationCenter
3179           removeObserver:self
3180                     name:NSWindowDidChangeBackingPropertiesNotification
3181                   object:oldWindow];
3182     }
3183     [notificationCenter
3184         removeObserver:self
3185                   name:NSWindowDidMoveNotification
3186                 object:oldWindow];
3187     [notificationCenter
3188         removeObserver:self
3189                   name:NSWindowDidEndLiveResizeNotification
3190                 object:oldWindow];
3191   }
3192   if (newWindow) {
3193     if (supportsBackingPropertiesNotification) {
3194       [notificationCenter
3195           addObserver:self
3196              selector:@selector(windowDidChangeBackingProperties:)
3197                  name:NSWindowDidChangeBackingPropertiesNotification
3198                object:newWindow];
3199     }
3200     [notificationCenter
3201         addObserver:self
3202            selector:@selector(windowChangedGlobalFrame:)
3203                name:NSWindowDidMoveNotification
3204              object:newWindow];
3205     [notificationCenter
3206         addObserver:self
3207            selector:@selector(windowChangedGlobalFrame:)
3208                name:NSWindowDidEndLiveResizeNotification
3209              object:newWindow];
3210   }
3213 - (void)updateScreenProperties{
3214   renderWidgetHostView_->UpdateBackingStoreScaleFactor();
3215   renderWidgetHostView_->UpdateDisplayLink();
3218 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
3219 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
3220   // Background tabs check if their scale factor or vsync properties changed
3221   // when they are added to a window.
3223   // Allocating a CGLayerRef with the current scale factor immediately from
3224   // this handler doesn't work. Schedule the backing store update on the
3225   // next runloop cycle, then things are read for CGLayerRef allocations to
3226   // work.
3227   [self performSelector:@selector(updateScreenProperties)
3228              withObject:nil
3229              afterDelay:0];
3232 - (void)globalFrameDidChange:(NSNotification*)notification {
3233   if (handlingGlobalFrameDidChange_)
3234     return;
3236   handlingGlobalFrameDidChange_ = YES;
3237   if (!renderWidgetHostView_->use_core_animation_ &&
3238       renderWidgetHostView_->compositing_iosurface_context_) {
3239     [renderWidgetHostView_->compositing_iosurface_context_->nsgl_context()
3240         update];
3241   }
3242   handlingGlobalFrameDidChange_ = NO;
3245 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
3246   renderWidgetHostView_->UpdateScreenInfo(
3247       renderWidgetHostView_->GetNativeView());
3250 - (void)setFrameSize:(NSSize)newSize {
3251   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
3253   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
3254   // -setFrame: isn't neccessary.
3255   [super setFrameSize:newSize];
3257   if (!renderWidgetHostView_->render_widget_host_)
3258     return;
3260   // Move the CALayers to their positions in the new view size. Note that
3261   // this will not draw anything because the non-background layers' sizes
3262   // didn't actually change.
3263   renderWidgetHostView_->LayoutLayers();
3265   renderWidgetHostView_->render_widget_host_->SendScreenRects();
3266   renderWidgetHostView_->render_widget_host_->WasResized();
3267   if (renderWidgetHostView_->delegated_frame_host_)
3268     renderWidgetHostView_->delegated_frame_host_->WasResized();
3270   // Wait for the frame that WasResize might have requested. If the view is
3271   // being made visible at a new size, then this call will have no effect
3272   // because the view widget is still hidden, and the pause call in WasShown
3273   // will have this effect for us.
3274   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
3277 // Fills with white the parts of the area to the right and bottom for |rect|
3278 // that intersect |damagedRect|.
3279 - (void)fillBottomRightRemainderOfRect:(gfx::Rect)rect
3280                              dirtyRect:(gfx::Rect)damagedRect
3281                              inContext:(CGContextRef)context {
3282   if (damagedRect.right() > rect.right()) {
3283     int x = std::max(rect.right(), damagedRect.x());
3284     int y = std::min(rect.bottom(), damagedRect.bottom());
3285     int width = damagedRect.right() - x;
3286     int height = damagedRect.y() - y;
3288     // Extra fun to get around the fact that gfx::Rects can't have
3289     // negative sizes.
3290     if (width < 0) {
3291       x += width;
3292       width = -width;
3293     }
3294     if (height < 0) {
3295       y += height;
3296       height = -height;
3297     }
3299     NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
3300     CGContextSetFillColorWithColor(context,
3301                                    CGColorGetConstantColor(kCGColorWhite));
3302     CGContextFillRect(context, NSRectToCGRect(r));
3303   }
3304   if (damagedRect.bottom() > rect.bottom()) {
3305     int x = damagedRect.x();
3306     int y = damagedRect.bottom();
3307     int width = damagedRect.right() - x;
3308     int height = std::max(rect.bottom(), damagedRect.y()) - y;
3310     // Extra fun to get around the fact that gfx::Rects can't have
3311     // negative sizes.
3312     if (width < 0) {
3313       x += width;
3314       width = -width;
3315     }
3316     if (height < 0) {
3317       y += height;
3318       height = -height;
3319     }
3321     NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
3322     CGContextSetFillColorWithColor(context,
3323                                    CGColorGetConstantColor(kCGColorWhite));
3324     CGContextFillRect(context, NSRectToCGRect(r));
3325   }
3328 - (void)drawRect:(NSRect)dirtyRect {
3329   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::drawRect");
3330   DCHECK(!renderWidgetHostView_->use_core_animation_);
3332   if (!renderWidgetHostView_->render_widget_host_) {
3333     // When using CoreAnimation, this path is used to paint the contents area
3334     // white before any frames come in. When layers to draw frames exist, this
3335     // is not hit.
3336     [[NSColor whiteColor] set];
3337     NSRectFill(dirtyRect);
3338     return;
3339   }
3341   const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]);
3343   if (renderWidgetHostView_->last_frame_was_accelerated_ &&
3344       renderWidgetHostView_->compositing_iosurface_) {
3345     if (renderWidgetHostView_->allow_overlapping_views_) {
3346       // If overlapping views need to be allowed, punch a hole in the window
3347       // to expose the GL underlay.
3348       TRACE_EVENT2("gpu", "NSRectFill clear", "w", damagedRect.width(),
3349                    "h", damagedRect.height());
3350       // NSRectFill is extremely slow (15ms for a window on a fast MacPro), so
3351       // this is only done when it's a real invalidation from window damage (not
3352       // when a BuffersSwapped was received). Note that even a 1x1 NSRectFill
3353       // can take many milliseconds sometimes (!) so this is skipped completely
3354       // for drawRects that are triggered by BuffersSwapped messages.
3355       [[NSColor clearColor] set];
3356       NSRectFill(dirtyRect);
3357     }
3359     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
3360         renderWidgetHostView_->compositing_iosurface_context_->cgl_context());
3361     renderWidgetHostView_->DrawIOSurfaceWithoutCoreAnimation();
3362     return;
3363   }
3365   CGContextRef context = static_cast<CGContextRef>(
3366       [[NSGraphicsContext currentContext] graphicsPort]);
3367   [self drawWithDirtyRect:NSRectToCGRect(dirtyRect)
3368                 inContext:context];
3371 - (void)drawWithDirtyRect:(CGRect)dirtyRect
3372                 inContext:(CGContextRef)context {
3373   content::SoftwareFrameManager* software_frame_manager =
3374       renderWidgetHostView_->software_frame_manager_.get();
3375   if (software_frame_manager->HasCurrentFrame()) {
3376     // Note: All coordinates are in view units, not pixels.
3377     gfx::Rect bitmapRect(
3378             software_frame_manager->GetCurrentFrameSizeInDIP());
3380     // Specify the proper y offset to ensure that the view is rooted to the
3381     // upper left corner.  This can be negative, if the window was resized
3382     // smaller and the renderer hasn't yet repainted.
3383     int yOffset = NSHeight([self bounds]) - bitmapRect.height();
3385     NSRect nsDirtyRect = NSRectFromCGRect(dirtyRect);
3386     const gfx::Rect damagedRect([self flipNSRectToRect:nsDirtyRect]);
3388     gfx::Rect paintRect = gfx::IntersectRects(bitmapRect, damagedRect);
3389     if (!paintRect.IsEmpty()) {
3390       gfx::Size sizeInPixels =
3391           software_frame_manager->GetCurrentFrameSizeInPixels();
3392       base::ScopedCFTypeRef<CGDataProviderRef> dataProvider(
3393           CGDataProviderCreateWithData(
3394               NULL,
3395               software_frame_manager->GetCurrentFramePixels(),
3396               4 * sizeInPixels.width() * sizeInPixels.height(),
3397               NULL));
3398       base::ScopedCFTypeRef<CGImageRef> image(
3399           CGImageCreate(
3400               sizeInPixels.width(),
3401               sizeInPixels.height(),
3402               8,
3403               32,
3404               4 * sizeInPixels.width(),
3405               base::mac::GetSystemColorSpace(),
3406               kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
3407               dataProvider,
3408               NULL,
3409               false,
3410               kCGRenderingIntentDefault));
3411       CGRect imageRect = bitmapRect.ToCGRect();
3412       imageRect.origin.y = yOffset;
3413       CGContextDrawImage(context, imageRect, image);
3414     }
3416     renderWidgetHostView_->SendPendingLatencyInfoToHost();
3418     // Fill the remaining portion of the damagedRect with white
3419     [self fillBottomRightRemainderOfRect:bitmapRect
3420                                dirtyRect:damagedRect
3421                                inContext:context];
3423     if (!renderWidgetHostView_->whiteout_start_time_.is_null()) {
3424       base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
3425           renderWidgetHostView_->whiteout_start_time_;
3426       UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
3428       // Reset the start time to 0 so that we start recording again the next
3429       // time the backing store is NULL...
3430       renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks();
3431     }
3432     if (!renderWidgetHostView_->web_contents_switch_paint_time_.is_null()) {
3433       base::TimeDelta web_contents_switch_paint_duration =
3434           base::TimeTicks::Now() -
3435               renderWidgetHostView_->web_contents_switch_paint_time_;
3436       UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
3437           web_contents_switch_paint_duration);
3438       // Reset contents_switch_paint_time_ to 0 so future tab selections are
3439       // recorded.
3440       renderWidgetHostView_->web_contents_switch_paint_time_ =
3441           base::TimeTicks();
3442     }
3443   } else {
3444     CGContextSetFillColorWithColor(context,
3445                                    CGColorGetConstantColor(kCGColorWhite));
3446     CGContextFillRect(context, dirtyRect);
3447     if (renderWidgetHostView_->whiteout_start_time_.is_null())
3448       renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks::Now();
3449   }
3452 - (BOOL)canBecomeKeyView {
3453   if (!renderWidgetHostView_->render_widget_host_)
3454     return NO;
3456   return canBeKeyView_;
3459 - (BOOL)acceptsFirstResponder {
3460   if (!renderWidgetHostView_->render_widget_host_)
3461     return NO;
3463   return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
3466 - (BOOL)becomeFirstResponder {
3467   if (!renderWidgetHostView_->render_widget_host_)
3468     return NO;
3470   renderWidgetHostView_->render_widget_host_->Focus();
3471   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
3472   renderWidgetHostView_->SetTextInputActive(true);
3474   // Cancel any onging composition text which was left before we lost focus.
3475   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
3476   // somehow that method won't be called when switching among different tabs.
3477   // See http://crbug.com/47209
3478   [self cancelComposition];
3480   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
3481       [[self window] keyViewSelectionDirection]];
3482   NSDictionary* userInfo =
3483       [NSDictionary dictionaryWithObject:direction
3484                                   forKey:kSelectionDirection];
3485   [[NSNotificationCenter defaultCenter]
3486       postNotificationName:kViewDidBecomeFirstResponder
3487                     object:self
3488                   userInfo:userInfo];
3490   return YES;
3493 - (BOOL)resignFirstResponder {
3494   renderWidgetHostView_->SetTextInputActive(false);
3495   if (!renderWidgetHostView_->render_widget_host_)
3496     return YES;
3498   if (closeOnDeactivate_)
3499     renderWidgetHostView_->KillSelf();
3501   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
3502   renderWidgetHostView_->render_widget_host_->Blur();
3504   // We should cancel any onging composition whenever RWH's Blur() method gets
3505   // called, because in this case, webkit will confirm the ongoing composition
3506   // internally.
3507   [self cancelComposition];
3509   return YES;
3512 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
3513   if (responderDelegate_ &&
3514       [responderDelegate_
3515           respondsToSelector:@selector(validateUserInterfaceItem:
3516                                                      isValidItem:)]) {
3517     BOOL valid;
3518     BOOL known =
3519         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
3520     if (known)
3521       return valid;
3522   }
3524   SEL action = [item action];
3526   if (action == @selector(stopSpeaking:)) {
3527     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
3528            renderWidgetHostView_->IsSpeaking();
3529   }
3530   if (action == @selector(startSpeaking:)) {
3531     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
3532            renderWidgetHostView_->SupportsSpeech();
3533   }
3535   // For now, these actions are always enabled for render view,
3536   // this is sub-optimal.
3537   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
3538   if (action == @selector(undo:) ||
3539       action == @selector(redo:) ||
3540       action == @selector(cut:) ||
3541       action == @selector(copy:) ||
3542       action == @selector(copyToFindPboard:) ||
3543       action == @selector(paste:) ||
3544       action == @selector(pasteAndMatchStyle:)) {
3545     return renderWidgetHostView_->render_widget_host_->IsRenderView();
3546   }
3548   return editCommand_helper_->IsMenuItemEnabled(action, self);
3551 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
3552   return renderWidgetHostView_.get();
3555 // Determine whether we should autohide the cursor (i.e., hide it until mouse
3556 // move) for the given event. Customize here to be more selective about which
3557 // key presses to autohide on.
3558 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
3559   return ([event type] == NSKeyDown &&
3560              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
3563 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
3564                                          index:(NSUInteger)index
3565                                       maxCount:(NSUInteger)maxCount {
3566   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
3567   NSUInteger totalLength = [fullArray count];
3568   if (index >= totalLength)
3569     return nil;
3570   NSUInteger length = MIN(totalLength - index, maxCount);
3571   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
3574 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
3575   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
3576   return [fullArray count];
3579 - (id)accessibilityAttributeValue:(NSString *)attribute {
3580   BrowserAccessibilityManager* manager =
3581       renderWidgetHostView_->GetBrowserAccessibilityManager();
3583   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
3584   // BrowserAccessibilityManager. Children includes all subviews in addition to
3585   // contents. Currently we do not have subviews besides the document view.
3586   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
3587           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
3588       manager) {
3589     return [NSArray arrayWithObjects:manager->
3590         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
3591   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
3592     return NSAccessibilityScrollAreaRole;
3593   }
3594   id ret = [super accessibilityAttributeValue:attribute];
3595   return ret;
3598 - (NSArray*)accessibilityAttributeNames {
3599   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
3600   [ret addObject:NSAccessibilityContentsAttribute];
3601   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
3602   return ret;
3605 - (id)accessibilityHitTest:(NSPoint)point {
3606   if (!renderWidgetHostView_->GetBrowserAccessibilityManager())
3607     return self;
3608   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
3609   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
3610   localPoint.y = NSHeight([self bounds]) - localPoint.y;
3611   BrowserAccessibilityCocoa* root = renderWidgetHostView_->
3612       GetBrowserAccessibilityManager()->
3613           GetRoot()->ToBrowserAccessibilityCocoa();
3614   id obj = [root accessibilityHitTest:localPoint];
3615   return obj;
3618 - (BOOL)accessibilityIsIgnored {
3619   return !renderWidgetHostView_->GetBrowserAccessibilityManager();
3622 - (NSUInteger)accessibilityGetIndexOf:(id)child {
3623   BrowserAccessibilityManager* manager =
3624       renderWidgetHostView_->GetBrowserAccessibilityManager();
3625   // Only child is root.
3626   if (manager &&
3627       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
3628     return 0;
3629   } else {
3630     return NSNotFound;
3631   }
3634 - (id)accessibilityFocusedUIElement {
3635   BrowserAccessibilityManager* manager =
3636       renderWidgetHostView_->GetBrowserAccessibilityManager();
3637   if (manager) {
3638     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
3639     DCHECK(focused_item);
3640     if (focused_item) {
3641       BrowserAccessibilityCocoa* focused_item_cocoa =
3642           focused_item->ToBrowserAccessibilityCocoa();
3643       DCHECK(focused_item_cocoa);
3644       if (focused_item_cocoa)
3645         return focused_item_cocoa;
3646     }
3647   }
3648   return [super accessibilityFocusedUIElement];
3651 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
3652 // with minor modifications for code style and commenting.
3654 //  The 'public' interface is -setToolTipAtMousePoint:. This differs from
3655 // -setToolTip: in that the updated tooltip takes effect immediately,
3656 //  without the user's having to move the mouse out of and back into the view.
3658 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
3659 // the view, which in turn requires overriding some internal tracking-rect
3660 // methods (to keep track of its owner & userdata, which need to be filled out
3661 // in the fake events.) --snej 7/6/09
3665  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3666  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
3668  * Redistribution and use in source and binary forms, with or without
3669  * modification, are permitted provided that the following conditions
3670  * are met:
3672  * 1.  Redistributions of source code must retain the above copyright
3673  *     notice, this list of conditions and the following disclaimer.
3674  * 2.  Redistributions in binary form must reproduce the above copyright
3675  *     notice, this list of conditions and the following disclaimer in the
3676  *     documentation and/or other materials provided with the distribution.
3677  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
3678  *     its contributors may be used to endorse or promote products derived
3679  *     from this software without specific prior written permission.
3681  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
3682  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3683  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3684  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
3685  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3686  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3687  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3688  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3689  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3690  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3691  */
3693 // Any non-zero value will do, but using something recognizable might help us
3694 // debug some day.
3695 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
3697 // Override of a public NSView method, replacing the inherited functionality.
3698 // See above for rationale.
3699 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
3700                                owner:(id)owner
3701                             userData:(void *)data
3702                         assumeInside:(BOOL)assumeInside {
3703   DCHECK(trackingRectOwner_ == nil);
3704   trackingRectOwner_ = owner;
3705   trackingRectUserData_ = data;
3706   return kTrackingRectTag;
3709 // Override of (apparently) a private NSView method(!) See above for rationale.
3710 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
3711                                 owner:(id)owner
3712                              userData:(void *)data
3713                          assumeInside:(BOOL)assumeInside
3714                        useTrackingNum:(int)tag {
3715   DCHECK(tag == 0 || tag == kTrackingRectTag);
3716   DCHECK(trackingRectOwner_ == nil);
3717   trackingRectOwner_ = owner;
3718   trackingRectUserData_ = data;
3719   return kTrackingRectTag;
3722 // Override of (apparently) a private NSView method(!) See above for rationale.
3723 - (void)_addTrackingRects:(NSRect *)rects
3724                     owner:(id)owner
3725              userDataList:(void **)userDataList
3726          assumeInsideList:(BOOL *)assumeInsideList
3727              trackingNums:(NSTrackingRectTag *)trackingNums
3728                     count:(int)count {
3729   DCHECK(count == 1);
3730   DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
3731   DCHECK(trackingRectOwner_ == nil);
3732   trackingRectOwner_ = owner;
3733   trackingRectUserData_ = userDataList[0];
3734   trackingNums[0] = kTrackingRectTag;
3737 // Override of a public NSView method, replacing the inherited functionality.
3738 // See above for rationale.
3739 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
3740   if (tag == 0)
3741     return;
3743   if (tag == kTrackingRectTag) {
3744     trackingRectOwner_ = nil;
3745     return;
3746   }
3748   if (tag == lastToolTipTag_) {
3749     [super removeTrackingRect:tag];
3750     lastToolTipTag_ = 0;
3751     return;
3752   }
3754   // If any other tracking rect is being removed, we don't know how it was
3755   // created and it's possible there's a leak involved (see Radar 3500217).
3756   NOTREACHED();
3759 // Override of (apparently) a private NSView method(!)
3760 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
3761   for (int i = 0; i < count; ++i) {
3762     int tag = tags[i];
3763     if (tag == 0)
3764       continue;
3765     DCHECK(tag == kTrackingRectTag);
3766     trackingRectOwner_ = nil;
3767   }
3770 // Sends a fake NSMouseExited event to the view for its current tracking rect.
3771 - (void)_sendToolTipMouseExited {
3772   // Nothing matters except window, trackingNumber, and userData.
3773   int windowNumber = [[self window] windowNumber];
3774   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
3775                                               location:NSZeroPoint
3776                                          modifierFlags:0
3777                                              timestamp:0
3778                                           windowNumber:windowNumber
3779                                                context:NULL
3780                                            eventNumber:0
3781                                         trackingNumber:kTrackingRectTag
3782                                               userData:trackingRectUserData_];
3783   [trackingRectOwner_ mouseExited:fakeEvent];
3786 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
3787 - (void)_sendToolTipMouseEntered {
3788   // Nothing matters except window, trackingNumber, and userData.
3789   int windowNumber = [[self window] windowNumber];
3790   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
3791                                               location:NSZeroPoint
3792                                          modifierFlags:0
3793                                              timestamp:0
3794                                           windowNumber:windowNumber
3795                                                context:NULL
3796                                            eventNumber:0
3797                                         trackingNumber:kTrackingRectTag
3798                                               userData:trackingRectUserData_];
3799   [trackingRectOwner_ mouseEntered:fakeEvent];
3802 // Sets the view's current tooltip, to be displayed at the current mouse
3803 // location. (This does not make the tooltip appear -- as usual, it only
3804 // appears after a delay.) Pass null to remove the tooltip.
3805 - (void)setToolTipAtMousePoint:(NSString *)string {
3806   NSString *toolTip = [string length] == 0 ? nil : string;
3807   if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
3808       (!toolTip && !toolTip_)) {
3809     return;
3810   }
3812   if (toolTip_) {
3813     [self _sendToolTipMouseExited];
3814   }
3816   toolTip_.reset([toolTip copy]);
3818   if (toolTip) {
3819     // See radar 3500217 for why we remove all tooltips
3820     // rather than just the single one we created.
3821     [self removeAllToolTips];
3822     NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
3823     lastToolTipTag_ = [self addToolTipRect:wideOpenRect
3824                                      owner:self
3825                                   userData:NULL];
3826     [self _sendToolTipMouseEntered];
3827   }
3830 // NSView calls this to get the text when displaying the tooltip.
3831 - (NSString *)view:(NSView *)view
3832   stringForToolTip:(NSToolTipTag)tag
3833              point:(NSPoint)point
3834           userData:(void *)data {
3835   return [[toolTip_ copy] autorelease];
3838 // Below is our NSTextInputClient implementation.
3840 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
3841 // functions to process this event.
3843 // [WebHTMLView keyDown] ->
3844 //     EventHandler::keyEvent() ->
3845 //     ...
3846 //     [WebEditorClient handleKeyboardEvent] ->
3847 //     [WebHTMLView _interceptEditingKeyEvent] ->
3848 //     [NSResponder interpretKeyEvents] ->
3849 //     [WebHTMLView insertText] ->
3850 //     Editor::insertText()
3852 // Unfortunately, it is hard for Chromium to use this implementation because
3853 // it causes key-typing jank.
3854 // RenderWidgetHostViewMac is running in a browser process. On the other
3855 // hand, Editor and EventHandler are running in a renderer process.
3856 // So, if we used this implementation, a NSKeyDown event is dispatched to
3857 // the following functions of Chromium.
3859 // [RenderWidgetHostViewMac keyEvent] (browser) ->
3860 //     |Sync IPC (KeyDown)| (*1) ->
3861 //     EventHandler::keyEvent() (renderer) ->
3862 //     ...
3863 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
3864 //     |Sync IPC| (*2) ->
3865 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
3866 //     [self interpretKeyEvents] ->
3867 //     [RenderWidgetHostViewMac insertText] (browser) ->
3868 //     |Async IPC| ->
3869 //     Editor::insertText() (renderer)
3871 // (*1) we need to wait until this call finishes since WebHTMLView uses the
3872 // result of EventHandler::keyEvent().
3873 // (*2) we need to wait until this call finishes since WebEditorClient uses
3874 // the result of [WebHTMLView _interceptEditingKeyEvent].
3876 // This needs many sync IPC messages sent between a browser and a renderer for
3877 // each key event, which would probably result in key-typing jank.
3878 // To avoid this problem, this implementation processes key events (and input
3879 // method events) totally in a browser process and sends asynchronous input
3880 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
3881 // renderer process.
3883 // [RenderWidgetHostViewMac keyEvent] (browser) ->
3884 //     |Async IPC (RawKeyDown)| ->
3885 //     [self interpretKeyEvents] ->
3886 //     [RenderWidgetHostViewMac insertText] (browser) ->
3887 //     |Async IPC (Char)| ->
3888 //     Editor::insertText() (renderer)
3890 // Since this implementation doesn't have to wait any IPC calls, this doesn't
3891 // make any key-typing jank. --hbono 7/23/09
3893 extern "C" {
3894 extern NSString *NSTextInputReplacementRangeAttributeName;
3897 - (NSArray *)validAttributesForMarkedText {
3898   // This code is just copied from WebKit except renaming variables.
3899   if (!validAttributesForMarkedText_) {
3900     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
3901         NSUnderlineStyleAttributeName,
3902         NSUnderlineColorAttributeName,
3903         NSMarkedClauseSegmentAttributeName,
3904         NSTextInputReplacementRangeAttributeName,
3905         nil]);
3906   }
3907   return validAttributesForMarkedText_.get();
3910 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
3911   DCHECK([self window]);
3912   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
3913   // coordinates (upper left origin). Scroll offsets will be taken care of in
3914   // the renderer.
3915   thePoint = [[self window] convertScreenToBase:thePoint];
3916   thePoint = [self convertPoint:thePoint fromView:nil];
3917   thePoint.y = NSHeight([self frame]) - thePoint.y;
3919   NSUInteger index =
3920       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
3921           renderWidgetHostView_->render_widget_host_,
3922           gfx::Point(thePoint.x, thePoint.y));
3923   return index;
3926 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
3927                              actualRange:(NSRangePointer)actualRange {
3928   NSRect rect;
3929   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
3930           theRange,
3931           &rect,
3932           actualRange)) {
3933     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
3934         renderWidgetHostView_->render_widget_host_, theRange);
3936     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
3937     if (actualRange)
3938       *actualRange = theRange;
3939   }
3941   // The returned rectangle is in WebKit coordinates (upper left origin), so
3942   // flip the coordinate system.
3943   NSRect viewFrame = [self frame];
3944   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
3945   return rect;
3948 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
3949                          actualRange:(NSRangePointer)actualRange {
3950   NSRect rect = [self firstViewRectForCharacterRange:theRange
3951                                          actualRange:actualRange];
3953   // Convert into screen coordinates for return.
3954   rect = [self convertRect:rect toView:nil];
3955   rect.origin = [[self window] convertBaseToScreen:rect.origin];
3956   return rect;
3959 - (NSRange)markedRange {
3960   // An input method calls this method to check if an application really has
3961   // a text being composed when hasMarkedText call returns true.
3962   // Returns the range saved in the setMarkedText method so the input method
3963   // calls the setMarkedText method and we can update the composition node
3964   // there. (When this method returns an empty range, the input method doesn't
3965   // call the setMarkedText method.)
3966   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
3969 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
3970     actualRange:(NSRangePointer)actualRange {
3971   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
3972   if (actualRange)
3973     *actualRange = range;
3974   NSAttributedString* str =
3975       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
3976           renderWidgetHostView_->render_widget_host_, range);
3977   return str;
3980 - (NSInteger)conversationIdentifier {
3981   return reinterpret_cast<NSInteger>(self);
3984 // Each RenderWidgetHostViewCocoa has its own input context, but we return
3985 // nil when the caret is in non-editable content or password box to avoid
3986 // making input methods do their work.
3987 - (NSTextInputContext *)inputContext {
3988   if (focusedPluginIdentifier_ != -1)
3989     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
3991   switch(renderWidgetHostView_->text_input_type_) {
3992     case ui::TEXT_INPUT_TYPE_NONE:
3993     case ui::TEXT_INPUT_TYPE_PASSWORD:
3994       return nil;
3995     default:
3996       return [super inputContext];
3997   }
4000 - (BOOL)hasMarkedText {
4001   // An input method calls this function to figure out whether or not an
4002   // application is really composing a text. If it is composing, it calls
4003   // the markedRange method, and maybe calls the setMarkedText method.
4004   // It seems an input method usually calls this function when it is about to
4005   // cancel an ongoing composition. If an application has a non-empty marked
4006   // range, it calls the setMarkedText method to delete the range.
4007   return hasMarkedText_;
4010 - (void)unmarkText {
4011   // Delete the composition node of the renderer and finish an ongoing
4012   // composition.
4013   // It seems an input method calls the setMarkedText method and set an empty
4014   // text when it cancels an ongoing composition, i.e. I have never seen an
4015   // input method calls this method.
4016   hasMarkedText_ = NO;
4017   markedText_.clear();
4018   underlines_.clear();
4020   // If we are handling a key down event, then ConfirmComposition() will be
4021   // called in keyEvent: method.
4022   if (!handlingKeyDown_) {
4023     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
4024         base::string16(), gfx::Range::InvalidRange(), false);
4025   } else {
4026     unmarkTextCalled_ = YES;
4027   }
4030 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
4031                               replacementRange:(NSRange)replacementRange {
4032   // An input method updates the composition string.
4033   // We send the given text and range to the renderer so it can update the
4034   // composition node of WebKit.
4035   // TODO(suzhe): It's hard for us to support replacementRange without accessing
4036   // the full web content.
4037   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
4038   NSString* im_text = isAttributedString ? [string string] : string;
4039   int length = [im_text length];
4041   // |markedRange_| will get set on a callback from ImeSetComposition().
4042   selectedRange_ = newSelRange;
4043   markedText_ = base::SysNSStringToUTF16(im_text);
4044   hasMarkedText_ = (length > 0);
4046   underlines_.clear();
4047   if (isAttributedString) {
4048     ExtractUnderlines(string, &underlines_);
4049   } else {
4050     // Use a thin black underline by default.
4051     underlines_.push_back(
4052         blink::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
4053   }
4055   // If we are handling a key down event, then SetComposition() will be
4056   // called in keyEvent: method.
4057   // Input methods of Mac use setMarkedText calls with an empty text to cancel
4058   // an ongoing composition. So, we should check whether or not the given text
4059   // is empty to update the input method state. (Our input method backend can
4060   // automatically cancels an ongoing composition when we send an empty text.
4061   // So, it is OK to send an empty text to the renderer.)
4062   if (!handlingKeyDown_) {
4063     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
4064         markedText_, underlines_,
4065         newSelRange.location, NSMaxRange(newSelRange));
4066   }
4069 - (void)doCommandBySelector:(SEL)selector {
4070   // An input method calls this function to dispatch an editing command to be
4071   // handled by this view.
4072   if (selector == @selector(noop:))
4073     return;
4075   std::string command(
4076       [RenderWidgetHostViewMacEditCommandHelper::
4077           CommandNameForSelector(selector) UTF8String]);
4079   // If this method is called when handling a key down event, then we need to
4080   // handle the command in the key event handler. Otherwise we can just handle
4081   // it here.
4082   if (handlingKeyDown_) {
4083     hasEditCommands_ = YES;
4084     // We ignore commands that insert characters, because this was causing
4085     // strange behavior (e.g. tab always inserted a tab rather than moving to
4086     // the next field on the page).
4087     if (!StartsWithASCII(command, "insert", false))
4088       editCommands_.push_back(EditCommand(command, ""));
4089   } else {
4090     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
4091     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
4092                                               command, ""));
4093   }
4096 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
4097   // An input method has characters to be inserted.
4098   // Same as Linux, Mac calls this method not only:
4099   // * when an input method finishs composing text, but also;
4100   // * when we type an ASCII character (without using input methods).
4101   // When we aren't using input methods, we should send the given character as
4102   // a Char event so it is dispatched to an onkeypress() event handler of
4103   // JavaScript.
4104   // On the other hand, when we are using input methods, we should send the
4105   // given characters as an input method event and prevent the characters from
4106   // being dispatched to onkeypress() event handlers.
4107   // Text inserting might be initiated by other source instead of keyboard
4108   // events, such as the Characters dialog. In this case the text should be
4109   // sent as an input method event as well.
4110   // TODO(suzhe): It's hard for us to support replacementRange without accessing
4111   // the full web content.
4112   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
4113   NSString* im_text = isAttributedString ? [string string] : string;
4114   if (handlingKeyDown_) {
4115     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
4116   } else {
4117     gfx::Range replacement_range(replacementRange);
4118     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
4119         base::SysNSStringToUTF16(im_text), replacement_range, false);
4120   }
4122   // Inserting text will delete all marked text automatically.
4123   hasMarkedText_ = NO;
4126 - (void)insertText:(id)string {
4127   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
4130 - (void)viewDidMoveToWindow {
4131   if ([self window])
4132     [self updateScreenProperties];
4134   if (canBeKeyView_) {
4135     NSWindow* newWindow = [self window];
4136     // Pointer comparison only, since we don't know if lastWindow_ is still
4137     // valid.
4138     if (newWindow) {
4139       // If we move into a new window, refresh the frame information. We
4140       // don't need to do it if it was the same window as it used to be in,
4141       // since that case is covered by WasShown(). We only want to do this for
4142       // real browser views, not popups.
4143       if (newWindow != lastWindow_) {
4144         lastWindow_ = newWindow;
4145         renderWidgetHostView_->WindowFrameChanged();
4146       }
4147     }
4148   }
4150   // If we switch windows (or are removed from the view hierarchy), cancel any
4151   // open mouse-downs.
4152   if (hasOpenMouseDown_) {
4153     WebMouseEvent event;
4154     event.type = WebInputEvent::MouseUp;
4155     event.button = WebMouseEvent::ButtonLeft;
4156     renderWidgetHostView_->ForwardMouseEvent(event);
4158     hasOpenMouseDown_ = NO;
4159   }
4162 - (void)undo:(id)sender {
4163   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4164   if (web_contents)
4165     web_contents->Undo();
4168 - (void)redo:(id)sender {
4169   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4170   if (web_contents)
4171     web_contents->Redo();
4174 - (void)cut:(id)sender {
4175   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4176   if (web_contents)
4177     web_contents->Cut();
4180 - (void)copy:(id)sender {
4181   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4182   if (web_contents)
4183     web_contents->Copy();
4186 - (void)copyToFindPboard:(id)sender {
4187   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4188   if (web_contents)
4189     web_contents->CopyToFindPboard();
4192 - (void)paste:(id)sender {
4193   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4194   if (web_contents)
4195     web_contents->Paste();
4198 - (void)pasteAndMatchStyle:(id)sender {
4199   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4200   if (web_contents)
4201     web_contents->PasteAndMatchStyle();
4204 - (void)selectAll:(id)sender {
4205   // editCommand_helper_ adds implementations for most NSResponder methods
4206   // dynamically. But the renderer side only sends selection results back to
4207   // the browser if they were triggered by a keyboard event or went through
4208   // one of the Select methods on RWH. Since selectAll: is called from the
4209   // menu handler, neither is true.
4210   // Explicitly call SelectAll() here to make sure the renderer returns
4211   // selection results.
4212   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4213   if (web_contents)
4214     web_contents->SelectAll();
4217 - (void)startSpeaking:(id)sender {
4218   renderWidgetHostView_->SpeakSelection();
4221 - (void)stopSpeaking:(id)sender {
4222   renderWidgetHostView_->StopSpeaking();
4225 - (void)cancelComposition {
4226   if (!hasMarkedText_)
4227     return;
4229   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
4230   // doesn't call any NSTextInput functions, such as setMarkedText or
4231   // insertText. So, we need to send an IPC message to a renderer so it can
4232   // delete the composition node.
4233   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
4234   [currentInputManager markedTextAbandoned:self];
4236   hasMarkedText_ = NO;
4237   // Should not call [self unmarkText] here, because it'll send unnecessary
4238   // cancel composition IPC message to the renderer.
4241 - (void)confirmComposition {
4242   if (!hasMarkedText_)
4243     return;
4245   if (renderWidgetHostView_->render_widget_host_)
4246     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
4247         base::string16(), gfx::Range::InvalidRange(), false);
4249   [self cancelComposition];
4252 - (void)setPluginImeActive:(BOOL)active {
4253   if (active == pluginImeActive_)
4254     return;
4256   pluginImeActive_ = active;
4257   if (!active) {
4258     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
4259     renderWidgetHostView_->PluginImeCompositionCompleted(
4260         base::string16(), focusedPluginIdentifier_);
4261   }
4264 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
4265   if (focused)
4266     focusedPluginIdentifier_ = pluginId;
4267   else if (focusedPluginIdentifier_ == pluginId)
4268     focusedPluginIdentifier_ = -1;
4270   // Whenever plugin focus changes, plugin IME resets.
4271   [self setPluginImeActive:NO];
4274 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
4275   if (!pluginImeActive_)
4276     return false;
4278   ComplexTextInputPanel* inputPanel =
4279       [ComplexTextInputPanel sharedComplexTextInputPanel];
4280   NSString* composited_string = nil;
4281   BOOL handled = [inputPanel interpretKeyEvent:event
4282                                         string:&composited_string];
4283   if (composited_string) {
4284     renderWidgetHostView_->PluginImeCompositionCompleted(
4285         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
4286     pluginImeActive_ = NO;
4287   }
4288   return handled;
4291 - (void)checkForPluginImeCancellation {
4292   if (pluginImeActive_ &&
4293       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
4294     renderWidgetHostView_->PluginImeCompositionCompleted(
4295         base::string16(), focusedPluginIdentifier_);
4296     pluginImeActive_ = NO;
4297   }
4300 // Overriding a NSResponder method to support application services.
4302 - (id)validRequestorForSendType:(NSString*)sendType
4303                      returnType:(NSString*)returnType {
4304   id requestor = nil;
4305   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
4306   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
4307   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
4308   BOOL takesText =
4309       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
4311   if (sendTypeIsString && hasText && !returnType) {
4312     requestor = self;
4313   } else if (!sendType && returnTypeIsString && takesText) {
4314     requestor = self;
4315   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
4316     requestor = self;
4317   } else {
4318     requestor = [super validRequestorForSendType:sendType
4319                                       returnType:returnType];
4320   }
4321   return requestor;
4324 - (void)viewWillStartLiveResize {
4325   [super viewWillStartLiveResize];
4326   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
4327   if (widget)
4328     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
4331 - (void)viewDidEndLiveResize {
4332   [super viewDidEndLiveResize];
4333   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
4334   if (widget)
4335     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
4338 - (void)updateCursor:(NSCursor*)cursor {
4339   if (currentCursor_ == cursor)
4340     return;
4342   currentCursor_.reset([cursor retain]);
4343   [[self window] invalidateCursorRectsForView:self];
4346 - (void)popupWindowWillClose:(NSNotification *)notification {
4347   renderWidgetHostView_->KillSelf();
4350 @end
4353 // Supporting application services
4355 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
4357 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
4358                              types:(NSArray*)types {
4359   const std::string& str = renderWidgetHostView_->selected_text();
4360   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
4362   base::scoped_nsobject<NSString> text(
4363       [[NSString alloc] initWithUTF8String:str.c_str()]);
4364   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
4365   [pboard declareTypes:toDeclare owner:nil];
4366   return [pboard setString:text forType:NSStringPboardType];
4369 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
4370   NSString *string = [pboard stringForType:NSStringPboardType];
4371   if (!string) return NO;
4373   // If the user is currently using an IME, confirm the IME input,
4374   // and then insert the text from the service, the same as TextEdit and Safari.
4375   [self confirmComposition];
4376   [self insertText:string];
4377   return YES;
4380 - (BOOL)isOpaque {
4381   if (renderWidgetHostView_->use_core_animation_)
4382     return YES;
4383   return [super isOpaque];
4386 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
4387 // regions that are not draggable. (See ControlRegionView in
4388 // native_app_window_cocoa.mm). This requires the render host view to be
4389 // draggable by default.
4390 - (BOOL)mouseDownCanMoveWindow {
4391   return YES;
4394 @end