X Tutup
Skip to content

Commit 03b49df

Browse files
CzarekCzarek
authored andcommitted
Fixed the way objects are freed, previous method was not reliable
and could result in app errors. This includes PyBrowser, PyFrame and python callbacks.
1 parent be7dc4c commit 03b49df

File tree

11 files changed

+392
-131
lines changed

11 files changed

+392
-131
lines changed

cefpython/browser.pyx

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -79,29 +79,16 @@ cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser):
7979
pyBrowser.SetJavascriptBindings(javascriptBindings)
8080
return pyBrowser
8181

82-
cdef public void ProcessMessage_OnBrowserDestroyed(
83-
int browserId
84-
) except * with gil:
85-
# CefRenderProcessHandler::OnBrowserDestroyed() sends a process
86-
# message to the browser process. The browser is probably already
87-
# destroyed in the renderer process when this message arrives to
88-
# the browser process.
89-
# --
90-
# Due to multi-process architecture in CEF 3, this function won't
91-
# get called for the main browser, to send a message from the
92-
# renderer a parent browser is used, as you can't send a process
93-
# message to a browser that is being destroyed.
94-
try:
95-
RemovePyBrowser(browserId)
96-
except:
97-
(exc_type, exc_value, exc_trace) = sys.exc_info()
98-
sys.excepthook(exc_type, exc_value, exc_trace)
99-
10082
cdef void RemovePyBrowser(int browserId) except *:
101-
# Called from ProcessMessage_OnBrowserDestroyed().
83+
# Called from LifespanHandler_OnBeforeClose().
10284
# TODO: call this function also in CEF 1, currently called only in CEF 3.
103-
Debug("del g_pyBrowsers[%s]" % browserId)
104-
del g_pyBrowsers[browserId]
85+
global g_pyBrowsers
86+
if g_pyBrowsers.has_key(browserId):
87+
Debug("del g_pyBrowsers[%s]" % browserId)
88+
del g_pyBrowsers[browserId]
89+
else:
90+
Debug("RemovePyBrowser() FAILED: browser not found, id = %s" \
91+
% browserId)
10592

10693
cpdef PyBrowser GetBrowserByWindowHandle(WindowHandle windowHandle):
10794
cdef PyBrowser pyBrowser
@@ -667,8 +654,8 @@ cdef class PyBrowser:
667654
# | message.get().GetArgumentList().swap(arguments)
668655
cdef CefRefPtr[CefListValue] messageArguments = \
669656
message.get().GetArgumentList()
670-
PyListToExistingCefListValue(frameId, pyArguments,
671-
messageArguments)
657+
PyListToExistingCefListValue(self.GetIdentifier(), frameId,
658+
pyArguments, messageArguments)
672659
Debug("SendProcessMessage(): message=%s, arguments size=%d" % (
673660
messageName,
674661
message.get().GetArgumentList().get().GetSize()))

cefpython/cef3/client_handler/client_handler.cpp

Lines changed: 127 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#include "cefpython_public_api.h"
77
#include "DebugLog.h"
88

9+
// ----------------------------------------------------------------------------
10+
// CefClient
11+
// ----------------------------------------------------------------------------
12+
913
bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
1014
CefProcessId source_process,
1115
CefRefPtr<CefProcessMessage> message) {
@@ -59,18 +63,6 @@ bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
5963
", messageName = V8FunctionHandler::Execute");
6064
return false;
6165
}
62-
} else if (messageName == "OnBrowserDestroyed") {
63-
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
64-
if (arguments->GetSize() == 1 && arguments->GetType(0) == VTYPE_INT) {
65-
int browserId = arguments->GetInt(0);
66-
// NOT browser->GetIdentifier()!
67-
ProcessMessage_OnBrowserDestroyed(browserId);
68-
return true;
69-
} else {
70-
DebugLog("Browser: OnProcessMessageReceived(): invalid arguments" \
71-
", messageName = OnBrowserDestroyed");
72-
return false;
73-
}
7466
} else if (messageName == "ExecutePythonCallback") {
7567
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
7668
if (arguments->GetSize() == 2
@@ -99,3 +91,126 @@ bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
9991
}
10092
return false;
10193
}
94+
95+
// ----------------------------------------------------------------------------
96+
// CefLifeSpanHandler
97+
// ----------------------------------------------------------------------------
98+
99+
///
100+
// Called on the IO thread before a new popup window is created. The |browser|
101+
// and |frame| parameters represent the source of the popup request. The
102+
// |target_url| and |target_frame_name| values may be empty if none were
103+
// specified with the request. The |popupFeatures| structure contains
104+
// information about the requested popup window. To allow creation of the
105+
// popup window optionally modify |windowInfo|, |client|, |settings| and
106+
// |no_javascript_access| and return false. To cancel creation of the popup
107+
// window return true. The |client| and |settings| values will default to the
108+
// source browser's values. The |no_javascript_access| value indicates whether
109+
// the new browser window should be scriptable and in the same process as the
110+
// source browser.
111+
/*--cef(optional_param=target_url,optional_param=target_frame_name)--*/
112+
bool ClientHandler::OnBeforePopup(CefRefPtr<CefBrowser> browser,
113+
CefRefPtr<CefFrame> frame,
114+
const CefString& target_url,
115+
const CefString& target_frame_name,
116+
const CefPopupFeatures& popupFeatures,
117+
CefWindowInfo& windowInfo,
118+
CefRefPtr<CefClient>& client,
119+
CefBrowserSettings& settings,
120+
bool* no_javascript_access) {
121+
return false;
122+
}
123+
124+
///
125+
// Called after a new browser is created.
126+
///
127+
/*--cef()--*/
128+
void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
129+
}
130+
131+
///
132+
// Called when a modal window is about to display and the modal loop should
133+
// begin running. Return false to use the default modal loop implementation or
134+
// true to use a custom implementation.
135+
///
136+
/*--cef()--*/
137+
bool ClientHandler::RunModal(CefRefPtr<CefBrowser> browser) {
138+
return false;
139+
}
140+
141+
///
142+
// Called when a browser has recieved a request to close. This may result
143+
// directly from a call to CefBrowserHost::CloseBrowser() or indirectly if the
144+
// browser is a top-level OS window created by CEF and the user attempts to
145+
// close the window. This method will be called after the JavaScript
146+
// 'onunload' event has been fired. It will not be called for browsers after
147+
// the associated OS window has been destroyed (for those browsers it is no
148+
// longer possible to cancel the close).
149+
//
150+
// If CEF created an OS window for the browser returning false will send an OS
151+
// close notification to the browser window's top-level owner (e.g. WM_CLOSE
152+
// on Windows, performClose: on OS-X and "delete_event" on Linux). If no OS
153+
// window exists (window rendering disabled) returning false will cause the
154+
// browser object to be destroyed immediately. Return true if the browser is
155+
// parented to another window and that other window needs to receive close
156+
// notification via some non-standard technique.
157+
//
158+
// If an application provides its own top-level window it should handle OS
159+
// close notifications by calling CefBrowserHost::CloseBrowser(false) instead
160+
// of immediately closing (see the example below). This gives CEF an
161+
// opportunity to process the 'onbeforeunload' event and optionally cancel the
162+
// close before DoClose() is called.
163+
//
164+
// The CefLifeSpanHandler::OnBeforeClose() method will be called immediately
165+
// before the browser object is destroyed. The application should only exit
166+
// after OnBeforeClose() has been called for all existing browsers.
167+
//
168+
// If the browser represents a modal window and a custom modal loop
169+
// implementation was provided in CefLifeSpanHandler::RunModal() this callback
170+
// should be used to restore the opener window to a usable state.
171+
//
172+
// By way of example consider what should happen during window close when the
173+
// browser is parented to an application-provided top-level OS window.
174+
// 1. User clicks the window close button which sends an OS close
175+
// notification (e.g. WM_CLOSE on Windows, performClose: on OS-X and
176+
// "delete_event" on Linux).
177+
// 2. Application's top-level window receives the close notification and:
178+
// A. Calls CefBrowserHost::CloseBrowser(false).
179+
// B. Cancels the window close.
180+
// 3. JavaScript 'onbeforeunload' handler executes and shows the close
181+
// confirmation dialog (which can be overridden via
182+
// CefJSDialogHandler::OnBeforeUnloadDialog()).
183+
// 4. User approves the close.
184+
// 5. JavaScript 'onunload' handler executes.
185+
// 6. Application's DoClose() handler is called. Application will:
186+
// A. Call CefBrowserHost::ParentWindowWillClose() to notify CEF that the
187+
// parent window will be closing.
188+
// B. Set a flag to indicate that the next close attempt will be allowed.
189+
// C. Return false.
190+
// 7. CEF sends an OS close notification.
191+
// 8. Application's top-level window receives the OS close notification and
192+
// allows the window to close based on the flag from #6B.
193+
// 9. Browser OS window is destroyed.
194+
// 10. Application's CefLifeSpanHandler::OnBeforeClose() handler is called and
195+
// the browser object is destroyed.
196+
// 11. Application exits by calling CefQuitMessageLoop() if no other browsers
197+
// exist.
198+
///
199+
/*--cef()--*/
200+
bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser) {
201+
return false;
202+
}
203+
204+
///
205+
// Called just before a browser is destroyed. Release all references to the
206+
// browser object and do not attempt to execute any methods on the browser
207+
// object after this callback returns. If this is a modal window and a custom
208+
// modal loop implementation was provided in RunModal() this callback should
209+
// be used to exit the custom modal loop. See DoClose() documentation for
210+
// additional usage information.
211+
///
212+
/*--cef()--*/
213+
void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
214+
REQUIRE_UI_THREAD();
215+
LifespanHandler_OnBeforeClose(browser);
216+
}

cefpython/cef3/client_handler/client_handler.h

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
#include "cefpython_public_api.h"
1212

1313
class ClientHandler :
14-
public CefClient
14+
public CefClient,
15+
public CefLifeSpanHandler
1516
{
1617
public:
1718
ClientHandler(){}
@@ -99,7 +100,7 @@ class ClientHandler :
99100
///
100101
/*--cef()--*/
101102
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE {
102-
return NULL;
103+
return this;
103104
}
104105

105106
///
@@ -137,6 +138,125 @@ class ClientHandler :
137138
CefRefPtr<CefProcessMessage> message)
138139
OVERRIDE;
139140

141+
// --------------------------------------------------------------------------
142+
// CefLifeSpanHandler
143+
// --------------------------------------------------------------------------
144+
145+
///
146+
// Implement this interface to handle events related to browser life span. The
147+
// methods of this class will be called on the UI thread unless otherwise
148+
// indicated.
149+
///
150+
151+
///
152+
// Called on the IO thread before a new popup window is created. The |browser|
153+
// and |frame| parameters represent the source of the popup request. The
154+
// |target_url| and |target_frame_name| values may be empty if none were
155+
// specified with the request. The |popupFeatures| structure contains
156+
// information about the requested popup window. To allow creation of the
157+
// popup window optionally modify |windowInfo|, |client|, |settings| and
158+
// |no_javascript_access| and return false. To cancel creation of the popup
159+
// window return true. The |client| and |settings| values will default to the
160+
// source browser's values. The |no_javascript_access| value indicates whether
161+
// the new browser window should be scriptable and in the same process as the
162+
// source browser.
163+
/*--cef(optional_param=target_url,optional_param=target_frame_name)--*/
164+
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
165+
CefRefPtr<CefFrame> frame,
166+
const CefString& target_url,
167+
const CefString& target_frame_name,
168+
const CefPopupFeatures& popupFeatures,
169+
CefWindowInfo& windowInfo,
170+
CefRefPtr<CefClient>& client,
171+
CefBrowserSettings& settings,
172+
bool* no_javascript_access) OVERRIDE;
173+
174+
///
175+
// Called after a new browser is created.
176+
///
177+
/*--cef()--*/
178+
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
179+
180+
///
181+
// Called when a modal window is about to display and the modal loop should
182+
// begin running. Return false to use the default modal loop implementation or
183+
// true to use a custom implementation.
184+
///
185+
/*--cef()--*/
186+
virtual bool RunModal(CefRefPtr<CefBrowser> browser) OVERRIDE;
187+
188+
///
189+
// Called when a browser has recieved a request to close. This may result
190+
// directly from a call to CefBrowserHost::CloseBrowser() or indirectly if the
191+
// browser is a top-level OS window created by CEF and the user attempts to
192+
// close the window. This method will be called after the JavaScript
193+
// 'onunload' event has been fired. It will not be called for browsers after
194+
// the associated OS window has been destroyed (for those browsers it is no
195+
// longer possible to cancel the close).
196+
//
197+
// If CEF created an OS window for the browser returning false will send an OS
198+
// close notification to the browser window's top-level owner (e.g. WM_CLOSE
199+
// on Windows, performClose: on OS-X and "delete_event" on Linux). If no OS
200+
// window exists (window rendering disabled) returning false will cause the
201+
// browser object to be destroyed immediately. Return true if the browser is
202+
// parented to another window and that other window needs to receive close
203+
// notification via some non-standard technique.
204+
//
205+
// If an application provides its own top-level window it should handle OS
206+
// close notifications by calling CefBrowserHost::CloseBrowser(false) instead
207+
// of immediately closing (see the example below). This gives CEF an
208+
// opportunity to process the 'onbeforeunload' event and optionally cancel the
209+
// close before DoClose() is called.
210+
//
211+
// The CefLifeSpanHandler::OnBeforeClose() method will be called immediately
212+
// before the browser object is destroyed. The application should only exit
213+
// after OnBeforeClose() has been called for all existing browsers.
214+
//
215+
// If the browser represents a modal window and a custom modal loop
216+
// implementation was provided in CefLifeSpanHandler::RunModal() this callback
217+
// should be used to restore the opener window to a usable state.
218+
//
219+
// By way of example consider what should happen during window close when the
220+
// browser is parented to an application-provided top-level OS window.
221+
// 1. User clicks the window close button which sends an OS close
222+
// notification (e.g. WM_CLOSE on Windows, performClose: on OS-X and
223+
// "delete_event" on Linux).
224+
// 2. Application's top-level window receives the close notification and:
225+
// A. Calls CefBrowserHost::CloseBrowser(false).
226+
// B. Cancels the window close.
227+
// 3. JavaScript 'onbeforeunload' handler executes and shows the close
228+
// confirmation dialog (which can be overridden via
229+
// CefJSDialogHandler::OnBeforeUnloadDialog()).
230+
// 4. User approves the close.
231+
// 5. JavaScript 'onunload' handler executes.
232+
// 6. Application's DoClose() handler is called. Application will:
233+
// A. Call CefBrowserHost::ParentWindowWillClose() to notify CEF that the
234+
// parent window will be closing.
235+
// B. Set a flag to indicate that the next close attempt will be allowed.
236+
// C. Return false.
237+
// 7. CEF sends an OS close notification.
238+
// 8. Application's top-level window receives the OS close notification and
239+
// allows the window to close based on the flag from #6B.
240+
// 9. Browser OS window is destroyed.
241+
// 10. Application's CefLifeSpanHandler::OnBeforeClose() handler is called and
242+
// the browser object is destroyed.
243+
// 11. Application exits by calling CefQuitMessageLoop() if no other browsers
244+
// exist.
245+
///
246+
/*--cef()--*/
247+
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
248+
249+
///
250+
// Called just before a browser is destroyed. Release all references to the
251+
// browser object and do not attempt to execute any methods on the browser
252+
// object after this callback returns. If this is a modal window and a custom
253+
// modal loop implementation was provided in RunModal() this callback should
254+
// be used to exit the custom modal loop. See DoClose() documentation for
255+
// additional usage information.
256+
///
257+
/*--cef()--*/
258+
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
259+
140260
private:
141261

142262
// Include the default reference counting implementation.

cefpython/cef3/linux/setup/cefpython.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
#endif
1313
#endif
1414

15-
__PYX_EXTERN_C DL_IMPORT(void) ProcessMessage_OnBrowserDestroyed(int);
1615
__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextCreated(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>);
1716
__PYX_EXTERN_C DL_IMPORT(void) V8ContextHandler_OnContextReleased(int, int64);
1817
__PYX_EXTERN_C DL_IMPORT(void) V8FunctionHandler_Execute(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, CefString &, CefRefPtr<CefListValue>);
1918
__PYX_EXTERN_C DL_IMPORT(void) RemovePythonCallbacksForFrame(int);
2019
__PYX_EXTERN_C DL_IMPORT(bool) ExecutePythonCallback(CefRefPtr<CefBrowser>, int, CefRefPtr<CefListValue>);
20+
__PYX_EXTERN_C DL_IMPORT(void) LifespanHandler_OnBeforeClose(CefRefPtr<CefBrowser>);
2121

2222
#endif /* !__PYX_HAVE_API__cefpython_py27 */
2323

0 commit comments

Comments
 (0)
X Tutup