1// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights 2// reserved. Use of this source code is governed by a BSD-style license that 3// can be found in the LICENSE file. 4 5#include "tests/cefclient/browser/browser_window_osr_mac.h" 6 7#include <Cocoa/Cocoa.h> 8#include <OpenGL/gl.h> 9#import <objc/runtime.h> 10 11#include "include/base/cef_logging.h" 12#include "include/cef_parser.h" 13#include "include/wrapper/cef_closure_task.h" 14#include "tests/cefclient/browser/bytes_write_handler.h" 15#include "tests/cefclient/browser/main_context.h" 16#include "tests/cefclient/browser/osr_accessibility_helper.h" 17#include "tests/cefclient/browser/osr_accessibility_node.h" 18#include "tests/cefclient/browser/text_input_client_osr_mac.h" 19#include "tests/shared/browser/geometry_util.h" 20#include "tests/shared/browser/main_message_loop.h" 21 22#import <AppKit/NSAccessibility.h> 23 24@interface BrowserOpenGLView 25 : NSOpenGLView <NSDraggingSource, NSDraggingDestination, NSAccessibility> { 26 @private 27 NSTrackingArea* tracking_area_; 28 client::BrowserWindowOsrMac* browser_window_; 29 client::OsrRenderer* renderer_; 30 NSPoint last_mouse_pos_; 31 NSPoint cur_mouse_pos_; 32 bool rotating_; 33 34 bool was_last_mouse_down_on_view_; 35 36 float device_scale_factor_; 37 38 // Drag and drop. 39 CefRefPtr<CefDragData> current_drag_data_; 40 NSDragOperation current_drag_op_; 41 NSDragOperation current_allowed_ops_; 42 NSPasteboard* pasteboard_; 43 NSString* fileUTI_; 44 45 // For intreacting with IME. 46 NSTextInputContext* text_input_context_osr_mac_; 47 CefTextInputClientOSRMac* text_input_client_; 48 49 // Manages Accessibility Tree 50 client::OsrAccessibilityHelper* accessibility_helper_; 51 52 // Event monitor for scroll wheel end event. 53 id endWheelMonitor_; 54} 55 56@end // @interface BrowserOpenGLView 57 58namespace { 59 60NSString* const kCEFDragDummyPboardType = @"org.CEF.drag-dummy-type"; 61NSString* const kNSURLTitlePboardType = @"public.url-name"; 62 63class ScopedGLContext { 64 public: 65 ScopedGLContext(BrowserOpenGLView* view, bool swap_buffers) 66 : swap_buffers_(swap_buffers) { 67 context_ = [view openGLContext]; 68 [context_ makeCurrentContext]; 69 } 70 ~ScopedGLContext() { 71 [NSOpenGLContext clearCurrentContext]; 72 if (swap_buffers_) 73 [context_ flushBuffer]; 74 } 75 76 private: 77 NSOpenGLContext* context_; 78 const bool swap_buffers_; 79}; 80 81NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { 82 NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0); 83 return [window convertRectToScreen:point_rect].origin; 84} 85 86} // namespace 87 88@implementation BrowserOpenGLView 89 90- (id)initWithFrame:(NSRect)frame 91 andBrowserWindow:(client::BrowserWindowOsrMac*)browser_window 92 andRenderer:(client::OsrRenderer*)renderer { 93 NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] 94 initWithAttributes:(NSOpenGLPixelFormatAttribute[]){ 95 NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 32, 96 0}]; 97#if !__has_feature(objc_arc) 98 [pixelFormat autorelease]; 99#endif // !__has_feature(objc_arc) 100 101 if (self = [super initWithFrame:frame pixelFormat:pixelFormat]) { 102 browser_window_ = browser_window; 103 renderer_ = renderer; 104 rotating_ = false; 105 endWheelMonitor_ = nil; 106 device_scale_factor_ = 1.0f; 107 108 tracking_area_ = [[NSTrackingArea alloc] 109 initWithRect:frame 110 options:NSTrackingMouseMoved | NSTrackingActiveInActiveApp | 111 NSTrackingInVisibleRect 112 owner:self 113 userInfo:nil]; 114 [self addTrackingArea:tracking_area_]; 115 116 // enable HiDPI buffer 117 [self setWantsBestResolutionOpenGLSurface:YES]; 118 119 [self resetDragDrop]; 120 121 NSArray* types = [NSArray 122 arrayWithObjects:kCEFDragDummyPboardType, NSStringPboardType, 123 NSFilenamesPboardType, NSPasteboardTypeString, nil]; 124 [self registerForDraggedTypes:types]; 125 } 126 127 return self; 128} 129 130- (void)dealloc { 131 [[NSNotificationCenter defaultCenter] 132 removeObserver:self 133 name:NSWindowDidChangeBackingPropertiesNotification 134 object:nil]; 135#if !__has_feature(objc_arc) 136 if (text_input_context_osr_mac_) { 137 [text_input_client_ release]; 138 [text_input_context_osr_mac_ release]; 139 } 140 [super dealloc]; 141#endif // !__has_feature(objc_arc) 142} 143 144- (void)detach { 145 renderer_ = NULL; 146 browser_window_ = NULL; 147 if (text_input_client_) 148 [text_input_client_ detach]; 149} 150 151- (CefRefPtr<CefBrowser>)getBrowser { 152 if (browser_window_) 153 return browser_window_->GetBrowser(); 154 return NULL; 155} 156 157- (void)setFrame:(NSRect)frameRect { 158 CefRefPtr<CefBrowser> browser = [self getBrowser]; 159 if (!browser.get()) 160 return; 161 162 [super setFrame:frameRect]; 163 browser->GetHost()->WasResized(); 164} 165 166- (void)sendMouseClick:(NSEvent*)event 167 button:(CefBrowserHost::MouseButtonType)type 168 isUp:(bool)isUp { 169 CefRefPtr<CefBrowser> browser = [self getBrowser]; 170 if (!browser.get()) 171 return; 172 173 CefMouseEvent mouseEvent; 174 [self getMouseEvent:mouseEvent forEvent:event]; 175 176 // |point| is in OS X view coordinates. 177 NSPoint point = [self getClickPointForEvent:event]; 178 179 // Convert to device coordinates. 180 point = [self convertPointToBackingInternal:point]; 181 182 if (!isUp) { 183 was_last_mouse_down_on_view_ = ![self isOverPopupWidgetX:point.x 184 andY:point.y]; 185 } else if (was_last_mouse_down_on_view_ && 186 [self isOverPopupWidgetX:point.x andY:point.y] && 187 ([self getPopupXOffset] || [self getPopupYOffset])) { 188 return; 189 } 190 191 browser->GetHost()->SendMouseClickEvent(mouseEvent, type, isUp, 192 [event clickCount]); 193} 194 195- (void)mouseDown:(NSEvent*)event { 196 [self sendMouseClick:event button:MBT_LEFT isUp:false]; 197} 198 199- (void)rightMouseDown:(NSEvent*)event { 200 if ([event modifierFlags] & NSShiftKeyMask) { 201 // Start rotation effect. 202 last_mouse_pos_ = cur_mouse_pos_ = [self getClickPointForEvent:event]; 203 rotating_ = true; 204 return; 205 } 206 207 [self sendMouseClick:event button:MBT_RIGHT isUp:false]; 208} 209 210- (void)otherMouseDown:(NSEvent*)event { 211 [self sendMouseClick:event button:MBT_MIDDLE isUp:false]; 212} 213 214- (void)mouseUp:(NSEvent*)event { 215 [self sendMouseClick:event button:MBT_LEFT isUp:true]; 216} 217 218- (void)rightMouseUp:(NSEvent*)event { 219 if (rotating_) { 220 // End rotation effect. 221 renderer_->SetSpin(0, 0); 222 rotating_ = false; 223 [self setNeedsDisplay:YES]; 224 return; 225 } 226 [self sendMouseClick:event button:MBT_RIGHT isUp:true]; 227} 228 229- (void)otherMouseUp:(NSEvent*)event { 230 [self sendMouseClick:event button:MBT_MIDDLE isUp:true]; 231} 232 233- (void)mouseMoved:(NSEvent*)event { 234 CefRefPtr<CefBrowser> browser = [self getBrowser]; 235 if (!browser.get()) 236 return; 237 238 if (rotating_) { 239 // Apply rotation effect. 240 cur_mouse_pos_ = [self getClickPointForEvent:event]; 241 ; 242 renderer_->IncrementSpin((cur_mouse_pos_.x - last_mouse_pos_.x), 243 (cur_mouse_pos_.y - last_mouse_pos_.y)); 244 last_mouse_pos_ = cur_mouse_pos_; 245 [self setNeedsDisplay:YES]; 246 return; 247 } 248 249 CefMouseEvent mouseEvent; 250 [self getMouseEvent:mouseEvent forEvent:event]; 251 252 browser->GetHost()->SendMouseMoveEvent(mouseEvent, false); 253} 254 255- (void)mouseDragged:(NSEvent*)event { 256 [self mouseMoved:event]; 257} 258 259- (void)rightMouseDragged:(NSEvent*)event { 260 [self mouseMoved:event]; 261} 262 263- (void)otherMouseDragged:(NSEvent*)event { 264 [self mouseMoved:event]; 265} 266 267- (void)mouseEntered:(NSEvent*)event { 268 [self mouseMoved:event]; 269} 270 271- (void)mouseExited:(NSEvent*)event { 272 CefRefPtr<CefBrowser> browser = [self getBrowser]; 273 if (!browser.get()) 274 return; 275 276 CefMouseEvent mouseEvent; 277 [self getMouseEvent:mouseEvent forEvent:event]; 278 279 browser->GetHost()->SendMouseMoveEvent(mouseEvent, true); 280} 281 282- (void)keyDown:(NSEvent*)event { 283 CefRefPtr<CefBrowser> browser = [self getBrowser]; 284 if (!browser.get() || !text_input_context_osr_mac_) 285 return; 286 287 if ([event type] != NSFlagsChanged) { 288 if (text_input_client_) { 289 [text_input_client_ HandleKeyEventBeforeTextInputClient:event]; 290 291 // The return value of this method seems to always be set to YES, thus we 292 // ignore it and ask the host view whether IME is active or not. 293 [text_input_context_osr_mac_ handleEvent:event]; 294 295 CefKeyEvent keyEvent; 296 [self getKeyEvent:keyEvent forEvent:event]; 297 298 [text_input_client_ HandleKeyEventAfterTextInputClient:keyEvent]; 299 } 300 } 301 302 // Check for Caps lock and Toggle Touch Emulation 303 if (client::MainContext::Get()->TouchEventsEnabled()) 304 [self toggleTouchEmulation:event]; 305} 306 307// OSX does not have touch screens, so we emulate it by mapping multitouch 308// events on TrackPad to Touch Events on Screen. To ensure it does not 309// interfere with other Trackpad events, this mapping is only enabled if 310// touch-events=enabled commandline is passed and caps lock key is on. 311- (void)toggleTouchEmulation:(NSEvent*)event { 312 if ([event type] == NSFlagsChanged && [event keyCode] == 0x39) { 313 NSUInteger flags = [event modifierFlags]; 314 BOOL touch_enabled = flags & NSAlphaShiftKeyMask ? YES : NO; 315 [self setAcceptsTouchEvents:touch_enabled]; 316 } 317} 318 319- (cef_touch_event_type_t)getTouchPhase:(NSTouchPhase)phase { 320 cef_touch_event_type_t event_type = CEF_TET_RELEASED; 321 switch (phase) { 322 case NSTouchPhaseBegan: 323 event_type = CEF_TET_PRESSED; 324 break; 325 case NSTouchPhaseMoved: 326 event_type = CEF_TET_MOVED; 327 break; 328 case NSTouchPhaseEnded: 329 event_type = CEF_TET_RELEASED; 330 break; 331 case NSTouchPhaseCancelled: 332 event_type = CEF_TET_CANCELLED; 333 break; 334 default: 335 break; 336 } 337 return event_type; 338} 339 340// Translate NSTouch events to CefTouchEvents and send to browser. 341- (void)sendTouchEvent:(NSEvent*)event touchPhase:(NSTouchPhase)phase { 342 int modifiers = [self getModifiersForEvent:event]; 343 CefRefPtr<CefBrowser> browser = [self getBrowser]; 344 345 NSSet* touches = [event touchesMatchingPhase:phase inView:self]; 346 347 for (NSTouch* touch in touches) { 348 // Convert NSTouch to CefTouchEvent. 349 CefTouchEvent touch_event; 350 351 // NSTouch.identity is unique during the life of the touch 352 touch_event.id = touch.identity.hash; 353 touch_event.type = [self getTouchPhase:phase]; 354 355 NSPoint scaled_pos = [touch normalizedPosition]; 356 NSSize view_size = [self bounds].size; 357 358 // Map point on Touch Device to View coordinates. 359 NSPoint touch_point = NSMakePoint(scaled_pos.x * view_size.width, 360 scaled_pos.y * view_size.height); 361 362 NSPoint contentLocal = [self convertPoint:touch_point fromView:nil]; 363 NSPoint point; 364 point.x = contentLocal.x; 365 point.y = [self frame].size.height - contentLocal.y; // Flip y. 366 367 // Convert to device coordinates. 368 point = [self convertPointToBackingInternal:point]; 369 370 int device_x = point.x; 371 int device_y = point.y; 372 373 const float device_scale_factor = [self getDeviceScaleFactor]; 374 // Convert to browser view coordinates. 375 touch_event.x = client::DeviceToLogical(device_x, device_scale_factor); 376 touch_event.y = client::DeviceToLogical(device_y, device_scale_factor); 377 378 touch_event.radius_x = 0; 379 touch_event.radius_y = 0; 380 381 touch_event.rotation_angle = 0; 382 touch_event.pressure = 0; 383 384 touch_event.modifiers = modifiers; 385 386 // Notify the browser of touch event. 387 browser->GetHost()->SendTouchEvent(touch_event); 388 } 389} 390 391- (void)touchesBeganWithEvent:(NSEvent*)event { 392 [self sendTouchEvent:event touchPhase:NSTouchPhaseBegan]; 393} 394 395- (void)touchesMovedWithEvent:(NSEvent*)event { 396 [self sendTouchEvent:event touchPhase:NSTouchPhaseMoved]; 397} 398 399- (void)touchesEndedWithEvent:(NSEvent*)event { 400 [self sendTouchEvent:event touchPhase:NSTouchPhaseEnded]; 401} 402 403- (void)touchesCancelledWithEvent:(NSEvent*)event { 404 [self sendTouchEvent:event touchPhase:NSTouchPhaseCancelled]; 405} 406 407- (void)keyUp:(NSEvent*)event { 408 CefRefPtr<CefBrowser> browser = [self getBrowser]; 409 if (!browser.get()) 410 return; 411 412 CefKeyEvent keyEvent; 413 [self getKeyEvent:keyEvent forEvent:event]; 414 415 keyEvent.type = KEYEVENT_KEYUP; 416 browser->GetHost()->SendKeyEvent(keyEvent); 417} 418 419- (void)flagsChanged:(NSEvent*)event { 420 if ([self isKeyUpEvent:event]) 421 [self keyUp:event]; 422 else 423 [self keyDown:event]; 424} 425 426- (void)shortCircuitScrollWheelEvent:(NSEvent*)event { 427 if ([event phase] != NSEventPhaseEnded && 428 [event phase] != NSEventPhaseCancelled) 429 return; 430 431 [self sendScrollWheelEvet:event]; 432 433 if (endWheelMonitor_) { 434 [NSEvent removeMonitor:endWheelMonitor_]; 435 endWheelMonitor_ = nil; 436 } 437} 438 439- (void)scrollWheel:(NSEvent*)event { 440 // Use an NSEvent monitor to listen for the wheel-end end. This ensures that 441 // the event is received even when the mouse cursor is no longer over the 442 // view when the scrolling ends. Also it avoids sending duplicate scroll 443 // events to the renderer. 444 if ([event phase] == NSEventPhaseBegan && !endWheelMonitor_) { 445 endWheelMonitor_ = [NSEvent 446 addLocalMonitorForEventsMatchingMask:NSScrollWheelMask 447 handler:^(NSEvent* blockEvent) { 448 [self shortCircuitScrollWheelEvent: 449 blockEvent]; 450 return blockEvent; 451 }]; 452 } 453 454 [self sendScrollWheelEvet:event]; 455} 456 457- (void)sendScrollWheelEvet:(NSEvent*)event { 458 CefRefPtr<CefBrowser> browser = [self getBrowser]; 459 if (!browser.get()) 460 return; 461 462 CGEventRef cgEvent = [event CGEvent]; 463 DCHECK(cgEvent); 464 465 int deltaX = 466 CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2); 467 int deltaY = 468 CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); 469 470 CefMouseEvent mouseEvent; 471 [self getMouseEvent:mouseEvent forEvent:event]; 472 473 browser->GetHost()->SendMouseWheelEvent(mouseEvent, deltaX, deltaY); 474} 475 476- (BOOL)canBecomeKeyView { 477 CefRefPtr<CefBrowser> browser = [self getBrowser]; 478 return (browser.get() != NULL); 479} 480 481- (BOOL)acceptsFirstResponder { 482 CefRefPtr<CefBrowser> browser = [self getBrowser]; 483 return (browser.get() != NULL); 484} 485 486- (BOOL)becomeFirstResponder { 487 CefRefPtr<CefBrowser> browser = [self getBrowser]; 488 if (browser.get()) { 489 browser->GetHost()->SendFocusEvent(true); 490 return [super becomeFirstResponder]; 491 } 492 493 return NO; 494} 495 496- (BOOL)resignFirstResponder { 497 CefRefPtr<CefBrowser> browser = [self getBrowser]; 498 if (browser.get()) { 499 browser->GetHost()->SendFocusEvent(false); 500 return [super resignFirstResponder]; 501 } 502 503 return NO; 504} 505 506- (void)undo:(id)sender { 507 CefRefPtr<CefBrowser> browser = [self getBrowser]; 508 if (browser.get()) 509 browser->GetFocusedFrame()->Undo(); 510} 511 512- (void)redo:(id)sender { 513 CefRefPtr<CefBrowser> browser = [self getBrowser]; 514 if (browser.get()) 515 browser->GetFocusedFrame()->Redo(); 516} 517 518- (void)cut:(id)sender { 519 CefRefPtr<CefBrowser> browser = [self getBrowser]; 520 if (browser.get()) 521 browser->GetFocusedFrame()->Cut(); 522} 523 524- (void)copy:(id)sender { 525 CefRefPtr<CefBrowser> browser = [self getBrowser]; 526 if (browser.get()) 527 browser->GetFocusedFrame()->Copy(); 528} 529 530- (void)paste:(id)sender { 531 CefRefPtr<CefBrowser> browser = [self getBrowser]; 532 if (browser.get()) 533 browser->GetFocusedFrame()->Paste(); 534} 535 536- (void)delete:(id)sender { 537 CefRefPtr<CefBrowser> browser = [self getBrowser]; 538 if (browser.get()) 539 browser->GetFocusedFrame()->Delete(); 540} 541 542- (void)selectAll:(id)sender { 543 CefRefPtr<CefBrowser> browser = [self getBrowser]; 544 if (browser.get()) 545 browser->GetFocusedFrame()->SelectAll(); 546} 547 548- (NSPoint)getClickPointForEvent:(NSEvent*)event { 549 NSPoint windowLocal = [event locationInWindow]; 550 NSPoint contentLocal = [self convertPoint:windowLocal fromView:nil]; 551 552 NSPoint point; 553 point.x = contentLocal.x; 554 point.y = [self frame].size.height - contentLocal.y; // Flip y. 555 return point; 556} 557 558- (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event { 559 if ([event type] == NSKeyDown || [event type] == NSKeyUp) { 560 NSString* s = [event characters]; 561 if ([s length] > 0) 562 keyEvent.character = [s characterAtIndex:0]; 563 564 s = [event charactersIgnoringModifiers]; 565 if ([s length] > 0) 566 keyEvent.unmodified_character = [s characterAtIndex:0]; 567 } 568 569 if ([event type] == NSFlagsChanged) { 570 keyEvent.character = 0; 571 keyEvent.unmodified_character = 0; 572 } 573 574 keyEvent.native_key_code = [event keyCode]; 575 576 keyEvent.modifiers = [self getModifiersForEvent:event]; 577} 578 579- (NSTextInputContext*)inputContext { 580 if (!text_input_context_osr_mac_) { 581 text_input_client_ = 582 [[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]]; 583 text_input_context_osr_mac_ = 584 [[NSTextInputContext alloc] initWithClient:text_input_client_]; 585#if !__has_feature(objc_arc) 586 [text_input_client_ retain]; 587 [text_input_context_osr_mac_ retain]; 588#endif // !__has_feature(objc_arc) 589 } 590 591 return text_input_context_osr_mac_; 592} 593 594- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event { 595 const float device_scale_factor = [self getDeviceScaleFactor]; 596 597 // |point| is in OS X view coordinates. 598 NSPoint point = [self getClickPointForEvent:event]; 599 600 // Convert to device coordinates. 601 point = [self convertPointToBackingInternal:point]; 602 603 int device_x = point.x; 604 int device_y = point.y; 605 if ([self isOverPopupWidgetX:device_x andY:device_y]) 606 [self applyPopupOffsetToX:device_x andY:device_y]; 607 608 // Convert to browser view coordinates. 609 mouseEvent.x = client::DeviceToLogical(device_x, device_scale_factor); 610 mouseEvent.y = client::DeviceToLogical(device_y, device_scale_factor); 611 612 mouseEvent.modifiers = [self getModifiersForEvent:event]; 613} 614 615- (void)getMouseEvent:(CefMouseEvent&)mouseEvent 616 forDragInfo:(id<NSDraggingInfo>)info { 617 const float device_scale_factor = [self getDeviceScaleFactor]; 618 619 // |point| is in OS X view coordinates. 620 NSPoint windowPoint = [info draggingLocation]; 621 NSPoint point = [self flipWindowPointToView:windowPoint]; 622 623 // Convert to device coordinates. 624 point = [self convertPointToBackingInternal:point]; 625 626 // Convert to browser view coordinates. 627 mouseEvent.x = client::DeviceToLogical(point.x, device_scale_factor); 628 mouseEvent.y = client::DeviceToLogical(point.y, device_scale_factor); 629 630 mouseEvent.modifiers = [NSEvent modifierFlags]; 631} 632 633- (int)getModifiersForEvent:(NSEvent*)event { 634 int modifiers = 0; 635 636 if ([event modifierFlags] & NSControlKeyMask) 637 modifiers |= EVENTFLAG_CONTROL_DOWN; 638 if ([event modifierFlags] & NSShiftKeyMask) 639 modifiers |= EVENTFLAG_SHIFT_DOWN; 640 if ([event modifierFlags] & NSAlternateKeyMask) 641 modifiers |= EVENTFLAG_ALT_DOWN; 642 if ([event modifierFlags] & NSCommandKeyMask) 643 modifiers |= EVENTFLAG_COMMAND_DOWN; 644 if ([event modifierFlags] & NSAlphaShiftKeyMask) 645 modifiers |= EVENTFLAG_CAPS_LOCK_ON; 646 647 if ([event type] == NSKeyUp || [event type] == NSKeyDown || 648 [event type] == NSFlagsChanged) { 649 // Only perform this check for key events 650 if ([self isKeyPadEvent:event]) 651 modifiers |= EVENTFLAG_IS_KEY_PAD; 652 } 653 654 // OS X does not have a modifier for NumLock, so I'm not entirely sure how to 655 // set EVENTFLAG_NUM_LOCK_ON; 656 // 657 // There is no EVENTFLAG for the function key either. 658 659 // Mouse buttons 660 switch ([event type]) { 661 case NSLeftMouseDragged: 662 case NSLeftMouseDown: 663 case NSLeftMouseUp: 664 modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; 665 break; 666 case NSRightMouseDragged: 667 case NSRightMouseDown: 668 case NSRightMouseUp: 669 modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; 670 break; 671 case NSOtherMouseDragged: 672 case NSOtherMouseDown: 673 case NSOtherMouseUp: 674 modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; 675 break; 676 default: 677 break; 678 } 679 680 return modifiers; 681} 682 683- (BOOL)isKeyUpEvent:(NSEvent*)event { 684 if ([event type] != NSFlagsChanged) 685 return [event type] == NSKeyUp; 686 687 // FIXME: This logic fails if the user presses both Shift keys at once, for 688 // example: we treat releasing one of them as keyDown. 689 switch ([event keyCode]) { 690 case 54: // Right Command 691 case 55: // Left Command 692 return ([event modifierFlags] & NSCommandKeyMask) == 0; 693 694 case 57: // Capslock 695 return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0; 696 697 case 56: // Left Shift 698 case 60: // Right Shift 699 return ([event modifierFlags] & NSShiftKeyMask) == 0; 700 701 case 58: // Left Alt 702 case 61: // Right Alt 703 return ([event modifierFlags] & NSAlternateKeyMask) == 0; 704 705 case 59: // Left Ctrl 706 case 62: // Right Ctrl 707 return ([event modifierFlags] & NSControlKeyMask) == 0; 708 709 case 63: // Function 710 return ([event modifierFlags] & NSFunctionKeyMask) == 0; 711 } 712 return false; 713} 714 715- (BOOL)isKeyPadEvent:(NSEvent*)event { 716 if ([event modifierFlags] & NSNumericPadKeyMask) 717 return true; 718 719 switch ([event keyCode]) { 720 case 71: // Clear 721 case 81: // = 722 case 75: // / 723 case 67: // * 724 case 78: // - 725 case 69: // + 726 case 76: // Enter 727 case 65: // . 728 case 82: // 0 729 case 83: // 1 730 case 84: // 2 731 case 85: // 3 732 case 86: // 4 733 case 87: // 5 734 case 88: // 6 735 case 89: // 7 736 case 91: // 8 737 case 92: // 9 738 return true; 739 } 740 741 return false; 742} 743 744- (void)windowDidChangeBackingProperties:(NSNotification*)notification { 745 // This delegate method is only called on 10.7 and later, so don't worry about 746 // other backing changes calling it on 10.6 or earlier 747 [self resetDeviceScaleFactor]; 748} 749 750- (void)drawRect:(NSRect)dirtyRect { 751 CefRefPtr<CefBrowser> browser = [self getBrowser]; 752 if ([self inLiveResize] || !browser.get()) { 753 // Fill with the background color. 754 const cef_color_t background_color = 755 client::MainContext::Get()->GetBackgroundColor(); 756 NSColor* color = [NSColor 757 colorWithCalibratedRed:float(CefColorGetR(background_color)) / 255.0f 758 green:float(CefColorGetG(background_color)) / 255.0f 759 blue:float(CefColorGetB(background_color)) / 255.0f 760 alpha:1.f]; 761 [color setFill]; 762 NSRectFill(dirtyRect); 763 } 764 765 // The Invalidate below fixes flicker when resizing. 766 if ([self inLiveResize] && browser.get()) 767 browser->GetHost()->Invalidate(PET_VIEW); 768} 769 770// Drag and drop 771 772- (BOOL)startDragging:(CefRefPtr<CefDragData>)drag_data 773 allowedOps:(NSDragOperation)ops 774 point:(NSPoint)position { 775 DCHECK(!pasteboard_); 776 DCHECK(!fileUTI_); 777 DCHECK(!current_drag_data_.get()); 778 779 [self resetDragDrop]; 780 781 current_allowed_ops_ = ops; 782 current_drag_data_ = drag_data; 783 784 [self fillPasteboard]; 785 786 NSEvent* currentEvent = [[NSApplication sharedApplication] currentEvent]; 787 NSWindow* window = [self window]; 788 NSTimeInterval eventTime = [currentEvent timestamp]; 789 790 NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged 791 location:position 792 modifierFlags:NSLeftMouseDraggedMask 793 timestamp:eventTime 794 windowNumber:[window windowNumber] 795 context:nil 796 eventNumber:0 797 clickCount:1 798 pressure:1.0]; 799 800 // TODO(cef): Pass a non-nil value to dragImage (see issue #1715). For now 801 // work around the "callee requires a non-null argument" error that occurs 802 // when building with the 10.11 SDK. 803 id nilArg = nil; 804 [window dragImage:nilArg 805 at:position 806 offset:NSZeroSize 807 event:dragEvent 808 pasteboard:pasteboard_ 809 source:self 810 slideBack:YES]; 811 return YES; 812} 813 814- (void)setCurrentDragOp:(NSDragOperation)op { 815 current_drag_op_ = op; 816} 817 818// NSDraggingSource Protocol 819 820- (NSDragOperation)draggingSession:(NSDraggingSession*)session 821 sourceOperationMaskForDraggingContext:(NSDraggingContext)context { 822 switch (context) { 823 case NSDraggingContextOutsideApplication: 824 return current_allowed_ops_; 825 826 case NSDraggingContextWithinApplication: 827 default: 828 return current_allowed_ops_; 829 } 830} 831 832- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest { 833 if (![dropDest isFileURL]) 834 return nil; 835 836 if (!current_drag_data_) 837 return nil; 838 839 size_t expected_size = current_drag_data_->GetFileContents(NULL); 840 if (expected_size == 0) 841 return nil; 842 843 std::string path = [[dropDest path] UTF8String]; 844 path.append("/"); 845 path.append(current_drag_data_->GetFileName().ToString()); 846 847 CefRefPtr<CefStreamWriter> writer = CefStreamWriter::CreateForFile(path); 848 if (!writer) 849 return nil; 850 851 if (current_drag_data_->GetFileContents(writer) != expected_size) 852 return nil; 853 854 return @[ [NSString stringWithUTF8String:path.c_str()] ]; 855} 856 857- (void)draggedImage:(NSImage*)anImage 858 endedAt:(NSPoint)screenPoint 859 operation:(NSDragOperation)operation { 860 CefRefPtr<CefBrowser> browser = [self getBrowser]; 861 if (!browser.get()) 862 return; 863 864 if (operation == (NSDragOperationMove | NSDragOperationCopy)) 865 operation &= ~NSDragOperationMove; 866 867 NSPoint windowPoint = [[self window] convertScreenToBase:screenPoint]; 868 NSPoint pt = [self flipWindowPointToView:windowPoint]; 869 CefRenderHandler::DragOperation op = 870 static_cast<CefRenderHandler::DragOperation>(operation); 871 browser->GetHost()->DragSourceEndedAt(pt.x, pt.y, op); 872 browser->GetHost()->DragSourceSystemDragEnded(); 873 [self resetDragDrop]; 874} 875 876// NSDraggingDestination Protocol 877 878- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { 879 CefRefPtr<CefBrowser> browser = [self getBrowser]; 880 if (!browser.get()) 881 return NSDragOperationNone; 882 883 CefRefPtr<CefDragData> drag_data; 884 if (!current_drag_data_) { 885 drag_data = CefDragData::Create(); 886 [self populateDropData:drag_data fromPasteboard:[info draggingPasteboard]]; 887 } else { 888 drag_data = current_drag_data_->Clone(); 889 drag_data->ResetFileContents(); 890 } 891 892 CefMouseEvent mouseEvent; 893 [self getMouseEvent:mouseEvent forDragInfo:info]; 894 895 NSDragOperation mask = [info draggingSourceOperationMask]; 896 CefBrowserHost::DragOperationsMask allowed_ops = 897 static_cast<CefBrowserHost::DragOperationsMask>(mask); 898 899 browser->GetHost()->DragTargetDragEnter(drag_data, mouseEvent, allowed_ops); 900 browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops); 901 902 current_drag_op_ = NSDragOperationCopy; 903 return current_drag_op_; 904} 905 906- (void)draggingExited:(id<NSDraggingInfo>)sender { 907 CefRefPtr<CefBrowser> browser = [self getBrowser]; 908 if (browser.get()) 909 browser->GetHost()->DragTargetDragLeave(); 910} 911 912- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info { 913 return YES; 914} 915 916- (BOOL)performDragOperation:(id<NSDraggingInfo>)info { 917 CefRefPtr<CefBrowser> browser = [self getBrowser]; 918 if (!browser.get()) 919 return NO; 920 921 CefMouseEvent mouseEvent; 922 [self getMouseEvent:mouseEvent forDragInfo:info]; 923 924 browser->GetHost()->DragTargetDrop(mouseEvent); 925 926 return YES; 927} 928 929- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info { 930 CefRefPtr<CefBrowser> browser = [self getBrowser]; 931 if (!browser.get()) 932 return NSDragOperationNone; 933 934 CefMouseEvent mouseEvent; 935 [self getMouseEvent:mouseEvent forDragInfo:info]; 936 937 NSDragOperation mask = [info draggingSourceOperationMask]; 938 CefBrowserHost::DragOperationsMask allowed_ops = 939 static_cast<CefBrowserHost::DragOperationsMask>(mask); 940 941 browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops); 942 943 return current_drag_op_; 944} 945 946// NSPasteboardOwner Protocol 947 948- (void)pasteboard:(NSPasteboard*)pboard provideDataForType:(NSString*)type { 949 if (!current_drag_data_) { 950 return; 951 } 952 953 // URL. 954 if ([type isEqualToString:NSURLPboardType]) { 955 DCHECK(current_drag_data_->IsLink()); 956 NSString* strUrl = 957 [NSString stringWithUTF8String:current_drag_data_->GetLinkURL() 958 .ToString() 959 .c_str()]; 960 NSURL* url = [NSURL URLWithString:strUrl]; 961 [url writeToPasteboard:pboard]; 962 // URL title. 963 } else if ([type isEqualToString:kNSURLTitlePboardType]) { 964 NSString* strTitle = 965 [NSString stringWithUTF8String:current_drag_data_->GetLinkTitle() 966 .ToString() 967 .c_str()]; 968 [pboard setString:strTitle forType:kNSURLTitlePboardType]; 969 970 // File contents. 971 } else if ([type isEqualToString:fileUTI_]) { 972 size_t size = current_drag_data_->GetFileContents(NULL); 973 DCHECK_GT(size, 0U); 974 CefRefPtr<client::BytesWriteHandler> handler = 975 new client::BytesWriteHandler(size); 976 CefRefPtr<CefStreamWriter> writer = 977 CefStreamWriter::CreateForHandler(handler.get()); 978 current_drag_data_->GetFileContents(writer); 979 DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(size)); 980 981 [pboard setData:[NSData dataWithBytes:handler->GetData() 982 length:handler->GetDataSize()] 983 forType:fileUTI_]; 984 985 // Plain text. 986 } else if ([type isEqualToString:NSStringPboardType]) { 987 NSString* strTitle = 988 [NSString stringWithUTF8String:current_drag_data_->GetFragmentText() 989 .ToString() 990 .c_str()]; 991 [pboard setString:strTitle forType:NSStringPboardType]; 992 993 } else if ([type isEqualToString:kCEFDragDummyPboardType]) { 994 // The dummy type _was_ promised and someone decided to call the bluff. 995 [pboard setData:[NSData data] forType:kCEFDragDummyPboardType]; 996 } 997} 998 999// NSAccessibility Protocol implementation. 1000- (BOOL)accessibilityIsIgnored { 1001 if (!accessibility_helper_) 1002 return YES; 1003 else 1004 return NO; 1005} 1006 1007- (id)accessibilityAttributeValue:(NSString*)attribute { 1008 if (!accessibility_helper_) 1009 return [super accessibilityAttributeValue:attribute]; 1010 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { 1011 return NSAccessibilityGroupRole; 1012 } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { 1013 client::OsrAXNode* node = accessibility_helper_->GetRootNode(); 1014 std::string desc = node ? node->AxDescription() : ""; 1015 return [NSString stringWithUTF8String:desc.c_str()]; 1016 } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { 1017 client::OsrAXNode* node = accessibility_helper_->GetRootNode(); 1018 std::string desc = node ? node->AxValue() : ""; 1019 return [NSString stringWithUTF8String:desc.c_str()]; 1020 } else if ([attribute 1021 isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { 1022 return NSAccessibilityRoleDescriptionForUIElement(self); 1023 } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 1024 client::OsrAXNode* node = accessibility_helper_->GetRootNode(); 1025 // Add Root as first Kid 1026 NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1]; 1027 NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( 1028 node->GetNativeAccessibleObject(NULL)); 1029 [kids addObject:child]; 1030 return NSAccessibilityUnignoredChildren(kids); 1031 } else { 1032 return [super accessibilityAttributeValue:attribute]; 1033 } 1034} 1035 1036- (id)accessibilityFocusedUIElement { 1037 if (accessibility_helper_) { 1038 client::OsrAXNode* node = accessibility_helper_->GetFocusedNode(); 1039 return node ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( 1040 node->GetNativeAccessibleObject(NULL)) 1041 : nil; 1042 } 1043 return nil; 1044} 1045 1046// Utility methods. 1047- (void)resetDragDrop { 1048 current_drag_op_ = NSDragOperationNone; 1049 current_allowed_ops_ = NSDragOperationNone; 1050 current_drag_data_ = NULL; 1051 if (fileUTI_) { 1052#if !__has_feature(objc_arc) 1053 [fileUTI_ release]; 1054#endif // !__has_feature(objc_arc) 1055 fileUTI_ = nil; 1056 } 1057 if (pasteboard_) { 1058#if !__has_feature(objc_arc) 1059 [pasteboard_ release]; 1060#endif // !__has_feature(objc_arc) 1061 pasteboard_ = nil; 1062 } 1063} 1064 1065- (void)fillPasteboard { 1066 DCHECK(!pasteboard_); 1067 pasteboard_ = [NSPasteboard pasteboardWithName:NSDragPboard]; 1068#if !__has_feature(objc_arc) 1069 [pasteboard_ retain]; 1070#endif // !__has_feature(objc_arc) 1071 1072 [pasteboard_ declareTypes:@[ kCEFDragDummyPboardType ] owner:self]; 1073 1074 // URL (and title). 1075 if (current_drag_data_->IsLink()) { 1076 [pasteboard_ addTypes:@[ NSURLPboardType, kNSURLTitlePboardType ] 1077 owner:self]; 1078 } 1079 1080 // MIME type. 1081 CefString mimeType; 1082 size_t contents_size = current_drag_data_->GetFileContents(NULL); 1083 CefString download_metadata = current_drag_data_->GetLinkMetadata(); 1084 CefString file_name = current_drag_data_->GetFileName(); 1085 1086 // File. 1087 if (contents_size > 0) { 1088 std::string file_name = current_drag_data_->GetFileName().ToString(); 1089 size_t sep = file_name.find_last_of("."); 1090 CefString extension = file_name.substr(sep + 1); 1091 1092 mimeType = CefGetMimeType(extension); 1093 1094 if (!mimeType.empty()) { 1095 CFStringRef mimeTypeCF; 1096 mimeTypeCF = CFStringCreateWithCString(kCFAllocatorDefault, 1097 mimeType.ToString().c_str(), 1098 kCFStringEncodingUTF8); 1099 fileUTI_ = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag( 1100 kUTTagClassMIMEType, mimeTypeCF, NULL); 1101 CFRelease(mimeTypeCF); 1102 // File (HFS) promise. 1103 NSArray* fileUTIList = @[ fileUTI_ ]; 1104 [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:self]; 1105 [pasteboard_ setPropertyList:fileUTIList 1106 forType:NSFilesPromisePboardType]; 1107 1108 [pasteboard_ addTypes:fileUTIList owner:self]; 1109 } 1110 } 1111 1112 // Plain text. 1113 if (!current_drag_data_->GetFragmentText().empty()) { 1114 [pasteboard_ addTypes:@[ NSStringPboardType ] owner:self]; 1115 } 1116} 1117 1118- (void)populateDropData:(CefRefPtr<CefDragData>)data 1119 fromPasteboard:(NSPasteboard*)pboard { 1120 DCHECK(data); 1121 DCHECK(pboard); 1122 DCHECK(data && !data->IsReadOnly()); 1123 NSArray* types = [pboard types]; 1124 1125 // Get plain text. 1126 if ([types containsObject:NSStringPboardType]) { 1127 data->SetFragmentText( 1128 [[pboard stringForType:NSStringPboardType] UTF8String]); 1129 } 1130 1131 // Get files. 1132 if ([types containsObject:NSFilenamesPboardType]) { 1133 NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; 1134 if ([files isKindOfClass:[NSArray class]] && [files count]) { 1135 for (NSUInteger i = 0; i < [files count]; i++) { 1136 NSString* filename = [files objectAtIndex:i]; 1137 BOOL exists = 1138 [[NSFileManager defaultManager] fileExistsAtPath:filename]; 1139 if (exists) { 1140 data->AddFile([filename UTF8String], CefString()); 1141 } 1142 } 1143 } 1144 } 1145} 1146 1147- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint { 1148 NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil]; 1149 NSRect viewFrame = [self frame]; 1150 viewPoint.y = viewFrame.size.height - viewPoint.y; 1151 return viewPoint; 1152} 1153 1154- (void)resetDeviceScaleFactor { 1155 float device_scale_factor = 1.0f; 1156 NSWindow* window = [self window]; 1157 if (window) 1158 device_scale_factor = [window backingScaleFactor]; 1159 [self setDeviceScaleFactor:device_scale_factor]; 1160} 1161 1162- (void)setDeviceScaleFactor:(float)device_scale_factor { 1163 if (device_scale_factor == device_scale_factor_) 1164 return; 1165 1166 // Apply some sanity checks. 1167 if (device_scale_factor < 1.0f || device_scale_factor > 4.0f) 1168 return; 1169 1170 device_scale_factor_ = device_scale_factor; 1171 1172 CefRefPtr<CefBrowser> browser = [self getBrowser]; 1173 if (browser) { 1174 browser->GetHost()->NotifyScreenInfoChanged(); 1175 browser->GetHost()->WasResized(); 1176 } 1177} 1178 1179- (float)getDeviceScaleFactor { 1180 return device_scale_factor_; 1181} 1182 1183- (void)viewDidChangeBackingProperties { 1184 const CGFloat device_scale_factor = [self getDeviceScaleFactor]; 1185 1186 if (device_scale_factor == device_scale_factor_) 1187 return; 1188 1189 CefRefPtr<CefBrowser> browser = [self getBrowser]; 1190 if (browser) { 1191 browser->GetHost()->NotifyScreenInfoChanged(); 1192 browser->GetHost()->WasResized(); 1193 } 1194} 1195 1196- (bool)isOverPopupWidgetX:(int)x andY:(int)y { 1197 CefRect rc = renderer_->popup_rect(); 1198 int popup_right = rc.x + rc.width; 1199 int popup_bottom = rc.y + rc.height; 1200 return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom); 1201} 1202 1203- (int)getPopupXOffset { 1204 return renderer_->original_popup_rect().x - renderer_->popup_rect().x; 1205} 1206 1207- (int)getPopupYOffset { 1208 return renderer_->original_popup_rect().y - renderer_->popup_rect().y; 1209} 1210 1211- (void)applyPopupOffsetToX:(int&)x andY:(int&)y { 1212 if ([self isOverPopupWidgetX:x andY:y]) { 1213 x += [self getPopupXOffset]; 1214 y += [self getPopupYOffset]; 1215 } 1216} 1217 1218// Convert from scaled coordinates to view coordinates. 1219- (NSPoint)convertPointFromBackingInternal:(NSPoint)aPoint { 1220 return [self convertPointFromBacking:aPoint]; 1221} 1222 1223// Convert from view coordinates to scaled coordinates. 1224- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint { 1225 return [self convertPointToBacking:aPoint]; 1226} 1227 1228// Convert from scaled coordinates to view coordinates. 1229- (NSRect)convertRectFromBackingInternal:(NSRect)aRect { 1230 return [self convertRectFromBacking:aRect]; 1231} 1232 1233// Convert from view coordinates to scaled coordinates. 1234- (NSRect)convertRectToBackingInternal:(NSRect)aRect { 1235 return [self convertRectToBacking:aRect]; 1236} 1237 1238- (void)ChangeCompositionRange:(CefRange)range 1239 character_bounds:(const CefRenderHandler::RectList&)bounds { 1240 if (text_input_client_) 1241 [text_input_client_ ChangeCompositionRange:range character_bounds:bounds]; 1242} 1243 1244- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value { 1245 if (!accessibility_helper_) { 1246 accessibility_helper_ = 1247 new client::OsrAccessibilityHelper(value, [self getBrowser]); 1248 } else { 1249 accessibility_helper_->UpdateAccessibilityTree(value); 1250 } 1251 1252 if (accessibility_helper_) { 1253 NSAccessibilityPostNotification(self, 1254 NSAccessibilityValueChangedNotification); 1255 } 1256 return; 1257} 1258 1259- (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value { 1260 if (accessibility_helper_) { 1261 accessibility_helper_->UpdateAccessibilityLocation(value); 1262 } 1263 1264 if (accessibility_helper_) { 1265 NSAccessibilityPostNotification(self, 1266 NSAccessibilityValueChangedNotification); 1267 } 1268 return; 1269} 1270@end 1271 1272namespace client { 1273 1274class BrowserWindowOsrMacImpl { 1275 public: 1276 BrowserWindowOsrMacImpl(BrowserWindow::Delegate* delegate, 1277 const std::string& startup_url, 1278 const OsrRendererSettings& settings, 1279 BrowserWindowOsrMac& browser_window); 1280 ~BrowserWindowOsrMacImpl(); 1281 1282 // BrowserWindow methods. 1283 void CreateBrowser(ClientWindowHandle parent_handle, 1284 const CefRect& rect, 1285 const CefBrowserSettings& settings, 1286 CefRefPtr<CefDictionaryValue> extra_info, 1287 CefRefPtr<CefRequestContext> request_context); 1288 void GetPopupConfig(CefWindowHandle temp_handle, 1289 CefWindowInfo& windowInfo, 1290 CefRefPtr<CefClient>& client, 1291 CefBrowserSettings& settings); 1292 void ShowPopup(ClientWindowHandle parent_handle, 1293 int x, 1294 int y, 1295 size_t width, 1296 size_t height); 1297 void Show(); 1298 void Hide(); 1299 void SetBounds(int x, int y, size_t width, size_t height); 1300 void SetFocus(bool focus); 1301 void SetDeviceScaleFactor(float device_scale_factor); 1302 float GetDeviceScaleFactor() const; 1303 ClientWindowHandle GetWindowHandle() const; 1304 1305 // ClientHandlerOsr::OsrDelegate methods. 1306 void OnAfterCreated(CefRefPtr<CefBrowser> browser); 1307 void OnBeforeClose(CefRefPtr<CefBrowser> browser); 1308 bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect); 1309 void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect); 1310 bool GetScreenPoint(CefRefPtr<CefBrowser> browser, 1311 int viewX, 1312 int viewY, 1313 int& screenX, 1314 int& screenY); 1315 bool GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo& screen_info); 1316 void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show); 1317 void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect); 1318 void OnPaint(CefRefPtr<CefBrowser> browser, 1319 CefRenderHandler::PaintElementType type, 1320 const CefRenderHandler::RectList& dirtyRects, 1321 const void* buffer, 1322 int width, 1323 int height); 1324 void OnCursorChange(CefRefPtr<CefBrowser> browser, 1325 CefCursorHandle cursor, 1326 cef_cursor_type_t type, 1327 const CefCursorInfo& custom_cursor_info); 1328 bool StartDragging(CefRefPtr<CefBrowser> browser, 1329 CefRefPtr<CefDragData> drag_data, 1330 CefRenderHandler::DragOperationsMask allowed_ops, 1331 int x, 1332 int y); 1333 void UpdateDragCursor(CefRefPtr<CefBrowser> browser, 1334 CefRenderHandler::DragOperation operation); 1335 void OnImeCompositionRangeChanged( 1336 CefRefPtr<CefBrowser> browser, 1337 const CefRange& selection_range, 1338 const CefRenderHandler::RectList& character_bounds); 1339 1340 void UpdateAccessibilityTree(CefRefPtr<CefValue> value); 1341 void UpdateAccessibilityLocation(CefRefPtr<CefValue> value); 1342 1343 private: 1344 // Create the NSView. 1345 void Create(ClientWindowHandle parent_handle, const CefRect& rect); 1346 1347 BrowserWindowOsrMac& browser_window_; 1348 // The below members will only be accessed on the main thread which should be 1349 // the same as the CEF UI thread. 1350 OsrRenderer renderer_; 1351 BrowserOpenGLView* native_browser_view_; 1352 bool hidden_; 1353 bool painting_popup_; 1354}; 1355 1356BrowserWindowOsrMacImpl::BrowserWindowOsrMacImpl( 1357 BrowserWindow::Delegate* delegate, 1358 const std::string& startup_url, 1359 const OsrRendererSettings& settings, 1360 BrowserWindowOsrMac& browser_window) 1361 : browser_window_(browser_window), 1362 renderer_(settings), 1363 native_browser_view_(nil), 1364 hidden_(false), 1365 painting_popup_(false) {} 1366 1367BrowserWindowOsrMacImpl::~BrowserWindowOsrMacImpl() { 1368 if (native_browser_view_) { 1369 // Disassociate the view with |this|. 1370 [native_browser_view_ detach]; 1371 } 1372} 1373 1374void BrowserWindowOsrMacImpl::CreateBrowser( 1375 ClientWindowHandle parent_handle, 1376 const CefRect& rect, 1377 const CefBrowserSettings& settings, 1378 CefRefPtr<CefDictionaryValue> extra_info, 1379 CefRefPtr<CefRequestContext> request_context) { 1380 REQUIRE_MAIN_THREAD(); 1381 1382 // Create the native NSView. 1383 Create(parent_handle, rect); 1384 1385 CefWindowInfo window_info; 1386 window_info.SetAsWindowless( 1387 CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_)); 1388 1389 // Create the browser asynchronously. 1390 CefBrowserHost::CreateBrowser(window_info, browser_window_.client_handler_, 1391 browser_window_.client_handler_->startup_url(), 1392 settings, extra_info, request_context); 1393} 1394 1395void BrowserWindowOsrMacImpl::GetPopupConfig(CefWindowHandle temp_handle, 1396 CefWindowInfo& windowInfo, 1397 CefRefPtr<CefClient>& client, 1398 CefBrowserSettings& settings) { 1399 CEF_REQUIRE_UI_THREAD(); 1400 1401 windowInfo.SetAsWindowless(temp_handle); 1402 client = browser_window_.client_handler_; 1403} 1404 1405void BrowserWindowOsrMacImpl::ShowPopup(ClientWindowHandle parent_handle, 1406 int x, 1407 int y, 1408 size_t width, 1409 size_t height) { 1410 REQUIRE_MAIN_THREAD(); 1411 DCHECK(browser_window_.browser_.get()); 1412 1413 // Create the native NSView. 1414 Create(parent_handle, 1415 CefRect(x, y, static_cast<int>(width), static_cast<int>(height))); 1416 1417 // Send resize notification so the compositor is assigned the correct 1418 // viewport size and begins rendering. 1419 browser_window_.browser_->GetHost()->WasResized(); 1420 1421 Show(); 1422} 1423 1424void BrowserWindowOsrMacImpl::Show() { 1425 REQUIRE_MAIN_THREAD(); 1426 1427 if (hidden_) { 1428 // Set the browser as visible. 1429 browser_window_.browser_->GetHost()->WasHidden(false); 1430 hidden_ = false; 1431 } 1432 1433 // Give focus to the browser. 1434 browser_window_.browser_->GetHost()->SendFocusEvent(true); 1435} 1436 1437void BrowserWindowOsrMacImpl::Hide() { 1438 REQUIRE_MAIN_THREAD(); 1439 1440 if (!browser_window_.browser_.get()) 1441 return; 1442 1443 // Remove focus from the browser. 1444 browser_window_.browser_->GetHost()->SendFocusEvent(false); 1445 1446 if (!hidden_) { 1447 // Set the browser as hidden. 1448 browser_window_.browser_->GetHost()->WasHidden(true); 1449 hidden_ = true; 1450 } 1451} 1452 1453void BrowserWindowOsrMacImpl::SetBounds(int x, 1454 int y, 1455 size_t width, 1456 size_t height) { 1457 REQUIRE_MAIN_THREAD(); 1458 // Nothing to do here. GTK will take care of positioning in the container. 1459} 1460 1461void BrowserWindowOsrMacImpl::SetFocus(bool focus) { 1462 REQUIRE_MAIN_THREAD(); 1463 if (native_browser_view_) 1464 [native_browser_view_.window makeFirstResponder:native_browser_view_]; 1465} 1466 1467void BrowserWindowOsrMacImpl::SetDeviceScaleFactor(float device_scale_factor) { 1468 REQUIRE_MAIN_THREAD(); 1469 if (native_browser_view_) 1470 [native_browser_view_ setDeviceScaleFactor:device_scale_factor]; 1471} 1472 1473float BrowserWindowOsrMacImpl::GetDeviceScaleFactor() const { 1474 REQUIRE_MAIN_THREAD(); 1475 if (native_browser_view_) 1476 return [native_browser_view_ getDeviceScaleFactor]; 1477 return 1.0f; 1478} 1479 1480ClientWindowHandle BrowserWindowOsrMacImpl::GetWindowHandle() const { 1481 REQUIRE_MAIN_THREAD(); 1482 return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_); 1483} 1484 1485void BrowserWindowOsrMacImpl::OnAfterCreated(CefRefPtr<CefBrowser> browser) { 1486 CEF_REQUIRE_UI_THREAD(); 1487} 1488 1489void BrowserWindowOsrMacImpl::OnBeforeClose(CefRefPtr<CefBrowser> browser) { 1490 CEF_REQUIRE_UI_THREAD(); 1491 REQUIRE_MAIN_THREAD(); 1492 1493 // Detach |this| from the ClientHandlerOsr. 1494 static_cast<ClientHandlerOsr*>(browser_window_.client_handler_.get()) 1495 ->DetachOsrDelegate(); 1496} 1497 1498bool BrowserWindowOsrMacImpl::GetRootScreenRect(CefRefPtr<CefBrowser> browser, 1499 CefRect& rect) { 1500 CEF_REQUIRE_UI_THREAD(); 1501 return false; 1502} 1503 1504void BrowserWindowOsrMacImpl::GetViewRect(CefRefPtr<CefBrowser> browser, 1505 CefRect& rect) { 1506 CEF_REQUIRE_UI_THREAD(); 1507 REQUIRE_MAIN_THREAD(); 1508 1509 rect.x = rect.y = 0; 1510 1511 if (!native_browser_view_) { 1512 // Never return an empty rectangle. 1513 rect.width = rect.height = 1; 1514 return; 1515 } 1516 1517 const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1518 1519 // |bounds| is in OS X view coordinates. 1520 NSRect bounds = native_browser_view_.bounds; 1521 1522 // Convert to device coordinates. 1523 bounds = [native_browser_view_ convertRectToBackingInternal:bounds]; 1524 1525 // Convert to browser view coordinates. 1526 rect.width = DeviceToLogical(bounds.size.width, device_scale_factor); 1527 if (rect.width == 0) 1528 rect.width = 1; 1529 rect.height = DeviceToLogical(bounds.size.height, device_scale_factor); 1530 if (rect.height == 0) 1531 rect.height = 1; 1532} 1533 1534bool BrowserWindowOsrMacImpl::GetScreenPoint(CefRefPtr<CefBrowser> browser, 1535 int viewX, 1536 int viewY, 1537 int& screenX, 1538 int& screenY) { 1539 CEF_REQUIRE_UI_THREAD(); 1540 REQUIRE_MAIN_THREAD(); 1541 1542 if (!native_browser_view_) 1543 return false; 1544 1545 const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1546 1547 // (viewX, viewX) is in browser view coordinates. 1548 // Convert to device coordinates. 1549 NSPoint view_pt = NSMakePoint(LogicalToDevice(viewX, device_scale_factor), 1550 LogicalToDevice(viewY, device_scale_factor)); 1551 1552 // Convert to OS X view coordinates. 1553 view_pt = [native_browser_view_ convertPointFromBackingInternal:view_pt]; 1554 1555 // Reverse the Y component. 1556 const NSRect bounds = native_browser_view_.bounds; 1557 view_pt.y = bounds.size.height - view_pt.y; 1558 1559 // Convert to screen coordinates. 1560 NSPoint window_pt = [native_browser_view_ convertPoint:view_pt toView:nil]; 1561 NSPoint screen_pt = 1562 ConvertPointFromWindowToScreen(native_browser_view_.window, window_pt); 1563 1564 screenX = screen_pt.x; 1565 screenY = screen_pt.y; 1566 return true; 1567} 1568 1569bool BrowserWindowOsrMacImpl::GetScreenInfo(CefRefPtr<CefBrowser> browser, 1570 CefScreenInfo& screen_info) { 1571 CEF_REQUIRE_UI_THREAD(); 1572 REQUIRE_MAIN_THREAD(); 1573 1574 if (!native_browser_view_) 1575 return false; 1576 1577 CefRect view_rect; 1578 GetViewRect(browser, view_rect); 1579 1580 screen_info.device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1581 1582 // The screen info rectangles are used by the renderer to create and position 1583 // popups. Keep popups inside the view rectangle. 1584 screen_info.rect = view_rect; 1585 screen_info.available_rect = view_rect; 1586 1587 return true; 1588} 1589 1590void BrowserWindowOsrMacImpl::OnPopupShow(CefRefPtr<CefBrowser> browser, 1591 bool show) { 1592 CEF_REQUIRE_UI_THREAD(); 1593 REQUIRE_MAIN_THREAD(); 1594 1595 if (!native_browser_view_) 1596 return; 1597 1598 if (!show) { 1599 renderer_.ClearPopupRects(); 1600 browser->GetHost()->Invalidate(PET_VIEW); 1601 } 1602 renderer_.OnPopupShow(browser, show); 1603} 1604 1605void BrowserWindowOsrMacImpl::OnPopupSize(CefRefPtr<CefBrowser> browser, 1606 const CefRect& rect) { 1607 CEF_REQUIRE_UI_THREAD(); 1608 REQUIRE_MAIN_THREAD(); 1609 1610 if (!native_browser_view_) 1611 return; 1612 1613 const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1614 1615 // |rect| is in browser view coordinates. Convert to device coordinates. 1616 CefRect device_rect = LogicalToDevice(rect, device_scale_factor); 1617 1618 renderer_.OnPopupSize(browser, device_rect); 1619} 1620 1621void BrowserWindowOsrMacImpl::OnPaint( 1622 CefRefPtr<CefBrowser> browser, 1623 CefRenderHandler::PaintElementType type, 1624 const CefRenderHandler::RectList& dirtyRects, 1625 const void* buffer, 1626 int width, 1627 int height) { 1628 CEF_REQUIRE_UI_THREAD(); 1629 REQUIRE_MAIN_THREAD(); 1630 1631 if (!native_browser_view_) 1632 return; 1633 1634 if (width <= 2 && height <= 2) { 1635 // Ignore really small buffer sizes while the widget is starting up. 1636 return; 1637 } 1638 1639 if (painting_popup_) { 1640 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); 1641 return; 1642 } 1643 1644 ScopedGLContext scoped_gl_context(native_browser_view_, true); 1645 1646 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); 1647 if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) { 1648 painting_popup_ = true; 1649 browser->GetHost()->Invalidate(PET_POPUP); 1650 painting_popup_ = false; 1651 } 1652 renderer_.Render(); 1653} 1654 1655void BrowserWindowOsrMacImpl::OnCursorChange( 1656 CefRefPtr<CefBrowser> browser, 1657 CefCursorHandle cursor, 1658 cef_cursor_type_t type, 1659 const CefCursorInfo& custom_cursor_info) { 1660 CEF_REQUIRE_UI_THREAD(); 1661 REQUIRE_MAIN_THREAD(); 1662 1663 [CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(cursor) set]; 1664} 1665 1666bool BrowserWindowOsrMacImpl::StartDragging( 1667 CefRefPtr<CefBrowser> browser, 1668 CefRefPtr<CefDragData> drag_data, 1669 CefRenderHandler::DragOperationsMask allowed_ops, 1670 int x, 1671 int y) { 1672 CEF_REQUIRE_UI_THREAD(); 1673 REQUIRE_MAIN_THREAD(); 1674 1675 if (!native_browser_view_) 1676 return false; 1677 1678 static float device_scale_factor = 1679 [native_browser_view_ getDeviceScaleFactor]; 1680 1681 // |point| is in browser view coordinates. 1682 NSPoint point = NSMakePoint(x, y); 1683 1684 // Convert to device coordinates. 1685 point.x = LogicalToDevice(point.x, device_scale_factor); 1686 point.y = LogicalToDevice(point.y, device_scale_factor); 1687 1688 // Convert to OS X view coordinates. 1689 point = [native_browser_view_ convertPointFromBackingInternal:point]; 1690 1691 return [native_browser_view_ 1692 startDragging:drag_data 1693 allowedOps:static_cast<NSDragOperation>(allowed_ops) 1694 point:point]; 1695} 1696 1697void BrowserWindowOsrMacImpl::UpdateDragCursor( 1698 CefRefPtr<CefBrowser> browser, 1699 CefRenderHandler::DragOperation operation) { 1700 CEF_REQUIRE_UI_THREAD(); 1701 REQUIRE_MAIN_THREAD(); 1702 1703 if (native_browser_view_) 1704 [native_browser_view_ setCurrentDragOp:operation]; 1705} 1706 1707void BrowserWindowOsrMacImpl::OnImeCompositionRangeChanged( 1708 CefRefPtr<CefBrowser> browser, 1709 const CefRange& selection_range, 1710 const CefRenderHandler::RectList& bounds) { 1711 CEF_REQUIRE_UI_THREAD(); 1712 1713 if (native_browser_view_) { 1714 [native_browser_view_ ChangeCompositionRange:selection_range 1715 character_bounds:bounds]; 1716 } 1717} 1718 1719void BrowserWindowOsrMacImpl::UpdateAccessibilityTree( 1720 CefRefPtr<CefValue> value) { 1721 CEF_REQUIRE_UI_THREAD(); 1722 1723 if (native_browser_view_) { 1724 [native_browser_view_ UpdateAccessibilityTree:value]; 1725 } 1726} 1727 1728void BrowserWindowOsrMacImpl::UpdateAccessibilityLocation( 1729 CefRefPtr<CefValue> value) { 1730 CEF_REQUIRE_UI_THREAD(); 1731 1732 if (native_browser_view_) { 1733 [native_browser_view_ UpdateAccessibilityLocation:value]; 1734 } 1735} 1736 1737void BrowserWindowOsrMacImpl::Create(ClientWindowHandle parent_handle, 1738 const CefRect& rect) { 1739 REQUIRE_MAIN_THREAD(); 1740 DCHECK(!native_browser_view_); 1741 1742 NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); 1743 native_browser_view_ = 1744 [[BrowserOpenGLView alloc] initWithFrame:window_rect 1745 andBrowserWindow:&browser_window_ 1746 andRenderer:&renderer_]; 1747 native_browser_view_.autoresizingMask = 1748 (NSViewWidthSizable | NSViewHeightSizable); 1749 native_browser_view_.autoresizesSubviews = YES; 1750 [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle) 1751 addSubview:native_browser_view_]; 1752 1753 // Determine the default scale factor. 1754 [native_browser_view_ resetDeviceScaleFactor]; 1755 1756 [[NSNotificationCenter defaultCenter] 1757 addObserver:native_browser_view_ 1758 selector:@selector(windowDidChangeBackingProperties:) 1759 name:NSWindowDidChangeBackingPropertiesNotification 1760 object:native_browser_view_.window]; 1761} 1762 1763BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate, 1764 const std::string& startup_url, 1765 const OsrRendererSettings& settings) 1766 : BrowserWindow(delegate) { 1767 client_handler_ = new ClientHandlerOsr(this, this, startup_url); 1768 impl_.reset( 1769 new BrowserWindowOsrMacImpl(delegate, startup_url, settings, *this)); 1770} 1771 1772BrowserWindowOsrMac::~BrowserWindowOsrMac() {} 1773 1774void BrowserWindowOsrMac::CreateBrowser( 1775 ClientWindowHandle parent_handle, 1776 const CefRect& rect, 1777 const CefBrowserSettings& settings, 1778 CefRefPtr<CefDictionaryValue> extra_info, 1779 CefRefPtr<CefRequestContext> request_context) { 1780 impl_->CreateBrowser(parent_handle, rect, settings, extra_info, 1781 request_context); 1782} 1783 1784void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle, 1785 CefWindowInfo& windowInfo, 1786 CefRefPtr<CefClient>& client, 1787 CefBrowserSettings& settings) { 1788 impl_->GetPopupConfig(temp_handle, windowInfo, client, settings); 1789} 1790 1791void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle, 1792 int x, 1793 int y, 1794 size_t width, 1795 size_t height) { 1796 impl_->ShowPopup(parent_handle, x, y, width, height); 1797} 1798 1799void BrowserWindowOsrMac::Show() { 1800 impl_->Show(); 1801} 1802 1803void BrowserWindowOsrMac::Hide() { 1804 impl_->Hide(); 1805} 1806 1807void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) { 1808 impl_->SetBounds(x, y, width, height); 1809} 1810 1811void BrowserWindowOsrMac::SetFocus(bool focus) { 1812 impl_->SetFocus(focus); 1813} 1814 1815void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) { 1816 impl_->SetDeviceScaleFactor(device_scale_factor); 1817} 1818 1819float BrowserWindowOsrMac::GetDeviceScaleFactor() const { 1820 return impl_->GetDeviceScaleFactor(); 1821} 1822 1823ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const { 1824 return impl_->GetWindowHandle(); 1825} 1826 1827void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr<CefBrowser> browser) { 1828 impl_->OnAfterCreated(browser); 1829} 1830 1831void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr<CefBrowser> browser) { 1832 impl_->OnBeforeClose(browser); 1833} 1834 1835bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr<CefBrowser> browser, 1836 CefRect& rect) { 1837 return impl_->GetRootScreenRect(browser, rect); 1838} 1839 1840void BrowserWindowOsrMac::GetViewRect(CefRefPtr<CefBrowser> browser, 1841 CefRect& rect) { 1842 impl_->GetViewRect(browser, rect); 1843} 1844 1845bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr<CefBrowser> browser, 1846 int viewX, 1847 int viewY, 1848 int& screenX, 1849 int& screenY) { 1850 return impl_->GetScreenPoint(browser, viewX, viewY, screenX, screenY); 1851} 1852 1853bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr<CefBrowser> browser, 1854 CefScreenInfo& screen_info) { 1855 return impl_->GetScreenInfo(browser, screen_info); 1856} 1857 1858void BrowserWindowOsrMac::OnPopupShow(CefRefPtr<CefBrowser> browser, 1859 bool show) { 1860 impl_->OnPopupShow(browser, show); 1861} 1862 1863void BrowserWindowOsrMac::OnPopupSize(CefRefPtr<CefBrowser> browser, 1864 const CefRect& rect) { 1865 impl_->OnPopupSize(browser, rect); 1866} 1867 1868void BrowserWindowOsrMac::OnPaint(CefRefPtr<CefBrowser> browser, 1869 CefRenderHandler::PaintElementType type, 1870 const CefRenderHandler::RectList& dirtyRects, 1871 const void* buffer, 1872 int width, 1873 int height) { 1874 impl_->OnPaint(browser, type, dirtyRects, buffer, width, height); 1875} 1876 1877void BrowserWindowOsrMac::OnCursorChange( 1878 CefRefPtr<CefBrowser> browser, 1879 CefCursorHandle cursor, 1880 cef_cursor_type_t type, 1881 const CefCursorInfo& custom_cursor_info) { 1882 impl_->OnCursorChange(browser, cursor, type, custom_cursor_info); 1883} 1884 1885bool BrowserWindowOsrMac::StartDragging( 1886 CefRefPtr<CefBrowser> browser, 1887 CefRefPtr<CefDragData> drag_data, 1888 CefRenderHandler::DragOperationsMask allowed_ops, 1889 int x, 1890 int y) { 1891 return impl_->StartDragging(browser, drag_data, allowed_ops, x, y); 1892} 1893 1894void BrowserWindowOsrMac::UpdateDragCursor( 1895 CefRefPtr<CefBrowser> browser, 1896 CefRenderHandler::DragOperation operation) { 1897 impl_->UpdateDragCursor(browser, operation); 1898} 1899 1900void BrowserWindowOsrMac::OnImeCompositionRangeChanged( 1901 CefRefPtr<CefBrowser> browser, 1902 const CefRange& selection_range, 1903 const CefRenderHandler::RectList& character_bounds) { 1904 impl_->OnImeCompositionRangeChanged(browser, selection_range, 1905 character_bounds); 1906} 1907 1908void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr<CefValue> value) { 1909 impl_->UpdateAccessibilityTree(value); 1910} 1911 1912void BrowserWindowOsrMac::UpdateAccessibilityLocation( 1913 CefRefPtr<CefValue> value) { 1914 impl_->UpdateAccessibilityLocation(value); 1915} 1916 1917} // namespace client 1918