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