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