diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 01db8e7f1..01ef7d1ae 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -166,12 +166,23 @@ typedef struct _cef_settings_t { /// // The path to a separate executable that will be launched for sub-processes. - // By default the browser process executable is used. See the comments on - // CefExecuteProcess() for details. Also configurable using the - // "browser-subprocess-path" command-line switch. + // If this value is empty on Windows or Linux then the main process executable + // will be used. If this value is empty on macOS then a helper executable must + // exist at "Contents/Frameworks/ Helper.app/Contents/MacOS/ Helper" + // in the top-level app bundle. See the comments on CefExecuteProcess() for + // details. Also configurable using the "browser-subprocess-path" command-line + // switch. /// cef_string_t browser_subprocess_path; + /// + // The path to the CEF framework directory on macOS. If this value is empty + // then the framework must exist at "Contents/Frameworks/Chromium Embedded + // Framework.framework" in the top-level app bundle. Also configurable using + // the "framework-dir-path" command-line switch. + /// + cef_string_t framework_dir_path; + /// // Set to true (1) to have the browser process message loop run in a separate // thread. If false (0) than the CefDoMessageLoopWork() function must be diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index ad091ce59..06afec7c7 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -620,6 +620,9 @@ void CefContentBrowserClient::AppendExtraCommandLineSwitches( // associated values) if present in the browser command line. static const char* const kSwitchNames[] = { switches::kDisablePackLoading, +#if defined(OS_MACOSX) + switches::kFrameworkDirPath, +#endif switches::kLang, switches::kLocalesDirPath, switches::kLogFile, diff --git a/libcef/common/cef_switches.cc b/libcef/common/cef_switches.cc index 7c3505b91..03ef0b95d 100644 --- a/libcef/common/cef_switches.cc +++ b/libcef/common/cef_switches.cc @@ -119,4 +119,9 @@ const char kEnablePreferenceTesting[] = "enable-preference-testing"; // Enable date-based expiration of built in network security information. const char kEnableNetSecurityExpiration[] = "enable-net-security-expiration"; +#if defined(OS_MACOSX) +// Path to the framework directory. +const char kFrameworkDirPath[] = "framework-dir-path"; +#endif + } // namespace switches diff --git a/libcef/common/cef_switches.h b/libcef/common/cef_switches.h index fd1ae3d65..799d705d1 100644 --- a/libcef/common/cef_switches.h +++ b/libcef/common/cef_switches.h @@ -8,6 +8,8 @@ #define CEF_LIBCEF_COMMON_CEF_SWITCHES_H_ #pragma once +#include "build/build_config.h" + namespace switches { extern const char kLogFile[]; @@ -51,6 +53,10 @@ extern const char kPluginPolicy_Block[]; extern const char kEnablePreferenceTesting[]; extern const char kEnableNetSecurityExpiration[]; +#if defined(OS_MACOSX) +extern const char kFrameworkDirPath[]; +#endif + } // namespace switches #endif // CEF_LIBCEF_COMMON_CEF_SWITCHES_H_ diff --git a/libcef/common/crash_reporter_client.cc b/libcef/common/crash_reporter_client.cc index f0c0184ce..11d11234f 100644 --- a/libcef/common/crash_reporter_client.cc +++ b/libcef/common/crash_reporter_client.cc @@ -20,7 +20,7 @@ #include "chrome/common/crash_keys.h" #if defined(OS_MACOSX) -#include "base/mac/foundation_util.h" +#include "libcef/common/util_mac.h" #endif #if defined(OS_POSIX) @@ -72,22 +72,20 @@ PathString GetCrashConfigPath() { config_path += L"crash_reporter.cfg"; return config_path; #elif defined(OS_POSIX) - // Start with the path to the running executable. base::FilePath config_path; - if (!PathService::Get(base::DIR_EXE, &config_path)) - return PathString(); #if defined(OS_MACOSX) - // Get the main app bundle path. - config_path = base::mac::GetAppBundlePath(config_path); - if (config_path.empty()) - return PathString(); - - // Go into the Contents/Resources directory. - config_path = config_path.Append(FILE_PATH_LITERAL("Contents")) - .Append(FILE_PATH_LITERAL("Resources")); + // Start with the path to the main app Resources directory. May be empty if + // not running in an app bundle. + config_path = util_mac::GetMainResourcesDirectory(); #endif + if (config_path.empty()) { + // Start with the path to the running executable. + if (!PathService::Get(base::DIR_EXE, &config_path)) + return PathString(); + } + return config_path.Append(FILE_PATH_LITERAL("crash_reporter.cfg")).value(); #endif // defined(OS_POSIX) } diff --git a/libcef/common/main_delegate.cc b/libcef/common/main_delegate.cc index 2b7cb06e2..cb870d965 100644 --- a/libcef/common/main_delegate.cc +++ b/libcef/common/main_delegate.cc @@ -70,57 +70,38 @@ namespace { #if defined(OS_MACOSX) -base::FilePath GetFrameworksPath() { - // Start out with the path to the running executable. - base::FilePath execPath; - PathService::Get(base::FILE_EXE, &execPath); - - // Get the main bundle path. - base::FilePath bundlePath = base::mac::GetAppBundlePath(execPath); - - // Go into the Contents/Frameworks directory. - return bundlePath.Append(FILE_PATH_LITERAL("Contents")) - .Append(FILE_PATH_LITERAL("Frameworks")); -} - -// The framework bundle path is used for loading resources, libraries, etc. -base::FilePath GetFrameworkBundlePath() { - return GetFrameworksPath().Append( - FILE_PATH_LITERAL("Chromium Embedded Framework.framework")); -} - base::FilePath GetResourcesFilePath() { - return GetFrameworkBundlePath().Append(FILE_PATH_LITERAL("Resources")); -} - -// Retrieve the name of the running executable. -std::string GetExecutableName() { - base::FilePath path; - PathService::Get(base::FILE_EXE, &path); - return path.BaseName().value(); + return util_mac::GetFrameworkResourcesDirectory(); } // Use a "~/Library/Logs/_debug.log" file where is the name // of the running executable. base::FilePath GetDefaultLogFile() { + std::string exe_name = util_mac::GetMainProcessPath().BaseName().value(); return base::mac::GetUserLibraryPath() .Append(FILE_PATH_LITERAL("Logs")) - .Append(FILE_PATH_LITERAL(GetExecutableName() + "_debug.log")); + .Append(FILE_PATH_LITERAL(exe_name + "_debug.log")); } void OverrideFrameworkBundlePath() { - base::mac::SetOverrideFrameworkBundlePath(GetFrameworkBundlePath()); + base::FilePath framework_path = util_mac::GetFrameworkDirectory(); + DCHECK(!framework_path.empty()); + + base::mac::SetOverrideFrameworkBundlePath(framework_path); } void OverrideChildProcessPath() { - const std::string& exe_name = GetExecutableName(); - base::FilePath helper_path = GetFrameworksPath() - .Append(FILE_PATH_LITERAL(exe_name + " Helper.app")) - .Append(FILE_PATH_LITERAL("Contents")) - .Append(FILE_PATH_LITERAL("MacOS")) - .Append(FILE_PATH_LITERAL(exe_name + " Helper")); + // ChildProcessHost::GetChildPath() requires either kBrowserSubprocessPath or + // CHILD_PROCESS_EXE but not both. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kBrowserSubprocessPath)) { + return; + } - PathService::Override(content::CHILD_PROCESS_EXE, helper_path); + base::FilePath child_process_path = util_mac::GetChildProcessPath(); + DCHECK(!child_process_path.empty()); + + PathService::Override(content::CHILD_PROCESS_EXE, child_process_path); } #else // !defined(OS_MACOSX) @@ -306,10 +287,6 @@ CefMainDelegate::~CefMainDelegate() { } bool CefMainDelegate::BasicStartupComplete(int* exit_code) { -#if defined(OS_MACOSX) - OverrideFrameworkBundlePath(); -#endif - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); @@ -354,6 +331,15 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { } } +#if defined(OS_MACOSX) + if (settings.framework_dir_path.length > 0) { + base::FilePath file_path = + base::FilePath(CefString(&settings.framework_dir_path)); + if (!file_path.empty()) + command_line->AppendSwitchPath(switches::kFrameworkDirPath, file_path); + } +#endif + if (no_sandbox) command_line->AppendSwitch(switches::kNoSandbox); @@ -508,6 +494,10 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { content::SetContentClient(&content_client_); +#if defined(OS_MACOSX) + OverrideFrameworkBundlePath(); +#endif + return false; } diff --git a/libcef/common/util_mac.h b/libcef/common/util_mac.h index fcdd54ef4..61a80d0db 100644 --- a/libcef/common/util_mac.h +++ b/libcef/common/util_mac.h @@ -1,6 +1,6 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright +// 2011 The Chromium 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_COMMON_UTIL_MAC_H_ #define CEF_LIBCEF_COMMON_UTIL_MAC_H_ @@ -12,8 +12,35 @@ class FilePath; namespace util_mac { +// Returns the path to the NSLibraryDirectory (e.g. "~/Library"). bool GetLocalLibraryDirectory(base::FilePath* result); +// Returns the path to the CEF framework directory inside the top-level app +// bundle (e.g. "myapp.app/Contents/Frameworks/Chromium Embedded +// Framework.framework"). May return an empty value if not running in an app +// bundle. +base::FilePath GetFrameworkDirectory(); + +// Returns the path to the Resources directory inside the CEF framework +// directory (e.g. "myapp.app/Contents/Frameworks/Chromium Embedded +// Framework.framework/Resources"). May return an empty value if not running in +// an app bundle. +base::FilePath GetFrameworkResourcesDirectory(); + +// Returns the path to the main (running) process executable (e.g. +// "myapp.app/Contents/MacOS/myapp"). +base::FilePath GetMainProcessPath(); + +// Returns the path to the Resources directory inside the top-level app bundle +// (e.g. "myapp.app/Contents/Resources"). May return an empty value if not +// running in an app bundle. +base::FilePath GetMainResourcesDirectory(); + +// Returns the path to the child process executable (e.g. "myapp.app/ +// Contents/Frameworks/myapp Helper.app/Contents/MacOS/myapp Helper"). May +// return an empty value if not running in an app bundle. +base::FilePath GetChildProcessPath(); + } // namespace util_mac #endif // CEF_LIBCEF_COMMON_UTIL_MAC_H_ diff --git a/libcef/common/util_mac.mm b/libcef/common/util_mac.mm index 54d3f8aa1..bdb34e421 100644 --- a/libcef/common/util_mac.mm +++ b/libcef/common/util_mac.mm @@ -1,15 +1,92 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright +// 2011 The Chromium 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/common/util_mac.h" +#include "libcef/common/cef_switches.h" + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/files/file_path.h" #include "base/mac/foundation_util.h" +#include "base/path_service.h" namespace util_mac { +namespace { + +// Returns the path to the top-level app bundle that contains the main process +// executable. +base::FilePath GetMainBundlePath() { + return base::mac::GetAppBundlePath(GetMainProcessPath()); +} + +// Returns the path to the Frameworks directory inside the top-level app bundle. +base::FilePath GetFrameworksPath() { + base::FilePath bundle_path = GetMainBundlePath(); + if (bundle_path.empty()) + return base::FilePath(); + + return bundle_path.Append(FILE_PATH_LITERAL("Contents")) + .Append(FILE_PATH_LITERAL("Frameworks")); +} + +} // namespace + bool GetLocalLibraryDirectory(base::FilePath* result) { return base::mac::GetLocalDirectory(NSLibraryDirectory, result); } +base::FilePath GetFrameworkDirectory() { + base::FilePath frameworks_path = + base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( + switches::kFrameworkDirPath); + if (!frameworks_path.empty()) + return frameworks_path; + + frameworks_path = GetFrameworksPath(); + if (frameworks_path.empty()) + return base::FilePath(); + + return frameworks_path.Append( + FILE_PATH_LITERAL("Chromium Embedded Framework.framework")); +} + +base::FilePath GetFrameworkResourcesDirectory() { + base::FilePath frameworks_path = GetFrameworkDirectory(); + if (frameworks_path.empty()) + return base::FilePath(); + + return frameworks_path.Append(FILE_PATH_LITERAL("Resources")); +} + +base::FilePath GetMainProcessPath() { + base::FilePath path; + PathService::Get(base::FILE_EXE, &path); + DCHECK(!path.empty()); + return path; +} + +base::FilePath GetMainResourcesDirectory() { + base::FilePath bundle_path = GetMainBundlePath(); + if (bundle_path.empty()) + return base::FilePath(); + + return bundle_path.Append(FILE_PATH_LITERAL("Contents")) + .Append(FILE_PATH_LITERAL("Resources")); +} + +base::FilePath GetChildProcessPath() { + base::FilePath frameworks_path = GetFrameworksPath(); + if (frameworks_path.empty()) + return base::FilePath(); + + std::string exe_name = GetMainProcessPath().BaseName().value(); + return frameworks_path.Append(FILE_PATH_LITERAL(exe_name + " Helper.app")) + .Append(FILE_PATH_LITERAL("Contents")) + .Append(FILE_PATH_LITERAL("MacOS")) + .Append(FILE_PATH_LITERAL(exe_name + " Helper")); +} + } // namespace util_mac diff --git a/tests/shared/browser/resource_util_mac.mm b/tests/shared/browser/resource_util_mac.mm index f7587643a..219f12bc4 100644 --- a/tests/shared/browser/resource_util_mac.mm +++ b/tests/shared/browser/resource_util_mac.mm @@ -27,27 +27,26 @@ bool AmIBundled() { } // namespace +// Implementation adapted from Chromium's base/base_path_mac.mm bool GetResourceDir(std::string& dir) { - // Implementation adapted from Chromium's base/base_path_mac.mm - if (AmIBundled()) { - // Retrieve the executable directory. - uint32_t pathSize = 0; - _NSGetExecutablePath(NULL, &pathSize); - if (pathSize > 0) { - dir.resize(pathSize); - _NSGetExecutablePath(const_cast(dir.c_str()), &pathSize); - } + // Retrieve the executable directory. + uint32_t pathSize = 0; + _NSGetExecutablePath(NULL, &pathSize); + if (pathSize > 0) { + dir.resize(pathSize); + _NSGetExecutablePath(const_cast(dir.c_str()), &pathSize); + } - // Trim executable name up to the last separator + if (AmIBundled()) { + // Trim executable name up to the last separator. std::string::size_type last_separator = dir.find_last_of("/"); dir.resize(last_separator); dir.append("/../Resources"); return true; - } else { - // TODO: Provide unbundled path - NOTIMPLEMENTED(); - return false; } + + dir.append("/Resources"); + return true; } } // namespace client