1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
7 #import <objc/runtime.h>
9 #include <QuartzCore/QuartzCore.h>
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"
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;
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);
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;
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
124 static NSString* const NSWindowDidChangeBackingPropertiesNotification =
125 @"NSWindowDidChangeBackingPropertiesNotification";
129 // This method will return YES for OS X versions 10.7.3 and later, and NO
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:),
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;
153 @interface RenderWidgetHostViewCocoa ()
154 @property(nonatomic, assign) NSRange selectedRange;
155 @property(nonatomic, assign) NSRange markedRange;
157 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
158 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
159 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
160 consumed:(BOOL)consumed;
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;
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
177 @implementation PepperFlashFullscreenWindow
179 - (BOOL)canBecomeKeyWindow {
183 - (BOOL)canBecomeMainWindow {
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.
196 @implementation RenderWidgetPopupWindow
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]) {
207 [self setBackgroundColor:[NSColor clearColor]];
208 [self startObservingClicks];
214 [self stopObservingClicks];
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 {
224 // Install the callback.
225 - (void)startObservingClicks {
226 clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
227 handler:^NSEvent* (NSEvent* event) {
228 if ([event window] == self)
230 NSEventType eventType = [event type];
231 if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
236 NSNotificationCenter* notificationCenter =
237 [NSNotificationCenter defaultCenter];
238 [notificationCenter addObserver:self
239 selector:@selector(beganTracking:)
240 name:NSMenuDidBeginTrackingNotification
241 object:[NSApp mainMenu]];
244 // Remove the callback.
245 - (void)stopObservingClicks {
249 [NSEvent removeMonitor:clickEventTap_];
250 clickEventTap_ = nil;
252 NSNotificationCenter* notificationCenter =
253 [NSNotificationCenter defaultCenter];
254 [notificationCenter removeObserver:self
255 name:NSMenuDidBeginTrackingNotification
256 object:[NSApp mainMenu]];
263 // Maximum number of characters we allow in a tooltip.
264 const size_t kMaxTooltipLength = 1024;
266 // TODO(suzhe): Upstream this function.
267 blink::WebColor WebColorFromNSColor(NSColor *color) {
269 [color getRed:&r green:&g blue:&b alpha:&a];
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));
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];
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]);
297 underlines->push_back(
298 blink::WebCompositionUnderline(range.location,
301 [style intValue] > 1,
302 SK_ColorTRANSPARENT));
304 i = range.location + range.length;
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
314 void EnablePasswordInput() {
315 CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
316 TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
317 sizeof(CFArrayRef), &inputSources);
318 CFRelease(inputSources);
321 void DisablePasswordInput() {
322 TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
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
331 bool g_screen_info_up_to_date = false;
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) {
339 [[[NSScreen screens] objectAtIndex:0] frame].size.height;
340 g_screen_info_up_to_date = true;
345 return screen_zero_height - y - rect_height;
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
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()));
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];
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;
375 return enclosing_window;
378 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
379 gfx::Display display =
380 gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
382 NSScreen* screen = [NSScreen deepestScreen];
384 blink::WebScreenInfo results;
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);
404 ////////////////////////////////////////////////////////////////////////////////
405 // DelegatedFrameHost, public:
407 ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
408 if (browser_compositor_view_)
409 return browser_compositor_view_->GetCompositor();
413 ui::Layer* RenderWidgetHostViewMac::GetLayer() {
414 return root_layer_.get();
417 RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
418 return render_widget_host_;
421 bool RenderWidgetHostViewMac::IsVisible() {
422 return !render_widget_host_->is_hidden();
425 gfx::Size RenderWidgetHostViewMac::DesiredFrameSize() {
426 return GetViewBounds().size();
429 float RenderWidgetHostViewMac::CurrentDeviceScaleFactor() {
430 return ViewScaleFactor();
433 gfx::Size RenderWidgetHostViewMac::ConvertViewSizeToPixel(
434 const gfx::Size& size) {
435 return gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size),
436 ViewScaleFactor())).size();
439 scoped_ptr<ResizeLock> RenderWidgetHostViewMac::CreateResizeLock(
440 bool defer_compositor_lock) {
442 ResizeLock* lock = NULL;
443 return scoped_ptr<ResizeLock>(lock);
446 DelegatedFrameHost* RenderWidgetHostViewMac::GetDelegatedFrameHost() const {
447 return delegated_frame_host_.get();
450 ////////////////////////////////////////////////////////////////////////////////
451 // BrowserCompositorViewMacClient, public:
453 bool RenderWidgetHostViewMac::BrowserCompositorViewShouldAckImmediately()
455 // The logic for delegated and non-delegated rendering is the same.
456 return AcceleratedLayerShouldAckImmediately();
459 void RenderWidgetHostViewMac::BrowserCompositorViewFrameSwapped(
460 const std::vector<ui::LatencyInfo>& all_latency_info) {
461 if (!render_widget_host_)
463 for (auto latency_info : all_latency_info) {
464 latency_info.AddLatencyNumber(
465 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
466 render_widget_host_->FrameSwapped(latency_info);
470 NSView* RenderWidgetHostViewMac::BrowserCompositorSuperview() {
474 ui::Layer* RenderWidgetHostViewMac::BrowserCompositorRootLayer() {
475 return root_layer_.get();
478 ///////////////////////////////////////////////////////////////////////////////
479 // RenderWidgetHostViewBase, public:
482 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
483 blink::WebScreenInfo* results) {
484 *results = GetWebScreenInfo(NULL);
487 ///////////////////////////////////////////////////////////////////////////////
488 // RenderWidgetHostViewMac, public:
490 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
491 : render_widget_host_(RenderWidgetHostImpl::From(widget)),
492 text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
493 can_compose_inline_(true),
494 browser_compositor_view_placeholder_(
495 new BrowserCompositorViewPlaceholderMac),
497 allow_pause_for_resize_or_repaint_(true),
499 fullscreen_parent_host_view_(NULL) {
500 // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
501 // goes away. Since we autorelease it, our caller must put
502 // |GetNativeView()| into the view hierarchy right after calling us.
503 cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
504 initWithRenderWidgetHostViewMac:this] autorelease];
506 // Make this view host a solid white layer when there is no content ready to
508 background_layer_.reset([[CALayer alloc] init]);
510 setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
511 [cocoa_view_ setLayer:background_layer_];
512 [cocoa_view_ setWantsLayer:YES];
514 if (IsDelegatedRendererEnabled()) {
515 root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
516 delegated_frame_host_.reset(new DelegatedFrameHost(this));
519 gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this);
521 render_widget_host_->SetView(this);
524 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
525 gfx::Screen::GetScreenFor(cocoa_view_)->RemoveObserver(this);
527 // This is being called from |cocoa_view_|'s destructor, so invalidate the
533 // Ensure that the browser compositor is destroyed in a safe order.
534 ShutdownBrowserCompositor();
536 // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
537 // RenderWidgetHost does we need to tell it not to hold a stale pointer to
539 if (render_widget_host_)
540 render_widget_host_->SetView(NULL);
543 void RenderWidgetHostViewMac::SetDelegate(
544 NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
545 [cocoa_view_ setResponderDelegate:delegate];
548 void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
549 allow_pause_for_resize_or_repaint_ = allow;
552 ///////////////////////////////////////////////////////////////////////////////
553 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
555 void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
556 if (browser_compositor_view_)
559 TRACE_EVENT0("browser",
560 "RenderWidgetHostViewMac::EnsureBrowserCompositorView");
562 browser_compositor_view_.reset(new BrowserCompositorViewMac(this));
563 delegated_frame_host_->AddedToWindow();
564 delegated_frame_host_->WasShown(ui::LatencyInfo());
567 void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
568 TRACE_EVENT0("browser",
569 "RenderWidgetHostViewMac::DestroyBrowserCompositorView");
570 if (!browser_compositor_view_)
573 // Marking the DelegatedFrameHost as removed from the window hierarchy is
574 // necessary to remove all connections to its old ui::Compositor.
575 delegated_frame_host_->WasHidden();
576 delegated_frame_host_->RemovingFromWindow();
577 browser_compositor_view_.reset();
580 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
582 IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
583 IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
584 IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
585 IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted,
586 OnGetRenderedTextCompleted)
587 IPC_MESSAGE_UNHANDLED(handled = false)
588 IPC_END_MESSAGE_MAP()
592 void RenderWidgetHostViewMac::InitAsChild(
593 gfx::NativeView parent_view) {
596 void RenderWidgetHostViewMac::InitAsPopup(
597 RenderWidgetHostView* parent_host_view,
598 const gfx::Rect& pos) {
599 bool activatable = popup_type_ == blink::WebPopupTypeNone;
600 [cocoa_view_ setCloseOnDeactivate:YES];
601 [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
603 NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
604 origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
606 popup_window_.reset([[RenderWidgetPopupWindow alloc]
607 initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
608 pos.width(), pos.height())
609 styleMask:NSBorderlessWindowMask
610 backing:NSBackingStoreBuffered
612 [popup_window_ setLevel:NSPopUpMenuWindowLevel];
613 [popup_window_ setReleasedWhenClosed:NO];
614 [popup_window_ makeKeyAndOrderFront:nil];
615 [[popup_window_ contentView] addSubview:cocoa_view_];
616 [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
617 [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
618 [[NSNotificationCenter defaultCenter]
619 addObserver:cocoa_view_
620 selector:@selector(popupWindowWillClose:)
621 name:NSWindowWillCloseNotification
622 object:popup_window_];
625 // This function creates the fullscreen window and hides the dock and menubar if
626 // necessary. Note, this codepath is only used for pepper flash when
627 // pp::FlashFullScreen::SetFullscreen() is called. If
628 // pp::FullScreen::SetFullscreen() is called then the entire browser window
629 // will enter fullscreen instead.
630 void RenderWidgetHostViewMac::InitAsFullscreen(
631 RenderWidgetHostView* reference_host_view) {
632 fullscreen_parent_host_view_ =
633 static_cast<RenderWidgetHostViewMac*>(reference_host_view);
634 NSWindow* parent_window = nil;
635 if (reference_host_view)
636 parent_window = [reference_host_view->GetNativeView() window];
637 NSScreen* screen = [parent_window screen];
639 screen = [NSScreen mainScreen];
641 pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
642 initWithContentRect:[screen frame]
643 styleMask:NSBorderlessWindowMask
644 backing:NSBackingStoreBuffered
646 [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
647 [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
648 [cocoa_view_ setCanBeKeyView:YES];
649 [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
650 [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
651 // If the pepper fullscreen window isn't opaque then there are performance
652 // issues when it's on the discrete GPU and the Chrome window is being drawn
653 // to. http://crbug.com/171911
654 [pepper_fullscreen_window_ setOpaque:YES];
656 // Note that this forms a reference cycle between the fullscreen window and
657 // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
658 // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
659 // This cycle is normally broken when -keyEvent: receives an <esc> key, which
660 // explicitly calls Shutdown on the render_widget_host_, which calls
661 // Destroy() on RWHVMac, which drops the reference to
662 // pepper_fullscreen_window_.
663 [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
665 // Note that this keeps another reference to pepper_fullscreen_window_.
666 fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
667 initWithWindow:pepper_fullscreen_window_.get()
668 desiredScreen:screen]);
669 [fullscreen_window_manager_ enterFullscreenMode];
670 [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
673 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
674 // See comment in InitAsFullscreen(): There is a reference cycle between
675 // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
676 // Tests that test pepper fullscreen mode without sending an <esc> event
677 // need to call this method to break the reference cycle.
678 [fullscreen_window_manager_ exitFullscreenMode];
679 fullscreen_window_manager_.reset();
680 [pepper_fullscreen_window_ close];
681 pepper_fullscreen_window_.reset();
684 int RenderWidgetHostViewMac::window_number() const {
685 NSWindow* window = [cocoa_view_ window];
688 return [window windowNumber];
691 float RenderWidgetHostViewMac::ViewScaleFactor() const {
692 return ui::GetScaleFactorForNativeView(cocoa_view_);
695 void RenderWidgetHostViewMac::UpdateDisplayLink() {
696 static bool is_vsync_disabled =
697 base::CommandLine::ForCurrentProcess()->HasSwitch(
698 switches::kDisableGpuVsync);
699 if (is_vsync_disabled)
702 NSScreen* screen = [[cocoa_view_ window] screen];
703 NSDictionary* screen_description = [screen deviceDescription];
704 NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
705 CGDirectDisplayID display_id = [screen_number unsignedIntValue];
707 display_link_ = DisplayLinkMac::GetForDisplay(display_id);
708 if (!display_link_) {
709 // Note that on some headless systems, the display link will fail to be
710 // created, so this should not be a fatal error.
711 LOG(ERROR) << "Failed to create display link.";
715 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
716 if (!render_widget_host_ || !display_link_)
719 if (!display_link_->GetVSyncParameters(&vsync_timebase_, &vsync_interval_)) {
720 vsync_timebase_ = base::TimeTicks();
721 vsync_interval_ = base::TimeDelta();
725 render_widget_host_->UpdateVSyncParameters(vsync_timebase_, vsync_interval_);
728 void RenderWidgetHostViewMac::SpeakText(const std::string& text) {
729 [NSApp speakString:base::SysUTF8ToNSString(text)];
732 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
733 if (!render_widget_host_)
735 render_widget_host_->NotifyScreenInfoChanged();
738 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
739 return render_widget_host_;
742 void RenderWidgetHostViewMac::WasShown() {
743 if (!render_widget_host_->is_hidden())
746 ui::LatencyInfo renderer_latency_info;
747 renderer_latency_info.AddLatencyNumber(
748 ui::TAB_SHOW_COMPONENT,
749 render_widget_host_->GetLatencyComponentId(),
751 render_widget_host_->WasShown(renderer_latency_info);
753 // If there is not a frame being currently drawn, kick one, so that the below
754 // pause will have a frame to wait on.
755 render_widget_host_->ScheduleComposite();
756 PauseForPendingResizeOrRepaintsAndDraw();
759 void RenderWidgetHostViewMac::WasHidden() {
760 if (render_widget_host_->is_hidden())
763 DestroyBrowserCompositorView();
765 // If we have a renderer, then inform it that we are being hidden so it can
766 // reduce its resource utilization.
767 render_widget_host_->WasHidden();
770 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
771 gfx::Rect rect = GetViewBounds();
776 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
777 // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
778 // TODO(thakis): fix, http://crbug.com/73362
779 if (render_widget_host_->is_hidden())
782 // During the initial creation of the RenderWidgetHostView in
783 // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
784 // an empty size. In the Windows code flow, it is not ignored because
785 // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
786 // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
787 // flags to keep things sized properly. On the other hand, if the size is not
788 // empty then this is a valid request for a pop-up.
789 if (rect.size().IsEmpty())
792 // Ignore the position of |rect| for non-popup rwhvs. This is because
793 // background tabs do not have a window, but the window is required for the
794 // coordinate conversions. Popups are always for a visible tab.
796 // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
797 // valid for resizing to be requested (e.g., during tab capture, to size the
798 // view to screen-capture resolution). In this case, simply treat the view as
799 // relative to the screen.
800 BOOL isRelativeToScreen = IsPopup() ||
801 ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
802 if (isRelativeToScreen) {
803 // The position of |rect| is screen coordinate system and we have to
804 // consider Cocoa coordinate system is upside-down and also multi-screen.
805 NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
806 NSSize size = NSMakeSize(rect.width(), rect.height());
807 size = [cocoa_view_ convertSize:size toView:nil];
808 origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
809 NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
810 size.width, size.height);
812 [popup_window_ setFrame:frame display:YES];
814 [cocoa_view_ setFrame:frame];
816 BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
817 gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
818 rect2.set_width(rect.width());
819 rect2.set_height(rect.height());
820 [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
824 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
828 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
829 return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
832 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
834 return static_cast<gfx::NativeViewAccessible>(NULL);
837 void RenderWidgetHostViewMac::MovePluginWindows(
838 const std::vector<WebPluginGeometry>& moves) {
839 // Must be overridden, but unused on this platform. Core Animation
840 // plugins are drawn by the GPU process (through the compositor),
841 // and Core Graphics plugins are drawn by the renderer process.
842 DCHECK_CURRENTLY_ON(BrowserThread::UI);
845 void RenderWidgetHostViewMac::Focus() {
846 [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
849 void RenderWidgetHostViewMac::Blur() {
851 [[cocoa_view_ window] makeFirstResponder:nil];
854 bool RenderWidgetHostViewMac::HasFocus() const {
855 return [[cocoa_view_ window] firstResponder] == cocoa_view_;
858 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
859 if (delegated_frame_host_)
860 return delegated_frame_host_->CanCopyToBitmap();
864 void RenderWidgetHostViewMac::Show() {
865 [cocoa_view_ setHidden:NO];
870 void RenderWidgetHostViewMac::Hide() {
871 [cocoa_view_ setHidden:YES];
876 bool RenderWidgetHostViewMac::IsShowing() {
877 return ![cocoa_view_ isHidden];
880 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
881 NSRect bounds = [cocoa_view_ bounds];
882 // TODO(shess): In case of !window, the view has been removed from
883 // the view hierarchy because the tab isn't main. Could retrieve
884 // the information from the main tab for our window.
885 NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
886 if (!enclosing_window)
887 return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
889 bounds = [cocoa_view_ convertRect:bounds toView:nil];
890 bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
891 return FlipNSRectToRectScreen(bounds);
894 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
895 WebCursor web_cursor = cursor;
896 [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
899 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
900 is_loading_ = is_loading;
901 // If we ever decide to show the waiting cursor while the page is loading
902 // like Chrome does on Windows, call |UpdateCursor()| here.
905 void RenderWidgetHostViewMac::TextInputStateChanged(
906 const ViewHostMsg_TextInputState_Params& params) {
907 if (text_input_type_ != params.type ||
908 can_compose_inline_ != params.can_compose_inline) {
909 text_input_type_ = params.type;
910 can_compose_inline_ = params.can_compose_inline;
912 SetTextInputActive(true);
914 // Let AppKit cache the new input context to make IMEs happy.
915 // See http://crbug.com/73039.
916 [NSApp updateWindows];
919 UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
925 void RenderWidgetHostViewMac::ImeCancelComposition() {
926 [cocoa_view_ cancelComposition];
929 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
930 const gfx::Range& range,
931 const std::vector<gfx::Rect>& character_bounds) {
932 // The RangeChanged message is only sent with valid values. The current
933 // caret position (start == end) will be sent if there is no IME range.
934 [cocoa_view_ setMarkedRange:range.ToNSRange()];
935 composition_range_ = range;
936 composition_bounds_ = character_bounds;
939 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
944 void RenderWidgetHostViewMac::Destroy() {
945 [[NSNotificationCenter defaultCenter]
946 removeObserver:cocoa_view_
947 name:NSWindowWillCloseNotification
948 object:popup_window_];
950 // We've been told to destroy.
951 [cocoa_view_ retain];
952 [cocoa_view_ removeFromSuperview];
953 [cocoa_view_ autorelease];
955 [popup_window_ close];
956 popup_window_.autorelease();
958 [fullscreen_window_manager_ exitFullscreenMode];
959 fullscreen_window_manager_.reset();
960 [pepper_fullscreen_window_ close];
962 // This can be called as part of processing the window's responder
963 // chain, for instance |-performKeyEquivalent:|. In that case the
964 // object needs to survive until the stack unwinds.
965 pepper_fullscreen_window_.autorelease();
967 // Delete the delegated frame state, which will reach back into
968 // render_widget_host_.
969 ShutdownBrowserCompositor();
971 // We get this call just before |render_widget_host_| deletes
972 // itself. But we are owned by |cocoa_view_|, which may be retained
973 // by some other code. Examples are WebContentsViewMac's
974 // |latent_focus_view_| and TabWindowController's
975 // |cachedContentView_|.
976 render_widget_host_ = NULL;
979 // Called from the renderer to tell us what the tooltip text should be. It
980 // calls us frequently so we need to cache the value to prevent doing a lot
982 void RenderWidgetHostViewMac::SetTooltipText(
983 const base::string16& tooltip_text) {
984 if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
985 tooltip_text_ = tooltip_text;
987 // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
988 // Windows; we're just trying to be polite. Don't persist the trimmed
989 // string, as then the comparison above will always fail and we'll try to
990 // set it again every single time the mouse moves.
991 base::string16 display_text = tooltip_text_;
992 if (tooltip_text_.length() > kMaxTooltipLength)
993 display_text = tooltip_text_.substr(0, kMaxTooltipLength);
995 NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
996 [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1000 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1001 return [NSApp respondsToSelector:@selector(speakString:)] &&
1002 [NSApp respondsToSelector:@selector(stopSpeaking:)];
1005 void RenderWidgetHostViewMac::SpeakSelection() {
1006 if (![NSApp respondsToSelector:@selector(speakString:)])
1009 if (selected_text_.empty() && render_widget_host_) {
1010 // If there's no selection, speak all text. Send an asynchronous IPC
1011 // request for fetching all the text for a webcontent.
1012 // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1013 render_widget_host_->Send(new ViewMsg_GetRenderedText(
1014 render_widget_host_->GetRoutingID()));
1018 SpeakText(selected_text_);
1021 bool RenderWidgetHostViewMac::IsSpeaking() const {
1022 return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1026 void RenderWidgetHostViewMac::StopSpeaking() {
1027 if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1028 [NSApp stopSpeaking:cocoa_view_];
1032 // RenderWidgetHostViewCocoa uses the stored selection text,
1033 // which implements NSServicesRequests protocol.
1035 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1037 const gfx::Range& range) {
1038 if (range.is_empty() || text.empty()) {
1039 selected_text_.clear();
1041 size_t pos = range.GetMin() - offset;
1042 size_t n = range.length();
1044 DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1045 if (pos >= text.length()) {
1046 DCHECK(false) << "The text can not cover range.";
1049 selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1052 [cocoa_view_ setSelectedRange:range.ToNSRange()];
1053 // Updates markedRange when there is no marked text so that retrieving
1054 // markedRange immediately after calling setMarkdText: returns the current
1056 if (![cocoa_view_ hasMarkedText]) {
1057 [cocoa_view_ setMarkedRange:range.ToNSRange()];
1060 RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1063 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1064 const ViewHostMsg_SelectionBounds_Params& params) {
1065 if (params.anchor_rect == params.focus_rect)
1066 caret_rect_ = params.anchor_rect;
1069 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1070 RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1072 // Create a fake mouse event to inform the render widget that the mouse
1074 NSWindow* window = [cocoa_view_ window];
1075 // TODO(asvitkine): If the location outside of the event stream doesn't
1076 // correspond to the current event (due to delayed event processing), then
1077 // this may result in a cursor flicker if there are later mouse move events
1078 // in the pipeline. Find a way to use the mouse location from the event that
1079 // dismissed the context menu.
1080 NSPoint location = [window mouseLocationOutsideOfEventStream];
1081 NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1085 windowNumber:window_number()
1090 WebMouseEvent web_event =
1091 WebInputEventFactory::mouseEvent(event, cocoa_view_);
1093 web_event.type = WebInputEvent::MouseLeave;
1094 ForwardMouseEvent(web_event);
1097 bool RenderWidgetHostViewMac::IsPopup() const {
1098 return popup_type_ != blink::WebPopupTypeNone;
1101 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1102 const gfx::Rect& src_subrect,
1103 const gfx::Size& dst_size,
1104 const base::Callback<void(bool, const SkBitmap&)>& callback,
1105 const SkColorType color_type) {
1106 if (delegated_frame_host_) {
1107 delegated_frame_host_->CopyFromCompositingSurface(
1108 src_subrect, dst_size, callback, color_type);
1112 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1113 const gfx::Rect& src_subrect,
1114 const scoped_refptr<media::VideoFrame>& target,
1115 const base::Callback<void(bool)>& callback) {
1116 if (delegated_frame_host_) {
1117 delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1118 src_subrect, target, callback);
1122 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1123 if (delegated_frame_host_)
1124 return delegated_frame_host_->CanCopyToVideoFrame();
1128 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1129 if (delegated_frame_host_)
1130 return delegated_frame_host_->CanSubscribeFrame();
1134 void RenderWidgetHostViewMac::BeginFrameSubscription(
1135 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1136 if (delegated_frame_host_)
1137 delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1140 void RenderWidgetHostViewMac::EndFrameSubscription() {
1141 if (delegated_frame_host_)
1142 delegated_frame_host_->EndFrameSubscription();
1145 // Sets whether or not to accept first responder status.
1146 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
1147 [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
1150 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1151 if (render_widget_host_)
1152 render_widget_host_->ForwardMouseEvent(event);
1154 if (event.type == WebInputEvent::MouseLeave) {
1155 [cocoa_view_ setToolTipAtMousePoint:nil];
1156 tooltip_text_.clear();
1160 void RenderWidgetHostViewMac::KillSelf() {
1161 if (!weak_factory_.HasWeakPtrs()) {
1162 [cocoa_view_ setHidden:YES];
1163 base::MessageLoop::current()->PostTask(FROM_HERE,
1164 base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1165 weak_factory_.GetWeakPtr()));
1169 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1170 const NativeWebKeyboardEvent& event) {
1171 // Check WebInputEvent type since multiple types of events can be sent into
1172 // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1173 // necessary to avoid double processing.
1174 // Also check the native type, since NSFlagsChanged is considered a key event
1175 // for WebKit purposes, but isn't considered a key event by the OS.
1176 if (event.type == WebInputEvent::RawKeyDown &&
1177 [event.os_event type] == NSKeyDown)
1178 return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1182 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1183 const base::string16& text, int plugin_id) {
1184 if (render_widget_host_) {
1185 render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1186 render_widget_host_->GetRoutingID(), text, plugin_id));
1190 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1191 const std::vector<gfx::Rect>& bounds,
1192 const gfx::Range& range,
1193 size_t* line_break_point) {
1194 DCHECK(line_break_point);
1195 if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1198 // We can't check line breaking completely from only rectangle array. Thus we
1199 // assume the line breaking as the next character's y offset is larger than
1200 // a threshold. Currently the threshold is determined as minimum y offset plus
1201 // 75% of maximum height.
1202 // TODO(nona): Check the threshold is reliable or not.
1203 // TODO(nona): Bidi support.
1204 const size_t loop_end_idx = std::min(bounds.size(), range.end());
1206 int min_y_offset = kint32max;
1207 for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1208 max_height = std::max(max_height, bounds[idx].height());
1209 min_y_offset = std::min(min_y_offset, bounds[idx].y());
1211 int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1212 for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1213 if (bounds[idx].y() > line_break_threshold) {
1214 *line_break_point = idx;
1221 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1222 const gfx::Range& range,
1223 gfx::Range* actual_range) {
1224 DCHECK(actual_range);
1225 DCHECK(!composition_bounds_.empty());
1226 DCHECK(range.start() <= composition_bounds_.size());
1227 DCHECK(range.end() <= composition_bounds_.size());
1229 if (range.is_empty()) {
1230 *actual_range = range;
1231 if (range.start() == composition_bounds_.size()) {
1232 return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1233 composition_bounds_[range.start() - 1].y(),
1235 composition_bounds_[range.start() - 1].height());
1237 return gfx::Rect(composition_bounds_[range.start()].x(),
1238 composition_bounds_[range.start()].y(),
1240 composition_bounds_[range.start()].height());
1245 if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1246 end_idx = range.end();
1248 *actual_range = gfx::Range(range.start(), end_idx);
1249 gfx::Rect rect = composition_bounds_[range.start()];
1250 for (size_t i = range.start() + 1; i < end_idx; ++i) {
1251 rect.Union(composition_bounds_[i]);
1256 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1257 const gfx::Range& request_range) {
1258 if (composition_range_.is_empty())
1259 return gfx::Range::InvalidRange();
1261 if (request_range.is_reversed())
1262 return gfx::Range::InvalidRange();
1264 if (request_range.start() < composition_range_.start() ||
1265 request_range.start() > composition_range_.end() ||
1266 request_range.end() > composition_range_.end()) {
1267 return gfx::Range::InvalidRange();
1271 request_range.start() - composition_range_.start(),
1272 request_range.end() - composition_range_.start());
1275 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1276 if (!render_widget_host_->IsRenderView())
1279 return WebContents::FromRenderViewHost(
1280 RenderViewHost::From(render_widget_host_));
1283 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1286 NSRange* actual_range) {
1288 // This exists to make IMEs more responsive, see http://crbug.com/115920
1289 TRACE_EVENT0("browser",
1290 "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1292 // If requested range is same as caret location, we can just return it.
1293 if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1295 *actual_range = range;
1296 *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1300 const gfx::Range request_range_in_composition =
1301 ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1302 if (request_range_in_composition == gfx::Range::InvalidRange())
1305 // If firstRectForCharacterRange in WebFrame is failed in renderer,
1306 // ImeCompositionRangeChanged will be sent with empty vector.
1307 if (composition_bounds_.empty())
1309 DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1311 gfx::Range ui_actual_range;
1312 *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1313 request_range_in_composition,
1314 &ui_actual_range).ToCGRect());
1316 *actual_range = gfx::Range(
1317 composition_range_.start() + ui_actual_range.start(),
1318 composition_range_.start() + ui_actual_range.end()).ToNSRange();
1323 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
1324 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1328 void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
1329 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1333 void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
1336 void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
1339 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1340 const gfx::Size& desired_size) {
1341 if (browser_compositor_view_)
1342 return browser_compositor_view_->HasFrameOfSize(desired_size);
1346 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1347 uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1348 TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1350 if (frame->delegated_frame_data) {
1351 float scale_factor = frame->metadata.device_scale_factor;
1353 // Compute the frame size based on the root render pass rect size.
1354 cc::RenderPass* root_pass =
1355 frame->delegated_frame_data->render_pass_list.back();
1356 gfx::Size pixel_size = root_pass->output_rect.size();
1357 gfx::Size dip_size =
1358 ConvertSizeToDIP(scale_factor, pixel_size);
1360 root_layer_->SetBounds(gfx::Rect(dip_size));
1361 if (!render_widget_host_->is_hidden()) {
1362 EnsureBrowserCompositorView();
1363 browser_compositor_view_->GetCompositor()->SetScaleAndSize(
1364 scale_factor, pixel_size);
1367 SendVSyncParametersToRenderer();
1369 delegated_frame_host_->SwapDelegatedFrame(
1371 frame->delegated_frame_data.Pass(),
1372 frame->metadata.device_scale_factor,
1373 frame->metadata.latency_info);
1375 DLOG(ERROR) << "Received unexpected frame type.";
1377 base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1378 render_widget_host_->GetProcess()->ReceivedBadMessage();
1382 void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
1386 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1387 *results = GetWebScreenInfo(GetNativeView());
1390 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1391 // TODO(shess): In case of !window, the view has been removed from
1392 // the view hierarchy because the tab isn't main. Could retrieve
1393 // the information from the main tab for our window.
1394 NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1395 if (!enclosing_window)
1398 NSRect bounds = [enclosing_window frame];
1399 return FlipNSRectToRectScreen(bounds);
1402 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1403 // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1404 // completely on Mac OS.
1405 return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
1408 bool RenderWidgetHostViewMac::LockMouse() {
1412 mouse_locked_ = true;
1414 // Lock position of mouse cursor and hide it.
1415 CGAssociateMouseAndMouseCursorPosition(NO);
1418 // Clear the tooltip window.
1419 SetTooltipText(base::string16());
1424 void RenderWidgetHostViewMac::UnlockMouse() {
1427 mouse_locked_ = false;
1429 // Unlock position of mouse cursor and unhide it.
1430 CGAssociateMouseAndMouseCursorPosition(YES);
1433 if (render_widget_host_)
1434 render_widget_host_->LostMouseLock();
1437 void RenderWidgetHostViewMac::WheelEventAck(
1438 const blink::WebMouseWheelEvent& event,
1439 InputEventAckState ack_result) {
1440 bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1441 // Only record a wheel event as unhandled if JavaScript handlers got a chance
1442 // to see it (no-op wheel events are ignored by the event dispatcher)
1443 if (event.deltaX || event.deltaY)
1444 [cocoa_view_ processedWheelEvent:event consumed:consumed];
1447 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1448 if (render_widget_host_)
1449 return render_widget_host_->Send(message);
1454 void RenderWidgetHostViewMac::ShutdownHost() {
1455 weak_factory_.InvalidateWeakPtrs();
1456 render_widget_host_->Shutdown();
1457 // Do not touch any members at this point, |this| has been deleted.
1460 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1461 DestroyBrowserCompositorView();
1462 delegated_frame_host_.reset();
1463 root_layer_.reset();
1464 browser_compositor_view_placeholder_.reset();
1467 void RenderWidgetHostViewMac::SetActive(bool active) {
1468 if (render_widget_host_) {
1469 render_widget_host_->SetActive(active);
1472 render_widget_host_->Focus();
1474 render_widget_host_->Blur();
1478 SetTextInputActive(active);
1480 [cocoa_view_ setPluginImeActive:NO];
1485 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1486 if (render_widget_host_) {
1487 render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1488 render_widget_host_->GetRoutingID(), visible));
1492 void RenderWidgetHostViewMac::WindowFrameChanged() {
1493 if (render_widget_host_) {
1494 render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1495 render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1500 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1501 RenderWidgetHostViewMacDictionaryHelper helper(this);
1502 helper.ShowDefinitionForSelection();
1505 void RenderWidgetHostViewMac::SetBackgroundOpaque(bool opaque) {
1506 RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
1507 if (render_widget_host_)
1508 render_widget_host_->SetBackgroundOpaque(opaque);
1511 BrowserAccessibilityManager*
1512 RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1513 BrowserAccessibilityDelegate* delegate) {
1514 return new BrowserAccessibilityManagerMac(
1516 BrowserAccessibilityManagerMac::GetEmptyDocument(),
1520 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1521 const gfx::Rect& bounds) {
1522 NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1523 NSSize size = NSMakeSize(bounds.width(), bounds.height());
1524 origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1525 NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1526 NSPoint originInScreen =
1527 [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1528 originInScreen.y = originInScreen.y - size.height;
1529 return gfx::Point(originInScreen.x, originInScreen.y);
1532 void RenderWidgetHostViewMac::AccessibilityShowMenu(const gfx::Point& point) {
1533 NSPoint location = NSMakePoint(point.x(), point.y());
1534 location = [[cocoa_view_ window] convertScreenToBase:location];
1535 NSEvent* fakeRightClick = [NSEvent
1536 mouseEventWithType:NSRightMouseDown
1540 windowNumber:[[cocoa_view_ window] windowNumber]
1541 context:[NSGraphicsContext currentContext]
1546 [cocoa_view_ mouseEvent:fakeRightClick];
1549 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1551 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1552 EnablePasswordInput();
1554 DisablePasswordInput();
1556 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1557 DisablePasswordInput();
1561 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1563 [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1566 void RenderWidgetHostViewMac::OnStartPluginIme() {
1567 [cocoa_view_ setPluginImeActive:YES];
1570 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1571 const std::string& text) {
1575 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1576 if (!render_widget_host_ || render_widget_host_->is_hidden())
1579 // Pausing for one view prevents others from receiving frames.
1580 // This may lead to large delays, causing overlaps. See crbug.com/352020.
1581 if (!allow_pause_for_resize_or_repaint_)
1584 // Wait for a frame of the right size to come in.
1585 if (browser_compositor_view_)
1586 browser_compositor_view_->BeginPumpingFrames();
1587 render_widget_host_->PauseForPendingResizeOrRepaints();
1588 if (browser_compositor_view_)
1589 browser_compositor_view_->EndPumpingFrames();
1592 SkColorType RenderWidgetHostViewMac::PreferredReadbackFormat() {
1593 return kN32_SkColorType;
1596 ////////////////////////////////////////////////////////////////////////////////
1597 // CompositingIOSurfaceLayerClient, public:
1599 bool RenderWidgetHostViewMac::AcceleratedLayerShouldAckImmediately() const {
1600 // If vsync is disabled, then always draw and ack frames immediately.
1601 static bool is_vsync_disabled =
1602 base::CommandLine::ForCurrentProcess()->HasSwitch(
1603 switches::kDisableGpuVsync);
1604 if (is_vsync_disabled)
1607 // If the window is occluded, then this frame's display call may be severely
1608 // throttled. This is a good thing, unless tab capture may be active, because
1609 // the broadcast will be inappropriately throttled.
1610 // http://crbug.com/350410
1612 // If tab capture isn't active then only ack frames when we draw them.
1613 if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber())
1616 NSWindow* window = [cocoa_view_ window];
1617 // If the view isn't even in the heirarchy then frames will never be drawn,
1618 // so ack them immediately.
1622 // Check the window occlusion API.
1623 if ([window respondsToSelector:@selector(occlusionState)]) {
1624 if ([window occlusionState] & NSWindowOcclusionStateVisible) {
1625 // If the window is visible then it is safe to wait until frames are
1626 // drawn to ack them.
1629 // If the window is occluded then frames may never be drawn, so ack them
1635 // If the window occlusion API is not present then ack frames when we draw
1640 void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame() {
1643 void RenderWidgetHostViewMac::AcceleratedLayerHitError() {
1646 ////////////////////////////////////////////////////////////////////////////////
1647 // gfx::DisplayObserver, public:
1649 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1652 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1655 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1656 const gfx::Display& display, uint32_t metrics) {
1657 gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1658 if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1661 UpdateScreenInfo(cocoa_view_);
1664 } // namespace content
1666 // RenderWidgetHostViewCocoa ---------------------------------------------------
1668 @implementation RenderWidgetHostViewCocoa
1669 @synthesize selectedRange = selectedRange_;
1670 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1671 @synthesize markedRange = markedRange_;
1673 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1674 self = [super initWithFrame:NSZeroRect];
1676 self.acceptsTouchEvents = YES;
1677 editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1678 editCommand_helper_->AddEditingSelectorsToClass([self class]);
1680 renderWidgetHostView_.reset(r);
1681 canBeKeyView_ = YES;
1682 focusedPluginIdentifier_ = -1;
1685 if ([self respondsToSelector:
1686 @selector(setWantsBestResolutionOpenGLSurface:)]) {
1687 [self setWantsBestResolutionOpenGLSurface:YES];
1689 [[NSNotificationCenter defaultCenter]
1691 selector:@selector(didChangeScreenParameters:)
1692 name:NSApplicationDidChangeScreenParametersNotification
1699 // Unbind the GL context from this view. If this is not done before super's
1700 // dealloc is called then the GL context will crash when it reaches into
1701 // the view in its destructor.
1702 // http://crbug.com/255608
1703 if (renderWidgetHostView_)
1704 renderWidgetHostView_->AcceleratedSurfaceRelease();
1706 if (responderDelegate_ &&
1707 [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1708 [responderDelegate_ viewGone:self];
1709 responderDelegate_.reset();
1711 [[NSNotificationCenter defaultCenter] removeObserver:self];
1716 - (void)didChangeScreenParameters:(NSNotification*)notify {
1717 g_screen_info_up_to_date = false;
1720 - (void)setResponderDelegate:
1721 (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1722 DCHECK(!responderDelegate_);
1723 responderDelegate_.reset([delegate retain]);
1726 - (void)resetCursorRects {
1727 if (currentCursor_) {
1728 [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1729 [currentCursor_ setOnMouseEntered:YES];
1733 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1734 consumed:(BOOL)consumed {
1735 [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1738 - (BOOL)respondsToSelector:(SEL)selector {
1739 // Trickiness: this doesn't mean "does this object's superclass respond to
1740 // this selector" but rather "does the -respondsToSelector impl from the
1741 // superclass say that this class responds to the selector".
1742 if ([super respondsToSelector:selector])
1745 if (responderDelegate_)
1746 return [responderDelegate_ respondsToSelector:selector];
1751 - (id)forwardingTargetForSelector:(SEL)selector {
1752 if ([responderDelegate_ respondsToSelector:selector])
1753 return responderDelegate_.get();
1755 return [super forwardingTargetForSelector:selector];
1758 - (void)setCanBeKeyView:(BOOL)can {
1759 canBeKeyView_ = can;
1762 - (BOOL)acceptsMouseEventsWhenInactive {
1763 // Some types of windows (balloons, always-on-top panels) want to accept mouse
1764 // clicks w/o the first click being treated as 'activation'. Same applies to
1765 // mouse move events.
1766 return [[self window] level] > NSNormalWindowLevel;
1769 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1770 return [self acceptsMouseEventsWhenInactive];
1773 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
1774 takesFocusOnlyOnMouseDown_ = b;
1777 - (void)setCloseOnDeactivate:(BOOL)b {
1778 closeOnDeactivate_ = b;
1781 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1782 NSWindow* window = [self window];
1783 // If this is a background window, don't handle mouse movement events. This
1784 // is the expected behavior on the Mac as evidenced by other applications.
1785 if ([theEvent type] == NSMouseMoved &&
1786 ![self acceptsMouseEventsWhenInactive] &&
1787 ![window isKeyWindow]) {
1791 // Use hitTest to check whether the mouse is over a nonWebContentView - in
1792 // which case the mouse event should not be handled by the render host.
1793 const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1794 NSView* contentView = [window contentView];
1795 NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1796 // Traverse the superview hierarchy as the hitTest will return the frontmost
1797 // view, such as an NSTextView, while nonWebContentView may be specified by
1800 if ([view respondsToSelector:nonWebContentViewSelector] &&
1801 [view performSelector:nonWebContentViewSelector]) {
1802 // The cursor is over a nonWebContentView - ignore this mouse event.
1805 if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1806 !hasOpenMouseDown_) {
1807 // The cursor is over an overlapping render widget. This check is done by
1808 // both views so the one that's returned by -hitTest: will end up
1809 // processing the event.
1810 // Note that while dragging, we only get events for the render view where
1811 // drag started, even if mouse is actually over another view or outside
1812 // the window. Cocoa does this for us. We should handle these events and
1813 // not ignore (since there is no other render view to handle them). Thus
1814 // the |!hasOpenMouseDown_| check above.
1817 view = [view superview];
1822 - (void)mouseEvent:(NSEvent*)theEvent {
1823 TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1824 if (responderDelegate_ &&
1825 [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1826 BOOL handled = [responderDelegate_ handleEvent:theEvent];
1831 if ([self shouldIgnoreMouseEvent:theEvent]) {
1832 // If this is the first such event, send a mouse exit to the host view.
1833 if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1834 WebMouseEvent exitEvent =
1835 WebInputEventFactory::mouseEvent(theEvent, self);
1836 exitEvent.type = WebInputEvent::MouseLeave;
1837 exitEvent.button = WebMouseEvent::ButtonNone;
1838 renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1840 mouseEventWasIgnored_ = YES;
1844 if (mouseEventWasIgnored_) {
1845 // If this is the first mouse event after a previous event that was ignored
1846 // due to the hitTest, send a mouse enter event to the host view.
1847 if (renderWidgetHostView_->render_widget_host_) {
1848 WebMouseEvent enterEvent =
1849 WebInputEventFactory::mouseEvent(theEvent, self);
1850 enterEvent.type = WebInputEvent::MouseMove;
1851 enterEvent.button = WebMouseEvent::ButtonNone;
1852 renderWidgetHostView_->ForwardMouseEvent(enterEvent);
1855 mouseEventWasIgnored_ = NO;
1857 // TODO(rohitrao): Probably need to handle other mouse down events here.
1858 if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
1859 if (renderWidgetHostView_->render_widget_host_)
1860 renderWidgetHostView_->render_widget_host_->OnPointerEventActivate();
1862 // Manually take focus after the click but before forwarding it to the
1864 [[self window] makeFirstResponder:self];
1867 // Don't cancel child popups; killing them on a mouse click would prevent the
1868 // user from positioning the insertion point in the text field spawning the
1869 // popup. A click outside the text field would cause the text field to drop
1870 // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1871 // the popup anyway, so we're OK.
1873 NSEventType type = [theEvent type];
1874 if (type == NSLeftMouseDown)
1875 hasOpenMouseDown_ = YES;
1876 else if (type == NSLeftMouseUp)
1877 hasOpenMouseDown_ = NO;
1879 // TODO(suzhe): We should send mouse events to the input method first if it
1880 // wants to handle them. But it won't work without implementing method
1881 // - (NSUInteger)characterIndexForPoint:.
1882 // See: http://code.google.com/p/chromium/issues/detail?id=47141
1883 // Instead of sending mouse events to the input method first, we now just
1884 // simply confirm all ongoing composition here.
1885 if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1886 type == NSOtherMouseDown) {
1887 [self confirmComposition];
1890 const WebMouseEvent event =
1891 WebInputEventFactory::mouseEvent(theEvent, self);
1892 renderWidgetHostView_->ForwardMouseEvent(event);
1895 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1896 // |performKeyEquivalent:| is sent to all views of a window, not only down the
1897 // responder chain (cf. "Handling Key Equivalents" in
1898 // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1899 // ). We only want to handle key equivalents if we're first responder.
1900 if ([[self window] firstResponder] != self)
1903 // If the event is reserved by the system, then do not pass it to web content.
1904 if (EventIsReservedBySystem(theEvent))
1907 // If we return |NO| from this function, cocoa will send the key event to
1908 // the menu and only if the menu does not process the event to |keyDown:|. We
1909 // want to send the event to a renderer _before_ sending it to the menu, so
1910 // we need to return |YES| for all events that might be swallowed by the menu.
1911 // We do not return |YES| for every keypress because we don't get |keyDown:|
1912 // events for keys that we handle this way.
1913 NSUInteger modifierFlags = [theEvent modifierFlags];
1914 if ((modifierFlags & NSCommandKeyMask) == 0) {
1915 // Make sure the menu does not contain key equivalents that don't
1917 DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1921 // Command key combinations are sent via performKeyEquivalent rather than
1922 // keyDown:. We just forward this on and if WebCore doesn't want to handle
1923 // it, we let the WebContentsView figure out how to reinject it.
1924 [self keyEvent:theEvent wasKeyEquivalent:YES];
1928 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1929 // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1930 // returned NO. If this function returns |YES|, Cocoa sends the event to
1931 // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1932 // to us instead of doing key view loop control, ctrl-left/right get handled
1934 // (However, there are still some keys that Cocoa swallows, e.g. the key
1935 // equivalent that Cocoa uses for toggling the input language. In this case,
1936 // that's actually a good thing, though -- see http://crbug.com/26115 .)
1940 - (EventHandled)keyEvent:(NSEvent*)theEvent {
1941 if (responderDelegate_ &&
1942 [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1943 BOOL handled = [responderDelegate_ handleEvent:theEvent];
1945 return kEventHandled;
1948 [self keyEvent:theEvent wasKeyEquivalent:NO];
1949 return kEventHandled;
1952 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
1953 TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
1955 // If the user changes the system hotkey mapping after Chrome has been
1956 // launched, then it is possible that a formerly reserved system hotkey is no
1957 // longer reserved. The hotkey would have skipped the renderer, but would
1958 // also have not been handled by the system. If this is the case, immediately
1960 // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
1961 // api to monitor changes to system hotkeys. This logic will have to be
1963 // http://crbug.com/383558.
1964 if (EventIsReservedBySystem(theEvent))
1967 DCHECK([theEvent type] != NSKeyDown ||
1968 !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
1970 if ([theEvent type] == NSFlagsChanged) {
1971 // Ignore NSFlagsChanged events from the NumLock and Fn keys as
1972 // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
1973 int keyCode = [theEvent keyCode];
1974 if (!keyCode || keyCode == 10 || keyCode == 63)
1978 // Don't cancel child popups; the key events are probably what's triggering
1979 // the popup in the first place.
1981 RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
1984 NativeWebKeyboardEvent event(theEvent);
1986 // Force fullscreen windows to close on Escape so they won't keep the keyboard
1987 // grabbed or be stuck onscreen if the renderer is hanging.
1988 if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
1989 event.windowsKeyCode == ui::VKEY_ESCAPE &&
1990 renderWidgetHostView_->pepper_fullscreen_window()) {
1991 RenderWidgetHostViewMac* parent =
1992 renderWidgetHostView_->fullscreen_parent_host_view();
1994 parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
1995 widgetHost->Shutdown();
1999 // Suppress the escape key up event if necessary.
2000 if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
2001 if (event.type == NativeWebKeyboardEvent::KeyUp)
2002 suppressNextEscapeKeyUp_ = NO;
2006 // We only handle key down events and just simply forward other events.
2007 if ([theEvent type] != NSKeyDown) {
2008 widgetHost->ForwardKeyboardEvent(event);
2010 // Possibly autohide the cursor.
2011 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2012 [NSCursor setHiddenUntilMouseMoves:YES];
2017 base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2019 // Records the current marked text state, so that we can know if the marked
2020 // text was deleted or not after handling the key down event.
2021 BOOL oldHasMarkedText = hasMarkedText_;
2023 // This method should not be called recursively.
2024 DCHECK(!handlingKeyDown_);
2026 // Tells insertText: and doCommandBySelector: that we are handling a key
2028 handlingKeyDown_ = YES;
2030 // These variables might be set when handling the keyboard event.
2031 // Clear them here so that we can know whether they have changed afterwards.
2032 textToBeInserted_.clear();
2033 markedText_.clear();
2034 underlines_.clear();
2035 unmarkTextCalled_ = NO;
2036 hasEditCommands_ = NO;
2037 editCommands_.clear();
2039 // Before doing anything with a key down, check to see if plugin IME has been
2040 // cancelled, since the plugin host needs to be informed of that before
2041 // receiving the keydown.
2042 if ([theEvent type] == NSKeyDown)
2043 [self checkForPluginImeCancellation];
2045 // Sends key down events to input method first, then we can decide what should
2046 // be done according to input method's feedback.
2047 // If a plugin is active, bypass this step since events are forwarded directly
2048 // to the plugin IME.
2049 if (focusedPluginIdentifier_ == -1)
2050 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2052 handlingKeyDown_ = NO;
2054 // Indicates if we should send the key event and corresponding editor commands
2055 // after processing the input method result.
2056 BOOL delayEventUntilAfterImeCompostion = NO;
2058 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2059 // while an input method is composing or inserting a text.
2060 // Gmail checks this code in its onkeydown handler to stop auto-completing
2061 // e-mail addresses while composing a CJK text.
2062 // If the text to be inserted has only one character, then we don't need this
2063 // trick, because we'll send the text as a key press event instead.
2064 if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2065 NativeWebKeyboardEvent fakeEvent = event;
2066 fakeEvent.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY
2067 fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2068 fakeEvent.skip_in_browser = true;
2069 widgetHost->ForwardKeyboardEvent(fakeEvent);
2070 // If this key event was handled by the input method, but
2071 // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2072 // enqueued edit commands, then in order to let webkit handle them
2073 // correctly, we need to send the real key event and corresponding edit
2074 // commands after processing the input method result.
2075 // We shouldn't do this if a new marked text was set by the input method,
2076 // otherwise the new marked text might be cancelled by webkit.
2077 if (hasEditCommands_ && !hasMarkedText_)
2078 delayEventUntilAfterImeCompostion = YES;
2080 if (!editCommands_.empty()) {
2081 widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2082 widgetHost->GetRoutingID(), editCommands_));
2084 widgetHost->ForwardKeyboardEvent(event);
2087 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2088 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2089 // be set to NULL. So we check it here and return immediately if it's NULL.
2090 if (!renderWidgetHostView_->render_widget_host_)
2093 // Then send keypress and/or composition related events.
2094 // If there was a marked text or the text to be inserted is longer than 1
2095 // character, then we send the text by calling ConfirmComposition().
2096 // Otherwise, if the text to be inserted only contains 1 character, then we
2097 // can just send a keypress event which is fabricated by changing the type of
2098 // the keydown event, so that we can retain all necessary informations, such
2099 // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2100 // prevent the browser from handling it again.
2101 // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2102 // handle BMP characters here, as we can always insert non-BMP characters as
2104 BOOL textInserted = NO;
2105 if (textToBeInserted_.length() >
2106 ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2107 widgetHost->ImeConfirmComposition(
2108 textToBeInserted_, gfx::Range::InvalidRange(), false);
2112 // Updates or cancels the composition. If some text has been inserted, then
2113 // we don't need to cancel the composition explicitly.
2114 if (hasMarkedText_ && markedText_.length()) {
2115 // Sends the updated marked text to the renderer so it can update the
2116 // composition node in WebKit.
2117 // When marked text is available, |selectedRange_| will be the range being
2118 // selected inside the marked text.
2119 widgetHost->ImeSetComposition(markedText_, underlines_,
2120 selectedRange_.location,
2121 NSMaxRange(selectedRange_));
2122 } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2123 if (unmarkTextCalled_) {
2124 widgetHost->ImeConfirmComposition(
2125 base::string16(), gfx::Range::InvalidRange(), false);
2127 widgetHost->ImeCancelComposition();
2131 // If the key event was handled by the input method but it also generated some
2132 // edit commands, then we need to send the real key event and corresponding
2133 // edit commands here. This usually occurs when the input method wants to
2134 // finish current composition session but still wants the application to
2135 // handle the key event. See http://crbug.com/48161 for reference.
2136 if (delayEventUntilAfterImeCompostion) {
2137 // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2138 // with windowsKeyCode == 0xE5 has already been sent to webkit.
2139 // So before sending the real key down event, we need to send a fake key up
2140 // event to balance it.
2141 NativeWebKeyboardEvent fakeEvent = event;
2142 fakeEvent.type = blink::WebInputEvent::KeyUp;
2143 fakeEvent.skip_in_browser = true;
2144 widgetHost->ForwardKeyboardEvent(fakeEvent);
2145 // Not checking |renderWidgetHostView_->render_widget_host_| here because
2146 // a key event with |skip_in_browser| == true won't be handled by browser,
2147 // thus it won't destroy the widget.
2149 if (!editCommands_.empty()) {
2150 widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2151 widgetHost->GetRoutingID(), editCommands_));
2153 widgetHost->ForwardKeyboardEvent(event);
2155 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2156 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2157 // be set to NULL. So we check it here and return immediately if it's NULL.
2158 if (!renderWidgetHostView_->render_widget_host_)
2162 const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2163 // Only send a corresponding key press event if there is no marked text.
2164 if (!hasMarkedText_) {
2165 if (!textInserted && textToBeInserted_.length() == 1) {
2166 // If a single character was inserted, then we just send it as a keypress
2168 event.type = blink::WebInputEvent::Char;
2169 event.text[0] = textToBeInserted_[0];
2171 event.skip_in_browser = true;
2172 widgetHost->ForwardKeyboardEvent(event);
2173 } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2174 [[theEvent characters] length] > 0 &&
2175 (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2176 (hasEditCommands_ && editCommands_.empty()))) {
2177 // We don't get insertText: calls if ctrl or cmd is down, or the key event
2178 // generates an insert command. So synthesize a keypress event for these
2179 // cases, unless the key event generated any other command.
2180 event.type = blink::WebInputEvent::Char;
2181 event.skip_in_browser = true;
2182 widgetHost->ForwardKeyboardEvent(event);
2186 // Possibly autohide the cursor.
2187 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2188 [NSCursor setHiddenUntilMouseMoves:YES];
2191 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2192 DCHECK(base::mac::IsOSLionOrLater());
2194 if ([event phase] != NSEventPhaseEnded &&
2195 [event phase] != NSEventPhaseCancelled) {
2199 if (renderWidgetHostView_->render_widget_host_) {
2200 // History-swiping is not possible if the logic reaches this point.
2201 // Allow rubber-banding in both directions.
2202 bool canRubberbandLeft = true;
2203 bool canRubberbandRight = true;
2204 const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2205 event, self, canRubberbandLeft, canRubberbandRight);
2206 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2209 if (endWheelMonitor_) {
2210 [NSEvent removeMonitor:endWheelMonitor_];
2211 endWheelMonitor_ = nil;
2215 - (void)beginGestureWithEvent:(NSEvent*)event {
2216 [responderDelegate_ beginGestureWithEvent:event];
2218 - (void)endGestureWithEvent:(NSEvent*)event {
2219 [responderDelegate_ endGestureWithEvent:event];
2221 - (void)touchesMovedWithEvent:(NSEvent*)event {
2222 [responderDelegate_ touchesMovedWithEvent:event];
2224 - (void)touchesBeganWithEvent:(NSEvent*)event {
2225 [responderDelegate_ touchesBeganWithEvent:event];
2227 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2228 [responderDelegate_ touchesCancelledWithEvent:event];
2230 - (void)touchesEndedWithEvent:(NSEvent*)event {
2231 [responderDelegate_ touchesEndedWithEvent:event];
2234 // This is invoked only on 10.8 or newer when the user taps a word using
2236 - (void)quickLookWithEvent:(NSEvent*)event {
2237 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2238 TextInputClientMac::GetInstance()->GetStringAtPoint(
2239 renderWidgetHostView_->render_widget_host_,
2240 gfx::Point(point.x, NSHeight([self frame]) - point.y),
2241 ^(NSAttributedString* string, NSPoint baselinePoint) {
2242 if (string && [string length] > 0) {
2243 dispatch_async(dispatch_get_main_queue(), ^{
2244 [self showDefinitionForAttributedString:string
2245 atPoint:baselinePoint];
2252 // This method handles 2 different types of hardware events.
2253 // (Apple does not distinguish between them).
2254 // a. Scrolling the middle wheel of a mouse.
2255 // b. Swiping on the track pad.
2257 // This method is responsible for 2 types of behavior:
2258 // a. Scrolling the content of window.
2259 // b. Navigating forwards/backwards in history.
2261 // This is a brief description of the logic:
2262 // 1. If the content can be scrolled, scroll the content.
2263 // (This requires a roundtrip to blink to determine whether the content
2264 // can be scrolled.)
2265 // Once this logic is triggered, the navigate logic cannot be triggered
2266 // until the gesture finishes.
2267 // 2. If the user is making a horizontal swipe, start the navigate
2268 // forward/backwards UI.
2269 // Once this logic is triggered, the user can either cancel or complete
2270 // the gesture. If the user completes the gesture, all remaining touches
2271 // are swallowed, and not allowed to scroll the content. If the user
2272 // cancels the gesture, all remaining touches are forwarded to the content
2273 // scroll logic. The user cannot trigger the navigation logic again.
2274 - (void)scrollWheel:(NSEvent*)event {
2275 if (responderDelegate_ &&
2276 [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2277 BOOL handled = [responderDelegate_ handleEvent:event];
2282 // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2283 // the event is received even when the mouse cursor is no longer over the view
2284 // when the scrolling ends (e.g. if the tab was switched). This is necessary
2285 // for ending rubber-banding in such cases.
2286 if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2287 !endWheelMonitor_) {
2289 [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2290 handler:^(NSEvent* blockEvent) {
2291 [self shortCircuitScrollWheelEvent:blockEvent];
2296 // This is responsible for content scrolling!
2297 if (renderWidgetHostView_->render_widget_host_) {
2298 BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2299 BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2300 const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2301 event, self, canRubberbandLeft, canRubberbandRight);
2302 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2306 // Called repeatedly during a pinch gesture, with incremental change values.
2307 - (void)magnifyWithEvent:(NSEvent*)event {
2308 if (renderWidgetHostView_->render_widget_host_) {
2309 // Send a GesturePinchUpdate event.
2310 // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
2311 // GestureSrollBegin/End) as is done for touchscreen. Keeping track of when
2312 // a pinch is active would take a little more work here, and we don't need
2313 // it for anything yet.
2314 const WebGestureEvent& webEvent =
2315 WebInputEventFactory::gestureEvent(event, self);
2316 renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
2320 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2321 NSWindow* oldWindow = [self window];
2323 NSNotificationCenter* notificationCenter =
2324 [NSNotificationCenter defaultCenter];
2326 // Backing property notifications crash on 10.6 when building with the 10.7
2327 // SDK, see http://crbug.com/260595.
2328 static BOOL supportsBackingPropertiesNotification =
2329 SupportsBackingPropertiesChangedNotification();
2332 if (supportsBackingPropertiesNotification) {
2335 name:NSWindowDidChangeBackingPropertiesNotification
2340 name:NSWindowDidMoveNotification
2344 name:NSWindowDidEndLiveResizeNotification
2348 if (supportsBackingPropertiesNotification) {
2351 selector:@selector(windowDidChangeBackingProperties:)
2352 name:NSWindowDidChangeBackingPropertiesNotification
2357 selector:@selector(windowChangedGlobalFrame:)
2358 name:NSWindowDidMoveNotification
2362 selector:@selector(windowChangedGlobalFrame:)
2363 name:NSWindowDidEndLiveResizeNotification
2368 - (void)updateScreenProperties{
2369 renderWidgetHostView_->UpdateBackingStoreScaleFactor();
2370 renderWidgetHostView_->UpdateDisplayLink();
2373 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2374 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2375 // Background tabs check if their scale factor or vsync properties changed
2376 // when they are added to a window.
2378 // Allocating a CGLayerRef with the current scale factor immediately from
2379 // this handler doesn't work. Schedule the backing store update on the
2380 // next runloop cycle, then things are read for CGLayerRef allocations to
2382 [self performSelector:@selector(updateScreenProperties)
2387 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2388 renderWidgetHostView_->UpdateScreenInfo(
2389 renderWidgetHostView_->GetNativeView());
2392 - (void)setFrameSize:(NSSize)newSize {
2393 TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2395 // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2396 // -setFrame: isn't neccessary.
2397 [super setFrameSize:newSize];
2399 if (!renderWidgetHostView_->render_widget_host_)
2402 renderWidgetHostView_->render_widget_host_->SendScreenRects();
2403 renderWidgetHostView_->render_widget_host_->WasResized();
2404 if (renderWidgetHostView_->delegated_frame_host_)
2405 renderWidgetHostView_->delegated_frame_host_->WasResized();
2407 // Wait for the frame that WasResize might have requested. If the view is
2408 // being made visible at a new size, then this call will have no effect
2409 // because the view widget is still hidden, and the pause call in WasShown
2410 // will have this effect for us.
2411 renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2414 - (BOOL)canBecomeKeyView {
2415 if (!renderWidgetHostView_->render_widget_host_)
2418 return canBeKeyView_;
2421 - (BOOL)acceptsFirstResponder {
2422 if (!renderWidgetHostView_->render_widget_host_)
2425 return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
2428 - (BOOL)becomeFirstResponder {
2429 if (!renderWidgetHostView_->render_widget_host_)
2432 renderWidgetHostView_->render_widget_host_->Focus();
2433 renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
2434 renderWidgetHostView_->SetTextInputActive(true);
2436 // Cancel any onging composition text which was left before we lost focus.
2437 // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2438 // somehow that method won't be called when switching among different tabs.
2439 // See http://crbug.com/47209
2440 [self cancelComposition];
2442 NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2443 [[self window] keyViewSelectionDirection]];
2444 NSDictionary* userInfo =
2445 [NSDictionary dictionaryWithObject:direction
2446 forKey:kSelectionDirection];
2447 [[NSNotificationCenter defaultCenter]
2448 postNotificationName:kViewDidBecomeFirstResponder
2455 - (BOOL)resignFirstResponder {
2456 renderWidgetHostView_->SetTextInputActive(false);
2457 if (!renderWidgetHostView_->render_widget_host_)
2460 if (closeOnDeactivate_)
2461 renderWidgetHostView_->KillSelf();
2463 renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
2464 renderWidgetHostView_->render_widget_host_->Blur();
2466 // We should cancel any onging composition whenever RWH's Blur() method gets
2467 // called, because in this case, webkit will confirm the ongoing composition
2469 [self cancelComposition];
2474 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2475 if (responderDelegate_ &&
2477 respondsToSelector:@selector(validateUserInterfaceItem:
2481 [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2486 SEL action = [item action];
2488 if (action == @selector(stopSpeaking:)) {
2489 return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2490 renderWidgetHostView_->IsSpeaking();
2492 if (action == @selector(startSpeaking:)) {
2493 return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2494 renderWidgetHostView_->SupportsSpeech();
2497 // For now, these actions are always enabled for render view,
2498 // this is sub-optimal.
2499 // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2500 if (action == @selector(undo:) ||
2501 action == @selector(redo:) ||
2502 action == @selector(cut:) ||
2503 action == @selector(copy:) ||
2504 action == @selector(copyToFindPboard:) ||
2505 action == @selector(paste:) ||
2506 action == @selector(pasteAndMatchStyle:)) {
2507 return renderWidgetHostView_->render_widget_host_->IsRenderView();
2510 return editCommand_helper_->IsMenuItemEnabled(action, self);
2513 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2514 return renderWidgetHostView_.get();
2517 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2518 // move) for the given event. Customize here to be more selective about which
2519 // key presses to autohide on.
2520 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2521 return ([event type] == NSKeyDown &&
2522 !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2525 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2526 index:(NSUInteger)index
2527 maxCount:(NSUInteger)maxCount {
2528 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2529 NSUInteger totalLength = [fullArray count];
2530 if (index >= totalLength)
2532 NSUInteger length = MIN(totalLength - index, maxCount);
2533 return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2536 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2537 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2538 return [fullArray count];
2541 - (id)accessibilityAttributeValue:(NSString *)attribute {
2542 BrowserAccessibilityManager* manager =
2543 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2545 // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2546 // BrowserAccessibilityManager. Children includes all subviews in addition to
2547 // contents. Currently we do not have subviews besides the document view.
2548 if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2549 [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2551 return [NSArray arrayWithObjects:manager->
2552 GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2553 } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2554 return NSAccessibilityScrollAreaRole;
2556 id ret = [super accessibilityAttributeValue:attribute];
2560 - (NSArray*)accessibilityAttributeNames {
2561 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2562 [ret addObject:NSAccessibilityContentsAttribute];
2563 [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2567 - (id)accessibilityHitTest:(NSPoint)point {
2568 BrowserAccessibilityManager* manager =
2569 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2572 NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2573 NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2574 localPoint.y = NSHeight([self bounds]) - localPoint.y;
2575 BrowserAccessibilityCocoa* root =
2576 manager->GetRoot()->ToBrowserAccessibilityCocoa();
2577 id obj = [root accessibilityHitTest:localPoint];
2581 - (BOOL)accessibilityIsIgnored {
2582 BrowserAccessibilityManager* manager =
2583 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2587 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2588 BrowserAccessibilityManager* manager =
2589 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2590 // Only child is root.
2592 manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2599 - (id)accessibilityFocusedUIElement {
2600 BrowserAccessibilityManager* manager =
2601 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2603 BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2604 DCHECK(focused_item);
2606 BrowserAccessibilityCocoa* focused_item_cocoa =
2607 focused_item->ToBrowserAccessibilityCocoa();
2608 DCHECK(focused_item_cocoa);
2609 if (focused_item_cocoa)
2610 return focused_item_cocoa;
2613 return [super accessibilityFocusedUIElement];
2616 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
2617 // with minor modifications for code style and commenting.
2619 // The 'public' interface is -setToolTipAtMousePoint:. This differs from
2620 // -setToolTip: in that the updated tooltip takes effect immediately,
2621 // without the user's having to move the mouse out of and back into the view.
2623 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
2624 // the view, which in turn requires overriding some internal tracking-rect
2625 // methods (to keep track of its owner & userdata, which need to be filled out
2626 // in the fake events.) --snej 7/6/09
2630 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
2631 * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
2633 * Redistribution and use in source and binary forms, with or without
2634 * modification, are permitted provided that the following conditions
2637 * 1. Redistributions of source code must retain the above copyright
2638 * notice, this list of conditions and the following disclaimer.
2639 * 2. Redistributions in binary form must reproduce the above copyright
2640 * notice, this list of conditions and the following disclaimer in the
2641 * documentation and/or other materials provided with the distribution.
2642 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
2643 * its contributors may be used to endorse or promote products derived
2644 * from this software without specific prior written permission.
2646 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
2647 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2648 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2649 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2650 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2651 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2652 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2653 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2654 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2655 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2658 // Any non-zero value will do, but using something recognizable might help us
2660 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
2662 // Override of a public NSView method, replacing the inherited functionality.
2663 // See above for rationale.
2664 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
2666 userData:(void *)data
2667 assumeInside:(BOOL)assumeInside {
2668 DCHECK(trackingRectOwner_ == nil);
2669 trackingRectOwner_ = owner;
2670 trackingRectUserData_ = data;
2671 return kTrackingRectTag;
2674 // Override of (apparently) a private NSView method(!) See above for rationale.
2675 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
2677 userData:(void *)data
2678 assumeInside:(BOOL)assumeInside
2679 useTrackingNum:(int)tag {
2680 DCHECK(tag == 0 || tag == kTrackingRectTag);
2681 DCHECK(trackingRectOwner_ == nil);
2682 trackingRectOwner_ = owner;
2683 trackingRectUserData_ = data;
2684 return kTrackingRectTag;
2687 // Override of (apparently) a private NSView method(!) See above for rationale.
2688 - (void)_addTrackingRects:(NSRect *)rects
2690 userDataList:(void **)userDataList
2691 assumeInsideList:(BOOL *)assumeInsideList
2692 trackingNums:(NSTrackingRectTag *)trackingNums
2695 DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
2696 DCHECK(trackingRectOwner_ == nil);
2697 trackingRectOwner_ = owner;
2698 trackingRectUserData_ = userDataList[0];
2699 trackingNums[0] = kTrackingRectTag;
2702 // Override of a public NSView method, replacing the inherited functionality.
2703 // See above for rationale.
2704 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
2708 if (tag == kTrackingRectTag) {
2709 trackingRectOwner_ = nil;
2713 if (tag == lastToolTipTag_) {
2714 [super removeTrackingRect:tag];
2715 lastToolTipTag_ = 0;
2719 // If any other tracking rect is being removed, we don't know how it was
2720 // created and it's possible there's a leak involved (see Radar 3500217).
2724 // Override of (apparently) a private NSView method(!)
2725 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
2726 for (int i = 0; i < count; ++i) {
2730 DCHECK(tag == kTrackingRectTag);
2731 trackingRectOwner_ = nil;
2735 // Sends a fake NSMouseExited event to the view for its current tracking rect.
2736 - (void)_sendToolTipMouseExited {
2737 // Nothing matters except window, trackingNumber, and userData.
2738 int windowNumber = [[self window] windowNumber];
2739 NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2740 location:NSZeroPoint
2743 windowNumber:windowNumber
2746 trackingNumber:kTrackingRectTag
2747 userData:trackingRectUserData_];
2748 [trackingRectOwner_ mouseExited:fakeEvent];
2751 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
2752 - (void)_sendToolTipMouseEntered {
2753 // Nothing matters except window, trackingNumber, and userData.
2754 int windowNumber = [[self window] windowNumber];
2755 NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2756 location:NSZeroPoint
2759 windowNumber:windowNumber
2762 trackingNumber:kTrackingRectTag
2763 userData:trackingRectUserData_];
2764 [trackingRectOwner_ mouseEntered:fakeEvent];
2767 // Sets the view's current tooltip, to be displayed at the current mouse
2768 // location. (This does not make the tooltip appear -- as usual, it only
2769 // appears after a delay.) Pass null to remove the tooltip.
2770 - (void)setToolTipAtMousePoint:(NSString *)string {
2771 NSString *toolTip = [string length] == 0 ? nil : string;
2772 if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
2773 (!toolTip && !toolTip_)) {
2778 [self _sendToolTipMouseExited];
2781 toolTip_.reset([toolTip copy]);
2784 // See radar 3500217 for why we remove all tooltips
2785 // rather than just the single one we created.
2786 [self removeAllToolTips];
2787 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2788 lastToolTipTag_ = [self addToolTipRect:wideOpenRect
2791 [self _sendToolTipMouseEntered];
2795 // NSView calls this to get the text when displaying the tooltip.
2796 - (NSString *)view:(NSView *)view
2797 stringForToolTip:(NSToolTipTag)tag
2798 point:(NSPoint)point
2799 userData:(void *)data {
2800 return [[toolTip_ copy] autorelease];
2803 // Below is our NSTextInputClient implementation.
2805 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2806 // functions to process this event.
2808 // [WebHTMLView keyDown] ->
2809 // EventHandler::keyEvent() ->
2811 // [WebEditorClient handleKeyboardEvent] ->
2812 // [WebHTMLView _interceptEditingKeyEvent] ->
2813 // [NSResponder interpretKeyEvents] ->
2814 // [WebHTMLView insertText] ->
2815 // Editor::insertText()
2817 // Unfortunately, it is hard for Chromium to use this implementation because
2818 // it causes key-typing jank.
2819 // RenderWidgetHostViewMac is running in a browser process. On the other
2820 // hand, Editor and EventHandler are running in a renderer process.
2821 // So, if we used this implementation, a NSKeyDown event is dispatched to
2822 // the following functions of Chromium.
2824 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2825 // |Sync IPC (KeyDown)| (*1) ->
2826 // EventHandler::keyEvent() (renderer) ->
2828 // EditorClientImpl::handleKeyboardEvent() (renderer) ->
2829 // |Sync IPC| (*2) ->
2830 // [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2831 // [self interpretKeyEvents] ->
2832 // [RenderWidgetHostViewMac insertText] (browser) ->
2834 // Editor::insertText() (renderer)
2836 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2837 // result of EventHandler::keyEvent().
2838 // (*2) we need to wait until this call finishes since WebEditorClient uses
2839 // the result of [WebHTMLView _interceptEditingKeyEvent].
2841 // This needs many sync IPC messages sent between a browser and a renderer for
2842 // each key event, which would probably result in key-typing jank.
2843 // To avoid this problem, this implementation processes key events (and input
2844 // method events) totally in a browser process and sends asynchronous input
2845 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2846 // renderer process.
2848 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2849 // |Async IPC (RawKeyDown)| ->
2850 // [self interpretKeyEvents] ->
2851 // [RenderWidgetHostViewMac insertText] (browser) ->
2852 // |Async IPC (Char)| ->
2853 // Editor::insertText() (renderer)
2855 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2856 // make any key-typing jank. --hbono 7/23/09
2859 extern NSString *NSTextInputReplacementRangeAttributeName;
2862 - (NSArray *)validAttributesForMarkedText {
2863 // This code is just copied from WebKit except renaming variables.
2864 if (!validAttributesForMarkedText_) {
2865 validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2866 NSUnderlineStyleAttributeName,
2867 NSUnderlineColorAttributeName,
2868 NSMarkedClauseSegmentAttributeName,
2869 NSTextInputReplacementRangeAttributeName,
2872 return validAttributesForMarkedText_.get();
2875 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2876 DCHECK([self window]);
2877 // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2878 // coordinates (upper left origin). Scroll offsets will be taken care of in
2880 thePoint = [[self window] convertScreenToBase:thePoint];
2881 thePoint = [self convertPoint:thePoint fromView:nil];
2882 thePoint.y = NSHeight([self frame]) - thePoint.y;
2885 TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2886 renderWidgetHostView_->render_widget_host_,
2887 gfx::Point(thePoint.x, thePoint.y));
2891 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2892 actualRange:(NSRangePointer)actualRange {
2894 if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2898 rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2899 renderWidgetHostView_->render_widget_host_, theRange);
2901 // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2903 *actualRange = theRange;
2906 // The returned rectangle is in WebKit coordinates (upper left origin), so
2907 // flip the coordinate system.
2908 NSRect viewFrame = [self frame];
2909 rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2913 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2914 actualRange:(NSRangePointer)actualRange {
2915 NSRect rect = [self firstViewRectForCharacterRange:theRange
2916 actualRange:actualRange];
2918 // Convert into screen coordinates for return.
2919 rect = [self convertRect:rect toView:nil];
2920 rect.origin = [[self window] convertBaseToScreen:rect.origin];
2924 - (NSRange)markedRange {
2925 // An input method calls this method to check if an application really has
2926 // a text being composed when hasMarkedText call returns true.
2927 // Returns the range saved in the setMarkedText method so the input method
2928 // calls the setMarkedText method and we can update the composition node
2929 // there. (When this method returns an empty range, the input method doesn't
2930 // call the setMarkedText method.)
2931 return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2934 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2935 actualRange:(NSRangePointer)actualRange {
2936 // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2938 *actualRange = range;
2939 NSAttributedString* str =
2940 TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2941 renderWidgetHostView_->render_widget_host_, range);
2945 - (NSInteger)conversationIdentifier {
2946 return reinterpret_cast<NSInteger>(self);
2949 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2950 // nil when the caret is in non-editable content or password box to avoid
2951 // making input methods do their work.
2952 - (NSTextInputContext *)inputContext {
2953 if (focusedPluginIdentifier_ != -1)
2954 return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2956 switch(renderWidgetHostView_->text_input_type_) {
2957 case ui::TEXT_INPUT_TYPE_NONE:
2958 case ui::TEXT_INPUT_TYPE_PASSWORD:
2961 return [super inputContext];
2965 - (BOOL)hasMarkedText {
2966 // An input method calls this function to figure out whether or not an
2967 // application is really composing a text. If it is composing, it calls
2968 // the markedRange method, and maybe calls the setMarkedText method.
2969 // It seems an input method usually calls this function when it is about to
2970 // cancel an ongoing composition. If an application has a non-empty marked
2971 // range, it calls the setMarkedText method to delete the range.
2972 return hasMarkedText_;
2975 - (void)unmarkText {
2976 // Delete the composition node of the renderer and finish an ongoing
2978 // It seems an input method calls the setMarkedText method and set an empty
2979 // text when it cancels an ongoing composition, i.e. I have never seen an
2980 // input method calls this method.
2981 hasMarkedText_ = NO;
2982 markedText_.clear();
2983 underlines_.clear();
2985 // If we are handling a key down event, then ConfirmComposition() will be
2986 // called in keyEvent: method.
2987 if (!handlingKeyDown_) {
2988 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2989 base::string16(), gfx::Range::InvalidRange(), false);
2991 unmarkTextCalled_ = YES;
2995 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
2996 replacementRange:(NSRange)replacementRange {
2997 // An input method updates the composition string.
2998 // We send the given text and range to the renderer so it can update the
2999 // composition node of WebKit.
3000 // TODO(suzhe): It's hard for us to support replacementRange without accessing
3001 // the full web content.
3002 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3003 NSString* im_text = isAttributedString ? [string string] : string;
3004 int length = [im_text length];
3006 // |markedRange_| will get set on a callback from ImeSetComposition().
3007 selectedRange_ = newSelRange;
3008 markedText_ = base::SysNSStringToUTF16(im_text);
3009 hasMarkedText_ = (length > 0);
3011 underlines_.clear();
3012 if (isAttributedString) {
3013 ExtractUnderlines(string, &underlines_);
3015 // Use a thin black underline by default.
3016 underlines_.push_back(blink::WebCompositionUnderline(
3017 0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
3020 // If we are handling a key down event, then SetComposition() will be
3021 // called in keyEvent: method.
3022 // Input methods of Mac use setMarkedText calls with an empty text to cancel
3023 // an ongoing composition. So, we should check whether or not the given text
3024 // is empty to update the input method state. (Our input method backend can
3025 // automatically cancels an ongoing composition when we send an empty text.
3026 // So, it is OK to send an empty text to the renderer.)
3027 if (!handlingKeyDown_) {
3028 renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3029 markedText_, underlines_,
3030 newSelRange.location, NSMaxRange(newSelRange));
3034 - (void)doCommandBySelector:(SEL)selector {
3035 // An input method calls this function to dispatch an editing command to be
3036 // handled by this view.
3037 if (selector == @selector(noop:))
3040 std::string command(
3041 [RenderWidgetHostViewMacEditCommandHelper::
3042 CommandNameForSelector(selector) UTF8String]);
3044 // If this method is called when handling a key down event, then we need to
3045 // handle the command in the key event handler. Otherwise we can just handle
3047 if (handlingKeyDown_) {
3048 hasEditCommands_ = YES;
3049 // We ignore commands that insert characters, because this was causing
3050 // strange behavior (e.g. tab always inserted a tab rather than moving to
3051 // the next field on the page).
3052 if (!StartsWithASCII(command, "insert", false))
3053 editCommands_.push_back(EditCommand(command, ""));
3055 RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3056 rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3061 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3062 // An input method has characters to be inserted.
3063 // Same as Linux, Mac calls this method not only:
3064 // * when an input method finishs composing text, but also;
3065 // * when we type an ASCII character (without using input methods).
3066 // When we aren't using input methods, we should send the given character as
3067 // a Char event so it is dispatched to an onkeypress() event handler of
3069 // On the other hand, when we are using input methods, we should send the
3070 // given characters as an input method event and prevent the characters from
3071 // being dispatched to onkeypress() event handlers.
3072 // Text inserting might be initiated by other source instead of keyboard
3073 // events, such as the Characters dialog. In this case the text should be
3074 // sent as an input method event as well.
3075 // TODO(suzhe): It's hard for us to support replacementRange without accessing
3076 // the full web content.
3077 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3078 NSString* im_text = isAttributedString ? [string string] : string;
3079 if (handlingKeyDown_) {
3080 textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3082 gfx::Range replacement_range(replacementRange);
3083 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3084 base::SysNSStringToUTF16(im_text), replacement_range, false);
3087 // Inserting text will delete all marked text automatically.
3088 hasMarkedText_ = NO;
3091 - (void)insertText:(id)string {
3092 [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3095 - (void)viewDidMoveToWindow {
3097 [self updateScreenProperties];
3099 if (canBeKeyView_) {
3100 NSWindow* newWindow = [self window];
3101 // Pointer comparison only, since we don't know if lastWindow_ is still
3104 // If we move into a new window, refresh the frame information. We
3105 // don't need to do it if it was the same window as it used to be in,
3106 // since that case is covered by WasShown(). We only want to do this for
3107 // real browser views, not popups.
3108 if (newWindow != lastWindow_) {
3109 lastWindow_ = newWindow;
3110 renderWidgetHostView_->WindowFrameChanged();
3115 // If we switch windows (or are removed from the view hierarchy), cancel any
3116 // open mouse-downs.
3117 if (hasOpenMouseDown_) {
3118 WebMouseEvent event;
3119 event.type = WebInputEvent::MouseUp;
3120 event.button = WebMouseEvent::ButtonLeft;
3121 renderWidgetHostView_->ForwardMouseEvent(event);
3123 hasOpenMouseDown_ = NO;
3127 - (void)undo:(id)sender {
3128 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3130 web_contents->Undo();
3133 - (void)redo:(id)sender {
3134 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3136 web_contents->Redo();
3139 - (void)cut:(id)sender {
3140 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3142 web_contents->Cut();
3145 - (void)copy:(id)sender {
3146 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3148 web_contents->Copy();
3151 - (void)copyToFindPboard:(id)sender {
3152 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3154 web_contents->CopyToFindPboard();
3157 - (void)paste:(id)sender {
3158 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3160 web_contents->Paste();
3163 - (void)pasteAndMatchStyle:(id)sender {
3164 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3166 web_contents->PasteAndMatchStyle();
3169 - (void)selectAll:(id)sender {
3170 // editCommand_helper_ adds implementations for most NSResponder methods
3171 // dynamically. But the renderer side only sends selection results back to
3172 // the browser if they were triggered by a keyboard event or went through
3173 // one of the Select methods on RWH. Since selectAll: is called from the
3174 // menu handler, neither is true.
3175 // Explicitly call SelectAll() here to make sure the renderer returns
3176 // selection results.
3177 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3179 web_contents->SelectAll();
3182 - (void)startSpeaking:(id)sender {
3183 renderWidgetHostView_->SpeakSelection();
3186 - (void)stopSpeaking:(id)sender {
3187 renderWidgetHostView_->StopSpeaking();
3190 - (void)cancelComposition {
3191 if (!hasMarkedText_)
3194 // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3195 // doesn't call any NSTextInput functions, such as setMarkedText or
3196 // insertText. So, we need to send an IPC message to a renderer so it can
3197 // delete the composition node.
3198 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3199 [currentInputManager markedTextAbandoned:self];
3201 hasMarkedText_ = NO;
3202 // Should not call [self unmarkText] here, because it'll send unnecessary
3203 // cancel composition IPC message to the renderer.
3206 - (void)confirmComposition {
3207 if (!hasMarkedText_)
3210 if (renderWidgetHostView_->render_widget_host_)
3211 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3212 base::string16(), gfx::Range::InvalidRange(), false);
3214 [self cancelComposition];
3217 - (void)setPluginImeActive:(BOOL)active {
3218 if (active == pluginImeActive_)
3221 pluginImeActive_ = active;
3223 [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3224 renderWidgetHostView_->PluginImeCompositionCompleted(
3225 base::string16(), focusedPluginIdentifier_);
3229 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3231 focusedPluginIdentifier_ = pluginId;
3232 else if (focusedPluginIdentifier_ == pluginId)
3233 focusedPluginIdentifier_ = -1;
3235 // Whenever plugin focus changes, plugin IME resets.
3236 [self setPluginImeActive:NO];
3239 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3240 if (!pluginImeActive_)
3243 ComplexTextInputPanel* inputPanel =
3244 [ComplexTextInputPanel sharedComplexTextInputPanel];
3245 NSString* composited_string = nil;
3246 BOOL handled = [inputPanel interpretKeyEvent:event
3247 string:&composited_string];
3248 if (composited_string) {
3249 renderWidgetHostView_->PluginImeCompositionCompleted(
3250 base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3251 pluginImeActive_ = NO;
3256 - (void)checkForPluginImeCancellation {
3257 if (pluginImeActive_ &&
3258 ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3259 renderWidgetHostView_->PluginImeCompositionCompleted(
3260 base::string16(), focusedPluginIdentifier_);
3261 pluginImeActive_ = NO;
3265 // Overriding a NSResponder method to support application services.
3267 - (id)validRequestorForSendType:(NSString*)sendType
3268 returnType:(NSString*)returnType {
3270 BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3271 BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3272 BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3274 renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3276 if (sendTypeIsString && hasText && !returnType) {
3278 } else if (!sendType && returnTypeIsString && takesText) {
3280 } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3283 requestor = [super validRequestorForSendType:sendType
3284 returnType:returnType];
3289 - (void)viewWillStartLiveResize {
3290 [super viewWillStartLiveResize];
3291 RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3293 widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3296 - (void)viewDidEndLiveResize {
3297 [super viewDidEndLiveResize];
3298 RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3300 widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3303 - (void)updateCursor:(NSCursor*)cursor {
3304 if (currentCursor_ == cursor)
3307 currentCursor_.reset([cursor retain]);
3308 [[self window] invalidateCursorRectsForView:self];
3311 - (void)popupWindowWillClose:(NSNotification *)notification {
3312 renderWidgetHostView_->KillSelf();
3318 // Supporting application services
3320 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3322 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3323 types:(NSArray*)types {
3324 const std::string& str = renderWidgetHostView_->selected_text();
3325 if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3327 base::scoped_nsobject<NSString> text(
3328 [[NSString alloc] initWithUTF8String:str.c_str()]);
3329 NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3330 [pboard declareTypes:toDeclare owner:nil];
3331 return [pboard setString:text forType:NSStringPboardType];
3334 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3335 NSString *string = [pboard stringForType:NSStringPboardType];
3336 if (!string) return NO;
3338 // If the user is currently using an IME, confirm the IME input,
3339 // and then insert the text from the service, the same as TextEdit and Safari.
3340 [self confirmComposition];
3341 [self insertText:string];
3349 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3350 // regions that are not draggable. (See ControlRegionView in
3351 // native_app_window_cocoa.mm). This requires the render host view to be
3352 // draggable by default.
3353 - (BOOL)mouseDownCanMoveWindow {