Skip to content

Commit be7dc4c

Browse files
CzarekCzarek
authored andcommitted
Implemented python callbacks in CEF Python 3.
A call to context->Exit() was missing when converting CEF 3 values to V8 values.
1 parent d377aa8 commit be7dc4c

25 files changed

+499
-156
lines changed

cefpython/browser.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ cdef class PyBrowser:
657657
# virtual void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) =0;
658658

659659
cdef void SendProcessMessage(self, cef_process_id_t targetProcess,
660-
py_string messageName, list pyArguments
660+
object frameId, py_string messageName, list pyArguments
661661
) except *:
662662
cdef CefRefPtr[CefProcessMessage] message = \
663663
CefProcessMessage_Create(PyToCefStringValue(messageName))
@@ -667,7 +667,8 @@ cdef class PyBrowser:
667667
# | message.get().GetArgumentList().swap(arguments)
668668
cdef CefRefPtr[CefListValue] messageArguments = \
669669
message.get().GetArgumentList()
670-
PyListToExistingCefListValue(pyArguments, messageArguments)
670+
PyListToExistingCefListValue(frameId, pyArguments,
671+
messageArguments)
671672
Debug("SendProcessMessage(): message=%s, arguments size=%d" % (
672673
messageName,
673674
message.get().GetArgumentList().get().GetSize()))

cefpython/cef3/client_handler/client_handler.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
4646
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
4747
if (arguments->GetSize() == 3
4848
&& arguments->GetType(0) == VTYPE_INT // frameId
49-
&& arguments->GetType(1) == VTYPE_STRING // funcName
50-
&& arguments->GetType(2) == VTYPE_LIST) { // funcArguments
49+
&& arguments->GetType(1) == VTYPE_STRING // functionName
50+
&& arguments->GetType(2) == VTYPE_LIST) { // functionArguments
5151
int64 frameId = arguments->GetInt(0);
52-
CefString funcName = arguments->GetString(1);
53-
CefRefPtr<CefListValue> funcArguments = arguments->GetList(2);
52+
CefString functionName = arguments->GetString(1);
53+
CefRefPtr<CefListValue> functionArguments = arguments->GetList(2);
5454
CefRefPtr<CefFrame> frame = browser->GetFrame(frameId);
55-
V8FunctionHandler_Execute(browser, frame, funcName, funcArguments);
55+
V8FunctionHandler_Execute(browser, frame, functionName, functionArguments);
5656
return true;
5757
} else {
5858
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
@@ -71,7 +71,31 @@ bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
7171
", messageName = OnBrowserDestroyed");
7272
return false;
7373
}
74-
74+
} else if (messageName == "ExecutePythonCallback") {
75+
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
76+
if (arguments->GetSize() == 2
77+
&& arguments->GetType(0) == VTYPE_INT // callbackId
78+
&& arguments->GetType(1) == VTYPE_LIST) { // functionArguments
79+
int callbackId = arguments->GetInt(0);
80+
CefRefPtr<CefListValue> functionArguments = arguments->GetList(1);
81+
ExecutePythonCallback(browser, callbackId, functionArguments);
82+
return true;
83+
} else {
84+
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
85+
", messageName = ExecutePythonCallback");
86+
return false;
87+
}
88+
} else if (messageName == "RemovePythonCallbacksForFrame") {
89+
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
90+
if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) {
91+
int frameId = arguments->GetInt(0);
92+
RemovePythonCallbacksForFrame(frameId);
93+
return true;
94+
} else {
95+
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
96+
", messageName = ExecutePythonCallback");
97+
return false;
98+
}
7599
}
76100
return false;
77101
}

cefpython/cef3/linux/binaries_32bit/wxpython.html

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,81 @@ <h3>Javascript bindings</h3>
7373
'python called from js and then js called from python')</a>
7474

7575

76+
77+
<h3>Javascript callbacks</h3>
78+
<script>
79+
function JSCallback(arg1) {
80+
window.alert(arg1)
81+
}
82+
</script>
83+
<pre>
84+
&lt;script&gt;
85+
function JSCallback(arg1) {
86+
window.alert(arg1)
87+
}
88+
&lt;/script&gt;
89+
</pre>
90+
<pre>
91+
def TestJSCallback(self, jsCallback):
92+
print("jsCallback.GetFunctionName() = %s" % jsCallback.GetFunctionName())
93+
print("jsCallback.GetFrame().GetIdentifier() = %s" % \
94+
jsCallback.GetFrame().GetIdentifier())
95+
jsCallback.Call("This message was sent from python using js callback")
96+
</pre>
97+
<a href="javascript:external.TestJSCallback(JSCallback)">
98+
external.TestJSCallback(JSCallback)</a>
99+
<script>
100+
function JSCallback2() {
101+
window.alert(JSON.stringify(arguments))
102+
}
103+
</script>
104+
<pre>
105+
&lt;script&gt;
106+
function JSCallback2() {
107+
window.alert(JSON.stringify(arguments))
108+
}
109+
&lt;/script&gt;
110+
</pre>
111+
<pre>
112+
def TestJSCallbackComplexArguments(self, jsObject):
113+
jsCallback = jsObject["myCallback"];
114+
jsCallback.Call(1, None, 2.14, "string", ["list", ["nested list", \
115+
{"nested object":None}]], \
116+
{"nested list next":[{"deeply nested object":1}]})
117+
</pre>
118+
<a href="javascript:external.TestJSCallbackComplexArguments(
119+
{'myCallback':JSCallback2})">
120+
external.TestJSCallbackComplexArguments({"myCallback": JSCallback2})
121+
</a>
122+
123+
124+
125+
<h3>Python callbacks</h3>
126+
<script>
127+
function JSCallback3(pyCallback) {
128+
pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
129+
}
130+
</script>
131+
<pre>
132+
&lt;script&gt;
133+
function JSCallback3(pyCallback) {
134+
pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
135+
}
136+
&lt;/script&gt;
137+
</pre>
138+
<pre>
139+
def TestPythonCallback(self, jsCallback):
140+
jsCallback.Call(PyCallback)
141+
142+
def PyCallback(self, *args):
143+
message = "PyCallback() was executed successfully! Arguments: %s" \
144+
% str(args)
145+
print(message)
146+
self.mainBrowser.GetMainFrame().ExecuteJavascript(
147+
"window.alert(\"%s\")" % message)
148+
</pre>
149+
<a href="javascript:external.TestPythonCallback(JSCallback3)">
150+
external.TestPythonCallback(JSCallback3)</a>
151+
76152
</body>
77153
</html>

cefpython/cef3/linux/binaries_32bit/wxpython.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ def TestAllTypes(self, *args):
156156
def ExecuteFunction(self, *args):
157157
self.mainBrowser.GetMainFrame().ExecuteFunction(*args)
158158

159+
def TestJSCallback(self, jsCallback):
160+
print("jsCallback.GetFunctionName() = %s" % jsCallback.GetFunctionName())
161+
print("jsCallback.GetFrame().GetIdentifier() = %s" % \
162+
jsCallback.GetFrame().GetIdentifier())
163+
jsCallback.Call("This message was sent from python using js callback")
164+
165+
def TestJSCallbackComplexArguments(self, jsObject):
166+
jsCallback = jsObject["myCallback"];
167+
jsCallback.Call(1, None, 2.14, "string", ["list", ["nested list", \
168+
{"nested object":None}]], \
169+
{"nested list next":[{"deeply nested object":1}]})
170+
171+
def TestPythonCallback(self, jsCallback):
172+
jsCallback.Call(self.PyCallback)
173+
174+
def PyCallback(self, *args):
175+
message = "PyCallback() was executed successfully! Arguments: %s" \
176+
% str(args)
177+
print(message)
178+
self.mainBrowser.GetMainFrame().ExecuteJavascript(
179+
"window.alert(\"%s\")" % message)
180+
159181
class MyApp(wx.App):
160182
timer = None
161183
timerID = 1

cefpython/cef3/linux/binaries_64bit/wxpython.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,34 @@ <h3>Javascript callbacks</h3>
120120
external.TestJSCallbackComplexArguments({"myCallback": JSCallback2})
121121
</a>
122122

123+
124+
125+
<h3>Python callbacks</h3>
126+
<script>
127+
function JSCallback3(pyCallback) {
128+
pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
129+
}
130+
</script>
131+
<pre>
132+
&lt;script&gt;
133+
function JSCallback3(pyCallback) {
134+
pyCallback(1, 2.14, "string", [1, [2, {"key": "value"}]], {"list": [1,2]});
135+
}
136+
&lt;/script&gt;
137+
</pre>
138+
<pre>
139+
def TestPythonCallback(self, jsCallback):
140+
jsCallback.Call(PyCallback)
141+
142+
def PyCallback(self, *args):
143+
message = "PyCallback() was executed successfully! Arguments: %s" \
144+
% str(args)
145+
print(message)
146+
self.mainBrowser.GetMainFrame().ExecuteJavascript(
147+
"window.alert(\"%s\")" % message)
148+
</pre>
149+
<a href="javascript:external.TestPythonCallback(JSCallback3)">
150+
external.TestPythonCallback(JSCallback3)</a>
151+
123152
</body>
124153
</html>

cefpython/cef3/linux/binaries_64bit/wxpython.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,16 @@ def TestJSCallbackComplexArguments(self, jsObject):
168168
{"nested object":None}]], \
169169
{"nested list next":[{"deeply nested object":1}]})
170170

171+
def TestPythonCallback(self, jsCallback):
172+
jsCallback.Call(self.PyCallback)
173+
174+
def PyCallback(self, *args):
175+
message = "PyCallback() was executed successfully! Arguments: %s" \
176+
% str(args)
177+
print(message)
178+
self.mainBrowser.GetMainFrame().ExecuteJavascript(
179+
"window.alert(\"%s\")" % message)
180+
171181
class MyApp(wx.App):
172182
timer = None
173183
timerID = 1

cefpython/cef3/linux/setup/cefpython.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ __PYX_EXTERN_C DL_IMPORT(void) ProcessMessage_OnBrowserDestroyed(int);
1616
__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextCreated(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>);
1717
__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextReleased(int, int64);
1818
__PYX_EXTERN_C DL_IMPORT(void) V8FunctionHandler_Execute(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, CefString &, CefRefPtr<CefListValue>);
19+
__PYX_EXTERN_C DL_IMPORT(void) RemovePythonCallbacksForFrame(int);
20+
__PYX_EXTERN_C DL_IMPORT(bool) ExecutePythonCallback(CefRefPtr<CefBrowser>, int, CefRefPtr<CefListValue>);
1921

2022
#endif /* !__PYX_HAVE_API__cefpython_py27 */
2123

cefpython/cef3/subprocess/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ subprocess:
1818
# -fPIC is required only for libraries included by Cython.
1919
g++ -Wall -Werror $(INC) $(LIB) main.cpp cefpython_app.cpp \
2020
v8function_handler.cpp v8utils.cpp javascript_callback.cpp \
21-
python_callback.cpp -lcef_dll_wrapper -lcef -o subprocess -Wl,-rpath,.
21+
-lcef_dll_wrapper -lcef -o subprocess -Wl,-rpath,.

cefpython/cef3/subprocess/Makefile-libcefpythonapp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ CC = g++
1212
CCFLAGS = -g -fPIC -Wall -Werror
1313

1414
SRC = cefpython_app.cpp v8function_handler.cpp v8utils.cpp \
15-
javascript_callback.cpp python_callback.cpp
15+
javascript_callback.cpp
1616
OBJ = $(SRC:.cpp=.o)
1717
OUT = libcefpythonapp.a
1818

cefpython/cef3/subprocess/cefpython_app.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <vector>
1010
#include "v8utils.h"
1111
#include "javascript_callback.h"
12-
#include "python_callback.h"
12+
#include "v8function_handler.h"
1313

1414
CefRefPtr<CefBrowser> g_mainBrowser;
1515

@@ -187,9 +187,13 @@ void CefPythonApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
187187
CefRefPtr<CefV8Context> context) {
188188
DebugLog("Renderer: OnContextReleased()");
189189
if (g_mainBrowser.get()) {
190-
CefRefPtr<CefProcessMessage> message = CefProcessMessage::Create(
191-
"OnContextReleased");
192-
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
190+
CefRefPtr<CefProcessMessage> message;
191+
CefRefPtr<CefListValue> arguments;
192+
// --------------------------------------------------------------------
193+
// 1. Send "OnContextReleased" message.
194+
// --------------------------------------------------------------------
195+
message = CefProcessMessage::Create("OnContextReleased");
196+
arguments = message->GetArgumentList();
193197
arguments->SetInt(0, browser->GetIdentifier());
194198
// TODO: losing int64 precision, the solution is to convert
195199
// it to string and then in the Browser process back
@@ -201,6 +205,14 @@ void CefPythonApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
201205
// when this is not the main frame? It could fail, so
202206
// it is more reliable to always use the main browser.
203207
g_mainBrowser->SendProcessMessage(PID_BROWSER, message);
208+
// --------------------------------------------------------------------
209+
// 2. Remove python callbacks for a frame.
210+
// --------------------------------------------------------------------
211+
message = CefProcessMessage::Create("RemovePythonCallbacksForFrame");
212+
arguments = message->GetArgumentList();
213+
// TODO: int64 precision lost
214+
arguments->SetInt(0, (int)(frame->GetIdentifier()));
215+
g_mainBrowser->SendProcessMessage(PID_BROWSER, message);
204216
} else {
205217
DebugLog("Renderer: OnContextReleased(): ERROR: main browser not " \
206218
"found, cannot send process message to the browser process " \
@@ -209,7 +221,6 @@ void CefPythonApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
209221
}
210222
// Clear javascript callbacks.
211223
RemoveJavascriptCallbacksForFrame(frame);
212-
RemovePythonCallbacksForFrame(frame);
213224
}
214225

215226
void CefPythonApp::OnUncaughtException(CefRefPtr<CefBrowser> browser,
@@ -424,7 +435,7 @@ void CefPythonApp::DoJavascriptBindingsForFrame(CefRefPtr<CefBrowser> browser,
424435
}
425436
CefRefPtr<CefV8Value> v8Window = context->GetGlobal();
426437
CefRefPtr<CefV8Value> v8Function;
427-
CefRefPtr<CefV8Handler> v8FunctionHandler(new V8FunctionHandler(this));
438+
CefRefPtr<CefV8Handler> v8FunctionHandler(new V8FunctionHandler(this, 0));
428439
// FUNCTIONS.
429440
std::vector<CefString> functionsVector;
430441
if (!functions->GetKeys(functionsVector)) {

0 commit comments

Comments
 (0)