1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
content / browser / renderer_host / input / synthetic_gesture_target_mac.mm [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/input/synthetic_gesture_target_mac.h"
#include "components/input/render_widget_host_input_event_router.h"
#import "content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
// Unlike some event APIs, Apple does not provide a way to programmatically
// build a zoom event. To work around this, we leverage ObjectiveC's flexible
// typing and build up an object with the right interface to provide a zoom
// event.
@interface SyntheticPinchEvent : NSObject
// Populated based on desired zoom level.
@property CGFloat magnification;
@property NSPoint locationInWindow;
@property NSEventType type;
@property NSTimeInterval timestamp;
@property NSEventPhase phase;
// Filled with default values.
@property(readonly) CGFloat deltaX;
@property(readonly) CGFloat deltaY;
@property(readonly) NSEventModifierFlags modifierFlags;
@end
@implementation SyntheticPinchEvent
@synthesize magnification = _magnification;
@synthesize locationInWindow = _locationInWindow;
@synthesize type = _type;
@synthesize timestamp = _timestamp;
@synthesize phase = _phase;
@synthesize deltaX = _deltaX;
@synthesize deltaY = _deltaY;
@synthesize modifierFlags = _modifierFlags;
- (instancetype)initWithMagnification:(float)magnification
locationInWindow:(NSPoint)location
timestamp:(NSTimeInterval)timestamp {
if (self = [super init]) {
_type = NSEventTypeMagnify;
_phase = NSEventPhaseChanged;
_magnification = magnification;
_locationInWindow = location;
_timestamp = timestamp;
_deltaX = 0;
_deltaY = 0;
_modifierFlags = 0;
}
return self;
}
+ (instancetype)eventWithMagnification:(float)magnification
locationInWindow:(NSPoint)location
timestamp:(NSTimeInterval)timestamp
phase:(NSEventPhase)phase {
SyntheticPinchEvent* event =
[[SyntheticPinchEvent alloc] initWithMagnification:magnification
locationInWindow:location
timestamp:timestamp];
event.phase = phase;
return event;
}
@end
using blink::WebInputEvent;
using blink::WebGestureEvent;
namespace content {
SyntheticGestureTargetMac::SyntheticGestureTargetMac(
RenderWidgetHostImpl* host,
RenderWidgetHostViewCocoa* cocoa_view)
: SyntheticGestureTargetBase(host), cocoa_view_(cocoa_view) {}
void SyntheticGestureTargetMac::DispatchWebGestureEventToPlatform(
const WebGestureEvent& web_gesture,
const ui::LatencyInfo& latency_info) {
// Create an autorelease pool so that we clean up any synthetic events we
// generate.
@autoreleasepool {
NSPoint content_local = NSMakePoint(
web_gesture.PositionInWidget().x(),
cocoa_view_.frame.size.height - web_gesture.PositionInWidget().y());
NSPoint location_in_window = [cocoa_view_ convertPoint:content_local
toView:nil];
NSTimeInterval timestamp =
ui::EventTimeStampToSeconds(web_gesture.TimeStamp());
switch (web_gesture.GetType()) {
case WebInputEvent::Type::kGesturePinchBegin: {
id cocoa_event =
[SyntheticPinchEvent eventWithMagnification:0.0f
locationInWindow:location_in_window
timestamp:timestamp
phase:NSEventPhaseBegan];
[cocoa_view_ handleBeginGestureWithEvent:cocoa_event
isSyntheticallyInjected:YES];
return;
}
case WebInputEvent::Type::kGesturePinchEnd: {
id cocoa_event =
[SyntheticPinchEvent eventWithMagnification:0.0f
locationInWindow:location_in_window
timestamp:timestamp
phase:NSEventPhaseEnded];
[cocoa_view_ handleEndGestureWithEvent:cocoa_event];
return;
}
case WebInputEvent::Type::kGesturePinchUpdate: {
id cocoa_event = [SyntheticPinchEvent
eventWithMagnification:web_gesture.data.pinch_update.scale - 1.0f
locationInWindow:location_in_window
timestamp:timestamp
phase:NSEventPhaseChanged];
[cocoa_view_ magnifyWithEvent:cocoa_event];
return;
}
default:
NOTREACHED_IN_MIGRATION();
}
}
}
void SyntheticGestureTargetMac::DispatchWebTouchEventToPlatform(
const blink::WebTouchEvent& web_touch,
const ui::LatencyInfo& latency_info) {
GetView()->InjectTouchEvent(web_touch, latency_info);
}
void SyntheticGestureTargetMac::DispatchWebMouseWheelEventToPlatform(
const blink::WebMouseWheelEvent& web_wheel,
const ui::LatencyInfo& latency_info) {
blink::WebMouseWheelEvent wheel_event = web_wheel;
wheel_event.wheel_ticks_x =
web_wheel.delta_x / ui::kScrollbarPixelsPerCocoaTick;
wheel_event.wheel_ticks_y =
web_wheel.delta_y / ui::kScrollbarPixelsPerCocoaTick;
// Manually route the WebMouseWheelEvent to any open popup window if the
// mouse is currently over the pop window, because window-level event routing
// on Mac happens at the OS API level which we cannot easily inject the
// events into.
if (GetView()->PopupChildHostView() &&
PointIsWithinContents(GetView()->PopupChildHostView(),
web_wheel.PositionInWidget())) {
GetView()->PopupChildHostView()->RouteOrProcessWheelEvent(wheel_event);
} else {
GetView()->RouteOrProcessWheelEvent(wheel_event);
}
if (web_wheel.phase == blink::WebMouseWheelEvent::kPhaseEnded) {
// Send the pending wheel end event immediately. Otherwise, the
// MouseWheelPhaseHandler will defer the end event in case of momentum
// scrolling. We want the end event sent before resolving the completion
// callback.
GetView()->GetMouseWheelPhaseHandler()->DispatchPendingWheelEndEvent();
}
}
void SyntheticGestureTargetMac::DispatchWebMouseEventToPlatform(
const blink::WebMouseEvent& web_mouse,
const ui::LatencyInfo& latency_info) {
GetView()->RouteOrProcessMouseEvent(web_mouse);
}
content::mojom::GestureSourceType
SyntheticGestureTargetMac::GetDefaultSyntheticGestureSourceType() const {
return content::mojom::GestureSourceType::kMouseInput;
}
float SyntheticGestureTargetMac::GetTouchSlopInDips() const {
return ui::GestureConfiguration::GetInstance()
->max_touch_move_in_pixels_for_click();
}
float SyntheticGestureTargetMac::GetSpanSlopInDips() const {
return ui::GestureConfiguration::GetInstance()->span_slop();
}
float SyntheticGestureTargetMac::GetMinScalingSpanInDips() const {
return ui::GestureConfiguration::GetInstance()->min_scaling_span_in_pixels();
}
RenderWidgetHostViewMac* SyntheticGestureTargetMac::GetView() const {
auto* view =
static_cast<RenderWidgetHostViewMac*>(render_widget_host()->GetView());
DCHECK(view);
return view;
}
bool SyntheticGestureTargetMac::PointIsWithinContents(
RenderWidgetHostView* view,
const gfx::PointF& point) {
gfx::Rect bounds = view->GetViewBounds();
gfx::Rect bounds_in_window =
bounds - bounds.OffsetFromOrigin(); // Translate the bounds to (0,0).
return bounds_in_window.Contains(point.x(), point.y());
}
} // namespace content