658 lines
22 KiB
Plaintext
658 lines
22 KiB
Plaintext
// Copyright 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 "libcef/browser/native/browser_platform_delegate_native_mac.h"
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <CoreServices/CoreServices.h>
|
|
|
|
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
|
|
#include "libcef/browser/context.h"
|
|
#include "libcef/browser/native/javascript_dialog_runner_mac.h"
|
|
#include "libcef/browser/native/menu_runner_mac.h"
|
|
#include "libcef/browser/thread_util.h"
|
|
|
|
#include "base/mac/scoped_nsautorelease_pool.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/threading/thread_restrictions.h"
|
|
#include "content/browser/renderer_host/render_widget_host_view_mac.h"
|
|
#include "content/public/browser/native_web_keyboard_event.h"
|
|
#include "content/public/browser/render_widget_host_view.h"
|
|
#include "content/public/browser/web_contents.h"
|
|
#include "third_party/blink/public/common/input/web_input_event.h"
|
|
#include "third_party/blink/public/common/input/web_mouse_event.h"
|
|
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
|
|
#include "ui/display/screen.h"
|
|
#include "ui/events/base_event_utils.h"
|
|
#include "ui/events/keycodes/keyboard_codes_posix.h"
|
|
#include "ui/gfx/geometry/rect.h"
|
|
|
|
// Wrapper NSView for the native view. Necessary to destroy the browser when
|
|
// the view is deleted.
|
|
@interface CefBrowserHostView : NSView {
|
|
@private
|
|
CefBrowserHostBase* browser_; // weak
|
|
}
|
|
|
|
@property(nonatomic, assign) CefBrowserHostBase* browser;
|
|
|
|
@end
|
|
|
|
@implementation CefBrowserHostView
|
|
|
|
@synthesize browser = browser_;
|
|
|
|
- (void)dealloc {
|
|
if (browser_) {
|
|
// Force the browser to be destroyed and release the reference added in
|
|
// PlatformCreateWindow().
|
|
static_cast<AlloyBrowserHostImpl*>(browser_)->WindowDestroyed();
|
|
}
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
// Receives notifications from the browser window. Will delete itself when done.
|
|
@interface CefWindowDelegate : NSObject <NSWindowDelegate> {
|
|
@private
|
|
CefBrowserHostBase* browser_; // weak
|
|
NSWindow* window_;
|
|
}
|
|
- (id)initWithWindow:(NSWindow*)window andBrowser:(CefBrowserHostBase*)browser;
|
|
@end
|
|
|
|
@implementation CefWindowDelegate
|
|
|
|
- (id)initWithWindow:(NSWindow*)window andBrowser:(CefBrowserHostBase*)browser {
|
|
if (self = [super init]) {
|
|
window_ = window;
|
|
browser_ = browser;
|
|
|
|
[window_ setDelegate:self];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (BOOL)windowShouldClose:(id)window {
|
|
if (browser_ && !browser_->TryCloseBrowser()) {
|
|
// Cancel the close.
|
|
return NO;
|
|
}
|
|
|
|
// Clean ourselves up after clearing the stack of anything that might have the
|
|
// window on it.
|
|
[self performSelectorOnMainThread:@selector(cleanup:)
|
|
withObject:window
|
|
waitUntilDone:NO];
|
|
|
|
// Allow the close.
|
|
return YES;
|
|
}
|
|
|
|
- (void)cleanup:(id)window {
|
|
[window_ setDelegate:nil];
|
|
[self release];
|
|
}
|
|
|
|
@end
|
|
|
|
namespace {
|
|
|
|
NSTimeInterval currentEventTimestamp() {
|
|
NSEvent* currentEvent = [NSApp currentEvent];
|
|
if (currentEvent) {
|
|
return [currentEvent timestamp];
|
|
} else {
|
|
// FIXME(API): In case there is no current event, the timestamp could be
|
|
// obtained by getting the time since the application started. This involves
|
|
// taking some more static functions from Chromium code.
|
|
// Another option is to have the timestamp as a field in CefEvent structures
|
|
// and let the client provide it.
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
NSUInteger NativeModifiers(int cef_modifiers) {
|
|
NSUInteger native_modifiers = 0;
|
|
if (cef_modifiers & EVENTFLAG_SHIFT_DOWN) {
|
|
native_modifiers |= NSEventModifierFlagShift;
|
|
}
|
|
if (cef_modifiers & EVENTFLAG_CONTROL_DOWN) {
|
|
native_modifiers |= NSEventModifierFlagControl;
|
|
}
|
|
if (cef_modifiers & EVENTFLAG_ALT_DOWN) {
|
|
native_modifiers |= NSEventModifierFlagOption;
|
|
}
|
|
if (cef_modifiers & EVENTFLAG_COMMAND_DOWN) {
|
|
native_modifiers |= NSEventModifierFlagCommand;
|
|
}
|
|
if (cef_modifiers & EVENTFLAG_CAPS_LOCK_ON) {
|
|
native_modifiers |= NSEventModifierFlagCapsLock;
|
|
}
|
|
if (cef_modifiers & EVENTFLAG_NUM_LOCK_ON) {
|
|
native_modifiers |= NSEventModifierFlagNumericPad;
|
|
}
|
|
|
|
return native_modifiers;
|
|
}
|
|
|
|
constexpr int kDefaultHeight = 750;
|
|
constexpr int kDefaultWidth = 750;
|
|
constexpr NSWindowStyleMask kDefaultStyleMask =
|
|
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
|
|
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
|
|
|
|
// Keep the frame bounds inside the display work area.
|
|
NSRect ClampNSBoundsToWorkArea(const NSRect& frame_bounds,
|
|
const gfx::Rect& display_bounds,
|
|
const gfx::Rect& work_area) {
|
|
NSRect bounds = frame_bounds;
|
|
|
|
// Convert from DIP coordinates (top-left origin) to macOS coordinates
|
|
// (bottom-left origin).
|
|
const int work_area_y =
|
|
display_bounds.height() - work_area.height() - work_area.y();
|
|
|
|
if (bounds.size.width > work_area.width()) {
|
|
bounds.size.width = work_area.width();
|
|
}
|
|
if (bounds.size.height > work_area.height()) {
|
|
bounds.size.height = work_area.height();
|
|
}
|
|
|
|
if (bounds.origin.x < work_area.x()) {
|
|
bounds.origin.x = work_area.x();
|
|
} else if (bounds.origin.x + bounds.size.width >=
|
|
work_area.x() + work_area.width()) {
|
|
bounds.origin.x = work_area.x() + work_area.width() - bounds.size.width;
|
|
}
|
|
|
|
if (bounds.origin.y < work_area_y) {
|
|
bounds.origin.y = work_area_y;
|
|
} else if (bounds.origin.y + bounds.size.height >=
|
|
work_area_y + work_area.height()) {
|
|
bounds.origin.y = work_area_y + work_area.height() - bounds.size.height;
|
|
}
|
|
|
|
return bounds;
|
|
}
|
|
|
|
// Get frame and content area rects matching the input DIP screen bounds. The
|
|
// resulting window frame will be kept inside the closest display work area. If
|
|
// |input_content_bounds| is true the input size is used for the content area
|
|
// and the input origin is used for the frame. Otherwise, both input size and
|
|
// origin are used for the frame.
|
|
void GetNSBoundsInDisplay(const gfx::Rect& dip_bounds,
|
|
bool input_content_bounds,
|
|
NSWindowStyleMask style_mask,
|
|
NSRect& frame_rect,
|
|
NSRect& content_rect) {
|
|
// Identify the closest display.
|
|
const auto display =
|
|
display::Screen::GetScreen()->GetDisplayMatching(dip_bounds);
|
|
const auto& display_bounds = display.bounds();
|
|
const auto& display_work_area = display.work_area();
|
|
|
|
// Convert from DIP coordinates (top-left origin) to macOS coordinates
|
|
// (bottom-left origin).
|
|
NSRect requested_rect = NSMakeRect(dip_bounds.x(), dip_bounds.y(),
|
|
dip_bounds.width(), dip_bounds.height());
|
|
requested_rect.origin.y = display_bounds.height() -
|
|
requested_rect.size.height -
|
|
requested_rect.origin.y;
|
|
|
|
// Calculate the equivalent frame and content bounds.
|
|
if (input_content_bounds) {
|
|
// Compute frame rect from content rect. Keep the requested origin.
|
|
content_rect = requested_rect;
|
|
frame_rect = [NSWindow frameRectForContentRect:content_rect
|
|
styleMask:style_mask];
|
|
frame_rect.origin = requested_rect.origin;
|
|
} else {
|
|
// Compute content rect from frame rect.
|
|
frame_rect = requested_rect;
|
|
content_rect = [NSWindow contentRectForFrameRect:frame_rect
|
|
styleMask:style_mask];
|
|
}
|
|
|
|
// Keep the frame inside the display work area.
|
|
const NSRect new_frame_rect =
|
|
ClampNSBoundsToWorkArea(frame_rect, display_bounds, display_work_area);
|
|
if (!NSEqualRects(frame_rect, new_frame_rect)) {
|
|
frame_rect = new_frame_rect;
|
|
content_rect = [NSWindow contentRectForFrameRect:frame_rect
|
|
styleMask:style_mask];
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CefBrowserPlatformDelegateNativeMac::CefBrowserPlatformDelegateNativeMac(
|
|
const CefWindowInfo& window_info,
|
|
SkColor background_color)
|
|
: CefBrowserPlatformDelegateNative(window_info, background_color),
|
|
host_window_created_(false) {}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::BrowserDestroyed(
|
|
CefBrowserHostBase* browser) {
|
|
CefBrowserPlatformDelegateNative::BrowserDestroyed(browser);
|
|
|
|
if (host_window_created_) {
|
|
// Release the reference added in CreateHostWindow().
|
|
browser->Release();
|
|
}
|
|
}
|
|
|
|
bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() {
|
|
base::mac::ScopedNSAutoreleasePool autorelease_pool;
|
|
|
|
NSWindow* new_window = nil;
|
|
|
|
NSView* parent_view =
|
|
CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.parent_view);
|
|
NSRect browser_view_rect =
|
|
NSMakeRect(window_info_.bounds.x, window_info_.bounds.y,
|
|
window_info_.bounds.width, window_info_.bounds.height);
|
|
|
|
if (parent_view == nil) {
|
|
// TODO(port): If no x,y position is specified the window will always appear
|
|
// in the upper-left corner. Maybe there's a better default place to put it?
|
|
const gfx::Rect dip_bounds(
|
|
window_info_.bounds.x, window_info_.bounds.y,
|
|
window_info_.bounds.width <= 0 ? kDefaultWidth
|
|
: window_info_.bounds.width,
|
|
window_info_.bounds.height <= 0 ? kDefaultHeight
|
|
: window_info_.bounds.height);
|
|
|
|
// Calculate the equivalent frame and content area bounds.
|
|
NSRect frame_rect, content_rect;
|
|
GetNSBoundsInDisplay(dip_bounds, /*input_content_bounds=*/true,
|
|
kDefaultStyleMask, frame_rect, content_rect);
|
|
|
|
// Create a new window.
|
|
new_window = [[NSWindow alloc] initWithContentRect:content_rect
|
|
styleMask:kDefaultStyleMask
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO];
|
|
|
|
// Create the delegate for control and browser window events.
|
|
[[CefWindowDelegate alloc] initWithWindow:new_window andBrowser:browser_];
|
|
|
|
parent_view = [new_window contentView];
|
|
browser_view_rect = [parent_view bounds];
|
|
|
|
window_info_.parent_view = parent_view;
|
|
|
|
// Make the content view for the window have a layer. This will make all
|
|
// sub-views have layers. This is necessary to ensure correct layer
|
|
// ordering of all child views and their layers.
|
|
[parent_view setWantsLayer:YES];
|
|
|
|
// Place the window at the target point. This is required for proper
|
|
// placement if the point is on a secondary display.
|
|
[new_window setFrameOrigin:frame_rect.origin];
|
|
}
|
|
|
|
host_window_created_ = true;
|
|
|
|
// Add a reference that will be released in BrowserDestroyed().
|
|
browser_->AddRef();
|
|
|
|
// Create the browser view.
|
|
CefBrowserHostView* browser_view =
|
|
[[CefBrowserHostView alloc] initWithFrame:browser_view_rect];
|
|
browser_view.browser = browser_;
|
|
[parent_view addSubview:browser_view];
|
|
[browser_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
|
|
[browser_view setNeedsDisplay:YES];
|
|
[browser_view release];
|
|
|
|
// Parent the WebContents to the browser view.
|
|
const NSRect bounds = [browser_view bounds];
|
|
NSView* native_view = web_contents_->GetNativeView().GetNativeNSView();
|
|
[browser_view addSubview:native_view];
|
|
[native_view setFrame:bounds];
|
|
[native_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
|
|
[native_view setNeedsDisplay:YES];
|
|
|
|
window_info_.view = browser_view;
|
|
|
|
if (new_window != nil && !window_info_.hidden) {
|
|
// Show the window.
|
|
[new_window makeKeyAndOrderFront:nil];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::CloseHostWindow() {
|
|
NSView* nsview = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.view);
|
|
if (nsview != nil) {
|
|
[[nsview window] performSelectorOnMainThread:@selector(performClose:)
|
|
withObject:nil
|
|
waitUntilDone:NO];
|
|
}
|
|
}
|
|
|
|
CefWindowHandle CefBrowserPlatformDelegateNativeMac::GetHostWindowHandle()
|
|
const {
|
|
if (windowless_handler_) {
|
|
return windowless_handler_->GetParentWindowHandle();
|
|
}
|
|
return window_info_.view;
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::SendKeyEvent(
|
|
const CefKeyEvent& event) {
|
|
auto view = GetHostView();
|
|
if (!view) {
|
|
return;
|
|
}
|
|
|
|
content::NativeWebKeyboardEvent web_event = TranslateWebKeyEvent(event);
|
|
view->ForwardKeyboardEvent(web_event, ui::LatencyInfo());
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::SendMouseClickEvent(
|
|
const CefMouseEvent& event,
|
|
CefBrowserHost::MouseButtonType type,
|
|
bool mouseUp,
|
|
int clickCount) {
|
|
auto view = GetHostView();
|
|
if (!view) {
|
|
return;
|
|
}
|
|
|
|
blink::WebMouseEvent web_event =
|
|
TranslateWebClickEvent(event, type, mouseUp, clickCount);
|
|
view->RouteOrProcessMouseEvent(web_event);
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::SendMouseMoveEvent(
|
|
const CefMouseEvent& event,
|
|
bool mouseLeave) {
|
|
auto view = GetHostView();
|
|
if (!view) {
|
|
return;
|
|
}
|
|
|
|
blink::WebMouseEvent web_event = TranslateWebMoveEvent(event, mouseLeave);
|
|
view->RouteOrProcessMouseEvent(web_event);
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::SendMouseWheelEvent(
|
|
const CefMouseEvent& event,
|
|
int deltaX,
|
|
int deltaY) {
|
|
auto view = GetHostView();
|
|
if (!view) {
|
|
return;
|
|
}
|
|
|
|
blink::WebMouseWheelEvent web_event =
|
|
TranslateWebWheelEvent(event, deltaX, deltaY);
|
|
view->RouteOrProcessMouseEvent(web_event);
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::SendTouchEvent(
|
|
const CefTouchEvent& event) {
|
|
NOTIMPLEMENTED();
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::SetFocus(bool setFocus) {
|
|
auto view = GetHostView();
|
|
if (view) {
|
|
view->SetActive(setFocus);
|
|
|
|
if (setFocus) {
|
|
// Give keyboard focus to the native view.
|
|
NSView* nsview = web_contents_->GetContentNativeView().GetNativeNSView();
|
|
DCHECK([nsview canBecomeKeyView]);
|
|
[[nsview window] makeFirstResponder:nsview];
|
|
}
|
|
}
|
|
}
|
|
|
|
gfx::Point CefBrowserPlatformDelegateNativeMac::GetScreenPoint(
|
|
const gfx::Point& view,
|
|
bool want_dip_coords) const {
|
|
// Mac always operates in DIP coordinates so |want_dip_coords| is ignored.
|
|
if (windowless_handler_) {
|
|
return windowless_handler_->GetParentScreenPoint(view, want_dip_coords);
|
|
}
|
|
|
|
NSView* nsview = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.parent_view);
|
|
if (nsview) {
|
|
NSRect bounds = [nsview bounds];
|
|
NSPoint view_pt = {static_cast<CGFloat>(view.x()),
|
|
bounds.size.height - static_cast<CGFloat>(view.y())};
|
|
NSPoint window_pt = [nsview convertPoint:view_pt toView:nil];
|
|
NSPoint screen_pt = [[nsview window] convertPointToScreen:window_pt];
|
|
return gfx::Point(screen_pt.x, screen_pt.y);
|
|
}
|
|
return gfx::Point();
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::ViewText(const std::string& text) {
|
|
// TODO(cef): Implement this functionality.
|
|
NOTIMPLEMENTED();
|
|
}
|
|
|
|
bool CefBrowserPlatformDelegateNativeMac::HandleKeyboardEvent(
|
|
const content::NativeWebKeyboardEvent& event) {
|
|
// Give the top level menu equivalents a chance to handle the event.
|
|
if ([event.os_event type] == NSEventTypeKeyDown) {
|
|
return [[NSApp mainMenu] performKeyEquivalent:event.os_event];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// static
|
|
void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {}
|
|
|
|
CefEventHandle CefBrowserPlatformDelegateNativeMac::GetEventHandle(
|
|
const content::NativeWebKeyboardEvent& event) const {
|
|
return event.os_event;
|
|
}
|
|
|
|
std::unique_ptr<CefJavaScriptDialogRunner>
|
|
CefBrowserPlatformDelegateNativeMac::CreateJavaScriptDialogRunner() {
|
|
return base::WrapUnique(new CefJavaScriptDialogRunnerMac);
|
|
}
|
|
|
|
std::unique_ptr<CefMenuRunner>
|
|
CefBrowserPlatformDelegateNativeMac::CreateMenuRunner() {
|
|
return base::WrapUnique(new CefMenuRunnerMac);
|
|
}
|
|
|
|
content::NativeWebKeyboardEvent
|
|
CefBrowserPlatformDelegateNativeMac::TranslateWebKeyEvent(
|
|
const CefKeyEvent& key_event) const {
|
|
content::NativeWebKeyboardEvent result(
|
|
blink::WebInputEvent::Type::kUndefined,
|
|
blink::WebInputEvent::Modifiers::kNoModifiers, ui::EventTimeForNow());
|
|
|
|
// Use a synthetic NSEvent in order to obtain the windowsKeyCode member from
|
|
// the NativeWebKeyboardEvent constructor. This is the only member which can
|
|
// not be easily translated (without hardcoding keyCodes)
|
|
// Determining whether a modifier key is left or right seems to be done
|
|
// through the key code as well.
|
|
NSEventType event_type;
|
|
if (key_event.character == 0 && key_event.unmodified_character == 0) {
|
|
// Check if both character and unmodified_characther are empty to determine
|
|
// if this was a NSEventTypeFlagsChanged event.
|
|
// A dead key will have an empty character, but a non-empty unmodified
|
|
// character
|
|
event_type = NSEventTypeFlagsChanged;
|
|
} else {
|
|
switch (key_event.type) {
|
|
case KEYEVENT_RAWKEYDOWN:
|
|
case KEYEVENT_KEYDOWN:
|
|
case KEYEVENT_CHAR:
|
|
event_type = NSEventTypeKeyDown;
|
|
break;
|
|
case KEYEVENT_KEYUP:
|
|
event_type = NSEventTypeKeyUp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NSString* charactersIgnoringModifiers =
|
|
[[[NSString alloc] initWithCharacters:&key_event.unmodified_character
|
|
length:1] autorelease];
|
|
NSString* characters =
|
|
[[[NSString alloc] initWithCharacters:&key_event.character
|
|
length:1] autorelease];
|
|
|
|
NSEvent* synthetic_event =
|
|
[NSEvent keyEventWithType:event_type
|
|
location:NSMakePoint(0, 0)
|
|
modifierFlags:NativeModifiers(key_event.modifiers)
|
|
timestamp:currentEventTimestamp()
|
|
windowNumber:0
|
|
context:nil
|
|
characters:characters
|
|
charactersIgnoringModifiers:charactersIgnoringModifiers
|
|
isARepeat:NO
|
|
keyCode:key_event.native_key_code];
|
|
|
|
result = content::NativeWebKeyboardEvent(synthetic_event);
|
|
if (key_event.type == KEYEVENT_CHAR) {
|
|
result.SetType(blink::WebInputEvent::Type::kChar);
|
|
}
|
|
|
|
result.is_system_key = key_event.is_system_key;
|
|
|
|
return result;
|
|
}
|
|
|
|
blink::WebMouseEvent
|
|
CefBrowserPlatformDelegateNativeMac::TranslateWebClickEvent(
|
|
const CefMouseEvent& mouse_event,
|
|
CefBrowserHost::MouseButtonType type,
|
|
bool mouseUp,
|
|
int clickCount) const {
|
|
blink::WebMouseEvent result;
|
|
TranslateWebMouseEvent(result, mouse_event);
|
|
|
|
switch (type) {
|
|
case MBT_LEFT:
|
|
result.SetType(mouseUp ? blink::WebInputEvent::Type::kMouseUp
|
|
: blink::WebInputEvent::Type::kMouseDown);
|
|
result.button = blink::WebMouseEvent::Button::kLeft;
|
|
break;
|
|
case MBT_MIDDLE:
|
|
result.SetType(mouseUp ? blink::WebInputEvent::Type::kMouseUp
|
|
: blink::WebInputEvent::Type::kMouseDown);
|
|
result.button = blink::WebMouseEvent::Button::kMiddle;
|
|
break;
|
|
case MBT_RIGHT:
|
|
result.SetType(mouseUp ? blink::WebInputEvent::Type::kMouseUp
|
|
: blink::WebInputEvent::Type::kMouseDown);
|
|
result.button = blink::WebMouseEvent::Button::kRight;
|
|
break;
|
|
default:
|
|
DCHECK(false);
|
|
}
|
|
|
|
result.click_count = clickCount;
|
|
|
|
return result;
|
|
}
|
|
|
|
blink::WebMouseEvent CefBrowserPlatformDelegateNativeMac::TranslateWebMoveEvent(
|
|
const CefMouseEvent& mouse_event,
|
|
bool mouseLeave) const {
|
|
blink::WebMouseEvent result;
|
|
TranslateWebMouseEvent(result, mouse_event);
|
|
|
|
if (!mouseLeave) {
|
|
result.SetType(blink::WebInputEvent::Type::kMouseMove);
|
|
if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON) {
|
|
result.button = blink::WebMouseEvent::Button::kLeft;
|
|
} else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON) {
|
|
result.button = blink::WebMouseEvent::Button::kMiddle;
|
|
} else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON) {
|
|
result.button = blink::WebMouseEvent::Button::kRight;
|
|
} else {
|
|
result.button = blink::WebMouseEvent::Button::kNoButton;
|
|
}
|
|
} else {
|
|
result.SetType(blink::WebInputEvent::Type::kMouseLeave);
|
|
result.button = blink::WebMouseEvent::Button::kNoButton;
|
|
}
|
|
|
|
result.click_count = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
blink::WebMouseWheelEvent
|
|
CefBrowserPlatformDelegateNativeMac::TranslateWebWheelEvent(
|
|
const CefMouseEvent& mouse_event,
|
|
int deltaX,
|
|
int deltaY) const {
|
|
blink::WebMouseWheelEvent result;
|
|
TranslateWebMouseEvent(result, mouse_event);
|
|
|
|
result.SetType(blink::WebInputEvent::Type::kMouseWheel);
|
|
|
|
static const double scrollbarPixelsPerCocoaTick = 40.0;
|
|
result.delta_x = deltaX;
|
|
result.delta_y = deltaY;
|
|
result.wheel_ticks_x = deltaX / scrollbarPixelsPerCocoaTick;
|
|
result.wheel_ticks_y = deltaY / scrollbarPixelsPerCocoaTick;
|
|
result.delta_units = ui::ScrollGranularity::kScrollByPrecisePixel;
|
|
|
|
if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON) {
|
|
result.button = blink::WebMouseEvent::Button::kLeft;
|
|
} else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON) {
|
|
result.button = blink::WebMouseEvent::Button::kMiddle;
|
|
} else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON) {
|
|
result.button = blink::WebMouseEvent::Button::kRight;
|
|
} else {
|
|
result.button = blink::WebMouseEvent::Button::kNoButton;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CefBrowserPlatformDelegateNativeMac::TranslateWebMouseEvent(
|
|
blink::WebMouseEvent& result,
|
|
const CefMouseEvent& mouse_event) const {
|
|
// position
|
|
result.SetPositionInWidget(mouse_event.x, mouse_event.y);
|
|
|
|
const gfx::Point& screen_pt = GetScreenPoint(
|
|
gfx::Point(mouse_event.x, mouse_event.y), /*want_dip_coords=*/true);
|
|
result.SetPositionInScreen(screen_pt.x(), screen_pt.y());
|
|
|
|
// modifiers
|
|
result.SetModifiers(result.GetModifiers() |
|
|
TranslateWebEventModifiers(mouse_event.modifiers));
|
|
|
|
// timestamp
|
|
result.SetTimeStamp(base::TimeTicks() +
|
|
base::Seconds(currentEventTimestamp()));
|
|
|
|
result.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
|
|
}
|
|
|
|
content::RenderWidgetHostViewMac*
|
|
CefBrowserPlatformDelegateNativeMac::GetHostView() const {
|
|
if (!web_contents_) {
|
|
return nullptr;
|
|
}
|
|
return static_cast<content::RenderWidgetHostViewMac*>(
|
|
web_contents_->GetRenderWidgetHostView());
|
|
}
|