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_ = nullptr; 146 browser_window_ = nullptr; 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 nullptr; 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() != nullptr); 479} 480 481- (BOOL)acceptsFirstResponder { 482 CefRefPtr<CefBrowser> browser = [self getBrowser]; 483 return (browser.get() != nullptr); 484} 485 486- (BOOL)becomeFirstResponder { 487 CefRefPtr<CefBrowser> browser = [self getBrowser]; 488 if (browser.get()) { 489 browser->GetHost()->SetFocus(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()->SetFocus(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(nullptr); 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(nullptr); 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(nullptr)); 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(nullptr)) 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_ = nullptr; 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(nullptr); 1083 CefString download_metadata = current_drag_data_->GetLinkMetadata(); 1084 1085 // File. 1086 if (contents_size > 0) { 1087 std::string file_name = current_drag_data_->GetFileName().ToString(); 1088 size_t sep = file_name.find_last_of("."); 1089 CefString extension = file_name.substr(sep + 1); 1090 1091 mimeType = CefGetMimeType(extension); 1092 1093 if (!mimeType.empty()) { 1094 CFStringRef mimeTypeCF; 1095 mimeTypeCF = CFStringCreateWithCString(kCFAllocatorDefault, 1096 mimeType.ToString().c_str(), 1097 kCFStringEncodingUTF8); 1098 fileUTI_ = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag( 1099 kUTTagClassMIMEType, mimeTypeCF, nullptr); 1100 CFRelease(mimeTypeCF); 1101 // File (HFS) promise. 1102 NSArray* fileUTIList = @[ fileUTI_ ]; 1103 [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:self]; 1104 [pasteboard_ setPropertyList:fileUTIList 1105 forType:NSFilesPromisePboardType]; 1106 1107 [pasteboard_ addTypes:fileUTIList owner:self]; 1108 } 1109 } 1110 1111 // Plain text. 1112 if (!current_drag_data_->GetFragmentText().empty()) { 1113 [pasteboard_ addTypes:@[ NSStringPboardType ] owner:self]; 1114 } 1115} 1116 1117- (void)populateDropData:(CefRefPtr<CefDragData>)data 1118 fromPasteboard:(NSPasteboard*)pboard { 1119 DCHECK(data); 1120 DCHECK(pboard); 1121 DCHECK(data && !data->IsReadOnly()); 1122 NSArray* types = [pboard types]; 1123 1124 // Get plain text. 1125 if ([types containsObject:NSStringPboardType]) { 1126 data->SetFragmentText( 1127 [[pboard stringForType:NSStringPboardType] UTF8String]); 1128 } 1129 1130 // Get files. 1131 if ([types containsObject:NSFilenamesPboardType]) { 1132 NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; 1133 if ([files isKindOfClass:[NSArray class]] && [files count]) { 1134 for (NSUInteger i = 0; i < [files count]; i++) { 1135 NSString* filename = [files objectAtIndex:i]; 1136 BOOL exists = 1137 [[NSFileManager defaultManager] fileExistsAtPath:filename]; 1138 if (exists) { 1139 data->AddFile([filename UTF8String], CefString()); 1140 } 1141 } 1142 } 1143 } 1144} 1145 1146- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint { 1147 NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil]; 1148 NSRect viewFrame = [self frame]; 1149 viewPoint.y = viewFrame.size.height - viewPoint.y; 1150 return viewPoint; 1151} 1152 1153- (void)resetDeviceScaleFactor { 1154 float device_scale_factor = 1.0f; 1155 NSWindow* window = [self window]; 1156 if (window) 1157 device_scale_factor = [window backingScaleFactor]; 1158 [self setDeviceScaleFactor:device_scale_factor]; 1159} 1160 1161- (void)setDeviceScaleFactor:(float)device_scale_factor { 1162 if (device_scale_factor == device_scale_factor_) 1163 return; 1164 1165 // Apply some sanity checks. 1166 if (device_scale_factor < 1.0f || device_scale_factor > 4.0f) 1167 return; 1168 1169 device_scale_factor_ = device_scale_factor; 1170 1171 CefRefPtr<CefBrowser> browser = [self getBrowser]; 1172 if (browser) { 1173 browser->GetHost()->NotifyScreenInfoChanged(); 1174 browser->GetHost()->WasResized(); 1175 } 1176} 1177 1178- (float)getDeviceScaleFactor { 1179 return device_scale_factor_; 1180} 1181 1182- (void)viewDidChangeBackingProperties { 1183 const CGFloat device_scale_factor = [self getDeviceScaleFactor]; 1184 1185 if (device_scale_factor == device_scale_factor_) 1186 return; 1187 1188 CefRefPtr<CefBrowser> browser = [self getBrowser]; 1189 if (browser) { 1190 browser->GetHost()->NotifyScreenInfoChanged(); 1191 browser->GetHost()->WasResized(); 1192 } 1193} 1194 1195- (bool)isOverPopupWidgetX:(int)x andY:(int)y { 1196 CefRect rc = renderer_->popup_rect(); 1197 int popup_right = rc.x + rc.width; 1198 int popup_bottom = rc.y + rc.height; 1199 return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom); 1200} 1201 1202- (int)getPopupXOffset { 1203 return renderer_->original_popup_rect().x - renderer_->popup_rect().x; 1204} 1205 1206- (int)getPopupYOffset { 1207 return renderer_->original_popup_rect().y - renderer_->popup_rect().y; 1208} 1209 1210- (void)applyPopupOffsetToX:(int&)x andY:(int&)y { 1211 if ([self isOverPopupWidgetX:x andY:y]) { 1212 x += [self getPopupXOffset]; 1213 y += [self getPopupYOffset]; 1214 } 1215} 1216 1217// Convert from scaled coordinates to view coordinates. 1218- (NSPoint)convertPointFromBackingInternal:(NSPoint)aPoint { 1219 return [self convertPointFromBacking:aPoint]; 1220} 1221 1222// Convert from view coordinates to scaled coordinates. 1223- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint { 1224 return [self convertPointToBacking:aPoint]; 1225} 1226 1227// Convert from scaled coordinates to view coordinates. 1228- (NSRect)convertRectFromBackingInternal:(NSRect)aRect { 1229 return [self convertRectFromBacking:aRect]; 1230} 1231 1232// Convert from view coordinates to scaled coordinates. 1233- (NSRect)convertRectToBackingInternal:(NSRect)aRect { 1234 return [self convertRectToBacking:aRect]; 1235} 1236 1237- (void)ChangeCompositionRange:(CefRange)range 1238 character_bounds:(const CefRenderHandler::RectList&)bounds { 1239 if (text_input_client_) 1240 [text_input_client_ ChangeCompositionRange:range character_bounds:bounds]; 1241} 1242 1243- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value { 1244 if (!accessibility_helper_) { 1245 accessibility_helper_ = 1246 new client::OsrAccessibilityHelper(value, [self getBrowser]); 1247 } else { 1248 accessibility_helper_->UpdateAccessibilityTree(value); 1249 } 1250 1251 if (accessibility_helper_) { 1252 NSAccessibilityPostNotification(self, 1253 NSAccessibilityValueChangedNotification); 1254 } 1255 return; 1256} 1257 1258- (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value { 1259 if (accessibility_helper_) { 1260 accessibility_helper_->UpdateAccessibilityLocation(value); 1261 } 1262 1263 if (accessibility_helper_) { 1264 NSAccessibilityPostNotification(self, 1265 NSAccessibilityValueChangedNotification); 1266 } 1267 return; 1268} 1269@end 1270 1271namespace client { 1272 1273class BrowserWindowOsrMacImpl { 1274 public: 1275 BrowserWindowOsrMacImpl(BrowserWindow::Delegate* delegate, 1276 const std::string& startup_url, 1277 const OsrRendererSettings& settings, 1278 BrowserWindowOsrMac& browser_window); 1279 ~BrowserWindowOsrMacImpl(); 1280 1281 // BrowserWindow methods. 1282 void CreateBrowser(ClientWindowHandle parent_handle, 1283 const CefRect& rect, 1284 const CefBrowserSettings& settings, 1285 CefRefPtr<CefDictionaryValue> extra_info, 1286 CefRefPtr<CefRequestContext> request_context); 1287 void GetPopupConfig(CefWindowHandle temp_handle, 1288 CefWindowInfo& windowInfo, 1289 CefRefPtr<CefClient>& client, 1290 CefBrowserSettings& settings); 1291 void ShowPopup(ClientWindowHandle parent_handle, 1292 int x, 1293 int y, 1294 size_t width, 1295 size_t height); 1296 void Show(); 1297 void Hide(); 1298 void SetBounds(int x, int y, size_t width, size_t height); 1299 void SetFocus(bool focus); 1300 void SetDeviceScaleFactor(float device_scale_factor); 1301 float GetDeviceScaleFactor() const; 1302 ClientWindowHandle GetWindowHandle() const; 1303 1304 // ClientHandlerOsr::OsrDelegate methods. 1305 void OnAfterCreated(CefRefPtr<CefBrowser> browser); 1306 void OnBeforeClose(CefRefPtr<CefBrowser> browser); 1307 bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect); 1308 void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect); 1309 bool GetScreenPoint(CefRefPtr<CefBrowser> browser, 1310 int viewX, 1311 int viewY, 1312 int& screenX, 1313 int& screenY); 1314 bool GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo& screen_info); 1315 void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show); 1316 void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect); 1317 void OnPaint(CefRefPtr<CefBrowser> browser, 1318 CefRenderHandler::PaintElementType type, 1319 const CefRenderHandler::RectList& dirtyRects, 1320 const void* buffer, 1321 int width, 1322 int height); 1323 void OnCursorChange(CefRefPtr<CefBrowser> browser, 1324 CefCursorHandle cursor, 1325 cef_cursor_type_t type, 1326 const CefCursorInfo& custom_cursor_info); 1327 bool StartDragging(CefRefPtr<CefBrowser> browser, 1328 CefRefPtr<CefDragData> drag_data, 1329 CefRenderHandler::DragOperationsMask allowed_ops, 1330 int x, 1331 int y); 1332 void UpdateDragCursor(CefRefPtr<CefBrowser> browser, 1333 CefRenderHandler::DragOperation operation); 1334 void OnImeCompositionRangeChanged( 1335 CefRefPtr<CefBrowser> browser, 1336 const CefRange& selection_range, 1337 const CefRenderHandler::RectList& character_bounds); 1338 1339 void UpdateAccessibilityTree(CefRefPtr<CefValue> value); 1340 void UpdateAccessibilityLocation(CefRefPtr<CefValue> value); 1341 1342 private: 1343 // Create the NSView. 1344 void Create(ClientWindowHandle parent_handle, const CefRect& rect); 1345 1346 BrowserWindowOsrMac& browser_window_; 1347 // The below members will only be accessed on the main thread which should be 1348 // the same as the CEF UI thread. 1349 OsrRenderer renderer_; 1350 BrowserOpenGLView* native_browser_view_; 1351 bool hidden_; 1352 bool painting_popup_; 1353}; 1354 1355BrowserWindowOsrMacImpl::BrowserWindowOsrMacImpl( 1356 BrowserWindow::Delegate* delegate, 1357 const std::string& startup_url, 1358 const OsrRendererSettings& settings, 1359 BrowserWindowOsrMac& browser_window) 1360 : browser_window_(browser_window), 1361 renderer_(settings), 1362 native_browser_view_(nil), 1363 hidden_(false), 1364 painting_popup_(false) {} 1365 1366BrowserWindowOsrMacImpl::~BrowserWindowOsrMacImpl() { 1367 if (native_browser_view_) { 1368 // Disassociate the view with |this|. 1369 [native_browser_view_ detach]; 1370 } 1371} 1372 1373void BrowserWindowOsrMacImpl::CreateBrowser( 1374 ClientWindowHandle parent_handle, 1375 const CefRect& rect, 1376 const CefBrowserSettings& settings, 1377 CefRefPtr<CefDictionaryValue> extra_info, 1378 CefRefPtr<CefRequestContext> request_context) { 1379 REQUIRE_MAIN_THREAD(); 1380 1381 // Create the native NSView. 1382 Create(parent_handle, rect); 1383 1384 CefWindowInfo window_info; 1385 window_info.SetAsWindowless( 1386 CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_)); 1387 1388 // Create the browser asynchronously. 1389 CefBrowserHost::CreateBrowser(window_info, browser_window_.client_handler_, 1390 browser_window_.client_handler_->startup_url(), 1391 settings, extra_info, request_context); 1392} 1393 1394void BrowserWindowOsrMacImpl::GetPopupConfig(CefWindowHandle temp_handle, 1395 CefWindowInfo& windowInfo, 1396 CefRefPtr<CefClient>& client, 1397 CefBrowserSettings& settings) { 1398 CEF_REQUIRE_UI_THREAD(); 1399 1400 windowInfo.SetAsWindowless(temp_handle); 1401 client = browser_window_.client_handler_; 1402} 1403 1404void BrowserWindowOsrMacImpl::ShowPopup(ClientWindowHandle parent_handle, 1405 int x, 1406 int y, 1407 size_t width, 1408 size_t height) { 1409 REQUIRE_MAIN_THREAD(); 1410 DCHECK(browser_window_.browser_.get()); 1411 1412 // Create the native NSView. 1413 Create(parent_handle, 1414 CefRect(x, y, static_cast<int>(width), static_cast<int>(height))); 1415 1416 // Send resize notification so the compositor is assigned the correct 1417 // viewport size and begins rendering. 1418 browser_window_.browser_->GetHost()->WasResized(); 1419 1420 Show(); 1421} 1422 1423void BrowserWindowOsrMacImpl::Show() { 1424 REQUIRE_MAIN_THREAD(); 1425 1426 if (hidden_) { 1427 // Set the browser as visible. 1428 browser_window_.browser_->GetHost()->WasHidden(false); 1429 hidden_ = false; 1430 } 1431 1432 // Give focus to the browser. 1433 browser_window_.browser_->GetHost()->SetFocus(true); 1434} 1435 1436void BrowserWindowOsrMacImpl::Hide() { 1437 REQUIRE_MAIN_THREAD(); 1438 1439 if (!browser_window_.browser_.get()) 1440 return; 1441 1442 // Remove focus from the browser. 1443 browser_window_.browser_->GetHost()->SetFocus(false); 1444 1445 if (!hidden_) { 1446 // Set the browser as hidden. 1447 browser_window_.browser_->GetHost()->WasHidden(true); 1448 hidden_ = true; 1449 } 1450} 1451 1452void BrowserWindowOsrMacImpl::SetBounds(int x, 1453 int y, 1454 size_t width, 1455 size_t height) { 1456 REQUIRE_MAIN_THREAD(); 1457 // Nothing to do here. GTK will take care of positioning in the container. 1458} 1459 1460void BrowserWindowOsrMacImpl::SetFocus(bool focus) { 1461 REQUIRE_MAIN_THREAD(); 1462 if (native_browser_view_) 1463 [native_browser_view_.window makeFirstResponder:native_browser_view_]; 1464} 1465 1466void BrowserWindowOsrMacImpl::SetDeviceScaleFactor(float device_scale_factor) { 1467 REQUIRE_MAIN_THREAD(); 1468 if (native_browser_view_) 1469 [native_browser_view_ setDeviceScaleFactor:device_scale_factor]; 1470} 1471 1472float BrowserWindowOsrMacImpl::GetDeviceScaleFactor() const { 1473 REQUIRE_MAIN_THREAD(); 1474 if (native_browser_view_) 1475 return [native_browser_view_ getDeviceScaleFactor]; 1476 return 1.0f; 1477} 1478 1479ClientWindowHandle BrowserWindowOsrMacImpl::GetWindowHandle() const { 1480 REQUIRE_MAIN_THREAD(); 1481 return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_); 1482} 1483 1484void BrowserWindowOsrMacImpl::OnAfterCreated(CefRefPtr<CefBrowser> browser) { 1485 CEF_REQUIRE_UI_THREAD(); 1486} 1487 1488void BrowserWindowOsrMacImpl::OnBeforeClose(CefRefPtr<CefBrowser> browser) { 1489 CEF_REQUIRE_UI_THREAD(); 1490 REQUIRE_MAIN_THREAD(); 1491 1492 // Detach |this| from the ClientHandlerOsr. 1493 static_cast<ClientHandlerOsr*>(browser_window_.client_handler_.get()) 1494 ->DetachOsrDelegate(); 1495} 1496 1497bool BrowserWindowOsrMacImpl::GetRootScreenRect(CefRefPtr<CefBrowser> browser, 1498 CefRect& rect) { 1499 CEF_REQUIRE_UI_THREAD(); 1500 return false; 1501} 1502 1503void BrowserWindowOsrMacImpl::GetViewRect(CefRefPtr<CefBrowser> browser, 1504 CefRect& rect) { 1505 CEF_REQUIRE_UI_THREAD(); 1506 REQUIRE_MAIN_THREAD(); 1507 1508 rect.x = rect.y = 0; 1509 1510 if (!native_browser_view_) { 1511 // Never return an empty rectangle. 1512 rect.width = rect.height = 1; 1513 return; 1514 } 1515 1516 const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1517 1518 // |bounds| is in OS X view coordinates. 1519 NSRect bounds = native_browser_view_.bounds; 1520 1521 // Convert to device coordinates. 1522 bounds = [native_browser_view_ convertRectToBackingInternal:bounds]; 1523 1524 // Convert to browser view coordinates. 1525 rect.width = DeviceToLogical(bounds.size.width, device_scale_factor); 1526 if (rect.width == 0) 1527 rect.width = 1; 1528 rect.height = DeviceToLogical(bounds.size.height, device_scale_factor); 1529 if (rect.height == 0) 1530 rect.height = 1; 1531} 1532 1533bool BrowserWindowOsrMacImpl::GetScreenPoint(CefRefPtr<CefBrowser> browser, 1534 int viewX, 1535 int viewY, 1536 int& screenX, 1537 int& screenY) { 1538 CEF_REQUIRE_UI_THREAD(); 1539 REQUIRE_MAIN_THREAD(); 1540 1541 if (!native_browser_view_) 1542 return false; 1543 1544 const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1545 1546 // (viewX, viewX) is in browser view coordinates. 1547 // Convert to device coordinates. 1548 NSPoint view_pt = NSMakePoint(LogicalToDevice(viewX, device_scale_factor), 1549 LogicalToDevice(viewY, device_scale_factor)); 1550 1551 // Convert to OS X view coordinates. 1552 view_pt = [native_browser_view_ convertPointFromBackingInternal:view_pt]; 1553 1554 // Reverse the Y component. 1555 const NSRect bounds = native_browser_view_.bounds; 1556 view_pt.y = bounds.size.height - view_pt.y; 1557 1558 // Convert to screen coordinates. 1559 NSPoint window_pt = [native_browser_view_ convertPoint:view_pt toView:nil]; 1560 NSPoint screen_pt = 1561 ConvertPointFromWindowToScreen(native_browser_view_.window, window_pt); 1562 1563 screenX = screen_pt.x; 1564 screenY = screen_pt.y; 1565 return true; 1566} 1567 1568bool BrowserWindowOsrMacImpl::GetScreenInfo(CefRefPtr<CefBrowser> browser, 1569 CefScreenInfo& screen_info) { 1570 CEF_REQUIRE_UI_THREAD(); 1571 REQUIRE_MAIN_THREAD(); 1572 1573 if (!native_browser_view_) 1574 return false; 1575 1576 CefRect view_rect; 1577 GetViewRect(browser, view_rect); 1578 1579 screen_info.device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1580 1581 // The screen info rectangles are used by the renderer to create and position 1582 // popups. Keep popups inside the view rectangle. 1583 screen_info.rect = view_rect; 1584 screen_info.available_rect = view_rect; 1585 1586 return true; 1587} 1588 1589void BrowserWindowOsrMacImpl::OnPopupShow(CefRefPtr<CefBrowser> browser, 1590 bool show) { 1591 CEF_REQUIRE_UI_THREAD(); 1592 REQUIRE_MAIN_THREAD(); 1593 1594 if (!native_browser_view_) 1595 return; 1596 1597 if (!show) { 1598 renderer_.ClearPopupRects(); 1599 browser->GetHost()->Invalidate(PET_VIEW); 1600 } 1601 renderer_.OnPopupShow(browser, show); 1602} 1603 1604void BrowserWindowOsrMacImpl::OnPopupSize(CefRefPtr<CefBrowser> browser, 1605 const CefRect& rect) { 1606 CEF_REQUIRE_UI_THREAD(); 1607 REQUIRE_MAIN_THREAD(); 1608 1609 if (!native_browser_view_) 1610 return; 1611 1612 const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; 1613 1614 // |rect| is in browser view coordinates. Convert to device coordinates. 1615 CefRect device_rect = LogicalToDevice(rect, device_scale_factor); 1616 1617 renderer_.OnPopupSize(browser, device_rect); 1618} 1619 1620void BrowserWindowOsrMacImpl::OnPaint( 1621 CefRefPtr<CefBrowser> browser, 1622 CefRenderHandler::PaintElementType type, 1623 const CefRenderHandler::RectList& dirtyRects, 1624 const void* buffer, 1625 int width, 1626 int height) { 1627 CEF_REQUIRE_UI_THREAD(); 1628 REQUIRE_MAIN_THREAD(); 1629 1630 if (!native_browser_view_) 1631 return; 1632 1633 if (width <= 2 && height <= 2) { 1634 // Ignore really small buffer sizes while the widget is starting up. 1635 return; 1636 } 1637 1638 if (painting_popup_) { 1639 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); 1640 return; 1641 } 1642 1643 ScopedGLContext scoped_gl_context(native_browser_view_, true); 1644 1645 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); 1646 if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) { 1647 painting_popup_ = true; 1648 browser->GetHost()->Invalidate(PET_POPUP); 1649 painting_popup_ = false; 1650 } 1651 renderer_.Render(); 1652} 1653 1654void BrowserWindowOsrMacImpl::OnCursorChange( 1655 CefRefPtr<CefBrowser> browser, 1656 CefCursorHandle cursor, 1657 cef_cursor_type_t type, 1658 const CefCursorInfo& custom_cursor_info) { 1659 CEF_REQUIRE_UI_THREAD(); 1660 REQUIRE_MAIN_THREAD(); 1661 1662 [CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(cursor) set]; 1663} 1664 1665bool BrowserWindowOsrMacImpl::StartDragging( 1666 CefRefPtr<CefBrowser> browser, 1667 CefRefPtr<CefDragData> drag_data, 1668 CefRenderHandler::DragOperationsMask allowed_ops, 1669 int x, 1670 int y) { 1671 CEF_REQUIRE_UI_THREAD(); 1672 REQUIRE_MAIN_THREAD(); 1673 1674 if (!native_browser_view_) 1675 return false; 1676 1677 static float device_scale_factor = 1678 [native_browser_view_ getDeviceScaleFactor]; 1679 1680 // |point| is in browser view coordinates. 1681 NSPoint point = NSMakePoint(x, y); 1682 1683 // Convert to device coordinates. 1684 point.x = LogicalToDevice(point.x, device_scale_factor); 1685 point.y = LogicalToDevice(point.y, device_scale_factor); 1686 1687 // Convert to OS X view coordinates. 1688 point = [native_browser_view_ convertPointFromBackingInternal:point]; 1689 1690 return [native_browser_view_ 1691 startDragging:drag_data 1692 allowedOps:static_cast<NSDragOperation>(allowed_ops) 1693 point:point]; 1694} 1695 1696void BrowserWindowOsrMacImpl::UpdateDragCursor( 1697 CefRefPtr<CefBrowser> browser, 1698 CefRenderHandler::DragOperation operation) { 1699 CEF_REQUIRE_UI_THREAD(); 1700 REQUIRE_MAIN_THREAD(); 1701 1702 if (native_browser_view_) 1703 [native_browser_view_ setCurrentDragOp:operation]; 1704} 1705 1706void BrowserWindowOsrMacImpl::OnImeCompositionRangeChanged( 1707 CefRefPtr<CefBrowser> browser, 1708 const CefRange& selection_range, 1709 const CefRenderHandler::RectList& bounds) { 1710 CEF_REQUIRE_UI_THREAD(); 1711 1712 if (native_browser_view_) { 1713 [native_browser_view_ ChangeCompositionRange:selection_range 1714 character_bounds:bounds]; 1715 } 1716} 1717 1718void BrowserWindowOsrMacImpl::UpdateAccessibilityTree( 1719 CefRefPtr<CefValue> value) { 1720 CEF_REQUIRE_UI_THREAD(); 1721 1722 if (native_browser_view_) { 1723 [native_browser_view_ UpdateAccessibilityTree:value]; 1724 } 1725} 1726 1727void BrowserWindowOsrMacImpl::UpdateAccessibilityLocation( 1728 CefRefPtr<CefValue> value) { 1729 CEF_REQUIRE_UI_THREAD(); 1730 1731 if (native_browser_view_) { 1732 [native_browser_view_ UpdateAccessibilityLocation:value]; 1733 } 1734} 1735 1736void BrowserWindowOsrMacImpl::Create(ClientWindowHandle parent_handle, 1737 const CefRect& rect) { 1738 REQUIRE_MAIN_THREAD(); 1739 DCHECK(!native_browser_view_); 1740 1741 NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); 1742 native_browser_view_ = 1743 [[BrowserOpenGLView alloc] initWithFrame:window_rect 1744 andBrowserWindow:&browser_window_ 1745 andRenderer:&renderer_]; 1746 native_browser_view_.autoresizingMask = 1747 (NSViewWidthSizable | NSViewHeightSizable); 1748 native_browser_view_.autoresizesSubviews = YES; 1749 [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle) 1750 addSubview:native_browser_view_]; 1751 1752 // Determine the default scale factor. 1753 [native_browser_view_ resetDeviceScaleFactor]; 1754 1755 [[NSNotificationCenter defaultCenter] 1756 addObserver:native_browser_view_ 1757 selector:@selector(windowDidChangeBackingProperties:) 1758 name:NSWindowDidChangeBackingPropertiesNotification 1759 object:native_browser_view_.window]; 1760} 1761 1762BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate, 1763 const std::string& startup_url, 1764 const OsrRendererSettings& settings) 1765 : BrowserWindow(delegate) { 1766 client_handler_ = new ClientHandlerOsr(this, this, startup_url); 1767 impl_.reset( 1768 new BrowserWindowOsrMacImpl(delegate, startup_url, settings, *this)); 1769} 1770 1771BrowserWindowOsrMac::~BrowserWindowOsrMac() {} 1772 1773void BrowserWindowOsrMac::CreateBrowser( 1774 ClientWindowHandle parent_handle, 1775 const CefRect& rect, 1776 const CefBrowserSettings& settings, 1777 CefRefPtr<CefDictionaryValue> extra_info, 1778 CefRefPtr<CefRequestContext> request_context) { 1779 impl_->CreateBrowser(parent_handle, rect, settings, extra_info, 1780 request_context); 1781} 1782 1783void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle, 1784 CefWindowInfo& windowInfo, 1785 CefRefPtr<CefClient>& client, 1786 CefBrowserSettings& settings) { 1787 impl_->GetPopupConfig(temp_handle, windowInfo, client, settings); 1788} 1789 1790void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle, 1791 int x, 1792 int y, 1793 size_t width, 1794 size_t height) { 1795 impl_->ShowPopup(parent_handle, x, y, width, height); 1796} 1797 1798void BrowserWindowOsrMac::Show() { 1799 impl_->Show(); 1800} 1801 1802void BrowserWindowOsrMac::Hide() { 1803 impl_->Hide(); 1804} 1805 1806void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) { 1807 impl_->SetBounds(x, y, width, height); 1808} 1809 1810void BrowserWindowOsrMac::SetFocus(bool focus) { 1811 impl_->SetFocus(focus); 1812} 1813 1814void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) { 1815 impl_->SetDeviceScaleFactor(device_scale_factor); 1816} 1817 1818float BrowserWindowOsrMac::GetDeviceScaleFactor() const { 1819 return impl_->GetDeviceScaleFactor(); 1820} 1821 1822ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const { 1823 return impl_->GetWindowHandle(); 1824} 1825 1826void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr<CefBrowser> browser) { 1827 impl_->OnAfterCreated(browser); 1828} 1829 1830void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr<CefBrowser> browser) { 1831 impl_->OnBeforeClose(browser); 1832} 1833 1834bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr<CefBrowser> browser, 1835 CefRect& rect) { 1836 return impl_->GetRootScreenRect(browser, rect); 1837} 1838 1839void BrowserWindowOsrMac::GetViewRect(CefRefPtr<CefBrowser> browser, 1840 CefRect& rect) { 1841 impl_->GetViewRect(browser, rect); 1842} 1843 1844bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr<CefBrowser> browser, 1845 int viewX, 1846 int viewY, 1847 int& screenX, 1848 int& screenY) { 1849 return impl_->GetScreenPoint(browser, viewX, viewY, screenX, screenY); 1850} 1851 1852bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr<CefBrowser> browser, 1853 CefScreenInfo& screen_info) { 1854 return impl_->GetScreenInfo(browser, screen_info); 1855} 1856 1857void BrowserWindowOsrMac::OnPopupShow(CefRefPtr<CefBrowser> browser, 1858 bool show) { 1859 impl_->OnPopupShow(browser, show); 1860} 1861 1862void BrowserWindowOsrMac::OnPopupSize(CefRefPtr<CefBrowser> browser, 1863 const CefRect& rect) { 1864 impl_->OnPopupSize(browser, rect); 1865} 1866 1867void BrowserWindowOsrMac::OnPaint(CefRefPtr<CefBrowser> browser, 1868 CefRenderHandler::PaintElementType type, 1869 const CefRenderHandler::RectList& dirtyRects, 1870 const void* buffer, 1871 int width, 1872 int height) { 1873 impl_->OnPaint(browser, type, dirtyRects, buffer, width, height); 1874} 1875 1876void BrowserWindowOsrMac::OnCursorChange( 1877 CefRefPtr<CefBrowser> browser, 1878 CefCursorHandle cursor, 1879 cef_cursor_type_t type, 1880 const CefCursorInfo& custom_cursor_info) { 1881 impl_->OnCursorChange(browser, cursor, type, custom_cursor_info); 1882} 1883 1884bool BrowserWindowOsrMac::StartDragging( 1885 CefRefPtr<CefBrowser> browser, 1886 CefRefPtr<CefDragData> drag_data, 1887 CefRenderHandler::DragOperationsMask allowed_ops, 1888 int x, 1889 int y) { 1890 return impl_->StartDragging(browser, drag_data, allowed_ops, x, y); 1891} 1892 1893void BrowserWindowOsrMac::UpdateDragCursor( 1894 CefRefPtr<CefBrowser> browser, 1895 CefRenderHandler::DragOperation operation) { 1896 impl_->UpdateDragCursor(browser, operation); 1897} 1898 1899void BrowserWindowOsrMac::OnImeCompositionRangeChanged( 1900 CefRefPtr<CefBrowser> browser, 1901 const CefRange& selection_range, 1902 const CefRenderHandler::RectList& character_bounds) { 1903 impl_->OnImeCompositionRangeChanged(browser, selection_range, 1904 character_bounds); 1905} 1906 1907void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr<CefValue> value) { 1908 impl_->UpdateAccessibilityTree(value); 1909} 1910 1911void BrowserWindowOsrMac::UpdateAccessibilityLocation( 1912 CefRefPtr<CefValue> value) { 1913 impl_->UpdateAccessibilityLocation(value); 1914} 1915 1916} // namespace client 1917