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