Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac.mm
blob2e89069f8921a6a50058307ebd33f37c9f5ea571
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/frame_host/frame_tree.h"
31 #include "content/browser/frame_host/frame_tree_node.h"
32 #include "content/browser/frame_host/render_frame_host_impl.h"
33 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
34 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
35 #include "content/browser/renderer_host/compositing_iosurface_mac.h"
36 #include "content/browser/renderer_host/render_view_host_impl.h"
37 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
38 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
39 #import "content/browser/renderer_host/text_input_client_mac.h"
40 #include "content/common/accessibility_messages.h"
41 #include "content/common/edit_command.h"
42 #include "content/common/gpu/gpu_messages.h"
43 #include "content/common/input_messages.h"
44 #include "content/common/view_messages.h"
45 #include "content/common/webplugin_geometry.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/native_web_keyboard_event.h"
48 #include "content/public/browser/notification_service.h"
49 #include "content/public/browser/notification_types.h"
50 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
51 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
52 #include "content/public/browser/user_metrics.h"
53 #include "content/public/browser/web_contents.h"
54 #include "skia/ext/platform_canvas.h"
55 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
56 #include "third_party/WebKit/public/web/WebInputEvent.h"
57 #include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
58 #import "third_party/mozilla/ComplexTextInputPanel.h"
59 #include "ui/base/cocoa/animation_utils.h"
60 #import "ui/base/cocoa/fullscreen_window_manager.h"
61 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
62 #include "ui/events/keycodes/keyboard_codes.h"
63 #include "ui/base/layout.h"
64 #include "ui/gfx/display.h"
65 #include "ui/gfx/point.h"
66 #include "ui/gfx/rect_conversions.h"
67 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
68 #include "ui/gfx/screen.h"
69 #include "ui/gfx/size_conversions.h"
70 #include "ui/gl/gl_switches.h"
71 #include "ui/gl/io_surface_support_mac.h"
73 using content::BrowserAccessibility;
74 using content::BrowserAccessibilityManager;
75 using content::EditCommand;
76 using content::FrameTreeNode;
77 using content::NativeWebKeyboardEvent;
78 using content::RenderFrameHost;
79 using content::RenderViewHost;
80 using content::RenderViewHostImpl;
81 using content::RenderWidgetHostImpl;
82 using content::RenderWidgetHostViewMac;
83 using content::RenderWidgetHostViewMacEditCommandHelper;
84 using content::TextInputClientMac;
85 using content::WebContents;
86 using blink::WebInputEvent;
87 using blink::WebInputEventFactory;
88 using blink::WebMouseEvent;
89 using blink::WebMouseWheelEvent;
90 using blink::WebGestureEvent;
92 // These are not documented, so use only after checking -respondsToSelector:.
93 @interface NSApplication (UndocumentedSpeechMethods)
94 - (void)speakString:(NSString*)string;
95 - (void)stopSpeaking:(id)sender;
96 - (BOOL)isSpeaking;
97 @end
99 // Declare things that are part of the 10.7 SDK.
100 #if !defined(MAC_OS_X_VERSION_10_7) || \
101     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
102 @interface NSView (NSOpenGLSurfaceResolutionLionAPI)
103 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
104 @end
106 static NSString* const NSWindowDidChangeBackingPropertiesNotification =
107     @"NSWindowDidChangeBackingPropertiesNotification";
109 #endif  // 10.7
111 // This method will return YES for OS X versions 10.7.3 and later, and NO
112 // otherwise.
113 // Used to prevent a crash when building with the 10.7 SDK and accessing the
114 // notification below. See: http://crbug.com/260595.
115 static BOOL SupportsBackingPropertiesChangedNotification() {
116   // windowDidChangeBackingProperties: method has been added to the
117   // NSWindowDelegate protocol in 10.7.3, at the same time as the
118   // NSWindowDidChangeBackingPropertiesNotification notification was added.
119   // If the protocol contains this method description, the notification should
120   // be supported as well.
121   Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
122   struct objc_method_description methodDescription =
123       protocol_getMethodDescription(
124           windowDelegateProtocol,
125           @selector(windowDidChangeBackingProperties:),
126           NO,
127           YES);
129   // If the protocol does not contain the method, the returned method
130   // description is {NULL, NULL}
131   return methodDescription.name != NULL || methodDescription.types != NULL;
134 static float ScaleFactorForView(NSView* view) {
135   return ui::GetImageScale(ui::GetScaleFactorForNativeView(view));
138 // Private methods:
139 @interface RenderWidgetHostViewCocoa ()
140 @property(nonatomic, assign) NSRange selectedRange;
141 @property(nonatomic, assign) NSRange markedRange;
143 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
144 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
145 - (void)gotUnhandledWheelEvent;
146 - (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right;
147 - (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar;
148 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
149 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
150 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
151 - (void)drawWithDirtyRect:(CGRect)dirtyRect
152                 inContext:(CGContextRef)context;
153 - (void)checkForPluginImeCancellation;
154 - (void)updateScreenProperties;
155 - (void)setResponderDelegate:
156         (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
157 @end
159 // A window subclass that allows the fullscreen window to become main and gain
160 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
161 // handled by the browser.
162 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
163 @end
165 @implementation PepperFlashFullscreenWindow
167 - (BOOL)canBecomeKeyWindow {
168   return YES;
171 - (BOOL)canBecomeMainWindow {
172   return YES;
175 @end
177 @interface RenderWidgetPopupWindow : NSWindow {
178    // The event tap that allows monitoring of all events, to properly close with
179    // a click outside the bounds of the window.
180   id clickEventTap_;
182 @end
184 @implementation RenderWidgetPopupWindow
186 - (id)initWithContentRect:(NSRect)contentRect
187                 styleMask:(NSUInteger)windowStyle
188                   backing:(NSBackingStoreType)bufferingType
189                     defer:(BOOL)deferCreation {
190   if (self = [super initWithContentRect:contentRect
191                               styleMask:windowStyle
192                                 backing:bufferingType
193                                   defer:deferCreation]) {
194     DCHECK_EQ(content::CORE_ANIMATION_DISABLED,
195               content::GetCoreAnimationStatus());
196     [self setOpaque:NO];
197     [self setBackgroundColor:[NSColor clearColor]];
198     [self startObservingClicks];
199   }
200   return self;
203 - (void)close {
204   [self stopObservingClicks];
205   [super close];
208 // Gets called when the menubar is clicked.
209 // Needed because the local event monitor doesn't see the click on the menubar.
210 - (void)beganTracking:(NSNotification*)notification {
211   [self close];
214 // Install the callback.
215 - (void)startObservingClicks {
216   clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
217       handler:^NSEvent* (NSEvent* event) {
218           if ([event window] == self)
219             return event;
220           NSEventType eventType = [event type];
221           if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
222             [self close];
223           return event;
224   }];
226   NSNotificationCenter* notificationCenter =
227       [NSNotificationCenter defaultCenter];
228   [notificationCenter addObserver:self
229          selector:@selector(beganTracking:)
230              name:NSMenuDidBeginTrackingNotification
231            object:[NSApp mainMenu]];
234 // Remove the callback.
235 - (void)stopObservingClicks {
236   if (!clickEventTap_)
237     return;
239   [NSEvent removeMonitor:clickEventTap_];
240    clickEventTap_ = nil;
242   NSNotificationCenter* notificationCenter =
243       [NSNotificationCenter defaultCenter];
244   [notificationCenter removeObserver:self
245                 name:NSMenuDidBeginTrackingNotification
246               object:[NSApp mainMenu]];
249 @end
251 namespace {
253 // Maximum number of characters we allow in a tooltip.
254 const size_t kMaxTooltipLength = 1024;
256 // TODO(suzhe): Upstream this function.
257 blink::WebColor WebColorFromNSColor(NSColor *color) {
258   CGFloat r, g, b, a;
259   [color getRed:&r green:&g blue:&b alpha:&a];
261   return
262       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
263       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
264       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
265       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
268 // Extract underline information from an attributed string. Mostly copied from
269 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
270 void ExtractUnderlines(
271     NSAttributedString* string,
272     std::vector<blink::WebCompositionUnderline>* underlines) {
273   int length = [[string string] length];
274   int i = 0;
275   while (i < length) {
276     NSRange range;
277     NSDictionary* attrs = [string attributesAtIndex:i
278                               longestEffectiveRange:&range
279                                             inRange:NSMakeRange(i, length - i)];
280     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
281       blink::WebColor color = SK_ColorBLACK;
282       if (NSColor *colorAttr =
283           [attrs objectForKey:NSUnderlineColorAttributeName]) {
284         color = WebColorFromNSColor(
285             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
286       }
287       underlines->push_back(blink::WebCompositionUnderline(
288           range.location, NSMaxRange(range), color, [style intValue] > 1));
289     }
290     i = range.location + range.length;
291   }
294 // EnablePasswordInput() and DisablePasswordInput() are copied from
295 // enableSecureTextInput() and disableSecureTextInput() functions in
296 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
297 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
298 // here, because they are already called in webkit and they are system wide
299 // functions.
300 void EnablePasswordInput() {
301   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
302   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
303                          sizeof(CFArrayRef), &inputSources);
304   CFRelease(inputSources);
307 void DisablePasswordInput() {
308   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
311 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
312 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
313 // value when screen info changes.
314 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
315 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
316 // is necessary.
317 bool g_screen_info_up_to_date = false;
319 float FlipYFromRectToScreen(float y, float rect_height) {
320   TRACE_EVENT0("browser", "FlipYFromRectToScreen");
321   static CGFloat screen_zero_height = 0;
322   if (!g_screen_info_up_to_date) {
323     if ([[NSScreen screens] count] > 0) {
324       screen_zero_height =
325           [[[NSScreen screens] objectAtIndex:0] frame].size.height;
326       g_screen_info_up_to_date = true;
327     } else {
328       return y;
329     }
330   }
331   return screen_zero_height - y - rect_height;
334 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
335 // left of the primary screen (Carbon coordinates), and stuffs it into a
336 // gfx::Rect.
337 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
338   gfx::Rect new_rect(NSRectToCGRect(rect));
339   new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
340   return new_rect;
343 // Returns the window that visually contains the given view. This is different
344 // from [view window] in the case of tab dragging, where the view's owning
345 // window is a floating panel attached to the actual browser window that the tab
346 // is visually part of.
347 NSWindow* ApparentWindowForView(NSView* view) {
348   // TODO(shess): In case of !window, the view has been removed from
349   // the view hierarchy because the tab isn't main.  Could retrieve
350   // the information from the main tab for our window.
351   NSWindow* enclosing_window = [view window];
353   // See if this is a tab drag window. The width check is to distinguish that
354   // case from extension popup windows.
355   NSWindow* ancestor_window = [enclosing_window parentWindow];
356   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
357                           NSWidth([ancestor_window frame]))) {
358     enclosing_window = ancestor_window;
359   }
361   return enclosing_window;
364 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
365   gfx::Display display =
366       gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
368   NSScreen* screen = [NSScreen deepestScreen];
370   blink::WebScreenInfo results;
372   results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
373   results.depth = NSBitsPerPixelFromDepth([screen depth]);
374   results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
375   results.isMonochrome =
376       [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
377   results.rect = display.bounds();
378   results.availableRect = display.work_area();
379   results.orientationAngle = display.RotationAsDegree();
381   return results;
384 void RemoveLayerFromSuperlayer(
385     base::scoped_nsobject<CompositingIOSurfaceLayer> layer) {
386   // Disable the fade-out animation as the layer is removed.
387   ScopedCAActionDisabler disabler;
388   [layer removeFromSuperlayer];
391 }  // namespace
393 namespace content {
395 ///////////////////////////////////////////////////////////////////////////////
396 // RenderWidgetHostViewBase, public:
398 // static
399 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
400     blink::WebScreenInfo* results) {
401   *results = GetWebScreenInfo(NULL);
404 ///////////////////////////////////////////////////////////////////////////////
405 // RenderWidgetHostViewMac, public:
407 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
408     : render_widget_host_(RenderWidgetHostImpl::From(widget)),
409       last_frame_was_accelerated_(false),
410       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
411       can_compose_inline_(true),
412       compositing_iosurface_layer_async_timer_(
413             FROM_HERE, base::TimeDelta::FromMilliseconds(250),
414             this, &RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired),
415       allow_overlapping_views_(false),
416       use_core_animation_(false),
417       pending_latency_info_delay_(0),
418       pending_latency_info_delay_weak_ptr_factory_(this),
419       backing_store_scale_factor_(1),
420       is_loading_(false),
421       weak_factory_(this),
422       fullscreen_parent_host_view_(NULL),
423       underlay_view_has_drawn_(false),
424       overlay_view_weak_factory_(this),
425       software_frame_weak_ptr_factory_(this) {
426   software_frame_manager_.reset(new SoftwareFrameManager(
427       software_frame_weak_ptr_factory_.GetWeakPtr()));
428   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
429   // goes away.  Since we autorelease it, our caller must put
430   // |GetNativeView()| into the view hierarchy right after calling us.
431   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
432                   initWithRenderWidgetHostViewMac:this] autorelease];
434   if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED) {
435     use_core_animation_ = true;
436     background_layer_.reset([[CALayer alloc] init]);
437     [background_layer_
438         setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
439     [cocoa_view_ setLayer:background_layer_];
440     [cocoa_view_ setWantsLayer:YES];
441   }
443   render_widget_host_->SetView(this);
446 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
447   // This is being called from |cocoa_view_|'s destructor, so invalidate the
448   // pointer.
449   cocoa_view_ = nil;
451   UnlockMouse();
453   // Make sure that the layer doesn't reach into the now-invalid object.
454   DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
455   DestroySoftwareLayer();
457   // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
458   // RenderWidgetHost does we need to tell it not to hold a stale pointer to
459   // us.
460   if (render_widget_host_)
461     render_widget_host_->SetView(NULL);
464 void RenderWidgetHostViewMac::SetDelegate(
465     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
466   [cocoa_view_ setResponderDelegate:delegate];
469 void RenderWidgetHostViewMac::SetAllowOverlappingViews(bool overlapping) {
470   if (allow_overlapping_views_ == overlapping)
471     return;
472   allow_overlapping_views_ = overlapping;
473   [cocoa_view_ setNeedsDisplay:YES];
474   [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
477 ///////////////////////////////////////////////////////////////////////////////
478 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
480 bool RenderWidgetHostViewMac::EnsureCompositedIOSurface() {
481   // If the context or the IOSurface's context has had an error, re-build
482   // everything from scratch.
483   if (compositing_iosurface_context_ &&
484       compositing_iosurface_context_->HasBeenPoisoned()) {
485     LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
486                << "context was poisoned";
487     return false;
488   }
489   if (compositing_iosurface_ &&
490       compositing_iosurface_->HasBeenPoisoned()) {
491     LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
492                << "surface was poisoned";
493     return false;
494   }
496   int current_window_number = use_core_animation_ ?
497       CompositingIOSurfaceContext::kOffscreenContextWindowNumber :
498       window_number();
499   bool new_surface_needed = !compositing_iosurface_;
500   bool new_context_needed =
501     !compositing_iosurface_context_ ||
502         (compositing_iosurface_context_ &&
503             compositing_iosurface_context_->window_number() !=
504                 current_window_number);
506   if (!new_surface_needed && !new_context_needed)
507     return true;
509   // Create the GL context and shaders.
510   if (new_context_needed) {
511     scoped_refptr<CompositingIOSurfaceContext> new_context =
512         CompositingIOSurfaceContext::Get(current_window_number);
513     // Un-bind the GL context from this view before binding the new GL
514     // context. Having two GL contexts bound to a view will result in
515     // crashes and corruption.
516     // http://crbug.com/230883
517     ClearBoundContextDrawable();
518     if (!new_context) {
519       LOG(ERROR) << "Failed to create CompositingIOSurfaceContext";
520       return false;
521     }
522     compositing_iosurface_context_ = new_context;
523   }
525   // Create the IOSurface texture.
526   if (new_surface_needed) {
527     compositing_iosurface_.reset(CompositingIOSurfaceMac::Create());
528     if (!compositing_iosurface_) {
529       LOG(ERROR) << "Failed to create CompositingIOSurface";
530       return false;
531     }
532   }
534   return true;
537 void RenderWidgetHostViewMac::EnsureSoftwareLayer() {
538   TRACE_EVENT0("browser", "RenderWidgetHostViewMac::EnsureSoftwareLayer");
539   if (software_layer_ || !use_core_animation_)
540     return;
542   software_layer_.reset([[SoftwareLayer alloc]
543       initWithRenderWidgetHostViewMac:this]);
544   DCHECK(software_layer_);
546   // Disable the fade-in animation as the layer is added.
547   ScopedCAActionDisabler disabler;
548   [background_layer_ addSublayer:software_layer_];
551 void RenderWidgetHostViewMac::DestroySoftwareLayer() {
552   if (!software_layer_)
553     return;
555   // Disable the fade-out animation as the layer is removed.
556   ScopedCAActionDisabler disabler;
557   [software_layer_ removeFromSuperlayer];
558   [software_layer_ disableRendering];
559   software_layer_.reset();
562 void RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer() {
563   TRACE_EVENT0("browser",
564                "RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer");
565   DCHECK(compositing_iosurface_context_);
566   if (compositing_iosurface_layer_ || !use_core_animation_)
567     return;
569   compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc]
570       initWithRenderWidgetHostViewMac:this]);
571   DCHECK(compositing_iosurface_layer_);
573   // Disable the fade-in animation as the layer is added.
574   ScopedCAActionDisabler disabler;
575   [background_layer_ addSublayer:compositing_iosurface_layer_];
578 void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceLayer(
579     DestroyCompositedIOSurfaceLayerBehavior destroy_layer_behavior) {
580   if (!compositing_iosurface_layer_)
581     return;
583   if (destroy_layer_behavior == kRemoveLayerFromHierarchy) {
584     // Disable the fade-out animation as the layer is removed.
585     ScopedCAActionDisabler disabler;
586     [compositing_iosurface_layer_ removeFromSuperlayer];
587   }
588   [compositing_iosurface_layer_ disableCompositing];
589   compositing_iosurface_layer_.reset();
592 void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer(
593     DestroyContextBehavior destroy_context_behavior) {
594   // Any pending frames will not be displayed, so ack them now.
595   SendPendingSwapAck();
597   DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
598   compositing_iosurface_.reset();
600   switch (destroy_context_behavior) {
601     case kLeaveContextBoundToView:
602       break;
603     case kDestroyContext:
604       ClearBoundContextDrawable();
605       compositing_iosurface_context_ = NULL;
606       break;
607     default:
608       NOTREACHED();
609       break;
610   }
613 void RenderWidgetHostViewMac::ClearBoundContextDrawable() {
614   if (use_core_animation_)
615     return;
617   if (compositing_iosurface_context_ &&
618       cocoa_view_ &&
619       [[compositing_iosurface_context_->nsgl_context() view]
620           isEqual:cocoa_view_]) {
621     // Disable screen updates because removing the GL context from below can
622     // cause flashes.
623     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
624     [compositing_iosurface_context_->nsgl_context() clearDrawable];
625   }
628 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
629   bool handled = true;
630   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
631     IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
632     IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
633     IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeScrollbarsForMainFrame,
634                         OnDidChangeScrollbarsForMainFrame)
635     IPC_MESSAGE_UNHANDLED(handled = false)
636   IPC_END_MESSAGE_MAP()
637   return handled;
640 void RenderWidgetHostViewMac::InitAsChild(
641     gfx::NativeView parent_view) {
644 void RenderWidgetHostViewMac::InitAsPopup(
645     RenderWidgetHostView* parent_host_view,
646     const gfx::Rect& pos) {
647   bool activatable = popup_type_ == blink::WebPopupTypeNone;
648   [cocoa_view_ setCloseOnDeactivate:YES];
649   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
651   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
652   origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
654   popup_window_.reset([[RenderWidgetPopupWindow alloc]
655       initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
656                                      pos.width(), pos.height())
657                 styleMask:NSBorderlessWindowMask
658                   backing:NSBackingStoreBuffered
659                     defer:NO]);
660   [popup_window_ setLevel:NSPopUpMenuWindowLevel];
661   [popup_window_ setReleasedWhenClosed:NO];
662   [popup_window_ makeKeyAndOrderFront:nil];
663   [[popup_window_ contentView] addSubview:cocoa_view_];
664   [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
665   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
666   [[NSNotificationCenter defaultCenter]
667       addObserver:cocoa_view_
668          selector:@selector(popupWindowWillClose:)
669              name:NSWindowWillCloseNotification
670            object:popup_window_];
673 // This function creates the fullscreen window and hides the dock and menubar if
674 // necessary. Note, this codepath is only used for pepper flash when
675 // pp::FlashFullScreen::SetFullscreen() is called. If
676 // pp::FullScreen::SetFullscreen() is called then the entire browser window
677 // will enter fullscreen instead.
678 void RenderWidgetHostViewMac::InitAsFullscreen(
679     RenderWidgetHostView* reference_host_view) {
680   fullscreen_parent_host_view_ =
681       static_cast<RenderWidgetHostViewMac*>(reference_host_view);
682   NSWindow* parent_window = nil;
683   if (reference_host_view)
684     parent_window = [reference_host_view->GetNativeView() window];
685   NSScreen* screen = [parent_window screen];
686   if (!screen)
687     screen = [NSScreen mainScreen];
689   pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
690       initWithContentRect:[screen frame]
691                 styleMask:NSBorderlessWindowMask
692                   backing:NSBackingStoreBuffered
693                     defer:NO]);
694   [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
695   [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
696   [cocoa_view_ setCanBeKeyView:YES];
697   [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
698   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
699   // If the pepper fullscreen window isn't opaque then there are performance
700   // issues when it's on the discrete GPU and the Chrome window is being drawn
701   // to. http://crbug.com/171911
702   [pepper_fullscreen_window_ setOpaque:YES];
704   // Note that this forms a reference cycle between the fullscreen window and
705   // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
706   // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
707   // This cycle is normally broken when -keyEvent: receives an <esc> key, which
708   // explicitly calls Shutdown on the render_widget_host_, which calls
709   // Destroy() on RWHVMac, which drops the reference to
710   // pepper_fullscreen_window_.
711   [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
713   // Note that this keeps another reference to pepper_fullscreen_window_.
714   fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
715       initWithWindow:pepper_fullscreen_window_.get()
716        desiredScreen:screen]);
717   [fullscreen_window_manager_ enterFullscreenMode];
718   [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
721 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
722   // See comment in InitAsFullscreen(): There is a reference cycle between
723   // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
724   // Tests that test pepper fullscreen mode without sending an <esc> event
725   // need to call this method to break the reference cycle.
726   [fullscreen_window_manager_ exitFullscreenMode];
727   fullscreen_window_manager_.reset();
728   [pepper_fullscreen_window_ close];
729   pepper_fullscreen_window_.reset();
732 int RenderWidgetHostViewMac::window_number() const {
733   NSWindow* window = [cocoa_view_ window];
734   if (!window)
735     return -1;
736   return [window windowNumber];
739 float RenderWidgetHostViewMac::ViewScaleFactor() const {
740   return ScaleFactorForView(cocoa_view_);
743 void RenderWidgetHostViewMac::UpdateDisplayLink() {
744   static bool is_vsync_disabled =
745       CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
746   if (is_vsync_disabled)
747     return;
749   NSScreen* screen = [[cocoa_view_ window] screen];
750   NSDictionary* screen_description = [screen deviceDescription];
751   NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
752   CGDirectDisplayID display_id = [screen_number unsignedIntValue];
754   display_link_ = DisplayLinkMac::GetForDisplay(display_id);
755   if (!display_link_) {
756     // Note that on some headless systems, the display link will fail to be
757     // created, so this should not be a fatal error.
758     LOG(ERROR) << "Failed to create display link.";
759   }
762 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
763   if (!render_widget_host_ || !display_link_)
764     return;
766   base::TimeTicks timebase;
767   base::TimeDelta interval;
768   if (!display_link_->GetVSyncParameters(&timebase, &interval))
769     return;
771   render_widget_host_->UpdateVSyncParameters(timebase, interval);
774 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
775   if (!render_widget_host_)
776     return;
778   float new_scale_factor = ScaleFactorForView(cocoa_view_);
779   if (new_scale_factor == backing_store_scale_factor_)
780     return;
781   backing_store_scale_factor_ = new_scale_factor;
783   render_widget_host_->NotifyScreenInfoChanged();
786 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
787   return render_widget_host_;
790 void RenderWidgetHostViewMac::WasShown() {
791   if (!render_widget_host_->is_hidden())
792     return;
794   if (web_contents_switch_paint_time_.is_null())
795     web_contents_switch_paint_time_ = base::TimeTicks::Now();
796   render_widget_host_->WasShown();
797   software_frame_manager_->SetVisibility(true);
799   // Call setNeedsDisplay before pausing for new frames to come in -- if any
800   // do, and are drawn, then the needsDisplay bit will be cleared.
801   [software_layer_ setNeedsDisplay];
802   [compositing_iosurface_layer_ setNeedsDisplay];
803   PauseForPendingResizeOrRepaintsAndDraw();
805   // We're messing with the window, so do this to ensure no flashes.
806   if (!use_core_animation_)
807     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
810 void RenderWidgetHostViewMac::WasHidden() {
811   if (render_widget_host_->is_hidden())
812     return;
814   // Any pending frames will not be displayed until this is shown again. Ack
815   // them now.
816   SendPendingSwapAck();
818   // If we have a renderer, then inform it that we are being hidden so it can
819   // reduce its resource utilization.
820   render_widget_host_->WasHidden();
821   software_frame_manager_->SetVisibility(false);
823   // There can be a transparent flash as this view is removed and the next is
824   // added, because of OSX windowing races between displaying the contents of
825   // the NSView and its corresponding OpenGL context.
826   // disableScreenUpdatesUntilFlush prevents the transparent flash by avoiding
827   // screen updates until the next tab draws.
828   if (!use_core_animation_)
829     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
831   web_contents_switch_paint_time_ = base::TimeTicks();
834 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
835   gfx::Rect rect = GetViewBounds();
836   rect.set_size(size);
837   SetBounds(rect);
840 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
841   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
842   // TODO(thakis): fix, http://crbug.com/73362
843   if (render_widget_host_->is_hidden())
844     return;
846   // During the initial creation of the RenderWidgetHostView in
847   // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
848   // an empty size. In the Windows code flow, it is not ignored because
849   // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
850   // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
851   // flags to keep things sized properly. On the other hand, if the size is not
852   // empty then this is a valid request for a pop-up.
853   if (rect.size().IsEmpty())
854     return;
856   // Ignore the position of |rect| for non-popup rwhvs. This is because
857   // background tabs do not have a window, but the window is required for the
858   // coordinate conversions. Popups are always for a visible tab.
859   //
860   // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
861   // valid for resizing to be requested (e.g., during tab capture, to size the
862   // view to screen-capture resolution). In this case, simply treat the view as
863   // relative to the screen.
864   BOOL isRelativeToScreen = IsPopup() ||
865       ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
866   if (isRelativeToScreen) {
867     // The position of |rect| is screen coordinate system and we have to
868     // consider Cocoa coordinate system is upside-down and also multi-screen.
869     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
870     NSSize size = NSMakeSize(rect.width(), rect.height());
871     size = [cocoa_view_ convertSize:size toView:nil];
872     origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
873     NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
874                               size.width, size.height);
875     if (IsPopup())
876       [popup_window_ setFrame:frame display:YES];
877     else
878       [cocoa_view_ setFrame:frame];
879   } else {
880     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
881     gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
882     rect2.set_width(rect.width());
883     rect2.set_height(rect.height());
884     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
885   }
888 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
889   return cocoa_view_;
892 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
893   return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
896 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
897   NOTIMPLEMENTED();
898   return static_cast<gfx::NativeViewAccessible>(NULL);
901 void RenderWidgetHostViewMac::MovePluginWindows(
902     const std::vector<WebPluginGeometry>& moves) {
903   // Must be overridden, but unused on this platform. Core Animation
904   // plugins are drawn by the GPU process (through the compositor),
905   // and Core Graphics plugins are drawn by the renderer process.
906   DCHECK_CURRENTLY_ON(BrowserThread::UI);
909 void RenderWidgetHostViewMac::Focus() {
910   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
913 void RenderWidgetHostViewMac::Blur() {
914   UnlockMouse();
915   [[cocoa_view_ window] makeFirstResponder:nil];
918 bool RenderWidgetHostViewMac::HasFocus() const {
919   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
922 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
923   return software_frame_manager_->HasCurrentFrame() ||
924          (compositing_iosurface_ && compositing_iosurface_->HasIOSurface());
927 void RenderWidgetHostViewMac::Show() {
928   [cocoa_view_ setHidden:NO];
930   WasShown();
933 void RenderWidgetHostViewMac::Hide() {
934   // We're messing with the window, so do this to ensure no flashes.
935   if (!use_core_animation_)
936     [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
938   [cocoa_view_ setHidden:YES];
940   WasHidden();
943 bool RenderWidgetHostViewMac::IsShowing() {
944   return ![cocoa_view_ isHidden];
947 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
948   NSRect bounds = [cocoa_view_ bounds];
949   // TODO(shess): In case of !window, the view has been removed from
950   // the view hierarchy because the tab isn't main.  Could retrieve
951   // the information from the main tab for our window.
952   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
953   if (!enclosing_window)
954     return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
956   bounds = [cocoa_view_ convertRect:bounds toView:nil];
957   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
958   return FlipNSRectToRectScreen(bounds);
961 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
962   WebCursor web_cursor = cursor;
963   [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
966 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
967   is_loading_ = is_loading;
968   // If we ever decide to show the waiting cursor while the page is loading
969   // like Chrome does on Windows, call |UpdateCursor()| here.
972 void RenderWidgetHostViewMac::TextInputTypeChanged(
973     ui::TextInputType type,
974     ui::TextInputMode input_mode,
975     bool can_compose_inline) {
976   if (text_input_type_ != type
977       || can_compose_inline_ != can_compose_inline) {
978     text_input_type_ = type;
979     can_compose_inline_ = can_compose_inline;
980     if (HasFocus()) {
981       SetTextInputActive(true);
983       // Let AppKit cache the new input context to make IMEs happy.
984       // See http://crbug.com/73039.
985       [NSApp updateWindows];
987 #ifndef __LP64__
988       UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
989 #endif
990     }
991   }
994 void RenderWidgetHostViewMac::ImeCancelComposition() {
995   [cocoa_view_ cancelComposition];
998 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
999     const gfx::Range& range,
1000     const std::vector<gfx::Rect>& character_bounds) {
1001   // The RangeChanged message is only sent with valid values. The current
1002   // caret position (start == end) will be sent if there is no IME range.
1003   [cocoa_view_ setMarkedRange:range.ToNSRange()];
1004   composition_range_ = range;
1005   composition_bounds_ = character_bounds;
1008 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
1009                                                 int error_code) {
1010   Destroy();
1013 void RenderWidgetHostViewMac::Destroy() {
1014   [[NSNotificationCenter defaultCenter]
1015       removeObserver:cocoa_view_
1016                 name:NSWindowWillCloseNotification
1017               object:popup_window_];
1019   // We've been told to destroy.
1020   [cocoa_view_ retain];
1021   [cocoa_view_ removeFromSuperview];
1022   [cocoa_view_ autorelease];
1024   [popup_window_ close];
1025   popup_window_.autorelease();
1027   [fullscreen_window_manager_ exitFullscreenMode];
1028   fullscreen_window_manager_.reset();
1029   [pepper_fullscreen_window_ close];
1031   // This can be called as part of processing the window's responder
1032   // chain, for instance |-performKeyEquivalent:|.  In that case the
1033   // object needs to survive until the stack unwinds.
1034   pepper_fullscreen_window_.autorelease();
1036   // We get this call just before |render_widget_host_| deletes
1037   // itself.  But we are owned by |cocoa_view_|, which may be retained
1038   // by some other code.  Examples are WebContentsViewMac's
1039   // |latent_focus_view_| and TabWindowController's
1040   // |cachedContentView_|.
1041   render_widget_host_ = NULL;
1044 // Called from the renderer to tell us what the tooltip text should be. It
1045 // calls us frequently so we need to cache the value to prevent doing a lot
1046 // of repeat work.
1047 void RenderWidgetHostViewMac::SetTooltipText(
1048     const base::string16& tooltip_text) {
1049   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1050     tooltip_text_ = tooltip_text;
1052     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1053     // Windows; we're just trying to be polite. Don't persist the trimmed
1054     // string, as then the comparison above will always fail and we'll try to
1055     // set it again every single time the mouse moves.
1056     base::string16 display_text = tooltip_text_;
1057     if (tooltip_text_.length() > kMaxTooltipLength)
1058       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1060     NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1061     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1062   }
1065 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1066   return [NSApp respondsToSelector:@selector(speakString:)] &&
1067          [NSApp respondsToSelector:@selector(stopSpeaking:)];
1070 void RenderWidgetHostViewMac::SpeakSelection() {
1071   if ([NSApp respondsToSelector:@selector(speakString:)])
1072     [NSApp speakString:base::SysUTF8ToNSString(selected_text_)];
1075 bool RenderWidgetHostViewMac::IsSpeaking() const {
1076   return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1077          [NSApp isSpeaking];
1080 void RenderWidgetHostViewMac::StopSpeaking() {
1081   if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1082     [NSApp stopSpeaking:cocoa_view_];
1086 // RenderWidgetHostViewCocoa uses the stored selection text,
1087 // which implements NSServicesRequests protocol.
1089 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1090                                                size_t offset,
1091                                                const gfx::Range& range) {
1092   if (range.is_empty() || text.empty()) {
1093     selected_text_.clear();
1094   } else {
1095     size_t pos = range.GetMin() - offset;
1096     size_t n = range.length();
1098     DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1099     if (pos >= text.length()) {
1100       DCHECK(false) << "The text can not cover range.";
1101       return;
1102     }
1103     selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1104   }
1106   [cocoa_view_ setSelectedRange:range.ToNSRange()];
1107   // Updates markedRange when there is no marked text so that retrieving
1108   // markedRange immediately after calling setMarkdText: returns the current
1109   // caret position.
1110   if (![cocoa_view_ hasMarkedText]) {
1111     [cocoa_view_ setMarkedRange:range.ToNSRange()];
1112   }
1114   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1117 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1118     const ViewHostMsg_SelectionBounds_Params& params) {
1119   if (params.anchor_rect == params.focus_rect)
1120     caret_rect_ = params.anchor_rect;
1123 void RenderWidgetHostViewMac::ScrollOffsetChanged() {
1126 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1127   RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1129   // Create a fake mouse event to inform the render widget that the mouse
1130   // left or entered.
1131   NSWindow* window = [cocoa_view_ window];
1132   // TODO(asvitkine): If the location outside of the event stream doesn't
1133   // correspond to the current event (due to delayed event processing), then
1134   // this may result in a cursor flicker if there are later mouse move events
1135   // in the pipeline. Find a way to use the mouse location from the event that
1136   // dismissed the context menu.
1137   NSPoint location = [window mouseLocationOutsideOfEventStream];
1138   NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1139                                       location:location
1140                                  modifierFlags:0
1141                                      timestamp:0
1142                                   windowNumber:window_number()
1143                                        context:nil
1144                                    eventNumber:0
1145                                     clickCount:0
1146                                       pressure:0];
1147   WebMouseEvent web_event =
1148       WebInputEventFactory::mouseEvent(event, cocoa_view_);
1149   if (showing)
1150     web_event.type = WebInputEvent::MouseLeave;
1151   ForwardMouseEvent(web_event);
1154 bool RenderWidgetHostViewMac::IsPopup() const {
1155   return popup_type_ != blink::WebPopupTypeNone;
1158 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1159     const gfx::Rect& src_subrect,
1160     const gfx::Size& dst_size,
1161     const base::Callback<void(bool, const SkBitmap&)>& callback,
1162     const SkBitmap::Config config) {
1163   if (config != SkBitmap::kARGB_8888_Config) {
1164     NOTIMPLEMENTED();
1165     callback.Run(false, SkBitmap());
1166   }
1167   base::ScopedClosureRunner scoped_callback_runner(
1168       base::Bind(callback, false, SkBitmap()));
1169   float scale = ScaleFactorForView(cocoa_view_);
1170   gfx::Size dst_pixel_size = gfx::ToFlooredSize(
1171       gfx::ScaleSize(dst_size, scale));
1172   if (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) {
1173     ignore_result(scoped_callback_runner.Release());
1174     compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect),
1175                                    dst_pixel_size,
1176                                    callback);
1177   } else if (software_frame_manager_->HasCurrentFrame()) {
1178     gfx::Rect src_pixel_rect = gfx::ToEnclosingRect(gfx::ScaleRect(
1179         src_subrect,
1180         software_frame_manager_->GetCurrentFrameDeviceScaleFactor()));
1181     SkBitmap source_bitmap;
1182     source_bitmap.setConfig(
1183         SkBitmap::kARGB_8888_Config,
1184         software_frame_manager_->GetCurrentFrameSizeInPixels().width(),
1185         software_frame_manager_->GetCurrentFrameSizeInPixels().height(),
1186         0,
1187         kOpaque_SkAlphaType);
1188     source_bitmap.setPixels(software_frame_manager_->GetCurrentFramePixels());
1190     SkBitmap target_bitmap;
1191     target_bitmap.setConfig(
1192         SkBitmap::kARGB_8888_Config,
1193         dst_pixel_size.width(),
1194         dst_pixel_size.height(),
1195         0,
1196         kOpaque_SkAlphaType);
1197     if (!target_bitmap.allocPixels())
1198       return;
1200     SkCanvas target_canvas(target_bitmap);
1201     SkRect src_pixel_skrect = SkRect::MakeXYWH(
1202         src_pixel_rect.x(), src_pixel_rect.y(),
1203         src_pixel_rect.width(), src_pixel_rect.height());
1204     target_canvas.drawBitmapRectToRect(
1205         source_bitmap,
1206         &src_pixel_skrect,
1207         SkRect::MakeXYWH(0, 0, dst_pixel_size.width(), dst_pixel_size.height()),
1208         NULL,
1209         SkCanvas::kNone_DrawBitmapRectFlag);
1211     ignore_result(scoped_callback_runner.Release());
1212     callback.Run(true, target_bitmap);
1213   }
1216 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1217       const gfx::Rect& src_subrect,
1218       const scoped_refptr<media::VideoFrame>& target,
1219       const base::Callback<void(bool)>& callback) {
1220   base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
1221   if (!render_widget_host_->is_accelerated_compositing_active() ||
1222       !compositing_iosurface_ ||
1223       !compositing_iosurface_->HasIOSurface())
1224     return;
1226   if (!target.get()) {
1227     NOTREACHED();
1228     return;
1229   }
1231   if (target->format() != media::VideoFrame::YV12 &&
1232       target->format() != media::VideoFrame::I420) {
1233     NOTREACHED();
1234     return;
1235   }
1237   if (src_subrect.IsEmpty())
1238     return;
1240   ignore_result(scoped_callback_runner.Release());
1241   compositing_iosurface_->CopyToVideoFrame(
1242       GetScaledOpenGLPixelRect(src_subrect),
1243       target,
1244       callback);
1247 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1248   return (!software_frame_manager_->HasCurrentFrame() &&
1249           render_widget_host_->is_accelerated_compositing_active() &&
1250           compositing_iosurface_ &&
1251           compositing_iosurface_->HasIOSurface());
1254 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1255   return !software_frame_manager_->HasCurrentFrame();
1258 void RenderWidgetHostViewMac::BeginFrameSubscription(
1259     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1260   frame_subscriber_ = subscriber.Pass();
1263 void RenderWidgetHostViewMac::EndFrameSubscription() {
1264   frame_subscriber_.reset();
1267 // Sets whether or not to accept first responder status.
1268 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
1269   [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
1272 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1273   if (render_widget_host_)
1274     render_widget_host_->ForwardMouseEvent(event);
1276   if (event.type == WebInputEvent::MouseLeave) {
1277     [cocoa_view_ setToolTipAtMousePoint:nil];
1278     tooltip_text_.clear();
1279   }
1282 void RenderWidgetHostViewMac::KillSelf() {
1283   if (!weak_factory_.HasWeakPtrs()) {
1284     [cocoa_view_ setHidden:YES];
1285     base::MessageLoop::current()->PostTask(FROM_HERE,
1286         base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1287                    weak_factory_.GetWeakPtr()));
1288   }
1291 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1292     const NativeWebKeyboardEvent& event) {
1293   // Check WebInputEvent type since multiple types of events can be sent into
1294   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1295   // necessary to avoid double processing.
1296   // Also check the native type, since NSFlagsChanged is considered a key event
1297   // for WebKit purposes, but isn't considered a key event by the OS.
1298   if (event.type == WebInputEvent::RawKeyDown &&
1299       [event.os_event type] == NSKeyDown)
1300     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1301   return false;
1304 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1305     const base::string16& text, int plugin_id) {
1306   if (render_widget_host_) {
1307     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1308         render_widget_host_->GetRoutingID(), text, plugin_id));
1309   }
1312 void RenderWidgetHostViewMac::CompositorSwapBuffers(
1313     uint64 surface_handle,
1314     const gfx::Size& size,
1315     float surface_scale_factor,
1316     const std::vector<ui::LatencyInfo>& latency_info) {
1317   // Ensure that the frame be acked unless it is explicitly passed to a
1318   // display function.
1319   base::ScopedClosureRunner scoped_ack(
1320       base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
1321                  weak_factory_.GetWeakPtr()));
1323   if (render_widget_host_->is_hidden())
1324     return;
1326   // Ensure that if this function exits before the frame is set up (but not
1327   // necessarily drawn) then it is treated as an error.
1328   base::ScopedClosureRunner scoped_error(
1329       base::Bind(&RenderWidgetHostViewMac::GotAcceleratedCompositingError,
1330                  weak_factory_.GetWeakPtr()));
1332   AddPendingLatencyInfo(latency_info);
1334   // If compositing_iosurface_ exists and has been poisoned, destroy it
1335   // and allow EnsureCompositedIOSurface to recreate it below. Keep a
1336   // reference to the destroyed layer around until after the below call
1337   // to LayoutLayers, to avoid flickers.
1338   base::ScopedClosureRunner scoped_layer_remover;
1339   if (compositing_iosurface_context_ &&
1340       compositing_iosurface_context_->HasBeenPoisoned()) {
1341     scoped_layer_remover.Reset(
1342         base::Bind(RemoveLayerFromSuperlayer, compositing_iosurface_layer_));
1343     DestroyCompositedIOSurfaceLayer(kLeaveLayerInHierarchy);
1344     DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1345   }
1347   // Ensure compositing_iosurface_ and compositing_iosurface_context_ be
1348   // allocated.
1349   if (!EnsureCompositedIOSurface()) {
1350     LOG(ERROR) << "Failed EnsureCompositingIOSurface";
1351     return;
1352   }
1354   // Make the context current and update the IOSurface with the handle
1355   // passed in by the swap command.
1356   {
1357     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
1358         compositing_iosurface_context_->cgl_context());
1359     if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent(
1360             compositing_iosurface_context_, surface_handle, size,
1361             surface_scale_factor)) {
1362       LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
1363       return;
1364     }
1365   }
1367   // Grab video frames now that the IOSurface has been set up. Note that this
1368   // will be done in an offscreen context, so it is necessary to re-set the
1369   // current context afterward.
1370   bool frame_was_captured = false;
1371   if (frame_subscriber_) {
1372     const base::TimeTicks present_time = base::TimeTicks::Now();
1373     scoped_refptr<media::VideoFrame> frame;
1374     RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
1375     if (frame_subscriber_->ShouldCaptureFrame(present_time,
1376                                               &frame, &callback)) {
1377       // Flush the context that updated the IOSurface, to ensure that the
1378       // context that does the copy picks up the correct version.
1379       {
1380         gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
1381             compositing_iosurface_context_->cgl_context());
1382         glFlush();
1383       }
1384       compositing_iosurface_->CopyToVideoFrame(
1385           gfx::Rect(size), frame,
1386           base::Bind(callback, present_time));
1387       frame_was_captured = true;
1388     }
1389   }
1391   // At this point the surface, its context, and its layer have been set up, so
1392   // don't generate an error (one may be generated when drawing).
1393   ignore_result(scoped_error.Release());
1395   GotAcceleratedFrame();
1397   gfx::Size window_size(NSSizeToCGSize([cocoa_view_ frame].size));
1398   if (window_size.IsEmpty()) {
1399     // setNeedsDisplay will never display and we'll never ack if the window is
1400     // empty, so ack now and don't bother calling setNeedsDisplay below.
1401     return;
1402   }
1403   if (window_number() <= 0) {
1404     // It's normal for a backgrounded tab that is being captured to have no
1405     // window but not be hidden. Immediately ack the frame, and don't try to
1406     // draw it.
1407     if (frame_was_captured)
1408       return;
1410     // If this frame was not captured, there is likely some sort of bug. Ack
1411     // the frame and hope for the best. Because the IOSurface and layer are
1412     // populated, it will likely be displayed when the view is added to a
1413     // window's hierarchy.
1415     // TODO(shess) If the view does not have a window, or the window
1416     // does not have backing, the IOSurface will log "invalid drawable"
1417     // in -setView:.  It is not clear how this code is reached with such
1418     // a case, so record some info into breakpad (some subset of
1419     // browsers are likely to crash later for unrelated reasons).
1420     // http://crbug.com/148882
1421     const char* const kCrashKey = "rwhvm_window";
1422     NSWindow* window = [cocoa_view_ window];
1423     if (!window) {
1424       base::debug::SetCrashKeyValue(kCrashKey, "Missing window");
1425     } else {
1426       std::string value =
1427           base::StringPrintf("window %s delegate %s controller %s",
1428               object_getClassName(window),
1429               object_getClassName([window delegate]),
1430               object_getClassName([window windowController]));
1431       base::debug::SetCrashKeyValue(kCrashKey, value);
1432     }
1433     return;
1434   }
1436   // If we reach here, then the frame will be displayed by a future draw
1437   // call, so don't make the callback.
1438   ignore_result(scoped_ack.Release());
1439   if (use_core_animation_) {
1440     DCHECK(compositing_iosurface_layer_);
1441     compositing_iosurface_layer_async_timer_.Reset();
1442     [compositing_iosurface_layer_ gotNewFrame];
1443   } else {
1444     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
1445         compositing_iosurface_context_->cgl_context());
1446     DrawIOSurfaceWithoutCoreAnimation();
1447   }
1449   // Try to finish previous copy requests after draw to get better pipelining.
1450   if (compositing_iosurface_)
1451     compositing_iosurface_->CheckIfAllCopiesAreFinished(false);
1453   // The IOSurface's size may have changed, so re-layout the layers to take
1454   // this into account. This may force an immediate draw.
1455   LayoutLayers();
1458 void RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
1459   CHECK(!use_core_animation_);
1460   CHECK(compositing_iosurface_);
1462   // If there is a pending frame, it should be acked by the end of this
1463   // function. Note that the ack should happen only after all drawing is
1464   // complete, so that the ack happens after any blocking due to vsync.
1465   base::ScopedClosureRunner scoped_ack(
1466       base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
1467                  weak_factory_.GetWeakPtr()));
1469   GLint old_gl_surface_order = 0;
1470   GLint new_gl_surface_order = allow_overlapping_views_ ? -1 : 1;
1471   [compositing_iosurface_context_->nsgl_context()
1472       getValues:&old_gl_surface_order
1473       forParameter:NSOpenGLCPSurfaceOrder];
1474   if (old_gl_surface_order != new_gl_surface_order) {
1475     [compositing_iosurface_context_->nsgl_context()
1476         setValues:&new_gl_surface_order
1477         forParameter:NSOpenGLCPSurfaceOrder];
1478   }
1480   // Instead of drawing, request that underlay view redraws.
1481   if (underlay_view_ &&
1482       underlay_view_->compositing_iosurface_ &&
1483       underlay_view_has_drawn_) {
1484     [underlay_view_->cocoa_view() setNeedsDisplayInRect:NSMakeRect(0, 0, 1, 1)];
1485     return;
1486   }
1488   bool has_overlay = overlay_view_ && overlay_view_->compositing_iosurface_;
1489   if (has_overlay) {
1490     // Un-bind the overlay view's OpenGL context, since its content will be
1491     // drawn by this context. Not doing this can result in corruption.
1492     // http://crbug.com/330701
1493     overlay_view_->ClearBoundContextDrawable();
1494   }
1495   [compositing_iosurface_context_->nsgl_context() setView:cocoa_view_];
1497   gfx::Rect view_rect(NSRectToCGRect([cocoa_view_ frame]));
1498   if (!compositing_iosurface_->DrawIOSurface(
1499           compositing_iosurface_context_, view_rect,
1500           ViewScaleFactor(), !has_overlay)) {
1501     GotAcceleratedCompositingError();
1502     return;
1503   }
1505   if (has_overlay) {
1506     overlay_view_->underlay_view_has_drawn_ = true;
1507     gfx::Rect overlay_view_rect(
1508         NSRectToCGRect([overlay_view_->cocoa_view() frame]));
1509     overlay_view_rect.set_x(overlay_view_offset_.x());
1510     overlay_view_rect.set_y(view_rect.height() -
1511                             overlay_view_rect.height() -
1512                             overlay_view_offset_.y());
1513     if (!overlay_view_->compositing_iosurface_->DrawIOSurface(
1514             compositing_iosurface_context_, overlay_view_rect,
1515             overlay_view_->ViewScaleFactor(), true)) {
1516       GotAcceleratedCompositingError();
1517       return;
1518     }
1519   }
1521   SendPendingLatencyInfoToHost();
1524 void RenderWidgetHostViewMac::GotAcceleratedCompositingError() {
1525   LOG(ERROR) << "Encountered accelerated compositing error";
1526   base::MessageLoop::current()->PostTask(
1527       FROM_HERE,
1528       base::Bind(&RenderWidgetHostViewMac::DestroyCompositingStateOnError,
1529                  weak_factory_.GetWeakPtr()));
1532 void RenderWidgetHostViewMac::DestroyCompositingStateOnError() {
1533   // This should be called with a clean stack. Make sure that no context is
1534   // current.
1535   DCHECK(!CGLGetCurrentContext());
1537   // The existing GL contexts may be in a bad state, so don't re-use any of the
1538   // existing ones anymore, rather, allocate new ones.
1539   if (compositing_iosurface_context_)
1540     compositing_iosurface_context_->PoisonContextAndSharegroup();
1542   DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1544   // Request that a new frame be generated and dirty the view.
1545   if (render_widget_host_)
1546     render_widget_host_->ScheduleComposite();
1547   [cocoa_view_ setNeedsDisplay:YES];
1549   // Mark the last frame as not accelerated (so that the window is prepared for
1550   // an underlay next time an accelerated frame comes in).
1551   last_frame_was_accelerated_ = false;
1553   // TODO(ccameron): It may be a good idea to request that the renderer recreate
1554   // its GL context as well, and fall back to software if this happens
1555   // repeatedly.
1558 void RenderWidgetHostViewMac::SetOverlayView(
1559     RenderWidgetHostViewMac* overlay, const gfx::Point& offset) {
1560   if (overlay_view_)
1561     overlay_view_->underlay_view_.reset();
1563   overlay_view_ = overlay->overlay_view_weak_factory_.GetWeakPtr();
1564   overlay_view_->underlay_view_ = overlay_view_weak_factory_.GetWeakPtr();
1565   if (use_core_animation_)
1566     return;
1568   overlay_view_offset_ = offset;
1569   overlay_view_->underlay_view_has_drawn_ = false;
1571   [cocoa_view_ setNeedsDisplay:YES];
1572   [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
1575 void RenderWidgetHostViewMac::RemoveOverlayView() {
1576   if (overlay_view_) {
1577     overlay_view_->underlay_view_.reset();
1578     overlay_view_.reset();
1579   }
1580   if (use_core_animation_)
1581     return;
1583   [cocoa_view_ setNeedsDisplay:YES];
1584   [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
1587 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1588     const std::vector<gfx::Rect>& bounds,
1589     const gfx::Range& range,
1590     size_t* line_break_point) {
1591   DCHECK(line_break_point);
1592   if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1593     return false;
1595   // We can't check line breaking completely from only rectangle array. Thus we
1596   // assume the line breaking as the next character's y offset is larger than
1597   // a threshold. Currently the threshold is determined as minimum y offset plus
1598   // 75% of maximum height.
1599   // TODO(nona): Check the threshold is reliable or not.
1600   // TODO(nona): Bidi support.
1601   const size_t loop_end_idx = std::min(bounds.size(), range.end());
1602   int max_height = 0;
1603   int min_y_offset = kint32max;
1604   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1605     max_height = std::max(max_height, bounds[idx].height());
1606     min_y_offset = std::min(min_y_offset, bounds[idx].y());
1607   }
1608   int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1609   for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1610     if (bounds[idx].y() > line_break_threshold) {
1611       *line_break_point = idx;
1612       return true;
1613     }
1614   }
1615   return false;
1618 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1619     const gfx::Range& range,
1620     gfx::Range* actual_range) {
1621   DCHECK(actual_range);
1622   DCHECK(!composition_bounds_.empty());
1623   DCHECK(range.start() <= composition_bounds_.size());
1624   DCHECK(range.end() <= composition_bounds_.size());
1626   if (range.is_empty()) {
1627     *actual_range = range;
1628     if (range.start() == composition_bounds_.size()) {
1629       return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1630                        composition_bounds_[range.start() - 1].y(),
1631                        0,
1632                        composition_bounds_[range.start() - 1].height());
1633     } else {
1634       return gfx::Rect(composition_bounds_[range.start()].x(),
1635                        composition_bounds_[range.start()].y(),
1636                        0,
1637                        composition_bounds_[range.start()].height());
1638     }
1639   }
1641   size_t end_idx;
1642   if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1643     end_idx = range.end();
1644   }
1645   *actual_range = gfx::Range(range.start(), end_idx);
1646   gfx::Rect rect = composition_bounds_[range.start()];
1647   for (size_t i = range.start() + 1; i < end_idx; ++i) {
1648     rect.Union(composition_bounds_[i]);
1649   }
1650   return rect;
1653 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1654     const gfx::Range& request_range) {
1655   if (composition_range_.is_empty())
1656     return gfx::Range::InvalidRange();
1658   if (request_range.is_reversed())
1659     return gfx::Range::InvalidRange();
1661   if (request_range.start() < composition_range_.start() ||
1662       request_range.start() > composition_range_.end() ||
1663       request_range.end() > composition_range_.end()) {
1664     return gfx::Range::InvalidRange();
1665   }
1667   return gfx::Range(
1668       request_range.start() - composition_range_.start(),
1669       request_range.end() - composition_range_.start());
1672 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1673   if (!render_widget_host_->IsRenderView())
1674     return NULL;
1676   return WebContents::FromRenderViewHost(
1677       RenderViewHost::From(render_widget_host_));
1680 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1681     NSRange range,
1682     NSRect* rect,
1683     NSRange* actual_range) {
1684   DCHECK(rect);
1685   // This exists to make IMEs more responsive, see http://crbug.com/115920
1686   TRACE_EVENT0("browser",
1687                "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1689   // If requested range is same as caret location, we can just return it.
1690   if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1691     if (actual_range)
1692       *actual_range = range;
1693     *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1694     return true;
1695   }
1697   const gfx::Range request_range_in_composition =
1698       ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1699   if (request_range_in_composition == gfx::Range::InvalidRange())
1700     return false;
1702   // If firstRectForCharacterRange in WebFrame is failed in renderer,
1703   // ImeCompositionRangeChanged will be sent with empty vector.
1704   if (composition_bounds_.empty())
1705     return false;
1706   DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1708   gfx::Range ui_actual_range;
1709   *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1710                                request_range_in_composition,
1711                                &ui_actual_range).ToCGRect());
1712   if (actual_range) {
1713     *actual_range = gfx::Range(
1714         composition_range_.start() + ui_actual_range.start(),
1715         composition_range_.start() + ui_actual_range.end()).ToNSRange();
1716   }
1717   return true;
1720 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
1721     const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1722     int gpu_host_id) {
1723   TRACE_EVENT0("browser",
1724       "RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped");
1725   DCHECK_CURRENTLY_ON(BrowserThread::UI);
1727   AddPendingSwapAck(params.route_id,
1728                     gpu_host_id,
1729                     compositing_iosurface_ ?
1730                         compositing_iosurface_->GetRendererID() : 0);
1731   CompositorSwapBuffers(params.surface_handle,
1732                         params.size,
1733                         params.scale_factor,
1734                         params.latency_info);
1737 void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
1738     const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1739     int gpu_host_id) {
1740   TRACE_EVENT0("browser",
1741       "RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer");
1742   DCHECK_CURRENTLY_ON(BrowserThread::UI);
1744   AddPendingSwapAck(params.route_id,
1745                     gpu_host_id,
1746                     compositing_iosurface_ ?
1747                         compositing_iosurface_->GetRendererID() : 0);
1748   CompositorSwapBuffers(params.surface_handle,
1749                         params.surface_size,
1750                         params.surface_scale_factor,
1751                         params.latency_info);
1754 void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
1755   if (compositing_iosurface_)
1756     compositing_iosurface_->UnrefIOSurface();
1759 void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
1760   DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1763 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1764       const gfx::Size& desired_size) {
1765   if (last_frame_was_accelerated_) {
1766     return compositing_iosurface_ &&
1767            compositing_iosurface_->HasIOSurface() &&
1768            (desired_size.IsEmpty() ||
1769                compositing_iosurface_->dip_io_surface_size() == desired_size);
1770   } else {
1771     return (software_frame_manager_->HasCurrentFrame() &&
1772            (desired_size.IsEmpty() ||
1773                software_frame_manager_->GetCurrentFrameSizeInDIP() ==
1774                    desired_size));
1775   }
1776   return false;
1779 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1780     uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1781   // Only software compositor frames are accepted.
1782   if (!frame->software_frame_data) {
1783     DLOG(ERROR) << "Received unexpected frame type.";
1784     RecordAction(
1785         base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1786     render_widget_host_->GetProcess()->ReceivedBadMessage();
1787     return;
1788   }
1790   if (!software_frame_manager_->SwapToNewFrame(
1791           output_surface_id,
1792           frame->software_frame_data.get(),
1793           frame->metadata.device_scale_factor,
1794           render_widget_host_->GetProcess()->GetHandle())) {
1795     render_widget_host_->GetProcess()->ReceivedBadMessage();
1796     return;
1797   }
1799   // Add latency info to report when the frame finishes drawing.
1800   AddPendingLatencyInfo(frame->metadata.latency_info);
1801   GotSoftwareFrame();
1803   cc::CompositorFrameAck ack;
1804   RenderWidgetHostImpl::SendSwapCompositorFrameAck(
1805       render_widget_host_->GetRoutingID(),
1806       software_frame_manager_->GetCurrentFrameOutputSurfaceId(),
1807       render_widget_host_->GetProcess()->GetID(),
1808       ack);
1809   software_frame_manager_->SwapToNewFrameComplete(
1810       !render_widget_host_->is_hidden());
1812   // Notify observers, tab capture observers in particular, that a new software
1813   // frame has come in.
1814   NotificationService::current()->Notify(
1815       NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
1816       Source<RenderWidgetHost>(render_widget_host_),
1817       NotificationService::NoDetails());
1820 void RenderWidgetHostViewMac::OnAcceleratedCompositingStateChange() {
1823 void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
1824                                                             int route_id) {
1827 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1828   *results = GetWebScreenInfo(GetNativeView());
1831 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1832   // TODO(shess): In case of !window, the view has been removed from
1833   // the view hierarchy because the tab isn't main.  Could retrieve
1834   // the information from the main tab for our window.
1835   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1836   if (!enclosing_window)
1837     return gfx::Rect();
1839   NSRect bounds = [enclosing_window frame];
1840   return FlipNSRectToRectScreen(bounds);
1843 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1844   // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1845   // completely on Mac OS.
1846   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
1849 void RenderWidgetHostViewMac::SetScrollOffsetPinning(
1850     bool is_pinned_to_left, bool is_pinned_to_right) {
1851   [cocoa_view_ scrollOffsetPinnedToLeft:is_pinned_to_left
1852                                 toRight:is_pinned_to_right];
1855 bool RenderWidgetHostViewMac::LockMouse() {
1856   if (mouse_locked_)
1857     return true;
1859   mouse_locked_ = true;
1861   // Lock position of mouse cursor and hide it.
1862   CGAssociateMouseAndMouseCursorPosition(NO);
1863   [NSCursor hide];
1865   // Clear the tooltip window.
1866   SetTooltipText(base::string16());
1868   return true;
1871 void RenderWidgetHostViewMac::UnlockMouse() {
1872   if (!mouse_locked_)
1873     return;
1874   mouse_locked_ = false;
1876   // Unlock position of mouse cursor and unhide it.
1877   CGAssociateMouseAndMouseCursorPosition(YES);
1878   [NSCursor unhide];
1880   if (render_widget_host_)
1881     render_widget_host_->LostMouseLock();
1884 void RenderWidgetHostViewMac::UnhandledWheelEvent(
1885     const blink::WebMouseWheelEvent& event) {
1886   // Only record a wheel event as unhandled if JavaScript handlers got a chance
1887   // to see it (no-op wheel events are ignored by the event dispatcher)
1888   if (event.deltaX || event.deltaY)
1889     [cocoa_view_ gotUnhandledWheelEvent];
1892 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1893   if (render_widget_host_)
1894     return render_widget_host_->Send(message);
1895   delete message;
1896   return false;
1899 void RenderWidgetHostViewMac::SoftwareFrameWasFreed(
1900     uint32 output_surface_id, unsigned frame_id) {
1901   if (!render_widget_host_)
1902     return;
1903   cc::CompositorFrameAck ack;
1904   ack.last_software_frame_id = frame_id;
1905   RenderWidgetHostImpl::SendReclaimCompositorResources(
1906       render_widget_host_->GetRoutingID(),
1907       output_surface_id,
1908       render_widget_host_->GetProcess()->GetID(),
1909       ack);
1912 void RenderWidgetHostViewMac::ReleaseReferencesToSoftwareFrame() {
1913   DestroySoftwareLayer();
1916 void RenderWidgetHostViewMac::ShutdownHost() {
1917   weak_factory_.InvalidateWeakPtrs();
1918   render_widget_host_->Shutdown();
1919   // Do not touch any members at this point, |this| has been deleted.
1922 void RenderWidgetHostViewMac::GotAcceleratedFrame() {
1923   EnsureCompositedIOSurfaceLayer();
1924   SendVSyncParametersToRenderer();
1925   if (!last_frame_was_accelerated_) {
1926     last_frame_was_accelerated_ = true;
1928     if (!use_core_animation_) {
1929       // Need to wipe the software view with transparency to expose the GL
1930       // underlay. Invalidate the whole window to do that.
1931       [cocoa_view_ setNeedsDisplay:YES];
1932     }
1934     // Delete software backingstore and layer.
1935     software_frame_manager_->DiscardCurrentFrame();
1936     DestroySoftwareLayer();
1937   }
1940 void RenderWidgetHostViewMac::GotSoftwareFrame() {
1941   if (!render_widget_host_)
1942     return;
1944   EnsureSoftwareLayer();
1945   LayoutLayers();
1946   SendVSyncParametersToRenderer();
1948   // Draw the contents of the frame immediately. It is critical that this
1949   // happen before the frame be acked, otherwise the new frame will likely be
1950   // ready before the drawing is complete, thrashing the browser main thread.
1951   if (use_core_animation_) {
1952     [software_layer_ setNeedsDisplay];
1953     [software_layer_ displayIfNeeded];
1954   } else {
1955     [cocoa_view_ setNeedsDisplay:YES];
1956     [cocoa_view_ displayIfNeeded];
1957   }
1959   if (last_frame_was_accelerated_) {
1960     last_frame_was_accelerated_ = false;
1962     // If overlapping views are allowed, then don't unbind the context
1963     // from the view (that is, don't call clearDrawble -- just delete the
1964     // texture and IOSurface). Rather, let it sit behind the software frame
1965     // that will be put up in front. This will prevent transparent
1966     // flashes.
1967     // http://crbug.com/154531
1968     // Also note that it is necessary that clearDrawable be called if
1969     // overlapping views are not allowed, e.g, for content shell.
1970     // http://crbug.com/178408
1971     // Disable screen updates so that the changes of flashes is minimized.
1972     // http://crbug.com/279472
1973     if (!use_core_animation_)
1974       [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
1975     if (allow_overlapping_views_)
1976       DestroyCompositedIOSurfaceAndLayer(kLeaveContextBoundToView);
1977     else
1978       DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
1979   }
1982 void RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired() {
1983   [compositing_iosurface_layer_ timerSinceGotNewFrameFired];
1986 void RenderWidgetHostViewMac::SetActive(bool active) {
1987   if (render_widget_host_) {
1988     render_widget_host_->SetActive(active);
1989     if (active) {
1990       if (HasFocus())
1991         render_widget_host_->Focus();
1992     } else {
1993       render_widget_host_->Blur();
1994     }
1995   }
1996   if (HasFocus())
1997     SetTextInputActive(active);
1998   if (!active) {
1999     [cocoa_view_ setPluginImeActive:NO];
2000     UnlockMouse();
2001   }
2004 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
2005   if (render_widget_host_) {
2006     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
2007         render_widget_host_->GetRoutingID(), visible));
2008   }
2011 void RenderWidgetHostViewMac::WindowFrameChanged() {
2012   if (render_widget_host_) {
2013     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
2014         render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
2015         GetViewBounds()));
2016   }
2018   if (compositing_iosurface_ && !use_core_animation_) {
2019     // This will migrate the context to the appropriate window.
2020     if (!EnsureCompositedIOSurface())
2021       GotAcceleratedCompositingError();
2022   }
2025 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
2026   RenderWidgetHostViewMacDictionaryHelper helper(this);
2027   helper.ShowDefinitionForSelection();
2030 void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) {
2031   RenderWidgetHostViewBase::SetBackground(background);
2032   if (render_widget_host_)
2033     render_widget_host_->Send(new ViewMsg_SetBackground(
2034         render_widget_host_->GetRoutingID(), background));
2037 void RenderWidgetHostViewMac::CreateBrowserAccessibilityManagerIfNeeded() {
2038   if (!GetBrowserAccessibilityManager()) {
2039     SetBrowserAccessibilityManager(
2040         new BrowserAccessibilityManagerMac(
2041             cocoa_view_,
2042             BrowserAccessibilityManagerMac::GetEmptyDocument(),
2043             render_widget_host_));
2044   }
2047 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
2048     const gfx::Rect& bounds) {
2049   NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
2050   NSSize size = NSMakeSize(bounds.width(), bounds.height());
2051   origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
2052   NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
2053   NSPoint originInScreen =
2054       [[cocoa_view_ window] convertBaseToScreen:originInWindow];
2055   originInScreen.y = originInScreen.y - size.height;
2056   return gfx::Point(originInScreen.x, originInScreen.y);
2059 void RenderWidgetHostViewMac::OnAccessibilitySetFocus(int accObjId) {
2060   // Immediately set the focused item even though we have not officially set
2061   // focus on it as VoiceOver expects to get the focused item after this
2062   // method returns.
2063   BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
2064   if (manager)
2065     manager->SetFocus(manager->GetFromID(accObjId), false);
2068 void RenderWidgetHostViewMac::AccessibilityShowMenu(int accObjId) {
2069   BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
2070   if (!manager)
2071     return;
2072   BrowserAccessibilityCocoa* obj =
2073       manager->GetFromID(accObjId)->ToBrowserAccessibilityCocoa();
2075   // Performs a right click copying WebKit's
2076   // accessibilityPerformShowMenuAction.
2077   NSPoint objOrigin = [obj origin];
2078   NSSize size = [[obj size] sizeValue];
2079   gfx::Point origin = AccessibilityOriginInScreen(
2080       gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
2081   NSPoint location = NSMakePoint(origin.x(), origin.y());
2082   location = [[cocoa_view_ window] convertScreenToBase:location];
2083   location.x += size.width/2;
2084   location.y += size.height/2;
2086   NSEvent* fakeRightClick = [NSEvent
2087                           mouseEventWithType:NSRightMouseDown
2088                                     location:location
2089                                modifierFlags:0
2090                                    timestamp:0
2091                                 windowNumber:[[cocoa_view_ window] windowNumber]
2092                                      context:[NSGraphicsContext currentContext]
2093                                  eventNumber:0
2094                                   clickCount:1
2095                                     pressure:0];
2097   [cocoa_view_ mouseEvent:fakeRightClick];
2102 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
2103   if (active) {
2104     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
2105       EnablePasswordInput();
2106     else
2107       DisablePasswordInput();
2108   } else {
2109     if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
2110       DisablePasswordInput();
2111   }
2114 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
2115                                                    int plugin_id) {
2116   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
2119 void RenderWidgetHostViewMac::OnStartPluginIme() {
2120   [cocoa_view_ setPluginImeActive:YES];
2123 void RenderWidgetHostViewMac::OnDidChangeScrollbarsForMainFrame(
2124     bool has_horizontal_scrollbar, bool has_vertical_scrollbar) {
2125   [cocoa_view_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
2128 gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect(
2129     const gfx::Rect& rect) {
2130   gfx::Rect src_gl_subrect = rect;
2131   src_gl_subrect.set_y(GetViewBounds().height() - rect.bottom());
2133   return gfx::ToEnclosingRect(gfx::ScaleRect(src_gl_subrect,
2134                                              ViewScaleFactor()));
2137 void RenderWidgetHostViewMac::AddPendingLatencyInfo(
2138     const std::vector<ui::LatencyInfo>& latency_info) {
2139   // If a screenshot is being taken when using CoreAnimation, send a few extra
2140   // calls to setNeedsDisplay and wait for their resulting display calls,
2141   // before reporting that the frame has reached the screen.
2142   if (use_core_animation_) {
2143     bool should_defer = false;
2144     for (size_t i = 0; i < latency_info.size(); i++) {
2145       if (latency_info[i].FindLatency(
2146               ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
2147               render_widget_host_->GetLatencyComponentId(),
2148               NULL)) {
2149         should_defer = true;
2150       }
2151     }
2152     if (should_defer) {
2153       // Multiple pending screenshot requests will work, but if every frame
2154       // requests a screenshot, then the delay will never expire. Assert this
2155       // here to avoid this.
2156       CHECK_EQ(pending_latency_info_delay_, 0u);
2157       // Wait a fixed number of frames (calls to CALayer::display) before
2158       // claiming that the screenshot has reached the screen. This number
2159       // comes from taking the first number where tests didn't fail (six),
2160       // and doubling it.
2161       const uint32 kScreenshotLatencyDelayInFrames = 12;
2162       pending_latency_info_delay_ = kScreenshotLatencyDelayInFrames;
2163       TickPendingLatencyInfoDelay();
2164     }
2165   }
2167   for (size_t i = 0; i < latency_info.size(); i++) {
2168     pending_latency_info_.push_back(latency_info[i]);
2169   }
2172 void RenderWidgetHostViewMac::SendPendingLatencyInfoToHost() {
2173   if (pending_latency_info_delay_) {
2174     pending_latency_info_delay_ -= 1;
2175     return;
2176   }
2177   pending_latency_info_delay_weak_ptr_factory_.InvalidateWeakPtrs();
2179   for (size_t i = 0; i < pending_latency_info_.size(); i++) {
2180     pending_latency_info_[i].AddLatencyNumber(
2181         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
2182     render_widget_host_->FrameSwapped(pending_latency_info_[i]);
2183   }
2184   pending_latency_info_.clear();
2187 void RenderWidgetHostViewMac::TickPendingLatencyInfoDelay() {
2188   if (compositing_iosurface_layer_) {
2189     // Keep calling gotNewFrame in a loop until enough display calls come in.
2190     // Each call will be separated by about a vsync.
2191     base::MessageLoop::current()->PostTask(
2192         FROM_HERE,
2193         base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
2194                    pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()));
2195     [compositing_iosurface_layer_ gotNewFrame];
2196   }
2197   if (software_layer_) {
2198     // In software mode, setNeedsDisplay will almost immediately result in the
2199     // layer's draw function being called, so manually insert a pretend-vsync
2200     // at 60 Hz.
2201     base::MessageLoop::current()->PostDelayedTask(
2202         FROM_HERE,
2203         base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
2204                    pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()),
2205         base::TimeDelta::FromMilliseconds(1000/60));
2206     [software_layer_ setNeedsDisplay];
2207   }
2210 void RenderWidgetHostViewMac::AddPendingSwapAck(
2211     int32 route_id, int gpu_host_id, int32 renderer_id) {
2212   // Note that multiple un-acked swaps can come in the event of a GPU process
2213   // loss. Drop the old acks.
2214   pending_swap_ack_.reset(new PendingSwapAck(
2215       route_id, gpu_host_id, renderer_id));
2217   // A trace value of 2 indicates that there is a pending swap ack. See
2218   // CompositingIOSurfaceLayer's canDrawInCGLContext for other value meanings.
2219   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 2);
2222 void RenderWidgetHostViewMac::SendPendingSwapAck() {
2223   if (!pending_swap_ack_)
2224     return;
2226   AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
2227   ack_params.sync_point = 0;
2228   ack_params.renderer_id = pending_swap_ack_->renderer_id;
2229   RenderWidgetHostImpl::AcknowledgeBufferPresent(pending_swap_ack_->route_id,
2230                                                  pending_swap_ack_->gpu_host_id,
2231                                                  ack_params);
2232   pending_swap_ack_.reset();
2233   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0);
2236 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
2237   if (!render_widget_host_ || render_widget_host_->is_hidden())
2238     return;
2240   // Pausing for the overlay view prevents the underlay from receiving
2241   // frames. This may lead to large delays, causing overlaps. If both
2242   // overlay and underlay resize at the same time, let them both to have
2243   // some time waiting. See crbug.com/352020.
2244   if (underlay_view_ &&
2245       underlay_view_->render_widget_host_ &&
2246       !underlay_view_->render_widget_host_->
2247           CanPauseForPendingResizeOrRepaints())
2248     return;
2250   // Ensure that all frames are acked before waiting for a frame to come in.
2251   // Note that we will draw a frame at the end of this function, so it is safe
2252   // to ack a never-drawn frame here.
2253   SendPendingSwapAck();
2255   // Wait for a frame of the right size to come in.
2256   render_widget_host_->PauseForPendingResizeOrRepaints();
2258   // Immediately draw any frames that haven't been drawn yet. This is necessary
2259   // to keep the window and the window's contents in sync.
2260   [cocoa_view_ displayIfNeeded];
2261   [software_layer_ displayIfNeeded];
2262   [compositing_iosurface_layer_ displayIfNeeded];
2265 void RenderWidgetHostViewMac::LayoutLayers() {
2266   if (!use_core_animation_)
2267     return;
2269   // Disable animation of the layer's resizing or change in contents scale.
2270   ScopedCAActionDisabler disabler;
2272   CGRect new_background_frame = NSRectToCGRect([cocoa_view() bounds]);
2274   // Dynamically calling setContentsScale on a CAOpenGLLayer for which
2275   // setAsynchronous is dynamically toggled can result in flashes of corrupt
2276   // content. Work around this by replacing the entire layer when the scale
2277   // factor changes.
2278   if (compositing_iosurface_ &&
2279       [compositing_iosurface_layer_
2280           respondsToSelector:(@selector(contentsScale))]) {
2281     if (compositing_iosurface_->scale_factor() !=
2282         [compositing_iosurface_layer_ contentsScale]) {
2283       DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
2284       EnsureCompositedIOSurfaceLayer();
2285     }
2286   }
2287   if (compositing_iosurface_ &&
2288       compositing_iosurface_->HasIOSurface() &&
2289       compositing_iosurface_layer_) {
2290     CGRect layer_bounds = CGRectMake(
2291       0,
2292       0,
2293       compositing_iosurface_->dip_io_surface_size().width(),
2294       compositing_iosurface_->dip_io_surface_size().height());
2295     CGPoint layer_position = CGPointMake(
2296       0,
2297       CGRectGetHeight(new_background_frame) - CGRectGetHeight(layer_bounds));
2298     bool bounds_changed = !CGRectEqualToRect(
2299         layer_bounds, [compositing_iosurface_layer_ bounds]);
2300     [compositing_iosurface_layer_ setPosition:layer_position];
2301     [compositing_iosurface_layer_ setBounds:layer_bounds];
2303     // If the bounds changed, then draw the frame immediately, to ensure that
2304     // content displayed is in sync with the window size.
2305     if (bounds_changed) {
2306       // Also, sometimes, especially when infobars are being removed, the
2307       // setNeedsDisplay calls are dropped on the floor, and stale content is
2308       // displayed. Calling displayIfNeeded will ensure that the right size
2309       // frame is drawn to the screen.
2310       // http://crbug.com/350817
2311       [compositing_iosurface_layer_ setNeedsDisplay];
2312       [compositing_iosurface_layer_ displayIfNeeded];
2313     }
2314   }
2316   // Dynamically update the software layer's contents scale to match the
2317   // software frame.
2318   if (software_frame_manager_->HasCurrentFrame() &&
2319       [software_layer_ respondsToSelector:(@selector(contentsScale))] &&
2320       [software_layer_ respondsToSelector:(@selector(setContentsScale:))]) {
2321     if (software_frame_manager_->GetCurrentFrameDeviceScaleFactor() !=
2322         [software_layer_ contentsScale]) {
2323       [software_layer_ setContentsScale:
2324           software_frame_manager_->GetCurrentFrameDeviceScaleFactor()];
2325     }
2326   }
2327   // Changing the software layer's bounds and position doesn't always result
2328   // in the layer being anchored to the top-left. Set the layer's frame
2329   // explicitly, since this is more reliable in practice.
2330   if (software_layer_) {
2331     bool frame_changed = !CGRectEqualToRect(
2332         new_background_frame, [software_layer_ frame]);
2333     if (frame_changed) {
2334       [software_layer_ setFrame:new_background_frame];
2335       [software_layer_ setNeedsDisplay];
2336     }
2337   }
2340 SkBitmap::Config RenderWidgetHostViewMac::PreferredReadbackFormat() {
2341   return SkBitmap::kARGB_8888_Config;
2344 }  // namespace content
2346 // RenderWidgetHostViewCocoa ---------------------------------------------------
2348 @implementation RenderWidgetHostViewCocoa
2349 @synthesize selectedRange = selectedRange_;
2350 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
2351 @synthesize markedRange = markedRange_;
2353 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
2354   self = [super initWithFrame:NSZeroRect];
2355   if (self) {
2356     self.acceptsTouchEvents = YES;
2357     editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
2358     editCommand_helper_->AddEditingSelectorsToClass([self class]);
2360     renderWidgetHostView_.reset(r);
2361     canBeKeyView_ = YES;
2362     focusedPluginIdentifier_ = -1;
2363     renderWidgetHostView_->backing_store_scale_factor_ =
2364         ScaleFactorForView(self);
2366     // OpenGL support:
2367     if ([self respondsToSelector:
2368         @selector(setWantsBestResolutionOpenGLSurface:)]) {
2369       [self setWantsBestResolutionOpenGLSurface:YES];
2370     }
2371     handlingGlobalFrameDidChange_ = NO;
2372     [[NSNotificationCenter defaultCenter]
2373         addObserver:self
2374            selector:@selector(globalFrameDidChange:)
2375                name:NSViewGlobalFrameDidChangeNotification
2376              object:self];
2377     [[NSNotificationCenter defaultCenter]
2378         addObserver:self
2379            selector:@selector(didChangeScreenParameters:)
2380                name:NSApplicationDidChangeScreenParametersNotification
2381              object:nil];
2382   }
2383   return self;
2386 - (void)dealloc {
2387   // Unbind the GL context from this view. If this is not done before super's
2388   // dealloc is called then the GL context will crash when it reaches into
2389   // the view in its destructor.
2390   // http://crbug.com/255608
2391   if (renderWidgetHostView_)
2392     renderWidgetHostView_->AcceleratedSurfaceRelease();
2394   if (responderDelegate_ &&
2395       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
2396     [responderDelegate_ viewGone:self];
2397   responderDelegate_.reset();
2399   [[NSNotificationCenter defaultCenter] removeObserver:self];
2401   [super dealloc];
2404 - (void)didChangeScreenParameters:(NSNotification*)notify {
2405   g_screen_info_up_to_date = false;
2408 - (void)setResponderDelegate:
2409             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
2410   DCHECK(!responderDelegate_);
2411   responderDelegate_.reset([delegate retain]);
2414 - (void)resetCursorRects {
2415   if (currentCursor_) {
2416     [self addCursorRect:[self visibleRect] cursor:currentCursor_];
2417     [currentCursor_ setOnMouseEntered:YES];
2418   }
2421 - (void)gotUnhandledWheelEvent {
2422   if (responderDelegate_ &&
2423       [responderDelegate_
2424           respondsToSelector:@selector(gotUnhandledWheelEvent)]) {
2425     [responderDelegate_ gotUnhandledWheelEvent];
2426   }
2429 - (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right {
2430   if (responderDelegate_ &&
2431       [responderDelegate_
2432           respondsToSelector:@selector(scrollOffsetPinnedToLeft:toRight:)]) {
2433     [responderDelegate_ scrollOffsetPinnedToLeft:left toRight:right];
2434   }
2437 - (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar {
2438   if (responderDelegate_ &&
2439       [responderDelegate_
2440           respondsToSelector:@selector(setHasHorizontalScrollbar:)]) {
2441     [responderDelegate_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
2442   }
2445 - (BOOL)respondsToSelector:(SEL)selector {
2446   // Trickiness: this doesn't mean "does this object's superclass respond to
2447   // this selector" but rather "does the -respondsToSelector impl from the
2448   // superclass say that this class responds to the selector".
2449   if ([super respondsToSelector:selector])
2450     return YES;
2452   if (responderDelegate_)
2453     return [responderDelegate_ respondsToSelector:selector];
2455   return NO;
2458 - (id)forwardingTargetForSelector:(SEL)selector {
2459   if ([responderDelegate_ respondsToSelector:selector])
2460     return responderDelegate_.get();
2462   return [super forwardingTargetForSelector:selector];
2465 - (void)setCanBeKeyView:(BOOL)can {
2466   canBeKeyView_ = can;
2469 - (BOOL)acceptsMouseEventsWhenInactive {
2470   // Some types of windows (balloons, always-on-top panels) want to accept mouse
2471   // clicks w/o the first click being treated as 'activation'. Same applies to
2472   // mouse move events.
2473   return [[self window] level] > NSNormalWindowLevel;
2476 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
2477   return [self acceptsMouseEventsWhenInactive];
2480 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
2481   takesFocusOnlyOnMouseDown_ = b;
2484 - (void)setCloseOnDeactivate:(BOOL)b {
2485   closeOnDeactivate_ = b;
2488 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
2489   NSWindow* window = [self window];
2490   // If this is a background window, don't handle mouse movement events. This
2491   // is the expected behavior on the Mac as evidenced by other applications.
2492   if ([theEvent type] == NSMouseMoved &&
2493       ![self acceptsMouseEventsWhenInactive] &&
2494       ![window isKeyWindow]) {
2495     return YES;
2496   }
2498   // Use hitTest to check whether the mouse is over a nonWebContentView - in
2499   // which case the mouse event should not be handled by the render host.
2500   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
2501   NSView* contentView = [window contentView];
2502   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
2503   // Traverse the superview hierarchy as the hitTest will return the frontmost
2504   // view, such as an NSTextView, while nonWebContentView may be specified by
2505   // its parent view.
2506   while (view) {
2507     if ([view respondsToSelector:nonWebContentViewSelector] &&
2508         [view performSelector:nonWebContentViewSelector]) {
2509       // The cursor is over a nonWebContentView - ignore this mouse event.
2510       return YES;
2511     }
2512     if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
2513         !hasOpenMouseDown_) {
2514       // The cursor is over an overlapping render widget. This check is done by
2515       // both views so the one that's returned by -hitTest: will end up
2516       // processing the event.
2517       // Note that while dragging, we only get events for the render view where
2518       // drag started, even if mouse is  actually over another view or outside
2519       // the window. Cocoa does this for us. We should handle these events and
2520       // not ignore (since there is no other render view to handle them). Thus
2521       // the |!hasOpenMouseDown_| check above.
2522       return YES;
2523     }
2524     view = [view superview];
2525   }
2526   return NO;
2529 - (void)mouseEvent:(NSEvent*)theEvent {
2530   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
2531   if (responderDelegate_ &&
2532       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2533     BOOL handled = [responderDelegate_ handleEvent:theEvent];
2534     if (handled)
2535       return;
2536   }
2538   if ([self shouldIgnoreMouseEvent:theEvent]) {
2539     // If this is the first such event, send a mouse exit to the host view.
2540     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
2541       WebMouseEvent exitEvent =
2542           WebInputEventFactory::mouseEvent(theEvent, self);
2543       exitEvent.type = WebInputEvent::MouseLeave;
2544       exitEvent.button = WebMouseEvent::ButtonNone;
2545       renderWidgetHostView_->ForwardMouseEvent(exitEvent);
2546     }
2547     mouseEventWasIgnored_ = YES;
2548     return;
2549   }
2551   if (mouseEventWasIgnored_) {
2552     // If this is the first mouse event after a previous event that was ignored
2553     // due to the hitTest, send a mouse enter event to the host view.
2554     if (renderWidgetHostView_->render_widget_host_) {
2555       WebMouseEvent enterEvent =
2556           WebInputEventFactory::mouseEvent(theEvent, self);
2557       enterEvent.type = WebInputEvent::MouseMove;
2558       enterEvent.button = WebMouseEvent::ButtonNone;
2559       renderWidgetHostView_->ForwardMouseEvent(enterEvent);
2560     }
2561   }
2562   mouseEventWasIgnored_ = NO;
2564   // TODO(rohitrao): Probably need to handle other mouse down events here.
2565   if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
2566     if (renderWidgetHostView_->render_widget_host_)
2567       renderWidgetHostView_->render_widget_host_->OnPointerEventActivate();
2569     // Manually take focus after the click but before forwarding it to the
2570     // renderer.
2571     [[self window] makeFirstResponder:self];
2572   }
2574   // Don't cancel child popups; killing them on a mouse click would prevent the
2575   // user from positioning the insertion point in the text field spawning the
2576   // popup. A click outside the text field would cause the text field to drop
2577   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
2578   // the popup anyway, so we're OK.
2580   NSEventType type = [theEvent type];
2581   if (type == NSLeftMouseDown)
2582     hasOpenMouseDown_ = YES;
2583   else if (type == NSLeftMouseUp)
2584     hasOpenMouseDown_ = NO;
2586   // TODO(suzhe): We should send mouse events to the input method first if it
2587   // wants to handle them. But it won't work without implementing method
2588   // - (NSUInteger)characterIndexForPoint:.
2589   // See: http://code.google.com/p/chromium/issues/detail?id=47141
2590   // Instead of sending mouse events to the input method first, we now just
2591   // simply confirm all ongoing composition here.
2592   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
2593       type == NSOtherMouseDown) {
2594     [self confirmComposition];
2595   }
2597   const WebMouseEvent event =
2598       WebInputEventFactory::mouseEvent(theEvent, self);
2599   renderWidgetHostView_->ForwardMouseEvent(event);
2602 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
2603   // |performKeyEquivalent:| is sent to all views of a window, not only down the
2604   // responder chain (cf. "Handling Key Equivalents" in
2605   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
2606   // ). We only want to handle key equivalents if we're first responder.
2607   if ([[self window] firstResponder] != self)
2608     return NO;
2610   // If we return |NO| from this function, cocoa will send the key event to
2611   // the menu and only if the menu does not process the event to |keyDown:|. We
2612   // want to send the event to a renderer _before_ sending it to the menu, so
2613   // we need to return |YES| for all events that might be swallowed by the menu.
2614   // We do not return |YES| for every keypress because we don't get |keyDown:|
2615   // events for keys that we handle this way.
2616   NSUInteger modifierFlags = [theEvent modifierFlags];
2617   if ((modifierFlags & NSCommandKeyMask) == 0) {
2618     // Make sure the menu does not contain key equivalents that don't
2619     // contain cmd.
2620     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
2621     return NO;
2622   }
2624   // Command key combinations are sent via performKeyEquivalent rather than
2625   // keyDown:. We just forward this on and if WebCore doesn't want to handle
2626   // it, we let the WebContentsView figure out how to reinject it.
2627   [self keyEvent:theEvent wasKeyEquivalent:YES];
2628   return YES;
2631 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
2632   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
2633   // returned NO. If this function returns |YES|, Cocoa sends the event to
2634   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
2635   // to us instead of doing key view loop control, ctrl-left/right get handled
2636   // correctly, etc.
2637   // (However, there are still some keys that Cocoa swallows, e.g. the key
2638   // equivalent that Cocoa uses for toggling the input language. In this case,
2639   // that's actually a good thing, though -- see http://crbug.com/26115 .)
2640   return YES;
2643 - (EventHandled)keyEvent:(NSEvent*)theEvent {
2644   if (responderDelegate_ &&
2645       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2646     BOOL handled = [responderDelegate_ handleEvent:theEvent];
2647     if (handled)
2648       return kEventHandled;
2649   }
2651   [self keyEvent:theEvent wasKeyEquivalent:NO];
2652   return kEventHandled;
2655 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
2656   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
2657   DCHECK([theEvent type] != NSKeyDown ||
2658          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
2660   if ([theEvent type] == NSFlagsChanged) {
2661     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
2662     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
2663     int keyCode = [theEvent keyCode];
2664     if (!keyCode || keyCode == 10 || keyCode == 63)
2665       return;
2666   }
2668   // Don't cancel child popups; the key events are probably what's triggering
2669   // the popup in the first place.
2671   RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
2672   DCHECK(widgetHost);
2674   NativeWebKeyboardEvent event(theEvent);
2676   // Force fullscreen windows to close on Escape so they won't keep the keyboard
2677   // grabbed or be stuck onscreen if the renderer is hanging.
2678   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
2679       event.windowsKeyCode == ui::VKEY_ESCAPE &&
2680       renderWidgetHostView_->pepper_fullscreen_window()) {
2681     RenderWidgetHostViewMac* parent =
2682         renderWidgetHostView_->fullscreen_parent_host_view();
2683     if (parent)
2684       parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
2685     widgetHost->Shutdown();
2686     return;
2687   }
2689   // Suppress the escape key up event if necessary.
2690   if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
2691     if (event.type == NativeWebKeyboardEvent::KeyUp)
2692       suppressNextEscapeKeyUp_ = NO;
2693     return;
2694   }
2696   // We only handle key down events and just simply forward other events.
2697   if ([theEvent type] != NSKeyDown) {
2698     widgetHost->ForwardKeyboardEvent(event);
2700     // Possibly autohide the cursor.
2701     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2702       [NSCursor setHiddenUntilMouseMoves:YES];
2704     return;
2705   }
2707   base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2709   // Records the current marked text state, so that we can know if the marked
2710   // text was deleted or not after handling the key down event.
2711   BOOL oldHasMarkedText = hasMarkedText_;
2713   // This method should not be called recursively.
2714   DCHECK(!handlingKeyDown_);
2716   // Tells insertText: and doCommandBySelector: that we are handling a key
2717   // down event.
2718   handlingKeyDown_ = YES;
2720   // These variables might be set when handling the keyboard event.
2721   // Clear them here so that we can know whether they have changed afterwards.
2722   textToBeInserted_.clear();
2723   markedText_.clear();
2724   underlines_.clear();
2725   unmarkTextCalled_ = NO;
2726   hasEditCommands_ = NO;
2727   editCommands_.clear();
2729   // Before doing anything with a key down, check to see if plugin IME has been
2730   // cancelled, since the plugin host needs to be informed of that before
2731   // receiving the keydown.
2732   if ([theEvent type] == NSKeyDown)
2733     [self checkForPluginImeCancellation];
2735   // Sends key down events to input method first, then we can decide what should
2736   // be done according to input method's feedback.
2737   // If a plugin is active, bypass this step since events are forwarded directly
2738   // to the plugin IME.
2739   if (focusedPluginIdentifier_ == -1)
2740     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2742   handlingKeyDown_ = NO;
2744   // Indicates if we should send the key event and corresponding editor commands
2745   // after processing the input method result.
2746   BOOL delayEventUntilAfterImeCompostion = NO;
2748   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2749   // while an input method is composing or inserting a text.
2750   // Gmail checks this code in its onkeydown handler to stop auto-completing
2751   // e-mail addresses while composing a CJK text.
2752   // If the text to be inserted has only one character, then we don't need this
2753   // trick, because we'll send the text as a key press event instead.
2754   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2755     NativeWebKeyboardEvent fakeEvent = event;
2756     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
2757     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2758     fakeEvent.skip_in_browser = true;
2759     widgetHost->ForwardKeyboardEvent(fakeEvent);
2760     // If this key event was handled by the input method, but
2761     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2762     // enqueued edit commands, then in order to let webkit handle them
2763     // correctly, we need to send the real key event and corresponding edit
2764     // commands after processing the input method result.
2765     // We shouldn't do this if a new marked text was set by the input method,
2766     // otherwise the new marked text might be cancelled by webkit.
2767     if (hasEditCommands_ && !hasMarkedText_)
2768       delayEventUntilAfterImeCompostion = YES;
2769   } else {
2770     if (!editCommands_.empty()) {
2771       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2772           widgetHost->GetRoutingID(), editCommands_));
2773     }
2774     widgetHost->ForwardKeyboardEvent(event);
2775   }
2777   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2778   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2779   // be set to NULL. So we check it here and return immediately if it's NULL.
2780   if (!renderWidgetHostView_->render_widget_host_)
2781     return;
2783   // Then send keypress and/or composition related events.
2784   // If there was a marked text or the text to be inserted is longer than 1
2785   // character, then we send the text by calling ConfirmComposition().
2786   // Otherwise, if the text to be inserted only contains 1 character, then we
2787   // can just send a keypress event which is fabricated by changing the type of
2788   // the keydown event, so that we can retain all necessary informations, such
2789   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2790   // prevent the browser from handling it again.
2791   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2792   // handle BMP characters here, as we can always insert non-BMP characters as
2793   // text.
2794   BOOL textInserted = NO;
2795   if (textToBeInserted_.length() >
2796       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2797     widgetHost->ImeConfirmComposition(
2798         textToBeInserted_, gfx::Range::InvalidRange(), false);
2799     textInserted = YES;
2800   }
2802   // Updates or cancels the composition. If some text has been inserted, then
2803   // we don't need to cancel the composition explicitly.
2804   if (hasMarkedText_ && markedText_.length()) {
2805     // Sends the updated marked text to the renderer so it can update the
2806     // composition node in WebKit.
2807     // When marked text is available, |selectedRange_| will be the range being
2808     // selected inside the marked text.
2809     widgetHost->ImeSetComposition(markedText_, underlines_,
2810                                   selectedRange_.location,
2811                                   NSMaxRange(selectedRange_));
2812   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2813     if (unmarkTextCalled_) {
2814       widgetHost->ImeConfirmComposition(
2815           base::string16(), gfx::Range::InvalidRange(), false);
2816     } else {
2817       widgetHost->ImeCancelComposition();
2818     }
2819   }
2821   // If the key event was handled by the input method but it also generated some
2822   // edit commands, then we need to send the real key event and corresponding
2823   // edit commands here. This usually occurs when the input method wants to
2824   // finish current composition session but still wants the application to
2825   // handle the key event. See http://crbug.com/48161 for reference.
2826   if (delayEventUntilAfterImeCompostion) {
2827     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2828     // with windowsKeyCode == 0xE5 has already been sent to webkit.
2829     // So before sending the real key down event, we need to send a fake key up
2830     // event to balance it.
2831     NativeWebKeyboardEvent fakeEvent = event;
2832     fakeEvent.type = blink::WebInputEvent::KeyUp;
2833     fakeEvent.skip_in_browser = true;
2834     widgetHost->ForwardKeyboardEvent(fakeEvent);
2835     // Not checking |renderWidgetHostView_->render_widget_host_| here because
2836     // a key event with |skip_in_browser| == true won't be handled by browser,
2837     // thus it won't destroy the widget.
2839     if (!editCommands_.empty()) {
2840       widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2841           widgetHost->GetRoutingID(), editCommands_));
2842     }
2843     widgetHost->ForwardKeyboardEvent(event);
2845     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2846     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2847     // be set to NULL. So we check it here and return immediately if it's NULL.
2848     if (!renderWidgetHostView_->render_widget_host_)
2849       return;
2850   }
2852   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2853   // Only send a corresponding key press event if there is no marked text.
2854   if (!hasMarkedText_) {
2855     if (!textInserted && textToBeInserted_.length() == 1) {
2856       // If a single character was inserted, then we just send it as a keypress
2857       // event.
2858       event.type = blink::WebInputEvent::Char;
2859       event.text[0] = textToBeInserted_[0];
2860       event.text[1] = 0;
2861       event.skip_in_browser = true;
2862       widgetHost->ForwardKeyboardEvent(event);
2863     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2864                [[theEvent characters] length] > 0 &&
2865                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2866                 (hasEditCommands_ && editCommands_.empty()))) {
2867       // We don't get insertText: calls if ctrl or cmd is down, or the key event
2868       // generates an insert command. So synthesize a keypress event for these
2869       // cases, unless the key event generated any other command.
2870       event.type = blink::WebInputEvent::Char;
2871       event.skip_in_browser = true;
2872       widgetHost->ForwardKeyboardEvent(event);
2873     }
2874   }
2876   // Possibly autohide the cursor.
2877   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2878     [NSCursor setHiddenUntilMouseMoves:YES];
2881 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2882   DCHECK(base::mac::IsOSLionOrLater());
2884   if ([event phase] != NSEventPhaseEnded &&
2885       [event phase] != NSEventPhaseCancelled) {
2886     return;
2887   }
2889   if (renderWidgetHostView_->render_widget_host_) {
2890     // History-swiping is not possible if the logic reaches this point.
2891     // Allow rubber-banding in both directions.
2892     bool canRubberbandLeft = true;
2893     bool canRubberbandRight = true;
2894     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2895         event, self, canRubberbandLeft, canRubberbandRight);
2896     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2897   }
2899   if (endWheelMonitor_) {
2900     [NSEvent removeMonitor:endWheelMonitor_];
2901     endWheelMonitor_ = nil;
2902   }
2905 - (void)beginGestureWithEvent:(NSEvent*)event {
2906   [responderDelegate_ beginGestureWithEvent:event];
2908 - (void)endGestureWithEvent:(NSEvent*)event {
2909   [responderDelegate_ endGestureWithEvent:event];
2911 - (void)touchesMovedWithEvent:(NSEvent*)event {
2912   [responderDelegate_ touchesMovedWithEvent:event];
2914 - (void)touchesBeganWithEvent:(NSEvent*)event {
2915   [responderDelegate_ touchesBeganWithEvent:event];
2917 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2918   [responderDelegate_ touchesCancelledWithEvent:event];
2920 - (void)touchesEndedWithEvent:(NSEvent*)event {
2921   [responderDelegate_ touchesEndedWithEvent:event];
2924 // This is invoked only on 10.8 or newer when the user taps a word using
2925 // three fingers.
2926 - (void)quickLookWithEvent:(NSEvent*)event {
2927   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2928   TextInputClientMac::GetInstance()->GetStringAtPoint(
2929       renderWidgetHostView_->render_widget_host_,
2930       gfx::Point(point.x, NSHeight([self frame]) - point.y),
2931       ^(NSAttributedString* string, NSPoint baselinePoint) {
2932           if (string && [string length] > 0) {
2933             dispatch_async(dispatch_get_main_queue(), ^{
2934                 [self showDefinitionForAttributedString:string
2935                                                 atPoint:baselinePoint];
2936             });
2937           }
2938       }
2939   );
2942 // This method handles 2 different types of hardware events.
2943 // (Apple does not distinguish between them).
2944 //  a. Scrolling the middle wheel of a mouse.
2945 //  b. Swiping on the track pad.
2947 // This method is responsible for 2 types of behavior:
2948 //  a. Scrolling the content of window.
2949 //  b. Navigating forwards/backwards in history.
2951 // This is a brief description of the logic:
2952 //  1. If the content can be scrolled, scroll the content.
2953 //     (This requires a roundtrip to blink to determine whether the content
2954 //      can be scrolled.)
2955 //     Once this logic is triggered, the navigate logic cannot be triggered
2956 //     until the gesture finishes.
2957 //  2. If the user is making a horizontal swipe, start the navigate
2958 //     forward/backwards UI.
2959 //     Once this logic is triggered, the user can either cancel or complete
2960 //     the gesture. If the user completes the gesture, all remaining touches
2961 //     are swallowed, and not allowed to scroll the content. If the user
2962 //     cancels the gesture, all remaining touches are forwarded to the content
2963 //     scroll logic. The user cannot trigger the navigation logic again.
2964 - (void)scrollWheel:(NSEvent*)event {
2965   if (responderDelegate_ &&
2966       [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2967     BOOL handled = [responderDelegate_ handleEvent:event];
2968     if (handled)
2969       return;
2970   }
2972   // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2973   // the event is received even when the mouse cursor is no longer over the view
2974   // when the scrolling ends (e.g. if the tab was switched). This is necessary
2975   // for ending rubber-banding in such cases.
2976   if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2977       !endWheelMonitor_) {
2978     endWheelMonitor_ =
2979       [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2980       handler:^(NSEvent* blockEvent) {
2981           [self shortCircuitScrollWheelEvent:blockEvent];
2982           return blockEvent;
2983       }];
2984   }
2986   // This is responsible for content scrolling!
2987   if (renderWidgetHostView_->render_widget_host_) {
2988     BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2989     BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2990     const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2991         event, self, canRubberbandLeft, canRubberbandRight);
2992     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2993   }
2996 // Called repeatedly during a pinch gesture, with incremental change values.
2997 - (void)magnifyWithEvent:(NSEvent*)event {
2998   if (renderWidgetHostView_->render_widget_host_) {
2999     // Send a GesturePinchUpdate event.
3000     // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
3001     // GestureSrollBegin/End) as is done for touchscreen.  Keeping track of when
3002     // a pinch is active would take a little more work here, and we don't need
3003     // it for anything yet.
3004     const WebGestureEvent& webEvent =
3005         WebInputEventFactory::gestureEvent(event, self);
3006     renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
3007   }
3010 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
3011   NSWindow* oldWindow = [self window];
3013   // We're messing with the window, so do this to ensure no flashes. This one
3014   // prevents a flash when the current tab is closed.
3015   if (!renderWidgetHostView_->use_core_animation_)
3016     [oldWindow disableScreenUpdatesUntilFlush];
3018   NSNotificationCenter* notificationCenter =
3019       [NSNotificationCenter defaultCenter];
3021   // Backing property notifications crash on 10.6 when building with the 10.7
3022   // SDK, see http://crbug.com/260595.
3023   static BOOL supportsBackingPropertiesNotification =
3024       SupportsBackingPropertiesChangedNotification();
3026   if (oldWindow) {
3027     if (supportsBackingPropertiesNotification) {
3028       [notificationCenter
3029           removeObserver:self
3030                     name:NSWindowDidChangeBackingPropertiesNotification
3031                   object:oldWindow];
3032     }
3033     [notificationCenter
3034         removeObserver:self
3035                   name:NSWindowDidMoveNotification
3036                 object:oldWindow];
3037     [notificationCenter
3038         removeObserver:self
3039                   name:NSWindowDidEndLiveResizeNotification
3040                 object:oldWindow];
3041   }
3042   if (newWindow) {
3043     if (supportsBackingPropertiesNotification) {
3044       [notificationCenter
3045           addObserver:self
3046              selector:@selector(windowDidChangeBackingProperties:)
3047                  name:NSWindowDidChangeBackingPropertiesNotification
3048                object:newWindow];
3049     }
3050     [notificationCenter
3051         addObserver:self
3052            selector:@selector(windowChangedGlobalFrame:)
3053                name:NSWindowDidMoveNotification
3054              object:newWindow];
3055     [notificationCenter
3056         addObserver:self
3057            selector:@selector(windowChangedGlobalFrame:)
3058                name:NSWindowDidEndLiveResizeNotification
3059              object:newWindow];
3060   }
3063 - (void)updateScreenProperties{
3064   renderWidgetHostView_->UpdateBackingStoreScaleFactor();
3065   renderWidgetHostView_->UpdateDisplayLink();
3068 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
3069 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
3070   // Background tabs check if their scale factor or vsync properties changed
3071   // when they are added to a window.
3073   // Allocating a CGLayerRef with the current scale factor immediately from
3074   // this handler doesn't work. Schedule the backing store update on the
3075   // next runloop cycle, then things are read for CGLayerRef allocations to
3076   // work.
3077   [self performSelector:@selector(updateScreenProperties)
3078              withObject:nil
3079              afterDelay:0];
3082 - (void)globalFrameDidChange:(NSNotification*)notification {
3083   if (handlingGlobalFrameDidChange_)
3084     return;
3086   handlingGlobalFrameDidChange_ = YES;
3087   if (!renderWidgetHostView_->use_core_animation_ &&
3088       renderWidgetHostView_->compositing_iosurface_context_) {
3089     [renderWidgetHostView_->compositing_iosurface_context_->nsgl_context()
3090         update];
3091   }
3092   handlingGlobalFrameDidChange_ = NO;
3095 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
3096   renderWidgetHostView_->UpdateScreenInfo(
3097       renderWidgetHostView_->GetNativeView());
3100 - (void)setFrameSize:(NSSize)newSize {
3101   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
3103   // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
3104   // -setFrame: isn't neccessary.
3105   [super setFrameSize:newSize];
3107   if (!renderWidgetHostView_->render_widget_host_)
3108     return;
3110   // Move the CALayers to their positions in the new view size. Note that
3111   // this will not draw anything because the non-background layers' sizes
3112   // didn't actually change.
3113   renderWidgetHostView_->LayoutLayers();
3115   renderWidgetHostView_->render_widget_host_->SendScreenRects();
3116   renderWidgetHostView_->render_widget_host_->WasResized();
3118   // Wait for the frame that WasResize might have requested. If the view is
3119   // being made visible at a new size, then this call will have no effect
3120   // because the view widget is still hidden, and the pause call in WasShown
3121   // will have this effect for us.
3122   renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
3125 // Fills with white the parts of the area to the right and bottom for |rect|
3126 // that intersect |damagedRect|.
3127 - (void)fillBottomRightRemainderOfRect:(gfx::Rect)rect
3128                              dirtyRect:(gfx::Rect)damagedRect
3129                              inContext:(CGContextRef)context {
3130   if (damagedRect.right() > rect.right()) {
3131     int x = std::max(rect.right(), damagedRect.x());
3132     int y = std::min(rect.bottom(), damagedRect.bottom());
3133     int width = damagedRect.right() - x;
3134     int height = damagedRect.y() - y;
3136     // Extra fun to get around the fact that gfx::Rects can't have
3137     // negative sizes.
3138     if (width < 0) {
3139       x += width;
3140       width = -width;
3141     }
3142     if (height < 0) {
3143       y += height;
3144       height = -height;
3145     }
3147     NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
3148     CGContextSetFillColorWithColor(context,
3149                                    CGColorGetConstantColor(kCGColorWhite));
3150     CGContextFillRect(context, NSRectToCGRect(r));
3151   }
3152   if (damagedRect.bottom() > rect.bottom()) {
3153     int x = damagedRect.x();
3154     int y = damagedRect.bottom();
3155     int width = damagedRect.right() - x;
3156     int height = std::max(rect.bottom(), damagedRect.y()) - y;
3158     // Extra fun to get around the fact that gfx::Rects can't have
3159     // negative sizes.
3160     if (width < 0) {
3161       x += width;
3162       width = -width;
3163     }
3164     if (height < 0) {
3165       y += height;
3166       height = -height;
3167     }
3169     NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
3170     CGContextSetFillColorWithColor(context,
3171                                    CGColorGetConstantColor(kCGColorWhite));
3172     CGContextFillRect(context, NSRectToCGRect(r));
3173   }
3176 - (void)drawRect:(NSRect)dirtyRect {
3177   TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::drawRect");
3178   DCHECK(!renderWidgetHostView_->use_core_animation_);
3180   if (!renderWidgetHostView_->render_widget_host_) {
3181     // When using CoreAnimation, this path is used to paint the contents area
3182     // white before any frames come in. When layers to draw frames exist, this
3183     // is not hit.
3184     [[NSColor whiteColor] set];
3185     NSRectFill(dirtyRect);
3186     return;
3187   }
3189   const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]);
3191   if (renderWidgetHostView_->last_frame_was_accelerated_ &&
3192       renderWidgetHostView_->compositing_iosurface_) {
3193     if (renderWidgetHostView_->allow_overlapping_views_) {
3194       // If overlapping views need to be allowed, punch a hole in the window
3195       // to expose the GL underlay.
3196       TRACE_EVENT2("gpu", "NSRectFill clear", "w", damagedRect.width(),
3197                    "h", damagedRect.height());
3198       // NSRectFill is extremely slow (15ms for a window on a fast MacPro), so
3199       // this is only done when it's a real invalidation from window damage (not
3200       // when a BuffersSwapped was received). Note that even a 1x1 NSRectFill
3201       // can take many milliseconds sometimes (!) so this is skipped completely
3202       // for drawRects that are triggered by BuffersSwapped messages.
3203       [[NSColor clearColor] set];
3204       NSRectFill(dirtyRect);
3205     }
3207     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
3208         renderWidgetHostView_->compositing_iosurface_context_->cgl_context());
3209     renderWidgetHostView_->DrawIOSurfaceWithoutCoreAnimation();
3210     return;
3211   }
3213   CGContextRef context = static_cast<CGContextRef>(
3214       [[NSGraphicsContext currentContext] graphicsPort]);
3215   [self drawWithDirtyRect:NSRectToCGRect(dirtyRect)
3216                 inContext:context];
3219 - (void)drawWithDirtyRect:(CGRect)dirtyRect
3220                 inContext:(CGContextRef)context {
3221   content::SoftwareFrameManager* software_frame_manager =
3222       renderWidgetHostView_->software_frame_manager_.get();
3223   if (software_frame_manager->HasCurrentFrame()) {
3224     // Note: All coordinates are in view units, not pixels.
3225     gfx::Rect bitmapRect(
3226             software_frame_manager->GetCurrentFrameSizeInDIP());
3228     // Specify the proper y offset to ensure that the view is rooted to the
3229     // upper left corner.  This can be negative, if the window was resized
3230     // smaller and the renderer hasn't yet repainted.
3231     int yOffset = NSHeight([self bounds]) - bitmapRect.height();
3233     NSRect nsDirtyRect = NSRectFromCGRect(dirtyRect);
3234     const gfx::Rect damagedRect([self flipNSRectToRect:nsDirtyRect]);
3236     gfx::Rect paintRect = gfx::IntersectRects(bitmapRect, damagedRect);
3237     if (!paintRect.IsEmpty()) {
3238       gfx::Size sizeInPixels =
3239           software_frame_manager->GetCurrentFrameSizeInPixels();
3240       base::ScopedCFTypeRef<CGDataProviderRef> dataProvider(
3241           CGDataProviderCreateWithData(
3242               NULL,
3243               software_frame_manager->GetCurrentFramePixels(),
3244               4 * sizeInPixels.width() * sizeInPixels.height(),
3245               NULL));
3246       base::ScopedCFTypeRef<CGImageRef> image(
3247           CGImageCreate(
3248               sizeInPixels.width(),
3249               sizeInPixels.height(),
3250               8,
3251               32,
3252               4 * sizeInPixels.width(),
3253               base::mac::GetSystemColorSpace(),
3254               kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
3255               dataProvider,
3256               NULL,
3257               false,
3258               kCGRenderingIntentDefault));
3259       CGRect imageRect = bitmapRect.ToCGRect();
3260       imageRect.origin.y = yOffset;
3261       CGContextDrawImage(context, imageRect, image);
3262     }
3264     renderWidgetHostView_->SendPendingLatencyInfoToHost();
3266     // Fill the remaining portion of the damagedRect with white
3267     [self fillBottomRightRemainderOfRect:bitmapRect
3268                                dirtyRect:damagedRect
3269                                inContext:context];
3271     if (!renderWidgetHostView_->whiteout_start_time_.is_null()) {
3272       base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
3273           renderWidgetHostView_->whiteout_start_time_;
3274       UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
3276       // Reset the start time to 0 so that we start recording again the next
3277       // time the backing store is NULL...
3278       renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks();
3279     }
3280     if (!renderWidgetHostView_->web_contents_switch_paint_time_.is_null()) {
3281       base::TimeDelta web_contents_switch_paint_duration =
3282           base::TimeTicks::Now() -
3283               renderWidgetHostView_->web_contents_switch_paint_time_;
3284       UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
3285           web_contents_switch_paint_duration);
3286       // Reset contents_switch_paint_time_ to 0 so future tab selections are
3287       // recorded.
3288       renderWidgetHostView_->web_contents_switch_paint_time_ =
3289           base::TimeTicks();
3290     }
3291   } else {
3292     CGContextSetFillColorWithColor(context,
3293                                    CGColorGetConstantColor(kCGColorWhite));
3294     CGContextFillRect(context, dirtyRect);
3295     if (renderWidgetHostView_->whiteout_start_time_.is_null())
3296       renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks::Now();
3297   }
3300 - (BOOL)canBecomeKeyView {
3301   if (!renderWidgetHostView_->render_widget_host_)
3302     return NO;
3304   return canBeKeyView_;
3307 - (BOOL)acceptsFirstResponder {
3308   if (!renderWidgetHostView_->render_widget_host_)
3309     return NO;
3311   return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
3314 - (BOOL)becomeFirstResponder {
3315   if (!renderWidgetHostView_->render_widget_host_)
3316     return NO;
3318   renderWidgetHostView_->render_widget_host_->Focus();
3319   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
3320   renderWidgetHostView_->SetTextInputActive(true);
3322   // Cancel any onging composition text which was left before we lost focus.
3323   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
3324   // somehow that method won't be called when switching among different tabs.
3325   // See http://crbug.com/47209
3326   [self cancelComposition];
3328   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
3329       [[self window] keyViewSelectionDirection]];
3330   NSDictionary* userInfo =
3331       [NSDictionary dictionaryWithObject:direction
3332                                   forKey:kSelectionDirection];
3333   [[NSNotificationCenter defaultCenter]
3334       postNotificationName:kViewDidBecomeFirstResponder
3335                     object:self
3336                   userInfo:userInfo];
3338   return YES;
3341 - (BOOL)resignFirstResponder {
3342   renderWidgetHostView_->SetTextInputActive(false);
3343   if (!renderWidgetHostView_->render_widget_host_)
3344     return YES;
3346   if (closeOnDeactivate_)
3347     renderWidgetHostView_->KillSelf();
3349   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
3350   renderWidgetHostView_->render_widget_host_->Blur();
3352   // We should cancel any onging composition whenever RWH's Blur() method gets
3353   // called, because in this case, webkit will confirm the ongoing composition
3354   // internally.
3355   [self cancelComposition];
3357   return YES;
3360 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
3361   if (responderDelegate_ &&
3362       [responderDelegate_
3363           respondsToSelector:@selector(validateUserInterfaceItem:
3364                                                      isValidItem:)]) {
3365     BOOL valid;
3366     BOOL known =
3367         [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
3368     if (known)
3369       return valid;
3370   }
3372   SEL action = [item action];
3374   if (action == @selector(stopSpeaking:)) {
3375     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
3376            renderWidgetHostView_->IsSpeaking();
3377   }
3378   if (action == @selector(startSpeaking:)) {
3379     return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
3380            renderWidgetHostView_->SupportsSpeech();
3381   }
3383   // For now, these actions are always enabled for render view,
3384   // this is sub-optimal.
3385   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
3386   if (action == @selector(undo:) ||
3387       action == @selector(redo:) ||
3388       action == @selector(cut:) ||
3389       action == @selector(copy:) ||
3390       action == @selector(copyToFindPboard:) ||
3391       action == @selector(paste:) ||
3392       action == @selector(pasteAndMatchStyle:)) {
3393     return renderWidgetHostView_->render_widget_host_->IsRenderView();
3394   }
3396   return editCommand_helper_->IsMenuItemEnabled(action, self);
3399 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
3400   return renderWidgetHostView_.get();
3403 // Determine whether we should autohide the cursor (i.e., hide it until mouse
3404 // move) for the given event. Customize here to be more selective about which
3405 // key presses to autohide on.
3406 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
3407   return ([event type] == NSKeyDown &&
3408              !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
3411 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
3412                                          index:(NSUInteger)index
3413                                       maxCount:(NSUInteger)maxCount {
3414   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
3415   NSUInteger totalLength = [fullArray count];
3416   if (index >= totalLength)
3417     return nil;
3418   NSUInteger length = MIN(totalLength - index, maxCount);
3419   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
3422 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
3423   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
3424   return [fullArray count];
3427 - (id)accessibilityAttributeValue:(NSString *)attribute {
3428   BrowserAccessibilityManager* manager =
3429       renderWidgetHostView_->GetBrowserAccessibilityManager();
3431   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
3432   // BrowserAccessibilityManager. Children includes all subviews in addition to
3433   // contents. Currently we do not have subviews besides the document view.
3434   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
3435           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
3436       manager) {
3437     return [NSArray arrayWithObjects:manager->
3438         GetRoot()->ToBrowserAccessibilityCocoa(), nil];
3439   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
3440     return NSAccessibilityScrollAreaRole;
3441   }
3442   id ret = [super accessibilityAttributeValue:attribute];
3443   return ret;
3446 - (NSArray*)accessibilityAttributeNames {
3447   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
3448   [ret addObject:NSAccessibilityContentsAttribute];
3449   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
3450   return ret;
3453 - (id)accessibilityHitTest:(NSPoint)point {
3454   if (!renderWidgetHostView_->GetBrowserAccessibilityManager())
3455     return self;
3456   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
3457   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
3458   localPoint.y = NSHeight([self bounds]) - localPoint.y;
3459   BrowserAccessibilityCocoa* root = renderWidgetHostView_->
3460       GetBrowserAccessibilityManager()->
3461           GetRoot()->ToBrowserAccessibilityCocoa();
3462   id obj = [root accessibilityHitTest:localPoint];
3463   return obj;
3466 - (BOOL)accessibilityIsIgnored {
3467   return !renderWidgetHostView_->GetBrowserAccessibilityManager();
3470 - (NSUInteger)accessibilityGetIndexOf:(id)child {
3471   BrowserAccessibilityManager* manager =
3472       renderWidgetHostView_->GetBrowserAccessibilityManager();
3473   // Only child is root.
3474   if (manager &&
3475       manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
3476     return 0;
3477   } else {
3478     return NSNotFound;
3479   }
3482 - (id)accessibilityFocusedUIElement {
3483   BrowserAccessibilityManager* manager =
3484       renderWidgetHostView_->GetBrowserAccessibilityManager();
3485   if (manager) {
3486     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
3487     DCHECK(focused_item);
3488     if (focused_item) {
3489       BrowserAccessibilityCocoa* focused_item_cocoa =
3490           focused_item->ToBrowserAccessibilityCocoa();
3491       DCHECK(focused_item_cocoa);
3492       if (focused_item_cocoa)
3493         return focused_item_cocoa;
3494     }
3495   }
3496   return [super accessibilityFocusedUIElement];
3499 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
3500 // with minor modifications for code style and commenting.
3502 //  The 'public' interface is -setToolTipAtMousePoint:. This differs from
3503 // -setToolTip: in that the updated tooltip takes effect immediately,
3504 //  without the user's having to move the mouse out of and back into the view.
3506 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
3507 // the view, which in turn requires overriding some internal tracking-rect
3508 // methods (to keep track of its owner & userdata, which need to be filled out
3509 // in the fake events.) --snej 7/6/09
3513  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3514  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
3516  * Redistribution and use in source and binary forms, with or without
3517  * modification, are permitted provided that the following conditions
3518  * are met:
3520  * 1.  Redistributions of source code must retain the above copyright
3521  *     notice, this list of conditions and the following disclaimer.
3522  * 2.  Redistributions in binary form must reproduce the above copyright
3523  *     notice, this list of conditions and the following disclaimer in the
3524  *     documentation and/or other materials provided with the distribution.
3525  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
3526  *     its contributors may be used to endorse or promote products derived
3527  *     from this software without specific prior written permission.
3529  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
3530  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3531  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3532  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
3533  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3534  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3535  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3536  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3537  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3538  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3539  */
3541 // Any non-zero value will do, but using something recognizable might help us
3542 // debug some day.
3543 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
3545 // Override of a public NSView method, replacing the inherited functionality.
3546 // See above for rationale.
3547 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
3548                                owner:(id)owner
3549                             userData:(void *)data
3550                         assumeInside:(BOOL)assumeInside {
3551   DCHECK(trackingRectOwner_ == nil);
3552   trackingRectOwner_ = owner;
3553   trackingRectUserData_ = data;
3554   return kTrackingRectTag;
3557 // Override of (apparently) a private NSView method(!) See above for rationale.
3558 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
3559                                 owner:(id)owner
3560                              userData:(void *)data
3561                          assumeInside:(BOOL)assumeInside
3562                        useTrackingNum:(int)tag {
3563   DCHECK(tag == 0 || tag == kTrackingRectTag);
3564   DCHECK(trackingRectOwner_ == nil);
3565   trackingRectOwner_ = owner;
3566   trackingRectUserData_ = data;
3567   return kTrackingRectTag;
3570 // Override of (apparently) a private NSView method(!) See above for rationale.
3571 - (void)_addTrackingRects:(NSRect *)rects
3572                     owner:(id)owner
3573              userDataList:(void **)userDataList
3574          assumeInsideList:(BOOL *)assumeInsideList
3575              trackingNums:(NSTrackingRectTag *)trackingNums
3576                     count:(int)count {
3577   DCHECK(count == 1);
3578   DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
3579   DCHECK(trackingRectOwner_ == nil);
3580   trackingRectOwner_ = owner;
3581   trackingRectUserData_ = userDataList[0];
3582   trackingNums[0] = kTrackingRectTag;
3585 // Override of a public NSView method, replacing the inherited functionality.
3586 // See above for rationale.
3587 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
3588   if (tag == 0)
3589     return;
3591   if (tag == kTrackingRectTag) {
3592     trackingRectOwner_ = nil;
3593     return;
3594   }
3596   if (tag == lastToolTipTag_) {
3597     [super removeTrackingRect:tag];
3598     lastToolTipTag_ = 0;
3599     return;
3600   }
3602   // If any other tracking rect is being removed, we don't know how it was
3603   // created and it's possible there's a leak involved (see Radar 3500217).
3604   NOTREACHED();
3607 // Override of (apparently) a private NSView method(!)
3608 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
3609   for (int i = 0; i < count; ++i) {
3610     int tag = tags[i];
3611     if (tag == 0)
3612       continue;
3613     DCHECK(tag == kTrackingRectTag);
3614     trackingRectOwner_ = nil;
3615   }
3618 // Sends a fake NSMouseExited event to the view for its current tracking rect.
3619 - (void)_sendToolTipMouseExited {
3620   // Nothing matters except window, trackingNumber, and userData.
3621   int windowNumber = [[self window] windowNumber];
3622   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
3623                                               location:NSZeroPoint
3624                                          modifierFlags:0
3625                                              timestamp:0
3626                                           windowNumber:windowNumber
3627                                                context:NULL
3628                                            eventNumber:0
3629                                         trackingNumber:kTrackingRectTag
3630                                               userData:trackingRectUserData_];
3631   [trackingRectOwner_ mouseExited:fakeEvent];
3634 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
3635 - (void)_sendToolTipMouseEntered {
3636   // Nothing matters except window, trackingNumber, and userData.
3637   int windowNumber = [[self window] windowNumber];
3638   NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
3639                                               location:NSZeroPoint
3640                                          modifierFlags:0
3641                                              timestamp:0
3642                                           windowNumber:windowNumber
3643                                                context:NULL
3644                                            eventNumber:0
3645                                         trackingNumber:kTrackingRectTag
3646                                               userData:trackingRectUserData_];
3647   [trackingRectOwner_ mouseEntered:fakeEvent];
3650 // Sets the view's current tooltip, to be displayed at the current mouse
3651 // location. (This does not make the tooltip appear -- as usual, it only
3652 // appears after a delay.) Pass null to remove the tooltip.
3653 - (void)setToolTipAtMousePoint:(NSString *)string {
3654   NSString *toolTip = [string length] == 0 ? nil : string;
3655   if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
3656       (!toolTip && !toolTip_)) {
3657     return;
3658   }
3660   if (toolTip_) {
3661     [self _sendToolTipMouseExited];
3662   }
3664   toolTip_.reset([toolTip copy]);
3666   if (toolTip) {
3667     // See radar 3500217 for why we remove all tooltips
3668     // rather than just the single one we created.
3669     [self removeAllToolTips];
3670     NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
3671     lastToolTipTag_ = [self addToolTipRect:wideOpenRect
3672                                      owner:self
3673                                   userData:NULL];
3674     [self _sendToolTipMouseEntered];
3675   }
3678 // NSView calls this to get the text when displaying the tooltip.
3679 - (NSString *)view:(NSView *)view
3680   stringForToolTip:(NSToolTipTag)tag
3681              point:(NSPoint)point
3682           userData:(void *)data {
3683   return [[toolTip_ copy] autorelease];
3686 // Below is our NSTextInputClient implementation.
3688 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
3689 // functions to process this event.
3691 // [WebHTMLView keyDown] ->
3692 //     EventHandler::keyEvent() ->
3693 //     ...
3694 //     [WebEditorClient handleKeyboardEvent] ->
3695 //     [WebHTMLView _interceptEditingKeyEvent] ->
3696 //     [NSResponder interpretKeyEvents] ->
3697 //     [WebHTMLView insertText] ->
3698 //     Editor::insertText()
3700 // Unfortunately, it is hard for Chromium to use this implementation because
3701 // it causes key-typing jank.
3702 // RenderWidgetHostViewMac is running in a browser process. On the other
3703 // hand, Editor and EventHandler are running in a renderer process.
3704 // So, if we used this implementation, a NSKeyDown event is dispatched to
3705 // the following functions of Chromium.
3707 // [RenderWidgetHostViewMac keyEvent] (browser) ->
3708 //     |Sync IPC (KeyDown)| (*1) ->
3709 //     EventHandler::keyEvent() (renderer) ->
3710 //     ...
3711 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
3712 //     |Sync IPC| (*2) ->
3713 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
3714 //     [self interpretKeyEvents] ->
3715 //     [RenderWidgetHostViewMac insertText] (browser) ->
3716 //     |Async IPC| ->
3717 //     Editor::insertText() (renderer)
3719 // (*1) we need to wait until this call finishes since WebHTMLView uses the
3720 // result of EventHandler::keyEvent().
3721 // (*2) we need to wait until this call finishes since WebEditorClient uses
3722 // the result of [WebHTMLView _interceptEditingKeyEvent].
3724 // This needs many sync IPC messages sent between a browser and a renderer for
3725 // each key event, which would probably result in key-typing jank.
3726 // To avoid this problem, this implementation processes key events (and input
3727 // method events) totally in a browser process and sends asynchronous input
3728 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
3729 // renderer process.
3731 // [RenderWidgetHostViewMac keyEvent] (browser) ->
3732 //     |Async IPC (RawKeyDown)| ->
3733 //     [self interpretKeyEvents] ->
3734 //     [RenderWidgetHostViewMac insertText] (browser) ->
3735 //     |Async IPC (Char)| ->
3736 //     Editor::insertText() (renderer)
3738 // Since this implementation doesn't have to wait any IPC calls, this doesn't
3739 // make any key-typing jank. --hbono 7/23/09
3741 extern "C" {
3742 extern NSString *NSTextInputReplacementRangeAttributeName;
3745 - (NSArray *)validAttributesForMarkedText {
3746   // This code is just copied from WebKit except renaming variables.
3747   if (!validAttributesForMarkedText_) {
3748     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
3749         NSUnderlineStyleAttributeName,
3750         NSUnderlineColorAttributeName,
3751         NSMarkedClauseSegmentAttributeName,
3752         NSTextInputReplacementRangeAttributeName,
3753         nil]);
3754   }
3755   return validAttributesForMarkedText_.get();
3758 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
3759   DCHECK([self window]);
3760   // |thePoint| is in screen coordinates, but needs to be converted to WebKit
3761   // coordinates (upper left origin). Scroll offsets will be taken care of in
3762   // the renderer.
3763   thePoint = [[self window] convertScreenToBase:thePoint];
3764   thePoint = [self convertPoint:thePoint fromView:nil];
3765   thePoint.y = NSHeight([self frame]) - thePoint.y;
3767   NSUInteger index =
3768       TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
3769           renderWidgetHostView_->render_widget_host_,
3770           gfx::Point(thePoint.x, thePoint.y));
3771   return index;
3774 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
3775                              actualRange:(NSRangePointer)actualRange {
3776   NSRect rect;
3777   if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
3778           theRange,
3779           &rect,
3780           actualRange)) {
3781     rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
3782         renderWidgetHostView_->render_widget_host_, theRange);
3784     // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
3785     if (actualRange)
3786       *actualRange = theRange;
3787   }
3789   // The returned rectangle is in WebKit coordinates (upper left origin), so
3790   // flip the coordinate system.
3791   NSRect viewFrame = [self frame];
3792   rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
3793   return rect;
3796 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
3797                          actualRange:(NSRangePointer)actualRange {
3798   NSRect rect = [self firstViewRectForCharacterRange:theRange
3799                                          actualRange:actualRange];
3801   // Convert into screen coordinates for return.
3802   rect = [self convertRect:rect toView:nil];
3803   rect.origin = [[self window] convertBaseToScreen:rect.origin];
3804   return rect;
3807 - (NSRange)markedRange {
3808   // An input method calls this method to check if an application really has
3809   // a text being composed when hasMarkedText call returns true.
3810   // Returns the range saved in the setMarkedText method so the input method
3811   // calls the setMarkedText method and we can update the composition node
3812   // there. (When this method returns an empty range, the input method doesn't
3813   // call the setMarkedText method.)
3814   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
3817 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
3818     actualRange:(NSRangePointer)actualRange {
3819   // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
3820   if (actualRange)
3821     *actualRange = range;
3822   NSAttributedString* str =
3823       TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
3824           renderWidgetHostView_->render_widget_host_, range);
3825   return str;
3828 - (NSInteger)conversationIdentifier {
3829   return reinterpret_cast<NSInteger>(self);
3832 // Each RenderWidgetHostViewCocoa has its own input context, but we return
3833 // nil when the caret is in non-editable content or password box to avoid
3834 // making input methods do their work.
3835 - (NSTextInputContext *)inputContext {
3836   if (focusedPluginIdentifier_ != -1)
3837     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
3839   switch(renderWidgetHostView_->text_input_type_) {
3840     case ui::TEXT_INPUT_TYPE_NONE:
3841     case ui::TEXT_INPUT_TYPE_PASSWORD:
3842       return nil;
3843     default:
3844       return [super inputContext];
3845   }
3848 - (BOOL)hasMarkedText {
3849   // An input method calls this function to figure out whether or not an
3850   // application is really composing a text. If it is composing, it calls
3851   // the markedRange method, and maybe calls the setMarkedText method.
3852   // It seems an input method usually calls this function when it is about to
3853   // cancel an ongoing composition. If an application has a non-empty marked
3854   // range, it calls the setMarkedText method to delete the range.
3855   return hasMarkedText_;
3858 - (void)unmarkText {
3859   // Delete the composition node of the renderer and finish an ongoing
3860   // composition.
3861   // It seems an input method calls the setMarkedText method and set an empty
3862   // text when it cancels an ongoing composition, i.e. I have never seen an
3863   // input method calls this method.
3864   hasMarkedText_ = NO;
3865   markedText_.clear();
3866   underlines_.clear();
3868   // If we are handling a key down event, then ConfirmComposition() will be
3869   // called in keyEvent: method.
3870   if (!handlingKeyDown_) {
3871     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3872         base::string16(), gfx::Range::InvalidRange(), false);
3873   } else {
3874     unmarkTextCalled_ = YES;
3875   }
3878 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
3879                               replacementRange:(NSRange)replacementRange {
3880   // An input method updates the composition string.
3881   // We send the given text and range to the renderer so it can update the
3882   // composition node of WebKit.
3883   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3884   // the full web content.
3885   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3886   NSString* im_text = isAttributedString ? [string string] : string;
3887   int length = [im_text length];
3889   // |markedRange_| will get set on a callback from ImeSetComposition().
3890   selectedRange_ = newSelRange;
3891   markedText_ = base::SysNSStringToUTF16(im_text);
3892   hasMarkedText_ = (length > 0);
3894   underlines_.clear();
3895   if (isAttributedString) {
3896     ExtractUnderlines(string, &underlines_);
3897   } else {
3898     // Use a thin black underline by default.
3899     underlines_.push_back(
3900         blink::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
3901   }
3903   // If we are handling a key down event, then SetComposition() will be
3904   // called in keyEvent: method.
3905   // Input methods of Mac use setMarkedText calls with an empty text to cancel
3906   // an ongoing composition. So, we should check whether or not the given text
3907   // is empty to update the input method state. (Our input method backend can
3908   // automatically cancels an ongoing composition when we send an empty text.
3909   // So, it is OK to send an empty text to the renderer.)
3910   if (!handlingKeyDown_) {
3911     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3912         markedText_, underlines_,
3913         newSelRange.location, NSMaxRange(newSelRange));
3914   }
3917 - (void)doCommandBySelector:(SEL)selector {
3918   // An input method calls this function to dispatch an editing command to be
3919   // handled by this view.
3920   if (selector == @selector(noop:))
3921     return;
3923   std::string command(
3924       [RenderWidgetHostViewMacEditCommandHelper::
3925           CommandNameForSelector(selector) UTF8String]);
3927   // If this method is called when handling a key down event, then we need to
3928   // handle the command in the key event handler. Otherwise we can just handle
3929   // it here.
3930   if (handlingKeyDown_) {
3931     hasEditCommands_ = YES;
3932     // We ignore commands that insert characters, because this was causing
3933     // strange behavior (e.g. tab always inserted a tab rather than moving to
3934     // the next field on the page).
3935     if (!StartsWithASCII(command, "insert", false))
3936       editCommands_.push_back(EditCommand(command, ""));
3937   } else {
3938     RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3939     rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3940                                               command, ""));
3941   }
3944 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3945   // An input method has characters to be inserted.
3946   // Same as Linux, Mac calls this method not only:
3947   // * when an input method finishs composing text, but also;
3948   // * when we type an ASCII character (without using input methods).
3949   // When we aren't using input methods, we should send the given character as
3950   // a Char event so it is dispatched to an onkeypress() event handler of
3951   // JavaScript.
3952   // On the other hand, when we are using input methods, we should send the
3953   // given characters as an input method event and prevent the characters from
3954   // being dispatched to onkeypress() event handlers.
3955   // Text inserting might be initiated by other source instead of keyboard
3956   // events, such as the Characters dialog. In this case the text should be
3957   // sent as an input method event as well.
3958   // TODO(suzhe): It's hard for us to support replacementRange without accessing
3959   // the full web content.
3960   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3961   NSString* im_text = isAttributedString ? [string string] : string;
3962   if (handlingKeyDown_) {
3963     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3964   } else {
3965     gfx::Range replacement_range(replacementRange);
3966     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3967         base::SysNSStringToUTF16(im_text), replacement_range, false);
3968   }
3970   // Inserting text will delete all marked text automatically.
3971   hasMarkedText_ = NO;
3974 - (void)insertText:(id)string {
3975   [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3978 - (void)viewDidMoveToWindow {
3979   if ([self window])
3980     [self updateScreenProperties];
3982   if (canBeKeyView_) {
3983     NSWindow* newWindow = [self window];
3984     // Pointer comparison only, since we don't know if lastWindow_ is still
3985     // valid.
3986     if (newWindow) {
3987       // If we move into a new window, refresh the frame information. We
3988       // don't need to do it if it was the same window as it used to be in,
3989       // since that case is covered by WasShown(). We only want to do this for
3990       // real browser views, not popups.
3991       if (newWindow != lastWindow_) {
3992         lastWindow_ = newWindow;
3993         renderWidgetHostView_->WindowFrameChanged();
3994       }
3995     }
3996   }
3998   // If we switch windows (or are removed from the view hierarchy), cancel any
3999   // open mouse-downs.
4000   if (hasOpenMouseDown_) {
4001     WebMouseEvent event;
4002     event.type = WebInputEvent::MouseUp;
4003     event.button = WebMouseEvent::ButtonLeft;
4004     renderWidgetHostView_->ForwardMouseEvent(event);
4006     hasOpenMouseDown_ = NO;
4007   }
4010 - (void)undo:(id)sender {
4011   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4012   if (web_contents)
4013     web_contents->Undo();
4016 - (void)redo:(id)sender {
4017   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4018   if (web_contents)
4019     web_contents->Redo();
4022 - (void)cut:(id)sender {
4023   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4024   if (web_contents)
4025     web_contents->Cut();
4028 - (void)copy:(id)sender {
4029   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4030   if (web_contents)
4031     web_contents->Copy();
4034 - (void)copyToFindPboard:(id)sender {
4035   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4036   if (web_contents)
4037     web_contents->CopyToFindPboard();
4040 - (void)paste:(id)sender {
4041   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4042   if (web_contents)
4043     web_contents->Paste();
4046 - (void)pasteAndMatchStyle:(id)sender {
4047   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4048   if (web_contents)
4049     web_contents->PasteAndMatchStyle();
4052 - (void)selectAll:(id)sender {
4053   // editCommand_helper_ adds implementations for most NSResponder methods
4054   // dynamically. But the renderer side only sends selection results back to
4055   // the browser if they were triggered by a keyboard event or went through
4056   // one of the Select methods on RWH. Since selectAll: is called from the
4057   // menu handler, neither is true.
4058   // Explicitly call SelectAll() here to make sure the renderer returns
4059   // selection results.
4060   WebContents* web_contents = renderWidgetHostView_->GetWebContents();
4061   if (web_contents)
4062     web_contents->SelectAll();
4065 - (void)startSpeaking:(id)sender {
4066   renderWidgetHostView_->SpeakSelection();
4069 - (void)stopSpeaking:(id)sender {
4070   renderWidgetHostView_->StopSpeaking();
4073 - (void)cancelComposition {
4074   if (!hasMarkedText_)
4075     return;
4077   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
4078   // doesn't call any NSTextInput functions, such as setMarkedText or
4079   // insertText. So, we need to send an IPC message to a renderer so it can
4080   // delete the composition node.
4081   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
4082   [currentInputManager markedTextAbandoned:self];
4084   hasMarkedText_ = NO;
4085   // Should not call [self unmarkText] here, because it'll send unnecessary
4086   // cancel composition IPC message to the renderer.
4089 - (void)confirmComposition {
4090   if (!hasMarkedText_)
4091     return;
4093   if (renderWidgetHostView_->render_widget_host_)
4094     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
4095         base::string16(), gfx::Range::InvalidRange(), false);
4097   [self cancelComposition];
4100 - (void)setPluginImeActive:(BOOL)active {
4101   if (active == pluginImeActive_)
4102     return;
4104   pluginImeActive_ = active;
4105   if (!active) {
4106     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
4107     renderWidgetHostView_->PluginImeCompositionCompleted(
4108         base::string16(), focusedPluginIdentifier_);
4109   }
4112 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
4113   if (focused)
4114     focusedPluginIdentifier_ = pluginId;
4115   else if (focusedPluginIdentifier_ == pluginId)
4116     focusedPluginIdentifier_ = -1;
4118   // Whenever plugin focus changes, plugin IME resets.
4119   [self setPluginImeActive:NO];
4122 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
4123   if (!pluginImeActive_)
4124     return false;
4126   ComplexTextInputPanel* inputPanel =
4127       [ComplexTextInputPanel sharedComplexTextInputPanel];
4128   NSString* composited_string = nil;
4129   BOOL handled = [inputPanel interpretKeyEvent:event
4130                                         string:&composited_string];
4131   if (composited_string) {
4132     renderWidgetHostView_->PluginImeCompositionCompleted(
4133         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
4134     pluginImeActive_ = NO;
4135   }
4136   return handled;
4139 - (void)checkForPluginImeCancellation {
4140   if (pluginImeActive_ &&
4141       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
4142     renderWidgetHostView_->PluginImeCompositionCompleted(
4143         base::string16(), focusedPluginIdentifier_);
4144     pluginImeActive_ = NO;
4145   }
4148 // Overriding a NSResponder method to support application services.
4150 - (id)validRequestorForSendType:(NSString*)sendType
4151                      returnType:(NSString*)returnType {
4152   id requestor = nil;
4153   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
4154   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
4155   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
4156   BOOL takesText =
4157       renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
4159   if (sendTypeIsString && hasText && !returnType) {
4160     requestor = self;
4161   } else if (!sendType && returnTypeIsString && takesText) {
4162     requestor = self;
4163   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
4164     requestor = self;
4165   } else {
4166     requestor = [super validRequestorForSendType:sendType
4167                                       returnType:returnType];
4168   }
4169   return requestor;
4172 - (void)viewWillStartLiveResize {
4173   [super viewWillStartLiveResize];
4174   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
4175   if (widget)
4176     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
4179 - (void)viewDidEndLiveResize {
4180   [super viewDidEndLiveResize];
4181   RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
4182   if (widget)
4183     widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
4186 - (void)updateCursor:(NSCursor*)cursor {
4187   if (currentCursor_ == cursor)
4188     return;
4190   currentCursor_.reset([cursor retain]);
4191   [[self window] invalidateCursorRectsForView:self];
4194 - (void)popupWindowWillClose:(NSNotification *)notification {
4195   renderWidgetHostView_->KillSelf();
4198 @end
4201 // Supporting application services
4203 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
4205 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
4206                              types:(NSArray*)types {
4207   const std::string& str = renderWidgetHostView_->selected_text();
4208   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
4210   base::scoped_nsobject<NSString> text(
4211       [[NSString alloc] initWithUTF8String:str.c_str()]);
4212   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
4213   [pboard declareTypes:toDeclare owner:nil];
4214   return [pboard setString:text forType:NSStringPboardType];
4217 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
4218   NSString *string = [pboard stringForType:NSStringPboardType];
4219   if (!string) return NO;
4221   // If the user is currently using an IME, confirm the IME input,
4222   // and then insert the text from the service, the same as TextEdit and Safari.
4223   [self confirmComposition];
4224   [self insertText:string];
4225   return YES;
4228 - (BOOL)isOpaque {
4229   if (renderWidgetHostView_->use_core_animation_)
4230     return YES;
4231   return [super isOpaque];
4234 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
4235 // regions that are not draggable. (See ControlRegionView in
4236 // native_app_window_cocoa.mm). This requires the render host view to be
4237 // draggable by default.
4238 - (BOOL)mouseDownCanMoveWindow {
4239   return YES;
4242 @end
4244 @implementation SoftwareLayer
4246 - (id)initWithRenderWidgetHostViewMac:(content::RenderWidgetHostViewMac*)r {
4247   if (self = [super init]) {
4248     renderWidgetHostView_ = r;
4250     [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
4251     [self setAnchorPoint:CGPointMake(0, 0)];
4252     // Setting contents gravity is necessary to prevent the layer from being
4253     // scaled during dyanmic resizes (especially with devtools open).
4254     [self setContentsGravity:kCAGravityTopLeft];
4255     if (renderWidgetHostView_->software_frame_manager_->HasCurrentFrame() &&
4256         [self respondsToSelector:(@selector(setContentsScale:))]) {
4257       [self setContentsScale:renderWidgetHostView_->software_frame_manager_->
4258           GetCurrentFrameDeviceScaleFactor()];
4259     }
4261     // Ensure that the transition between frames not be animated.
4262     [self setActions:@{ @"contents" : [NSNull null] }];
4263   }
4264   return self;
4267 - (void)drawInContext:(CGContextRef)context {
4268   TRACE_EVENT0("browser", "SoftwareLayer::drawInContext");
4270   CGRect clipRect = CGContextGetClipBoundingBox(context);
4271   if (renderWidgetHostView_) {
4272     [renderWidgetHostView_->cocoa_view() drawWithDirtyRect:clipRect
4273                                                  inContext:context];
4274   } else {
4275     CGContextSetFillColorWithColor(context,
4276                                    CGColorGetConstantColor(kCGColorWhite));
4277     CGContextFillRect(context, clipRect);
4278   }
4281 - (void)disableRendering {
4282   // Disable the fade-out animation as the layer is removed.
4283   ScopedCAActionDisabler disabler;
4284   [self removeFromSuperlayer];
4285   renderWidgetHostView_ = nil;
4288 @end  // implementation SoftwareLayer