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