chrome: Add callback for already running app relaunch (fixes #3609)

Adds a new CefBrowserProcessHandler::OnAlreadyRunningAppRelaunch
callback for when an already running app is relaunched with the
same CefSettings.root_cache_path.

Client apps should check the CefInitialize() return value for early
exit of the relaunch source process.
This commit is contained in:
Marshall Greenblatt
2023-11-28 20:33:44 -05:00
parent d6af79e7a6
commit a25f89f9e4
45 changed files with 553 additions and 178 deletions

View File

@@ -5,6 +5,7 @@
#include "libcef/browser/chrome/chrome_browser_main_extra_parts_cef.h"
#include "libcef/browser/chrome/chrome_context_menu_handler.h"
#include "libcef/browser/chrome/chrome_startup_browser_creator.h"
#include "libcef/browser/context.h"
#include "libcef/browser/file_dialog_runner.h"
#include "libcef/browser/net/chrome_scheme_handler.h"
@@ -13,6 +14,10 @@
#include "base/task/thread_pool.h"
#include "chrome/browser/profiles/profile.h"
#if BUILDFLAG(IS_LINUX)
#include "base/linux_util.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/win/app_icon.h"
#endif
@@ -38,6 +43,18 @@ void ChromeBrowserMainExtraPartsCef::PostProfileInit(Profile* profile,
CefRequestContextImpl::CreateGlobalRequestContext(settings);
}
void ChromeBrowserMainExtraPartsCef::PostBrowserStart() {
// Register the callback before ChromeBrowserMainParts::PostBrowserStart
// allows ProcessSingleton to begin processing messages.
startup_browser_creator::RegisterProcessCommandLineCallback();
#if BUILDFLAG(IS_LINUX)
// This may be called indirectly via StartupBrowserCreator::LaunchBrowser.
// Call it here before blocking is disallowed to avoid assertions.
base::GetLinuxDistro();
#endif
}
void ChromeBrowserMainExtraPartsCef::PreMainMessageLoopRun() {
background_task_runner_ = base::ThreadPool::CreateSingleThreadTaskRunner(
{base::TaskPriority::BEST_EFFORT,

View File

@@ -42,6 +42,7 @@ class ChromeBrowserMainExtraPartsCef : public ChromeBrowserMainExtraParts {
private:
// ChromeBrowserMainExtraParts overrides.
void PostProfileInit(Profile* profile, bool is_initial_profile) override;
void PostBrowserStart() override;
void PreMainMessageLoopRun() override;
CefRefPtr<CefRequestContextImpl> global_request_context_;

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2023 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#include "libcef/browser/chrome/chrome_startup_browser_creator.h"
#include <tuple>
#include "libcef/browser/browser_context.h"
#include "libcef/common/app_manager.h"
#include "libcef/common/command_line_impl.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
namespace startup_browser_creator {
namespace {
bool ProcessCommandLineCallback(const base::CommandLine& command_line,
const base::FilePath& cur_dir) {
bool handled = false;
if (auto app = CefAppManager::Get()->GetApplication()) {
if (auto handler = app->GetBrowserProcessHandler()) {
CefRefPtr<CefCommandLineImpl> commandLinePtr(
new CefCommandLineImpl(command_line));
handled = handler->OnAlreadyRunningAppRelaunch(commandLinePtr.get(),
cur_dir.value());
std::ignore = commandLinePtr->Detach(nullptr);
}
}
return handled;
}
} // namespace
void RegisterProcessCommandLineCallback() {
StartupBrowserCreator::RegisterProcessCommandLineCallback(
base::BindRepeating(&ProcessCommandLineCallback));
}
} // namespace startup_browser_creator

View File

@@ -0,0 +1,16 @@
// Copyright (c) 2023 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_CHROME_CHROME_STARTUP_BROWSER_CREATOR_H_
#define CEF_LIBCEF_BROWSER_CHROME_CHROME_STARTUP_BROWSER_CREATOR_H_
#pragma once
namespace startup_browser_creator {
// Register the process launch command line callback.
void RegisterProcessCommandLineCallback();
} // namespace startup_browser_creator
#endif // CEF_LIBCEF_BROWSER_CHROME_CHROME_STARTUP_BROWSER_CREATOR_H_

View File

@@ -307,8 +307,15 @@ bool CefInitialize(const CefMainArgs& args,
g_context = new CefContext();
// Initialize the global context.
return g_context->Initialize(args, settings, application,
windows_sandbox_info);
if (!g_context->Initialize(args, settings, application,
windows_sandbox_info)) {
// Initialization failed. Delete the global context object.
delete g_context;
g_context = nullptr;
return false;
}
return true;
}
void CefShutdown() {
@@ -469,10 +476,16 @@ bool CefContext::Initialize(const CefMainArgs& args,
main_runner_.reset(new CefMainRunner(settings_.multi_threaded_message_loop,
settings_.external_message_pump));
return main_runner_->Initialize(
&settings_, application, args, windows_sandbox_info, &initialized_,
base::BindOnce(&CefContext::OnContextInitialized,
base::Unretained(this)));
if (!main_runner_->Initialize(
&settings_, application, args, windows_sandbox_info, &initialized_,
base::BindOnce(&CefContext::OnContextInitialized,
base::Unretained(this)))) {
shutting_down_ = true;
FinalizeShutdown();
return false;
}
return true;
}
void CefContext::RunMessageLoop() {

View File

@@ -21,6 +21,7 @@
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chrome/common/chrome_result_codes.h"
#include "components/crash/core/app/crash_switches.h"
#include "content/app/content_main_runner_impl.h"
#include "content/browser/scheduler/browser_task_executor.h"
@@ -227,15 +228,21 @@ bool CefMainRunner::Initialize(CefSettings* settings,
settings->chrome_runtime ? RuntimeType::CHROME : RuntimeType::ALLOY, this,
settings, application);
const int exit_code =
int exit_code =
ContentMainInitialize(args, windows_sandbox_info, &settings->no_sandbox);
if (exit_code >= 0) {
DCHECK(false) << "ContentMainInitialize failed";
LOG(ERROR) << "ContentMainInitialize failed with exit code " << exit_code;
return false;
}
if (!ContentMainRun(initialized, std::move(context_initialized))) {
DCHECK(false) << "ContentMainRun failed";
exit_code = ContentMainRun(initialized, std::move(context_initialized));
if (exit_code != content::RESULT_CODE_NORMAL_EXIT) {
// Some exit codes are used to exit early, but are otherwise a normal
// result. Don't log for those codes.
if (!chrome::IsNormalResultCode(
static_cast<chrome::ResultCode>(exit_code))) {
LOG(ERROR) << "ContentMainRun failed with exit code " << exit_code;
}
return false;
}
@@ -383,10 +390,12 @@ int CefMainRunner::ContentMainInitialize(const CefMainArgs& args,
main_runner_.get());
}
bool CefMainRunner::ContentMainRun(bool* initialized,
base::OnceClosure context_initialized) {
int CefMainRunner::ContentMainRun(bool* initialized,
base::OnceClosure context_initialized) {
main_delegate_->BeforeMainThreadRun(multi_threaded_message_loop_);
int exit_code = -1;
if (multi_threaded_message_loop_) {
// Detach the CommandLine from the main thread so that it can be
// attached and modified from the UI thread going forward.
@@ -397,14 +406,15 @@ bool CefMainRunner::ContentMainRun(bool* initialized,
base::WaitableEvent::InitialState::NOT_SIGNALED);
if (!CreateUIThread(base::BindOnce(
[](CefMainRunner* runner, base::WaitableEvent* event) {
[](CefMainRunner* runner, base::WaitableEvent* event,
int* exit_code) {
runner->main_delegate_->BeforeUIThreadInitialize();
content::ContentMainRun(runner->main_runner_.get());
*exit_code = content::ContentMainRun(runner->main_runner_.get());
event->Signal();
},
base::Unretained(this),
base::Unretained(&uithread_startup_event)))) {
return false;
base::Unretained(this), base::Unretained(&uithread_startup_event),
base::Unretained(&exit_code)))) {
return exit_code;
}
*initialized = true;
@@ -414,19 +424,23 @@ bool CefMainRunner::ContentMainRun(bool* initialized,
} else {
*initialized = true;
main_delegate_->BeforeUIThreadInitialize();
content::ContentMainRun(main_runner_.get());
exit_code = content::ContentMainRun(main_runner_.get());
}
if (CEF_CURRENTLY_ON_UIT()) {
OnContextInitialized(std::move(context_initialized));
} else {
// Continue initialization on the UI thread.
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMainRunner::OnContextInitialized,
base::Unretained(this),
std::move(context_initialized)));
if (exit_code == content::RESULT_CODE_NORMAL_EXIT) {
// content::ContentMainRun was successful and we're not exiting early.
if (CEF_CURRENTLY_ON_UIT()) {
OnContextInitialized(std::move(context_initialized));
} else {
// Continue initialization on the UI thread.
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefMainRunner::OnContextInitialized,
base::Unretained(this),
std::move(context_initialized)));
}
}
return true;
return exit_code;
}
void CefMainRunner::PreBrowserMain() {

View File

@@ -59,7 +59,7 @@ class CefMainRunner : public CefMainRunnerHandler {
int ContentMainInitialize(const CefMainArgs& args,
void* windows_sandbox_info,
int* no_sandbox);
bool ContentMainRun(bool* initialized, base::OnceClosure context_initialized);
int ContentMainRun(bool* initialized, base::OnceClosure context_initialized);
// CefMainRunnerHandler methods:
void PreBrowserMain() override;