alloy: Add callback for already running app relaunch (fixes #3615)

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

Adds "Root Cache Path" value and related explainer text to
chrome://version.

Adds a LOG(WARNING) that will be output on startup if
CefSettings.root_cache_path is unset in the client app.
This commit is contained in:
Marshall Greenblatt
2023-12-04 18:45:54 -05:00
parent 5613af8f53
commit 0dd7dca229
12 changed files with 233 additions and 30 deletions

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=9e91adb231d67a65ce02294a0806d7effd40d280$ // $hash=dad764dddf92655cc580e0e7e85d3f3c34bdd46d$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_PROCESS_HANDLER_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_PROCESS_HANDLER_CAPI_H_
@ -122,8 +122,7 @@ typedef struct _cef_browser_process_handler_t {
/// therefore check the cef_initialize() return value for early exit before /// therefore check the cef_initialize() return value for early exit before
/// proceeding. /// proceeding.
/// ///
/// This function will be called on the browser process UI thread. Currently /// This function will be called on the browser process UI thread.
/// only used with the chrome runtime.
/// ///
int(CEF_CALLBACK* on_already_running_app_relaunch)( int(CEF_CALLBACK* on_already_running_app_relaunch)(
struct _cef_browser_process_handler_t* self, struct _cef_browser_process_handler_t* self,

View File

@ -113,8 +113,7 @@ class CefBrowserProcessHandler : public virtual CefBaseRefCounted {
/// therefore check the CefInitialize() return value for early exit before /// therefore check the CefInitialize() return value for early exit before
/// proceeding. /// proceeding.
/// ///
/// This method will be called on the browser process UI thread. Currently /// This method will be called on the browser process UI thread.
/// only used with the chrome runtime.
/// ///
/*--cef(optional_param=current_directory)--*/ /*--cef(optional_param=current_directory)--*/
virtual bool OnAlreadyRunningAppRelaunch( virtual bool OnAlreadyRunningAppRelaunch(

View File

@ -279,31 +279,46 @@ typedef struct _cef_settings_t {
int command_line_args_disabled; int command_line_args_disabled;
/// ///
/// The location where data for the global browser cache will be stored on /// The directory where data for the global browser cache will be stored on
/// disk. If this value is non-empty then it must be an absolute path that is /// disk. If this value is non-empty then it must be an absolute path that is
/// either equal to or a child directory of CefSettings.root_cache_path. If /// either equal to or a child directory of CefSettings.root_cache_path. If
/// this value is empty then browsers will be created in "incognito mode" /// this value is empty then browsers will be created in "incognito mode"
/// where in-memory caches are used for storage and no data is persisted to /// where in-memory caches are used for storage and no profile-specific data
/// disk. HTML5 databases such as localStorage will only persist across /// is persisted to disk (installation-specific data will still be persisted
/// sessions if a cache path is specified. Can be overridden for individual /// in root_cache_path). HTML5 databases such as localStorage will only
/// CefRequestContext instances via the CefRequestContextSettings.cache_path /// persist across sessions if a cache path is specified. Can be overridden
/// value. When using the Chrome runtime the "default" profile will be used if /// for individual CefRequestContext instances via the
/// |cache_path| and |root_cache_path| have the same value. /// CefRequestContextSettings.cache_path value. When using the Chrome runtime
/// any child directory value will be ignored and the "default" profile (also
/// a child directory) will be used instead.
/// ///
cef_string_t cache_path; cef_string_t cache_path;
/// ///
/// The root directory that all CefSettings.cache_path and /// The root directory for installation-specific data and the parent directory
/// CefRequestContextSettings.cache_path values must have in common. If this /// for profile-specific data. All CefSettings.cache_path and
/// value is empty and CefSettings.cache_path is non-empty then it will /// CefRequestContextSettings.cache_path values must have this parent
/// default to the CefSettings.cache_path value. If both values are empty /// directory in common. If this value is empty and CefSettings.cache_path is
/// then the default platform-specific directory will be used /// non-empty then it will default to the CefSettings.cache_path value. Any
/// non-empty value must be an absolute path. If both values are empty then
/// the default platform-specific directory will be used
/// ("~/.config/cef_user_data" directory on Linux, "~/Library/Application /// ("~/.config/cef_user_data" directory on Linux, "~/Library/Application
/// Support/CEF/User Data" directory on MacOS, "AppData\Local\CEF\User Data" /// Support/CEF/User Data" directory on MacOS, "AppData\Local\CEF\User Data"
/// directory under the user profile directory on Windows). If this value is /// directory under the user profile directory on Windows). Use of the default
/// non-empty then it must be an absolute path. Failure to set this value /// directory is not recommended in production applications (see below).
/// correctly may result in the sandbox blocking read/write access to certain ///
/// files. /// Multiple application instances writing to the same root_cache_path
/// directory could result in data corruption. A process singleton lock based
/// on the root_cache_path value is therefore used to protect against this.
/// This singleton behavior applies to all CEF-based applications using
/// version 120 or newer. You should customize root_cache_path for your
/// application and implement CefBrowserProcessHandler::
/// OnAlreadyRunningAppRelaunch, which will then be called on any app relaunch
/// with the same root_cache_path value.
///
/// Failure to set the root_cache_path value correctly may result in startup
/// crashes or other unexpected behaviors (for example, the sandbox blocking
/// read/write access to certain files).
/// ///
cef_string_t root_cache_path; cef_string_t root_cache_path;
@ -506,14 +521,15 @@ typedef struct _cef_request_context_settings_t {
size_t size; size_t size;
/// ///
/// The location where cache data for this request context will be stored on /// The directory where cache data for this request context will be stored on
/// disk. If this value is non-empty then it must be an absolute path that is /// disk. If this value is non-empty then it must be an absolute path that is
/// either equal to or a child directory of CefSettings.root_cache_path. If /// either equal to or a child directory of CefSettings.root_cache_path. If
/// this value is empty then browsers will be created in "incognito mode" /// this value is empty then browsers will be created in "incognito mode"
/// where in-memory caches are used for storage and no data is persisted to /// where in-memory caches are used for storage and no profile-specific data
/// disk. HTML5 databases such as localStorage will only persist across /// is persisted to disk (installation-specific data will still be persisted
/// sessions if a cache path is specified. To share the global browser cache /// in root_cache_path). HTML5 databases such as localStorage will only
/// and related configuration set this value to match the /// persist across sessions if a cache path is specified. To share the global
/// browser cache and related configuration set this value to match the
/// CefSettings.cache_path value. /// CefSettings.cache_path value.
/// ///
cef_string_t cache_path; cef_string_t cache_path;

View File

@ -19,6 +19,7 @@
#include "libcef/browser/permission_prompt.h" #include "libcef/browser/permission_prompt.h"
#include "libcef/browser/thread_util.h" #include "libcef/browser/thread_util.h"
#include "libcef/common/app_manager.h" #include "libcef/common/app_manager.h"
#include "libcef/common/command_line_impl.h"
#include "libcef/common/extensions/extensions_util.h" #include "libcef/common/extensions/extensions_util.h"
#include "libcef/common/net/net_resource_provider.h" #include "libcef/common/net/net_resource_provider.h"
@ -27,6 +28,7 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_process_singleton.h"
#include "chrome/browser/media/router/chrome_media_router_factory.h" #include "chrome/browser/media/router/chrome_media_router_factory.h"
#include "chrome/browser/net/system_network_context_manager.h" #include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/ui/color/chrome_color_mixers.h" #include "chrome/browser/ui/color/chrome_color_mixers.h"
@ -141,6 +143,56 @@ ui::LinuxUi* GetLinuxUI() {
#endif // BUILDFLAG(IS_LINUX) #endif // BUILDFLAG(IS_LINUX)
void ProcessSingletonNotificationCallbackImpl(
const base::CommandLine& command_line,
const base::FilePath& current_directory) {
// Drop the request if the browser process is already shutting down.
if (!CONTEXT_STATE_VALID()) {
return;
}
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(),
current_directory.value());
std::ignore = commandLinePtr->Detach(nullptr);
}
}
if (!handled) {
LOG(WARNING) << "Unhandled app relaunch; implement "
"CefBrowserProcessHandler::OnAlreadyRunningAppRelaunch.";
}
}
// Based on ChromeBrowserMainParts::ProcessSingletonNotificationCallback.
bool ProcessSingletonNotificationCallback(
const base::CommandLine& command_line,
const base::FilePath& current_directory) {
// Drop the request if the browser process is already shutting down.
// Note that we're going to post an async task below. Even if the browser
// process isn't shutting down right now, it could be by the time the task
// starts running. So, an additional check needs to happen when it starts.
// But regardless of any future check, there is no reason to post the task
// now if we know we're already shutting down.
if (!CONTEXT_STATE_VALID()) {
return false;
}
// In order to handle this request on Windows, there is platform specific
// code in browser_finder.cc that requires making outbound COM calls to
// cross-apartment shell objects (via IVirtualDesktopManager). That is not
// allowed within a SendMessage handler, which this function is a part of.
// So, we post a task to asynchronously finish the command line processing.
return base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&ProcessSingletonNotificationCallbackImpl,
command_line, current_directory));
}
} // namespace } // namespace
AlloyBrowserMainParts::AlloyBrowserMainParts() = default; AlloyBrowserMainParts::AlloyBrowserMainParts() = default;
@ -262,6 +314,10 @@ int AlloyBrowserMainParts::PreCreateThreads() {
return 0; return 0;
} }
void AlloyBrowserMainParts::PostCreateThreads() {
ChromeProcessSingleton::GetInstance()->StartWatching();
}
int AlloyBrowserMainParts::PreMainMessageLoopRun() { int AlloyBrowserMainParts::PreMainMessageLoopRun() {
#if defined(USE_AURA) #if defined(USE_AURA)
screen_ = views::CreateDesktopScreen(); screen_ = views::CreateDesktopScreen();
@ -339,6 +395,12 @@ int AlloyBrowserMainParts::PreMainMessageLoopRun() {
} }
#endif #endif
// Allow ProcessSingleton to process messages.
// This is done here instead of just relying on the main message loop's start
// to avoid rendezvous in RunLoops that may precede MainMessageLoopRun.
ChromeProcessSingleton::GetInstance()->Unlock(
base::BindRepeating(&ProcessSingletonNotificationCallback));
return content::RESULT_CODE_NORMAL_EXIT; return content::RESULT_CODE_NORMAL_EXIT;
} }
@ -346,6 +408,8 @@ void AlloyBrowserMainParts::PostMainMessageLoopRun() {
// NOTE: Destroy objects in reverse order of creation. // NOTE: Destroy objects in reverse order of creation.
CefDevToolsManagerDelegate::StopHttpHandler(); CefDevToolsManagerDelegate::StopHttpHandler();
ChromeProcessSingleton::GetInstance()->Cleanup();
// There should be no additional references to the global CefRequestContext // There should be no additional references to the global CefRequestContext
// during shutdown. Did you forget to release a CefBrowser reference? // during shutdown. Did you forget to release a CefBrowser reference?
DCHECK(global_request_context_->HasOneRef()); DCHECK(global_request_context_->HasOneRef());

View File

@ -49,6 +49,7 @@ class AlloyBrowserMainParts : public content::BrowserMainParts {
void PreCreateMainMessageLoop() override; void PreCreateMainMessageLoop() override;
void PostCreateMainMessageLoop() override; void PostCreateMainMessageLoop() override;
int PreCreateThreads() override; int PreCreateThreads() override;
void PostCreateThreads() override;
int PreMainMessageLoopRun() override; int PreMainMessageLoopRun() override;
void PostMainMessageLoopRun() override; void PostMainMessageLoopRun() override;
void PostDestroyThreads() override; void PostDestroyThreads() override;

View File

@ -35,6 +35,7 @@
#include "chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.h" #include "chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.h"
#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
#include "chrome/browser/ui/webui/theme_source.h" #include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
#include "content/browser/renderer_host/debug_urls.h" #include "content/browser/renderer_host/debug_urls.h"
#include "content/public/browser/browser_url_handler.h" #include "content/public/browser/browser_url_handler.h"
@ -385,6 +386,9 @@ bool OnVersionUI(Profile* profile,
return false; return false;
} }
base::FilePath user_data_dir =
base::PathService::CheckedGet(chrome::DIR_USER_DATA);
TemplateParser parser; TemplateParser parser;
parser.Add("YEAR", MAKE_STRING(COPYRIGHT_YEAR)); parser.Add("YEAR", MAKE_STRING(COPYRIGHT_YEAR));
parser.Add("CEF", CEF_VERSION); parser.Add("CEF", CEF_VERSION);
@ -400,6 +404,7 @@ bool OnVersionUI(Profile* profile,
CefAppManager::Get()->GetContentClient()->browser()->GetUserAgent()); CefAppManager::Get()->GetContentClient()->browser()->GetUserAgent());
parser.Add("COMMANDLINE", GetCommandLine()); parser.Add("COMMANDLINE", GetCommandLine());
parser.Add("MODULEPATH", GetModulePath()); parser.Add("MODULEPATH", GetModulePath());
parser.Add("ROOTCACHEPATH", CefString(user_data_dir.value()));
parser.Add("CACHEPATH", CefString(profile->GetPath().value())); parser.Add("CACHEPATH", CefString(profile->GetPath().value()));
parser.Parse(&tmpl); parser.Parse(&tmpl);

View File

@ -24,16 +24,22 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_process_singleton.h"
#include "chrome/child/pdf_child_init.h" #include "chrome/child/pdf_child_init.h"
#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_result_codes.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/utility/chrome_content_utility_client.h" #include "chrome/utility/chrome_content_utility_client.h"
#include "components/component_updater/component_updater_paths.h" #include "components/component_updater/component_updater_paths.h"
#include "components/content_settings/core/common/content_settings_pattern.h" #include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/embedder_support/switches.h" #include "components/embedder_support/switches.h"
#include "components/metrics/persistent_histograms.h"
#include "components/viz/common/features.h" #include "components/viz/common/features.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
@ -45,7 +51,9 @@
#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/switches.h" #include "third_party/blink/public/common/switches.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/base/resource/scoped_startup_resource_bundle.h"
#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_paths.h"
#include "ui/base/ui_base_switches.h" #include "ui/base/ui_base_switches.h"
@ -69,6 +77,46 @@ const char* const kNonWildcardDomainNonPortSchemes[] = {
const size_t kNonWildcardDomainNonPortSchemesSize = const size_t kNonWildcardDomainNonPortSchemesSize =
std::size(kNonWildcardDomainNonPortSchemes); std::size(kNonWildcardDomainNonPortSchemes);
absl::optional<int> AcquireProcessSingleton(
const base::FilePath& user_data_dir) {
// Take the Chrome process singleton lock. The process can become the
// Browser process if it succeed to take the lock. Otherwise, the
// command-line is sent to the actual Browser process and the current
// process can be exited.
ChromeProcessSingleton::CreateInstance(user_data_dir);
ProcessSingleton::NotifyResult notify_result =
ChromeProcessSingleton::GetInstance()->NotifyOtherProcessOrCreate();
switch (notify_result) {
case ProcessSingleton::PROCESS_NONE:
break;
case ProcessSingleton::PROCESS_NOTIFIED: {
// Ensure there is an instance of ResourceBundle that is initialized for
// localized string resource accesses.
ui::ScopedStartupResourceBundle startup_resource_bundle;
printf("%s\n", base::SysWideToNativeMB(
base::UTF16ToWide(l10n_util::GetStringUTF16(
IDS_USED_EXISTING_BROWSER)))
.c_str());
return chrome::RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED;
}
case ProcessSingleton::PROFILE_IN_USE:
return chrome::RESULT_CODE_PROFILE_IN_USE;
case ProcessSingleton::LOCK_ERROR:
LOG(ERROR) << "Failed to create a ProcessSingleton for your profile "
"directory. This means that running multiple instances "
"would start multiple browser processes rather than "
"opening a new window in the existing process. Aborting "
"now to avoid profile corruption.";
return chrome::RESULT_CODE_PROFILE_IN_USE;
}
return absl::nullopt;
}
} // namespace } // namespace
AlloyMainDelegate::AlloyMainDelegate(CefMainRunnerHandler* runner, AlloyMainDelegate::AlloyMainDelegate(CefMainRunnerHandler* runner,
@ -87,6 +135,35 @@ absl::optional<int> AlloyMainDelegate::PreBrowserMain() {
return absl::nullopt; return absl::nullopt;
} }
absl::optional<int> AlloyMainDelegate::PostEarlyInitialization(
InvokedIn invoked_in) {
const auto* invoked_in_browser =
absl::get_if<InvokedInBrowserProcess>(&invoked_in);
if (!invoked_in_browser) {
return absl::nullopt;
}
// Based on ChromeMainDelegate::PostEarlyInitialization.
// The User Data dir is guaranteed to be valid as per PreSandboxStartup.
base::FilePath user_data_dir =
base::PathService::CheckedGet(chrome::DIR_USER_DATA);
// On platforms that support the process rendezvous, acquire the process
// singleton. In case of failure, it means there is already a running browser
// instance that handled the command-line.
if (auto process_singleton_result = AcquireProcessSingleton(user_data_dir);
process_singleton_result.has_value()) {
// To ensure that the histograms emitted in this process are reported in
// case of early exit, report the metrics accumulated this session with a
// future session's metrics.
DeferBrowserMetrics(user_data_dir);
return process_singleton_result;
}
return absl::nullopt;
}
absl::optional<int> AlloyMainDelegate::BasicStartupComplete() { absl::optional<int> AlloyMainDelegate::BasicStartupComplete() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string process_type = std::string process_type =

View File

@ -47,6 +47,7 @@ class AlloyMainDelegate : public content::ContentMainDelegate,
// content::ContentMainDelegate overrides. // content::ContentMainDelegate overrides.
absl::optional<int> PreBrowserMain() override; absl::optional<int> PreBrowserMain() override;
absl::optional<int> PostEarlyInitialization(InvokedIn invoked_in) override;
absl::optional<int> BasicStartupComplete() override; absl::optional<int> BasicStartupComplete() override;
void PreSandboxStartup() override; void PreSandboxStartup() override;
absl::variant<int, content::MainFunctionParams> RunProcess( absl::variant<int, content::MainFunctionParams> RunProcess(

View File

@ -13,6 +13,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/logging.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_constants.h"
@ -89,6 +90,10 @@ base::FilePath GetUserDataPath(CefSettings* settings,
} }
if (!root_cache_path.empty()) { if (!root_cache_path.empty()) {
return base::FilePath(root_cache_path); return base::FilePath(root_cache_path);
} else {
LOG(WARNING) << "Please customize CefSettings.root_cache_path for your "
"application. Use of the default value may lead to "
"unintended process singleton behavior.";
} }
} }

View File

@ -43,6 +43,10 @@ body {
content: ':'; content: ':';
} }
.footnote {
font-size: 0.8em;
}
#logo { #logo {
float: right; float: right;
margin-left: 40px; margin-left: 40px;
@ -109,9 +113,26 @@ body {
<td class="value">$$MODULEPATH$$</td> <td class="value">$$MODULEPATH$$</td>
</tr> </tr>
<tr> <tr>
<td class="label" valign="top">Cache Path</td> <td class="label" valign="top">Root Cache Path <sup>[1][2]</sup></td>
<td class="value">$$ROOTCACHEPATH$$</td>
</tr>
<tr>
<td class="label" valign="top">Cache Path <sup>[1]</sup></td>
<td class="value">$$CACHEPATH$$</td> <td class="value">$$CACHEPATH$$</td>
</tr> </tr>
<tr>
<td colspan="2" class="footnote">
<br>[1] In <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md" target="_new">Chromium terminology</a>,
"Root Cache Path" (<a href="https://cef-builds.spotifycdn.com/docs/beta.html?structcef__settings__t.html#a2e2be03f34ddd93de90e1cf196757a19" target="_new">CefSettings.root_cache_path</a>)
is the "User Data Directory" and "Cache Path" (<a href="https://cef-builds.spotifycdn.com/docs/beta.html?structcef__settings__t.html#ad1644a7eb23cad969181db010f007710" target="_new">CefSettings.cache_path</a>)
is the "Profile Path". An empty "Cache Path" value means that the browser is using an <a href="https://support.google.com/chrome/answer/9845881" target="_new">Incognito Profile</a>.
Even in Incognito mode, some per-installation data will still be written to the "Root Cache Path" directory.
<br/><br/>[2] Chromium's <a href="https://chromium.googlesource.com/chromium/src/+/master/chrome/browser/process_singleton.h#35" target="_new">process singleton lock</a>
protects against multiple app instances writing to the same "Root Cache Path" directory.
Implement <a href="https://cef-builds.spotifycdn.com/docs/beta.html?classCefBrowserProcessHandler.html#a052a91639483467c0b546d57a05c2f06" target="_new">CefBrowserProcessHandler:: OnAlreadyRunningAppRelaunch</a>
to handle the case of app relaunch with the same directory.
</td>
</tr>
</table> </table>
</div> </div>
</body> </body>

View File

@ -68,6 +68,21 @@ class ClientBrowserDelegate : public ClientAppBrowser::Delegate {
CefRefPtr<ClientAppBrowser> app, CefRefPtr<ClientAppBrowser> app,
CefRefPtr<CefCommandLine> command_line, CefRefPtr<CefCommandLine> command_line,
const CefString& current_directory) override { const CefString& current_directory) override {
// Add logging for some common switches that the user may attempt to use.
static const char* kIgnoredSwitches[] = {
switches::kEnableChromeRuntime,
switches::kMultiThreadedMessageLoop,
switches::kOffScreenRenderingEnabled,
switches::kUseViews,
};
for (size_t i = 0;
i < sizeof(kIgnoredSwitches) / sizeof(kIgnoredSwitches[0]); ++i) {
if (command_line->HasSwitch(kIgnoredSwitches[i])) {
LOG(WARNING) << "The --" << kIgnoredSwitches[i]
<< " command-line switch is ignored on app relaunch.";
}
}
// Create a new root window based on |command_line|. // Create a new root window based on |command_line|.
auto config = std::make_unique<RootWindowConfig>(command_line->Copy()); auto config = std::make_unique<RootWindowConfig>(command_line->Copy());

View File

@ -36,9 +36,9 @@ enum class WindowType {
// Used to configure how a RootWindow is created. // Used to configure how a RootWindow is created.
struct RootWindowConfig { struct RootWindowConfig {
// |command_line| will be non-nullptr when used for new window creation // |command_line| will be non-nullptr when used for new window creation via
// via OnAlreadyRunningAppRelaunch (chrome runtime only). Otherwise, the // OnAlreadyRunningAppRelaunch. Otherwise, the global command-line will be
// global command-line will be used. // used.
RootWindowConfig(CefRefPtr<CefCommandLine> command_line = nullptr); RootWindowConfig(CefRefPtr<CefCommandLine> command_line = nullptr);
// Associated command-line. // Associated command-line.