From fd56eb0bb9389ade125781c7decbb909315ef4c0 Mon Sep 17 00:00:00 2001 From: Vinyarion <38413862+VinyarionHyarmendacil@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:10:07 -0500 Subject: [PATCH 1/3] Properly support persistent callbacks --- java/org/cef/callback/CefQueryCallback.java | 8 +++++++ java/org/cef/callback/CefQueryCallback_N.java | 18 ++++++++++++++-- .../handler/MessageRouterHandlerEx.java | 8 +++++++ .../tests/detailed/handler/binding_test2.html | 19 +++++++++++++++++ native/CefQueryCallback_N.cpp | 5 ++++- native/CefQueryCallback_N.h | 5 +++-- native/message_router_handler.cpp | 21 +++++++++++++------ 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/java/org/cef/callback/CefQueryCallback.java b/java/org/cef/callback/CefQueryCallback.java index 0972bf2e..6e9afa89 100644 --- a/java/org/cef/callback/CefQueryCallback.java +++ b/java/org/cef/callback/CefQueryCallback.java @@ -22,4 +22,12 @@ public interface CefQueryCallback { * @param error_message Error message passed to JavaScript. */ public void failure(int error_code, String error_message); + + /** + * Returns whether this callback is persistent and therefore whether + * {@link CefQueryCallback#success(String)} may be called multiple times. + * Persistent queries must be explicitly canceled, either from the browser + * or via {@link CefQueryCallback#failure(int, String)}. + */ + public boolean isPersistent(); } diff --git a/java/org/cef/callback/CefQueryCallback_N.java b/java/org/cef/callback/CefQueryCallback_N.java index 14b32e3a..67465a4c 100644 --- a/java/org/cef/callback/CefQueryCallback_N.java +++ b/java/org/cef/callback/CefQueryCallback_N.java @@ -16,7 +16,7 @@ protected void finalize() throws Throwable { @Override public void success(String response) { try { - N_Success(getNativeRef(null), response); + N_Success(getNativeRef(null), response, isPersistent()); } catch (UnsatisfiedLinkError ule) { ule.printStackTrace(); } @@ -31,6 +31,20 @@ public void failure(int error_code, String error_message) { } } - private final native void N_Success(long self, String response); + @Override + public boolean isPersistent() { + return false; + } + + private final native void N_Success(long self, String response, boolean persistent); private final native void N_Failure(long self, int error_code, String error_message); } + +class CefQueryCallback_N_Persistent extends CefQueryCallback_N { + CefQueryCallback_N_Persistent() {} + + @Override + public boolean isPersistent() { + return true; + } +} diff --git a/java/tests/detailed/handler/MessageRouterHandlerEx.java b/java/tests/detailed/handler/MessageRouterHandlerEx.java index bc4b780b..0e21eb6d 100644 --- a/java/tests/detailed/handler/MessageRouterHandlerEx.java +++ b/java/tests/detailed/handler/MessageRouterHandlerEx.java @@ -47,6 +47,14 @@ public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, String router_ = null; callback.success(""); } + } else if (request.startsWith("doPersistent")) { + if (persistent) { + callback.success("Hello,"); + callback.success("World!"); + callback.failure(0, "Finished"); + } else { + callback.failure(-1, "Request not marked as persistent"); + } } else { // not handled return false; diff --git a/java/tests/detailed/handler/binding_test2.html b/java/tests/detailed/handler/binding_test2.html index 8a61a96f..b9ebd3ae 100644 --- a/java/tests/detailed/handler/binding_test2.html +++ b/java/tests/detailed/handler/binding_test2.html @@ -17,6 +17,20 @@ }); } +function doPersistent() { + document.getElementById('persist').value = ''; + window.cefQuery({ + request: 'doPersistent', + persistent: true, + onSuccess: function(response) { + document.getElementById('persist').value += ' ' + response; + }, + onFailure: function(error_code, error_message) { + document.getElementById('persist').value += ' ' + error_code + ' ' + error_message; + } + }); +} + function execute(cmd) { window.cefQuery({ request: cmd, @@ -75,6 +89,11 @@

JavaScript Binding Test - Part 2


+

This button tests persistent queries. When the button is pressed, it clears the output, +and when it receives a success message, it appends it to the output. When it finally receives +a failure message, it appends the error_code followed by the error_message.

+ + diff --git a/native/CefQueryCallback_N.cpp b/native/CefQueryCallback_N.cpp index 017ff1aa..73b2d62c 100644 --- a/native/CefQueryCallback_N.cpp +++ b/native/CefQueryCallback_N.cpp @@ -26,11 +26,14 @@ JNIEXPORT void JNICALL Java_org_cef_callback_CefQueryCallback_1N_N_1Success(JNIEnv* env, jobject obj, jlong self, - jstring response) { + jstring response, + jboolean persistent) { CefRefPtr callback = GetSelf(self); if (!callback) return; callback->Success(GetJNIString(env, response)); + if (persistent) + return; ClearSelf(env, obj); } diff --git a/native/CefQueryCallback_N.h b/native/CefQueryCallback_N.h index f6ba930f..0d237a1a 100644 --- a/native/CefQueryCallback_N.h +++ b/native/CefQueryCallback_N.h @@ -10,13 +10,14 @@ extern "C" { /* * Class: org_cef_callback_CefQueryCallback_N * Method: N_Success - * Signature: (JLjava/lang/String;)V + * Signature: (JLjava/lang/String;Z)V */ JNIEXPORT void JNICALL Java_org_cef_callback_CefQueryCallback_1N_N_1Success(JNIEnv*, jobject, jlong, - jstring); + jstring, + jboolean); /* * Class: org_cef_callback_CefQueryCallback_N diff --git a/native/message_router_handler.cpp b/native/message_router_handler.cpp index 793ccf7c..9bcf5cf9 100644 --- a/native/message_router_handler.cpp +++ b/native/message_router_handler.cpp @@ -13,11 +13,16 @@ using CefQueryCallback = CefMessageRouterBrowserSide::Callback; // JNI CefQueryCallback object. class ScopedJNIQueryCallback : public ScopedJNIObject { public: - ScopedJNIQueryCallback(JNIEnv* env, CefRefPtr obj) - : ScopedJNIObject(env, - obj, - "org/cef/callback/CefQueryCallback_N", - "CefQueryCallback") {} + ScopedJNIQueryCallback(JNIEnv* env, + CefRefPtr obj, + bool persistent) + : ScopedJNIObject( + env, + obj, + persistent ? + "org/cef/callback/CefQueryCallback_N_Persistent" : + "org/cef/callback/CefQueryCallback_N", + "CefQueryCallback") {} }; } // namespace @@ -40,7 +45,11 @@ bool MessageRouterHandler::OnQuery( ScopedJNIFrame jframe(env, frame); jframe.SetTemporary(); ScopedJNIString jrequest(env, request); - ScopedJNIQueryCallback jcallback(env, callback); + + // Uses a subclass of CefQueryCallback_N for persistent queries, as otherwise + // the reference to the CefMessageRouterBrowserSide::Callback gets cleared + // immediately when N_Success is called + ScopedJNIQueryCallback jcallback(env, callback, persistent); jboolean jresult = JNI_FALSE; From d5bf8e536b242f74402666caa0f72fe271577b6b Mon Sep 17 00:00:00 2001 From: Vinyarion <38413862+VinyarionHyarmendacil@users.noreply.github.com> Date: Sat, 21 Mar 2026 20:27:34 -0500 Subject: [PATCH 2/3] Support for binary queries --- java/org/cef/callback/CefQueryCallback.java | 11 ++++- java/org/cef/callback/CefQueryCallback_N.java | 12 ++++++ .../cef/handler/CefMessageRouterHandler.java | 23 +++++++++- .../CefMessageRouterHandlerAdapter.java | 8 ++++ .../handler/MessageRouterHandlerEx.java | 24 +++++++++++ .../tests/detailed/handler/binding_test2.html | 35 ++++++++++++++- native/CefQueryCallback_N.cpp | 17 ++++++++ native/CefQueryCallback_N.h | 12 ++++++ native/message_router_handler.cpp | 43 +++++++++++++++++++ native/message_router_handler.h | 6 +++ 10 files changed, 187 insertions(+), 4 deletions(-) diff --git a/java/org/cef/callback/CefQueryCallback.java b/java/org/cef/callback/CefQueryCallback.java index 6e9afa89..6d437550 100644 --- a/java/org/cef/callback/CefQueryCallback.java +++ b/java/org/cef/callback/CefQueryCallback.java @@ -4,6 +4,8 @@ package org.cef.callback; +import java.nio.ByteBuffer; + /** * Interface representing a query callback. */ @@ -11,10 +13,17 @@ public interface CefQueryCallback { /** * Notify the associated JavaScript onSuccess callback that the query has * completed successfully. - * @param response Response passed to JavaScript. + * @param response Response text passed to JavaScript. */ public void success(String response); + /** + * Notify the associated JavaScript onSuccess callback that the query has + * completed successfully. + * @param response Response direct buffer passed to JavaScript. May be null. + */ + public void success(ByteBuffer response); + /** * Notify the associated JavaScript onFailure callback that the query has * failed. diff --git a/java/org/cef/callback/CefQueryCallback_N.java b/java/org/cef/callback/CefQueryCallback_N.java index 67465a4c..e62e0b15 100644 --- a/java/org/cef/callback/CefQueryCallback_N.java +++ b/java/org/cef/callback/CefQueryCallback_N.java @@ -4,6 +4,8 @@ package org.cef.callback; +import java.nio.ByteBuffer; + class CefQueryCallback_N extends CefNativeAdapter implements CefQueryCallback { CefQueryCallback_N() {} @@ -22,6 +24,15 @@ public void success(String response) { } } + @Override + public void success(ByteBuffer response) { + try { + N_SuccessBinary(getNativeRef(null), response, isPersistent()); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + @Override public void failure(int error_code, String error_message) { try { @@ -37,6 +48,7 @@ public boolean isPersistent() { } private final native void N_Success(long self, String response, boolean persistent); + private final native void N_SuccessBinary(long self, ByteBuffer response, boolean persistent); private final native void N_Failure(long self, int error_code, String error_message); } diff --git a/java/org/cef/handler/CefMessageRouterHandler.java b/java/org/cef/handler/CefMessageRouterHandler.java index 4827a476..53413a8f 100644 --- a/java/org/cef/handler/CefMessageRouterHandler.java +++ b/java/org/cef/handler/CefMessageRouterHandler.java @@ -9,18 +9,21 @@ import org.cef.callback.CefNative; import org.cef.callback.CefQueryCallback; +import java.nio.ByteBuffer; + /** * Implement this interface to handle queries. All methods will be executed on the browser process * UI thread. */ public interface CefMessageRouterHandler extends CefNative { /** - * Called when the browser receives a JavaScript query. + * Called when the browser receives a JavaScript text query. * * @param browser The corresponding browser. * @param frame The frame generating the event. Instance only valid within the scope of this * method. * @param queryId The unique ID for the query. + * @param request The query text. * @param persistent True if the query is persistent. * @param callback Object used to continue or cancel the query asynchronously. * @return True to handle the query or false to propagate the query to other registered @@ -31,6 +34,24 @@ public interface CefMessageRouterHandler extends CefNative { public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, String request, boolean persistent, CefQueryCallback callback); + /** + * Called when the browser receives a JavaScript binary query. + * + * @param browser The corresponding browser. + * @param frame The frame generating the event. Instance only valid within the scope of this + * method. + * @param queryId The unique ID for the query. + * @param request The query direct buffer. Valid only for the scope of this method. + * @param persistent True if the query is persistent. + * @param callback Object used to continue or cancel the query asynchronously. + * @return True to handle the query or false to propagate the query to other registered + * handlers, if any. If no handlers return true from this method then the query will be + * automatically canceled with an error code of -1 delivered to the JavaScript onFailure + * callback. + */ + public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, ByteBuffer request, + boolean persistent, CefQueryCallback callback); + /** * Called when a pending JavaScript query is canceled. * diff --git a/java/org/cef/handler/CefMessageRouterHandlerAdapter.java b/java/org/cef/handler/CefMessageRouterHandlerAdapter.java index c924dbfe..a91ba622 100644 --- a/java/org/cef/handler/CefMessageRouterHandlerAdapter.java +++ b/java/org/cef/handler/CefMessageRouterHandlerAdapter.java @@ -9,6 +9,8 @@ import org.cef.callback.CefNativeAdapter; import org.cef.callback.CefQueryCallback; +import java.nio.ByteBuffer; + /** * An abstract adapter class for receiving message router events. * The methods in this class are empty. @@ -22,6 +24,12 @@ public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, String return false; } + @Override + public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, ByteBuffer request, + boolean persistent, CefQueryCallback callback) { + return false; + } + @Override public void onQueryCanceled(CefBrowser browser, CefFrame frame, long queryId) { return; diff --git a/java/tests/detailed/handler/MessageRouterHandlerEx.java b/java/tests/detailed/handler/MessageRouterHandlerEx.java index 0e21eb6d..35f7579c 100644 --- a/java/tests/detailed/handler/MessageRouterHandlerEx.java +++ b/java/tests/detailed/handler/MessageRouterHandlerEx.java @@ -12,6 +12,8 @@ import org.cef.callback.CefQueryCallback; import org.cef.handler.CefMessageRouterHandlerAdapter; +import java.nio.ByteBuffer; + public class MessageRouterHandlerEx extends CefMessageRouterHandlerAdapter { private final CefClient client_; private final CefMessageRouterConfig config_ = @@ -62,6 +64,28 @@ public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, String return true; } + @Override + public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, ByteBuffer request, + boolean persistent, CefQueryCallback callback) { + if (request != null) { + int size = request.remaining(); + ByteBuffer response = ByteBuffer.allocateDirect(size); + // reverse bytes + for (int i = 0; i < size; i++) { + byte b = request.get(i); + response.put(size - i - 1, b); + } + callback.success(response); + if (persistent) { + callback.failure(0, "Finished"); + } + } else { + // not handled + return false; + } + return true; + } + private class JavaVersionMessageRouter extends CefMessageRouterHandlerAdapter { @Override public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, String request, diff --git a/java/tests/detailed/handler/binding_test2.html b/java/tests/detailed/handler/binding_test2.html index b9ebd3ae..866c2cd9 100644 --- a/java/tests/detailed/handler/binding_test2.html +++ b/java/tests/detailed/handler/binding_test2.html @@ -23,10 +23,34 @@ request: 'doPersistent', persistent: true, onSuccess: function(response) { - document.getElementById('persist').value += ' ' + response; + document.getElementById('persist').value += ' MESSAGE: [ ' + response + '];'; }, onFailure: function(error_code, error_message) { - document.getElementById('persist').value += ' ' + error_code + ' ' + error_message; + document.getElementById('persist').value += ' FINISHED: ' + error_code + ' ' + error_message; + } + }); +} + +function doBinary() { + document.getElementById('binary').value = 'ORIGINAL: ['; + const binaryData = new Uint8Array([10, 20, 30, 40, 50]); + for (let i = 0; i < binaryData.length; i++) { + document.getElementById('binary').value += ' ' + binaryData.at(i); + } + document.getElementById('binary').value += ' ];'; + window.cefQuery({ + request: binaryData.buffer, + persistent: true, + onSuccess: function(response) { + document.getElementById('binary').value += ' RESPONSE: ['; + const echoedBinaryData = new Uint8Array(response); + for (let i = 0; i < echoedBinaryData.length; i++) { + document.getElementById('binary').value += ' ' + echoedBinaryData.at(i); + } + document.getElementById('binary').value += ' ];'; + }, + onFailure: function(error_code, error_message) { + document.getElementById('binary').value += ' FINISHED: ' + error_code + ' ' + error_message; } }); } @@ -94,6 +118,13 @@

JavaScript Binding Test - Part 2

a failure message, it appends the error_code followed by the error_message.

+

This button tests binary queries. When the button is pressed, it clears the output and +replaces it with a representation of the binary data sent in the query. When it receives a +success message, it appends a similar representation of the binary data received (which has +been reversed by Java). When it finally receives a failure message, it appends the error_code +followed by the error_message.

+ + diff --git a/native/CefQueryCallback_N.cpp b/native/CefQueryCallback_N.cpp index 73b2d62c..abf99574 100644 --- a/native/CefQueryCallback_N.cpp +++ b/native/CefQueryCallback_N.cpp @@ -37,6 +37,23 @@ Java_org_cef_callback_CefQueryCallback_1N_N_1Success(JNIEnv* env, ClearSelf(env, obj); } +JNIEXPORT void JNICALL +Java_org_cef_callback_CefQueryCallback_1N_N_1SuccessBinary( + JNIEnv* env, + jobject obj, + jlong self, + jobject response, + jboolean persistent) { + CefRefPtr callback = GetSelf(self); + if (!callback) + return; + callback->Success(GetJNIByteBufferData(env, response), + GetJNIByteBufferLength(env, response)); + if (persistent) + return; + ClearSelf(env, obj); +} + JNIEXPORT void JNICALL Java_org_cef_callback_CefQueryCallback_1N_N_1Failure(JNIEnv* env, jobject obj, diff --git a/native/CefQueryCallback_N.h b/native/CefQueryCallback_N.h index 0d237a1a..35aa34dd 100644 --- a/native/CefQueryCallback_N.h +++ b/native/CefQueryCallback_N.h @@ -19,6 +19,18 @@ Java_org_cef_callback_CefQueryCallback_1N_N_1Success(JNIEnv*, jstring, jboolean); +/* + * Class: org_cef_callback_CefQueryCallback_N + * Method: N_Success + * Signature: (JLjava/nio/ByteBuffer;Z)V + */ +JNIEXPORT void JNICALL +Java_org_cef_callback_CefQueryCallback_1N_N_1SuccessBinary(JNIEnv*, + jobject, + jlong, + jobject, + jboolean); + /* * Class: org_cef_callback_CefQueryCallback_N * Method: N_Failure diff --git a/native/message_router_handler.cpp b/native/message_router_handler.cpp index 9bcf5cf9..f5b94243 100644 --- a/native/message_router_handler.cpp +++ b/native/message_router_handler.cpp @@ -70,6 +70,49 @@ bool MessageRouterHandler::OnQuery( return jresult != JNI_FALSE; } +bool MessageRouterHandler::OnQuery( + CefRefPtr browser, + CefRefPtr frame, + int64_t query_id, + CefRefPtr request, + bool persistent, + CefRefPtr callback) { + ScopedJNIEnv env; + if (!env) + return false; + + ScopedJNIBrowser jbrowser(env, browser); + ScopedJNIFrame jframe(env, frame); + jframe.SetTemporary(); + ScopedJNIObjectLocal jrequest( + env, + env->NewDirectByteBuffer(const_cast(request->GetData()), + (jlong)request->GetSize())); + + // Uses a subclass of CefQueryCallback_N for persistent queries, as otherwise + // the reference to the CefMessageRouterBrowserSide::Callback gets cleared + // immediately when N_Success is called + ScopedJNIQueryCallback jcallback(env, callback, persistent); + + jboolean jresult = JNI_FALSE; + + JNI_CALL_METHOD(env, handle_, "onQuery", + "(Lorg/cef/browser/CefBrowser;Lorg/cef/browser/" + "CefFrame;JLjava/nio/ByteBuffer;ZLorg/cef/" + "callback/CefQueryCallback;)Z", + Boolean, jresult, jbrowser.get(), jframe.get(), + (jlong)query_id, jrequest.get(), + persistent ? JNI_TRUE : JNI_FALSE, jcallback.get()); + + if (jresult == JNI_FALSE) { + // If the Java method returns "false" the callback won't be used and + // the reference can therefore be removed. + jcallback.SetTemporary(); + } + + return jresult != JNI_FALSE; +} + void MessageRouterHandler::OnQueryCanceled(CefRefPtr browser, CefRefPtr frame, int64_t query_id) { diff --git a/native/message_router_handler.h b/native/message_router_handler.h index 90b4ae77..d1701186 100644 --- a/native/message_router_handler.h +++ b/native/message_router_handler.h @@ -25,6 +25,12 @@ class MessageRouterHandler : public CefMessageRouterBrowserSide::Handler, const CefString& request, bool persistent, CefRefPtr callback) override; + virtual bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64_t query_id, + CefRefPtr request, + bool persistent, + CefRefPtr callback) override; virtual void OnQueryCanceled(CefRefPtr browser, CefRefPtr frame, int64_t query_id) override; From 612992f37cf5b8f70993e88df0f2e96130e32fae Mon Sep 17 00:00:00 2001 From: Vinyarion <38413862+VinyarionHyarmendacil@users.noreply.github.com> Date: Sun, 22 Mar 2026 10:26:21 -0500 Subject: [PATCH 3/3] Support array-backed buffers for CefQueryCallback.success(ByteBuffer) Test case updated as well --- java/org/cef/callback/CefQueryCallback.java | 2 +- .../handler/MessageRouterHandlerEx.java | 10 ++++-- native/CefQueryCallback_N.cpp | 32 ++++++++++++++++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/java/org/cef/callback/CefQueryCallback.java b/java/org/cef/callback/CefQueryCallback.java index 6d437550..fe54ce71 100644 --- a/java/org/cef/callback/CefQueryCallback.java +++ b/java/org/cef/callback/CefQueryCallback.java @@ -20,7 +20,7 @@ public interface CefQueryCallback { /** * Notify the associated JavaScript onSuccess callback that the query has * completed successfully. - * @param response Response direct buffer passed to JavaScript. May be null. + * @param response Response buffer passed to JavaScript. May be null. */ public void success(ByteBuffer response); diff --git a/java/tests/detailed/handler/MessageRouterHandlerEx.java b/java/tests/detailed/handler/MessageRouterHandlerEx.java index 35f7579c..f534f31b 100644 --- a/java/tests/detailed/handler/MessageRouterHandlerEx.java +++ b/java/tests/detailed/handler/MessageRouterHandlerEx.java @@ -19,6 +19,7 @@ public class MessageRouterHandlerEx extends CefMessageRouterHandlerAdapter { private final CefMessageRouterConfig config_ = new CefMessageRouterConfig("myQuery", "myQueryAbort"); private CefMessageRouter router_ = null; + private boolean binary_direct_ = false; public MessageRouterHandlerEx(final CefClient client) { client_ = client; @@ -68,8 +69,10 @@ public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, String public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, ByteBuffer request, boolean persistent, CefQueryCallback callback) { if (request != null) { - int size = request.remaining(); - ByteBuffer response = ByteBuffer.allocateDirect(size); + int size = request.capacity(); + boolean direct = (binary_direct_ = !binary_direct_); + ByteBuffer response = direct ? ByteBuffer.allocateDirect(size) + : ByteBuffer.allocate(size); // reverse bytes for (int i = 0; i < size; i++) { byte b = request.get(i); @@ -77,7 +80,8 @@ public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, ByteBu } callback.success(response); if (persistent) { - callback.failure(0, "Finished"); + callback.failure(0, direct ? "Finished (direct)" + : "Finished (array)"); } } else { // not handled diff --git a/native/CefQueryCallback_N.cpp b/native/CefQueryCallback_N.cpp index abf99574..232e1b82 100644 --- a/native/CefQueryCallback_N.cpp +++ b/native/CefQueryCallback_N.cpp @@ -33,7 +33,7 @@ Java_org_cef_callback_CefQueryCallback_1N_N_1Success(JNIEnv* env, return; callback->Success(GetJNIString(env, response)); if (persistent) - return; + return; ClearSelf(env, obj); } @@ -47,10 +47,34 @@ Java_org_cef_callback_CefQueryCallback_1N_N_1SuccessBinary( CefRefPtr callback = GetSelf(self); if (!callback) return; - callback->Success(GetJNIByteBufferData(env, response), - GetJNIByteBufferLength(env, response)); + jboolean isDirect = JNI_TRUE; + if (response) + JNI_CALL_BOOLEAN_METHOD(isDirect, env, response, "isDirect", "()Z"); + if (isDirect != JNI_FALSE) { + callback->Success(GetJNIByteBufferData(env, response), + GetJNIByteBufferLength(env, response)); + } else { + jobject responseArrayObj = nullptr; + jint responseOffset = 0; + jint responseLength = 0; + JNI_CALL_METHOD(env, response, "array", "()[B", Object, responseArrayObj); + JNI_CALL_METHOD(env, response, "arrayOffset", "()I", Int, responseOffset); + JNI_CALL_METHOD(env, response, "capacity", "()I", Int, responseLength); + jbyteArray responseArray = static_cast(responseArrayObj); + jsize responseArrayLength = env->GetArrayLength(responseArray); + if (responseLength >= 0 && responseLength <= responseArrayLength && + responseOffset >= 0 && responseOffset < responseLength) { + // isCopy is ignored + jboolean isCopy = JNI_FALSE; + jbyte *responseBytes = env->GetByteArrayElements(responseArray, &isCopy); + callback->Success(static_cast(responseBytes + responseOffset), + static_cast(responseLength)); + // JNI_ABORT: no need to copy back the data + env->ReleaseByteArrayElements(responseArray, responseBytes, JNI_ABORT); + } + } if (persistent) - return; + return; ClearSelf(env, obj); }