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