remote/CommandLineArgs.cpp (239 lines of code) (raw):
#include "CommandLineArgs.h"
#include "CefSettingsParser.h"
#include "Utils.h"
#include "log/Log.h"
#if defined(OS_MAC)
#include <boost/filesystem.hpp>
namespace CefUtils {
std::string getFrameworkDir();
}
#elif defined(OS_WIN)
#include <boost/filesystem.hpp>
#endif
namespace {
const bool doTrace = getBoolEnv("CEF_SERVER_TRACE_CommandLineArgs");
const bool dontUseDefaultChromiumSwitches = getBoolEnv("CEF_SERVER_TRACE_DontUseDefaultChromiumSwitches");
}
CommandLineArgs::CommandLineArgs() {
const long defVal = myOpenTransportCooldownMs;
myOpenTransportCooldownMs = getLongEnv("CEF_SERVER_TRANSPORT_OPEN_COOLDOWN_MS", defVal);
if (myOpenTransportCooldownMs != defVal) {
if (myOpenTransportCooldownMs < 0) myOpenTransportCooldownMs = 0;
if (myOpenTransportCooldownMs > 500) myOpenTransportCooldownMs = 500;
fprintf(stderr, "\tUse OpenTransportCooldownMs=%d\n", myOpenTransportCooldownMs);
}
}
void trace(const std::string & from, const std::vector<std::string> & cmdlineSwitches, const std::vector<std::pair<std::string, std::string>> & parsedSettings, const std::vector<std::pair<std::string, int>> & schemes) {
if (!doTrace || !Log::isTraceEnabled())
return;
if (!cmdlineSwitches.empty()) {
Log::trace("Command line switches (%s):", from.c_str());
for (auto& sw : cmdlineSwitches)
Log::trace("\t%s", sw.c_str());
}
if (!parsedSettings.empty()) {
Log::trace("Settings (%s):", from.c_str());
for (auto& st : parsedSettings)
Log::trace("\t%s=%s", st.first.c_str(), st.second.c_str());
}
if (!schemes.empty()) {
Log::trace("Custom schemes (%s):", from.c_str());
for (auto& sch : schemes)
Log::trace("\t%s [%d]", sch.first.c_str(), sch.second);
}
}
bool CommandLineArgs::init(int argc, char* argv[]) {
// This method is called very early.
// 1. Initialize logger at first and check '--help'
if (doTrace)
Log::trace("CommandLineArgs: init with args:");
for (int c = 0; c < argc; ++c) {
const char * arg = argv[c];
if (arg == nullptr || arg[0] == 0) continue;
std::string word(arg);
size_t tokenPos;
if (word.compare("--help") == 0 || word.compare("-h") == 0) {
fprintf(stdout, "Usage: cef-server [--port=PORT] [--params=PARAMS_FILE] [--loglevel=LEVEL] [--logfile=FILE]\nOther switches are enumerated in CommandLineArgs.cpp\n");
return false;
}
if ((tokenPos = word.find("--logfile=")) != word.npos) {
myPathLogFile = word.substr(tokenPos + 10);
} else if ((tokenPos = word.find("--loglevel=")) != word.npos) {
std::string sval = word.substr(tokenPos + 11);
try {
myLogLevel = std::stoi(sval);
} catch (const std::exception&) {
myLogLevel = Log::str2level(word);
}
}
if (doTrace)
Log::trace("\t%s", word.c_str());
} // for
Log::init(myLogLevel, myPathLogFile);
// 2. Parse other command line arguments.
for (int c = 0; c < argc; ++c) {
const char * arg = argv[c];
if (arg == nullptr || arg[0] == 0) continue;
// NOTE: these switches don't conflict with chromium one.
// See https://peter.sh/experiments/chromium-command-line-switches/
std::string word(arg);
if (word.find("--loglevel") == 0 || word.find("--logfile") == 0)
continue;
if (doTrace)
Log::trace("\tprocess cmd line arg: %s", word.c_str());
if (word == "--cef-server-wait-debugger") {
myWaitDebugger = true;
continue;
}
std::string stmp;
int ntmp;
size_t tokenPos;
if ((tokenPos = word.find("--port=")) != word.npos) {
std::string val = word.substr(tokenPos + 7);
myPort = std::stoi(val);
myUseTcp = true;
} else if ((tokenPos = word.find("--pipe=")) != word.npos) {
myPathPipe = word.substr(tokenPos + 7);
myUseTcp = false;
} else if ((tokenPos = word.find("--params=")) != word.npos) {
myPathParamsFile = word.substr(tokenPos + 9);
} else if ((tokenPos = word.find("--root=")) != word.npos) {
myPathRootCache = word.substr(tokenPos + 7);
} else if ((tokenPos = word.find("--logchromiumfile=")) != word.npos) {
myPathChromiumLogFile = word.substr(tokenPos + 18);
} else if ((tokenPos = word.find("--logchromiumlevel=")) != word.npos) {
std::string sval = word.substr(tokenPos + 19);
try {
myLogLevelChromium = std::stoi(sval);
} catch (const std::exception&) {}
} else if ((tokenPos = word.find("--deleteRootCacheDir")) != word.npos) {
myDeleteRootCacheDir = true;
} else if (CefSettingsParser::parseCefCmdLineSwitch(word, stmp)) {
myChromiumSwitches.push_back(stmp);
} else if (CefSettingsParser::parseCefSchemeWord(word, stmp, ntmp)) {
myCustomSchemes.push_back(std::make_pair(stmp, ntmp));
} else if (CefSettingsParser::parseCefSettingWord(word, myParsedCefSettings)) {
; // nothing to do
} else if (word.find("--") == 0) {
myChromiumSwitches.push_back(word);
} else {
if (doTrace)
Log::trace("Parse command line: skip unknown word %s", word.c_str());
}
} // for
trace("command line", myChromiumSwitches, myParsedCefSettings, myCustomSchemes);
if (!myPathParamsFile.empty()) {
std::vector<std::pair<std::string, std::string>> fileSettings;
std::vector<std::string> fileSwitches;
std::vector<std::pair<std::string, int>> fileSchemes;
CefSettingsParser::parseParamsFile(myPathParamsFile, fileSwitches, fileSettings, fileSchemes);
if (!fileSwitches.empty() || !fileSettings.empty() || !fileSchemes.empty()) {
Log::debug("Params file isn't empty, some command line arguments can be overriden.");
trace("file", fileSwitches, fileSettings, fileSchemes);
myChromiumSwitches.insert(myChromiumSwitches.end(), fileSwitches.begin(), fileSwitches.end());
myParsedCefSettings.insert(myParsedCefSettings.end(), fileSettings.begin(), fileSettings.end());
myCustomSchemes.insert(myCustomSchemes.end(), fileSchemes.begin(), fileSchemes.end());
}
}
// Init default chromium switches and CefSettings (if necessary)
if (myChromiumSwitches.empty() && !dontUseDefaultChromiumSwitches) { // NOTE: dontUseDefaultChromiumSwitches == false by default
Log::debug("Use default chromium switches.");
#if defined(OS_WIN)
myChromiumSwitches.push_back("--disable-features=SpareRendererForSitePerProcess");
myChromiumSwitches.push_back("--disable-gpu-process-crash-limit");
myChromiumSwitches.push_back("--autoplay-policy=no-user-gesture-required");
myChromiumSwitches.push_back("--disable-component-update");
#elif defined(OS_MAC)
myChromiumSwitches.push_back("--disable-in-process-stack-traces");
myChromiumSwitches.push_back("--use-mock-keychain");
myChromiumSwitches.push_back("--disable-features=SpareRendererForSitePerProcess");
myChromiumSwitches.push_back("--disable-notifications");
myChromiumSwitches.push_back("--disable-gpu-process-crash-limit");
myChromiumSwitches.push_back("--autoplay-policy=no-user-gesture-required");
myChromiumSwitches.push_back("--disable-component-update");
#else
myChromiumSwitches.push_back("--disable-features=SpareRendererForSitePerProcess");
myChromiumSwitches.push_back("--disable-gpu-process-crash-limit");
myChromiumSwitches.push_back("--autoplay-policy=no-user-gesture-required");
myChromiumSwitches.push_back("--disable-component-update");
myChromiumSwitches.push_back("--no-proxy-server");
#endif
} // myChromiumSwitches.empty()
if (myParsedCefSettings.empty()) {
Log::debug("Use default cef settings.");
myParsedCefSettings.push_back(std::make_pair("log_severity", Log::cefLogLevel2str(myLogLevelChromium)));
if (!myPathChromiumLogFile.empty())
myParsedCefSettings.push_back(std::make_pair("log_file", myPathChromiumLogFile));
}
trace("merged", myChromiumSwitches, myParsedCefSettings, myCustomSchemes);
return true;
}
void CommandLineArgs::prepareCefSettings(void * pCefSettings) {
CefSettings & settings = *reinterpret_cast<CefSettings*>(pCefSettings);
for (const auto & p: myParsedCefSettings) {
if (p.first.compare("cache_path") == 0 && !myPathRootCache.empty()) {
Log::debug("Setting 'cache_path' from params file (or cmd line) with value '%s' will be overrriden with cmd line arg '--root=%s'", p.second.c_str(), myPathRootCache.c_str());
CefString(&settings.cache_path) = myPathRootCache;
} else
CefSettingsParser::setSettingItem(settings, p.first, p.second);
}
settings.windowless_rendering_enabled = true;
settings.multi_threaded_message_loop = false;
settings.external_message_pump = false;
settings.no_sandbox = true; // TODO: support sandbox later.
#if defined(OS_MAC)
CefString(&settings.framework_dir_path) = CefUtils::getFrameworkDir();
if (settings.browser_subprocess_path.length == 0) {
// example: browser_subprocess_path=/Users/bocha/Downloads/jbr_jcef-25.0.1-osx-aarch64-b245.32/Contents/Frameworks/cef_server.app/Contents/Frameworks/cef_server Helper.app/Contents/MacOS/cef_server Helper
boost::filesystem::path path = boost::filesystem::current_path()
.append("..")
.append("Frameworks")
.append("cef_server Helper.app")
.append("Contents")
.append("MacOS")
.append("cef_server Helper")
.lexically_normal();
if (utils::isFileExist(path.c_str())) {
Log::debug("Set CefSettings.browser_subprocess_path=%s", path.c_str());
CefString(&settings.browser_subprocess_path) = path.string();
} else
Log::error("Empty browser_subprocess_path.");
}
#elif defined(OS_WIN)
const bool doSetResourcesPath = getBoolEnv("CEF_SERVER_CommandLineArgs_SetResourcesPath");
if (doSetResourcesPath) {
auto installation_root =
boost::filesystem::current_path().append("..").lexically_normal();
boost::filesystem::path resources_dir_path =
installation_root.append("lib");
boost::filesystem::path framework_dir_path =
installation_root.append("bin");
std::string resources_path = resources_dir_path.string();
std::string locales_dir_path =
resources_dir_path.append("locales").string();
CefString(&settings.resources_dir_path).FromString(resources_path);
CefString(&settings.locales_dir_path).FromString(locales_dir_path);
Log::debug("Set CefSettings.resources_dir_path=%s", resources_path.c_str());
Log::debug("Set CefSettings.locales_dir_path=%s", locales_dir_path.c_str());
}
#endif
#if defined(OS_POSIX)
settings.disable_signal_handlers = true;
#endif
if (Log::isTraceEnabled()) {
std::stringstream ss;
ss << "browser_subprocess_path=" << CefString(&settings.browser_subprocess_path).c_str() << std::endl;
ss << "cache_path=" << CefString(&settings.cache_path).c_str() << std::endl;
ss << "user_agent_product=" << CefString(&settings.user_agent_product).c_str() << std::endl;
ss << "user_agent=" << CefString(&settings.user_agent).c_str() << std::endl;
ss << "locales_dir_path=" << CefString(&settings.locales_dir_path).c_str() << std::endl;
ss << "locale=" << CefString(&settings.locale).c_str() << std::endl;
ss << "log_file=" << CefString(&settings.log_file).c_str() << std::endl;
ss << "log_severity=" << settings.log_severity << std::endl;
ss << "javascript_flags=" << CefString(&settings.javascript_flags).c_str() << std::endl;
ss << "resources_dir_path=" << CefString(&settings.resources_dir_path).c_str() << std::endl;
ss << "cookieable_schemes_list=" << CefString(&settings.cookieable_schemes_list).c_str() << std::endl;
ss << "windowless_rendering_enabled=" << settings.windowless_rendering_enabled << std::endl;
ss << "command_line_args_disabled=" << settings.command_line_args_disabled << std::endl;
ss << "persist_session_cookies=" << settings.persist_session_cookies << std::endl;
ss << "cookieable_schemes_exclude_defaults=" << settings.cookieable_schemes_exclude_defaults << std::endl;
ss << "no_sandbox=" << settings.no_sandbox << std::endl;
ss << "remote_debugging_port=" << settings.remote_debugging_port << std::endl;
ss << "uncaught_exception_stack_size=" << settings.uncaught_exception_stack_size << std::endl;
ss << "background_color=" << settings.background_color << std::endl;
Log::trace("Prepared CefSettings:\n%s", ss.str().c_str());
}
}