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