Skip to content

Commit 19a0863

Browse files
linesightclaude
andcommitted
fix: guard against NULL CefBrowser during rapid frame lifecycle transitions
CefFrame::GetBrowser() can return a null CefRefPtr while a frame is being created or destroyed during rapid navigation (e.g. heavy pages with many iframes). The naked .get().GetIdentifier() chain in GetPyFrame() was a latent SIGSEGV; all handler entry points that call GetPyFrame() or dereference the frame's browser pointer now return their safe defaults when the browser pointer is NULL. Affected files: - src/frame.pyx: store GetBrowser() result, check .get() before chaining - src/handlers/load_handler.pyx: OnLoadStart / OnLoadEnd / OnLoadError - src/handlers/display_handler.pyx: OnAddressChange - src/handlers/v8context_handler.pyx: OnContextCreated - src/handlers/request_handler.pyx: OnBeforeBrowse, OnBeforeResourceLoad, GetResourceHandler, OnResourceRedirect, GetAuthCredentials - src/handlers/cookie_access_filter.pyx: CanSendCookie, CanSaveCookie - src/handlers/lifespan_handler.pyx: OnBeforePopup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d8b9354 commit 19a0863

7 files changed

Lines changed: 35 additions & 1 deletion

File tree

src/frame.pyx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame):
2828

2929
cdef PyFrame pyFrame
3030
cdef CefString frameId = cefFrame.get().GetIdentifier()
31-
cdef int browserId = cefFrame.get().GetBrowser().get().GetIdentifier()
31+
cdef CefRefPtr[CefBrowser] cefBrowser = cefFrame.get().GetBrowser()
32+
if not cefBrowser.get():
33+
# GetBrowser() can return NULL when a frame is being created or
34+
# destroyed during rapid navigation (e.g. iframes on heavy pages).
35+
# Raising here prevents a SIGSEGV from the naked .get().GetIdentifier()
36+
# chain that was here before this check.
37+
raise Exception("GetPyFrame(): CefBrowser is NULL"
38+
" (frame lifecycle transition during navigation?)")
39+
cdef int browserId = cefBrowser.get().GetIdentifier()
3240
if not browserId:
3341
raise Exception("GetPyFrame(): browserId is 0 (browser not yet initialised)")
3442
# frameId may be empty for internal frames that CEF creates before the

src/handlers/cookie_access_filter.pyx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ cdef public cpp_bool CookieAccessFilter_CanSendCookie(
2525
# browser was closed.
2626
if IsBrowserClosed(cef_browser):
2727
return False
28+
if not cef_frame.get().GetBrowser().get():
29+
return True # default: allow cookie
2830

2931
browser = GetPyBrowser(cef_browser, "CanSendCookie")
3032
frame = GetPyFrame(cef_frame)
@@ -64,6 +66,8 @@ cdef public cpp_bool CookieAccessFilter_CanSaveCookie(
6466
# browser was closed.
6567
if IsBrowserClosed(cef_browser):
6668
return False
69+
if not cef_frame.get().GetBrowser().get():
70+
return True # default: allow cookie
6771

6872
browser = GetPyBrowser(cef_browser, "CanSaveCookie")
6973
frame = GetPyFrame(cef_frame)

src/handlers/display_handler.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ cdef public void DisplayHandler_OnAddressChange(
1515
cdef object pyUrl
1616
cdef object callback
1717
try:
18+
if not cefFrame.get().GetBrowser().get():
19+
return
1820
pyBrowser = GetPyBrowser(cefBrowser, "OnAddressChange")
1921
pyFrame = GetPyFrame(cefFrame)
2022
pyUrl = CefToPyString(cefUrl)

src/handlers/lifespan_handler.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ cdef public cpp_bool LifespanHandler_OnBeforePopup(
4848
cdef object callback
4949
cdef py_bool returnValue
5050
try:
51+
if not cefFrame.get().GetBrowser().get():
52+
return False # frame is being destroyed; cancel popup
5153
pyBrowser = GetPyBrowser(cefBrowser, "OnBeforePopup")
5254
pyFrame = GetPyFrame(cefFrame)
5355
pyTargetUrl = CefToPyString(targetUrl)

src/handlers/load_handler.pyx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ cdef public void LoadHandler_OnLoadStart(
3333
cdef PyFrame pyFrame
3434
cdef object clientCallback
3535
try:
36+
if not cefFrame.get().GetBrowser().get():
37+
return
3638
pyBrowser = GetPyBrowser(cefBrowser, "OnLoadStart")
3739
pyFrame = GetPyFrame(cefFrame)
3840
clientCallback = pyBrowser.GetClientCallback("OnLoadStart")
@@ -51,6 +53,8 @@ cdef public void LoadHandler_OnLoadEnd(
5153
cdef PyFrame pyFrame
5254
cdef object clientCallback
5355
try:
56+
if not cefFrame.get().GetBrowser().get():
57+
return
5458
pyBrowser = GetPyBrowser(cefBrowser, "OnLoadEnd")
5559
pyFrame = GetPyFrame(cefFrame)
5660
clientCallback = pyBrowser.GetClientCallback("OnLoadEnd")
@@ -78,6 +82,8 @@ cdef public void LoadHandler_OnLoadError(
7882
# the error code will be ERR_ABORTED. In such cases calls
7983
# to OnLoadError should be ignored and not handled by user
8084
# scripts. The wxpython example implements such behavior.
85+
if not cefFrame.get().GetBrowser().get():
86+
return
8187
pyBrowser = GetPyBrowser(cefBrowser, "OnLoadError")
8288
pyFrame = GetPyFrame(cefFrame)
8389
errorTextOut = [CefToPyString(cefErrorText)]

src/handlers/request_handler.pyx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ cdef public cpp_bool RequestHandler_OnBeforeBrowse(
7474
# browser was closed.
7575
if IsBrowserClosed(cefBrowser):
7676
return False
77+
if not cefFrame.get().GetBrowser().get():
78+
return False
7779

7880
pyBrowser = GetPyBrowser(cefBrowser, "OnBeforeBrowse")
7981
pyFrame = GetPyFrame(cefFrame)
@@ -109,6 +111,8 @@ cdef public cpp_bool RequestHandler_OnBeforeResourceLoad(
109111
# browser was closed.
110112
if IsBrowserClosed(cefBrowser):
111113
return False
114+
if not cefFrame.get().GetBrowser().get():
115+
return False
112116

113117
pyBrowser = GetPyBrowser(cefBrowser, "OnBeforeResourceLoad")
114118
pyFrame = GetPyFrame(cefFrame)
@@ -142,6 +146,8 @@ cdef public CefRefPtr[CefResourceHandler] RequestHandler_GetResourceHandler(
142146
# browser was closed.
143147
if IsBrowserClosed(cefBrowser):
144148
return <CefRefPtr[CefResourceHandler]>nullptr
149+
if not cefFrame.get().GetBrowser().get():
150+
return <CefRefPtr[CefResourceHandler]>nullptr
145151

146152
pyBrowser = GetPyBrowser(cefBrowser, "GetResourceHandler")
147153
pyFrame = GetPyFrame(cefFrame)
@@ -183,6 +189,8 @@ cdef public void RequestHandler_OnResourceRedirect(
183189
# browser was closed.
184190
if IsBrowserClosed(cefBrowser):
185191
return
192+
if not cefFrame.get().GetBrowser().get():
193+
return
186194

187195
pyBrowser = GetPyBrowser(cefBrowser, "OnResourceRedirect")
188196
pyFrame = GetPyFrame(cefFrame)
@@ -233,6 +241,8 @@ cdef public cpp_bool RequestHandler_GetAuthCredentials(
233241
# browser was closed.
234242
if IsBrowserClosed(cefBrowser):
235243
return False
244+
if not cefFrame.get().GetBrowser().get():
245+
return False
236246

237247
pyBrowser = GetPyBrowser(cefBrowser, "GetAuthCredentials")
238248
pyFrame = GetPyFrame(cefFrame)

src/handlers/v8context_handler.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ cdef public void V8ContextHandler_OnContextCreated(
2222
cdef object clientCallback
2323
cdef JavascriptBindings jsBindings
2424
try:
25+
if not cefFrame.get().GetBrowser().get():
26+
return
2527
pyBrowser = GetPyBrowser(cefBrowser, "OnContextCreated")
2628
pyBrowser.SetUserData("__v8ContextCreated", True)
2729
pyFrame = GetPyFrame(cefFrame)

0 commit comments

Comments
 (0)