| // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights |
| // reserved. Use of this source code is governed by a BSD-style license that |
| // can be found in the LICENSE file. |
| |
| #include "tests/cefclient/browser/browser_window_osr_mac.h" |
| |
| #include <Cocoa/Cocoa.h> |
| #include <OpenGL/gl.h> |
| #import <objc/runtime.h> |
| |
| #include "include/base/cef_logging.h" |
| #include "include/cef_parser.h" |
| #include "include/wrapper/cef_closure_task.h" |
| #include "tests/cefclient/browser/bytes_write_handler.h" |
| #include "tests/cefclient/browser/main_context.h" |
| #include "tests/cefclient/browser/osr_accessibility_helper.h" |
| #include "tests/cefclient/browser/osr_accessibility_node.h" |
| #include "tests/cefclient/browser/text_input_client_osr_mac.h" |
| #include "tests/shared/browser/geometry_util.h" |
| #include "tests/shared/browser/main_message_loop.h" |
| |
| #import <AppKit/NSAccessibility.h> |
| |
| @interface BrowserOpenGLView |
| : NSOpenGLView <NSDraggingSource, NSDraggingDestination, NSAccessibility> { |
| @private |
| NSTrackingArea* tracking_area_; |
| client::BrowserWindowOsrMac* browser_window_; |
| client::OsrRenderer* renderer_; |
| NSPoint last_mouse_pos_; |
| NSPoint cur_mouse_pos_; |
| bool rotating_; |
| |
| bool was_last_mouse_down_on_view_; |
| |
| float device_scale_factor_; |
| |
| // Drag and drop. |
| CefRefPtr<CefDragData> current_drag_data_; |
| NSDragOperation current_drag_op_; |
| NSDragOperation current_allowed_ops_; |
| NSPasteboard* pasteboard_; |
| NSString* fileUTI_; |
| |
| // For intreacting with IME. |
| NSTextInputContext* text_input_context_osr_mac_; |
| CefTextInputClientOSRMac* text_input_client_; |
| |
| // Manages Accessibility Tree |
| client::OsrAccessibilityHelper* accessibility_helper_; |
| |
| // Event monitor for scroll wheel end event. |
| id endWheelMonitor_; |
| } |
| |
| @end // @interface BrowserOpenGLView |
| |
| namespace { |
| |
| NSString* const kCEFDragDummyPboardType = @"org.CEF.drag-dummy-type"; |
| NSString* const kNSURLTitlePboardType = @"public.url-name"; |
| |
| class ScopedGLContext { |
| public: |
| ScopedGLContext(BrowserOpenGLView* view, bool swap_buffers) |
| : swap_buffers_(swap_buffers) { |
| context_ = [view openGLContext]; |
| [context_ makeCurrentContext]; |
| } |
| ~ScopedGLContext() { |
| [NSOpenGLContext clearCurrentContext]; |
| if (swap_buffers_) |
| [context_ flushBuffer]; |
| } |
| |
| private: |
| NSOpenGLContext* context_; |
| const bool swap_buffers_; |
| }; |
| |
| NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { |
| NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0); |
| return [window convertRectToScreen:point_rect].origin; |
| } |
| |
| } // namespace |
| |
| @implementation BrowserOpenGLView |
| |
| - (id)initWithFrame:(NSRect)frame |
| andBrowserWindow:(client::BrowserWindowOsrMac*)browser_window |
| andRenderer:(client::OsrRenderer*)renderer { |
| NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] |
| initWithAttributes:(NSOpenGLPixelFormatAttribute[]){ |
| NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 32, |
| 0}]; |
| #if !__has_feature(objc_arc) |
| [pixelFormat autorelease]; |
| #endif // !__has_feature(objc_arc) |
| |
| if (self = [super initWithFrame:frame pixelFormat:pixelFormat]) { |
| browser_window_ = browser_window; |
| renderer_ = renderer; |
| rotating_ = false; |
| endWheelMonitor_ = nil; |
| device_scale_factor_ = 1.0f; |
| |
| tracking_area_ = [[NSTrackingArea alloc] |
| initWithRect:frame |
| options:NSTrackingMouseMoved | NSTrackingActiveInActiveApp | |
| NSTrackingInVisibleRect |
| owner:self |
| userInfo:nil]; |
| [self addTrackingArea:tracking_area_]; |
| |
| // enable HiDPI buffer |
| [self setWantsBestResolutionOpenGLSurface:YES]; |
| |
| [self resetDragDrop]; |
| |
| NSArray* types = [NSArray |
| arrayWithObjects:kCEFDragDummyPboardType, NSStringPboardType, |
| NSFilenamesPboardType, NSPasteboardTypeString, nil]; |
| [self registerForDraggedTypes:types]; |
| } |
| |
| return self; |
| } |
| |
| - (void)dealloc { |
| [[NSNotificationCenter defaultCenter] |
| removeObserver:self |
| name:NSWindowDidChangeBackingPropertiesNotification |
| object:nil]; |
| #if !__has_feature(objc_arc) |
| if (text_input_context_osr_mac_) { |
| [text_input_client_ release]; |
| [text_input_context_osr_mac_ release]; |
| } |
| [super dealloc]; |
| #endif // !__has_feature(objc_arc) |
| } |
| |
| - (void)detach { |
| renderer_ = NULL; |
| browser_window_ = NULL; |
| if (text_input_client_) |
| [text_input_client_ detach]; |
| } |
| |
| - (CefRefPtr<CefBrowser>)getBrowser { |
| if (browser_window_) |
| return browser_window_->GetBrowser(); |
| return NULL; |
| } |
| |
| - (void)setFrame:(NSRect)frameRect { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| [super setFrame:frameRect]; |
| browser->GetHost()->WasResized(); |
| } |
| |
| - (void)sendMouseClick:(NSEvent*)event |
| button:(CefBrowserHost::MouseButtonType)type |
| isUp:(bool)isUp { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forEvent:event]; |
| |
| // |point| is in OS X view coordinates. |
| NSPoint point = [self getClickPointForEvent:event]; |
| |
| // Convert to device coordinates. |
| point = [self convertPointToBackingInternal:point]; |
| |
| if (!isUp) { |
| was_last_mouse_down_on_view_ = ![self isOverPopupWidgetX:point.x |
| andY:point.y]; |
| } else if (was_last_mouse_down_on_view_ && |
| [self isOverPopupWidgetX:point.x andY:point.y] && |
| ([self getPopupXOffset] || [self getPopupYOffset])) { |
| return; |
| } |
| |
| browser->GetHost()->SendMouseClickEvent(mouseEvent, type, isUp, |
| [event clickCount]); |
| } |
| |
| - (void)mouseDown:(NSEvent*)event { |
| [self sendMouseClick:event button:MBT_LEFT isUp:false]; |
| } |
| |
| - (void)rightMouseDown:(NSEvent*)event { |
| if ([event modifierFlags] & NSShiftKeyMask) { |
| // Start rotation effect. |
| last_mouse_pos_ = cur_mouse_pos_ = [self getClickPointForEvent:event]; |
| rotating_ = true; |
| return; |
| } |
| |
| [self sendMouseClick:event button:MBT_RIGHT isUp:false]; |
| } |
| |
| - (void)otherMouseDown:(NSEvent*)event { |
| [self sendMouseClick:event button:MBT_MIDDLE isUp:false]; |
| } |
| |
| - (void)mouseUp:(NSEvent*)event { |
| [self sendMouseClick:event button:MBT_LEFT isUp:true]; |
| } |
| |
| - (void)rightMouseUp:(NSEvent*)event { |
| if (rotating_) { |
| // End rotation effect. |
| renderer_->SetSpin(0, 0); |
| rotating_ = false; |
| [self setNeedsDisplay:YES]; |
| return; |
| } |
| [self sendMouseClick:event button:MBT_RIGHT isUp:true]; |
| } |
| |
| - (void)otherMouseUp:(NSEvent*)event { |
| [self sendMouseClick:event button:MBT_MIDDLE isUp:true]; |
| } |
| |
| - (void)mouseMoved:(NSEvent*)event { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| if (rotating_) { |
| // Apply rotation effect. |
| cur_mouse_pos_ = [self getClickPointForEvent:event]; |
| ; |
| renderer_->IncrementSpin((cur_mouse_pos_.x - last_mouse_pos_.x), |
| (cur_mouse_pos_.y - last_mouse_pos_.y)); |
| last_mouse_pos_ = cur_mouse_pos_; |
| [self setNeedsDisplay:YES]; |
| return; |
| } |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forEvent:event]; |
| |
| browser->GetHost()->SendMouseMoveEvent(mouseEvent, false); |
| } |
| |
| - (void)mouseDragged:(NSEvent*)event { |
| [self mouseMoved:event]; |
| } |
| |
| - (void)rightMouseDragged:(NSEvent*)event { |
| [self mouseMoved:event]; |
| } |
| |
| - (void)otherMouseDragged:(NSEvent*)event { |
| [self mouseMoved:event]; |
| } |
| |
| - (void)mouseEntered:(NSEvent*)event { |
| [self mouseMoved:event]; |
| } |
| |
| - (void)mouseExited:(NSEvent*)event { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forEvent:event]; |
| |
| browser->GetHost()->SendMouseMoveEvent(mouseEvent, true); |
| } |
| |
| - (void)keyDown:(NSEvent*)event { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get() || !text_input_context_osr_mac_) |
| return; |
| |
| if ([event type] != NSFlagsChanged) { |
| if (text_input_client_) { |
| [text_input_client_ HandleKeyEventBeforeTextInputClient:event]; |
| |
| // The return value of this method seems to always be set to YES, thus we |
| // ignore it and ask the host view whether IME is active or not. |
| [text_input_context_osr_mac_ handleEvent:event]; |
| |
| CefKeyEvent keyEvent; |
| [self getKeyEvent:keyEvent forEvent:event]; |
| |
| [text_input_client_ HandleKeyEventAfterTextInputClient:keyEvent]; |
| } |
| } |
| |
| // Check for Caps lock and Toggle Touch Emulation |
| if (client::MainContext::Get()->TouchEventsEnabled()) |
| [self toggleTouchEmulation:event]; |
| } |
| |
| // OSX does not have touch screens, so we emulate it by mapping multitouch |
| // events on TrackPad to Touch Events on Screen. To ensure it does not |
| // interfere with other Trackpad events, this mapping is only enabled if |
| // touch-events=enabled commandline is passed and caps lock key is on. |
| - (void)toggleTouchEmulation:(NSEvent*)event { |
| if ([event type] == NSFlagsChanged && [event keyCode] == 0x39) { |
| NSUInteger flags = [event modifierFlags]; |
| BOOL touch_enabled = flags & NSAlphaShiftKeyMask ? YES : NO; |
| [self setAcceptsTouchEvents:touch_enabled]; |
| } |
| } |
| |
| - (cef_touch_event_type_t)getTouchPhase:(NSTouchPhase)phase { |
| cef_touch_event_type_t event_type = CEF_TET_RELEASED; |
| switch (phase) { |
| case NSTouchPhaseBegan: |
| event_type = CEF_TET_PRESSED; |
| break; |
| case NSTouchPhaseMoved: |
| event_type = CEF_TET_MOVED; |
| break; |
| case NSTouchPhaseEnded: |
| event_type = CEF_TET_RELEASED; |
| break; |
| case NSTouchPhaseCancelled: |
| event_type = CEF_TET_CANCELLED; |
| break; |
| default: |
| break; |
| } |
| return event_type; |
| } |
| |
| // Translate NSTouch events to CefTouchEvents and send to browser. |
| - (void)sendTouchEvent:(NSEvent*)event touchPhase:(NSTouchPhase)phase { |
| int modifiers = [self getModifiersForEvent:event]; |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| |
| NSSet* touches = [event touchesMatchingPhase:phase inView:self]; |
| |
| for (NSTouch* touch in touches) { |
| // Convert NSTouch to CefTouchEvent. |
| CefTouchEvent touch_event; |
| |
| // NSTouch.identity is unique during the life of the touch |
| touch_event.id = touch.identity.hash; |
| touch_event.type = [self getTouchPhase:phase]; |
| |
| NSPoint scaled_pos = [touch normalizedPosition]; |
| NSSize view_size = [self bounds].size; |
| |
| // Map point on Touch Device to View coordinates. |
| NSPoint touch_point = NSMakePoint(scaled_pos.x * view_size.width, |
| scaled_pos.y * view_size.height); |
| |
| NSPoint contentLocal = [self convertPoint:touch_point fromView:nil]; |
| NSPoint point; |
| point.x = contentLocal.x; |
| point.y = [self frame].size.height - contentLocal.y; // Flip y. |
| |
| // Convert to device coordinates. |
| point = [self convertPointToBackingInternal:point]; |
| |
| int device_x = point.x; |
| int device_y = point.y; |
| |
| const float device_scale_factor = [self getDeviceScaleFactor]; |
| // Convert to browser view coordinates. |
| touch_event.x = client::DeviceToLogical(device_x, device_scale_factor); |
| touch_event.y = client::DeviceToLogical(device_y, device_scale_factor); |
| |
| touch_event.radius_x = 0; |
| touch_event.radius_y = 0; |
| |
| touch_event.rotation_angle = 0; |
| touch_event.pressure = 0; |
| |
| touch_event.modifiers = modifiers; |
| |
| // Notify the browser of touch event. |
| browser->GetHost()->SendTouchEvent(touch_event); |
| } |
| } |
| |
| - (void)touchesBeganWithEvent:(NSEvent*)event { |
| [self sendTouchEvent:event touchPhase:NSTouchPhaseBegan]; |
| } |
| |
| - (void)touchesMovedWithEvent:(NSEvent*)event { |
| [self sendTouchEvent:event touchPhase:NSTouchPhaseMoved]; |
| } |
| |
| - (void)touchesEndedWithEvent:(NSEvent*)event { |
| [self sendTouchEvent:event touchPhase:NSTouchPhaseEnded]; |
| } |
| |
| - (void)touchesCancelledWithEvent:(NSEvent*)event { |
| [self sendTouchEvent:event touchPhase:NSTouchPhaseCancelled]; |
| } |
| |
| - (void)keyUp:(NSEvent*)event { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| CefKeyEvent keyEvent; |
| [self getKeyEvent:keyEvent forEvent:event]; |
| |
| keyEvent.type = KEYEVENT_KEYUP; |
| browser->GetHost()->SendKeyEvent(keyEvent); |
| } |
| |
| - (void)flagsChanged:(NSEvent*)event { |
| if ([self isKeyUpEvent:event]) |
| [self keyUp:event]; |
| else |
| [self keyDown:event]; |
| } |
| |
| - (void)shortCircuitScrollWheelEvent:(NSEvent*)event { |
| if ([event phase] != NSEventPhaseEnded && |
| [event phase] != NSEventPhaseCancelled) |
| return; |
| |
| [self sendScrollWheelEvet:event]; |
| |
| if (endWheelMonitor_) { |
| [NSEvent removeMonitor:endWheelMonitor_]; |
| endWheelMonitor_ = nil; |
| } |
| } |
| |
| - (void)scrollWheel:(NSEvent*)event { |
| // Use an NSEvent monitor to listen for the wheel-end end. This ensures that |
| // the event is received even when the mouse cursor is no longer over the |
| // view when the scrolling ends. Also it avoids sending duplicate scroll |
| // events to the renderer. |
| if ([event phase] == NSEventPhaseBegan && !endWheelMonitor_) { |
| endWheelMonitor_ = [NSEvent |
| addLocalMonitorForEventsMatchingMask:NSScrollWheelMask |
| handler:^(NSEvent* blockEvent) { |
| [self shortCircuitScrollWheelEvent: |
| blockEvent]; |
| return blockEvent; |
| }]; |
| } |
| |
| [self sendScrollWheelEvet:event]; |
| } |
| |
| - (void)sendScrollWheelEvet:(NSEvent*)event { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| CGEventRef cgEvent = [event CGEvent]; |
| DCHECK(cgEvent); |
| |
| int deltaX = |
| CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2); |
| int deltaY = |
| CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forEvent:event]; |
| |
| browser->GetHost()->SendMouseWheelEvent(mouseEvent, deltaX, deltaY); |
| } |
| |
| - (BOOL)canBecomeKeyView { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| return (browser.get() != NULL); |
| } |
| |
| - (BOOL)acceptsFirstResponder { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| return (browser.get() != NULL); |
| } |
| |
| - (BOOL)becomeFirstResponder { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) { |
| browser->GetHost()->SendFocusEvent(true); |
| return [super becomeFirstResponder]; |
| } |
| |
| return NO; |
| } |
| |
| - (BOOL)resignFirstResponder { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) { |
| browser->GetHost()->SendFocusEvent(false); |
| return [super resignFirstResponder]; |
| } |
| |
| return NO; |
| } |
| |
| - (void)undo:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->Undo(); |
| } |
| |
| - (void)redo:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->Redo(); |
| } |
| |
| - (void)cut:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->Cut(); |
| } |
| |
| - (void)copy:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->Copy(); |
| } |
| |
| - (void)paste:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->Paste(); |
| } |
| |
| - (void)delete:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->Delete(); |
| } |
| |
| - (void)selectAll:(id)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetFocusedFrame()->SelectAll(); |
| } |
| |
| - (NSPoint)getClickPointForEvent:(NSEvent*)event { |
| NSPoint windowLocal = [event locationInWindow]; |
| NSPoint contentLocal = [self convertPoint:windowLocal fromView:nil]; |
| |
| NSPoint point; |
| point.x = contentLocal.x; |
| point.y = [self frame].size.height - contentLocal.y; // Flip y. |
| return point; |
| } |
| |
| - (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event { |
| if ([event type] == NSKeyDown || [event type] == NSKeyUp) { |
| NSString* s = [event characters]; |
| if ([s length] > 0) |
| keyEvent.character = [s characterAtIndex:0]; |
| |
| s = [event charactersIgnoringModifiers]; |
| if ([s length] > 0) |
| keyEvent.unmodified_character = [s characterAtIndex:0]; |
| } |
| |
| if ([event type] == NSFlagsChanged) { |
| keyEvent.character = 0; |
| keyEvent.unmodified_character = 0; |
| } |
| |
| keyEvent.native_key_code = [event keyCode]; |
| |
| keyEvent.modifiers = [self getModifiersForEvent:event]; |
| } |
| |
| - (NSTextInputContext*)inputContext { |
| if (!text_input_context_osr_mac_) { |
| text_input_client_ = |
| [[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]]; |
| text_input_context_osr_mac_ = |
| [[NSTextInputContext alloc] initWithClient:text_input_client_]; |
| #if !__has_feature(objc_arc) |
| [text_input_client_ retain]; |
| [text_input_context_osr_mac_ retain]; |
| #endif // !__has_feature(objc_arc) |
| } |
| |
| return text_input_context_osr_mac_; |
| } |
| |
| - (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event { |
| const float device_scale_factor = [self getDeviceScaleFactor]; |
| |
| // |point| is in OS X view coordinates. |
| NSPoint point = [self getClickPointForEvent:event]; |
| |
| // Convert to device coordinates. |
| point = [self convertPointToBackingInternal:point]; |
| |
| int device_x = point.x; |
| int device_y = point.y; |
| if ([self isOverPopupWidgetX:device_x andY:device_y]) |
| [self applyPopupOffsetToX:device_x andY:device_y]; |
| |
| // Convert to browser view coordinates. |
| mouseEvent.x = client::DeviceToLogical(device_x, device_scale_factor); |
| mouseEvent.y = client::DeviceToLogical(device_y, device_scale_factor); |
| |
| mouseEvent.modifiers = [self getModifiersForEvent:event]; |
| } |
| |
| - (void)getMouseEvent:(CefMouseEvent&)mouseEvent |
| forDragInfo:(id<NSDraggingInfo>)info { |
| const float device_scale_factor = [self getDeviceScaleFactor]; |
| |
| // |point| is in OS X view coordinates. |
| NSPoint windowPoint = [info draggingLocation]; |
| NSPoint point = [self flipWindowPointToView:windowPoint]; |
| |
| // Convert to device coordinates. |
| point = [self convertPointToBackingInternal:point]; |
| |
| // Convert to browser view coordinates. |
| mouseEvent.x = client::DeviceToLogical(point.x, device_scale_factor); |
| mouseEvent.y = client::DeviceToLogical(point.y, device_scale_factor); |
| |
| mouseEvent.modifiers = [NSEvent modifierFlags]; |
| } |
| |
| - (int)getModifiersForEvent:(NSEvent*)event { |
| int modifiers = 0; |
| |
| if ([event modifierFlags] & NSControlKeyMask) |
| modifiers |= EVENTFLAG_CONTROL_DOWN; |
| if ([event modifierFlags] & NSShiftKeyMask) |
| modifiers |= EVENTFLAG_SHIFT_DOWN; |
| if ([event modifierFlags] & NSAlternateKeyMask) |
| modifiers |= EVENTFLAG_ALT_DOWN; |
| if ([event modifierFlags] & NSCommandKeyMask) |
| modifiers |= EVENTFLAG_COMMAND_DOWN; |
| if ([event modifierFlags] & NSAlphaShiftKeyMask) |
| modifiers |= EVENTFLAG_CAPS_LOCK_ON; |
| |
| if ([event type] == NSKeyUp || [event type] == NSKeyDown || |
| [event type] == NSFlagsChanged) { |
| // Only perform this check for key events |
| if ([self isKeyPadEvent:event]) |
| modifiers |= EVENTFLAG_IS_KEY_PAD; |
| } |
| |
| // OS X does not have a modifier for NumLock, so I'm not entirely sure how to |
| // set EVENTFLAG_NUM_LOCK_ON; |
| // |
| // There is no EVENTFLAG for the function key either. |
| |
| // Mouse buttons |
| switch ([event type]) { |
| case NSLeftMouseDragged: |
| case NSLeftMouseDown: |
| case NSLeftMouseUp: |
| modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; |
| break; |
| case NSRightMouseDragged: |
| case NSRightMouseDown: |
| case NSRightMouseUp: |
| modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; |
| break; |
| case NSOtherMouseDragged: |
| case NSOtherMouseDown: |
| case NSOtherMouseUp: |
| modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; |
| break; |
| default: |
| break; |
| } |
| |
| return modifiers; |
| } |
| |
| - (BOOL)isKeyUpEvent:(NSEvent*)event { |
| if ([event type] != NSFlagsChanged) |
| return [event type] == NSKeyUp; |
| |
| // FIXME: This logic fails if the user presses both Shift keys at once, for |
| // example: we treat releasing one of them as keyDown. |
| switch ([event keyCode]) { |
| case 54: // Right Command |
| case 55: // Left Command |
| return ([event modifierFlags] & NSCommandKeyMask) == 0; |
| |
| case 57: // Capslock |
| return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0; |
| |
| case 56: // Left Shift |
| case 60: // Right Shift |
| return ([event modifierFlags] & NSShiftKeyMask) == 0; |
| |
| case 58: // Left Alt |
| case 61: // Right Alt |
| return ([event modifierFlags] & NSAlternateKeyMask) == 0; |
| |
| case 59: // Left Ctrl |
| case 62: // Right Ctrl |
| return ([event modifierFlags] & NSControlKeyMask) == 0; |
| |
| case 63: // Function |
| return ([event modifierFlags] & NSFunctionKeyMask) == 0; |
| } |
| return false; |
| } |
| |
| - (BOOL)isKeyPadEvent:(NSEvent*)event { |
| if ([event modifierFlags] & NSNumericPadKeyMask) |
| return true; |
| |
| switch ([event keyCode]) { |
| case 71: // Clear |
| case 81: // = |
| case 75: // / |
| case 67: // * |
| case 78: // - |
| case 69: // + |
| case 76: // Enter |
| case 65: // . |
| case 82: // 0 |
| case 83: // 1 |
| case 84: // 2 |
| case 85: // 3 |
| case 86: // 4 |
| case 87: // 5 |
| case 88: // 6 |
| case 89: // 7 |
| case 91: // 8 |
| case 92: // 9 |
| return true; |
| } |
| |
| return false; |
| } |
| |
| - (void)windowDidChangeBackingProperties:(NSNotification*)notification { |
| // This delegate method is only called on 10.7 and later, so don't worry about |
| // other backing changes calling it on 10.6 or earlier |
| [self resetDeviceScaleFactor]; |
| } |
| |
| - (void)drawRect:(NSRect)dirtyRect { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if ([self inLiveResize] || !browser.get()) { |
| // Fill with the background color. |
| const cef_color_t background_color = |
| client::MainContext::Get()->GetBackgroundColor(); |
| NSColor* color = [NSColor |
| colorWithCalibratedRed:float(CefColorGetR(background_color)) / 255.0f |
| green:float(CefColorGetG(background_color)) / 255.0f |
| blue:float(CefColorGetB(background_color)) / 255.0f |
| alpha:1.f]; |
| [color setFill]; |
| NSRectFill(dirtyRect); |
| } |
| |
| // The Invalidate below fixes flicker when resizing. |
| if ([self inLiveResize] && browser.get()) |
| browser->GetHost()->Invalidate(PET_VIEW); |
| } |
| |
| // Drag and drop |
| |
| - (BOOL)startDragging:(CefRefPtr<CefDragData>)drag_data |
| allowedOps:(NSDragOperation)ops |
| point:(NSPoint)position { |
| DCHECK(!pasteboard_); |
| DCHECK(!fileUTI_); |
| DCHECK(!current_drag_data_.get()); |
| |
| [self resetDragDrop]; |
| |
| current_allowed_ops_ = ops; |
| current_drag_data_ = drag_data; |
| |
| [self fillPasteboard]; |
| |
| NSEvent* currentEvent = [[NSApplication sharedApplication] currentEvent]; |
| NSWindow* window = [self window]; |
| NSTimeInterval eventTime = [currentEvent timestamp]; |
| |
| NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged |
| location:position |
| modifierFlags:NSLeftMouseDraggedMask |
| timestamp:eventTime |
| windowNumber:[window windowNumber] |
| context:nil |
| eventNumber:0 |
| clickCount:1 |
| pressure:1.0]; |
| |
| // TODO(cef): Pass a non-nil value to dragImage (see issue #1715). For now |
| // work around the "callee requires a non-null argument" error that occurs |
| // when building with the 10.11 SDK. |
| id nilArg = nil; |
| [window dragImage:nilArg |
| at:position |
| offset:NSZeroSize |
| event:dragEvent |
| pasteboard:pasteboard_ |
| source:self |
| slideBack:YES]; |
| return YES; |
| } |
| |
| - (void)setCurrentDragOp:(NSDragOperation)op { |
| current_drag_op_ = op; |
| } |
| |
| // NSDraggingSource Protocol |
| |
| - (NSDragOperation)draggingSession:(NSDraggingSession*)session |
| sourceOperationMaskForDraggingContext:(NSDraggingContext)context { |
| switch (context) { |
| case NSDraggingContextOutsideApplication: |
| return current_allowed_ops_; |
| |
| case NSDraggingContextWithinApplication: |
| default: |
| return current_allowed_ops_; |
| } |
| } |
| |
| - (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest { |
| if (![dropDest isFileURL]) |
| return nil; |
| |
| if (!current_drag_data_) |
| return nil; |
| |
| size_t expected_size = current_drag_data_->GetFileContents(NULL); |
| if (expected_size == 0) |
| return nil; |
| |
| std::string path = [[dropDest path] UTF8String]; |
| path.append("/"); |
| path.append(current_drag_data_->GetFileName().ToString()); |
| |
| CefRefPtr<CefStreamWriter> writer = CefStreamWriter::CreateForFile(path); |
| if (!writer) |
| return nil; |
| |
| if (current_drag_data_->GetFileContents(writer) != expected_size) |
| return nil; |
| |
| return @[ [NSString stringWithUTF8String:path.c_str()] ]; |
| } |
| |
| - (void)draggedImage:(NSImage*)anImage |
| endedAt:(NSPoint)screenPoint |
| operation:(NSDragOperation)operation { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return; |
| |
| if (operation == (NSDragOperationMove | NSDragOperationCopy)) |
| operation &= ~NSDragOperationMove; |
| |
| NSPoint windowPoint = [[self window] convertScreenToBase:screenPoint]; |
| NSPoint pt = [self flipWindowPointToView:windowPoint]; |
| CefRenderHandler::DragOperation op = |
| static_cast<CefRenderHandler::DragOperation>(operation); |
| browser->GetHost()->DragSourceEndedAt(pt.x, pt.y, op); |
| browser->GetHost()->DragSourceSystemDragEnded(); |
| [self resetDragDrop]; |
| } |
| |
| // NSDraggingDestination Protocol |
| |
| - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return NSDragOperationNone; |
| |
| CefRefPtr<CefDragData> drag_data; |
| if (!current_drag_data_) { |
| drag_data = CefDragData::Create(); |
| [self populateDropData:drag_data fromPasteboard:[info draggingPasteboard]]; |
| } else { |
| drag_data = current_drag_data_->Clone(); |
| drag_data->ResetFileContents(); |
| } |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forDragInfo:info]; |
| |
| NSDragOperation mask = [info draggingSourceOperationMask]; |
| CefBrowserHost::DragOperationsMask allowed_ops = |
| static_cast<CefBrowserHost::DragOperationsMask>(mask); |
| |
| browser->GetHost()->DragTargetDragEnter(drag_data, mouseEvent, allowed_ops); |
| browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops); |
| |
| current_drag_op_ = NSDragOperationCopy; |
| return current_drag_op_; |
| } |
| |
| - (void)draggingExited:(id<NSDraggingInfo>)sender { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser.get()) |
| browser->GetHost()->DragTargetDragLeave(); |
| } |
| |
| - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info { |
| return YES; |
| } |
| |
| - (BOOL)performDragOperation:(id<NSDraggingInfo>)info { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return NO; |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forDragInfo:info]; |
| |
| browser->GetHost()->DragTargetDrop(mouseEvent); |
| |
| return YES; |
| } |
| |
| - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info { |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (!browser.get()) |
| return NSDragOperationNone; |
| |
| CefMouseEvent mouseEvent; |
| [self getMouseEvent:mouseEvent forDragInfo:info]; |
| |
| NSDragOperation mask = [info draggingSourceOperationMask]; |
| CefBrowserHost::DragOperationsMask allowed_ops = |
| static_cast<CefBrowserHost::DragOperationsMask>(mask); |
| |
| browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops); |
| |
| return current_drag_op_; |
| } |
| |
| // NSPasteboardOwner Protocol |
| |
| - (void)pasteboard:(NSPasteboard*)pboard provideDataForType:(NSString*)type { |
| if (!current_drag_data_) { |
| return; |
| } |
| |
| // URL. |
| if ([type isEqualToString:NSURLPboardType]) { |
| DCHECK(current_drag_data_->IsLink()); |
| NSString* strUrl = |
| [NSString stringWithUTF8String:current_drag_data_->GetLinkURL() |
| .ToString() |
| .c_str()]; |
| NSURL* url = [NSURL URLWithString:strUrl]; |
| [url writeToPasteboard:pboard]; |
| // URL title. |
| } else if ([type isEqualToString:kNSURLTitlePboardType]) { |
| NSString* strTitle = |
| [NSString stringWithUTF8String:current_drag_data_->GetLinkTitle() |
| .ToString() |
| .c_str()]; |
| [pboard setString:strTitle forType:kNSURLTitlePboardType]; |
| |
| // File contents. |
| } else if ([type isEqualToString:fileUTI_]) { |
| size_t size = current_drag_data_->GetFileContents(NULL); |
| DCHECK_GT(size, 0U); |
| CefRefPtr<client::BytesWriteHandler> handler = |
| new client::BytesWriteHandler(size); |
| CefRefPtr<CefStreamWriter> writer = |
| CefStreamWriter::CreateForHandler(handler.get()); |
| current_drag_data_->GetFileContents(writer); |
| DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(size)); |
| |
| [pboard setData:[NSData dataWithBytes:handler->GetData() |
| length:handler->GetDataSize()] |
| forType:fileUTI_]; |
| |
| // Plain text. |
| } else if ([type isEqualToString:NSStringPboardType]) { |
| NSString* strTitle = |
| [NSString stringWithUTF8String:current_drag_data_->GetFragmentText() |
| .ToString() |
| .c_str()]; |
| [pboard setString:strTitle forType:NSStringPboardType]; |
| |
| } else if ([type isEqualToString:kCEFDragDummyPboardType]) { |
| // The dummy type _was_ promised and someone decided to call the bluff. |
| [pboard setData:[NSData data] forType:kCEFDragDummyPboardType]; |
| } |
| } |
| |
| // NSAccessibility Protocol implementation. |
| - (BOOL)accessibilityIsIgnored { |
| if (!accessibility_helper_) |
| return YES; |
| else |
| return NO; |
| } |
| |
| - (id)accessibilityAttributeValue:(NSString*)attribute { |
| if (!accessibility_helper_) |
| return [super accessibilityAttributeValue:attribute]; |
| if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { |
| return NSAccessibilityGroupRole; |
| } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { |
| client::OsrAXNode* node = accessibility_helper_->GetRootNode(); |
| std::string desc = node ? node->AxDescription() : ""; |
| return [NSString stringWithUTF8String:desc.c_str()]; |
| } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { |
| client::OsrAXNode* node = accessibility_helper_->GetRootNode(); |
| std::string desc = node ? node->AxValue() : ""; |
| return [NSString stringWithUTF8String:desc.c_str()]; |
| } else if ([attribute |
| isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { |
| return NSAccessibilityRoleDescriptionForUIElement(self); |
| } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { |
| client::OsrAXNode* node = accessibility_helper_->GetRootNode(); |
| // Add Root as first Kid |
| NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1]; |
| NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( |
| node->GetNativeAccessibleObject(NULL)); |
| [kids addObject:child]; |
| return NSAccessibilityUnignoredChildren(kids); |
| } else { |
| return [super accessibilityAttributeValue:attribute]; |
| } |
| } |
| |
| - (id)accessibilityFocusedUIElement { |
| if (accessibility_helper_) { |
| client::OsrAXNode* node = accessibility_helper_->GetFocusedNode(); |
| return node ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( |
| node->GetNativeAccessibleObject(NULL)) |
| : nil; |
| } |
| return nil; |
| } |
| |
| // Utility methods. |
| - (void)resetDragDrop { |
| current_drag_op_ = NSDragOperationNone; |
| current_allowed_ops_ = NSDragOperationNone; |
| current_drag_data_ = NULL; |
| if (fileUTI_) { |
| #if !__has_feature(objc_arc) |
| [fileUTI_ release]; |
| #endif // !__has_feature(objc_arc) |
| fileUTI_ = nil; |
| } |
| if (pasteboard_) { |
| #if !__has_feature(objc_arc) |
| [pasteboard_ release]; |
| #endif // !__has_feature(objc_arc) |
| pasteboard_ = nil; |
| } |
| } |
| |
| - (void)fillPasteboard { |
| DCHECK(!pasteboard_); |
| pasteboard_ = [NSPasteboard pasteboardWithName:NSDragPboard]; |
| #if !__has_feature(objc_arc) |
| [pasteboard_ retain]; |
| #endif // !__has_feature(objc_arc) |
| |
| [pasteboard_ declareTypes:@[ kCEFDragDummyPboardType ] owner:self]; |
| |
| // URL (and title). |
| if (current_drag_data_->IsLink()) { |
| [pasteboard_ addTypes:@[ NSURLPboardType, kNSURLTitlePboardType ] |
| owner:self]; |
| } |
| |
| // MIME type. |
| CefString mimeType; |
| size_t contents_size = current_drag_data_->GetFileContents(NULL); |
| CefString download_metadata = current_drag_data_->GetLinkMetadata(); |
| CefString file_name = current_drag_data_->GetFileName(); |
| |
| // File. |
| if (contents_size > 0) { |
| std::string file_name = current_drag_data_->GetFileName().ToString(); |
| size_t sep = file_name.find_last_of("."); |
| CefString extension = file_name.substr(sep + 1); |
| |
| mimeType = CefGetMimeType(extension); |
| |
| if (!mimeType.empty()) { |
| CFStringRef mimeTypeCF; |
| mimeTypeCF = CFStringCreateWithCString(kCFAllocatorDefault, |
| mimeType.ToString().c_str(), |
| kCFStringEncodingUTF8); |
| fileUTI_ = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag( |
| kUTTagClassMIMEType, mimeTypeCF, NULL); |
| CFRelease(mimeTypeCF); |
| // File (HFS) promise. |
| NSArray* fileUTIList = @[ fileUTI_ ]; |
| [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:self]; |
| [pasteboard_ setPropertyList:fileUTIList |
| forType:NSFilesPromisePboardType]; |
| |
| [pasteboard_ addTypes:fileUTIList owner:self]; |
| } |
| } |
| |
| // Plain text. |
| if (!current_drag_data_->GetFragmentText().empty()) { |
| [pasteboard_ addTypes:@[ NSStringPboardType ] owner:self]; |
| } |
| } |
| |
| - (void)populateDropData:(CefRefPtr<CefDragData>)data |
| fromPasteboard:(NSPasteboard*)pboard { |
| DCHECK(data); |
| DCHECK(pboard); |
| DCHECK(data && !data->IsReadOnly()); |
| NSArray* types = [pboard types]; |
| |
| // Get plain text. |
| if ([types containsObject:NSStringPboardType]) { |
| data->SetFragmentText( |
| [[pboard stringForType:NSStringPboardType] UTF8String]); |
| } |
| |
| // Get files. |
| if ([types containsObject:NSFilenamesPboardType]) { |
| NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; |
| if ([files isKindOfClass:[NSArray class]] && [files count]) { |
| for (NSUInteger i = 0; i < [files count]; i++) { |
| NSString* filename = [files objectAtIndex:i]; |
| BOOL exists = |
| [[NSFileManager defaultManager] fileExistsAtPath:filename]; |
| if (exists) { |
| data->AddFile([filename UTF8String], CefString()); |
| } |
| } |
| } |
| } |
| } |
| |
| - (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint { |
| NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil]; |
| NSRect viewFrame = [self frame]; |
| viewPoint.y = viewFrame.size.height - viewPoint.y; |
| return viewPoint; |
| } |
| |
| - (void)resetDeviceScaleFactor { |
| float device_scale_factor = 1.0f; |
| NSWindow* window = [self window]; |
| if (window) |
| device_scale_factor = [window backingScaleFactor]; |
| [self setDeviceScaleFactor:device_scale_factor]; |
| } |
| |
| - (void)setDeviceScaleFactor:(float)device_scale_factor { |
| if (device_scale_factor == device_scale_factor_) |
| return; |
| |
| // Apply some sanity checks. |
| if (device_scale_factor < 1.0f || device_scale_factor > 4.0f) |
| return; |
| |
| device_scale_factor_ = device_scale_factor; |
| |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser) { |
| browser->GetHost()->NotifyScreenInfoChanged(); |
| browser->GetHost()->WasResized(); |
| } |
| } |
| |
| - (float)getDeviceScaleFactor { |
| return device_scale_factor_; |
| } |
| |
| - (void)viewDidChangeBackingProperties { |
| const CGFloat device_scale_factor = [self getDeviceScaleFactor]; |
| |
| if (device_scale_factor == device_scale_factor_) |
| return; |
| |
| CefRefPtr<CefBrowser> browser = [self getBrowser]; |
| if (browser) { |
| browser->GetHost()->NotifyScreenInfoChanged(); |
| browser->GetHost()->WasResized(); |
| } |
| } |
| |
| - (bool)isOverPopupWidgetX:(int)x andY:(int)y { |
| CefRect rc = renderer_->popup_rect(); |
| int popup_right = rc.x + rc.width; |
| int popup_bottom = rc.y + rc.height; |
| return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom); |
| } |
| |
| - (int)getPopupXOffset { |
| return renderer_->original_popup_rect().x - renderer_->popup_rect().x; |
| } |
| |
| - (int)getPopupYOffset { |
| return renderer_->original_popup_rect().y - renderer_->popup_rect().y; |
| } |
| |
| - (void)applyPopupOffsetToX:(int&)x andY:(int&)y { |
| if ([self isOverPopupWidgetX:x andY:y]) { |
| x += [self getPopupXOffset]; |
| y += [self getPopupYOffset]; |
| } |
| } |
| |
| // Convert from scaled coordinates to view coordinates. |
| - (NSPoint)convertPointFromBackingInternal:(NSPoint)aPoint { |
| return [self convertPointFromBacking:aPoint]; |
| } |
| |
| // Convert from view coordinates to scaled coordinates. |
| - (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint { |
| return [self convertPointToBacking:aPoint]; |
| } |
| |
| // Convert from scaled coordinates to view coordinates. |
| - (NSRect)convertRectFromBackingInternal:(NSRect)aRect { |
| return [self convertRectFromBacking:aRect]; |
| } |
| |
| // Convert from view coordinates to scaled coordinates. |
| - (NSRect)convertRectToBackingInternal:(NSRect)aRect { |
| return [self convertRectToBacking:aRect]; |
| } |
| |
| - (void)ChangeCompositionRange:(CefRange)range |
| character_bounds:(const CefRenderHandler::RectList&)bounds { |
| if (text_input_client_) |
| [text_input_client_ ChangeCompositionRange:range character_bounds:bounds]; |
| } |
| |
| - (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value { |
| if (!accessibility_helper_) { |
| accessibility_helper_ = |
| new client::OsrAccessibilityHelper(value, [self getBrowser]); |
| } else { |
| accessibility_helper_->UpdateAccessibilityTree(value); |
| } |
| |
| if (accessibility_helper_) { |
| NSAccessibilityPostNotification(self, |
| NSAccessibilityValueChangedNotification); |
| } |
| return; |
| } |
| |
| - (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value { |
| if (accessibility_helper_) { |
| accessibility_helper_->UpdateAccessibilityLocation(value); |
| } |
| |
| if (accessibility_helper_) { |
| NSAccessibilityPostNotification(self, |
| NSAccessibilityValueChangedNotification); |
| } |
| return; |
| } |
| @end |
| |
| namespace client { |
| |
| class BrowserWindowOsrMacImpl { |
| public: |
| BrowserWindowOsrMacImpl(BrowserWindow::Delegate* delegate, |
| const std::string& startup_url, |
| const OsrRendererSettings& settings, |
| BrowserWindowOsrMac& browser_window); |
| ~BrowserWindowOsrMacImpl(); |
| |
| // BrowserWindow methods. |
| void CreateBrowser(ClientWindowHandle parent_handle, |
| const CefRect& rect, |
| const CefBrowserSettings& settings, |
| CefRefPtr<CefDictionaryValue> extra_info, |
| CefRefPtr<CefRequestContext> request_context); |
| void GetPopupConfig(CefWindowHandle temp_handle, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& client, |
| CefBrowserSettings& settings); |
| void ShowPopup(ClientWindowHandle parent_handle, |
| int x, |
| int y, |
| size_t width, |
| size_t height); |
| void Show(); |
| void Hide(); |
| void SetBounds(int x, int y, size_t width, size_t height); |
| void SetFocus(bool focus); |
| void SetDeviceScaleFactor(float device_scale_factor); |
| float GetDeviceScaleFactor() const; |
| ClientWindowHandle GetWindowHandle() const; |
| |
| // ClientHandlerOsr::OsrDelegate methods. |
| void OnAfterCreated(CefRefPtr<CefBrowser> browser); |
| void OnBeforeClose(CefRefPtr<CefBrowser> browser); |
| bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect); |
| void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect); |
| bool GetScreenPoint(CefRefPtr<CefBrowser> browser, |
| int viewX, |
| int viewY, |
| int& screenX, |
| int& screenY); |
| bool GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo& screen_info); |
| void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show); |
| void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect); |
| void OnPaint(CefRefPtr<CefBrowser> browser, |
| CefRenderHandler::PaintElementType type, |
| const CefRenderHandler::RectList& dirtyRects, |
| const void* buffer, |
| int width, |
| int height); |
| void OnCursorChange(CefRefPtr<CefBrowser> browser, |
| CefCursorHandle cursor, |
| CefRenderHandler::CursorType type, |
| const CefCursorInfo& custom_cursor_info); |
| bool StartDragging(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefDragData> drag_data, |
| CefRenderHandler::DragOperationsMask allowed_ops, |
| int x, |
| int y); |
| void UpdateDragCursor(CefRefPtr<CefBrowser> browser, |
| CefRenderHandler::DragOperation operation); |
| void OnImeCompositionRangeChanged( |
| CefRefPtr<CefBrowser> browser, |
| const CefRange& selection_range, |
| const CefRenderHandler::RectList& character_bounds); |
| |
| void UpdateAccessibilityTree(CefRefPtr<CefValue> value); |
| void UpdateAccessibilityLocation(CefRefPtr<CefValue> value); |
| |
| private: |
| // Create the NSView. |
| void Create(ClientWindowHandle parent_handle, const CefRect& rect); |
| |
| BrowserWindowOsrMac& browser_window_; |
| // The below members will only be accessed on the main thread which should be |
| // the same as the CEF UI thread. |
| OsrRenderer renderer_; |
| BrowserOpenGLView* native_browser_view_; |
| bool hidden_; |
| bool painting_popup_; |
| }; |
| |
| BrowserWindowOsrMacImpl::BrowserWindowOsrMacImpl( |
| BrowserWindow::Delegate* delegate, |
| const std::string& startup_url, |
| const OsrRendererSettings& settings, |
| BrowserWindowOsrMac& browser_window) |
| : browser_window_(browser_window), |
| renderer_(settings), |
| native_browser_view_(nil), |
| hidden_(false), |
| painting_popup_(false) {} |
| |
| BrowserWindowOsrMacImpl::~BrowserWindowOsrMacImpl() { |
| if (native_browser_view_) { |
| // Disassociate the view with |this|. |
| [native_browser_view_ detach]; |
| } |
| } |
| |
| void BrowserWindowOsrMacImpl::CreateBrowser( |
| ClientWindowHandle parent_handle, |
| const CefRect& rect, |
| const CefBrowserSettings& settings, |
| CefRefPtr<CefDictionaryValue> extra_info, |
| CefRefPtr<CefRequestContext> request_context) { |
| REQUIRE_MAIN_THREAD(); |
| |
| // Create the native NSView. |
| Create(parent_handle, rect); |
| |
| CefWindowInfo window_info; |
| window_info.SetAsWindowless( |
| CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_)); |
| |
| // Create the browser asynchronously. |
| CefBrowserHost::CreateBrowser(window_info, browser_window_.client_handler_, |
| browser_window_.client_handler_->startup_url(), |
| settings, extra_info, request_context); |
| } |
| |
| void BrowserWindowOsrMacImpl::GetPopupConfig(CefWindowHandle temp_handle, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& client, |
| CefBrowserSettings& settings) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| windowInfo.SetAsWindowless(temp_handle); |
| client = browser_window_.client_handler_; |
| } |
| |
| void BrowserWindowOsrMacImpl::ShowPopup(ClientWindowHandle parent_handle, |
| int x, |
| int y, |
| size_t width, |
| size_t height) { |
| REQUIRE_MAIN_THREAD(); |
| DCHECK(browser_window_.browser_.get()); |
| |
| // Create the native NSView. |
| Create(parent_handle, |
| CefRect(x, y, static_cast<int>(width), static_cast<int>(height))); |
| |
| // Send resize notification so the compositor is assigned the correct |
| // viewport size and begins rendering. |
| browser_window_.browser_->GetHost()->WasResized(); |
| |
| Show(); |
| } |
| |
| void BrowserWindowOsrMacImpl::Show() { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (hidden_) { |
| // Set the browser as visible. |
| browser_window_.browser_->GetHost()->WasHidden(false); |
| hidden_ = false; |
| } |
| |
| // Give focus to the browser. |
| browser_window_.browser_->GetHost()->SendFocusEvent(true); |
| } |
| |
| void BrowserWindowOsrMacImpl::Hide() { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!browser_window_.browser_.get()) |
| return; |
| |
| // Remove focus from the browser. |
| browser_window_.browser_->GetHost()->SendFocusEvent(false); |
| |
| if (!hidden_) { |
| // Set the browser as hidden. |
| browser_window_.browser_->GetHost()->WasHidden(true); |
| hidden_ = true; |
| } |
| } |
| |
| void BrowserWindowOsrMacImpl::SetBounds(int x, |
| int y, |
| size_t width, |
| size_t height) { |
| REQUIRE_MAIN_THREAD(); |
| // Nothing to do here. GTK will take care of positioning in the container. |
| } |
| |
| void BrowserWindowOsrMacImpl::SetFocus(bool focus) { |
| REQUIRE_MAIN_THREAD(); |
| if (native_browser_view_) |
| [native_browser_view_.window makeFirstResponder:native_browser_view_]; |
| } |
| |
| void BrowserWindowOsrMacImpl::SetDeviceScaleFactor(float device_scale_factor) { |
| REQUIRE_MAIN_THREAD(); |
| if (native_browser_view_) |
| [native_browser_view_ setDeviceScaleFactor:device_scale_factor]; |
| } |
| |
| float BrowserWindowOsrMacImpl::GetDeviceScaleFactor() const { |
| REQUIRE_MAIN_THREAD(); |
| if (native_browser_view_) |
| return [native_browser_view_ getDeviceScaleFactor]; |
| return 1.0f; |
| } |
| |
| ClientWindowHandle BrowserWindowOsrMacImpl::GetWindowHandle() const { |
| REQUIRE_MAIN_THREAD(); |
| return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_); |
| } |
| |
| void BrowserWindowOsrMacImpl::OnAfterCreated(CefRefPtr<CefBrowser> browser) { |
| CEF_REQUIRE_UI_THREAD(); |
| } |
| |
| void BrowserWindowOsrMacImpl::OnBeforeClose(CefRefPtr<CefBrowser> browser) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| // Detach |this| from the ClientHandlerOsr. |
| static_cast<ClientHandlerOsr*>(browser_window_.client_handler_.get()) |
| ->DetachOsrDelegate(); |
| } |
| |
| bool BrowserWindowOsrMacImpl::GetRootScreenRect(CefRefPtr<CefBrowser> browser, |
| CefRect& rect) { |
| CEF_REQUIRE_UI_THREAD(); |
| return false; |
| } |
| |
| void BrowserWindowOsrMacImpl::GetViewRect(CefRefPtr<CefBrowser> browser, |
| CefRect& rect) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| rect.x = rect.y = 0; |
| |
| if (!native_browser_view_) { |
| // Never return an empty rectangle. |
| rect.width = rect.height = 1; |
| return; |
| } |
| |
| const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; |
| |
| // |bounds| is in OS X view coordinates. |
| NSRect bounds = native_browser_view_.bounds; |
| |
| // Convert to device coordinates. |
| bounds = [native_browser_view_ convertRectToBackingInternal:bounds]; |
| |
| // Convert to browser view coordinates. |
| rect.width = DeviceToLogical(bounds.size.width, device_scale_factor); |
| if (rect.width == 0) |
| rect.width = 1; |
| rect.height = DeviceToLogical(bounds.size.height, device_scale_factor); |
| if (rect.height == 0) |
| rect.height = 1; |
| } |
| |
| bool BrowserWindowOsrMacImpl::GetScreenPoint(CefRefPtr<CefBrowser> browser, |
| int viewX, |
| int viewY, |
| int& screenX, |
| int& screenY) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!native_browser_view_) |
| return false; |
| |
| const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; |
| |
| // (viewX, viewX) is in browser view coordinates. |
| // Convert to device coordinates. |
| NSPoint view_pt = NSMakePoint(LogicalToDevice(viewX, device_scale_factor), |
| LogicalToDevice(viewY, device_scale_factor)); |
| |
| // Convert to OS X view coordinates. |
| view_pt = [native_browser_view_ convertPointFromBackingInternal:view_pt]; |
| |
| // Reverse the Y component. |
| const NSRect bounds = native_browser_view_.bounds; |
| view_pt.y = bounds.size.height - view_pt.y; |
| |
| // Convert to screen coordinates. |
| NSPoint window_pt = [native_browser_view_ convertPoint:view_pt toView:nil]; |
| NSPoint screen_pt = |
| ConvertPointFromWindowToScreen(native_browser_view_.window, window_pt); |
| |
| screenX = screen_pt.x; |
| screenY = screen_pt.y; |
| return true; |
| } |
| |
| bool BrowserWindowOsrMacImpl::GetScreenInfo(CefRefPtr<CefBrowser> browser, |
| CefScreenInfo& screen_info) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!native_browser_view_) |
| return false; |
| |
| CefRect view_rect; |
| GetViewRect(browser, view_rect); |
| |
| screen_info.device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; |
| |
| // The screen info rectangles are used by the renderer to create and position |
| // popups. Keep popups inside the view rectangle. |
| screen_info.rect = view_rect; |
| screen_info.available_rect = view_rect; |
| |
| return true; |
| } |
| |
| void BrowserWindowOsrMacImpl::OnPopupShow(CefRefPtr<CefBrowser> browser, |
| bool show) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!native_browser_view_) |
| return; |
| |
| if (!show) { |
| renderer_.ClearPopupRects(); |
| browser->GetHost()->Invalidate(PET_VIEW); |
| } |
| renderer_.OnPopupShow(browser, show); |
| } |
| |
| void BrowserWindowOsrMacImpl::OnPopupSize(CefRefPtr<CefBrowser> browser, |
| const CefRect& rect) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!native_browser_view_) |
| return; |
| |
| const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; |
| |
| // |rect| is in browser view coordinates. Convert to device coordinates. |
| CefRect device_rect = LogicalToDevice(rect, device_scale_factor); |
| |
| renderer_.OnPopupSize(browser, device_rect); |
| } |
| |
| void BrowserWindowOsrMacImpl::OnPaint( |
| CefRefPtr<CefBrowser> browser, |
| CefRenderHandler::PaintElementType type, |
| const CefRenderHandler::RectList& dirtyRects, |
| const void* buffer, |
| int width, |
| int height) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!native_browser_view_) |
| return; |
| |
| if (width <= 2 && height <= 2) { |
| // Ignore really small buffer sizes while the widget is starting up. |
| return; |
| } |
| |
| if (painting_popup_) { |
| renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); |
| return; |
| } |
| |
| ScopedGLContext scoped_gl_context(native_browser_view_, true); |
| |
| renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); |
| if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) { |
| painting_popup_ = true; |
| browser->GetHost()->Invalidate(PET_POPUP); |
| painting_popup_ = false; |
| } |
| renderer_.Render(); |
| } |
| |
| void BrowserWindowOsrMacImpl::OnCursorChange( |
| CefRefPtr<CefBrowser> browser, |
| CefCursorHandle cursor, |
| CefRenderHandler::CursorType type, |
| const CefCursorInfo& custom_cursor_info) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| [CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(cursor) set]; |
| } |
| |
| bool BrowserWindowOsrMacImpl::StartDragging( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefDragData> drag_data, |
| CefRenderHandler::DragOperationsMask allowed_ops, |
| int x, |
| int y) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!native_browser_view_) |
| return false; |
| |
| static float device_scale_factor = |
| [native_browser_view_ getDeviceScaleFactor]; |
| |
| // |point| is in browser view coordinates. |
| NSPoint point = NSMakePoint(x, y); |
| |
| // Convert to device coordinates. |
| point.x = LogicalToDevice(point.x, device_scale_factor); |
| point.y = LogicalToDevice(point.y, device_scale_factor); |
| |
| // Convert to OS X view coordinates. |
| point = [native_browser_view_ convertPointFromBackingInternal:point]; |
| |
| return [native_browser_view_ |
| startDragging:drag_data |
| allowedOps:static_cast<NSDragOperation>(allowed_ops) |
| point:point]; |
| } |
| |
| void BrowserWindowOsrMacImpl::UpdateDragCursor( |
| CefRefPtr<CefBrowser> browser, |
| CefRenderHandler::DragOperation operation) { |
| CEF_REQUIRE_UI_THREAD(); |
| REQUIRE_MAIN_THREAD(); |
| |
| if (native_browser_view_) |
| [native_browser_view_ setCurrentDragOp:operation]; |
| } |
| |
| void BrowserWindowOsrMacImpl::OnImeCompositionRangeChanged( |
| CefRefPtr<CefBrowser> browser, |
| const CefRange& selection_range, |
| const CefRenderHandler::RectList& bounds) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (native_browser_view_) { |
| [native_browser_view_ ChangeCompositionRange:selection_range |
| character_bounds:bounds]; |
| } |
| } |
| |
| void BrowserWindowOsrMacImpl::UpdateAccessibilityTree( |
| CefRefPtr<CefValue> value) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (native_browser_view_) { |
| [native_browser_view_ UpdateAccessibilityTree:value]; |
| } |
| } |
| |
| void BrowserWindowOsrMacImpl::UpdateAccessibilityLocation( |
| CefRefPtr<CefValue> value) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (native_browser_view_) { |
| [native_browser_view_ UpdateAccessibilityLocation:value]; |
| } |
| } |
| |
| void BrowserWindowOsrMacImpl::Create(ClientWindowHandle parent_handle, |
| const CefRect& rect) { |
| REQUIRE_MAIN_THREAD(); |
| DCHECK(!native_browser_view_); |
| |
| NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); |
| native_browser_view_ = |
| [[BrowserOpenGLView alloc] initWithFrame:window_rect |
| andBrowserWindow:&browser_window_ |
| andRenderer:&renderer_]; |
| native_browser_view_.autoresizingMask = |
| (NSViewWidthSizable | NSViewHeightSizable); |
| native_browser_view_.autoresizesSubviews = YES; |
| [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle) |
| addSubview:native_browser_view_]; |
| |
| // Determine the default scale factor. |
| [native_browser_view_ resetDeviceScaleFactor]; |
| |
| [[NSNotificationCenter defaultCenter] |
| addObserver:native_browser_view_ |
| selector:@selector(windowDidChangeBackingProperties:) |
| name:NSWindowDidChangeBackingPropertiesNotification |
| object:native_browser_view_.window]; |
| } |
| |
| BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate, |
| const std::string& startup_url, |
| const OsrRendererSettings& settings) |
| : BrowserWindow(delegate) { |
| client_handler_ = new ClientHandlerOsr(this, this, startup_url); |
| impl_.reset( |
| new BrowserWindowOsrMacImpl(delegate, startup_url, settings, *this)); |
| } |
| |
| BrowserWindowOsrMac::~BrowserWindowOsrMac() {} |
| |
| void BrowserWindowOsrMac::CreateBrowser( |
| ClientWindowHandle parent_handle, |
| const CefRect& rect, |
| const CefBrowserSettings& settings, |
| CefRefPtr<CefDictionaryValue> extra_info, |
| CefRefPtr<CefRequestContext> request_context) { |
| impl_->CreateBrowser(parent_handle, rect, settings, extra_info, |
| request_context); |
| } |
| |
| void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& client, |
| CefBrowserSettings& settings) { |
| impl_->GetPopupConfig(temp_handle, windowInfo, client, settings); |
| } |
| |
| void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle, |
| int x, |
| int y, |
| size_t width, |
| size_t height) { |
| impl_->ShowPopup(parent_handle, x, y, width, height); |
| } |
| |
| void BrowserWindowOsrMac::Show() { |
| impl_->Show(); |
| } |
| |
| void BrowserWindowOsrMac::Hide() { |
| impl_->Hide(); |
| } |
| |
| void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) { |
| impl_->SetBounds(x, y, width, height); |
| } |
| |
| void BrowserWindowOsrMac::SetFocus(bool focus) { |
| impl_->SetFocus(focus); |
| } |
| |
| void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) { |
| impl_->SetDeviceScaleFactor(device_scale_factor); |
| } |
| |
| float BrowserWindowOsrMac::GetDeviceScaleFactor() const { |
| return impl_->GetDeviceScaleFactor(); |
| } |
| |
| ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const { |
| return impl_->GetWindowHandle(); |
| } |
| |
| void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr<CefBrowser> browser) { |
| impl_->OnAfterCreated(browser); |
| } |
| |
| void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr<CefBrowser> browser) { |
| impl_->OnBeforeClose(browser); |
| } |
| |
| bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr<CefBrowser> browser, |
| CefRect& rect) { |
| return impl_->GetRootScreenRect(browser, rect); |
| } |
| |
| void BrowserWindowOsrMac::GetViewRect(CefRefPtr<CefBrowser> browser, |
| CefRect& rect) { |
| impl_->GetViewRect(browser, rect); |
| } |
| |
| bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr<CefBrowser> browser, |
| int viewX, |
| int viewY, |
| int& screenX, |
| int& screenY) { |
| return impl_->GetScreenPoint(browser, viewX, viewY, screenX, screenY); |
| } |
| |
| bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr<CefBrowser> browser, |
| CefScreenInfo& screen_info) { |
| return impl_->GetScreenInfo(browser, screen_info); |
| } |
| |
| void BrowserWindowOsrMac::OnPopupShow(CefRefPtr<CefBrowser> browser, |
| bool show) { |
| impl_->OnPopupShow(browser, show); |
| } |
| |
| void BrowserWindowOsrMac::OnPopupSize(CefRefPtr<CefBrowser> browser, |
| const CefRect& rect) { |
| impl_->OnPopupSize(browser, rect); |
| } |
| |
| void BrowserWindowOsrMac::OnPaint(CefRefPtr<CefBrowser> browser, |
| CefRenderHandler::PaintElementType type, |
| const CefRenderHandler::RectList& dirtyRects, |
| const void* buffer, |
| int width, |
| int height) { |
| impl_->OnPaint(browser, type, dirtyRects, buffer, width, height); |
| } |
| |
| void BrowserWindowOsrMac::OnCursorChange( |
| CefRefPtr<CefBrowser> browser, |
| CefCursorHandle cursor, |
| CefRenderHandler::CursorType type, |
| const CefCursorInfo& custom_cursor_info) { |
| impl_->OnCursorChange(browser, cursor, type, custom_cursor_info); |
| } |
| |
| bool BrowserWindowOsrMac::StartDragging( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefDragData> drag_data, |
| CefRenderHandler::DragOperationsMask allowed_ops, |
| int x, |
| int y) { |
| return impl_->StartDragging(browser, drag_data, allowed_ops, x, y); |
| } |
| |
| void BrowserWindowOsrMac::UpdateDragCursor( |
| CefRefPtr<CefBrowser> browser, |
| CefRenderHandler::DragOperation operation) { |
| impl_->UpdateDragCursor(browser, operation); |
| } |
| |
| void BrowserWindowOsrMac::OnImeCompositionRangeChanged( |
| CefRefPtr<CefBrowser> browser, |
| const CefRange& selection_range, |
| const CefRenderHandler::RectList& character_bounds) { |
| impl_->OnImeCompositionRangeChanged(browser, selection_range, |
| character_bounds); |
| } |
| |
| void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr<CefValue> value) { |
| impl_->UpdateAccessibilityTree(value); |
| } |
| |
| void BrowserWindowOsrMac::UpdateAccessibilityLocation( |
| CefRefPtr<CefValue> value) { |
| impl_->UpdateAccessibilityLocation(value); |
| } |
| |
| } // namespace client |