Open prashantkn94 opened 1 month ago
Recommended usage is to call CreateBrowser from OnContextInitialized. However, this still sounds like a bug.
CEF Version: [e.g. 111.2.2]
Please try with a supported CEF version (M130 or newer)
May be related to #3469
Hi @magreenblatt Thanks for looking into it. We are using the cef version - 116.0.0, I have updated it in the description. Has it been fixed in 130 or newer versions of cef ?
Has it been fixed in 130 or newer versions of cef ?
It may be, please test.
sure, I'll update you once we test it
Hi @magreenblatt Seems like the bug is still present on the latest cef version. I was able to reproduce it by modifying the cefsimple app slightly. I am creating a couple of browsers inside my window, and closing the first browser after a certain timer. You'll observe that it has been removed from the view, but yet the OnBeforeClose
will not be called and the corresponding renderer process remains active. Can you please help ?
Below is the cefsimple_mac.mm
#import <Cocoa/Cocoa.h>
#include "include/cef_application_mac.h"
#include "include/cef_command_line.h"
#include "include/wrapper/cef_helpers.h"
#include "include/wrapper/cef_library_loader.h"
#include "tests/cefsimple/simple_app.h"
#include "tests/cefsimple/simple_handler.h"
NSView* view1;
// Receives notifications from the application.
@interface SimpleAppDelegate : NSObject <NSApplicationDelegate>
- (void)createApplication:(id)object;
- (void)tryToTerminateApplication:(NSApplication*)app;
@end
// Provide the CefAppProtocol implementation required by CEF.
@interface SimpleApplication : NSApplication <CefAppProtocol> {
@private
BOOL handlingSendEvent_;
}
@end
@implementation SimpleApplication
- (BOOL)isHandlingSendEvent {
return handlingSendEvent_;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
handlingSendEvent_ = handlingSendEvent;
}
- (void)sendEvent:(NSEvent*)event {
CefScopedSendingEvent sendingEventScoper;
[super sendEvent:event];
}
- (void)terminate:(id)sender {
SimpleAppDelegate* delegate =
static_cast<SimpleAppDelegate*>([NSApp delegate]);
[delegate tryToTerminateApplication:self];
// Return, don't exit. The application is responsible for exiting on its own.
}
@end
@implementation SimpleAppDelegate
// Create the application on the UI thread.
- (void)createApplication:(id)object {
[[NSBundle mainBundle] loadNibNamed:@"MainMenu"
owner:NSApp
topLevelObjects:nil];
// Set the delegate for application events.
[[NSApplication sharedApplication] setDelegate:self];
}
- (void)tryToTerminateApplication:(NSApplication*)app {
SimpleHandler* handler = SimpleHandler::GetInstance();
if (handler) {
[view1 removeFromSuperview];
handler->CloseFirstBrowsers(true);
}
}
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication*)sender {
return NSTerminateNow;
}
// Called when the user clicks the app dock icon while the application is
// already running.
- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
hasVisibleWindows:(BOOL)flag {
SimpleHandler* handler = SimpleHandler::GetInstance();
if (handler && !handler->IsClosing()) {
handler->ShowMainWindow();
}
return NO;
}
@end
void createWindow() {
CEF_REQUIRE_UI_THREAD();
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
// Information used when creating the native window.
CefWindowInfo window_info;
NSRect frame = NSMakeRect(350, 350, 700, 700);
NSWindow* window = [[NSWindow alloc] initWithContentRect:frame
styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable)
backing:NSBackingStoreBuffered
defer:NO];
[window setBackgroundColor:[NSColor grayColor]];
[window makeKeyAndOrderFront:NSApp];
NSRect mainFrame = NSMakeRect(0, 0, 700, 700);
NSView *mainView = [[NSView alloc] initWithFrame:mainFrame];
[[window contentView] addSubview:mainView];
NSRect frame1 = NSMakeRect(0, 350, 700, 350);
view1 = [[NSView alloc] initWithFrame:frame1];
[mainView addSubview:view1];
CefRect bounds = CefRect(0, 0, NSWidth([view1 bounds]), NSHeight([view1 bounds]));
CefWindowHandle cefView1 = (__bridge CefWindowHandle)view1;
window_info.SetAsChild(cefView1, bounds);
CefBrowserHost::CreateBrowser(window_info, SimpleHandler::GetInstance(), "http://www.wikipedia.org", browser_settings,nullptr, nullptr);
NSRect frame2 = NSMakeRect(0, 0, 700, 350);
NSView* view2 = [[NSView alloc] initWithFrame:frame2];
[mainView addSubview:view2];
bounds = CefRect(0, 0, NSWidth([view2 bounds]), NSHeight([view2 bounds]));
CefWindowHandle cefView2 = (__bridge CefWindowHandle)view2;
CefWindowInfo window_info1;
window_info1.SetAsChild(cefView2, bounds);
CefBrowserHost::CreateBrowser(window_info1, SimpleHandler::GetInstance(), "https://www.google.com", browser_settings,nullptr, nullptr);
}
// Entry point function for the browser process.
int main(int argc, char* argv[]) {
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
CefScopedLibraryLoader library_loader;
if (!library_loader.LoadInMain()) {
return 1;
}
// Provide CEF with command-line arguments.
CefMainArgs main_args(argc, argv);
@autoreleasepool {
// Initialize the SimpleApplication instance.
[SimpleApplication sharedApplication];
// If there was an invocation to NSApp prior to this method, then the NSApp
// will not be a SimpleApplication, but will instead be an NSApplication.
// This is undesirable and we must enforce that this doesn't happen.
CHECK([NSApp isKindOfClass:[SimpleApplication class]]);
// Parse command-line arguments for use in this method.
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::CreateCommandLine();
command_line->InitFromArgv(argc, argv);
// Specify CEF global settings here.
CefSettings settings;
// When generating projects with CMake the CEF_USE_SANDBOX value will be
// defined automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line
// to disable use of the sandbox.
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
if (!CefInitialize(main_args, settings, SimpleApp::GetInstance().get(), nullptr)) {
return CefGetExitCode();
}
SimpleHandler *simple = new SimpleHandler(true);
// below line avoids compilation error
simple->IsClosing();
// Create the application delegate.
SimpleAppDelegate* delegate = [[SimpleAppDelegate alloc] init];
// Set as the delegate for application events.
NSApp.delegate = delegate;
[NSTimer scheduledTimerWithTimeInterval:10.0
repeats:NO
block:^(NSTimer * _Nonnull timer) {
[delegate performSelectorOnMainThread:@selector(tryToTerminateApplication:)
withObject:nil
waitUntilDone:NO];
}];
createWindow();
/*
[delegate performSelectorOnMainThread:@selector(createApplication:)
withObject:nil
waitUntilDone:NO];
*/
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
// Release the delegate.
#if !__has_feature(objc_arc)
[delegate release];
#endif // !__has_feature(objc_arc)
delegate = nil;
} // @autoreleasepool
return 0;
}
And here is the simple_app.cpp
#include "tests/cefsimple/simple_app.h"
#include <string>
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/cefsimple/simple_handler.h"
namespace {
// When using the Views framework this object provides the delegate
// implementation for the CefWindow that hosts the Views-based browser.
class SimpleWindowDelegate : public CefWindowDelegate {
public:
SimpleWindowDelegate(CefRefPtr<CefBrowserView> browser_view,
cef_runtime_style_t runtime_style,
cef_show_state_t initial_show_state)
: browser_view_(browser_view),
runtime_style_(runtime_style),
initial_show_state_(initial_show_state) {}
void OnWindowCreated(CefRefPtr<CefWindow> window) override {
// Add the browser view and show the window.
window->AddChildView(browser_view_);
if (initial_show_state_ != CEF_SHOW_STATE_HIDDEN) {
window->Show();
}
if (initial_show_state_ != CEF_SHOW_STATE_MINIMIZED &&
initial_show_state_ != CEF_SHOW_STATE_HIDDEN) {
// Give keyboard focus to the browser view.
browser_view_->RequestFocus();
}
}
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
browser_view_ = nullptr;
}
bool CanClose(CefRefPtr<CefWindow> window) override {
// Allow the window to close if the browser says it's OK.
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
if (browser) {
return browser->GetHost()->TryCloseBrowser();
}
return true;
}
CefSize GetPreferredSize(CefRefPtr<CefView> view) override {
return CefSize(800, 600);
}
cef_show_state_t GetInitialShowState(CefRefPtr<CefWindow> window) override {
return initial_show_state_;
}
cef_runtime_style_t GetWindowRuntimeStyle() override {
return runtime_style_;
}
private:
CefRefPtr<CefBrowserView> browser_view_;
const cef_runtime_style_t runtime_style_;
const cef_show_state_t initial_show_state_;
IMPLEMENT_REFCOUNTING(SimpleWindowDelegate);
DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
};
class SimpleBrowserViewDelegate : public CefBrowserViewDelegate {
public:
explicit SimpleBrowserViewDelegate(cef_runtime_style_t runtime_style)
: runtime_style_(runtime_style) {}
bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) override {
// Create a new top-level Window for the popup. It will show itself after
// creation.
CefWindow::CreateTopLevelWindow(new SimpleWindowDelegate(
popup_browser_view, runtime_style_, CEF_SHOW_STATE_NORMAL));
// We created the Window.
return true;
}
cef_runtime_style_t GetBrowserRuntimeStyle() override {
return runtime_style_;
}
private:
const cef_runtime_style_t runtime_style_;
IMPLEMENT_REFCOUNTING(SimpleBrowserViewDelegate);
DISALLOW_COPY_AND_ASSIGN(SimpleBrowserViewDelegate);
};
} // namespace
SimpleApp::SimpleApp() = default;
void SimpleApp::OnContextInitialized() {
}
CefRefPtr<SimpleApp> SimpleApp::GetInstance() {
static CefRefPtr<SimpleApp> simple = nullptr;
if (!simple) {
simple = new SimpleApp();
}
return simple;
}
CefRefPtr<CefClient> SimpleApp::GetDefaultClient() {
// Called when a new browser window is created via Chrome style UI.
return SimpleHandler::GetInstance();
}
and simple_handler.cc
#include "tests/cefsimple/simple_handler.h"
#include <sstream>
#include <string>
#include "include/base/cef_callback.h"
#include "include/cef_app.h"
#include "include/cef_parser.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
namespace {
SimpleHandler* g_instance = nullptr;
// Returns a data: URI with the specified contents.
std::string GetDataURI(const std::string& data, const std::string& mime_type) {
return "data:" + mime_type + ";base64," +
CefURIEncode(CefBase64Encode(data.data(), data.size()), false)
.ToString();
}
} // namespace
SimpleHandler::SimpleHandler(bool is_alloy_style)
: is_alloy_style_(is_alloy_style) {
DCHECK(!g_instance);
g_instance = this;
}
SimpleHandler::~SimpleHandler() {
g_instance = nullptr;
}
// static
SimpleHandler* SimpleHandler::GetInstance() {
return g_instance;
}
void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {
CEF_REQUIRE_UI_THREAD();
if (auto browser_view = CefBrowserView::GetForBrowser(browser)) {
// Set the title of the window using the Views framework.
CefRefPtr<CefWindow> window = browser_view->GetWindow();
if (window) {
window->SetTitle(title);
}
} else if (is_alloy_style_) {
// Set the title of the window using platform APIs.
PlatformTitleChange(browser, title);
}
}
void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Sanity-check the configured runtime style.
CHECK_EQ(is_alloy_style_ ? CEF_RUNTIME_STYLE_ALLOY : CEF_RUNTIME_STYLE_CHROME,
browser->GetHost()->GetRuntimeStyle());
// Add to the list of existing browsers.
browser_list_.push_back(browser);
}
bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Closing the main window requires special handling. See the DoClose()
// documentation in the CEF header for a detailed destription of this
// process.
if (browser_list_.size() == 1) {
// Set a flag to indicate that the window close should be allowed.
is_closing_ = true;
}
// Allow the close. For windowed browsers this will result in the OS close
// event being sent.
return false;
}
void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Remove from the list of existing browsers.
BrowserList::iterator bit = browser_list_.begin();
for (; bit != browser_list_.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
browser_list_.erase(bit);
break;
}
}
if (browser_list_.empty()) {
// All browser windows have closed. Quit the application message loop.
CefQuitMessageLoop();
}
}
void SimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {
CEF_REQUIRE_UI_THREAD();
// Allow Chrome to show the error page.
if (!is_alloy_style_) {
return;
}
// Don't display an error for downloaded files.
if (errorCode == ERR_ABORTED) {
return;
}
// Display a load error message using a data: URI.
std::stringstream ss;
ss << "<html><body bgcolor=\"white\">"
"<h2>Failed to load URL "
<< std::string(failedUrl) << " with error " << std::string(errorText)
<< " (" << errorCode << ").</h2></body></html>";
frame->LoadURL(GetDataURI(ss.str(), "text/html"));
}
void SimpleHandler::ShowMainWindow() {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&SimpleHandler::ShowMainWindow, this));
return;
}
if (browser_list_.empty()) {
return;
}
auto main_browser = browser_list_.front();
if (auto browser_view = CefBrowserView::GetForBrowser(main_browser)) {
// Show the window using the Views framework.
if (auto window = browser_view->GetWindow()) {
window->Show();
}
} else if (is_alloy_style_) {
PlatformShowWindow(main_browser);
}
}
void SimpleHandler::CloseFirstBrowsers(bool force_close) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&SimpleHandler::CloseFirstBrowsers, this,
force_close));
return;
}
if (browser_list_.empty()) {
return;
}
BrowserList::const_iterator it = browser_list_.begin();
(*it)->GetHost()->CloseBrowser(force_close);
}
#if !defined(OS_MAC)
void SimpleHandler::PlatformShowWindow(CefRefPtr<CefBrowser> browser) {
NOTIMPLEMENTED();
}
#endif
Thanks for the update. I suggest you use one of the working options until this issue is addressed.
Describe the bug
There are two approaches I can take to create a browser using the CefBrowserHost::CreateBrowser API:
In the main method, after initializing CEF, I can create the view where the browser will load and then call CreateBrowser immediately before invoking CefRunMessageLoop.
Alternatively, after initializing CEF, I can schedule the creation of the view and the subsequent CreateBrowser call using performSelectorOnMainThread. The issue arises when calling CloseBrowser. In approach 2, the OnBeforeClose callback is triggered as expected. However, in approach 1, the callback does not execute, and the renderer process remains active.
In both the cases, the view in which browser url is loaded is a child of parent main window. The CloseBrowser() is explicitly closed by my application.
I understand that this question is better suited for the CEF forum, but despite multiple attempts to register with different email addresses, I haven't received the activation link. I also reached out to ceforum@magpcss.org regarding the issue but haven't received any response.
Versions (please complete the following information):
Additional context