-
-
Notifications
You must be signed in to change notification settings - Fork 193
Expand file tree
/
Copy pathCefBrowserWr.java
More file actions
438 lines (389 loc) · 16.7 KB
/
CefBrowserWr.java
File metadata and controls
438 lines (389 loc) · 16.7 KB
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
// Copyright (c) 2014 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.
package org.cef.browser;
import org.cef.CefBrowserSettings;
import org.cef.CefClient;
import org.cef.OS;
import org.cef.handler.CefWindowHandler;
import org.cef.handler.CefWindowHandlerAdapter;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
/**
* This class represents a windowed rendered browser.
* The visibility of this class is "package". To create a new
* CefBrowser instance, please use CefBrowserFactory.
*/
class CefBrowserWr extends CefBrowser_N {
private Canvas canvas_ = null;
private Component component_ = null;
private Rectangle content_rect_ = new Rectangle(0, 0, 0, 0);
private long window_handle_ = 0;
private boolean justCreated_ = false;
private double scaleFactor_ = 1.0;
private Timer delayedUpdate_ = new Timer(100, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isClosed()) return;
boolean hasCreatedUI = createBrowserIfRequired(true);
if (hasCreatedUI) {
delayedUpdate_.restart();
} else {
// If on Mac, this is needed due to the quirk described below
// (in org.cef.browser.CefBrowserWr.CefBrowserWr(...).new JPanel()
// {...}.paint(Graphics)). If on Linux, this is needed to invoke an
// XMoveResizeWindow call shortly after the UI was created. That seems to be
// necessary to actually get a windowed renderer to display something.
if (OS.isMacintosh() || OS.isLinux()) doUpdate();
}
}
});
}
});
private CefWindowHandlerAdapter win_handler_ = new CefWindowHandlerAdapter() {
private Point lastPos = new Point(-1, -1);
private long[] nextClick = new long[MouseInfo.getNumberOfButtons()];
private int[] clickCnt = new int[MouseInfo.getNumberOfButtons()];
@Override
public Rectangle getRect(CefBrowser browser) {
synchronized (content_rect_) {
return content_rect_;
}
}
@Override
public void onMouseEvent(CefBrowser browser, int event, final int screenX,
final int screenY, final int modifier, final int button) {
final Point pt = new Point(screenX, screenY);
if (event == MouseEvent.MOUSE_MOVED) {
// Remove mouse-moved events if the position of the cursor hasn't
// changed.
if (pt.equals(lastPos)) return;
lastPos = pt;
// Change mouse-moved event to mouse-dragged event if the left mouse
// button is pressed.
if ((modifier & MouseEvent.BUTTON1_DOWN_MASK) != 0)
event = MouseEvent.MOUSE_DRAGGED;
}
final int finalEvent = event;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Send mouse event to the root UI component instead to the browser UI.
// Otherwise no mouse-entered and no mouse-exited events would be fired.
Component parent = SwingUtilities.getRoot(component_);
if (parent == null) {
return;
}
SwingUtilities.convertPointFromScreen(pt, parent);
int clickCnt = 0;
long now = new Date().getTime();
if (finalEvent == MouseEvent.MOUSE_WHEEL) {
int scrollType = MouseWheelEvent.WHEEL_UNIT_SCROLL;
int rotation = button > 0 ? 1 : -1;
component_.dispatchEvent(new MouseWheelEvent(parent, finalEvent, now,
modifier, pt.x, pt.y, 0, false, scrollType, 3, rotation));
} else {
clickCnt = getClickCount(finalEvent, button);
component_.dispatchEvent(new MouseEvent(parent, finalEvent, now, modifier,
pt.x, pt.y, screenX, screenY, clickCnt, false, button));
}
// Always fire a mouse-clicked event after a mouse-released event.
if (finalEvent == MouseEvent.MOUSE_RELEASED) {
component_.dispatchEvent(
new MouseEvent(parent, MouseEvent.MOUSE_CLICKED, now, modifier,
pt.x, pt.y, screenX, screenY, clickCnt, false, button));
}
}
});
}
public int getClickCount(int event, int button) {
// avoid exceptions by using modulo
int idx = button % nextClick.length;
switch (event) {
case MouseEvent.MOUSE_PRESSED:
long currTime = new Date().getTime();
if (currTime > nextClick[idx]) {
nextClick[idx] = currTime
+ (Integer) Toolkit.getDefaultToolkit().getDesktopProperty(
"awt.multiClickInterval");
clickCnt[idx] = 1;
} else {
clickCnt[idx]++;
}
// FALL THRU
case MouseEvent.MOUSE_RELEASED:
return clickCnt[idx];
default:
return 0;
}
}
};
CefBrowserWr(
CefClient client, String url, CefRequestContext context, CefBrowserSettings settings) {
this(client, url, context, null, null, settings);
}
@SuppressWarnings("serial")
private CefBrowserWr(CefClient client, String url, CefRequestContext context,
CefBrowserWr parent, Point inspectAt, CefBrowserSettings settings) {
super(client, url, context, parent, inspectAt, settings);
delayedUpdate_.setRepeats(false);
// Disabling lightweight of popup menu is required because
// otherwise it will be displayed behind the content of component_
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
// We're using a JComponent instead of a Canvas now because the
// JComponent has clipping informations, which aren't accessible for Canvas.
component_ = new JPanel(new BorderLayout()) {
private boolean removed_ = true;
@Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
wasResized((int) (width * scaleFactor_), (int) (height * scaleFactor_));
}
@Override
public void setBounds(Rectangle r) {
setBounds(r.x, r.y, r.width, r.height);
}
@Override
public void setSize(int width, int height) {
super.setSize(width, height);
wasResized((int) (width * scaleFactor_), (int) (height * scaleFactor_));
}
@Override
public void setSize(Dimension d) {
setSize(d.width, d.height);
}
@Override
public void paint(Graphics g) {
// If the user resizes the UI component, the new size and clipping
// informations are forwarded to the native code.
// But on Mac the last resize information doesn't resize the native UI
// accurately (sometimes the native UI is too small). An easy way to
// solve this, is to send the last Update-Information again. Therefore
// we're setting up a delayedUpdate timer which is reset each time
// paint is called. This prevents the us of sending the UI update too
// often.
if (g instanceof Graphics2D) {
scaleFactor_ = ((Graphics2D) g).getTransform().getScaleX();
}
doUpdate();
delayedUpdate_.restart();
}
@Override
public void addNotify() {
super.addNotify();
if (removed_) {
setParent(getWindowHandle(this), canvas_);
removed_ = false;
}
}
@Override
public void removeNotify() {
if (!removed_) {
if (!isClosed()) {
setParent(0, null);
}
removed_ = true;
}
super.removeNotify();
}
};
// On windows we have to use a Canvas because its a heavyweight component
// and we need its native HWND as parent for the browser UI. The same
// technique is used on Linux as well.
if (OS.isWindows() || OS.isLinux()) {
canvas_ = new Canvas();
((JPanel) component_).add(canvas_, BorderLayout.CENTER);
}
// Initial minimal size of the component. Otherwise the UI won't work
// accordingly in panes like JSplitPane.
component_.setMinimumSize(new Dimension(0, 0));
component_.setFocusable(true);
component_.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
setFocus(false);
}
@Override
public void focusGained(FocusEvent e) {
// Dismiss any Java menus that are currently displayed.
MenuSelectionManager.defaultManager().clearSelectedPath();
setFocus(true);
}
});
component_.addHierarchyBoundsListener(new HierarchyBoundsListener() {
@Override
public void ancestorResized(HierarchyEvent e) {
doUpdate();
}
@Override
public void ancestorMoved(HierarchyEvent e) {
doUpdate();
notifyMoveOrResizeStarted();
}
});
component_.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
setWindowVisibility(e.getChanged().isVisible());
}
}
});
}
@Override
public void createImmediately() {
justCreated_ = true;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Create the browser immediately. It will be parented to the Java
// window once it becomes available.
createBrowserIfRequired(false);
}
});
}
@Override
public Component getUIComponent() {
return component_;
}
@Override
public CefWindowHandler getWindowHandler() {
return win_handler_;
}
@Override
protected CefBrowser_N createDevToolsBrowser(CefClient client, String url,
CefRequestContext context, CefBrowser_N parent, Point inspectAt) {
return new CefBrowserWr(client, url, context, (CefBrowserWr) this, inspectAt, null);
}
private synchronized long getWindowHandle() {
if (window_handle_ == 0 && OS.isMacintosh()) {
window_handle_ = getWindowHandle(component_);
}
return window_handle_;
}
private static long getWindowHandle(Component component) {
if (OS.isMacintosh()) {
try {
Class<?> cls = Class.forName("org.cef.browser.mac.CefBrowserWindowMac");
CefBrowserWindow browserWindow = (CefBrowserWindow) cls.newInstance();
if (browserWindow != null) {
return browserWindow.getWindowHandle(component);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return 0;
}
private void doUpdate() {
if (isClosed()) return;
Rectangle vr = ((JPanel) component_).getVisibleRect();
Rectangle clipping = new Rectangle((int) (vr.getX() * scaleFactor_),
(int) (vr.getY() * scaleFactor_), (int) (vr.getWidth() * scaleFactor_),
(int) (vr.getHeight() * scaleFactor_));
if (OS.isMacintosh()) {
Container parent = component_.getParent();
Point contentPos = component_.getLocation();
while (parent != null) {
Container next = parent.getParent();
if (next != null && next instanceof Window) break;
Point parentPos = parent.getLocation();
contentPos.translate(parentPos.x, parentPos.y);
parent = next;
}
contentPos.translate(clipping.x, clipping.y);
Point browserPos = clipping.getLocation();
browserPos.x *= -1;
browserPos.y *= -1;
synchronized (content_rect_) {
content_rect_ = new Rectangle(contentPos, clipping.getSize());
Rectangle browserRect = new Rectangle(browserPos, component_.getSize());
updateUI(content_rect_, browserRect);
}
} else {
synchronized (content_rect_) {
Rectangle bounds = null != canvas_ ? canvas_.getBounds() : component_.getBounds();
content_rect_ = new Rectangle((int) (bounds.getX() * scaleFactor_),
(int) (bounds.getY() * scaleFactor_),
(int) (bounds.getWidth() * scaleFactor_),
(int) (bounds.getHeight() * scaleFactor_));
updateUI(clipping, content_rect_);
}
}
}
private boolean createBrowserIfRequired(boolean hasParent) {
if (isClosed()) return false;
long windowHandle = 0;
Component canvas = null;
if (hasParent) {
windowHandle = getWindowHandle();
canvas = (OS.isWindows() || OS.isLinux()) ? canvas_ : component_;
}
if (getNativeRef("CefBrowser") == 0) {
if (getParentBrowser() != null) {
createDevTools(getParentBrowser(), getClient(), windowHandle, false, false, canvas,
getInspectAt());
return true;
} else {
createBrowser(getClient(), windowHandle, getUrl(), false, false, canvas,
getRequestContext());
return true;
}
} else if (hasParent && justCreated_) {
setParent(windowHandle, canvas);
setFocus(true);
justCreated_ = false;
}
return false;
}
@Override
public CompletableFuture<BufferedImage> createScreenshot(boolean nativeResolution) {
throw new UnsupportedOperationException("Unsupported for windowed rendering");
}
@Override
public void setWindowlessFrameRate(int frameRate) {
throw new UnsupportedOperationException(
"You can only set windowless framerate on OSR browser");
}
@Override
public CompletableFuture<Integer> getWindowlessFrameRate() {
throw new UnsupportedOperationException(
"You can only get windowless framerate on OSR browser");
}
}