Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Client/cefweb/CWebApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ namespace

// Enable external begin frame scheduling for MTA-controlled rendering
commandLine->AppendSwitch("enable-begin-frame-scheduling");
// Keep CEF from downgrading renderers to best-effort priority. On
// Windows 11 22H2+ that path calls SetProcessInformation with
// ProcessPowerThrottling to enable EcoQoS, which can hold the game
// thread for a long time when the first browser is opened. Chromium
// 144 applies EcoQoS from SetPriority() without a feature gate, and
// this switch is the only one that prevents the downgrade, so do not
// replace it with a disable-features entry.
commandLine->AppendSwitch("disable-renderer-backgrounding");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is how much CPU usage will increase after disabling EcoQoS? Perhaps it would be worth keeping it as an option that can be enabled in the settings? If the difference is significant, it could be useful for laptop users.

// Explicitly block account sign-in to avoid crashes when Google API keys are registered on the system
commandLine->AppendSwitchWithValue("allow-browser-signin", "false");

Expand Down
102 changes: 91 additions & 11 deletions Client/cefweb/CWebCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,60 @@ bool CWebCore::Initialise(bool gpuEnabled)

// Set DLL directory for CEFLauncher subprocess to locate required libraries
SString strCEFDir = PathJoin(strMTADir, "CEF");

#ifdef _WIN32
SetDllDirectoryW(FromUTF8(strCEFDir));
// Save the previous DLL search directory so a failed init can put it back
std::wstring previousDllDir;
bool previousDllDirKnown = true;
{
const DWORD len = GetDllDirectoryW(0, nullptr);
if (len > 0)
{
previousDllDir.resize(len);
// A successful read returns the path length without the trailing
// null, which is always smaller than the buffer size. A larger
// return means the path grew between the two calls and the buffer
// was not filled, so the previous path stays unknown.
const DWORD got = GetDllDirectoryW(len, previousDllDir.data());
if (got > 0 && got < len)
{
while (!previousDllDir.empty() && previousDllDir.back() == L'\0')
previousDllDir.pop_back();
Comment on lines +130 to +131

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetDllDirectoryW returns the length of the path excluding the terminating null character, so resize(got) is sufficient here and avoids manually removing trailing null characters.

}
else
{
// The read failed or the buffer was too small, so the previous
// path is unknown. Leave the search path untouched on restore
// instead of clearing it.
previousDllDir.clear();
previousDllDirKnown = false;
}
}
}

// Guard: failed init restores the loader search path. A successful
// init keeps it, since CEF loads its DLLs on demand after initialization.
struct DllDirGuard
{
std::wstring dir;
bool known = true;
bool restore = true;
explicit DllDirGuard(std::wstring d, bool isKnown) : dir(std::move(d)), known(isKnown) {}
~DllDirGuard()
{
if (restore && known)
SetDllDirectoryW(dir.empty() ? nullptr : dir.c_str());
}
} dllDirGuard(std::move(previousDllDir), previousDllDirKnown);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use std::scope_exit here instead of the custom DllDirGuard? Since we're using C++23, this would give us the same RAII based rollback behavior with less code. We can call release() after successful initialization so the CEF DLL directory remains active, while all failure paths automatically restore the previous directory.


// CEFLauncher cannot find the CEF DLLs without this, so fail closed here
if (!SetDllDirectoryW(FromUTF8(strCEFDir)))
{
g_pCore->GetConsole()->Printf("CEF initialization skipped - failed to set DLL directory: %s", *strCEFDir);
AddReportLog(8032, SString("CEF initialization skipped - failed to set DLL directory: %s", *strCEFDir));
m_bInitialised = false;
return false;
}
#else
// On Wine/Proton: Use environment variable for library search
const char* existingPath = std::getenv("LD_LIBRARY_PATH");
Expand All @@ -134,10 +186,6 @@ bool CWebCore::Initialise(bool gpuEnabled)
}
#endif

// Read GTA path from registry to pass to CEF subprocess
int iRegistryResult = 0;
const SString strGTAPath = GetCommonRegistryValue("", "GTA:SA Path", &iRegistryResult);

// Check if process is running with elevated privileges
// CEF subprocesses may have communication issues when running elevated
const bool bIsElevated = []() -> bool
Expand Down Expand Up @@ -223,7 +271,8 @@ bool CWebCore::Initialise(bool gpuEnabled)
const fs::path savedCwd = fs::current_path(ec);
if (ec)
{
AddReportLog(8025, SString("Failed to get current directory: %s", ec.message().c_str()));
g_pCore->GetConsole()->Printf("CEF initialization skipped - Failed to get current directory: %s", ec.message().c_str());
AddReportLog(8025, SString("CEF initialization skipped - Failed to get current directory: %s", ec.message().c_str()));
m_bInitialised = false;
return false;
}
Expand All @@ -245,7 +294,8 @@ bool CWebCore::Initialise(bool gpuEnabled)
fs::current_path(fs::path(FromUTF8(strMTADir)), ec);
if (ec)
{
AddReportLog(8026, SString("Failed to change CWD to MTA dir: %s", ec.message().c_str()));
g_pCore->GetConsole()->Printf("CEF initialization skipped - Failed to change CWD to MTA dir: %s", ec.message().c_str());
AddReportLog(8029, SString("CEF initialization skipped - Failed to change CWD to MTA dir: %s", ec.message().c_str()));
m_bInitialised = false;
return false;
}
Expand All @@ -261,7 +311,6 @@ bool CWebCore::Initialise(bool gpuEnabled)
#endif

CefSettings settings;
CefString(&settings.browser_subprocess_path).FromWString(FromUTF8(strLauncherPath));
#ifndef CEF_ENABLE_SANDBOX
settings.no_sandbox = true;
#endif
Expand All @@ -280,12 +329,15 @@ bool CWebCore::Initialise(bool gpuEnabled)
settings.windowless_rendering_enabled = true;

// Wrap CefInitialize in try-catch for exception safety
bool bInitializeThrew = false;
try
{
m_bInitialised = CefInitialize(mainArgs, settings, app, sandboxInfo);
}
catch (...)
{
// Remember the throw so the failure log below is not written twice
bInitializeThrew = true;
g_pCore->GetConsole()->Printf("CefInitialize threw exception - CEF features will be disabled");
AddReportLog(8003, "CefInitialize threw exception - CEF features will be disabled");
m_bInitialised = false;
Expand All @@ -295,10 +347,38 @@ bool CWebCore::Initialise(bool gpuEnabled)

if (m_bInitialised)
{
// Register custom scheme handler factory only if initialization succeeded
CefRegisterSchemeHandlerFactory("http", "mta", app);
#ifdef _WIN32
// Keep the CEF DLL directory: CEF loads its DLLs on demand after init
dllDirGuard.restore = false;
#endif
// Register custom scheme handler factory only if initialization succeeded.
// A missing handler breaks mta:// pages, so a throw or a failed
// registration disables browser features instead of running CEF
// half-registered. CEF is already up, so the DLL directory stays set.
bool bSchemeHandlerThrew = false;
bool bSchemeHandlerRegistered = false;
try
{
bSchemeHandlerRegistered = CefRegisterSchemeHandlerFactory("http", "mta", app);
}
catch (...)
{
bSchemeHandlerThrew = true;
g_pCore->GetConsole()->Printf("CefRegisterSchemeHandlerFactory threw exception - CEF features will be disabled");
AddReportLog(8030, "CefRegisterSchemeHandlerFactory threw exception - CEF features will be disabled");
}

if (!bSchemeHandlerRegistered)
{
if (!bSchemeHandlerThrew)
{
g_pCore->GetConsole()->Printf("CefRegisterSchemeHandlerFactory failed - CEF features will be disabled");
AddReportLog(8031, "CefRegisterSchemeHandlerFactory failed - CEF features will be disabled");
}
m_bInitialised = false;
}
}
else
else if (!bInitializeThrew)
{
// Log initialization failure
g_pCore->GetConsole()->Printf("CefInitialize failed - CEF features will be disabled");
Expand Down
Loading