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