From 8ff2de3ee62a37d317dca711c7de48cbe329004f Mon Sep 17 00:00:00 2001 From: Rene Schneider Date: Mon, 27 Nov 2023 18:19:27 +0000 Subject: [PATCH 01/11] Update to CEF version 119.4.3+gc76a3b9+chromium-119.0.6045.159 --- CMakeLists.txt | 2 +- java/org/cef/handler/CefLoadHandler.java | 4 ---- native/jni_util.cpp | 8 -------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d3db080..8895daa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON) # Specify the CEF distribution version. if(NOT DEFINED CEF_VERSION) - set(CEF_VERSION "116.0.19+gc6a20bc+chromium-116.0.5845.141") + set(CEF_VERSION "119.4.3+gc76a3b9+chromium-119.0.6045.159") endif() # Determine the platform. diff --git a/java/org/cef/handler/CefLoadHandler.java b/java/org/cef/handler/CefLoadHandler.java index 37d5a3e8..af50686e 100644 --- a/java/org/cef/handler/CefLoadHandler.java +++ b/java/org/cef/handler/CefLoadHandler.java @@ -187,12 +187,8 @@ enum ErrorCode { ERR_INVALID_HTTP_RESPONSE(-370), ERR_CONTENT_DECODING_INIT_FAILED(-371), ERR_HTTP2_RST_STREAM_NO_ERROR_RECEIVED(-372), - ERR_HTTP2_PUSHED_STREAM_NOT_AVAILABLE(-373), - ERR_HTTP2_CLAIMED_PUSHED_STREAM_RESET_BY_SERVER(-374), ERR_TOO_MANY_RETRIES(-375), ERR_HTTP2_STREAM_CLOSED(-376), - ERR_HTTP2_CLIENT_REFUSED_STREAM(-377), - ERR_HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH(-378), ERR_HTTP_RESPONSE_CODE_FAILURE(-379), ERR_QUIC_CERT_ROOT_NOT_KNOWN(-380), ERR_QUIC_GOAWAY_REQUEST_CAN_BE_RETRIED(-381), diff --git a/native/jni_util.cpp b/native/jni_util.cpp index b71fc29e..d2c2488e 100644 --- a/native/jni_util.cpp +++ b/native/jni_util.cpp @@ -633,18 +633,10 @@ jobject NewJNIErrorCode(JNIEnv* env, cef_errorcode_t errorCode) { ERR_CONTENT_DECODING_INIT_FAILED, jerrorCode); JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", ERR_HTTP2_RST_STREAM_NO_ERROR_RECEIVED, jerrorCode); - JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", - ERR_HTTP2_PUSHED_STREAM_NOT_AVAILABLE, jerrorCode); - JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", - ERR_HTTP2_CLAIMED_PUSHED_STREAM_RESET_BY_SERVER, jerrorCode); JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", ERR_TOO_MANY_RETRIES, jerrorCode); JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", ERR_HTTP2_STREAM_CLOSED, jerrorCode); - JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", - ERR_HTTP2_CLIENT_REFUSED_STREAM, jerrorCode); - JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", - ERR_HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH, jerrorCode); JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", ERR_HTTP_RESPONSE_CODE_FAILURE, jerrorCode); JNI_CASE(env, "org/cef/handler/CefLoadHandler$ErrorCode", From 1fdf12d7599d124d2c9509666bc9b4caf45f4d46 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 5 Jan 2024 17:26:17 +0000 Subject: [PATCH 02/11] Update to CEF version 119.4.7+g55e15c8+chromium-119.0.6045.199 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8895daa8..54d592b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON) # Specify the CEF distribution version. if(NOT DEFINED CEF_VERSION) - set(CEF_VERSION "119.4.3+gc76a3b9+chromium-119.0.6045.159") + set(CEF_VERSION "119.4.7+g55e15c8+chromium-119.0.6045.199") endif() # Determine the platform. From beb93367c934e4dc62b31ee11a836331b4401961 Mon Sep 17 00:00:00 2001 From: Rene Schneider Date: Tue, 9 Jan 2024 18:19:20 +0000 Subject: [PATCH 03/11] Fix HTTP auth credential request forwarding of realm and scheme params --- native/request_handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/request_handler.cpp b/native/request_handler.cpp index 9d6b89e3..26d50dbd 100644 --- a/native/request_handler.cpp +++ b/native/request_handler.cpp @@ -120,8 +120,8 @@ bool RequestHandler::GetAuthCredentials(CefRefPtr browser, ScopedJNIBrowser jbrowser(env, browser); ScopedJNIString joriginUrl(env, origin_url); ScopedJNIString jhost(env, host); - ScopedJNIString jrealm(env, host); - ScopedJNIString jscheme(env, host); + ScopedJNIString jrealm(env, realm); + ScopedJNIString jscheme(env, scheme); ScopedJNIAuthCallback jcallback(env, callback); jboolean jresult = JNI_FALSE; From 812891babd55b482c5b4bc318ad10117057c1c61 Mon Sep 17 00:00:00 2001 From: Antoine Veldhoven Date: Tue, 9 Jan 2024 19:56:22 +0000 Subject: [PATCH 04/11] Add CefFrame.selectAll (fixes #428) Also implements common keyboard shortcuts on Mac. --- java/org/cef/browser/CefFrame.java | 7 ++++- java/org/cef/browser/CefFrame_N.java | 9 +++++++ .../detailed/handler/KeyboardHandler.java | 26 +++++++++++++++++++ native/CefFrame_N.cpp | 11 ++++++++ native/CefFrame_N.h | 9 +++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/java/org/cef/browser/CefFrame.java b/java/org/cef/browser/CefFrame.java index 3a2393fb..280f1e35 100644 --- a/java/org/cef/browser/CefFrame.java +++ b/java/org/cef/browser/CefFrame.java @@ -98,4 +98,9 @@ public interface CefFrame { * Execute paste in this frame. */ public void paste(); -} \ No newline at end of file + + /** + * Execute selectAll in this frame. + */ + public void selectAll(); +} diff --git a/java/org/cef/browser/CefFrame_N.java b/java/org/cef/browser/CefFrame_N.java index 8e129f1a..c68e1fbf 100644 --- a/java/org/cef/browser/CefFrame_N.java +++ b/java/org/cef/browser/CefFrame_N.java @@ -148,6 +148,14 @@ public void paste() { } } + public void selectAll() { + try { + N_SelectAll(getNativeRef(null)); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + private final native void N_Dispose(long self); private final native long N_GetIdentifier(long self); private final native String N_GetURL(long self); @@ -162,4 +170,5 @@ public void paste() { private final native void N_Cut(long self); private final native void N_Copy(long self); private final native void N_Paste(long self); + private final native void N_SelectAll(long self); } diff --git a/java/tests/detailed/handler/KeyboardHandler.java b/java/tests/detailed/handler/KeyboardHandler.java index 350d0f3b..6785b550 100644 --- a/java/tests/detailed/handler/KeyboardHandler.java +++ b/java/tests/detailed/handler/KeyboardHandler.java @@ -20,6 +20,32 @@ public boolean onKeyEvent(CefBrowser browser, CefKeyEvent event) { browser.executeJavaScript("alert('You pressed the space bar!');", "", 0); } return true; + } else if (event.type == CefKeyEvent.EventType.KEYEVENT_RAWKEYDOWN && event.is_system_key) { + // CMD+[key] is not working on a Mac. + // This switch statement delegates the common keyboard shortcuts to the browser + switch (event.unmodified_character) { + case 'a': + browser.getFocusedFrame().selectAll(); + break; + case 'c': + browser.getFocusedFrame().copy(); + break; + case 'v': + browser.getFocusedFrame().paste(); + break; + case 'x': + browser.getFocusedFrame().cut(); + break; + case 'z': + browser.getFocusedFrame().undo(); + break; + case 'Z': + browser.getFocusedFrame().redo(); + break; + default: + return false; + } + return true; } return false; } diff --git a/native/CefFrame_N.cpp b/native/CefFrame_N.cpp index 3fc93790..46f039fb 100644 --- a/native/CefFrame_N.cpp +++ b/native/CefFrame_N.cpp @@ -164,3 +164,14 @@ JNIEXPORT void JNICALL Java_org_cef_browser_CefFrame_1N_N_1Paste(JNIEnv* env, frame->Paste(); } + +JNIEXPORT void JNICALL +Java_org_cef_browser_CefFrame_1N_N_1SelectAll(JNIEnv* env, + jobject obj, + jlong self) { + CefRefPtr frame = GetSelf(self); + if (!frame) + return; + + frame->SelectAll(); +} diff --git a/native/CefFrame_N.h b/native/CefFrame_N.h index e1f6b459..c184d384 100644 --- a/native/CefFrame_N.h +++ b/native/CefFrame_N.h @@ -135,6 +135,15 @@ JNIEXPORT void JNICALL Java_org_cef_browser_CefFrame_1N_N_1Paste(JNIEnv*, jobject, jlong); +/* + * Class: org_cef_browser_CefFrame_N + * Method: N_SelectAll + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_cef_browser_CefFrame_1N_N_1SelectAll(JNIEnv*, + jobject, + jlong); + #ifdef __cplusplus } #endif From 566dc4e166383559f781d2f7b4672633345bb496 Mon Sep 17 00:00:00 2001 From: Sander van den Berg Date: Thu, 11 Jan 2024 16:53:23 +0000 Subject: [PATCH 05/11] Add CefDevToolsClient --- java/org/cef/browser/CefBrowser.java | 14 +- java/org/cef/browser/CefBrowser_N.java | 50 +++++- java/org/cef/browser/CefDevToolsClient.java | 165 ++++++++++++++++++ .../browser/CefDevToolsMessageObserver.java | 34 ++++ java/org/cef/browser/CefRegistration.java | 24 +++ java/org/cef/browser/CefRegistration_N.java | 33 ++++ java/tests/detailed/ui/MenuBar.java | 59 ++++++- native/CMakeLists.txt | 6 + native/CefBrowser_N.cpp | 80 +++++++++ native/CefBrowser_N.h | 37 +++- native/CefFileDialogCallback_N.h | 3 +- native/CefRegistration_N.cpp | 14 ++ native/CefRegistration_N.h | 22 +++ native/devtools_message_observer.cpp | 44 +++++ native/devtools_message_observer.h | 37 ++++ native/devtools_method_callback.cpp | 21 +++ native/devtools_method_callback.h | 31 ++++ tools/make_all_jni_headers.bat | 1 + 18 files changed, 663 insertions(+), 12 deletions(-) create mode 100644 java/org/cef/browser/CefDevToolsClient.java create mode 100644 java/org/cef/browser/CefDevToolsMessageObserver.java create mode 100644 java/org/cef/browser/CefRegistration.java create mode 100644 java/org/cef/browser/CefRegistration_N.java create mode 100644 native/CefRegistration_N.cpp create mode 100644 native/CefRegistration_N.h create mode 100644 native/devtools_message_observer.cpp create mode 100644 native/devtools_message_observer.h create mode 100644 native/devtools_method_callback.cpp create mode 100644 native/devtools_method_callback.h diff --git a/java/org/cef/browser/CefBrowser.java b/java/org/cef/browser/CefBrowser.java index 43ae8f60..de0b2268 100644 --- a/java/org/cef/browser/CefBrowser.java +++ b/java/org/cef/browser/CefBrowser.java @@ -337,19 +337,29 @@ public void runFileDialog(FileDialogMode mode, String title, String defaultFileP public void stopFinding(boolean clearSelection); /** - * Get an instance of the dev tools to be displayed in its own window or to be + * Get an instance of the DevTools to be displayed in its own window or to be * embedded within your UI. Only one instance per browser is available. */ public CefBrowser getDevTools(); /** - * Get an instance of the dev tools to be displayed in its own window or to be + * Get an instance of the DevTools to be displayed in its own window or to be * embedded within your UI. Only one instance per browser is available. * * @param inspectAt a position in the UI which should be inspected. */ public CefBrowser getDevTools(Point inspectAt); + /** + * Get an instance of a client that can be used to leverage the DevTools + * protocol. Only one instance per browser is available. + * + * @see {@link CefDevToolsClient} + * @return DevTools client, or null if this browser is not yet created + * or if it is closed or closing + */ + public CefDevToolsClient getDevToolsClient(); + /** * If a misspelled word is currently selected in an editable node calling * this method will replace it with the specified |word|. diff --git a/java/org/cef/browser/CefBrowser_N.java b/java/org/cef/browser/CefBrowser_N.java index 406d0256..a5685bc5 100644 --- a/java/org/cef/browser/CefBrowser_N.java +++ b/java/org/cef/browser/CefBrowser_N.java @@ -5,7 +5,7 @@ package org.cef.browser; import org.cef.CefClient; -import org.cef.browser.CefRequestContext; +import org.cef.browser.CefDevToolsClient.DevToolsException; import org.cef.callback.CefDragData; import org.cef.callback.CefNativeAdapter; import org.cef.callback.CefPdfPrintCallback; @@ -18,7 +18,6 @@ import org.cef.misc.CefPdfPrintSettings; import org.cef.network.CefRequest; -import java.awt.Canvas; import java.awt.Component; import java.awt.Point; import java.awt.Rectangle; @@ -28,6 +27,7 @@ import java.awt.event.MouseWheelEvent; import java.awt.event.WindowEvent; import java.util.Vector; +import java.util.concurrent.CompletableFuture; import javax.swing.SwingUtilities; @@ -45,6 +45,7 @@ abstract class CefBrowser_N extends CefNativeAdapter implements CefBrowser { private volatile CefBrowser_N parent_ = null; private volatile Point inspectAt_ = null; private volatile CefBrowser_N devTools_ = null; + private volatile CefDevToolsClient devToolsClient_ = null; private boolean closeAllowed_ = false; private volatile boolean isClosed_ = false; private volatile boolean isClosing_ = false; @@ -130,6 +131,9 @@ public synchronized void onBeforeClose() { parent_.devTools_ = null; parent_ = null; } + if (devToolsClient_ != null) { + devToolsClient_.close(); + } } @Override @@ -145,6 +149,37 @@ public synchronized CefBrowser getDevTools(Point inspectAt) { return devTools_; } + @Override + public synchronized CefDevToolsClient getDevToolsClient() { + if (!isPending_ || isClosing_ || isClosed_) { + return null; + } + if (devToolsClient_ == null || devToolsClient_.isClosed()) { + devToolsClient_ = new CefDevToolsClient(this); + } + return devToolsClient_; + } + + CompletableFuture executeDevToolsMethod(String method, String parametersAsJson) { + CompletableFuture future = new CompletableFuture<>(); + N_ExecuteDevToolsMethod(method, parametersAsJson, new DevToolsMethodCallback() { + @Override + public void onComplete(int generatedMessageId) { + if (generatedMessageId <= 0) { + future.completeExceptionally(new DevToolsException( + String.format("Failed to execute DevTools method %s", method))); + } else { + future.complete(generatedMessageId); + } + } + }); + return future; + } + + CefRegistration addDevToolsMessageObserver(CefDevToolsMessageObserver observer) { + return N_AddDevToolsMessageObserver(observer); + } + protected abstract CefBrowser_N createDevToolsBrowser(CefClient client, String url, CefRequestContext context, CefBrowser_N parent, Point inspectAt); @@ -379,6 +414,7 @@ public boolean hasDocument() { return false; } + @Override public void viewSource() { try { N_ViewSource(); @@ -387,6 +423,7 @@ public void viewSource() { } } + @Override public void getSource(CefStringVisitor visitor) { try { N_GetSource(visitor); @@ -395,6 +432,7 @@ public void getSource(CefStringVisitor visitor) { } } + @Override public void getText(CefStringVisitor visitor) { try { N_GetText(visitor); @@ -751,11 +789,19 @@ protected final void notifyMoveOrResizeStarted() { } } + private interface DevToolsMethodCallback { + void onComplete(int generatedMessageId); + } + private final native boolean N_CreateBrowser(CefClientHandler clientHandler, long windowHandle, String url, boolean osr, boolean transparent, Component canvas, CefRequestContext context); private final native boolean N_CreateDevTools(CefBrowser parent, CefClientHandler clientHandler, long windowHandle, boolean osr, boolean transparent, Component canvas, Point inspectAt); + private final native void N_ExecuteDevToolsMethod( + String method, String parametersAsJson, DevToolsMethodCallback callback); + private final native CefRegistration N_AddDevToolsMessageObserver( + CefDevToolsMessageObserver observer); private final native long N_GetWindowHandle(long surfaceHandle); private final native boolean N_CanGoBack(); private final native void N_GoBack(); diff --git a/java/org/cef/browser/CefDevToolsClient.java b/java/org/cef/browser/CefDevToolsClient.java new file mode 100644 index 00000000..ee119469 --- /dev/null +++ b/java/org/cef/browser/CefDevToolsClient.java @@ -0,0 +1,165 @@ +// Copyright (c) 2024 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 java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class CefDevToolsClient implements AutoCloseable { + private final Map> queuedCommands_ = + Collections.synchronizedMap(new HashMap<>()); + private final Set eventListeners_ = + Collections.synchronizedSet(new LinkedHashSet<>()); + private CefRegistration registration_; + private final CefBrowser_N browser_; + + /** + * Use {@link CefBrowser#getDevToolsClient()} to get an instance of this class. + */ + CefDevToolsClient(CefBrowser_N browser) { + this.browser_ = browser; + + registration_ = browser.addDevToolsMessageObserver(new CefDevToolsMessageObserver() { + @Override + public void onDevToolsMethodResult( + CefBrowser browser, int messageId, boolean success, String result) { + CompletableFuture future = getQueuedCommand(messageId); + if (success) { + future.complete(result); + } else { + future.completeExceptionally( + new DevToolsException("DevTools method failed", result)); + } + } + + @Override + public void onDevToolsEvent(CefBrowser browser, String method, String parameters) { + for (EventListener eventListener : eventListeners_) { + eventListener.onEvent(method, parameters); + } + } + }); + } + + @Override + public void close() { + queuedCommands_.clear(); + eventListeners_.clear(); + registration_ = null; + } + + public boolean isClosed() { + return registration_ == null; + } + + private CompletableFuture getQueuedCommand(Integer messageId) { + return queuedCommands_.computeIfAbsent(messageId, key -> new CompletableFuture<>()); + } + + /** + * Execute a method call over the DevTools protocol. See the DevTools protocol documentation + * for details of supported methods and the expected syntax for parameters. + * + *

If an error occurs the returned future is completed exceptionally, otherwise its value is + * asynchronously set to the method result. + * + *

Call {@link #addEventListener(String, EventListener)} to subscribe to events. + * + * @param method the method name + * @return return a future with the method result if the method was executed successfully + */ + public CompletableFuture executeDevToolsMethod(String method) { + return executeDevToolsMethod(method, null); + } + + /** + * Execute a method call over the DevTools protocol. See the DevTools protocol documentation + * for details of supported methods and the expected syntax for parameters. + * + *

If an error occurs the returned future is completed exceptionally, otherwise its value is + * asynchronously set to the method result. + * + *

Call {@link #addEventListener(String, EventListener)} to subscribe to events. + * + * @param method the method name + * @param parametersAsJson JSON object with parameters, or null if no parameters are needed + * @return return a future with the method result if the method was executed successfully + */ + public CompletableFuture executeDevToolsMethod(String method, String parametersAsJson) { + if (isClosed()) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new DevToolsException("Client is closed")); + return future; + } + + return browser_.executeDevToolsMethod(method, parametersAsJson) + .thenCompose(this::getQueuedCommand); + } + + /** + * Add an event listener for DevTools protocol events. Events by default are disabled + * and need to be enabled on a per domain basis, e.g. by sending Network.enable to enable + * network related events. + * + * @param eventListener the listener to add + */ + public void addEventListener(EventListener eventListener) { + eventListeners_.add(eventListener); + } + + /** + * Remove an event listener for DevTools protocol events. + * + * @param eventListener the listener to remove + */ + public void removeEventListener(EventListener eventListener) { + eventListeners_.remove(eventListener); + } + + public interface EventListener { + /** + * Method that will be called on receipt of an event. + * @param eventName the event name + * @param messageAsJson JSON object with the event message + */ + void onEvent(String eventName, String messageAsJson); + } + + public static final class DevToolsException extends Exception { + private static final long serialVersionUID = 3952948449841375372L; + + private final String json_; + + public DevToolsException(String message) { + this(message, null); + } + + public DevToolsException(String message, String json) { + super(message); + this.json_ = json; + } + + @Override + public String getMessage() { + String message = super.getMessage(); + if (json_ != null) message += ": " + json_; + return message; + } + + /** + * JSON object with the error details that were passed back by the DevTools + * host, or null if no further details are available. + */ + public String getJson() { + return json_; + } + } +} diff --git a/java/org/cef/browser/CefDevToolsMessageObserver.java b/java/org/cef/browser/CefDevToolsMessageObserver.java new file mode 100644 index 00000000..3a851356 --- /dev/null +++ b/java/org/cef/browser/CefDevToolsMessageObserver.java @@ -0,0 +1,34 @@ +// Copyright (c) 2024 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; + +/** + * Used internally by {@link CefDevToolsClient}. + *

+ * Callback interface for {@link CefBrowser#addDevToolsMessageObserver(CefDevToolsMessageObserver)}. + * The methods of this class will be called on the CEF UI thread. + */ +interface CefDevToolsMessageObserver { + /** + * Method that will be called after attempted execution of a DevTools protocol method. + * + * @param browser the originating browser instance + * @param messageId the ID that identifies the originating method call + * @param success if the method succeeded, |success| will be true and |result| will be a JSON + * object containing the method call result. If the method call failed, |success| will + * be false and |result| will be a JSON object describing the error. + * @param result method call result or an error + */ + void onDevToolsMethodResult(CefBrowser browser, int messageId, boolean success, String result); + + /** + * Method that will be called on receipt of a DevTools protocol event. + * + * @param browser the originating browser instance + * @param method the method name + * @param parameters the event data + */ + void onDevToolsEvent(CefBrowser browser, String method, String parameters); +} diff --git a/java/org/cef/browser/CefRegistration.java b/java/org/cef/browser/CefRegistration.java new file mode 100644 index 00000000..11051404 --- /dev/null +++ b/java/org/cef/browser/CefRegistration.java @@ -0,0 +1,24 @@ +// Copyright (c) 2024 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; + +/** + * Used internally by {@link CefDevToolsClient}. + *

+ * Handle to observer registration, As long as this object is alive, the observer will stay + * registered. + */ +abstract class CefRegistration { + /** + * Removes the native reference from an unused object. + */ + public abstract void dispose(); + + @Override + public void finalize() throws Throwable { + dispose(); + super.finalize(); + } +} diff --git a/java/org/cef/browser/CefRegistration_N.java b/java/org/cef/browser/CefRegistration_N.java new file mode 100644 index 00000000..a7defd13 --- /dev/null +++ b/java/org/cef/browser/CefRegistration_N.java @@ -0,0 +1,33 @@ +// Copyright (c) 2024 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.callback.CefNative; + +class CefRegistration_N extends CefRegistration implements CefNative { + // Used internally to store a pointer to the CEF object. + private long N_CefHandle = 0; + + @Override + public void setNativeRef(String identifier, long nativeRef) { + N_CefHandle = nativeRef; + } + + @Override + public long getNativeRef(String identifier) { + return N_CefHandle; + } + + @Override + public void dispose() { + try { + N_Dispose(N_CefHandle); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + + private final native void N_Dispose(long self); +} diff --git a/java/tests/detailed/ui/MenuBar.java b/java/tests/detailed/ui/MenuBar.java index c69ae215..850e2104 100644 --- a/java/tests/detailed/ui/MenuBar.java +++ b/java/tests/detailed/ui/MenuBar.java @@ -4,10 +4,9 @@ package tests.detailed.ui; -import org.cef.CefApp; -import org.cef.CefClient; import org.cef.OS; import org.cef.browser.CefBrowser; +import org.cef.browser.CefDevToolsClient; import org.cef.callback.CefPdfPrintCallback; import org.cef.callback.CefRunFileDialogCallback; import org.cef.callback.CefStringVisitor; @@ -21,12 +20,10 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; -import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; -import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; @@ -40,10 +37,10 @@ import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; -import javax.swing.JLayeredPane; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; @@ -86,6 +83,7 @@ public void visit(String string) { private final DownloadDialog downloadDialog_; private final CefCookieManager cookieManager_; private boolean reparentPending_ = false; + private CefDevToolsClient devToolsClient_; public MenuBar(MainFrame owner, CefBrowser browser, ControlPanel control_pane, DownloadDialog downloadDialog, CefCookieManager cookieManager) { @@ -382,6 +380,37 @@ public void componentHidden(ComponentEvent e) { }); testMenu.add(showDevTools); + JMenu devToolsProtocolMenu = new JMenu("DevTools Protocol"); + JMenuItem autoDarkMode = devToolsProtocolMenu.add(new JCheckBoxMenuItem("Auto Dark Mode")); + autoDarkMode.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Toggle the auto dark mode override + String params = String.format("{ \"enabled\": %s }", autoDarkMode.isSelected()); + executeDevToolsMethod("Emulation.setAutoDarkModeOverride", params); + } + }); + JMenuItem checkContrast = devToolsProtocolMenu.add(new JMenuItem("Check Contrast")); + checkContrast.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Check contrast, which usually triggers a series of Audits.issueAdded events + executeDevToolsMethod("Audits.checkContrast"); + } + }); + JMenuItem enableCSS = devToolsProtocolMenu.add(new JMenuItem("Enable CSS Agent")); + enableCSS.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Enable the CSS agent, which usually triggers a series of CSS.styleSheetAdded + // events. We can only enable the CSS agent if the DOM agent is enabled first, so we + // need to chain the two commands. + executeDevToolsMethod("DOM.enable") + .thenCompose(unused -> executeDevToolsMethod("CSS.enable")); + } + }); + testMenu.add(devToolsProtocolMenu); + JMenuItem testURLRequest = new JMenuItem("URL Request"); testURLRequest.addActionListener(new ActionListener() { @Override @@ -549,6 +578,26 @@ private void displayScreenshot(BufferedImage aScreenshot) { frame.pack(); } + private CompletableFuture executeDevToolsMethod(String methodName) { + return executeDevToolsMethod(methodName, null); + } + + private CompletableFuture executeDevToolsMethod( + String methodName, String paramsAsJson) { + if (devToolsClient_ == null) { + devToolsClient_ = browser_.getDevToolsClient(); + devToolsClient_.addEventListener( + (method, json) -> System.out.println("CDP event " + method + ": " + json)); + } + + return devToolsClient_.executeDevToolsMethod(methodName, paramsAsJson) + .handle((error, json) -> { + System.out.println( + "CDP result of " + methodName + ": " + (error != null ? error : json)); + return null; + }); + } + public void addBookmarkSeparator() { bookmarkMenu_.addSeparator(); } diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 7662405f..e3635be2 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -54,6 +54,8 @@ set(JCEF_SRCS CefPrintSettings_N.h CefQueryCallback_N.cpp CefQueryCallback_N.h + CefRegistration_N.cpp + CefRegistration_N.h CefRequest_N.cpp CefRequest_N.h CefCallback_N.cpp @@ -83,6 +85,10 @@ set(JCEF_SRCS cookie_visitor.cpp cookie_visitor.h critical_wait.h + devtools_message_observer.cpp + devtools_message_observer.h + devtools_method_callback.cpp + devtools_method_callback.h dialog_handler.cpp dialog_handler.h display_handler.cpp diff --git a/native/CefBrowser_N.cpp b/native/CefBrowser_N.cpp index c3c08813..c754c89a 100644 --- a/native/CefBrowser_N.cpp +++ b/native/CefBrowser_N.cpp @@ -6,12 +6,15 @@ #include "include/base/cef_callback.h" #include "include/cef_browser.h" +#include "include/cef_parser.h" #include "include/cef_task.h" #include "include/wrapper/cef_closure_task.h" #include "browser_process_handler.h" #include "client_handler.h" #include "critical_wait.h" +#include "devtools_message_observer.h" +#include "devtools_method_callback.h" #include "jni_util.h" #include "life_span_handler.h" #include "pdf_print_callback.h" @@ -1035,6 +1038,26 @@ void getZoomLevel(CefRefPtr host, } } +void executeDevToolsMethod(CefRefPtr host, + const CefString& method, + const CefString& parametersAsJson, + CefRefPtr callback) { + CefRefPtr parameters = nullptr; + if (!parametersAsJson.empty()) { + CefRefPtr value = CefParseJSON( + parametersAsJson, cef_json_parser_options_t::JSON_PARSER_RFC); + + if (!value || value->GetType() != VTYPE_DICTIONARY) { + callback->onComplete(0); + return; + } + + parameters = value->GetDictionary(); + } + + callback->onComplete(host->ExecuteDevToolsMethod(0, method, parameters)); +} + void OnAfterParentChanged(CefRefPtr browser) { if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce(&OnAfterParentChanged, browser)); @@ -1138,6 +1161,16 @@ CefPdfPrintSettings GetJNIPdfPrintSettings(JNIEnv* env, jobject obj) { return settings; } +// JNI CefRegistration object. +class ScopedJNIRegistration : public ScopedJNIObject { + public: + ScopedJNIRegistration(JNIEnv* env, CefRefPtr obj) + : ScopedJNIObject(env, + obj, + "org/cef/misc/CefRegistration_N", + "CefRegistration") {} +}; + } // namespace JNIEXPORT jboolean JNICALL @@ -1183,6 +1216,53 @@ Java_org_cef_browser_CefBrowser_1N_N_1CreateDevTools(JNIEnv* env, return JNI_FALSE; // set asynchronously } +JNIEXPORT void JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1ExecuteDevToolsMethod( + JNIEnv* env, + jobject jbrowser, + jstring method, + jstring parametersAsJson, + jobject jcallback) { + CefRefPtr callback = + new DevToolsMethodCallback(env, jcallback); + + CefRefPtr browser = GetJNIBrowser(env, jbrowser); + if (!browser.get()) { + callback->onComplete(0); + return; + } + + CefString strMethod = GetJNIString(env, method); + CefString strParametersAsJson = GetJNIString(env, parametersAsJson); + + if (CefCurrentlyOn(TID_UI)) { + executeDevToolsMethod(browser->GetHost(), strMethod, strParametersAsJson, + callback); + } else { + CefPostTask(TID_UI, + base::BindOnce(executeDevToolsMethod, browser->GetHost(), + strMethod, strParametersAsJson, callback)); + } +} + +JNIEXPORT jobject JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1AddDevToolsMessageObserver( + JNIEnv* env, + jobject jbrowser, + jobject jobserver) { + CefRefPtr browser = + JNI_GET_BROWSER_OR_RETURN(env, jbrowser, NULL); + + CefRefPtr observer = + new DevToolsMessageObserver(env, jobserver); + + CefRefPtr registration = + browser->GetHost()->AddDevToolsMessageObserver(observer); + + ScopedJNIRegistration jregistration(env, registration); + return jregistration.Release(); +} + JNIEXPORT jlong JNICALL Java_org_cef_browser_CefBrowser_1N_N_1GetWindowHandle(JNIEnv* env, jobject obj, diff --git a/native/CefBrowser_N.h b/native/CefBrowser_N.h index 9e03d8ca..85176735 100644 --- a/native/CefBrowser_N.h +++ b/native/CefBrowser_N.h @@ -41,6 +41,30 @@ Java_org_cef_browser_CefBrowser_1N_N_1CreateDevTools(JNIEnv*, jobject, jobject); +/* + * Class: org_cef_browser_CefBrowser_N + * Method: N_ExecuteDevToolsMethod + * Signature: + * (Ljava/lang/String;Ljava/lang/String;Lorg/cef/browser/CefBrowser_N/DevToolsMethodCallback;)V + */ +JNIEXPORT void JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1ExecuteDevToolsMethod(JNIEnv*, + jobject, + jstring, + jstring, + jobject); + +/* + * Class: org_cef_browser_CefBrowser_N + * Method: N_AddDevToolsMessageObserver + * Signature: + * (Lorg/cef/browser/CefDevToolsMessageObserver;)Lorg/cef/browser/CefRegistration; + */ +JNIEXPORT jobject JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1AddDevToolsMessageObserver(JNIEnv*, + jobject, + jobject); + /* * Class: org_cef_browser_CefBrowser_N * Method: N_GetWindowHandle @@ -348,7 +372,7 @@ Java_org_cef_browser_CefBrowser_1N_N_1PrintToPDF(JNIEnv*, /* * Class: org_cef_browser_CefBrowser_N * Method: N_Find - * Signature: (ILjava/lang/String;ZZZ)V + * Signature: (Ljava/lang/String;ZZZ)V */ JNIEXPORT void JNICALL Java_org_cef_browser_CefBrowser_1N_N_1Find(JNIEnv*, jobject, @@ -524,3 +548,14 @@ Java_org_cef_browser_CefBrowser_1N_N_1NotifyMoveOrResizeStarted(JNIEnv*, } #endif #endif +/* Header for class org_cef_browser_CefBrowser_N_DevToolsMethodCallback */ + +#ifndef _Included_org_cef_browser_CefBrowser_N_DevToolsMethodCallback +#define _Included_org_cef_browser_CefBrowser_N_DevToolsMethodCallback +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/CefFileDialogCallback_N.h b/native/CefFileDialogCallback_N.h index 1f11dae9..1da3ded8 100644 --- a/native/CefFileDialogCallback_N.h +++ b/native/CefFileDialogCallback_N.h @@ -10,13 +10,12 @@ extern "C" { /* * Class: org_cef_callback_CefFileDialogCallback_N * Method: N_Continue - * Signature: (JILjava/util/Vector;)V + * Signature: (JLjava/util/Vector;)V */ JNIEXPORT void JNICALL Java_org_cef_callback_CefFileDialogCallback_1N_N_1Continue(JNIEnv*, jobject, jlong, - jint, jobject); /* diff --git a/native/CefRegistration_N.cpp b/native/CefRegistration_N.cpp new file mode 100644 index 00000000..295002d3 --- /dev/null +++ b/native/CefRegistration_N.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2024 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 "CefRegistration_N.h" +#include "include/cef_registration.h" +#include "jni_scoped_helpers.h" + +JNIEXPORT void JNICALL +Java_org_cef_misc_CefRegistration_1N_N_1Dispose(JNIEnv* env, + jobject obj, + jlong self) { + SetCefForJNIObject(env, obj, NULL, "CefRegistration"); +} diff --git a/native/CefRegistration_N.h b/native/CefRegistration_N.h new file mode 100644 index 00000000..3c48c7bc --- /dev/null +++ b/native/CefRegistration_N.h @@ -0,0 +1,22 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_cef_misc_CefRegistration_N */ + +#ifndef _Included_org_cef_misc_CefRegistration_N +#define _Included_org_cef_misc_CefRegistration_N +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_cef_misc_CefRegistration_N + * Method: N_Dispose + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_cef_misc_CefRegistration_1N_N_1Dispose(JNIEnv*, + jobject, + jlong); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/devtools_message_observer.cpp b/native/devtools_message_observer.cpp new file mode 100644 index 00000000..84a47fbf --- /dev/null +++ b/native/devtools_message_observer.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2024 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 "devtools_message_observer.h" + +#include "jni_util.h" + +DevToolsMessageObserver::DevToolsMessageObserver(JNIEnv* env, jobject observer) + : handle_(env, observer) {} + +void DevToolsMessageObserver::OnDevToolsMethodResult( + CefRefPtr browser, + int message_id, + bool success, + const void* result, + size_t result_size) { + ScopedJNIEnv env; + if (!env) + return; + + ScopedJNIBrowser jbrowser(env, browser); + std::string strResult(static_cast(result), result_size); + JNI_CALL_VOID_METHOD( + env, handle_, "onDevToolsMethodResult", + "(Lorg/cef/browser/CefBrowser;IZLjava/lang/String;)V", jbrowser.get(), + message_id, success ? JNI_TRUE : JNI_FALSE, NewJNIString(env, strResult)); +} + +void DevToolsMessageObserver::OnDevToolsEvent(CefRefPtr browser, + const CefString& method, + const void* params, + size_t params_size) { + ScopedJNIEnv env; + if (!env) + return; + ScopedJNIBrowser jbrowser(env, browser); + + std::string strParams(static_cast(params), params_size); + JNI_CALL_VOID_METHOD( + env, handle_, "onDevToolsEvent", + "(Lorg/cef/browser/CefBrowser;Ljava/lang/String;Ljava/lang/String;)V", + jbrowser.get(), NewJNIString(env, method), NewJNIString(env, strParams)); +} diff --git a/native/devtools_message_observer.h b/native/devtools_message_observer.h new file mode 100644 index 00000000..19e34bb9 --- /dev/null +++ b/native/devtools_message_observer.h @@ -0,0 +1,37 @@ +// Copyright (c) 2024 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. + +#ifndef JCEF_NATIVE_DEVTOOLS_MESSAGE_OBSERVER_H_ +#define JCEF_NATIVE_DEVTOOLS_MESSAGE_OBSERVER_H_ +#pragma once + +#include +#include "include/cef_devtools_message_observer.h" + +#include "jni_scoped_helpers.h" + +// DevToolsMessageObserver implementation. +class DevToolsMessageObserver : public CefDevToolsMessageObserver { + public: + DevToolsMessageObserver(JNIEnv* env, jobject observer); + + // CefDevToolsMessageObserver methods + virtual void OnDevToolsMethodResult(CefRefPtr browser, + int message_id, + bool success, + const void* result, + size_t result_size) override; + virtual void OnDevToolsEvent(CefRefPtr browser, + const CefString& method, + const void* params, + size_t params_size) override; + + protected: + ScopedJNIObjectGlobal handle_; + + // Include the default reference counting implementation. + IMPLEMENT_REFCOUNTING(DevToolsMessageObserver); +}; + +#endif // JCEF_NATIVE_DEVTOOLS_MESSAGE_OBSERVER_H_ diff --git a/native/devtools_method_callback.cpp b/native/devtools_method_callback.cpp new file mode 100644 index 00000000..b8057d52 --- /dev/null +++ b/native/devtools_method_callback.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2024 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 "devtools_method_callback.h" + +#include "jni_scoped_helpers.h" +#include "jni_util.h" +#include "util.h" + +DevToolsMethodCallback::DevToolsMethodCallback(JNIEnv* env, jobject jcallback) + : handle_(env, jcallback) {} + +void DevToolsMethodCallback::onComplete(int generatedMessageId) { + ScopedJNIEnv env; + if (!env) + return; + + JNI_CALL_VOID_METHOD(env, handle_, "onComplete", "(I)V", + (jint)generatedMessageId); +} diff --git a/native/devtools_method_callback.h b/native/devtools_method_callback.h new file mode 100644 index 00000000..1c219f90 --- /dev/null +++ b/native/devtools_method_callback.h @@ -0,0 +1,31 @@ +// Copyright (c) 2024 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. + +#ifndef JCEF_NATIVE_DEVTOOLS_METHOD_CALLBACK_H_ +#define JCEF_NATIVE_DEVTOOLS_METHOD_CALLBACK_H_ +#pragma once + +#include + +#include "jni_scoped_helpers.h" + +// Callback for CefBrowserHost::ExecuteDevToolsMethod. The methods of +// this class will be called on the browser process UI thread. +class DevToolsMethodCallback : public virtual CefBaseRefCounted { + public: + DevToolsMethodCallback(JNIEnv* env, jobject jcallback); + + // Method that will be called when the DevTools method has been executed. + // |generatedMessageId| is the generated message ID, or 0 if an error + // occurred. + virtual void onComplete(int generatedMessageId); + + protected: + ScopedJNIObjectGlobal handle_; + + // Include the default reference counting implementation. + IMPLEMENT_REFCOUNTING(DevToolsMethodCallback); +}; + +#endif // JCEF_NATIVE_DEVTOOLS_METHOD_CALLBACK_H_ diff --git a/tools/make_all_jni_headers.bat b/tools/make_all_jni_headers.bat index 3f101d3a..9c463161 100644 --- a/tools/make_all_jni_headers.bat +++ b/tools/make_all_jni_headers.bat @@ -37,6 +37,7 @@ call make_jni_header.bat %1 org.cef.callback.CefQueryCallback_N call make_jni_header.bat %1 org.cef.callback.CefSchemeRegistrar_N call make_jni_header.bat %1 org.cef.handler.CefClientHandler call make_jni_header.bat %1 org.cef.misc.CefPrintSettings_N +call make_jni_header.bat %1 org.cef.misc.CefRegistration_N call make_jni_header.bat %1 org.cef.network.CefCookieManager_N call make_jni_header.bat %1 org.cef.network.CefPostData_N call make_jni_header.bat %1 org.cef.network.CefPostDataElement_N From ade64c361b98671924d198e958e2cde4976e55d3 Mon Sep 17 00:00:00 2001 From: Osiris Team Date: Thu, 11 Jan 2024 16:59:07 +0000 Subject: [PATCH 06/11] Add rendered image callback for CefBrowserOsr/CefRenderer (fixes #432) --- java/org/cef/SystemBootstrap.java | 4 +- java/org/cef/browser/CefBrowserOsr.java | 28 ++++++++++ java/org/cef/browser/CefPaintEvent.java | 51 +++++++++++++++++++ .../org/cef/handler/CefAppHandlerAdapter.java | 7 +-- java/org/cef/handler/CefRenderHandler.java | 20 ++++++++ 5 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 java/org/cef/browser/CefPaintEvent.java diff --git a/java/org/cef/SystemBootstrap.java b/java/org/cef/SystemBootstrap.java index 0a217ac4..342aaa82 100644 --- a/java/org/cef/SystemBootstrap.java +++ b/java/org/cef/SystemBootstrap.java @@ -13,7 +13,9 @@ public class SystemBootstrap { /** * Simple interface for how a library by name should be loaded. */ - static public interface Loader { public void loadLibrary(String libname); } + static public interface Loader { + public void loadLibrary(String libname); + } /** * Default implementation is to call System.loadLibrary diff --git a/java/org/cef/browser/CefBrowserOsr.java b/java/org/cef/browser/CefBrowserOsr.java index 902bc862..506f6e63 100644 --- a/java/org/cef/browser/CefBrowserOsr.java +++ b/java/org/cef/browser/CefBrowserOsr.java @@ -60,9 +60,11 @@ import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; import javax.swing.MenuSelectionManager; import javax.swing.SwingUtilities; @@ -84,6 +86,9 @@ class CefBrowserOsr extends CefBrowser_N implements CefRenderHandler { private int depth_per_component = 8; private boolean isTransparent_; + private CopyOnWriteArrayList> onPaintListeners = + new CopyOnWriteArrayList<>(); + CefBrowserOsr(CefClient client, String url, boolean transparent, CefRequestContext context) { this(client, url, transparent, context, null, null); } @@ -354,6 +359,22 @@ public void onPopupSize(CefBrowser browser, Rectangle size) { renderer_.onPopupSize(size); } + @Override + public void addOnPaintListener(Consumer listener) { + onPaintListeners.add(listener); + } + + @Override + public void setOnPaintListener(Consumer listener) { + onPaintListeners.clear(); + onPaintListeners.add(listener); + } + + @Override + public void removeOnPaintListener(Consumer listener) { + onPaintListeners.remove(listener); + } + @Override public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height) { @@ -376,6 +397,13 @@ public void run() { canvas_.display(); } }); + if (!onPaintListeners.isEmpty()) { + CefPaintEvent paintEvent = + new CefPaintEvent(browser, popup, dirtyRects, buffer, width, height); + for (Consumer l : onPaintListeners) { + l.accept(paintEvent); + } + } } @Override diff --git a/java/org/cef/browser/CefPaintEvent.java b/java/org/cef/browser/CefPaintEvent.java new file mode 100644 index 00000000..aed9af31 --- /dev/null +++ b/java/org/cef/browser/CefPaintEvent.java @@ -0,0 +1,51 @@ +// Copyright (c) 2024 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 java.awt.*; +import java.nio.ByteBuffer; + +public class CefPaintEvent { + private final CefBrowser browser; + private final boolean popup; + private final Rectangle[] dirtyRects; + private final ByteBuffer renderedFrame; + private final int width; + private final int height; + + public CefPaintEvent(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, + ByteBuffer renderedFrame, int width, int height) { + this.browser = browser; + this.popup = popup; + this.dirtyRects = dirtyRects; + this.renderedFrame = renderedFrame; + this.width = width; + this.height = height; + } + + public CefBrowser getBrowser() { + return browser; + } + + public boolean getPopup() { + return popup; + } + + public Rectangle[] getDirtyRects() { + return dirtyRects; + } + + public ByteBuffer getRenderedFrame() { + return renderedFrame; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } +} \ No newline at end of file diff --git a/java/org/cef/handler/CefAppHandlerAdapter.java b/java/org/cef/handler/CefAppHandlerAdapter.java index d74eee48..47f7ae53 100644 --- a/java/org/cef/handler/CefAppHandlerAdapter.java +++ b/java/org/cef/handler/CefAppHandlerAdapter.java @@ -32,9 +32,10 @@ public void onBeforeCommandLineProcessing(String process_type, CefCommandLine co continue; } // Arguments with '--', '-' and, on Windows, '/' prefixes are considered switches. - int switchCnt = arg.startsWith("--") - ? 2 - : arg.startsWith("/") ? 1 : arg.startsWith("-") ? 1 : 0; + int switchCnt = arg.startsWith("--") ? 2 + : arg.startsWith("/") ? 1 + : arg.startsWith("-") ? 1 + : 0; switch (switchCnt) { case 2: // An argument of "--" will terminate switch parsing with all subsequent diff --git a/java/org/cef/handler/CefRenderHandler.java b/java/org/cef/handler/CefRenderHandler.java index 0a94bb2f..258df884 100644 --- a/java/org/cef/handler/CefRenderHandler.java +++ b/java/org/cef/handler/CefRenderHandler.java @@ -5,11 +5,13 @@ package org.cef.handler; import org.cef.browser.CefBrowser; +import org.cef.browser.CefPaintEvent; import org.cef.callback.CefDragData; import java.awt.Point; import java.awt.Rectangle; import java.nio.ByteBuffer; +import java.util.function.Consumer; /** * Implement this interface to handle events when window rendering is disabled. @@ -65,6 +67,24 @@ public interface CefRenderHandler { public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height); + /** + * Add provided listener. + * @param listener Code that gets executed after a frame was rendered. + */ + public void addOnPaintListener(Consumer listener); + + /** + * Remove existing listeners and replace with provided listener. + * @param listener Code that gets executed after a frame was rendered. + */ + public void setOnPaintListener(Consumer listener); + + /** + * Remove provided listener. + * @param listener Code that gets executed after a frame was rendered. + */ + public void removeOnPaintListener(Consumer listener); + /** * Handle cursor changes. * @param browser The browser generating the event. From 423f5b8e9663c6718f954a9be5154a1e1be3a8fb Mon Sep 17 00:00:00 2001 From: Filip Date: Thu, 11 Jan 2024 18:25:25 +0000 Subject: [PATCH 07/11] Add CefBrowserSettings.windowless_frame_rate (see #459) Also adds methods to dynamically set/get the frame rate and fixes some pre-existing compile issues. --- java/org/cef/CefBrowserSettings.java | 31 +++++++++ java/org/cef/CefClient.java | 27 ++++++-- java/org/cef/browser/CefBrowser.java | 22 ++++++ java/org/cef/browser/CefBrowserFactory.java | 8 ++- java/org/cef/browser/CefBrowserOsr.java | 13 ++-- java/org/cef/browser/CefBrowserWr.java | 24 +++++-- java/org/cef/browser/CefBrowser_N.java | 43 +++++++++--- java/tests/detailed/MainFrame.java | 28 ++++++-- java/tests/detailed/ui/MenuBar.java | 2 +- native/CMakeLists.txt | 4 +- native/CefBrowser_N.cpp | 68 ++++++++++++++++--- native/CefBrowser_N.h | 25 ++++++- native/devtools_method_callback.h | 31 --------- ...s_method_callback.cpp => int_callback.cpp} | 9 ++- native/int_callback.h | 28 ++++++++ 15 files changed, 279 insertions(+), 84 deletions(-) create mode 100644 java/org/cef/CefBrowserSettings.java delete mode 100644 native/devtools_method_callback.h rename native/{devtools_method_callback.cpp => int_callback.cpp} (54%) create mode 100644 native/int_callback.h diff --git a/java/org/cef/CefBrowserSettings.java b/java/org/cef/CefBrowserSettings.java new file mode 100644 index 00000000..8733a418 --- /dev/null +++ b/java/org/cef/CefBrowserSettings.java @@ -0,0 +1,31 @@ +// Copyright (c) 2024 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 initialization settings. Specify NULL or 0 to get the recommended + * default values. The consequences of using custom values may not be well + * tested. Many of these and other settings can also configured using command- + * line switches. + */ +public class CefBrowserSettings { + /** + * The maximum rate in frames per second (fps) that CefRenderHandler::OnPaint + * will be called for a windowless browser. The actual fps may be lower if + * the browser cannot generate frames at the requested rate. The minimum + * value is 1 and the maximum value is 60 (default 30). This value can also + * be changed dynamically via {@code CefBrowser#setWindowlessFrameRate} + */ + public int windowless_frame_rate = 0; + + public CefBrowserSettings() {} + + @Override + public CefBrowserSettings clone() { + CefBrowserSettings tmp = new CefBrowserSettings(); + tmp.windowless_frame_rate = windowless_frame_rate; + return tmp; + } +} diff --git a/java/org/cef/CefClient.java b/java/org/cef/CefClient.java index 7f87bc5c..90122936 100644 --- a/java/org/cef/CefClient.java +++ b/java/org/cef/CefClient.java @@ -4,11 +4,7 @@ package org.cef; -import org.cef.browser.CefBrowser; -import org.cef.browser.CefBrowserFactory; -import org.cef.browser.CefFrame; -import org.cef.browser.CefMessageRouter; -import org.cef.browser.CefRequestContext; +import org.cef.browser.*; import org.cef.callback.CefAuthCallback; import org.cef.callback.CefBeforeDownloadCallback; import org.cef.callback.CefCallback; @@ -60,6 +56,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Vector; +import java.util.function.Consumer; import javax.swing.SwingUtilities; @@ -141,7 +138,16 @@ public CefBrowser createBrowser(String url, boolean isOffscreenRendered, boolean CefRequestContext context) { if (isDisposed_) throw new IllegalStateException("Can't create browser. CefClient is disposed"); - return CefBrowserFactory.create(this, url, isOffscreenRendered, isTransparent, context); + return CefBrowserFactory.create( + this, url, isOffscreenRendered, isTransparent, context, null); + } + + public CefBrowser createBrowser(String url, boolean isOffscreenRendered, boolean isTransparent, + CefRequestContext context, CefBrowserSettings settings) { + if (isDisposed_) + throw new IllegalStateException("Can't create browser. CefClient is disposed"); + return CefBrowserFactory.create( + this, url, isOffscreenRendered, isTransparent, context, settings); } @Override @@ -753,6 +759,15 @@ public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, realHandler.onPaint(browser, popup, dirtyRects, buffer, width, height); } + @Override + public void addOnPaintListener(Consumer listener) {} + + @Override + public void setOnPaintListener(Consumer listener) {} + + @Override + public void removeOnPaintListener(Consumer listener) {} + @Override public boolean startDragging(CefBrowser browser, CefDragData dragData, int mask, int x, int y) { if (browser == null) return false; diff --git a/java/org/cef/browser/CefBrowser.java b/java/org/cef/browser/CefBrowser.java index de0b2268..f6c72ee5 100644 --- a/java/org/cef/browser/CefBrowser.java +++ b/java/org/cef/browser/CefBrowser.java @@ -390,4 +390,26 @@ public void runFileDialog(FileDialogMode mode, String title, String defaultFileP * @throws UnsupportedOperationException if not supported */ public CompletableFuture createScreenshot(boolean nativeResolution); + + /** + * Set the maximum rate in frames per second (fps) that {@code CefRenderHandler::onPaint} + * will be called for a windowless browser. The actual fps may be + * lower if the browser cannot generate frames at the requested rate. The + * minimum value is 1, and the maximum value is 60 (default 30). + * + * @param frameRate the maximum frame rate + * @throws UnsupportedOperationException if not supported + */ + public void setWindowlessFrameRate(int frameRate); + + /** + * Returns the maximum rate in frames per second (fps) that {@code CefRenderHandler::onPaint} + * will be called for a windowless browser. The actual fps may be lower if the browser cannot + * generate frames at the requested rate. The minimum value is 1, and the maximum value is 60 + * (default 30). + * + * @return the framerate, 0 if an error occurs + * @throws UnsupportedOperationException if not supported + */ + public CompletableFuture getWindowlessFrameRate(); } diff --git a/java/org/cef/browser/CefBrowserFactory.java b/java/org/cef/browser/CefBrowserFactory.java index 708d0f24..299dfcfb 100644 --- a/java/org/cef/browser/CefBrowserFactory.java +++ b/java/org/cef/browser/CefBrowserFactory.java @@ -4,6 +4,7 @@ package org.cef.browser; +import org.cef.CefBrowserSettings; import org.cef.CefClient; /** @@ -11,8 +12,9 @@ */ public class CefBrowserFactory { public static CefBrowser create(CefClient client, String url, boolean isOffscreenRendered, - boolean isTransparent, CefRequestContext context) { - if (isOffscreenRendered) return new CefBrowserOsr(client, url, isTransparent, context); - return new CefBrowserWr(client, url, context); + boolean isTransparent, CefRequestContext context, CefBrowserSettings settings) { + if (isOffscreenRendered) + return new CefBrowserOsr(client, url, isTransparent, context, settings); + return new CefBrowserWr(client, url, context, settings); } } diff --git a/java/org/cef/browser/CefBrowserOsr.java b/java/org/cef/browser/CefBrowserOsr.java index 506f6e63..b999b6ba 100644 --- a/java/org/cef/browser/CefBrowserOsr.java +++ b/java/org/cef/browser/CefBrowserOsr.java @@ -15,6 +15,7 @@ import com.jogamp.opengl.awt.GLCanvas; import com.jogamp.opengl.util.GLBuffers; +import org.cef.CefBrowserSettings; import org.cef.CefClient; import org.cef.OS; import org.cef.callback.CefDragData; @@ -89,13 +90,15 @@ class CefBrowserOsr extends CefBrowser_N implements CefRenderHandler { private CopyOnWriteArrayList> onPaintListeners = new CopyOnWriteArrayList<>(); - CefBrowserOsr(CefClient client, String url, boolean transparent, CefRequestContext context) { - this(client, url, transparent, context, null, null); + CefBrowserOsr(CefClient client, String url, boolean transparent, CefRequestContext context, + CefBrowserSettings settings) { + this(client, url, transparent, context, null, null, settings); } private CefBrowserOsr(CefClient client, String url, boolean transparent, - CefRequestContext context, CefBrowserOsr parent, Point inspectAt) { - super(client, url, context, parent, inspectAt); + CefRequestContext context, CefBrowserOsr parent, Point inspectAt, + CefBrowserSettings settings) { + super(client, url, context, parent, inspectAt, settings); isTransparent_ = transparent; renderer_ = new CefRenderer(transparent); createGLCanvas(); @@ -122,7 +125,7 @@ public CefRenderHandler getRenderHandler() { protected CefBrowser_N createDevToolsBrowser(CefClient client, String url, CefRequestContext context, CefBrowser_N parent, Point inspectAt) { return new CefBrowserOsr( - client, url, isTransparent_, context, (CefBrowserOsr) this, inspectAt); + client, url, isTransparent_, context, (CefBrowserOsr) this, inspectAt, null); } private synchronized long getWindowHandle() { diff --git a/java/org/cef/browser/CefBrowserWr.java b/java/org/cef/browser/CefBrowserWr.java index 583c4f41..677ddce7 100644 --- a/java/org/cef/browser/CefBrowserWr.java +++ b/java/org/cef/browser/CefBrowserWr.java @@ -4,6 +4,7 @@ package org.cef.browser; +import org.cef.CefBrowserSettings; import org.cef.CefClient; import org.cef.OS; import org.cef.handler.CefWindowHandler; @@ -166,14 +167,15 @@ public int getClickCount(int event, int button) { } }; - CefBrowserWr(CefClient client, String url, CefRequestContext context) { - this(client, url, context, null, null); + 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) { - super(client, url, context, parent, inspectAt); + CefBrowserWr parent, Point inspectAt, CefBrowserSettings settings) { + super(client, url, context, parent, inspectAt, settings); delayedUpdate_.setRepeats(false); // Disabling lightweight of popup menu is required because @@ -318,7 +320,7 @@ public CefWindowHandler getWindowHandler() { @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); + return new CefBrowserWr(client, url, context, (CefBrowserWr) this, inspectAt, null); } private synchronized long getWindowHandle() { @@ -421,4 +423,16 @@ private boolean createBrowserIfRequired(boolean hasParent) { public CompletableFuture 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 getWindowlessFrameRate() { + throw new UnsupportedOperationException( + "You can only get windowless framerate on OSR browser"); + } } diff --git a/java/org/cef/browser/CefBrowser_N.java b/java/org/cef/browser/CefBrowser_N.java index a5685bc5..d7c62e0f 100644 --- a/java/org/cef/browser/CefBrowser_N.java +++ b/java/org/cef/browser/CefBrowser_N.java @@ -4,6 +4,7 @@ package org.cef.browser; +import org.cef.CefBrowserSettings; import org.cef.CefClient; import org.cef.browser.CefDevToolsClient.DevToolsException; import org.cef.callback.CefDragData; @@ -49,14 +50,19 @@ abstract class CefBrowser_N extends CefNativeAdapter implements CefBrowser { private boolean closeAllowed_ = false; private volatile boolean isClosed_ = false; private volatile boolean isClosing_ = false; + private final CefBrowserSettings settings_; protected CefBrowser_N(CefClient client, String url, CefRequestContext context, - CefBrowser_N parent, Point inspectAt) { + CefBrowser_N parent, Point inspectAt, CefBrowserSettings settings) { client_ = client; url_ = url; request_context_ = context; parent_ = parent; inspectAt_ = inspectAt; + if (settings != null) + settings_ = settings.clone(); + else + settings_ = new CefBrowserSettings(); } protected String getUrl() { @@ -162,7 +168,7 @@ public synchronized CefDevToolsClient getDevToolsClient() { CompletableFuture executeDevToolsMethod(String method, String parametersAsJson) { CompletableFuture future = new CompletableFuture<>(); - N_ExecuteDevToolsMethod(method, parametersAsJson, new DevToolsMethodCallback() { + N_ExecuteDevToolsMethod(method, parametersAsJson, new IntCallback() { @Override public void onComplete(int generatedMessageId) { if (generatedMessageId <= 0) { @@ -190,8 +196,8 @@ protected void createBrowser(CefClientHandler clientHandler, long windowHandle, boolean osr, boolean transparent, Component canvas, CefRequestContext context) { if (getNativeRef("CefBrowser") == 0 && !isPending_) { try { - N_CreateBrowser( - clientHandler, windowHandle, url, osr, transparent, canvas, context); + N_CreateBrowser(clientHandler, windowHandle, url, osr, transparent, canvas, context, + settings_); } catch (UnsatisfiedLinkError err) { err.printStackTrace(); } @@ -789,17 +795,36 @@ protected final void notifyMoveOrResizeStarted() { } } - private interface DevToolsMethodCallback { - void onComplete(int generatedMessageId); + public void setWindowlessFrameRate(int frameRate) { + try { + N_SetWindowlessFrameRate(frameRate); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + + public CompletableFuture getWindowlessFrameRate() { + final CompletableFuture future = new CompletableFuture<>(); + try { + N_GetWindowlessFrameRate(future::complete); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + future.complete(0); + } + return future; + } + + private interface IntCallback { + void onComplete(int value); } private final native boolean N_CreateBrowser(CefClientHandler clientHandler, long windowHandle, String url, boolean osr, boolean transparent, Component canvas, - CefRequestContext context); + CefRequestContext context, CefBrowserSettings settings); private final native boolean N_CreateDevTools(CefBrowser parent, CefClientHandler clientHandler, long windowHandle, boolean osr, boolean transparent, Component canvas, Point inspectAt); private final native void N_ExecuteDevToolsMethod( - String method, String parametersAsJson, DevToolsMethodCallback callback); + String method, String parametersAsJson, IntCallback callback); private final native CefRegistration N_AddDevToolsMessageObserver( CefDevToolsMessageObserver observer); private final native long N_GetWindowHandle(long surfaceHandle); @@ -860,4 +885,6 @@ private final native void N_DragTargetDragEnter( private final native void N_UpdateUI(Rectangle contentRect, Rectangle browserRect); private final native void N_SetParent(long windowHandle, Component canvas); private final native void N_NotifyMoveOrResizeStarted(); + private final native void N_SetWindowlessFrameRate(int frameRate); + private final native void N_GetWindowlessFrameRate(IntCallback frameRateCallback); } diff --git a/java/tests/detailed/MainFrame.java b/java/tests/detailed/MainFrame.java index f13b321d..85eb3cdb 100644 --- a/java/tests/detailed/MainFrame.java +++ b/java/tests/detailed/MainFrame.java @@ -6,6 +6,7 @@ import org.cef.CefApp; import org.cef.CefApp.CefVersion; +import org.cef.CefBrowserSettings; import org.cef.CefClient; import org.cef.CefSettings; import org.cef.CefSettings.ColorType; @@ -58,6 +59,7 @@ public static void main(String[] args) { boolean osrEnabledArg = false; boolean transparentPaintingEnabledArg = false; boolean createImmediately = false; + int windowless_frame_rate = 0; for (String arg : args) { arg = arg.toLowerCase(); if (arg.equals("--off-screen-rendering-enabled")) { @@ -66,6 +68,8 @@ public static void main(String[] args) { transparentPaintingEnabledArg = true; } else if (arg.equals("--create-immediately")) { createImmediately = true; + } else if (arg.equals("--windowless-frame-rate-60")) { + windowless_frame_rate = 60; } } @@ -73,10 +77,21 @@ public static void main(String[] args) { // MainFrame keeps all the knowledge to display the embedded browser // frame. - final MainFrame frame = new MainFrame( - osrEnabledArg, transparentPaintingEnabledArg, createImmediately, args); + final MainFrame frame = new MainFrame(osrEnabledArg, transparentPaintingEnabledArg, + createImmediately, windowless_frame_rate, args); frame.setSize(800, 600); frame.setVisible(true); + + if (osrEnabledArg && windowless_frame_rate != 0) { + frame.getBrowser().getWindowlessFrameRate().thenAccept( + framerate -> System.out.println("Framerate is:" + framerate)); + + frame.getBrowser().setWindowlessFrameRate(2); + frame.getBrowser().getWindowlessFrameRate().thenAccept( + framerate -> System.out.println("Framerate is:" + framerate)); + + frame.getBrowser().setWindowlessFrameRate(windowless_frame_rate); + } } private final CefClient client_; @@ -88,7 +103,7 @@ public static void main(String[] args) { private boolean transparent_painting_enabled_; public MainFrame(boolean osrEnabled, boolean transparentPaintingEnabled, - boolean createImmediately, String[] args) { + boolean createImmediately, int windowless_frame_rate, String[] args) { this.osr_enabled_ = osrEnabled; this.transparent_painting_enabled_ = transparentPaintingEnabled; @@ -200,9 +215,12 @@ public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, } }); + CefBrowserSettings browserSettings = new CefBrowserSettings(); + browserSettings.windowless_frame_rate = windowless_frame_rate; + // Create the browser. - CefBrowser browser = client_.createBrowser( - "http://www.google.com", osrEnabled, transparentPaintingEnabled, null); + CefBrowser browser = client_.createBrowser("http://www.google.com", osrEnabled, + transparentPaintingEnabled, null, browserSettings); setBrowser(browser); // Set up the UI for this example implementation. diff --git a/java/tests/detailed/ui/MenuBar.java b/java/tests/detailed/ui/MenuBar.java index 850e2104..0ed8bf1a 100644 --- a/java/tests/detailed/ui/MenuBar.java +++ b/java/tests/detailed/ui/MenuBar.java @@ -470,7 +470,7 @@ public void run() { newwindow.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - final MainFrame frame = new MainFrame(OS.isLinux(), false, false, null); + final MainFrame frame = new MainFrame(OS.isLinux(), false, false, 0, null); frame.setSize(800, 600); frame.setVisible(true); } diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index e3635be2..257bd60a 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -87,8 +87,6 @@ set(JCEF_SRCS critical_wait.h devtools_message_observer.cpp devtools_message_observer.h - devtools_method_callback.cpp - devtools_method_callback.h dialog_handler.cpp dialog_handler.h display_handler.cpp @@ -99,6 +97,8 @@ set(JCEF_SRCS drag_handler.h focus_handler.cpp focus_handler.h + int_callback.cpp + int_callback.h jcef_version.h jni_scoped_helpers.h jni_scoped_helpers.cpp diff --git a/native/CefBrowser_N.cpp b/native/CefBrowser_N.cpp index c754c89a..92c43636 100644 --- a/native/CefBrowser_N.cpp +++ b/native/CefBrowser_N.cpp @@ -14,7 +14,7 @@ #include "client_handler.h" #include "critical_wait.h" #include "devtools_message_observer.h" -#include "devtools_method_callback.h" +#include "int_callback.h" #include "jni_util.h" #include "life_span_handler.h" #include "pdf_print_callback.h" @@ -902,6 +902,7 @@ struct JNIObjectsForCreate { ScopedJNIObjectGlobal canvas; ScopedJNIObjectGlobal jcontext; ScopedJNIObjectGlobal jinspectAt; + ScopedJNIObjectGlobal jbrowserSettings; JNIObjectsForCreate(JNIEnv* env, jobject _jbrowser, @@ -910,7 +911,8 @@ struct JNIObjectsForCreate { jstring _url, jobject _canvas, jobject _jcontext, - jobject _jinspectAt) + jobject _jinspectAt, + jobject _browserSettings) : jbrowser(env, _jbrowser), @@ -919,7 +921,8 @@ struct JNIObjectsForCreate { url(env, _url), canvas(env, _canvas), jcontext(env, _jcontext), - jinspectAt(env, _jinspectAt) {} + jinspectAt(env, _jinspectAt), + jbrowserSettings(env, _browserSettings) {} }; void create(std::shared_ptr objs, @@ -982,6 +985,13 @@ void create(std::shared_ptr objs, settings.background_color = CefColorSetARGB(255, 255, 255, 255); } + ScopedJNIClass cefBrowserSettings(env, "org/cef/CefBrowserSettings"); + if (cefBrowserSettings != nullptr && + objs->jbrowserSettings != nullptr) { // Dev-tools settings are null + GetJNIFieldInt(env, cefBrowserSettings, objs->jbrowserSettings, + "windowless_frame_rate", &settings.windowless_frame_rate); + } + CefRefPtr browserObj; CefString strUrl = GetJNIString(env, static_cast(objs->url.get())); @@ -1041,7 +1051,7 @@ void getZoomLevel(CefRefPtr host, void executeDevToolsMethod(CefRefPtr host, const CefString& method, const CefString& parametersAsJson, - CefRefPtr callback) { + CefRefPtr callback) { CefRefPtr parameters = nullptr; if (!parametersAsJson.empty()) { CefRefPtr value = CefParseJSON( @@ -1167,7 +1177,7 @@ class ScopedJNIRegistration : public ScopedJNIObject { ScopedJNIRegistration(JNIEnv* env, CefRefPtr obj) : ScopedJNIObject(env, obj, - "org/cef/misc/CefRegistration_N", + "org/cef/browser/CefRegistration_N", "CefRegistration") {} }; @@ -1182,9 +1192,11 @@ Java_org_cef_browser_CefBrowser_1N_N_1CreateBrowser(JNIEnv* env, jboolean osr, jboolean transparent, jobject canvas, - jobject jcontext) { - std::shared_ptr objs(new JNIObjectsForCreate( - env, jbrowser, nullptr, jclientHandler, url, canvas, jcontext, nullptr)); + jobject jcontext, + jobject browserSettings) { + std::shared_ptr objs( + new JNIObjectsForCreate(env, jbrowser, nullptr, jclientHandler, url, + canvas, jcontext, nullptr, browserSettings)); if (CefCurrentlyOn(TID_UI)) { create(objs, windowHandle, osr, transparent); } else { @@ -1206,7 +1218,7 @@ Java_org_cef_browser_CefBrowser_1N_N_1CreateDevTools(JNIEnv* env, jobject inspect) { std::shared_ptr objs( new JNIObjectsForCreate(env, jbrowser, jparent, jclientHandler, nullptr, - canvas, nullptr, inspect)); + canvas, nullptr, inspect, nullptr)); if (CefCurrentlyOn(TID_UI)) { create(objs, windowHandle, osr, transparent); } else { @@ -1223,8 +1235,7 @@ Java_org_cef_browser_CefBrowser_1N_N_1ExecuteDevToolsMethod( jstring method, jstring parametersAsJson, jobject jcallback) { - CefRefPtr callback = - new DevToolsMethodCallback(env, jcallback); + CefRefPtr callback = new IntCallback(env, jcallback); CefRefPtr browser = GetJNIBrowser(env, jbrowser); if (!browser.get()) { @@ -2151,3 +2162,38 @@ Java_org_cef_browser_CefBrowser_1N_N_1NotifyMoveOrResizeStarted(JNIEnv* env, } #endif } + +JNIEXPORT void JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1SetWindowlessFrameRate(JNIEnv* env, + jobject jbrowser, + jint frameRate) { + CefRefPtr browser = JNI_GET_BROWSER_OR_RETURN(env, jbrowser); + CefRefPtr host = browser->GetHost(); + host->SetWindowlessFrameRate(frameRate); +} + +void getWindowlessFrameRate(CefRefPtr host, + CefRefPtr callback) { + callback->onComplete((jint)host->GetWindowlessFrameRate()); +} + +JNIEXPORT void JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1GetWindowlessFrameRate( + JNIEnv* env, + jobject jbrowser, + jobject jintCallback) { + CefRefPtr callback = new IntCallback(env, jintCallback); + + CefRefPtr browser = GetJNIBrowser(env, jbrowser); + if (!browser.get()) { + callback->onComplete(0); + return; + } + + CefRefPtr host = browser->GetHost(); + if (CefCurrentlyOn(TID_UI)) { + getWindowlessFrameRate(host, callback); + } else { + CefPostTask(TID_UI, base::BindOnce(getWindowlessFrameRate, host, callback)); + } +} diff --git a/native/CefBrowser_N.h b/native/CefBrowser_N.h index 85176735..ae103a9b 100644 --- a/native/CefBrowser_N.h +++ b/native/CefBrowser_N.h @@ -11,7 +11,7 @@ extern "C" { * Class: org_cef_browser_CefBrowser_N * Method: N_CreateBrowser * Signature: - * (Lorg/cef/handler/CefClientHandler;JLjava/lang/String;ZZLjava/awt/Component;Lorg/cef/browser/CefRequestContext;)Z + * (Lorg/cef/handler/CefClientHandler;JLjava/lang/String;ZZLjava/awt/Component;Lorg/cef/browser/CefRequestContext;Lorg/cef/CefBrowserSettings;)Z */ JNIEXPORT jboolean JNICALL Java_org_cef_browser_CefBrowser_1N_N_1CreateBrowser(JNIEnv*, @@ -22,6 +22,7 @@ Java_org_cef_browser_CefBrowser_1N_N_1CreateBrowser(JNIEnv*, jboolean, jboolean, jobject, + jobject, jobject); /* @@ -45,7 +46,7 @@ Java_org_cef_browser_CefBrowser_1N_N_1CreateDevTools(JNIEnv*, * Class: org_cef_browser_CefBrowser_N * Method: N_ExecuteDevToolsMethod * Signature: - * (Ljava/lang/String;Ljava/lang/String;Lorg/cef/browser/CefBrowser_N/DevToolsMethodCallback;)V + * (Ljava/lang/String;Ljava/lang/String;Lorg/cef/browser/CefBrowser_N/IntCallback;)V */ JNIEXPORT void JNICALL Java_org_cef_browser_CefBrowser_1N_N_1ExecuteDevToolsMethod(JNIEnv*, @@ -544,6 +545,26 @@ JNIEXPORT void JNICALL Java_org_cef_browser_CefBrowser_1N_N_1NotifyMoveOrResizeStarted(JNIEnv*, jobject); +/* + * Class: org_cef_browser_CefBrowser_N + * Method: N_SetWindowlessFrameRate + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1SetWindowlessFrameRate(JNIEnv*, + jobject, + jint); + +/* + * Class: org_cef_browser_CefBrowser_N + * Method: N_GetWindowlessFrameRate + * Signature: (Lorg/cef/browser/CefBrowser_N/IntCallback;)V + */ +JNIEXPORT void JNICALL +Java_org_cef_browser_CefBrowser_1N_N_1GetWindowlessFrameRate(JNIEnv*, + jobject, + jobject); + #ifdef __cplusplus } #endif diff --git a/native/devtools_method_callback.h b/native/devtools_method_callback.h deleted file mode 100644 index 1c219f90..00000000 --- a/native/devtools_method_callback.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2024 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. - -#ifndef JCEF_NATIVE_DEVTOOLS_METHOD_CALLBACK_H_ -#define JCEF_NATIVE_DEVTOOLS_METHOD_CALLBACK_H_ -#pragma once - -#include - -#include "jni_scoped_helpers.h" - -// Callback for CefBrowserHost::ExecuteDevToolsMethod. The methods of -// this class will be called on the browser process UI thread. -class DevToolsMethodCallback : public virtual CefBaseRefCounted { - public: - DevToolsMethodCallback(JNIEnv* env, jobject jcallback); - - // Method that will be called when the DevTools method has been executed. - // |generatedMessageId| is the generated message ID, or 0 if an error - // occurred. - virtual void onComplete(int generatedMessageId); - - protected: - ScopedJNIObjectGlobal handle_; - - // Include the default reference counting implementation. - IMPLEMENT_REFCOUNTING(DevToolsMethodCallback); -}; - -#endif // JCEF_NATIVE_DEVTOOLS_METHOD_CALLBACK_H_ diff --git a/native/devtools_method_callback.cpp b/native/int_callback.cpp similarity index 54% rename from native/devtools_method_callback.cpp rename to native/int_callback.cpp index b8057d52..a45471a4 100644 --- a/native/devtools_method_callback.cpp +++ b/native/int_callback.cpp @@ -2,20 +2,19 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. -#include "devtools_method_callback.h" +#include "int_callback.h" #include "jni_scoped_helpers.h" #include "jni_util.h" #include "util.h" -DevToolsMethodCallback::DevToolsMethodCallback(JNIEnv* env, jobject jcallback) +IntCallback::IntCallback(JNIEnv* env, jobject jcallback) : handle_(env, jcallback) {} -void DevToolsMethodCallback::onComplete(int generatedMessageId) { +void IntCallback::onComplete(int value) { ScopedJNIEnv env; if (!env) return; - JNI_CALL_VOID_METHOD(env, handle_, "onComplete", "(I)V", - (jint)generatedMessageId); + JNI_CALL_VOID_METHOD(env, handle_, "onComplete", "(I)V", (jint)value); } diff --git a/native/int_callback.h b/native/int_callback.h new file mode 100644 index 00000000..6437479c --- /dev/null +++ b/native/int_callback.h @@ -0,0 +1,28 @@ +// Copyright (c) 2024 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. + +#ifndef JCEF_NATIVE_INT_CALLBACK_H_ +#define JCEF_NATIVE_INT_CALLBACK_H_ +#pragma once + +#include + +#include "jni_scoped_helpers.h" + +// Callback for returning int primatives. The methods of +// this class will be called on the browser process UI thread. +class IntCallback : public virtual CefBaseRefCounted { + public: + IntCallback(JNIEnv* env, jobject jcallback); + + virtual void onComplete(int value); + + protected: + ScopedJNIObjectGlobal handle_; + + // Include the default reference counting implementation. + IMPLEMENT_REFCOUNTING(IntCallback); +}; + +#endif // JCEF_NATIVE_INT_CALLBACK_H_ From b5612ba49821008c7e7b1d3bc069f30eb545d39c Mon Sep 17 00:00:00 2001 From: Sander van den Berg Date: Mon, 15 Jan 2024 18:30:13 +0000 Subject: [PATCH 08/11] Fix UnsatisfiedLinkError with CefRegistration_N --- native/CefBrowser_N.h | 6 +++--- native/CefRegistration_N.cpp | 6 +++--- native/CefRegistration_N.h | 13 ++++++------- tools/make_all_jni_headers.bat | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/native/CefBrowser_N.h b/native/CefBrowser_N.h index ae103a9b..f01e1f5a 100644 --- a/native/CefBrowser_N.h +++ b/native/CefBrowser_N.h @@ -569,10 +569,10 @@ Java_org_cef_browser_CefBrowser_1N_N_1GetWindowlessFrameRate(JNIEnv*, } #endif #endif -/* Header for class org_cef_browser_CefBrowser_N_DevToolsMethodCallback */ +/* Header for class org_cef_browser_CefBrowser_N_IntCallback */ -#ifndef _Included_org_cef_browser_CefBrowser_N_DevToolsMethodCallback -#define _Included_org_cef_browser_CefBrowser_N_DevToolsMethodCallback +#ifndef _Included_org_cef_browser_CefBrowser_N_IntCallback +#define _Included_org_cef_browser_CefBrowser_N_IntCallback #ifdef __cplusplus extern "C" { #endif diff --git a/native/CefRegistration_N.cpp b/native/CefRegistration_N.cpp index 295002d3..dc23751d 100644 --- a/native/CefRegistration_N.cpp +++ b/native/CefRegistration_N.cpp @@ -7,8 +7,8 @@ #include "jni_scoped_helpers.h" JNIEXPORT void JNICALL -Java_org_cef_misc_CefRegistration_1N_N_1Dispose(JNIEnv* env, - jobject obj, - jlong self) { +Java_org_cef_browser_CefRegistration_1N_N_1Dispose(JNIEnv* env, + jobject obj, + jlong self) { SetCefForJNIObject(env, obj, NULL, "CefRegistration"); } diff --git a/native/CefRegistration_N.h b/native/CefRegistration_N.h index 3c48c7bc..9747b04b 100644 --- a/native/CefRegistration_N.h +++ b/native/CefRegistration_N.h @@ -1,20 +1,19 @@ /* DO NOT EDIT THIS FILE - it is machine generated */ #include -/* Header for class org_cef_misc_CefRegistration_N */ +/* Header for class org_cef_browser_CefRegistration_N */ -#ifndef _Included_org_cef_misc_CefRegistration_N -#define _Included_org_cef_misc_CefRegistration_N +#ifndef _Included_org_cef_browser_CefRegistration_N +#define _Included_org_cef_browser_CefRegistration_N #ifdef __cplusplus extern "C" { #endif /* - * Class: org_cef_misc_CefRegistration_N + * Class: org_cef_browser_CefRegistration_N * Method: N_Dispose * Signature: (J)V */ -JNIEXPORT void JNICALL Java_org_cef_misc_CefRegistration_1N_N_1Dispose(JNIEnv*, - jobject, - jlong); +JNIEXPORT void JNICALL +Java_org_cef_browser_CefRegistration_1N_N_1Dispose(JNIEnv*, jobject, jlong); #ifdef __cplusplus } diff --git a/tools/make_all_jni_headers.bat b/tools/make_all_jni_headers.bat index 9c463161..ecb1dee0 100644 --- a/tools/make_all_jni_headers.bat +++ b/tools/make_all_jni_headers.bat @@ -37,7 +37,7 @@ call make_jni_header.bat %1 org.cef.callback.CefQueryCallback_N call make_jni_header.bat %1 org.cef.callback.CefSchemeRegistrar_N call make_jni_header.bat %1 org.cef.handler.CefClientHandler call make_jni_header.bat %1 org.cef.misc.CefPrintSettings_N -call make_jni_header.bat %1 org.cef.misc.CefRegistration_N +call make_jni_header.bat %1 org.cef.browser.CefRegistration_N call make_jni_header.bat %1 org.cef.network.CefCookieManager_N call make_jni_header.bat %1 org.cef.network.CefPostData_N call make_jni_header.bat %1 org.cef.network.CefPostDataElement_N From 3df5f071df9a5f080cfa7d606f75189d3850706b Mon Sep 17 00:00:00 2001 From: Milos Ratkovic Date: Fri, 19 Jan 2024 20:42:12 +0000 Subject: [PATCH 09/11] Add CefDisplayHandler.onFullscreenModeChange callback (fixes #239) - Detailed test: Don't display error for frames other than main frame --- java/org/cef/CefClient.java | 6 ++ java/org/cef/handler/CefDisplayHandler.java | 7 ++ .../cef/handler/CefDisplayHandlerAdapter.java | 5 ++ java/tests/detailed/MainFrame.java | 68 ++++++++++++++++--- java/tests/simple/MainFrame.java | 42 +++++++++++- native/display_handler.cpp | 12 ++++ native/display_handler.h | 2 + 7 files changed, 128 insertions(+), 14 deletions(-) diff --git a/java/org/cef/CefClient.java b/java/org/cef/CefClient.java index 90122936..7fa5fdaf 100644 --- a/java/org/cef/CefClient.java +++ b/java/org/cef/CefClient.java @@ -311,6 +311,12 @@ public void onTitleChange(CefBrowser browser, String title) { displayHandler_.onTitleChange(browser, title); } + @Override + public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen) { + if (displayHandler_ != null && browser != null) + displayHandler_.OnFullscreenModeChange(browser, fullscreen); + } + @Override public boolean onTooltip(CefBrowser browser, String text) { if (displayHandler_ != null && browser != null) { diff --git a/java/org/cef/handler/CefDisplayHandler.java b/java/org/cef/handler/CefDisplayHandler.java index 0affefc5..5913794e 100644 --- a/java/org/cef/handler/CefDisplayHandler.java +++ b/java/org/cef/handler/CefDisplayHandler.java @@ -28,6 +28,13 @@ public interface CefDisplayHandler { */ public void onTitleChange(CefBrowser browser, String title); + /** + * Browser fullscreen mode changed. + * @param browser The browser generating the event. + * @param fullscreen True if fullscreen mode is on. + */ + public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen); + /** * About to display a tooltip. * @param browser The browser generating the event. diff --git a/java/org/cef/handler/CefDisplayHandlerAdapter.java b/java/org/cef/handler/CefDisplayHandlerAdapter.java index 80ab96b6..302ae6b0 100644 --- a/java/org/cef/handler/CefDisplayHandlerAdapter.java +++ b/java/org/cef/handler/CefDisplayHandlerAdapter.java @@ -24,6 +24,11 @@ public void onTitleChange(CefBrowser browser, String title) { return; } + @Override + public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen) { + return; + } + @Override public boolean onTooltip(CefBrowser browser, String text) { return false; diff --git a/java/tests/detailed/MainFrame.java b/java/tests/detailed/MainFrame.java index 85eb3cdb..afa9d823 100644 --- a/java/tests/detailed/MainFrame.java +++ b/java/tests/detailed/MainFrame.java @@ -22,6 +22,8 @@ import org.cef.network.CefCookieManager; import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GraphicsConfiguration; import java.awt.KeyboardFocusManager; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; @@ -30,6 +32,7 @@ import javax.swing.JFrame; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import tests.detailed.dialog.DownloadDialog; import tests.detailed.handler.AppHandler; @@ -101,6 +104,8 @@ public static void main(String[] args) { private boolean browserFocus_ = true; private boolean osr_enabled_; private boolean transparent_painting_enabled_; + private JPanel contentPanel_; + private JFrame fullscreenFrame_; public MainFrame(boolean osrEnabled, boolean transparentPaintingEnabled, boolean createImmediately, int windowless_frame_rate, String[] args) { @@ -178,6 +183,10 @@ public void onTitleChange(CefBrowser browser, String title) { public void onStatusMessage(CefBrowser browser, String value) { status_panel_.setStatusText(value); } + @Override + public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen) { + setBrowserFullscreen(fullscreen); + } }); // 2.2) To disable/enable navigation buttons and to display a prgress bar @@ -190,19 +199,25 @@ public void onStatusMessage(CefBrowser browser, String value) { @Override public void onLoadingStateChange(CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) { - control_pane_.update(browser, isLoading, canGoBack, canGoForward); - status_panel_.setIsInProgress(isLoading); - - if (!isLoading && !errorMsg_.isEmpty()) { - browser.loadURL(DataUri.create("text/html", errorMsg_)); - errorMsg_ = ""; - } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + control_pane_.update(browser, isLoading, canGoBack, canGoForward); + status_panel_.setIsInProgress(isLoading); + + if (!isLoading && !errorMsg_.isEmpty()) { + browser.loadURL(DataUri.create("text/html", errorMsg_)); + errorMsg_ = ""; + } + } + }); } @Override public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, String errorText, String failedUrl) { - if (errorCode != ErrorCode.ERR_NONE && errorCode != ErrorCode.ERR_ABORTED) { + if (errorCode != ErrorCode.ERR_NONE && errorCode != ErrorCode.ERR_ABORTED + && frame == browser.getMainFrame()) { errorMsg_ = ""; errorMsg_ += "Error while loading"; errorMsg_ += ""; @@ -224,8 +239,8 @@ public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, setBrowser(browser); // Set up the UI for this example implementation. - JPanel contentPanel = createContentPanel(); - getContentPane().add(contentPanel, BorderLayout.CENTER); + contentPanel_ = createContentPanel(); + getContentPane().add(contentPanel_, BorderLayout.CENTER); // Clear focus from the browser when the address field gains focus. control_pane_.getAddressField().addFocusListener(new FocusAdapter() { @@ -257,7 +272,7 @@ public void onTakeFocus(CefBrowser browser, boolean next) { if (createImmediately) browser.createImmediately(); // Add the browser to the UI. - contentPanel.add(getBrowser().getUIComponent(), BorderLayout.CENTER); + contentPanel_.add(getBrowser().getUIComponent(), BorderLayout.CENTER); MenuBar menuBar = new MenuBar( this, browser, control_pane_, downloadDialog, CefCookieManager.getGlobalManager()); @@ -277,6 +292,8 @@ public void onTakeFocus(CefBrowser browser, boolean next) { menuBar.addBookmark("Spellcheck Test", "client://tests/spellcheck.html"); menuBar.addBookmark("LocalStorage Test", "client://tests/localstorage.html"); menuBar.addBookmark("Transparency Test", "client://tests/transparency.html"); + menuBar.addBookmark("Fullscreen Test", + "https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_fullscreen2"); menuBar.addBookmarkSeparator(); menuBar.addBookmark( "javachromiumembedded", "https://bitbucket.org/chromiumembedded/java-cef"); @@ -300,4 +317,33 @@ public boolean isOsrEnabled() { public boolean isTransparentPaintingEnabled() { return transparent_painting_enabled_; } + + public void setBrowserFullscreen(boolean fullscreen) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + Component browserUI = getBrowser().getUIComponent(); + if (fullscreen) { + if (fullscreenFrame_ == null) { + fullscreenFrame_ = new JFrame(); + fullscreenFrame_.setUndecorated(true); + fullscreenFrame_.setResizable(true); + } + GraphicsConfiguration gc = MainFrame.this.getGraphicsConfiguration(); + fullscreenFrame_.setBounds(gc.getBounds()); + gc.getDevice().setFullScreenWindow(fullscreenFrame_); + + contentPanel_.remove(browserUI); + fullscreenFrame_.add(browserUI); + fullscreenFrame_.setVisible(true); + fullscreenFrame_.validate(); + } else { + fullscreenFrame_.remove(browserUI); + fullscreenFrame_.setVisible(false); + contentPanel_.add(browserUI, BorderLayout.CENTER); + contentPanel_.validate(); + } + } + }); + } } diff --git a/java/tests/simple/MainFrame.java b/java/tests/simple/MainFrame.java index 7b2b21ab..109ccb2a 100644 --- a/java/tests/simple/MainFrame.java +++ b/java/tests/simple/MainFrame.java @@ -17,6 +17,7 @@ import java.awt.BorderLayout; import java.awt.Component; +import java.awt.GraphicsConfiguration; import java.awt.KeyboardFocusManager; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -26,7 +27,9 @@ import java.awt.event.WindowEvent; import javax.swing.JFrame; +import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.SwingUtilities; /** * This is a simple example application using JCEF. @@ -46,8 +49,9 @@ public class MainFrame extends JFrame { private final CefApp cefApp_; private final CefClient client_; private final CefBrowser browser_; - private final Component browerUI_; + private final Component browserUI_; private boolean browserFocus_ = true; + private JFrame fullscreenFrame_; /** * To display a simple browser window, it suffices completely to create an @@ -105,7 +109,7 @@ public void stateHasChanged(org.cef.CefApp.CefAppState state) { // The UI component is inherited from a java.awt.Component and therefore // it can be embedded into any AWT UI. browser_ = client_.createBrowser(startURL, useOSR, isTransparent); - browerUI_ = browser_.getUIComponent(); + browserUI_ = browser_.getUIComponent(); // (4) For this minimal browser, we need only a text field to enter an URL // we want to navigate to and a CefBrowser window to display the content @@ -128,6 +132,10 @@ public void actionPerformed(ActionEvent e) { public void onAddressChange(CefBrowser browser, CefFrame frame, String url) { address_.setText(url); } + @Override + public void OnFullscreenModeChange(CefBrowser browser, boolean fullscreen) { + setBrowserFullscreen(fullscreen); + } }); // Clear focus from the browser when the address field gains focus. @@ -160,7 +168,7 @@ public void onTakeFocus(CefBrowser browser, boolean next) { // (5) All UI components are assigned to the default content pane of this // JFrame and afterwards the frame is made visible to the user. getContentPane().add(address_, BorderLayout.NORTH); - getContentPane().add(browerUI_, BorderLayout.CENTER); + getContentPane().add(browserUI_, BorderLayout.CENTER); pack(); setSize(800, 600); setVisible(true); @@ -177,6 +185,34 @@ public void windowClosing(WindowEvent e) { }); } + public void setBrowserFullscreen(boolean fullscreen) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (fullscreen) { + if (fullscreenFrame_ == null) { + fullscreenFrame_ = new JFrame(); + fullscreenFrame_.setUndecorated(true); + fullscreenFrame_.setResizable(true); + } + GraphicsConfiguration gc = MainFrame.this.getGraphicsConfiguration(); + fullscreenFrame_.setBounds(gc.getBounds()); + gc.getDevice().setFullScreenWindow(fullscreenFrame_); + + getContentPane().remove(browserUI_); + fullscreenFrame_.add(browserUI_); + fullscreenFrame_.setVisible(true); + fullscreenFrame_.validate(); + } else { + fullscreenFrame_.remove(browserUI_); + fullscreenFrame_.setVisible(false); + getContentPane().add(browserUI_, BorderLayout.CENTER); + getContentPane().validate(); + } + } + }); + } + public static void main(String[] args) { // Perform startup initialization on platforms that require it. if (!CefApp.startup(args)) { diff --git a/native/display_handler.cpp b/native/display_handler.cpp index cf088682..33f9e17e 100644 --- a/native/display_handler.cpp +++ b/native/display_handler.cpp @@ -112,6 +112,18 @@ void DisplayHandler::OnTitleChange(CefRefPtr browser, jbrowser.get(), jtitle.get()); } +void DisplayHandler::OnFullscreenModeChange(CefRefPtr browser, + bool fullscreen) { + ScopedJNIEnv env; + if (!env) + return; + + ScopedJNIBrowser jbrowser(env, browser); + JNI_CALL_VOID_METHOD(env, handle_, "OnFullscreenModeChange", + "(Lorg/cef/browser/CefBrowser;Z)V", jbrowser.get(), + (jboolean)fullscreen); +} + bool DisplayHandler::OnTooltip(CefRefPtr browser, CefString& text) { ScopedJNIEnv env; if (!env) diff --git a/native/display_handler.h b/native/display_handler.h index 3b015b62..30615fdb 100644 --- a/native/display_handler.h +++ b/native/display_handler.h @@ -23,6 +23,8 @@ class DisplayHandler : public CefDisplayHandler { const CefString& url) override; void OnTitleChange(CefRefPtr browser, const CefString& title) override; + void OnFullscreenModeChange(CefRefPtr browser, + bool fullscreen) override; bool OnTooltip(CefRefPtr browser, CefString& text) override; void OnStatusMessage(CefRefPtr browser, const CefString& value) override; From e75a3c050cc6891fabce9a3918a6c06558a0c045 Mon Sep 17 00:00:00 2001 From: Rene Schneider Date: Mon, 4 Mar 2024 18:43:25 +0000 Subject: [PATCH 10/11] Update to CEF 121.3.9+g1e0a38f+chromium-121.0.6167.184 Added root_cache_path setting and onAlreadyRunningAppRelaunch event to deal with new CEF restrictions regarding parallel use of root_cache_path dir starting with CEF 120. --- CMakeLists.txt | 2 +- java/org/cef/CefApp.java | 12 ++++++- java/org/cef/CefSettings.java | 32 ++++++++++++++++++- java/org/cef/handler/CefAppHandler.java | 19 +++++++++++ .../org/cef/handler/CefAppHandlerAdapter.java | 7 ++++ native/browser_process_handler.cpp | 25 +++++++++++++++ native/browser_process_handler.h | 2 ++ native/context.cpp | 5 +++ 8 files changed, 101 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54d592b3..4d198bdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON) # Specify the CEF distribution version. if(NOT DEFINED CEF_VERSION) - set(CEF_VERSION "119.4.7+g55e15c8+chromium-119.0.6045.199") + set(CEF_VERSION "121.3.9+g1e0a38f+chromium-121.0.6167.184") endif() # Determine the platform. diff --git a/java/org/cef/CefApp.java b/java/org/cef/CefApp.java index afd10f04..e623b834 100644 --- a/java/org/cef/CefApp.java +++ b/java/org/cef/CefApp.java @@ -102,6 +102,12 @@ public enum CefAppState { */ INITIALIZED, + /** + * CEF initialization has failed (for example due to a second process using + * the same root_cache_path). + */ + INITIALIZATION_FAILED, + /** * CefApp is in its shutdown process. All CefClients and CefBrowser * instances will be disposed. No new CefClient or CefBrowser is allowed to @@ -424,7 +430,11 @@ public void run() { } } - if (N_Initialize(appHandler_, settings)) setState(CefAppState.INITIALIZED); + if (N_Initialize(appHandler_, settings)) { + setState(CefAppState.INITIALIZED); + } else { + setState(CefAppState.INITIALIZATION_FAILED); + } } }; if (SwingUtilities.isEventDispatchThread()) diff --git a/java/org/cef/CefSettings.java b/java/org/cef/CefSettings.java index 5d756d99..577a96ea 100644 --- a/java/org/cef/CefSettings.java +++ b/java/org/cef/CefSettings.java @@ -102,10 +102,39 @@ public ColorType clone() { * The location where cache data will be stored on disk. If empty an in-memory * cache will be used for some features and a temporary disk cache for others. * HTML5 databases such as localStorage will only persist across sessions if a - * cache path is specified. + * cache path is specified. If this is set and root_cache_path is also set, the cache_path + * directory must reside within root_cache_path. */ public String cache_path = null; + /** + * The root directory for installation-specific data and the parent directory + * for profile-specific data. All CefSettings.cache_path and + * CefRequestContextSettings.cache_path values must have this parent + * directory in common. If this value is empty and CefSettings.cache_path is + * non-empty then it will default to the CefSettings.cache_path value. Any + * non-empty value must be an absolute path. If both values are empty then + * the default platform-specific directory will be used + * ("~/.config/cef_user_data" directory on Linux, "~/Library/Application + * Support/CEF/User Data" directory on MacOS, "AppData\Local\CEF\User Data" + * directory under the user profile directory on Windows). Use of the default + * directory is not recommended in production applications (see below). + * + * Multiple application instances writing to the same root_cache_path + * directory could result in data corruption. A process singleton lock based + * on the root_cache_path value is therefore used to protect against this. + * This singleton behavior applies to all CEF-based applications using + * version 120 or newer. You should customize root_cache_path for your + * application and implement CefAppHandler:: + * onAlreadyRunningAppRelaunch, which will then be called on any app relaunch + * with the same root_cache_path value. + * + * Failure to set the root_cache_path value correctly may result in startup + * crashes or other unexpected behaviors (for example, the sandbox blocking + * read/write access to certain files). + */ + public String root_cache_path = null; + /** * To persist session cookies (cookies without an expiry date or validity * interval) by default when using the global cookie manager set this value to @@ -239,6 +268,7 @@ public CefSettings clone() { tmp.windowless_rendering_enabled = windowless_rendering_enabled; tmp.command_line_args_disabled = command_line_args_disabled; tmp.cache_path = cache_path; + tmp.root_cache_path = root_cache_path; tmp.persist_session_cookies = persist_session_cookies; tmp.user_agent = user_agent; tmp.user_agent_product = user_agent_product; diff --git a/java/org/cef/handler/CefAppHandler.java b/java/org/cef/handler/CefAppHandler.java index 095e4747..b6ede710 100644 --- a/java/org/cef/handler/CefAppHandler.java +++ b/java/org/cef/handler/CefAppHandler.java @@ -72,4 +72,23 @@ public interface CefAppHandler { * currently pending scheduled call should be cancelled. */ public void onScheduleMessagePumpWork(long delay_ms); + + /** + * Implement this method to provide app-specific behavior when an already + * running app is relaunched with the same CefSettings.root_cache_path value. + * For example, activate an existing app window or create a new app window. + * |command_line| will be read-only. Do not keep a reference to + * |command_line| outside of this method. Return true if the relaunch is + * handled or false for default relaunch behavior. Default behavior will + * create a new default styled Chrome window. + * + * To avoid cache corruption only a single app instance is allowed to run for + * a given CefSettings.root_cache_path value. On relaunch the app checks a + * process singleton lock and then forwards the new launch arguments to the + * already running app process before exiting early. Client apps should + * therefore check the CefInitialize() return value for early exit before + * proceeding. + */ + public boolean onAlreadyRunningAppRelaunch( + CefCommandLine command_line, String current_directory); } diff --git a/java/org/cef/handler/CefAppHandlerAdapter.java b/java/org/cef/handler/CefAppHandlerAdapter.java index 47f7ae53..1d0bc15b 100644 --- a/java/org/cef/handler/CefAppHandlerAdapter.java +++ b/java/org/cef/handler/CefAppHandlerAdapter.java @@ -89,4 +89,11 @@ public void onContextInitialized() { public void onScheduleMessagePumpWork(long delay_ms) { CefApp.getInstance().doMessageLoopWork(delay_ms); } + + @Override + public boolean onAlreadyRunningAppRelaunch( + CefCommandLine command_line, String current_directory) { + // The default implementation does nothing + return false; + } } diff --git a/native/browser_process_handler.cpp b/native/browser_process_handler.cpp index 38c8fdca..ad583e87 100644 --- a/native/browser_process_handler.cpp +++ b/native/browser_process_handler.cpp @@ -50,6 +50,31 @@ void BrowserProcessHandler::OnScheduleMessagePumpWork(int64_t delay_ms) { delay_ms); } +bool BrowserProcessHandler::OnAlreadyRunningAppRelaunch( + CefRefPtr command_line, + const CefString& current_directory) { + if (!handle_) + return false; + + ScopedJNIEnv env; + if (!env) + return false; + + ScopedJNIObject jcommandLine( + env, command_line, "org/cef/callback/CefCommandLine_N", "CefCommandLine"); + jcommandLine.SetTemporary(); + ScopedJNIString jcurrentDirectory(env, current_directory); + + jboolean jresult = 0; + + JNI_CALL_BOOLEAN_METHOD( + jresult, env, handle_, "onAlreadyRunningAppRelaunch", + "(Lorg/cef/callback/CefCommandLine;Ljava/lang/String;)Z", + jcommandLine.get(), jcurrentDirectory.get()); + + return jresult; +} + // static CefRefPtr BrowserProcessHandler::GetMessageRouterConfigs() { int idx = 0; diff --git a/native/browser_process_handler.h b/native/browser_process_handler.h index 260ebd66..8bbf29e3 100644 --- a/native/browser_process_handler.h +++ b/native/browser_process_handler.h @@ -34,6 +34,8 @@ class BrowserProcessHandler : public CefBrowserProcessHandler { void OnContextInitialized() override; void OnScheduleMessagePumpWork(int64_t delay_ms) override; + bool OnAlreadyRunningAppRelaunch(CefRefPtr command_line, + const CefString& current_directory) override; static CefRefPtr GetMessageRouterConfigs(); static void AddMessageRouterConfig(const CefMessageRouterConfig& cfg); diff --git a/native/context.cpp b/native/context.cpp index 7f894ad9..a9fd67ae 100644 --- a/native/context.cpp +++ b/native/context.cpp @@ -44,6 +44,11 @@ CefSettings GetJNISettings(JNIEnv* env, jobject obj) { CefString(&settings.cache_path) = tmp; tmp.clear(); } + if (GetJNIFieldString(env, cls, obj, "root_cache_path", &tmp) && + !tmp.empty()) { + CefString(&settings.root_cache_path) = tmp; + tmp.clear(); + } GetJNIFieldBoolean(env, cls, obj, "persist_session_cookies", &settings.persist_session_cookies); if (GetJNIFieldString(env, cls, obj, "user_agent", &tmp) && !tmp.empty()) { From 0b8e42e52acf707e84b4a0e92124b1d012ffdda0 Mon Sep 17 00:00:00 2001 From: Rene Schneider Date: Thu, 7 Mar 2024 17:26:56 +0000 Subject: [PATCH 11/11] Update to CEF 122.1.10+gc902316+chromium-122.0.6261.112 Frame identifiers change from long to String. --- CMakeLists.txt | 2 +- java/org/cef/browser/CefBrowser.java | 6 ++--- java/org/cef/browser/CefBrowser_N.java | 16 ++++++------ java/org/cef/browser/CefFrame.java | 4 +-- java/org/cef/browser/CefFrame_N.java | 6 ++--- native/CefBrowser_N.cpp | 36 ++++++++------------------ native/CefBrowser_N.h | 12 +++++---- native/CefFrame_N.cpp | 6 ++--- native/CefFrame_N.h | 4 +-- 9 files changed, 40 insertions(+), 52 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d198bdf..8efab352 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON) # Specify the CEF distribution version. if(NOT DEFINED CEF_VERSION) - set(CEF_VERSION "121.3.9+g1e0a38f+chromium-121.0.6167.184") + set(CEF_VERSION "122.1.10+gc902316+chromium-122.0.6261.112") endif() # Determine the platform. diff --git a/java/org/cef/browser/CefBrowser.java b/java/org/cef/browser/CefBrowser.java index f6c72ee5..437a0192 100644 --- a/java/org/cef/browser/CefBrowser.java +++ b/java/org/cef/browser/CefBrowser.java @@ -125,20 +125,20 @@ public interface CefBrowser { * @param identifier The unique frame identifier * @return The frame or NULL if not found */ - public CefFrame getFrame(long identifier); + public CefFrame getFrameByIdentifier(String identifier); /** * Returns the frame with the specified name, or NULL if not found. * @param name The specified name * @return The frame or NULL if not found */ - public CefFrame getFrame(String name); + public CefFrame getFrameByName(String name); /** * Returns the identifiers of all existing frames. * @return All identifiers of existing frames. */ - public Vector getFrameIdentifiers(); + public Vector getFrameIdentifiers(); /** * Returns the names of all existing frames. diff --git a/java/org/cef/browser/CefBrowser_N.java b/java/org/cef/browser/CefBrowser_N.java index d7c62e0f..94530dab 100644 --- a/java/org/cef/browser/CefBrowser_N.java +++ b/java/org/cef/browser/CefBrowser_N.java @@ -351,9 +351,9 @@ public CefFrame getFocusedFrame() { } @Override - public CefFrame getFrame(long identifier) { + public CefFrame getFrameByIdentifier(String identifier) { try { - return N_GetFrame(identifier); + return N_GetFrameByIdentifier(identifier); } catch (UnsatisfiedLinkError ule) { ule.printStackTrace(); return null; @@ -361,9 +361,9 @@ public CefFrame getFrame(long identifier) { } @Override - public CefFrame getFrame(String name) { + public CefFrame getFrameByName(String name) { try { - return N_GetFrame2(name); + return N_GetFrameByName(name); } catch (UnsatisfiedLinkError ule) { ule.printStackTrace(); return null; @@ -371,7 +371,7 @@ public CefFrame getFrame(String name) { } @Override - public Vector getFrameIdentifiers() { + public Vector getFrameIdentifiers() { try { return N_GetFrameIdentifiers(); } catch (UnsatisfiedLinkError ule) { @@ -839,9 +839,9 @@ private final native CefRegistration N_AddDevToolsMessageObserver( private final native int N_GetIdentifier(); private final native CefFrame N_GetMainFrame(); private final native CefFrame N_GetFocusedFrame(); - private final native CefFrame N_GetFrame(long identifier); - private final native CefFrame N_GetFrame2(String name); - private final native Vector N_GetFrameIdentifiers(); + private final native CefFrame N_GetFrameByIdentifier(String identifier); + private final native CefFrame N_GetFrameByName(String name); + private final native Vector N_GetFrameIdentifiers(); private final native Vector N_GetFrameNames(); private final native int N_GetFrameCount(); private final native boolean N_IsPopup(); diff --git a/java/org/cef/browser/CefFrame.java b/java/org/cef/browser/CefFrame.java index 280f1e35..a64b4ed9 100644 --- a/java/org/cef/browser/CefFrame.java +++ b/java/org/cef/browser/CefFrame.java @@ -14,11 +14,11 @@ public interface CefFrame { void dispose(); /** - * Returns the globally unique identifier for this frame or < 0 if the + * Returns the globally unique identifier for this frame or an empty string if the * underlying frame does not yet exist. * @return The frame identifier */ - long getIdentifier(); + String getIdentifier(); /** * Emits the URL currently loaded in this frame. diff --git a/java/org/cef/browser/CefFrame_N.java b/java/org/cef/browser/CefFrame_N.java index c68e1fbf..0bd1c6af 100644 --- a/java/org/cef/browser/CefFrame_N.java +++ b/java/org/cef/browser/CefFrame_N.java @@ -30,12 +30,12 @@ public void dispose() { } @Override - public long getIdentifier() { + public String getIdentifier() { try { return N_GetIdentifier(getNativeRef(null)); } catch (UnsatisfiedLinkError ule) { ule.printStackTrace(); - return -1; + return null; } } @@ -157,7 +157,7 @@ public void selectAll() { } private final native void N_Dispose(long self); - private final native long N_GetIdentifier(long self); + private final native String N_GetIdentifier(long self); private final native String N_GetURL(long self); private final native String N_GetName(long self); private final native boolean N_IsMain(long self); diff --git a/native/CefBrowser_N.cpp b/native/CefBrowser_N.cpp index 92c43636..e00f5b88 100644 --- a/native/CefBrowser_N.cpp +++ b/native/CefBrowser_N.cpp @@ -1086,21 +1086,6 @@ void OnAfterParentChanged(CefRefPtr browser) { } } -jobject NewJNILongVector(JNIEnv* env, const std::vector& vals) { - ScopedJNIObjectLocal jvector(env, "java/util/Vector"); - if (!jvector) - return nullptr; - - std::vector::const_iterator iter; - for (iter = vals.begin(); iter != vals.end(); ++iter) { - ScopedJNIObjectLocal argument( - env, NewJNIObject(env, "java/lang/Long", "(J)V", (jlong)*iter)); - JNI_CALL_VOID_METHOD(env, jvector, "addElement", "(Ljava/lang/Object;)V", - argument.get()); - } - return jvector.Release(); -} - CefPdfPrintSettings GetJNIPdfPrintSettings(JNIEnv* env, jobject obj) { CefString tmp; CefPdfPrintSettings settings; @@ -1369,11 +1354,12 @@ Java_org_cef_browser_CefBrowser_1N_N_1GetFocusedFrame(JNIEnv* env, } JNIEXPORT jobject JNICALL -Java_org_cef_browser_CefBrowser_1N_N_1GetFrame(JNIEnv* env, - jobject obj, - jlong identifier) { +Java_org_cef_browser_CefBrowser_1N_N_1GetFrameByIdentifier(JNIEnv* env, + jobject obj, + jstring identifier) { CefRefPtr browser = JNI_GET_BROWSER_OR_RETURN(env, obj, nullptr); - CefRefPtr frame = browser->GetFrame(identifier); + CefRefPtr frame = + browser->GetFrameByIdentifier(GetJNIString(env, identifier)); if (!frame) return nullptr; ScopedJNIFrame jframe(env, frame); @@ -1381,11 +1367,11 @@ Java_org_cef_browser_CefBrowser_1N_N_1GetFrame(JNIEnv* env, } JNIEXPORT jobject JNICALL -Java_org_cef_browser_CefBrowser_1N_N_1GetFrame2(JNIEnv* env, - jobject obj, - jstring name) { +Java_org_cef_browser_CefBrowser_1N_N_1GetFrameByName(JNIEnv* env, + jobject obj, + jstring name) { CefRefPtr browser = JNI_GET_BROWSER_OR_RETURN(env, obj, nullptr); - CefRefPtr frame = browser->GetFrame(GetJNIString(env, name)); + CefRefPtr frame = browser->GetFrameByName(GetJNIString(env, name)); if (!frame) return nullptr; ScopedJNIFrame jframe(env, frame); @@ -1402,9 +1388,9 @@ JNIEXPORT jobject JNICALL Java_org_cef_browser_CefBrowser_1N_N_1GetFrameIdentifiers(JNIEnv* env, jobject obj) { CefRefPtr browser = JNI_GET_BROWSER_OR_RETURN(env, obj, nullptr); - std::vector identifiers; + std::vector identifiers; browser->GetFrameIdentifiers(identifiers); - return NewJNILongVector(env, identifiers); + return NewJNIStringVector(env, identifiers); } JNIEXPORT jobject JNICALL diff --git a/native/CefBrowser_N.h b/native/CefBrowser_N.h index f01e1f5a..b0348d5d 100644 --- a/native/CefBrowser_N.h +++ b/native/CefBrowser_N.h @@ -164,19 +164,21 @@ Java_org_cef_browser_CefBrowser_1N_N_1GetFocusedFrame(JNIEnv*, jobject); /* * Class: org_cef_browser_CefBrowser_N - * Method: N_GetFrame - * Signature: (J)Lorg/cef/browser/CefFrame; + * Method: N_GetFrameByIdentifier + * Signature: (Ljava/lang/String;)Lorg/cef/browser/CefFrame; */ JNIEXPORT jobject JNICALL -Java_org_cef_browser_CefBrowser_1N_N_1GetFrame(JNIEnv*, jobject, jlong); +Java_org_cef_browser_CefBrowser_1N_N_1GetFrameByIdentifier(JNIEnv*, + jobject, + jstring); /* * Class: org_cef_browser_CefBrowser_N - * Method: N_GetFrame2 + * Method: N_GetFrameByName * Signature: (Ljava/lang/String;)Lorg/cef/browser/CefFrame; */ JNIEXPORT jobject JNICALL -Java_org_cef_browser_CefBrowser_1N_N_1GetFrame2(JNIEnv*, jobject, jstring); +Java_org_cef_browser_CefBrowser_1N_N_1GetFrameByName(JNIEnv*, jobject, jstring); /* * Class: org_cef_browser_CefBrowser_N diff --git a/native/CefFrame_N.cpp b/native/CefFrame_N.cpp index 46f039fb..cab338ed 100644 --- a/native/CefFrame_N.cpp +++ b/native/CefFrame_N.cpp @@ -27,14 +27,14 @@ JNIEXPORT void JNICALL Java_org_cef_browser_CefFrame_1N_N_1Dispose(JNIEnv* env, ClearSelf(env, obj); } -JNIEXPORT jlong JNICALL +JNIEXPORT jstring JNICALL Java_org_cef_browser_CefFrame_1N_N_1GetIdentifier(JNIEnv* env, jobject obj, jlong self) { CefRefPtr frame = GetSelf(self); if (!frame) - return (jlong)-1; - return (jlong)frame->GetIdentifier(); + return nullptr; + return NewJNIString(env, frame->GetIdentifier()); } JNIEXPORT jstring JNICALL diff --git a/native/CefFrame_N.h b/native/CefFrame_N.h index c184d384..a1568181 100644 --- a/native/CefFrame_N.h +++ b/native/CefFrame_N.h @@ -19,9 +19,9 @@ JNIEXPORT void JNICALL Java_org_cef_browser_CefFrame_1N_N_1Dispose(JNIEnv*, /* * Class: org_cef_browser_CefFrame_N * Method: N_GetIdentifier - * Signature: (J)J + * Signature: (J)Ljava/lang/String; */ -JNIEXPORT jlong JNICALL +JNIEXPORT jstring JNICALL Java_org_cef_browser_CefFrame_1N_N_1GetIdentifier(JNIEnv*, jobject, jlong); /*