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/io_surface_layer_mac.h"
34 #include "content/browser/compositor/resize_lock.h"
35 #include "content/browser/compositor/software_layer_mac.h"
36 #include "content/browser/frame_host/frame_tree.h"
37 #include "content/browser/frame_host/frame_tree_node.h"
38 #include "content/browser/frame_host/render_frame_host_impl.h"
39 #include "content/browser/gpu/compositor_util.h"
40 #include "content/browser/renderer_host/render_widget_helper.h"
41 #include "content/browser/renderer_host/render_view_host_impl.h"
42 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
43 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
44 #import "content/browser/renderer_host/text_input_client_mac.h"
45 #include "content/common/accessibility_messages.h"
46 #include "content/common/edit_command.h"
47 #include "content/common/gpu/gpu_messages.h"
48 #include "content/common/gpu/surface_handle_types_mac.h"
49 #include "content/common/input_messages.h"
50 #include "content/common/view_messages.h"
51 #include "content/common/webplugin_geometry.h"
52 #include "content/public/browser/browser_thread.h"
53 #include "content/public/browser/native_web_keyboard_event.h"
54 #include "content/public/browser/notification_service.h"
55 #include "content/public/browser/notification_types.h"
56 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
57 #import "content/public/browser/render_widget_host_view_mac_delegate.h"
58 #include "content/public/browser/user_metrics.h"
59 #include "content/public/browser/web_contents.h"
60 #include "skia/ext/platform_canvas.h"
61 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
62 #include "third_party/WebKit/public/web/WebInputEvent.h"
63 #include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
64 #import "third_party/mozilla/ComplexTextInputPanel.h"
65 #include "ui/base/cocoa/animation_utils.h"
66 #import "ui/base/cocoa/fullscreen_window_manager.h"
67 #import "ui/base/cocoa/underlay_opengl_hosting_window.h"
68 #include "ui/events/keycodes/keyboard_codes.h"
69 #include "ui/base/layout.h"
70 #include "ui/compositor/compositor.h"
71 #include "ui/compositor/layer.h"
72 #include "ui/gfx/display.h"
73 #include "ui/gfx/frame_time.h"
74 #include "ui/gfx/point.h"
75 #include "ui/gfx/rect_conversions.h"
76 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
77 #include "ui/gfx/screen.h"
78 #include "ui/gfx/size_conversions.h"
79 #include "ui/gl/gl_switches.h"
81 using content::BrowserAccessibility;
82 using content::BrowserAccessibilityManager;
83 using content::EditCommand;
84 using content::FrameTreeNode;
85 using content::NativeWebKeyboardEvent;
86 using content::RenderFrameHost;
87 using content::RenderViewHost;
88 using content::RenderViewHostImpl;
89 using content::RenderWidgetHostImpl;
90 using content::RenderWidgetHostViewMac;
91 using content::RenderWidgetHostViewMacEditCommandHelper;
92 using content::TextInputClientMac;
93 using content::WebContents;
94 using blink::WebInputEvent;
95 using blink::WebInputEventFactory;
96 using blink::WebMouseEvent;
97 using blink::WebMouseWheelEvent;
98 using blink::WebGestureEvent;
102 // Whether a keyboard event has been reserved by OSX.
103 BOOL EventIsReservedBySystem(NSEvent* event) {
104 content::SystemHotkeyHelperMac* helper =
105 content::SystemHotkeyHelperMac::GetInstance();
106 return helper->map()->IsEventReserved(event);
111 // These are not documented, so use only after checking -respondsToSelector:.
112 @interface NSApplication (UndocumentedSpeechMethods)
113 - (void)speakString:(NSString*)string;
114 - (void)stopSpeaking:(id)sender;
118 // Declare things that are part of the 10.7 SDK.
119 #if !defined(MAC_OS_X_VERSION_10_7) || \
120 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
122 static NSString* const NSWindowDidChangeBackingPropertiesNotification =
123 @"NSWindowDidChangeBackingPropertiesNotification";
127 // This method will return YES for OS X versions 10.7.3 and later, and NO
129 // Used to prevent a crash when building with the 10.7 SDK and accessing the
130 // notification below. See: http://crbug.com/260595.
131 static BOOL SupportsBackingPropertiesChangedNotification() {
132 // windowDidChangeBackingProperties: method has been added to the
133 // NSWindowDelegate protocol in 10.7.3, at the same time as the
134 // NSWindowDidChangeBackingPropertiesNotification notification was added.
135 // If the protocol contains this method description, the notification should
136 // be supported as well.
137 Protocol* windowDelegateProtocol = NSProtocolFromString(@"NSWindowDelegate");
138 struct objc_method_description methodDescription =
139 protocol_getMethodDescription(
140 windowDelegateProtocol,
141 @selector(windowDidChangeBackingProperties:),
145 // If the protocol does not contain the method, the returned method
146 // description is {NULL, NULL}
147 return methodDescription.name != NULL || methodDescription.types != NULL;
151 @interface RenderWidgetHostViewCocoa ()
152 @property(nonatomic, assign) NSRange selectedRange;
153 @property(nonatomic, assign) NSRange markedRange;
155 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
156 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
157 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
158 consumed:(BOOL)consumed;
160 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
161 - (void)windowDidChangeBackingProperties:(NSNotification*)notification;
162 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
163 - (void)checkForPluginImeCancellation;
164 - (void)updateScreenProperties;
165 - (void)setResponderDelegate:
166 (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
169 // A window subclass that allows the fullscreen window to become main and gain
170 // keyboard focus. This is only used for pepper flash. Normal fullscreen is
171 // handled by the browser.
172 @interface PepperFlashFullscreenWindow : UnderlayOpenGLHostingWindow
175 @implementation PepperFlashFullscreenWindow
177 - (BOOL)canBecomeKeyWindow {
181 - (BOOL)canBecomeMainWindow {
187 @interface RenderWidgetPopupWindow : NSWindow {
188 // The event tap that allows monitoring of all events, to properly close with
189 // a click outside the bounds of the window.
194 @implementation RenderWidgetPopupWindow
196 - (id)initWithContentRect:(NSRect)contentRect
197 styleMask:(NSUInteger)windowStyle
198 backing:(NSBackingStoreType)bufferingType
199 defer:(BOOL)deferCreation {
200 if (self = [super initWithContentRect:contentRect
201 styleMask:windowStyle
202 backing:bufferingType
203 defer:deferCreation]) {
205 [self setBackgroundColor:[NSColor clearColor]];
206 [self startObservingClicks];
212 [self stopObservingClicks];
216 // Gets called when the menubar is clicked.
217 // Needed because the local event monitor doesn't see the click on the menubar.
218 - (void)beganTracking:(NSNotification*)notification {
222 // Install the callback.
223 - (void)startObservingClicks {
224 clickEventTap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSAnyEventMask
225 handler:^NSEvent* (NSEvent* event) {
226 if ([event window] == self)
228 NSEventType eventType = [event type];
229 if (eventType == NSLeftMouseDown || eventType == NSRightMouseDown)
234 NSNotificationCenter* notificationCenter =
235 [NSNotificationCenter defaultCenter];
236 [notificationCenter addObserver:self
237 selector:@selector(beganTracking:)
238 name:NSMenuDidBeginTrackingNotification
239 object:[NSApp mainMenu]];
242 // Remove the callback.
243 - (void)stopObservingClicks {
247 [NSEvent removeMonitor:clickEventTap_];
248 clickEventTap_ = nil;
250 NSNotificationCenter* notificationCenter =
251 [NSNotificationCenter defaultCenter];
252 [notificationCenter removeObserver:self
253 name:NSMenuDidBeginTrackingNotification
254 object:[NSApp mainMenu]];
261 // Maximum number of characters we allow in a tooltip.
262 const size_t kMaxTooltipLength = 1024;
264 // TODO(suzhe): Upstream this function.
265 blink::WebColor WebColorFromNSColor(NSColor *color) {
267 [color getRed:&r green:&g blue:&b alpha:&a];
270 std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
271 std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
272 std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8 |
273 std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
276 // Extract underline information from an attributed string. Mostly copied from
277 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
278 void ExtractUnderlines(
279 NSAttributedString* string,
280 std::vector<blink::WebCompositionUnderline>* underlines) {
281 int length = [[string string] length];
285 NSDictionary* attrs = [string attributesAtIndex:i
286 longestEffectiveRange:&range
287 inRange:NSMakeRange(i, length - i)];
288 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
289 blink::WebColor color = SK_ColorBLACK;
290 if (NSColor *colorAttr =
291 [attrs objectForKey:NSUnderlineColorAttributeName]) {
292 color = WebColorFromNSColor(
293 [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
295 underlines->push_back(
296 blink::WebCompositionUnderline(range.location,
299 [style intValue] > 1,
300 SK_ColorTRANSPARENT));
302 i = range.location + range.length;
306 // EnablePasswordInput() and DisablePasswordInput() are copied from
307 // enableSecureTextInput() and disableSecureTextInput() functions in
308 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
309 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
310 // here, because they are already called in webkit and they are system wide
312 void EnablePasswordInput() {
313 CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
314 TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
315 sizeof(CFArrayRef), &inputSources);
316 CFRelease(inputSources);
319 void DisablePasswordInput() {
320 TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
323 // Calls to [NSScreen screens], required by FlipYFromRectToScreen and
324 // FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
325 // value when screen info changes.
326 // TODO(ccameron): An observer on every RWHVCocoa will set this to false
327 // on NSApplicationDidChangeScreenParametersNotification. Only one observer
329 bool g_screen_info_up_to_date = false;
331 float FlipYFromRectToScreen(float y, float rect_height) {
332 TRACE_EVENT0("browser", "FlipYFromRectToScreen");
333 static CGFloat screen_zero_height = 0;
334 if (!g_screen_info_up_to_date) {
335 if ([[NSScreen screens] count] > 0) {
337 [[[NSScreen screens] objectAtIndex:0] frame].size.height;
338 g_screen_info_up_to_date = true;
343 return screen_zero_height - y - rect_height;
346 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
347 // left of the primary screen (Carbon coordinates), and stuffs it into a
349 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
350 gfx::Rect new_rect(NSRectToCGRect(rect));
351 new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
355 // Returns the window that visually contains the given view. This is different
356 // from [view window] in the case of tab dragging, where the view's owning
357 // window is a floating panel attached to the actual browser window that the tab
358 // is visually part of.
359 NSWindow* ApparentWindowForView(NSView* view) {
360 // TODO(shess): In case of !window, the view has been removed from
361 // the view hierarchy because the tab isn't main. Could retrieve
362 // the information from the main tab for our window.
363 NSWindow* enclosing_window = [view window];
365 // See if this is a tab drag window. The width check is to distinguish that
366 // case from extension popup windows.
367 NSWindow* ancestor_window = [enclosing_window parentWindow];
368 if (ancestor_window && (NSWidth([enclosing_window frame]) ==
369 NSWidth([ancestor_window frame]))) {
370 enclosing_window = ancestor_window;
373 return enclosing_window;
376 blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
377 gfx::Display display =
378 gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
380 NSScreen* screen = [NSScreen deepestScreen];
382 blink::WebScreenInfo results;
384 results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
385 results.depth = NSBitsPerPixelFromDepth([screen depth]);
386 results.depthPerComponent = NSBitsPerSampleFromDepth([screen depth]);
387 results.isMonochrome =
388 [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
389 results.rect = display.bounds();
390 results.availableRect = display.work_area();
391 results.orientationAngle = display.RotationAsDegree();
392 results.orientationType =
393 content::RenderWidgetHostViewBase::GetOrientationTypeForDesktop(display);
402 ////////////////////////////////////////////////////////////////////////////////
403 // DelegatedFrameHost, public:
405 ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
406 if (browser_compositor_view_)
407 return browser_compositor_view_->GetCompositor();
411 ui::Layer* RenderWidgetHostViewMac::GetLayer() {
412 return root_layer_.get();
415 RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
416 return render_widget_host_;
419 bool RenderWidgetHostViewMac::IsVisible() {
420 return !render_widget_host_->is_hidden();
423 gfx::Size RenderWidgetHostViewMac::DesiredFrameSize() {
424 return GetViewBounds().size();
427 float RenderWidgetHostViewMac::CurrentDeviceScaleFactor() {
428 return ViewScaleFactor();
431 gfx::Size RenderWidgetHostViewMac::ConvertViewSizeToPixel(
432 const gfx::Size& size) {
433 return gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size),
434 ViewScaleFactor())).size();
437 scoped_ptr<ResizeLock> RenderWidgetHostViewMac::CreateResizeLock(
438 bool defer_compositor_lock) {
440 ResizeLock* lock = NULL;
441 return scoped_ptr<ResizeLock>(lock);
444 DelegatedFrameHost* RenderWidgetHostViewMac::GetDelegatedFrameHost() const {
445 return delegated_frame_host_.get();
448 ////////////////////////////////////////////////////////////////////////////////
449 // BrowserCompositorViewMacClient, public:
451 bool RenderWidgetHostViewMac::BrowserCompositorViewShouldAckImmediately()
453 // If vsync is disabled, then always draw and ack frames immediately.
454 static bool is_vsync_disabled =
455 base::CommandLine::ForCurrentProcess()->HasSwitch(
456 switches::kDisableGpuVsync);
457 if (is_vsync_disabled)
460 // If the window is occluded, then this frame's display call may be severely
461 // throttled. This is a good thing, unless tab capture may be active, because
462 // the broadcast will be inappropriately throttled.
463 // http://crbug.com/350410
465 // If tab capture isn't active then only ack frames when we draw them.
466 if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber())
469 NSWindow* window = [cocoa_view_ window];
470 // If the view isn't even in the heirarchy then frames will never be drawn,
471 // so ack them immediately.
475 // Check the window occlusion API.
476 if ([window respondsToSelector:@selector(occlusionState)]) {
477 if ([window occlusionState] & NSWindowOcclusionStateVisible) {
478 // If the window is visible then it is safe to wait until frames are
479 // drawn to ack them.
482 // If the window is occluded then frames may never be drawn, so ack them
488 // If the window occlusion API is not present then ack frames when we draw
493 void RenderWidgetHostViewMac::BrowserCompositorViewFrameSwapped(
494 const std::vector<ui::LatencyInfo>& all_latency_info) {
495 if (!render_widget_host_)
497 for (auto latency_info : all_latency_info) {
498 latency_info.AddLatencyNumber(
499 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
500 render_widget_host_->FrameSwapped(latency_info);
504 NSView* RenderWidgetHostViewMac::BrowserCompositorSuperview() {
508 ui::Layer* RenderWidgetHostViewMac::BrowserCompositorRootLayer() {
509 return root_layer_.get();
512 ///////////////////////////////////////////////////////////////////////////////
513 // RenderWidgetHostViewBase, public:
516 void RenderWidgetHostViewBase::GetDefaultScreenInfo(
517 blink::WebScreenInfo* results) {
518 *results = GetWebScreenInfo(NULL);
521 ///////////////////////////////////////////////////////////////////////////////
522 // RenderWidgetHostViewMac, public:
524 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
525 : render_widget_host_(RenderWidgetHostImpl::From(widget)),
526 text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
527 can_compose_inline_(true),
528 browser_compositor_view_placeholder_(
529 new BrowserCompositorViewPlaceholderMac),
531 allow_pause_for_resize_or_repaint_(true),
533 fullscreen_parent_host_view_(NULL) {
534 // |cocoa_view_| owns us and we will be deleted when |cocoa_view_|
535 // goes away. Since we autorelease it, our caller must put
536 // |GetNativeView()| into the view hierarchy right after calling us.
537 cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
538 initWithRenderWidgetHostViewMac:this] autorelease];
540 // Make this view host a solid white layer when there is no content ready to
542 background_layer_.reset([[CALayer alloc] init]);
544 setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
545 [cocoa_view_ setLayer:background_layer_];
546 [cocoa_view_ setWantsLayer:YES];
548 if (IsDelegatedRendererEnabled()) {
549 root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
550 delegated_frame_host_.reset(new DelegatedFrameHost(this));
553 gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this);
555 render_widget_host_->SetView(this);
558 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
559 gfx::Screen::GetScreenFor(cocoa_view_)->RemoveObserver(this);
561 // This is being called from |cocoa_view_|'s destructor, so invalidate the
567 // Ensure that the browser compositor is destroyed in a safe order.
568 ShutdownBrowserCompositor();
570 // We are owned by RenderWidgetHostViewCocoa, so if we go away before the
571 // RenderWidgetHost does we need to tell it not to hold a stale pointer to
573 if (render_widget_host_)
574 render_widget_host_->SetView(NULL);
577 void RenderWidgetHostViewMac::SetDelegate(
578 NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
579 [cocoa_view_ setResponderDelegate:delegate];
582 void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
583 allow_pause_for_resize_or_repaint_ = allow;
586 ///////////////////////////////////////////////////////////////////////////////
587 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
589 void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
590 if (browser_compositor_view_)
593 TRACE_EVENT0("browser",
594 "RenderWidgetHostViewMac::EnsureBrowserCompositorView");
596 browser_compositor_view_.reset(new BrowserCompositorViewMac(this));
597 delegated_frame_host_->AddedToWindow();
598 delegated_frame_host_->WasShown(ui::LatencyInfo());
601 void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
602 TRACE_EVENT0("browser",
603 "RenderWidgetHostViewMac::DestroyBrowserCompositorView");
604 if (!browser_compositor_view_)
607 // Marking the DelegatedFrameHost as removed from the window hierarchy is
608 // necessary to remove all connections to its old ui::Compositor.
609 delegated_frame_host_->WasHidden();
610 delegated_frame_host_->RemovingFromWindow();
611 browser_compositor_view_.reset();
614 bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
616 IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
617 IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
618 IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
619 IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted,
620 OnGetRenderedTextCompleted)
621 IPC_MESSAGE_UNHANDLED(handled = false)
622 IPC_END_MESSAGE_MAP()
626 void RenderWidgetHostViewMac::InitAsChild(
627 gfx::NativeView parent_view) {
630 void RenderWidgetHostViewMac::InitAsPopup(
631 RenderWidgetHostView* parent_host_view,
632 const gfx::Rect& pos) {
633 bool activatable = popup_type_ == blink::WebPopupTypeNone;
634 [cocoa_view_ setCloseOnDeactivate:YES];
635 [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
637 NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
638 origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
640 popup_window_.reset([[RenderWidgetPopupWindow alloc]
641 initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
642 pos.width(), pos.height())
643 styleMask:NSBorderlessWindowMask
644 backing:NSBackingStoreBuffered
646 [popup_window_ setLevel:NSPopUpMenuWindowLevel];
647 [popup_window_ setReleasedWhenClosed:NO];
648 [popup_window_ makeKeyAndOrderFront:nil];
649 [[popup_window_ contentView] addSubview:cocoa_view_];
650 [cocoa_view_ setFrame:[[popup_window_ contentView] bounds]];
651 [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
652 [[NSNotificationCenter defaultCenter]
653 addObserver:cocoa_view_
654 selector:@selector(popupWindowWillClose:)
655 name:NSWindowWillCloseNotification
656 object:popup_window_];
659 // This function creates the fullscreen window and hides the dock and menubar if
660 // necessary. Note, this codepath is only used for pepper flash when
661 // pp::FlashFullScreen::SetFullscreen() is called. If
662 // pp::FullScreen::SetFullscreen() is called then the entire browser window
663 // will enter fullscreen instead.
664 void RenderWidgetHostViewMac::InitAsFullscreen(
665 RenderWidgetHostView* reference_host_view) {
666 fullscreen_parent_host_view_ =
667 static_cast<RenderWidgetHostViewMac*>(reference_host_view);
668 NSWindow* parent_window = nil;
669 if (reference_host_view)
670 parent_window = [reference_host_view->GetNativeView() window];
671 NSScreen* screen = [parent_window screen];
673 screen = [NSScreen mainScreen];
675 pepper_fullscreen_window_.reset([[PepperFlashFullscreenWindow alloc]
676 initWithContentRect:[screen frame]
677 styleMask:NSBorderlessWindowMask
678 backing:NSBackingStoreBuffered
680 [pepper_fullscreen_window_ setLevel:NSFloatingWindowLevel];
681 [pepper_fullscreen_window_ setReleasedWhenClosed:NO];
682 [cocoa_view_ setCanBeKeyView:YES];
683 [cocoa_view_ setFrame:[[pepper_fullscreen_window_ contentView] bounds]];
684 [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
685 // If the pepper fullscreen window isn't opaque then there are performance
686 // issues when it's on the discrete GPU and the Chrome window is being drawn
687 // to. http://crbug.com/171911
688 [pepper_fullscreen_window_ setOpaque:YES];
690 // Note that this forms a reference cycle between the fullscreen window and
691 // the rwhvmac: The PepperFlashFullscreenWindow retains cocoa_view_,
692 // but cocoa_view_ keeps pepper_fullscreen_window_ in an instance variable.
693 // This cycle is normally broken when -keyEvent: receives an <esc> key, which
694 // explicitly calls Shutdown on the render_widget_host_, which calls
695 // Destroy() on RWHVMac, which drops the reference to
696 // pepper_fullscreen_window_.
697 [[pepper_fullscreen_window_ contentView] addSubview:cocoa_view_];
699 // Note that this keeps another reference to pepper_fullscreen_window_.
700 fullscreen_window_manager_.reset([[FullscreenWindowManager alloc]
701 initWithWindow:pepper_fullscreen_window_.get()
702 desiredScreen:screen]);
703 [fullscreen_window_manager_ enterFullscreenMode];
704 [pepper_fullscreen_window_ makeKeyAndOrderFront:nil];
707 void RenderWidgetHostViewMac::release_pepper_fullscreen_window_for_testing() {
708 // See comment in InitAsFullscreen(): There is a reference cycle between
709 // rwhvmac and fullscreen window, which is usually broken by hitting <esc>.
710 // Tests that test pepper fullscreen mode without sending an <esc> event
711 // need to call this method to break the reference cycle.
712 [fullscreen_window_manager_ exitFullscreenMode];
713 fullscreen_window_manager_.reset();
714 [pepper_fullscreen_window_ close];
715 pepper_fullscreen_window_.reset();
718 int RenderWidgetHostViewMac::window_number() const {
719 NSWindow* window = [cocoa_view_ window];
722 return [window windowNumber];
725 float RenderWidgetHostViewMac::ViewScaleFactor() const {
726 return ui::GetScaleFactorForNativeView(cocoa_view_);
729 void RenderWidgetHostViewMac::UpdateDisplayLink() {
730 static bool is_vsync_disabled =
731 base::CommandLine::ForCurrentProcess()->HasSwitch(
732 switches::kDisableGpuVsync);
733 if (is_vsync_disabled)
736 NSScreen* screen = [[cocoa_view_ window] screen];
737 NSDictionary* screen_description = [screen deviceDescription];
738 NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
739 CGDirectDisplayID display_id = [screen_number unsignedIntValue];
741 display_link_ = DisplayLinkMac::GetForDisplay(display_id);
742 if (!display_link_.get()) {
743 // Note that on some headless systems, the display link will fail to be
744 // created, so this should not be a fatal error.
745 LOG(ERROR) << "Failed to create display link.";
749 void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
750 if (!render_widget_host_ || !display_link_.get())
753 if (!display_link_->GetVSyncParameters(&vsync_timebase_, &vsync_interval_)) {
754 vsync_timebase_ = base::TimeTicks();
755 vsync_interval_ = base::TimeDelta();
759 render_widget_host_->UpdateVSyncParameters(vsync_timebase_, vsync_interval_);
762 void RenderWidgetHostViewMac::SpeakText(const std::string& text) {
763 [NSApp speakString:base::SysUTF8ToNSString(text)];
766 void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
767 if (!render_widget_host_)
769 render_widget_host_->NotifyScreenInfoChanged();
772 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
773 return render_widget_host_;
776 void RenderWidgetHostViewMac::WasShown() {
777 if (!render_widget_host_->is_hidden())
780 ui::LatencyInfo renderer_latency_info;
781 renderer_latency_info.AddLatencyNumber(
782 ui::TAB_SHOW_COMPONENT,
783 render_widget_host_->GetLatencyComponentId(),
785 render_widget_host_->WasShown(renderer_latency_info);
787 // If there is not a frame being currently drawn, kick one, so that the below
788 // pause will have a frame to wait on.
789 render_widget_host_->ScheduleComposite();
790 PauseForPendingResizeOrRepaintsAndDraw();
793 void RenderWidgetHostViewMac::WasHidden() {
794 if (render_widget_host_->is_hidden())
797 DestroyBrowserCompositorView();
799 // If we have a renderer, then inform it that we are being hidden so it can
800 // reduce its resource utilization.
801 render_widget_host_->WasHidden();
804 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
805 gfx::Rect rect = GetViewBounds();
810 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
811 // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
812 // TODO(thakis): fix, http://crbug.com/73362
813 if (render_widget_host_->is_hidden())
816 // During the initial creation of the RenderWidgetHostView in
817 // WebContentsImpl::CreateRenderViewForRenderManager, SetSize is called with
818 // an empty size. In the Windows code flow, it is not ignored because
819 // subsequent sizing calls from the OS flow through TCVW::WasSized which calls
820 // SetSize() again. On Cocoa, we rely on the Cocoa view struture and resizer
821 // flags to keep things sized properly. On the other hand, if the size is not
822 // empty then this is a valid request for a pop-up.
823 if (rect.size().IsEmpty())
826 // Ignore the position of |rect| for non-popup rwhvs. This is because
827 // background tabs do not have a window, but the window is required for the
828 // coordinate conversions. Popups are always for a visible tab.
830 // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
831 // valid for resizing to be requested (e.g., during tab capture, to size the
832 // view to screen-capture resolution). In this case, simply treat the view as
833 // relative to the screen.
834 BOOL isRelativeToScreen = IsPopup() ||
835 ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
836 if (isRelativeToScreen) {
837 // The position of |rect| is screen coordinate system and we have to
838 // consider Cocoa coordinate system is upside-down and also multi-screen.
839 NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
840 NSSize size = NSMakeSize(rect.width(), rect.height());
841 size = [cocoa_view_ convertSize:size toView:nil];
842 origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
843 NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
844 size.width, size.height);
846 [popup_window_ setFrame:frame display:YES];
848 [cocoa_view_ setFrame:frame];
850 BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
851 gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
852 rect2.set_width(rect.width());
853 rect2.set_height(rect.height());
854 [cocoa_view_ setFrame:[superview flipRectToNSRect:rect2]];
858 gfx::Vector2dF RenderWidgetHostViewMac::GetLastScrollOffset() const {
859 return last_scroll_offset_;
862 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() const {
866 gfx::NativeViewId RenderWidgetHostViewMac::GetNativeViewId() const {
867 return reinterpret_cast<gfx::NativeViewId>(GetNativeView());
870 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
872 return static_cast<gfx::NativeViewAccessible>(NULL);
875 void RenderWidgetHostViewMac::MovePluginWindows(
876 const std::vector<WebPluginGeometry>& moves) {
877 // Must be overridden, but unused on this platform. Core Animation
878 // plugins are drawn by the GPU process (through the compositor),
879 // and Core Graphics plugins are drawn by the renderer process.
880 DCHECK_CURRENTLY_ON(BrowserThread::UI);
883 void RenderWidgetHostViewMac::Focus() {
884 [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
887 void RenderWidgetHostViewMac::Blur() {
889 [[cocoa_view_ window] makeFirstResponder:nil];
892 bool RenderWidgetHostViewMac::HasFocus() const {
893 return [[cocoa_view_ window] firstResponder] == cocoa_view_;
896 bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
897 if (delegated_frame_host_)
898 return delegated_frame_host_->CanCopyToBitmap();
902 void RenderWidgetHostViewMac::Show() {
903 [cocoa_view_ setHidden:NO];
908 void RenderWidgetHostViewMac::Hide() {
909 [cocoa_view_ setHidden:YES];
914 bool RenderWidgetHostViewMac::IsShowing() {
915 return ![cocoa_view_ isHidden];
918 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
919 NSRect bounds = [cocoa_view_ bounds];
920 // TODO(shess): In case of !window, the view has been removed from
921 // the view hierarchy because the tab isn't main. Could retrieve
922 // the information from the main tab for our window.
923 NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
924 if (!enclosing_window)
925 return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
927 bounds = [cocoa_view_ convertRect:bounds toView:nil];
928 bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
929 return FlipNSRectToRectScreen(bounds);
932 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
933 WebCursor web_cursor = cursor;
934 [cocoa_view_ updateCursor:web_cursor.GetNativeCursor()];
937 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
938 is_loading_ = is_loading;
939 // If we ever decide to show the waiting cursor while the page is loading
940 // like Chrome does on Windows, call |UpdateCursor()| here.
943 void RenderWidgetHostViewMac::TextInputTypeChanged(
944 ui::TextInputType type,
945 ui::TextInputMode input_mode,
946 bool can_compose_inline) {
947 if (text_input_type_ != type
948 || can_compose_inline_ != can_compose_inline) {
949 text_input_type_ = type;
950 can_compose_inline_ = can_compose_inline;
952 SetTextInputActive(true);
954 // Let AppKit cache the new input context to make IMEs happy.
955 // See http://crbug.com/73039.
956 [NSApp updateWindows];
959 UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_);
965 void RenderWidgetHostViewMac::ImeCancelComposition() {
966 [cocoa_view_ cancelComposition];
969 void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
970 const gfx::Range& range,
971 const std::vector<gfx::Rect>& character_bounds) {
972 // The RangeChanged message is only sent with valid values. The current
973 // caret position (start == end) will be sent if there is no IME range.
974 [cocoa_view_ setMarkedRange:range.ToNSRange()];
975 composition_range_ = range;
976 composition_bounds_ = character_bounds;
979 void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
984 void RenderWidgetHostViewMac::RenderWidgetHostGone() {
985 // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never
986 // called on the view.
987 // http://crbug.com/404828
988 ShutdownBrowserCompositor();
991 void RenderWidgetHostViewMac::Destroy() {
992 [[NSNotificationCenter defaultCenter]
993 removeObserver:cocoa_view_
994 name:NSWindowWillCloseNotification
995 object:popup_window_];
997 // We've been told to destroy.
998 [cocoa_view_ retain];
999 [cocoa_view_ removeFromSuperview];
1000 [cocoa_view_ autorelease];
1002 [popup_window_ close];
1003 popup_window_.autorelease();
1005 [fullscreen_window_manager_ exitFullscreenMode];
1006 fullscreen_window_manager_.reset();
1007 [pepper_fullscreen_window_ close];
1009 // This can be called as part of processing the window's responder
1010 // chain, for instance |-performKeyEquivalent:|. In that case the
1011 // object needs to survive until the stack unwinds.
1012 pepper_fullscreen_window_.autorelease();
1014 // Delete the delegated frame state, which will reach back into
1015 // render_widget_host_.
1016 ShutdownBrowserCompositor();
1018 // We get this call just before |render_widget_host_| deletes
1019 // itself. But we are owned by |cocoa_view_|, which may be retained
1020 // by some other code. Examples are WebContentsViewMac's
1021 // |latent_focus_view_| and TabWindowController's
1022 // |cachedContentView_|.
1023 render_widget_host_ = NULL;
1026 // Called from the renderer to tell us what the tooltip text should be. It
1027 // calls us frequently so we need to cache the value to prevent doing a lot
1029 void RenderWidgetHostViewMac::SetTooltipText(
1030 const base::string16& tooltip_text) {
1031 if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
1032 tooltip_text_ = tooltip_text;
1034 // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
1035 // Windows; we're just trying to be polite. Don't persist the trimmed
1036 // string, as then the comparison above will always fail and we'll try to
1037 // set it again every single time the mouse moves.
1038 base::string16 display_text = tooltip_text_;
1039 if (tooltip_text_.length() > kMaxTooltipLength)
1040 display_text = tooltip_text_.substr(0, kMaxTooltipLength);
1042 NSString* tooltip_nsstring = base::SysUTF16ToNSString(display_text);
1043 [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
1047 bool RenderWidgetHostViewMac::SupportsSpeech() const {
1048 return [NSApp respondsToSelector:@selector(speakString:)] &&
1049 [NSApp respondsToSelector:@selector(stopSpeaking:)];
1052 void RenderWidgetHostViewMac::SpeakSelection() {
1053 if (![NSApp respondsToSelector:@selector(speakString:)])
1056 if (selected_text_.empty() && render_widget_host_) {
1057 // If there's no selection, speak all text. Send an asynchronous IPC
1058 // request for fetching all the text for a webcontent.
1059 // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver.
1060 render_widget_host_->Send(new ViewMsg_GetRenderedText(
1061 render_widget_host_->GetRoutingID()));
1065 SpeakText(selected_text_);
1068 bool RenderWidgetHostViewMac::IsSpeaking() const {
1069 return [NSApp respondsToSelector:@selector(isSpeaking)] &&
1073 void RenderWidgetHostViewMac::StopSpeaking() {
1074 if ([NSApp respondsToSelector:@selector(stopSpeaking:)])
1075 [NSApp stopSpeaking:cocoa_view_];
1079 // RenderWidgetHostViewCocoa uses the stored selection text,
1080 // which implements NSServicesRequests protocol.
1082 void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
1084 const gfx::Range& range) {
1085 if (range.is_empty() || text.empty()) {
1086 selected_text_.clear();
1088 size_t pos = range.GetMin() - offset;
1089 size_t n = range.length();
1091 DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
1092 if (pos >= text.length()) {
1093 DCHECK(false) << "The text can not cover range.";
1096 selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
1099 [cocoa_view_ setSelectedRange:range.ToNSRange()];
1100 // Updates markedRange when there is no marked text so that retrieving
1101 // markedRange immediately after calling setMarkdText: returns the current
1103 if (![cocoa_view_ hasMarkedText]) {
1104 [cocoa_view_ setMarkedRange:range.ToNSRange()];
1107 RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
1110 void RenderWidgetHostViewMac::SelectionBoundsChanged(
1111 const ViewHostMsg_SelectionBounds_Params& params) {
1112 if (params.anchor_rect == params.focus_rect)
1113 caret_rect_ = params.anchor_rect;
1116 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
1117 RenderWidgetHostViewBase::SetShowingContextMenu(showing);
1119 // Create a fake mouse event to inform the render widget that the mouse
1121 NSWindow* window = [cocoa_view_ window];
1122 // TODO(asvitkine): If the location outside of the event stream doesn't
1123 // correspond to the current event (due to delayed event processing), then
1124 // this may result in a cursor flicker if there are later mouse move events
1125 // in the pipeline. Find a way to use the mouse location from the event that
1126 // dismissed the context menu.
1127 NSPoint location = [window mouseLocationOutsideOfEventStream];
1128 NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
1132 windowNumber:window_number()
1137 WebMouseEvent web_event =
1138 WebInputEventFactory::mouseEvent(event, cocoa_view_);
1140 web_event.type = WebInputEvent::MouseLeave;
1141 ForwardMouseEvent(web_event);
1144 bool RenderWidgetHostViewMac::IsPopup() const {
1145 return popup_type_ != blink::WebPopupTypeNone;
1148 void RenderWidgetHostViewMac::CopyFromCompositingSurface(
1149 const gfx::Rect& src_subrect,
1150 const gfx::Size& dst_size,
1151 const base::Callback<void(bool, const SkBitmap&)>& callback,
1152 const SkColorType color_type) {
1153 if (delegated_frame_host_) {
1154 delegated_frame_host_->CopyFromCompositingSurface(
1155 src_subrect, dst_size, callback, color_type);
1159 void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
1160 const gfx::Rect& src_subrect,
1161 const scoped_refptr<media::VideoFrame>& target,
1162 const base::Callback<void(bool)>& callback) {
1163 if (delegated_frame_host_) {
1164 delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
1165 src_subrect, target, callback);
1169 bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
1170 if (delegated_frame_host_)
1171 return delegated_frame_host_->CanCopyToVideoFrame();
1175 bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
1176 if (delegated_frame_host_)
1177 return delegated_frame_host_->CanSubscribeFrame();
1181 void RenderWidgetHostViewMac::BeginFrameSubscription(
1182 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
1183 if (delegated_frame_host_)
1184 delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
1187 void RenderWidgetHostViewMac::EndFrameSubscription() {
1188 if (delegated_frame_host_)
1189 delegated_frame_host_->EndFrameSubscription();
1192 // Sets whether or not to accept first responder status.
1193 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
1194 [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
1197 void RenderWidgetHostViewMac::ForwardMouseEvent(const WebMouseEvent& event) {
1198 if (render_widget_host_)
1199 render_widget_host_->ForwardMouseEvent(event);
1201 if (event.type == WebInputEvent::MouseLeave) {
1202 [cocoa_view_ setToolTipAtMousePoint:nil];
1203 tooltip_text_.clear();
1207 void RenderWidgetHostViewMac::KillSelf() {
1208 if (!weak_factory_.HasWeakPtrs()) {
1209 [cocoa_view_ setHidden:YES];
1210 base::MessageLoop::current()->PostTask(FROM_HERE,
1211 base::Bind(&RenderWidgetHostViewMac::ShutdownHost,
1212 weak_factory_.GetWeakPtr()));
1216 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
1217 const NativeWebKeyboardEvent& event) {
1218 // Check WebInputEvent type since multiple types of events can be sent into
1219 // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
1220 // necessary to avoid double processing.
1221 // Also check the native type, since NSFlagsChanged is considered a key event
1222 // for WebKit purposes, but isn't considered a key event by the OS.
1223 if (event.type == WebInputEvent::RawKeyDown &&
1224 [event.os_event type] == NSKeyDown)
1225 return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
1229 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
1230 const base::string16& text, int plugin_id) {
1231 if (render_widget_host_) {
1232 render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
1233 render_widget_host_->GetRoutingID(), text, plugin_id));
1237 bool RenderWidgetHostViewMac::GetLineBreakIndex(
1238 const std::vector<gfx::Rect>& bounds,
1239 const gfx::Range& range,
1240 size_t* line_break_point) {
1241 DCHECK(line_break_point);
1242 if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
1245 // We can't check line breaking completely from only rectangle array. Thus we
1246 // assume the line breaking as the next character's y offset is larger than
1247 // a threshold. Currently the threshold is determined as minimum y offset plus
1248 // 75% of maximum height.
1249 // TODO(nona): Check the threshold is reliable or not.
1250 // TODO(nona): Bidi support.
1251 const size_t loop_end_idx = std::min(bounds.size(), range.end());
1253 int min_y_offset = kint32max;
1254 for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1255 max_height = std::max(max_height, bounds[idx].height());
1256 min_y_offset = std::min(min_y_offset, bounds[idx].y());
1258 int line_break_threshold = min_y_offset + (max_height * 3 / 4);
1259 for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
1260 if (bounds[idx].y() > line_break_threshold) {
1261 *line_break_point = idx;
1268 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
1269 const gfx::Range& range,
1270 gfx::Range* actual_range) {
1271 DCHECK(actual_range);
1272 DCHECK(!composition_bounds_.empty());
1273 DCHECK(range.start() <= composition_bounds_.size());
1274 DCHECK(range.end() <= composition_bounds_.size());
1276 if (range.is_empty()) {
1277 *actual_range = range;
1278 if (range.start() == composition_bounds_.size()) {
1279 return gfx::Rect(composition_bounds_[range.start() - 1].right(),
1280 composition_bounds_[range.start() - 1].y(),
1282 composition_bounds_[range.start() - 1].height());
1284 return gfx::Rect(composition_bounds_[range.start()].x(),
1285 composition_bounds_[range.start()].y(),
1287 composition_bounds_[range.start()].height());
1292 if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
1293 end_idx = range.end();
1295 *actual_range = gfx::Range(range.start(), end_idx);
1296 gfx::Rect rect = composition_bounds_[range.start()];
1297 for (size_t i = range.start() + 1; i < end_idx; ++i) {
1298 rect.Union(composition_bounds_[i]);
1303 gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
1304 const gfx::Range& request_range) {
1305 if (composition_range_.is_empty())
1306 return gfx::Range::InvalidRange();
1308 if (request_range.is_reversed())
1309 return gfx::Range::InvalidRange();
1311 if (request_range.start() < composition_range_.start() ||
1312 request_range.start() > composition_range_.end() ||
1313 request_range.end() > composition_range_.end()) {
1314 return gfx::Range::InvalidRange();
1318 request_range.start() - composition_range_.start(),
1319 request_range.end() - composition_range_.start());
1322 WebContents* RenderWidgetHostViewMac::GetWebContents() {
1323 if (!render_widget_host_->IsRenderView())
1326 return WebContents::FromRenderViewHost(
1327 RenderViewHost::From(render_widget_host_));
1330 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
1333 NSRange* actual_range) {
1335 // This exists to make IMEs more responsive, see http://crbug.com/115920
1336 TRACE_EVENT0("browser",
1337 "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1339 // If requested range is same as caret location, we can just return it.
1340 if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) {
1342 *actual_range = range;
1343 *rect = NSRectFromCGRect(caret_rect_.ToCGRect());
1347 const gfx::Range request_range_in_composition =
1348 ConvertCharacterRangeToCompositionRange(gfx::Range(range));
1349 if (request_range_in_composition == gfx::Range::InvalidRange())
1352 // If firstRectForCharacterRange in WebFrame is failed in renderer,
1353 // ImeCompositionRangeChanged will be sent with empty vector.
1354 if (composition_bounds_.empty())
1356 DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
1358 gfx::Range ui_actual_range;
1359 *rect = NSRectFromCGRect(GetFirstRectForCompositionRange(
1360 request_range_in_composition,
1361 &ui_actual_range).ToCGRect());
1363 *actual_range = gfx::Range(
1364 composition_range_.start() + ui_actual_range.start(),
1365 composition_range_.start() + ui_actual_range.end()).ToNSRange();
1370 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
1371 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1375 void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
1376 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1380 void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
1383 void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
1386 bool RenderWidgetHostViewMac::HasAcceleratedSurface(
1387 const gfx::Size& desired_size) {
1388 if (browser_compositor_view_)
1389 return browser_compositor_view_->HasFrameOfSize(desired_size);
1393 void RenderWidgetHostViewMac::OnSwapCompositorFrame(
1394 uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
1395 TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
1397 last_scroll_offset_ = frame->metadata.root_scroll_offset;
1398 if (frame->delegated_frame_data) {
1399 float scale_factor = frame->metadata.device_scale_factor;
1401 // Compute the frame size based on the root render pass rect size.
1402 cc::RenderPass* root_pass =
1403 frame->delegated_frame_data->render_pass_list.back();
1404 gfx::Size pixel_size = root_pass->output_rect.size();
1405 gfx::Size dip_size =
1406 ConvertSizeToDIP(scale_factor, pixel_size);
1408 root_layer_->SetBounds(gfx::Rect(dip_size));
1409 if (!render_widget_host_->is_hidden()) {
1410 EnsureBrowserCompositorView();
1411 browser_compositor_view_->GetCompositor()->SetScaleAndSize(
1412 scale_factor, pixel_size);
1415 SendVSyncParametersToRenderer();
1417 delegated_frame_host_->SwapDelegatedFrame(
1419 frame->delegated_frame_data.Pass(),
1420 frame->metadata.device_scale_factor,
1421 frame->metadata.latency_info);
1423 DLOG(ERROR) << "Received unexpected frame type.";
1425 base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
1426 render_widget_host_->GetProcess()->ReceivedBadMessage();
1430 void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
1434 void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
1435 *results = GetWebScreenInfo(GetNativeView());
1438 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1439 // TODO(shess): In case of !window, the view has been removed from
1440 // the view hierarchy because the tab isn't main. Could retrieve
1441 // the information from the main tab for our window.
1442 NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
1443 if (!enclosing_window)
1446 NSRect bounds = [enclosing_window frame];
1447 return FlipNSRectToRectScreen(bounds);
1450 gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
1451 // TODO(kbr): may be able to eliminate PluginWindowHandle argument
1452 // completely on Mac OS.
1453 return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
1456 bool RenderWidgetHostViewMac::LockMouse() {
1460 mouse_locked_ = true;
1462 // Lock position of mouse cursor and hide it.
1463 CGAssociateMouseAndMouseCursorPosition(NO);
1466 // Clear the tooltip window.
1467 SetTooltipText(base::string16());
1472 void RenderWidgetHostViewMac::UnlockMouse() {
1475 mouse_locked_ = false;
1477 // Unlock position of mouse cursor and unhide it.
1478 CGAssociateMouseAndMouseCursorPosition(YES);
1481 if (render_widget_host_)
1482 render_widget_host_->LostMouseLock();
1485 void RenderWidgetHostViewMac::WheelEventAck(
1486 const blink::WebMouseWheelEvent& event,
1487 InputEventAckState ack_result) {
1488 bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
1489 // Only record a wheel event as unhandled if JavaScript handlers got a chance
1490 // to see it (no-op wheel events are ignored by the event dispatcher)
1491 if (event.deltaX || event.deltaY)
1492 [cocoa_view_ processedWheelEvent:event consumed:consumed];
1495 bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
1496 if (render_widget_host_)
1497 return render_widget_host_->Send(message);
1502 void RenderWidgetHostViewMac::ShutdownHost() {
1503 weak_factory_.InvalidateWeakPtrs();
1504 render_widget_host_->Shutdown();
1505 // Do not touch any members at this point, |this| has been deleted.
1508 void RenderWidgetHostViewMac::ShutdownBrowserCompositor() {
1509 DestroyBrowserCompositorView();
1510 delegated_frame_host_.reset();
1511 root_layer_.reset();
1512 browser_compositor_view_placeholder_.reset();
1515 void RenderWidgetHostViewMac::SetActive(bool active) {
1516 if (render_widget_host_) {
1517 render_widget_host_->SetActive(active);
1520 render_widget_host_->Focus();
1522 render_widget_host_->Blur();
1526 SetTextInputActive(active);
1528 [cocoa_view_ setPluginImeActive:NO];
1533 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
1534 if (render_widget_host_) {
1535 render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
1536 render_widget_host_->GetRoutingID(), visible));
1540 void RenderWidgetHostViewMac::WindowFrameChanged() {
1541 if (render_widget_host_) {
1542 render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
1543 render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
1548 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1549 RenderWidgetHostViewMacDictionaryHelper helper(this);
1550 helper.ShowDefinitionForSelection();
1553 void RenderWidgetHostViewMac::SetBackgroundOpaque(bool opaque) {
1554 RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
1555 if (render_widget_host_)
1556 render_widget_host_->SetBackgroundOpaque(opaque);
1559 BrowserAccessibilityManager*
1560 RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1561 BrowserAccessibilityDelegate* delegate) {
1562 return new BrowserAccessibilityManagerMac(
1564 BrowserAccessibilityManagerMac::GetEmptyDocument(),
1568 gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
1569 const gfx::Rect& bounds) {
1570 NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
1571 NSSize size = NSMakeSize(bounds.width(), bounds.height());
1572 origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
1573 NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
1574 NSPoint originInScreen =
1575 [[cocoa_view_ window] convertBaseToScreen:originInWindow];
1576 originInScreen.y = originInScreen.y - size.height;
1577 return gfx::Point(originInScreen.x, originInScreen.y);
1580 void RenderWidgetHostViewMac::AccessibilityShowMenu(const gfx::Point& point) {
1581 NSPoint location = NSMakePoint(point.x(), point.y());
1582 location = [[cocoa_view_ window] convertScreenToBase:location];
1583 NSEvent* fakeRightClick = [NSEvent
1584 mouseEventWithType:NSRightMouseDown
1588 windowNumber:[[cocoa_view_ window] windowNumber]
1589 context:[NSGraphicsContext currentContext]
1594 [cocoa_view_ mouseEvent:fakeRightClick];
1597 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1599 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1600 EnablePasswordInput();
1602 DisablePasswordInput();
1604 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
1605 DisablePasswordInput();
1609 void RenderWidgetHostViewMac::OnPluginFocusChanged(bool focused,
1611 [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
1614 void RenderWidgetHostViewMac::OnStartPluginIme() {
1615 [cocoa_view_ setPluginImeActive:YES];
1618 void RenderWidgetHostViewMac::OnGetRenderedTextCompleted(
1619 const std::string& text) {
1623 void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
1624 if (!render_widget_host_ || render_widget_host_->is_hidden())
1627 // Pausing for one view prevents others from receiving frames.
1628 // This may lead to large delays, causing overlaps. See crbug.com/352020.
1629 if (!allow_pause_for_resize_or_repaint_)
1632 // Wait for a frame of the right size to come in.
1633 if (browser_compositor_view_)
1634 browser_compositor_view_->BeginPumpingFrames();
1635 render_widget_host_->PauseForPendingResizeOrRepaints();
1636 if (browser_compositor_view_)
1637 browser_compositor_view_->EndPumpingFrames();
1640 SkColorType RenderWidgetHostViewMac::PreferredReadbackFormat() {
1641 return kN32_SkColorType;
1644 ////////////////////////////////////////////////////////////////////////////////
1645 // gfx::DisplayObserver, public:
1647 void RenderWidgetHostViewMac::OnDisplayAdded(const gfx::Display& display) {
1650 void RenderWidgetHostViewMac::OnDisplayRemoved(const gfx::Display& display) {
1653 void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
1654 const gfx::Display& display, uint32_t metrics) {
1655 gfx::Screen* screen = gfx::Screen::GetScreenFor(cocoa_view_);
1656 if (display.id() != screen->GetDisplayNearestWindow(cocoa_view_).id())
1659 UpdateScreenInfo(cocoa_view_);
1662 } // namespace content
1664 // RenderWidgetHostViewCocoa ---------------------------------------------------
1666 @implementation RenderWidgetHostViewCocoa
1667 @synthesize selectedRange = selectedRange_;
1668 @synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
1669 @synthesize markedRange = markedRange_;
1671 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
1672 self = [super initWithFrame:NSZeroRect];
1674 self.acceptsTouchEvents = YES;
1675 editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
1676 editCommand_helper_->AddEditingSelectorsToClass([self class]);
1678 renderWidgetHostView_.reset(r);
1679 canBeKeyView_ = YES;
1680 focusedPluginIdentifier_ = -1;
1683 if ([self respondsToSelector:
1684 @selector(setWantsBestResolutionOpenGLSurface:)]) {
1685 [self setWantsBestResolutionOpenGLSurface:YES];
1687 [[NSNotificationCenter defaultCenter]
1689 selector:@selector(didChangeScreenParameters:)
1690 name:NSApplicationDidChangeScreenParametersNotification
1697 // Unbind the GL context from this view. If this is not done before super's
1698 // dealloc is called then the GL context will crash when it reaches into
1699 // the view in its destructor.
1700 // http://crbug.com/255608
1701 if (renderWidgetHostView_)
1702 renderWidgetHostView_->AcceleratedSurfaceRelease();
1704 if (responderDelegate_ &&
1705 [responderDelegate_ respondsToSelector:@selector(viewGone:)])
1706 [responderDelegate_ viewGone:self];
1707 responderDelegate_.reset();
1709 [[NSNotificationCenter defaultCenter] removeObserver:self];
1714 - (void)didChangeScreenParameters:(NSNotification*)notify {
1715 g_screen_info_up_to_date = false;
1718 - (void)setResponderDelegate:
1719 (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
1720 DCHECK(!responderDelegate_);
1721 responderDelegate_.reset([delegate retain]);
1724 - (void)resetCursorRects {
1725 if (currentCursor_) {
1726 [self addCursorRect:[self visibleRect] cursor:currentCursor_];
1727 [currentCursor_ setOnMouseEntered:YES];
1731 - (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
1732 consumed:(BOOL)consumed {
1733 [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
1736 - (BOOL)respondsToSelector:(SEL)selector {
1737 // Trickiness: this doesn't mean "does this object's superclass respond to
1738 // this selector" but rather "does the -respondsToSelector impl from the
1739 // superclass say that this class responds to the selector".
1740 if ([super respondsToSelector:selector])
1743 if (responderDelegate_)
1744 return [responderDelegate_ respondsToSelector:selector];
1749 - (id)forwardingTargetForSelector:(SEL)selector {
1750 if ([responderDelegate_ respondsToSelector:selector])
1751 return responderDelegate_.get();
1753 return [super forwardingTargetForSelector:selector];
1756 - (void)setCanBeKeyView:(BOOL)can {
1757 canBeKeyView_ = can;
1760 - (BOOL)acceptsMouseEventsWhenInactive {
1761 // Some types of windows (balloons, always-on-top panels) want to accept mouse
1762 // clicks w/o the first click being treated as 'activation'. Same applies to
1763 // mouse move events.
1764 return [[self window] level] > NSNormalWindowLevel;
1767 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
1768 return [self acceptsMouseEventsWhenInactive];
1771 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
1772 takesFocusOnlyOnMouseDown_ = b;
1775 - (void)setCloseOnDeactivate:(BOOL)b {
1776 closeOnDeactivate_ = b;
1779 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
1780 NSWindow* window = [self window];
1781 // If this is a background window, don't handle mouse movement events. This
1782 // is the expected behavior on the Mac as evidenced by other applications.
1783 if ([theEvent type] == NSMouseMoved &&
1784 ![self acceptsMouseEventsWhenInactive] &&
1785 ![window isKeyWindow]) {
1789 // Use hitTest to check whether the mouse is over a nonWebContentView - in
1790 // which case the mouse event should not be handled by the render host.
1791 const SEL nonWebContentViewSelector = @selector(nonWebContentView);
1792 NSView* contentView = [window contentView];
1793 NSView* view = [contentView hitTest:[theEvent locationInWindow]];
1794 // Traverse the superview hierarchy as the hitTest will return the frontmost
1795 // view, such as an NSTextView, while nonWebContentView may be specified by
1798 if ([view respondsToSelector:nonWebContentViewSelector] &&
1799 [view performSelector:nonWebContentViewSelector]) {
1800 // The cursor is over a nonWebContentView - ignore this mouse event.
1803 if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
1804 !hasOpenMouseDown_) {
1805 // The cursor is over an overlapping render widget. This check is done by
1806 // both views so the one that's returned by -hitTest: will end up
1807 // processing the event.
1808 // Note that while dragging, we only get events for the render view where
1809 // drag started, even if mouse is actually over another view or outside
1810 // the window. Cocoa does this for us. We should handle these events and
1811 // not ignore (since there is no other render view to handle them). Thus
1812 // the |!hasOpenMouseDown_| check above.
1815 view = [view superview];
1820 - (void)mouseEvent:(NSEvent*)theEvent {
1821 TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
1822 if (responderDelegate_ &&
1823 [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1824 BOOL handled = [responderDelegate_ handleEvent:theEvent];
1829 if ([self shouldIgnoreMouseEvent:theEvent]) {
1830 // If this is the first such event, send a mouse exit to the host view.
1831 if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
1832 WebMouseEvent exitEvent =
1833 WebInputEventFactory::mouseEvent(theEvent, self);
1834 exitEvent.type = WebInputEvent::MouseLeave;
1835 exitEvent.button = WebMouseEvent::ButtonNone;
1836 renderWidgetHostView_->ForwardMouseEvent(exitEvent);
1838 mouseEventWasIgnored_ = YES;
1842 if (mouseEventWasIgnored_) {
1843 // If this is the first mouse event after a previous event that was ignored
1844 // due to the hitTest, send a mouse enter event to the host view.
1845 if (renderWidgetHostView_->render_widget_host_) {
1846 WebMouseEvent enterEvent =
1847 WebInputEventFactory::mouseEvent(theEvent, self);
1848 enterEvent.type = WebInputEvent::MouseMove;
1849 enterEvent.button = WebMouseEvent::ButtonNone;
1850 renderWidgetHostView_->ForwardMouseEvent(enterEvent);
1853 mouseEventWasIgnored_ = NO;
1855 // TODO(rohitrao): Probably need to handle other mouse down events here.
1856 if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
1857 if (renderWidgetHostView_->render_widget_host_)
1858 renderWidgetHostView_->render_widget_host_->OnPointerEventActivate();
1860 // Manually take focus after the click but before forwarding it to the
1862 [[self window] makeFirstResponder:self];
1865 // Don't cancel child popups; killing them on a mouse click would prevent the
1866 // user from positioning the insertion point in the text field spawning the
1867 // popup. A click outside the text field would cause the text field to drop
1868 // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
1869 // the popup anyway, so we're OK.
1871 NSEventType type = [theEvent type];
1872 if (type == NSLeftMouseDown)
1873 hasOpenMouseDown_ = YES;
1874 else if (type == NSLeftMouseUp)
1875 hasOpenMouseDown_ = NO;
1877 // TODO(suzhe): We should send mouse events to the input method first if it
1878 // wants to handle them. But it won't work without implementing method
1879 // - (NSUInteger)characterIndexForPoint:.
1880 // See: http://code.google.com/p/chromium/issues/detail?id=47141
1881 // Instead of sending mouse events to the input method first, we now just
1882 // simply confirm all ongoing composition here.
1883 if (type == NSLeftMouseDown || type == NSRightMouseDown ||
1884 type == NSOtherMouseDown) {
1885 [self confirmComposition];
1888 const WebMouseEvent event =
1889 WebInputEventFactory::mouseEvent(theEvent, self);
1890 renderWidgetHostView_->ForwardMouseEvent(event);
1893 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
1894 // |performKeyEquivalent:| is sent to all views of a window, not only down the
1895 // responder chain (cf. "Handling Key Equivalents" in
1896 // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
1897 // ). We only want to handle key equivalents if we're first responder.
1898 if ([[self window] firstResponder] != self)
1901 // If the event is reserved by the system, then do not pass it to web content.
1902 if (EventIsReservedBySystem(theEvent))
1905 // If we return |NO| from this function, cocoa will send the key event to
1906 // the menu and only if the menu does not process the event to |keyDown:|. We
1907 // want to send the event to a renderer _before_ sending it to the menu, so
1908 // we need to return |YES| for all events that might be swallowed by the menu.
1909 // We do not return |YES| for every keypress because we don't get |keyDown:|
1910 // events for keys that we handle this way.
1911 NSUInteger modifierFlags = [theEvent modifierFlags];
1912 if ((modifierFlags & NSCommandKeyMask) == 0) {
1913 // Make sure the menu does not contain key equivalents that don't
1915 DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
1919 // Command key combinations are sent via performKeyEquivalent rather than
1920 // keyDown:. We just forward this on and if WebCore doesn't want to handle
1921 // it, we let the WebContentsView figure out how to reinject it.
1922 [self keyEvent:theEvent wasKeyEquivalent:YES];
1926 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
1927 // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
1928 // returned NO. If this function returns |YES|, Cocoa sends the event to
1929 // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
1930 // to us instead of doing key view loop control, ctrl-left/right get handled
1932 // (However, there are still some keys that Cocoa swallows, e.g. the key
1933 // equivalent that Cocoa uses for toggling the input language. In this case,
1934 // that's actually a good thing, though -- see http://crbug.com/26115 .)
1938 - (EventHandled)keyEvent:(NSEvent*)theEvent {
1939 if (responderDelegate_ &&
1940 [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
1941 BOOL handled = [responderDelegate_ handleEvent:theEvent];
1943 return kEventHandled;
1946 [self keyEvent:theEvent wasKeyEquivalent:NO];
1947 return kEventHandled;
1950 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
1951 TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent");
1953 // If the user changes the system hotkey mapping after Chrome has been
1954 // launched, then it is possible that a formerly reserved system hotkey is no
1955 // longer reserved. The hotkey would have skipped the renderer, but would
1956 // also have not been handled by the system. If this is the case, immediately
1958 // TODO(erikchen): SystemHotkeyHelperMac should use the File System Events
1959 // api to monitor changes to system hotkeys. This logic will have to be
1961 // http://crbug.com/383558.
1962 if (EventIsReservedBySystem(theEvent))
1965 DCHECK([theEvent type] != NSKeyDown ||
1966 !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
1968 if ([theEvent type] == NSFlagsChanged) {
1969 // Ignore NSFlagsChanged events from the NumLock and Fn keys as
1970 // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
1971 int keyCode = [theEvent keyCode];
1972 if (!keyCode || keyCode == 10 || keyCode == 63)
1976 // Don't cancel child popups; the key events are probably what's triggering
1977 // the popup in the first place.
1979 RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
1982 NativeWebKeyboardEvent event(theEvent);
1984 // Force fullscreen windows to close on Escape so they won't keep the keyboard
1985 // grabbed or be stuck onscreen if the renderer is hanging.
1986 if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
1987 event.windowsKeyCode == ui::VKEY_ESCAPE &&
1988 renderWidgetHostView_->pepper_fullscreen_window()) {
1989 RenderWidgetHostViewMac* parent =
1990 renderWidgetHostView_->fullscreen_parent_host_view();
1992 parent->cocoa_view()->suppressNextEscapeKeyUp_ = YES;
1993 widgetHost->Shutdown();
1997 // Suppress the escape key up event if necessary.
1998 if (event.windowsKeyCode == ui::VKEY_ESCAPE && suppressNextEscapeKeyUp_) {
1999 if (event.type == NativeWebKeyboardEvent::KeyUp)
2000 suppressNextEscapeKeyUp_ = NO;
2004 // We only handle key down events and just simply forward other events.
2005 if ([theEvent type] != NSKeyDown) {
2006 widgetHost->ForwardKeyboardEvent(event);
2008 // Possibly autohide the cursor.
2009 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2010 [NSCursor setHiddenUntilMouseMoves:YES];
2015 base::scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
2017 // Records the current marked text state, so that we can know if the marked
2018 // text was deleted or not after handling the key down event.
2019 BOOL oldHasMarkedText = hasMarkedText_;
2021 // This method should not be called recursively.
2022 DCHECK(!handlingKeyDown_);
2024 // Tells insertText: and doCommandBySelector: that we are handling a key
2026 handlingKeyDown_ = YES;
2028 // These variables might be set when handling the keyboard event.
2029 // Clear them here so that we can know whether they have changed afterwards.
2030 textToBeInserted_.clear();
2031 markedText_.clear();
2032 underlines_.clear();
2033 unmarkTextCalled_ = NO;
2034 hasEditCommands_ = NO;
2035 editCommands_.clear();
2037 // Before doing anything with a key down, check to see if plugin IME has been
2038 // cancelled, since the plugin host needs to be informed of that before
2039 // receiving the keydown.
2040 if ([theEvent type] == NSKeyDown)
2041 [self checkForPluginImeCancellation];
2043 // Sends key down events to input method first, then we can decide what should
2044 // be done according to input method's feedback.
2045 // If a plugin is active, bypass this step since events are forwarded directly
2046 // to the plugin IME.
2047 if (focusedPluginIdentifier_ == -1)
2048 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
2050 handlingKeyDown_ = NO;
2052 // Indicates if we should send the key event and corresponding editor commands
2053 // after processing the input method result.
2054 BOOL delayEventUntilAfterImeCompostion = NO;
2056 // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
2057 // while an input method is composing or inserting a text.
2058 // Gmail checks this code in its onkeydown handler to stop auto-completing
2059 // e-mail addresses while composing a CJK text.
2060 // If the text to be inserted has only one character, then we don't need this
2061 // trick, because we'll send the text as a key press event instead.
2062 if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
2063 NativeWebKeyboardEvent fakeEvent = event;
2064 fakeEvent.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY
2065 fakeEvent.setKeyIdentifierFromWindowsKeyCode();
2066 fakeEvent.skip_in_browser = true;
2067 widgetHost->ForwardKeyboardEvent(fakeEvent);
2068 // If this key event was handled by the input method, but
2069 // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
2070 // enqueued edit commands, then in order to let webkit handle them
2071 // correctly, we need to send the real key event and corresponding edit
2072 // commands after processing the input method result.
2073 // We shouldn't do this if a new marked text was set by the input method,
2074 // otherwise the new marked text might be cancelled by webkit.
2075 if (hasEditCommands_ && !hasMarkedText_)
2076 delayEventUntilAfterImeCompostion = YES;
2078 if (!editCommands_.empty()) {
2079 widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2080 widgetHost->GetRoutingID(), editCommands_));
2082 widgetHost->ForwardKeyboardEvent(event);
2085 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2086 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2087 // be set to NULL. So we check it here and return immediately if it's NULL.
2088 if (!renderWidgetHostView_->render_widget_host_)
2091 // Then send keypress and/or composition related events.
2092 // If there was a marked text or the text to be inserted is longer than 1
2093 // character, then we send the text by calling ConfirmComposition().
2094 // Otherwise, if the text to be inserted only contains 1 character, then we
2095 // can just send a keypress event which is fabricated by changing the type of
2096 // the keydown event, so that we can retain all necessary informations, such
2097 // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
2098 // prevent the browser from handling it again.
2099 // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
2100 // handle BMP characters here, as we can always insert non-BMP characters as
2102 BOOL textInserted = NO;
2103 if (textToBeInserted_.length() >
2104 ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
2105 widgetHost->ImeConfirmComposition(
2106 textToBeInserted_, gfx::Range::InvalidRange(), false);
2110 // Updates or cancels the composition. If some text has been inserted, then
2111 // we don't need to cancel the composition explicitly.
2112 if (hasMarkedText_ && markedText_.length()) {
2113 // Sends the updated marked text to the renderer so it can update the
2114 // composition node in WebKit.
2115 // When marked text is available, |selectedRange_| will be the range being
2116 // selected inside the marked text.
2117 widgetHost->ImeSetComposition(markedText_, underlines_,
2118 selectedRange_.location,
2119 NSMaxRange(selectedRange_));
2120 } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
2121 if (unmarkTextCalled_) {
2122 widgetHost->ImeConfirmComposition(
2123 base::string16(), gfx::Range::InvalidRange(), false);
2125 widgetHost->ImeCancelComposition();
2129 // If the key event was handled by the input method but it also generated some
2130 // edit commands, then we need to send the real key event and corresponding
2131 // edit commands here. This usually occurs when the input method wants to
2132 // finish current composition session but still wants the application to
2133 // handle the key event. See http://crbug.com/48161 for reference.
2134 if (delayEventUntilAfterImeCompostion) {
2135 // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
2136 // with windowsKeyCode == 0xE5 has already been sent to webkit.
2137 // So before sending the real key down event, we need to send a fake key up
2138 // event to balance it.
2139 NativeWebKeyboardEvent fakeEvent = event;
2140 fakeEvent.type = blink::WebInputEvent::KeyUp;
2141 fakeEvent.skip_in_browser = true;
2142 widgetHost->ForwardKeyboardEvent(fakeEvent);
2143 // Not checking |renderWidgetHostView_->render_widget_host_| here because
2144 // a key event with |skip_in_browser| == true won't be handled by browser,
2145 // thus it won't destroy the widget.
2147 if (!editCommands_.empty()) {
2148 widgetHost->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
2149 widgetHost->GetRoutingID(), editCommands_));
2151 widgetHost->ForwardKeyboardEvent(event);
2153 // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
2154 // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
2155 // be set to NULL. So we check it here and return immediately if it's NULL.
2156 if (!renderWidgetHostView_->render_widget_host_)
2160 const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
2161 // Only send a corresponding key press event if there is no marked text.
2162 if (!hasMarkedText_) {
2163 if (!textInserted && textToBeInserted_.length() == 1) {
2164 // If a single character was inserted, then we just send it as a keypress
2166 event.type = blink::WebInputEvent::Char;
2167 event.text[0] = textToBeInserted_[0];
2169 event.skip_in_browser = true;
2170 widgetHost->ForwardKeyboardEvent(event);
2171 } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
2172 [[theEvent characters] length] > 0 &&
2173 (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
2174 (hasEditCommands_ && editCommands_.empty()))) {
2175 // We don't get insertText: calls if ctrl or cmd is down, or the key event
2176 // generates an insert command. So synthesize a keypress event for these
2177 // cases, unless the key event generated any other command.
2178 event.type = blink::WebInputEvent::Char;
2179 event.skip_in_browser = true;
2180 widgetHost->ForwardKeyboardEvent(event);
2184 // Possibly autohide the cursor.
2185 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
2186 [NSCursor setHiddenUntilMouseMoves:YES];
2189 - (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
2190 DCHECK(base::mac::IsOSLionOrLater());
2192 if ([event phase] != NSEventPhaseEnded &&
2193 [event phase] != NSEventPhaseCancelled) {
2197 if (renderWidgetHostView_->render_widget_host_) {
2198 // History-swiping is not possible if the logic reaches this point.
2199 // Allow rubber-banding in both directions.
2200 bool canRubberbandLeft = true;
2201 bool canRubberbandRight = true;
2202 const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2203 event, self, canRubberbandLeft, canRubberbandRight);
2204 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2207 if (endWheelMonitor_) {
2208 [NSEvent removeMonitor:endWheelMonitor_];
2209 endWheelMonitor_ = nil;
2213 - (void)beginGestureWithEvent:(NSEvent*)event {
2214 [responderDelegate_ beginGestureWithEvent:event];
2216 - (void)endGestureWithEvent:(NSEvent*)event {
2217 [responderDelegate_ endGestureWithEvent:event];
2219 - (void)touchesMovedWithEvent:(NSEvent*)event {
2220 [responderDelegate_ touchesMovedWithEvent:event];
2222 - (void)touchesBeganWithEvent:(NSEvent*)event {
2223 [responderDelegate_ touchesBeganWithEvent:event];
2225 - (void)touchesCancelledWithEvent:(NSEvent*)event {
2226 [responderDelegate_ touchesCancelledWithEvent:event];
2228 - (void)touchesEndedWithEvent:(NSEvent*)event {
2229 [responderDelegate_ touchesEndedWithEvent:event];
2232 // This is invoked only on 10.8 or newer when the user taps a word using
2234 - (void)quickLookWithEvent:(NSEvent*)event {
2235 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2236 TextInputClientMac::GetInstance()->GetStringAtPoint(
2237 renderWidgetHostView_->render_widget_host_,
2238 gfx::Point(point.x, NSHeight([self frame]) - point.y),
2239 ^(NSAttributedString* string, NSPoint baselinePoint) {
2240 if (string && [string length] > 0) {
2241 dispatch_async(dispatch_get_main_queue(), ^{
2242 [self showDefinitionForAttributedString:string
2243 atPoint:baselinePoint];
2250 // This method handles 2 different types of hardware events.
2251 // (Apple does not distinguish between them).
2252 // a. Scrolling the middle wheel of a mouse.
2253 // b. Swiping on the track pad.
2255 // This method is responsible for 2 types of behavior:
2256 // a. Scrolling the content of window.
2257 // b. Navigating forwards/backwards in history.
2259 // This is a brief description of the logic:
2260 // 1. If the content can be scrolled, scroll the content.
2261 // (This requires a roundtrip to blink to determine whether the content
2262 // can be scrolled.)
2263 // Once this logic is triggered, the navigate logic cannot be triggered
2264 // until the gesture finishes.
2265 // 2. If the user is making a horizontal swipe, start the navigate
2266 // forward/backwards UI.
2267 // Once this logic is triggered, the user can either cancel or complete
2268 // the gesture. If the user completes the gesture, all remaining touches
2269 // are swallowed, and not allowed to scroll the content. If the user
2270 // cancels the gesture, all remaining touches are forwarded to the content
2271 // scroll logic. The user cannot trigger the navigation logic again.
2272 - (void)scrollWheel:(NSEvent*)event {
2273 if (responderDelegate_ &&
2274 [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
2275 BOOL handled = [responderDelegate_ handleEvent:event];
2280 // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
2281 // the event is received even when the mouse cursor is no longer over the view
2282 // when the scrolling ends (e.g. if the tab was switched). This is necessary
2283 // for ending rubber-banding in such cases.
2284 if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
2285 !endWheelMonitor_) {
2287 [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
2288 handler:^(NSEvent* blockEvent) {
2289 [self shortCircuitScrollWheelEvent:blockEvent];
2294 // This is responsible for content scrolling!
2295 if (renderWidgetHostView_->render_widget_host_) {
2296 BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
2297 BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
2298 const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
2299 event, self, canRubberbandLeft, canRubberbandRight);
2300 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
2304 // Called repeatedly during a pinch gesture, with incremental change values.
2305 - (void)magnifyWithEvent:(NSEvent*)event {
2306 if (renderWidgetHostView_->render_widget_host_) {
2307 // Send a GesturePinchUpdate event.
2308 // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
2309 // GestureSrollBegin/End) as is done for touchscreen. Keeping track of when
2310 // a pinch is active would take a little more work here, and we don't need
2311 // it for anything yet.
2312 const WebGestureEvent& webEvent =
2313 WebInputEventFactory::gestureEvent(event, self);
2314 renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
2318 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
2319 NSWindow* oldWindow = [self window];
2321 NSNotificationCenter* notificationCenter =
2322 [NSNotificationCenter defaultCenter];
2324 // Backing property notifications crash on 10.6 when building with the 10.7
2325 // SDK, see http://crbug.com/260595.
2326 static BOOL supportsBackingPropertiesNotification =
2327 SupportsBackingPropertiesChangedNotification();
2330 if (supportsBackingPropertiesNotification) {
2333 name:NSWindowDidChangeBackingPropertiesNotification
2338 name:NSWindowDidMoveNotification
2342 name:NSWindowDidEndLiveResizeNotification
2346 if (supportsBackingPropertiesNotification) {
2349 selector:@selector(windowDidChangeBackingProperties:)
2350 name:NSWindowDidChangeBackingPropertiesNotification
2355 selector:@selector(windowChangedGlobalFrame:)
2356 name:NSWindowDidMoveNotification
2360 selector:@selector(windowChangedGlobalFrame:)
2361 name:NSWindowDidEndLiveResizeNotification
2366 - (void)updateScreenProperties{
2367 renderWidgetHostView_->UpdateBackingStoreScaleFactor();
2368 renderWidgetHostView_->UpdateDisplayLink();
2371 // http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
2372 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
2373 // Background tabs check if their scale factor or vsync properties changed
2374 // when they are added to a window.
2376 // Allocating a CGLayerRef with the current scale factor immediately from
2377 // this handler doesn't work. Schedule the backing store update on the
2378 // next runloop cycle, then things are read for CGLayerRef allocations to
2380 [self performSelector:@selector(updateScreenProperties)
2385 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
2386 renderWidgetHostView_->UpdateScreenInfo(
2387 renderWidgetHostView_->GetNativeView());
2390 - (void)setFrameSize:(NSSize)newSize {
2391 TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
2393 // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
2394 // -setFrame: isn't neccessary.
2395 [super setFrameSize:newSize];
2397 if (!renderWidgetHostView_->render_widget_host_)
2400 renderWidgetHostView_->render_widget_host_->SendScreenRects();
2401 renderWidgetHostView_->render_widget_host_->WasResized();
2402 if (renderWidgetHostView_->delegated_frame_host_)
2403 renderWidgetHostView_->delegated_frame_host_->WasResized();
2405 // Wait for the frame that WasResize might have requested. If the view is
2406 // being made visible at a new size, then this call will have no effect
2407 // because the view widget is still hidden, and the pause call in WasShown
2408 // will have this effect for us.
2409 renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
2412 - (BOOL)canBecomeKeyView {
2413 if (!renderWidgetHostView_->render_widget_host_)
2416 return canBeKeyView_;
2419 - (BOOL)acceptsFirstResponder {
2420 if (!renderWidgetHostView_->render_widget_host_)
2423 return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
2426 - (BOOL)becomeFirstResponder {
2427 if (!renderWidgetHostView_->render_widget_host_)
2430 renderWidgetHostView_->render_widget_host_->Focus();
2431 renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
2432 renderWidgetHostView_->SetTextInputActive(true);
2434 // Cancel any onging composition text which was left before we lost focus.
2435 // TODO(suzhe): We should do it in -resignFirstResponder: method, but
2436 // somehow that method won't be called when switching among different tabs.
2437 // See http://crbug.com/47209
2438 [self cancelComposition];
2440 NSNumber* direction = [NSNumber numberWithUnsignedInteger:
2441 [[self window] keyViewSelectionDirection]];
2442 NSDictionary* userInfo =
2443 [NSDictionary dictionaryWithObject:direction
2444 forKey:kSelectionDirection];
2445 [[NSNotificationCenter defaultCenter]
2446 postNotificationName:kViewDidBecomeFirstResponder
2453 - (BOOL)resignFirstResponder {
2454 renderWidgetHostView_->SetTextInputActive(false);
2455 if (!renderWidgetHostView_->render_widget_host_)
2458 if (closeOnDeactivate_)
2459 renderWidgetHostView_->KillSelf();
2461 renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
2462 renderWidgetHostView_->render_widget_host_->Blur();
2464 // We should cancel any onging composition whenever RWH's Blur() method gets
2465 // called, because in this case, webkit will confirm the ongoing composition
2467 [self cancelComposition];
2472 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
2473 if (responderDelegate_ &&
2475 respondsToSelector:@selector(validateUserInterfaceItem:
2479 [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
2484 SEL action = [item action];
2486 if (action == @selector(stopSpeaking:)) {
2487 return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2488 renderWidgetHostView_->IsSpeaking();
2490 if (action == @selector(startSpeaking:)) {
2491 return renderWidgetHostView_->render_widget_host_->IsRenderView() &&
2492 renderWidgetHostView_->SupportsSpeech();
2495 // For now, these actions are always enabled for render view,
2496 // this is sub-optimal.
2497 // TODO(suzhe): Plumb the "can*" methods up from WebCore.
2498 if (action == @selector(undo:) ||
2499 action == @selector(redo:) ||
2500 action == @selector(cut:) ||
2501 action == @selector(copy:) ||
2502 action == @selector(copyToFindPboard:) ||
2503 action == @selector(paste:) ||
2504 action == @selector(pasteAndMatchStyle:)) {
2505 return renderWidgetHostView_->render_widget_host_->IsRenderView();
2508 return editCommand_helper_->IsMenuItemEnabled(action, self);
2511 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
2512 return renderWidgetHostView_.get();
2515 // Determine whether we should autohide the cursor (i.e., hide it until mouse
2516 // move) for the given event. Customize here to be more selective about which
2517 // key presses to autohide on.
2518 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
2519 return ([event type] == NSKeyDown &&
2520 !([event modifierFlags] & NSCommandKeyMask)) ? YES : NO;
2523 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
2524 index:(NSUInteger)index
2525 maxCount:(NSUInteger)maxCount {
2526 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2527 NSUInteger totalLength = [fullArray count];
2528 if (index >= totalLength)
2530 NSUInteger length = MIN(totalLength - index, maxCount);
2531 return [fullArray subarrayWithRange:NSMakeRange(index, length)];
2534 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
2535 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
2536 return [fullArray count];
2539 - (id)accessibilityAttributeValue:(NSString *)attribute {
2540 BrowserAccessibilityManager* manager =
2541 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2543 // Contents specifies document view of RenderWidgetHostViewCocoa provided by
2544 // BrowserAccessibilityManager. Children includes all subviews in addition to
2545 // contents. Currently we do not have subviews besides the document view.
2546 if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
2547 [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
2549 return [NSArray arrayWithObjects:manager->
2550 GetRoot()->ToBrowserAccessibilityCocoa(), nil];
2551 } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
2552 return NSAccessibilityScrollAreaRole;
2554 id ret = [super accessibilityAttributeValue:attribute];
2558 - (NSArray*)accessibilityAttributeNames {
2559 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
2560 [ret addObject:NSAccessibilityContentsAttribute];
2561 [ret addObjectsFromArray:[super accessibilityAttributeNames]];
2565 - (id)accessibilityHitTest:(NSPoint)point {
2566 BrowserAccessibilityManager* manager =
2567 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2570 NSPoint pointInWindow = [[self window] convertScreenToBase:point];
2571 NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
2572 localPoint.y = NSHeight([self bounds]) - localPoint.y;
2573 BrowserAccessibilityCocoa* root =
2574 manager->GetRoot()->ToBrowserAccessibilityCocoa();
2575 id obj = [root accessibilityHitTest:localPoint];
2579 - (BOOL)accessibilityIsIgnored {
2580 BrowserAccessibilityManager* manager =
2581 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2585 - (NSUInteger)accessibilityGetIndexOf:(id)child {
2586 BrowserAccessibilityManager* manager =
2587 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2588 // Only child is root.
2590 manager->GetRoot()->ToBrowserAccessibilityCocoa() == child) {
2597 - (id)accessibilityFocusedUIElement {
2598 BrowserAccessibilityManager* manager =
2599 renderWidgetHostView_->GetHost()->GetRootBrowserAccessibilityManager();
2601 BrowserAccessibility* focused_item = manager->GetFocus(NULL);
2602 DCHECK(focused_item);
2604 BrowserAccessibilityCocoa* focused_item_cocoa =
2605 focused_item->ToBrowserAccessibilityCocoa();
2606 DCHECK(focused_item_cocoa);
2607 if (focused_item_cocoa)
2608 return focused_item_cocoa;
2611 return [super accessibilityFocusedUIElement];
2614 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
2615 // with minor modifications for code style and commenting.
2617 // The 'public' interface is -setToolTipAtMousePoint:. This differs from
2618 // -setToolTip: in that the updated tooltip takes effect immediately,
2619 // without the user's having to move the mouse out of and back into the view.
2621 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
2622 // the view, which in turn requires overriding some internal tracking-rect
2623 // methods (to keep track of its owner & userdata, which need to be filled out
2624 // in the fake events.) --snej 7/6/09
2628 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
2629 * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
2631 * Redistribution and use in source and binary forms, with or without
2632 * modification, are permitted provided that the following conditions
2635 * 1. Redistributions of source code must retain the above copyright
2636 * notice, this list of conditions and the following disclaimer.
2637 * 2. Redistributions in binary form must reproduce the above copyright
2638 * notice, this list of conditions and the following disclaimer in the
2639 * documentation and/or other materials provided with the distribution.
2640 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
2641 * its contributors may be used to endorse or promote products derived
2642 * from this software without specific prior written permission.
2644 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
2645 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2646 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2647 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2648 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2649 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2650 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2651 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2652 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2653 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2656 // Any non-zero value will do, but using something recognizable might help us
2658 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
2660 // Override of a public NSView method, replacing the inherited functionality.
2661 // See above for rationale.
2662 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
2664 userData:(void *)data
2665 assumeInside:(BOOL)assumeInside {
2666 DCHECK(trackingRectOwner_ == nil);
2667 trackingRectOwner_ = owner;
2668 trackingRectUserData_ = data;
2669 return kTrackingRectTag;
2672 // Override of (apparently) a private NSView method(!) See above for rationale.
2673 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
2675 userData:(void *)data
2676 assumeInside:(BOOL)assumeInside
2677 useTrackingNum:(int)tag {
2678 DCHECK(tag == 0 || tag == kTrackingRectTag);
2679 DCHECK(trackingRectOwner_ == nil);
2680 trackingRectOwner_ = owner;
2681 trackingRectUserData_ = data;
2682 return kTrackingRectTag;
2685 // Override of (apparently) a private NSView method(!) See above for rationale.
2686 - (void)_addTrackingRects:(NSRect *)rects
2688 userDataList:(void **)userDataList
2689 assumeInsideList:(BOOL *)assumeInsideList
2690 trackingNums:(NSTrackingRectTag *)trackingNums
2693 DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
2694 DCHECK(trackingRectOwner_ == nil);
2695 trackingRectOwner_ = owner;
2696 trackingRectUserData_ = userDataList[0];
2697 trackingNums[0] = kTrackingRectTag;
2700 // Override of a public NSView method, replacing the inherited functionality.
2701 // See above for rationale.
2702 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
2706 if (tag == kTrackingRectTag) {
2707 trackingRectOwner_ = nil;
2711 if (tag == lastToolTipTag_) {
2712 [super removeTrackingRect:tag];
2713 lastToolTipTag_ = 0;
2717 // If any other tracking rect is being removed, we don't know how it was
2718 // created and it's possible there's a leak involved (see Radar 3500217).
2722 // Override of (apparently) a private NSView method(!)
2723 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
2724 for (int i = 0; i < count; ++i) {
2728 DCHECK(tag == kTrackingRectTag);
2729 trackingRectOwner_ = nil;
2733 // Sends a fake NSMouseExited event to the view for its current tracking rect.
2734 - (void)_sendToolTipMouseExited {
2735 // Nothing matters except window, trackingNumber, and userData.
2736 int windowNumber = [[self window] windowNumber];
2737 NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2738 location:NSZeroPoint
2741 windowNumber:windowNumber
2744 trackingNumber:kTrackingRectTag
2745 userData:trackingRectUserData_];
2746 [trackingRectOwner_ mouseExited:fakeEvent];
2749 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
2750 - (void)_sendToolTipMouseEntered {
2751 // Nothing matters except window, trackingNumber, and userData.
2752 int windowNumber = [[self window] windowNumber];
2753 NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2754 location:NSZeroPoint
2757 windowNumber:windowNumber
2760 trackingNumber:kTrackingRectTag
2761 userData:trackingRectUserData_];
2762 [trackingRectOwner_ mouseEntered:fakeEvent];
2765 // Sets the view's current tooltip, to be displayed at the current mouse
2766 // location. (This does not make the tooltip appear -- as usual, it only
2767 // appears after a delay.) Pass null to remove the tooltip.
2768 - (void)setToolTipAtMousePoint:(NSString *)string {
2769 NSString *toolTip = [string length] == 0 ? nil : string;
2770 if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
2771 (!toolTip && !toolTip_)) {
2776 [self _sendToolTipMouseExited];
2779 toolTip_.reset([toolTip copy]);
2782 // See radar 3500217 for why we remove all tooltips
2783 // rather than just the single one we created.
2784 [self removeAllToolTips];
2785 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2786 lastToolTipTag_ = [self addToolTipRect:wideOpenRect
2789 [self _sendToolTipMouseEntered];
2793 // NSView calls this to get the text when displaying the tooltip.
2794 - (NSString *)view:(NSView *)view
2795 stringForToolTip:(NSToolTipTag)tag
2796 point:(NSPoint)point
2797 userData:(void *)data {
2798 return [[toolTip_ copy] autorelease];
2801 // Below is our NSTextInputClient implementation.
2803 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
2804 // functions to process this event.
2806 // [WebHTMLView keyDown] ->
2807 // EventHandler::keyEvent() ->
2809 // [WebEditorClient handleKeyboardEvent] ->
2810 // [WebHTMLView _interceptEditingKeyEvent] ->
2811 // [NSResponder interpretKeyEvents] ->
2812 // [WebHTMLView insertText] ->
2813 // Editor::insertText()
2815 // Unfortunately, it is hard for Chromium to use this implementation because
2816 // it causes key-typing jank.
2817 // RenderWidgetHostViewMac is running in a browser process. On the other
2818 // hand, Editor and EventHandler are running in a renderer process.
2819 // So, if we used this implementation, a NSKeyDown event is dispatched to
2820 // the following functions of Chromium.
2822 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2823 // |Sync IPC (KeyDown)| (*1) ->
2824 // EventHandler::keyEvent() (renderer) ->
2826 // EditorClientImpl::handleKeyboardEvent() (renderer) ->
2827 // |Sync IPC| (*2) ->
2828 // [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
2829 // [self interpretKeyEvents] ->
2830 // [RenderWidgetHostViewMac insertText] (browser) ->
2832 // Editor::insertText() (renderer)
2834 // (*1) we need to wait until this call finishes since WebHTMLView uses the
2835 // result of EventHandler::keyEvent().
2836 // (*2) we need to wait until this call finishes since WebEditorClient uses
2837 // the result of [WebHTMLView _interceptEditingKeyEvent].
2839 // This needs many sync IPC messages sent between a browser and a renderer for
2840 // each key event, which would probably result in key-typing jank.
2841 // To avoid this problem, this implementation processes key events (and input
2842 // method events) totally in a browser process and sends asynchronous input
2843 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
2844 // renderer process.
2846 // [RenderWidgetHostViewMac keyEvent] (browser) ->
2847 // |Async IPC (RawKeyDown)| ->
2848 // [self interpretKeyEvents] ->
2849 // [RenderWidgetHostViewMac insertText] (browser) ->
2850 // |Async IPC (Char)| ->
2851 // Editor::insertText() (renderer)
2853 // Since this implementation doesn't have to wait any IPC calls, this doesn't
2854 // make any key-typing jank. --hbono 7/23/09
2857 extern NSString *NSTextInputReplacementRangeAttributeName;
2860 - (NSArray *)validAttributesForMarkedText {
2861 // This code is just copied from WebKit except renaming variables.
2862 if (!validAttributesForMarkedText_) {
2863 validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
2864 NSUnderlineStyleAttributeName,
2865 NSUnderlineColorAttributeName,
2866 NSMarkedClauseSegmentAttributeName,
2867 NSTextInputReplacementRangeAttributeName,
2870 return validAttributesForMarkedText_.get();
2873 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
2874 DCHECK([self window]);
2875 // |thePoint| is in screen coordinates, but needs to be converted to WebKit
2876 // coordinates (upper left origin). Scroll offsets will be taken care of in
2878 thePoint = [[self window] convertScreenToBase:thePoint];
2879 thePoint = [self convertPoint:thePoint fromView:nil];
2880 thePoint.y = NSHeight([self frame]) - thePoint.y;
2883 TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
2884 renderWidgetHostView_->render_widget_host_,
2885 gfx::Point(thePoint.x, thePoint.y));
2889 - (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
2890 actualRange:(NSRangePointer)actualRange {
2892 if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(
2896 rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
2897 renderWidgetHostView_->render_widget_host_, theRange);
2899 // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2901 *actualRange = theRange;
2904 // The returned rectangle is in WebKit coordinates (upper left origin), so
2905 // flip the coordinate system.
2906 NSRect viewFrame = [self frame];
2907 rect.origin.y = NSHeight(viewFrame) - NSMaxY(rect);
2911 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
2912 actualRange:(NSRangePointer)actualRange {
2913 NSRect rect = [self firstViewRectForCharacterRange:theRange
2914 actualRange:actualRange];
2916 // Convert into screen coordinates for return.
2917 rect = [self convertRect:rect toView:nil];
2918 rect.origin = [[self window] convertBaseToScreen:rect.origin];
2922 - (NSRange)markedRange {
2923 // An input method calls this method to check if an application really has
2924 // a text being composed when hasMarkedText call returns true.
2925 // Returns the range saved in the setMarkedText method so the input method
2926 // calls the setMarkedText method and we can update the composition node
2927 // there. (When this method returns an empty range, the input method doesn't
2928 // call the setMarkedText method.)
2929 return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
2932 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
2933 actualRange:(NSRangePointer)actualRange {
2934 // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
2936 *actualRange = range;
2937 NSAttributedString* str =
2938 TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
2939 renderWidgetHostView_->render_widget_host_, range);
2943 - (NSInteger)conversationIdentifier {
2944 return reinterpret_cast<NSInteger>(self);
2947 // Each RenderWidgetHostViewCocoa has its own input context, but we return
2948 // nil when the caret is in non-editable content or password box to avoid
2949 // making input methods do their work.
2950 - (NSTextInputContext *)inputContext {
2951 if (focusedPluginIdentifier_ != -1)
2952 return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
2954 switch(renderWidgetHostView_->text_input_type_) {
2955 case ui::TEXT_INPUT_TYPE_NONE:
2956 case ui::TEXT_INPUT_TYPE_PASSWORD:
2959 return [super inputContext];
2963 - (BOOL)hasMarkedText {
2964 // An input method calls this function to figure out whether or not an
2965 // application is really composing a text. If it is composing, it calls
2966 // the markedRange method, and maybe calls the setMarkedText method.
2967 // It seems an input method usually calls this function when it is about to
2968 // cancel an ongoing composition. If an application has a non-empty marked
2969 // range, it calls the setMarkedText method to delete the range.
2970 return hasMarkedText_;
2973 - (void)unmarkText {
2974 // Delete the composition node of the renderer and finish an ongoing
2976 // It seems an input method calls the setMarkedText method and set an empty
2977 // text when it cancels an ongoing composition, i.e. I have never seen an
2978 // input method calls this method.
2979 hasMarkedText_ = NO;
2980 markedText_.clear();
2981 underlines_.clear();
2983 // If we are handling a key down event, then ConfirmComposition() will be
2984 // called in keyEvent: method.
2985 if (!handlingKeyDown_) {
2986 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
2987 base::string16(), gfx::Range::InvalidRange(), false);
2989 unmarkTextCalled_ = YES;
2993 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
2994 replacementRange:(NSRange)replacementRange {
2995 // An input method updates the composition string.
2996 // We send the given text and range to the renderer so it can update the
2997 // composition node of WebKit.
2998 // TODO(suzhe): It's hard for us to support replacementRange without accessing
2999 // the full web content.
3000 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3001 NSString* im_text = isAttributedString ? [string string] : string;
3002 int length = [im_text length];
3004 // |markedRange_| will get set on a callback from ImeSetComposition().
3005 selectedRange_ = newSelRange;
3006 markedText_ = base::SysNSStringToUTF16(im_text);
3007 hasMarkedText_ = (length > 0);
3009 underlines_.clear();
3010 if (isAttributedString) {
3011 ExtractUnderlines(string, &underlines_);
3013 // Use a thin black underline by default.
3014 underlines_.push_back(blink::WebCompositionUnderline(
3015 0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
3018 // If we are handling a key down event, then SetComposition() will be
3019 // called in keyEvent: method.
3020 // Input methods of Mac use setMarkedText calls with an empty text to cancel
3021 // an ongoing composition. So, we should check whether or not the given text
3022 // is empty to update the input method state. (Our input method backend can
3023 // automatically cancels an ongoing composition when we send an empty text.
3024 // So, it is OK to send an empty text to the renderer.)
3025 if (!handlingKeyDown_) {
3026 renderWidgetHostView_->render_widget_host_->ImeSetComposition(
3027 markedText_, underlines_,
3028 newSelRange.location, NSMaxRange(newSelRange));
3032 - (void)doCommandBySelector:(SEL)selector {
3033 // An input method calls this function to dispatch an editing command to be
3034 // handled by this view.
3035 if (selector == @selector(noop:))
3038 std::string command(
3039 [RenderWidgetHostViewMacEditCommandHelper::
3040 CommandNameForSelector(selector) UTF8String]);
3042 // If this method is called when handling a key down event, then we need to
3043 // handle the command in the key event handler. Otherwise we can just handle
3045 if (handlingKeyDown_) {
3046 hasEditCommands_ = YES;
3047 // We ignore commands that insert characters, because this was causing
3048 // strange behavior (e.g. tab always inserted a tab rather than moving to
3049 // the next field on the page).
3050 if (!StartsWithASCII(command, "insert", false))
3051 editCommands_.push_back(EditCommand(command, ""));
3053 RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
3054 rwh->Send(new InputMsg_ExecuteEditCommand(rwh->GetRoutingID(),
3059 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
3060 // An input method has characters to be inserted.
3061 // Same as Linux, Mac calls this method not only:
3062 // * when an input method finishs composing text, but also;
3063 // * when we type an ASCII character (without using input methods).
3064 // When we aren't using input methods, we should send the given character as
3065 // a Char event so it is dispatched to an onkeypress() event handler of
3067 // On the other hand, when we are using input methods, we should send the
3068 // given characters as an input method event and prevent the characters from
3069 // being dispatched to onkeypress() event handlers.
3070 // Text inserting might be initiated by other source instead of keyboard
3071 // events, such as the Characters dialog. In this case the text should be
3072 // sent as an input method event as well.
3073 // TODO(suzhe): It's hard for us to support replacementRange without accessing
3074 // the full web content.
3075 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3076 NSString* im_text = isAttributedString ? [string string] : string;
3077 if (handlingKeyDown_) {
3078 textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
3080 gfx::Range replacement_range(replacementRange);
3081 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3082 base::SysNSStringToUTF16(im_text), replacement_range, false);
3085 // Inserting text will delete all marked text automatically.
3086 hasMarkedText_ = NO;
3089 - (void)insertText:(id)string {
3090 [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
3093 - (void)viewDidMoveToWindow {
3095 [self updateScreenProperties];
3097 if (canBeKeyView_) {
3098 NSWindow* newWindow = [self window];
3099 // Pointer comparison only, since we don't know if lastWindow_ is still
3102 // If we move into a new window, refresh the frame information. We
3103 // don't need to do it if it was the same window as it used to be in,
3104 // since that case is covered by WasShown(). We only want to do this for
3105 // real browser views, not popups.
3106 if (newWindow != lastWindow_) {
3107 lastWindow_ = newWindow;
3108 renderWidgetHostView_->WindowFrameChanged();
3113 // If we switch windows (or are removed from the view hierarchy), cancel any
3114 // open mouse-downs.
3115 if (hasOpenMouseDown_) {
3116 WebMouseEvent event;
3117 event.type = WebInputEvent::MouseUp;
3118 event.button = WebMouseEvent::ButtonLeft;
3119 renderWidgetHostView_->ForwardMouseEvent(event);
3121 hasOpenMouseDown_ = NO;
3125 - (void)undo:(id)sender {
3126 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3128 web_contents->Undo();
3131 - (void)redo:(id)sender {
3132 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3134 web_contents->Redo();
3137 - (void)cut:(id)sender {
3138 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3140 web_contents->Cut();
3143 - (void)copy:(id)sender {
3144 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3146 web_contents->Copy();
3149 - (void)copyToFindPboard:(id)sender {
3150 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3152 web_contents->CopyToFindPboard();
3155 - (void)paste:(id)sender {
3156 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3158 web_contents->Paste();
3161 - (void)pasteAndMatchStyle:(id)sender {
3162 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3164 web_contents->PasteAndMatchStyle();
3167 - (void)selectAll:(id)sender {
3168 // editCommand_helper_ adds implementations for most NSResponder methods
3169 // dynamically. But the renderer side only sends selection results back to
3170 // the browser if they were triggered by a keyboard event or went through
3171 // one of the Select methods on RWH. Since selectAll: is called from the
3172 // menu handler, neither is true.
3173 // Explicitly call SelectAll() here to make sure the renderer returns
3174 // selection results.
3175 WebContents* web_contents = renderWidgetHostView_->GetWebContents();
3177 web_contents->SelectAll();
3180 - (void)startSpeaking:(id)sender {
3181 renderWidgetHostView_->SpeakSelection();
3184 - (void)stopSpeaking:(id)sender {
3185 renderWidgetHostView_->StopSpeaking();
3188 - (void)cancelComposition {
3189 if (!hasMarkedText_)
3192 // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
3193 // doesn't call any NSTextInput functions, such as setMarkedText or
3194 // insertText. So, we need to send an IPC message to a renderer so it can
3195 // delete the composition node.
3196 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3197 [currentInputManager markedTextAbandoned:self];
3199 hasMarkedText_ = NO;
3200 // Should not call [self unmarkText] here, because it'll send unnecessary
3201 // cancel composition IPC message to the renderer.
3204 - (void)confirmComposition {
3205 if (!hasMarkedText_)
3208 if (renderWidgetHostView_->render_widget_host_)
3209 renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
3210 base::string16(), gfx::Range::InvalidRange(), false);
3212 [self cancelComposition];
3215 - (void)setPluginImeActive:(BOOL)active {
3216 if (active == pluginImeActive_)
3219 pluginImeActive_ = active;
3221 [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
3222 renderWidgetHostView_->PluginImeCompositionCompleted(
3223 base::string16(), focusedPluginIdentifier_);
3227 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
3229 focusedPluginIdentifier_ = pluginId;
3230 else if (focusedPluginIdentifier_ == pluginId)
3231 focusedPluginIdentifier_ = -1;
3233 // Whenever plugin focus changes, plugin IME resets.
3234 [self setPluginImeActive:NO];
3237 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
3238 if (!pluginImeActive_)
3241 ComplexTextInputPanel* inputPanel =
3242 [ComplexTextInputPanel sharedComplexTextInputPanel];
3243 NSString* composited_string = nil;
3244 BOOL handled = [inputPanel interpretKeyEvent:event
3245 string:&composited_string];
3246 if (composited_string) {
3247 renderWidgetHostView_->PluginImeCompositionCompleted(
3248 base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
3249 pluginImeActive_ = NO;
3254 - (void)checkForPluginImeCancellation {
3255 if (pluginImeActive_ &&
3256 ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
3257 renderWidgetHostView_->PluginImeCompositionCompleted(
3258 base::string16(), focusedPluginIdentifier_);
3259 pluginImeActive_ = NO;
3263 // Overriding a NSResponder method to support application services.
3265 - (id)validRequestorForSendType:(NSString*)sendType
3266 returnType:(NSString*)returnType {
3268 BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
3269 BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
3270 BOOL hasText = !renderWidgetHostView_->selected_text().empty();
3272 renderWidgetHostView_->text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
3274 if (sendTypeIsString && hasText && !returnType) {
3276 } else if (!sendType && returnTypeIsString && takesText) {
3278 } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
3281 requestor = [super validRequestorForSendType:sendType
3282 returnType:returnType];
3287 - (void)viewWillStartLiveResize {
3288 [super viewWillStartLiveResize];
3289 RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3291 widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), true));
3294 - (void)viewDidEndLiveResize {
3295 [super viewDidEndLiveResize];
3296 RenderWidgetHostImpl* widget = renderWidgetHostView_->render_widget_host_;
3298 widget->Send(new ViewMsg_SetInLiveResize(widget->GetRoutingID(), false));
3301 - (void)updateCursor:(NSCursor*)cursor {
3302 if (currentCursor_ == cursor)
3305 currentCursor_.reset([cursor retain]);
3306 [[self window] invalidateCursorRectsForView:self];
3309 - (void)popupWindowWillClose:(NSNotification *)notification {
3310 renderWidgetHostView_->KillSelf();
3316 // Supporting application services
3318 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
3320 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
3321 types:(NSArray*)types {
3322 const std::string& str = renderWidgetHostView_->selected_text();
3323 if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
3325 base::scoped_nsobject<NSString> text(
3326 [[NSString alloc] initWithUTF8String:str.c_str()]);
3327 NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
3328 [pboard declareTypes:toDeclare owner:nil];
3329 return [pboard setString:text forType:NSStringPboardType];
3332 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
3333 NSString *string = [pboard stringForType:NSStringPboardType];
3334 if (!string) return NO;
3336 // If the user is currently using an IME, confirm the IME input,
3337 // and then insert the text from the service, the same as TextEdit and Safari.
3338 [self confirmComposition];
3339 [self insertText:string];
3347 // "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
3348 // regions that are not draggable. (See ControlRegionView in
3349 // native_app_window_cocoa.mm). This requires the render host view to be
3350 // draggable by default.
3351 - (BOOL)mouseDownCanMoveWindow {