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