From dd81904a2f505d46454ccd407c101150279b302c Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Mon, 9 Dec 2024 15:20:44 -0500 Subject: [PATCH] Add initial support for API versioning (see #3836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Generated files are now created when running cef_create_projects or the new version_manager.py tool. These files are still created in the cef/ source tree (same location as before) but Git ignores them due to the generated .gitignore file. - API hashes are committed to Git as a new cef_api_versions.json file. This file is used for both code generation and CEF version calculation (replacing the previous usage of cef_api_hash.h for this purpose). It will be updated by the CEF admin before merging breaking API changes upstream. - As an added benefit to the above, contributor PRs will no longer contain generated code that is susceptible to frequent merge conflicts. - From a code generation perspective, the main difference is that we now use versioned structs (e.g. cef_browser_0_t instead of cef_browser_t) on the libcef (dll/framework) side. Most of the make_*.py tool changes are related to supporting this. - From the client perspective, you can now define CEF_API_VERSION in the project configuration (or get CEF_EXPERIMENTAL by default). This define will change the API exposed in CEF’s include/ and include/capi header files. All client-side targets including libcef_dll_wrapper will need be recompiled when changing this define. - Examples of the new API-related define usage are provided in cef_api_version_test.h, api_version_test_impl.cc and api_version_unittest.cc. To test: - Run `ceftests --gtest_filter=ApiVersionTest.*` - Add `cef_api_version=13300` to GN_DEFINES. Re-run configure, build and ceftests steps. - Repeat with 13301, 13302, 13303 (all supported test versions). --- .gitignore.in | 55 + BUILD.gn | 114 +- cef_api_versions.json | 34 + cef_paths2.gypi | 4 +- cmake/cef_variables.cmake.in | 14 + include/base/cef_build.h | 3 + include/capi/cef_base_capi.h | 32 +- include/cef_api_hash.h | 129 +- include/cef_browser.h | 16 +- include/cef_command_handler.h | 29 +- include/cef_id_mappers.h | 89 ++ include/cef_resource_bundle.h | 17 +- include/cef_resource_bundle_handler.h | 17 +- include/cef_version_info.h | 62 + include/internal/cef_export.h | 4 +- include/test/cef_api_version_test.h | 1172 ++++++++++++++++++ include/test/cef_translator_test.h | 6 +- libcef/common/api_version_util.h | 54 + libcef/common/test/api_version_test_impl.cc | 1136 +++++++++++++++++ libcef/common/test/translator_test_impl.cc | 43 +- libcef/features/features.gni | 3 + libcef_dll/cpptoc/base_ref_counted_cpptoc.h | 3 + libcef_dll/cpptoc/base_scoped_cpptoc.h | 13 + libcef_dll/cpptoc/cpptoc_ref_counted.h | 29 +- libcef_dll/cpptoc/cpptoc_scoped.h | 49 +- libcef_dll/ctocpp/base_ref_counted_ctocpp.h | 3 + libcef_dll/ctocpp/base_scoped_ctocpp.h | 4 + libcef_dll/ctocpp/ctocpp_ref_counted.h | 35 +- libcef_dll/ctocpp/ctocpp_scoped.h | 40 +- libcef_dll/libcef_dll2.cc | 123 +- libcef_dll/wrapper/libcef_dll_wrapper2.cc | 18 +- patch/patches/chrome_browser_browser.patch | 11 +- tests/cefclient/browser/client_handler.cc | 50 +- tests/cefclient/cefclient_mac.mm | 90 +- tests/ceftests/api_version_unittest.cc | 868 +++++++++++++ tests/ceftests/pdf_viewer_unittest.cc | 1 - tests/ceftests/run_all_unittests.cc | 5 + tests/ceftests/version_unittest.cc | 8 +- tests/ceftests/views/scroll_view_unittest.cc | 1 - tests/ceftests/views/textfield_unittest.cc | 1 - tools/cef_api_hash.py | 272 ++-- tools/cef_parser.py | 511 ++++++-- tools/cef_version.py | 34 +- tools/clang_util.py | 50 +- tools/file_util.py | 183 ++- tools/gclient_hook.py | 4 +- tools/git_util.py | 95 +- tools/gn_args.py | 11 +- tools/make_api_hash_header.py | 95 -- tools/make_api_versions_header.py | 130 ++ tools/make_capi_header.py | 77 +- tools/make_capi_versions_header.py | 189 +++ tools/make_cpptoc_header.py | 89 +- tools/make_cpptoc_impl.py | 581 ++++++--- tools/make_ctocpp_header.py | 96 +- tools/make_ctocpp_impl.py | 545 +++++--- tools/make_distrib.py | 21 +- tools/make_gypi_file.py | 7 + tools/make_libcef_dll_dylib_impl.py | 29 +- tools/make_pack_header.py | 147 ++- tools/make_version_header.bat | 2 - tools/make_version_header.py | 30 - tools/make_version_header.sh | 2 - tools/make_wrapper_types_header.py | 4 +- tools/translator.README.txt | 8 - tools/translator.py | 483 +++++--- tools/version_manager.py | 549 ++++++++ tools/version_util.py | 154 +++ 68 files changed, 7492 insertions(+), 1291 deletions(-) create mode 100644 .gitignore.in create mode 100644 cef_api_versions.json create mode 100644 include/cef_id_mappers.h create mode 100644 include/cef_version_info.h create mode 100644 include/test/cef_api_version_test.h create mode 100644 libcef/common/api_version_util.h create mode 100644 libcef/common/test/api_version_test_impl.cc create mode 100644 tests/ceftests/api_version_unittest.cc delete mode 100644 tools/make_api_hash_header.py create mode 100644 tools/make_api_versions_header.py create mode 100644 tools/make_capi_versions_header.py delete mode 100644 tools/make_version_header.bat delete mode 100755 tools/make_version_header.sh create mode 100644 tools/version_manager.py create mode 100644 tools/version_util.py diff --git a/.gitignore.in b/.gitignore.in new file mode 100644 index 0000000000..7a89347cd4 --- /dev/null +++ b/.gitignore.in @@ -0,0 +1,55 @@ +*.gypcmd +*.mk +*.ncb +*.opensdf +*.props +*.pyc +*.rules +*.sdf +*.sln +*.sublime-project +*.sublime-workspace +*.suo +*.targets +*.user +*.vcproj +*.vcxproj +*.vcxproj.filters +*.vpj +*.vpw +*.vpwhistu +*.vtg +*.xcodeproj +*.xcworkspace +*_proto.xml +*_proto_cpp.xml +*~ +!Android.mk +.*.sw? +.DS_Store +.classpath +.cproject +.gdb_history +.gdbinit +.landmines +.metadata +.project +.pydevproject +.vscode +# Settings directory for eclipse +/.settings +.checkstyle +cscope.* +Session.vim +tags +Thumbs.db +# IDE's +.vs/ +.kdev4/ +*.kdev4 +# CEF generated directories +/binary_distrib +/docs +# CEF generated files +.ccls-cache/ +/cef_api_untracked.json diff --git a/BUILD.gn b/BUILD.gn index 06cdda6ceb..a01395a762 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -360,6 +360,8 @@ if (is_win) { } deps = [ + ":make_version_header", + "//components/crash/core/common", # crash_keys # Required by chrome_switches.cc @@ -441,6 +443,7 @@ source_set("libcef_test_support") { "libcef/browser/test/test_helpers_impl.cc", "libcef/browser/test/test_server_impl.cc", "libcef/browser/test/test_server_impl.h", + "libcef/common/test/api_version_test_impl.cc", "libcef/common/test/translator_test_impl.cc", ] @@ -451,6 +454,10 @@ source_set("libcef_test_support") { # Support for UI input events. "//ui/views:test_support", ] + + configs += [ + ":libcef_includes_config", + ] } @@ -758,6 +765,7 @@ source_set("libcef_static") { "libcef/browser/xml_reader_impl.h", "libcef/browser/zip_reader_impl.cc", "libcef/browser/zip_reader_impl.h", + "libcef/common/api_version_util.h", "libcef/common/app_manager.cc", "libcef/common/app_manager.h", "libcef/common/base_impl.cc", @@ -1159,6 +1167,10 @@ config("libcef_dll_wrapper_config") { if (is_mac) { cflags_objcc = [ "-std=c++17" ] } + + if (cef_api_version != "") { + defines = [ "CEF_API_VERSION=$cef_api_version" ] + } } # libcef_dll_wrapper target. @@ -1245,16 +1257,16 @@ grit("cef_resources") { # Helper for generating pack header files. template("make_pack_header") { assert(defined(invoker.header)) + assert(defined(invoker.inc)) assert(defined(invoker.inputs)) action("make_pack_header_${target_name}") { script = "tools/make_pack_header.py" inputs = invoker.inputs - outputs = [ invoker.header ] + outputs = [ invoker.header, invoker.inc ] - args = rebase_path(outputs, root_build_dir) + - rebase_path(inputs, root_build_dir) + args = rebase_path(outputs + inputs, root_build_dir) if (defined(invoker.deps)) { deps = invoker.deps @@ -1265,6 +1277,7 @@ template("make_pack_header") { # Generate cef_pack_resources.h. make_pack_header("resources") { header = "$root_out_dir/includes/cef/include/cef_pack_resources.h" + inc = "$root_gen_dir/cef/libcef_dll/cef_pack_resources.inc" inputs = [ "$root_gen_dir/base/tracing/protos/grit/tracing_proto_resources.h", "$root_gen_dir/cef/grit/cef_resources.h", @@ -1331,6 +1344,7 @@ make_pack_header("resources") { # Generate cef_pack_strings.h. make_pack_header("strings") { header = "$root_out_dir/includes/cef/include/cef_pack_strings.h" + inc = "$root_gen_dir/cef/libcef_dll/cef_pack_strings.inc" inputs = [ "$root_gen_dir/cef/grit/cef_strings.h", "$root_gen_dir/chrome/grit/branded_strings.h", @@ -1374,26 +1388,39 @@ make_pack_header("strings") { # Generate cef_command_ids.h. make_pack_header("command_ids") { header = "$root_out_dir/includes/cef/include/cef_command_ids.h" + inc = "$root_gen_dir/cef/libcef_dll/cef_command_ids.inc" inputs = [ "//chrome/app/chrome_command_ids.h", ] } -# Generate cef_api_hash.h. -action("make_api_hash_header") { - script = "tools/make_api_hash_header.py" - - # List of all C API files that will be checked for changes by cef_api_hash.py. - inputs = gypi_paths2.includes_common_capi + - gypi_paths2.includes_linux_capi + - gypi_paths2.includes_mac_capi + - gypi_paths2.includes_win_capi + - gypi_paths2.includes_capi + - gypi_paths.autogen_capi_includes - include_dir = [ "include" ] - outputs = [ "$root_out_dir/includes/cef/include/cef_api_hash.h" ] - - args = rebase_path(outputs + include_dir, root_build_dir) +# Generate cef_api_versions.h. +action("make_api_versions_header") { + script = "tools/make_api_versions_header.py" + + inputs = [ + "cef_api_versions.json", + "cef_api_untracked.json", + ] + outputs = [ + "$root_out_dir/includes/cef/include/cef_api_versions.h", + "$root_gen_dir/cef/libcef_dll/cef_api_versions.inc", + ] + + args = rebase_path(outputs + inputs, root_build_dir) +} + +# Generate cef_version.h. +action("make_version_header") { + script = "tools/make_version_header.py" + + inputs = [ + "VERSION.stamp", + "//chrome/VERSION", + ] + outputs = [ "$root_out_dir/includes/cef/include/cef_version.h" ] + + args = rebase_path(outputs, root_build_dir) } # This no-op action lists args.gn as an output, allowing it to be referenced as @@ -1436,7 +1463,8 @@ group("cef_make_headers") { ":make_pack_header_resources", ":make_pack_header_strings", ":make_pack_header_command_ids", - ":make_api_hash_header", + ":make_api_versions_header", + ":make_version_header", ":make_config_header", ":make_colorids_header", ] @@ -1447,6 +1475,28 @@ group("cef_make_headers") { # libcef dll/framework target. # +libcef_sources_common = includes_common + + gypi_paths.autogen_cpp_includes + + gypi_paths2.includes_capi + + gypi_paths.autogen_capi_includes + + gypi_paths.autogen_capi_versions_includes + + gypi_paths2.libcef_sources_common + + gypi_paths.autogen_library_side + [ + "$root_gen_dir/cef/libcef_dll/cef_pack_resources.inc", + "$root_gen_dir/cef/libcef_dll/cef_pack_strings.inc", + "$root_gen_dir/cef/libcef_dll/cef_command_ids.inc", + "$root_gen_dir/cef/libcef_dll/cef_api_versions.inc", +] + +libcef_deps_common = [ + ":libcef_static", + ":libcef_test_support", + ":make_pack_header_resources", + ":make_pack_header_strings", + ":make_pack_header_command_ids", + ":make_api_versions_header", +] + if (is_mac) { cef_framework_name = "Chromium Embedded Framework" @@ -1530,24 +1580,17 @@ if (is_mac) { "Resources", ] - sources = includes_common + - includes_mac + - gypi_paths.autogen_cpp_includes + - gypi_paths2.includes_capi + - gypi_paths.autogen_capi_includes + - gypi_paths2.libcef_sources_common + - gypi_paths.autogen_library_side + sources = libcef_sources_common + includes_mac - deps = [ + deps = libcef_deps_common + [ ":cef_framework_angle_binaries", ":cef_framework_resources", ":cef_framework_swiftshader_binaries", - ":libcef_static", - ":libcef_test_support", ] configs += [ ":libcef_autogen_config", + ":libcef_includes_config", ] # We don't link the framework so just use the path from the main executable. @@ -1586,20 +1629,13 @@ if (is_mac) { # Necessary because the libcef_test_support target is testonly. testonly = true - sources = includes_common + - gypi_paths.autogen_cpp_includes + - gypi_paths2.includes_capi + - gypi_paths.autogen_capi_includes + - gypi_paths2.libcef_sources_common + - gypi_paths.autogen_library_side + sources = libcef_sources_common - deps = [ - ":libcef_static", - ":libcef_test_support", - ] + deps = libcef_deps_common configs += [ ":libcef_autogen_config", + ":libcef_includes_config", ":pdb_larger_than_4gb", ] diff --git a/cef_api_versions.json b/cef_api_versions.json new file mode 100644 index 0000000000..24dbe10099 --- /dev/null +++ b/cef_api_versions.json @@ -0,0 +1,34 @@ +{ + "hashes": { + "13300": { + "comment": "Added January 07, 2025.", + "linux": "f0b073047a026b83e911ba60aa1a83f036d31b0e", + "mac": "39c1f7df430aeaf39911147032b266416658c11d", + "universal": "dd40c0c97ba3f4a1f4ec53ff64d19fea30217d3d", + "windows": "4d97ebe2ed64b448b23625c6bd3943797ac3e137" + }, + "13301": { + "comment": "Added January 07, 2025.", + "linux": "41f72b80f8a2d00ea8301cda42d37b8fdf0240a5", + "mac": "e44f5eb6d634f3d4353f52ff383bf213f3894bbd", + "universal": "f7edff150ad480bc2f5cfc85fd6bfa23fbc4a73f", + "windows": "130d2eed0662c065a1cee3c782a6d855c63d67e8" + }, + "13302": { + "comment": "Added January 07, 2025.", + "linux": "2ef9e3f071838f2d7705d144cf66e0f5ae69b32f", + "mac": "ec74fb1f9aff97f55882dbb35ba979934c8ab1a7", + "universal": "d9c1eedf985ddcd4e9df05f7664b111462d45626", + "windows": "f4d05b712907d8d64df80029d9e2e8edbee814ac" + }, + "13303": { + "comment": "Added January 07, 2025.", + "linux": "397ad962a3049dad4b36b356c8d108f78603de60", + "mac": "f7a5c733b8c3cd6fa3a284ce9b307566c6aa5860", + "universal": "91d5546d2b3f601ab2453f218a7bd69d2fd39b1b", + "windows": "b24722530b6b77bd1a633af5d72088225468eee2" + } + }, + "last": "13303", + "min": "13300" +} \ No newline at end of file diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 88fc7b230b..d923dec718 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -36,7 +36,6 @@ 'include/base/internal/cef_thread_checker_impl.h', 'include/cef_api_hash.h', 'include/cef_base.h', - 'include/cef_version.h', 'include/internal/cef_export.h', 'include/internal/cef_ptr.h', 'include/internal/cef_string_wrappers.h', @@ -44,6 +43,8 @@ 'include/internal/cef_types_wrappers.h', ], 'includes_common_capi': [ + 'include/cef_id_mappers.h', + 'include/cef_version_info.h', 'include/internal/cef_dump_without_crashing_internal.h', 'include/internal/cef_logging_internal.h', 'include/internal/cef_string.h', @@ -483,6 +484,7 @@ 'tests/ceftests/resources/net/data/ssl/certificates/root_ca_cert.pem', ], 'ceftests_sources_common': [ + 'tests/ceftests/api_version_unittest.cc', 'tests/ceftests/audio_output_unittest.cc', 'tests/ceftests/browser_info_map_unittest.cc', 'tests/ceftests/certificate_error_unittest.cc', diff --git a/cmake/cef_variables.cmake.in b/cmake/cef_variables.cmake.in index 423f58f7a3..fd32a5e4c6 100644 --- a/cmake/cef_variables.cmake.in +++ b/cmake/cef_variables.cmake.in @@ -69,6 +69,20 @@ list(APPEND CEF_COMPILER_DEFINES option(USE_SANDBOX "Enable or disable use of the sandbox." ON) +# Optionally configure the CEF API version by adding `-D api_version=XXXXX` to the +# cmake command-line where XXXXX is the desired version number. For background see +# https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md +if(DEFINED api_version) + string(LENGTH "${api_version}" length) + if (NOT length EQUAL 5 OR NOT api_version MATCHES "^[0-9]+$") + message(FATAL_ERROR "Expected a 5 digit number for api_version, got '${api_version}'") + endif() + list(APPEND CEF_COMPILER_DEFINES + CEF_API_VERSION=${api_version} + ) +endif() + + # # Linux configuration. # diff --git a/include/base/cef_build.h b/include/base/cef_build.h index b476c78c1c..2980deb4d5 100644 --- a/include/base/cef_build.h +++ b/include/base/cef_build.h @@ -85,7 +85,10 @@ #endif #else // !USING_CHROMIUM_INCLUDES + +#if !defined(GENERATING_CEF_API_HASH) #include "include/cef_config.h" +#endif // The following is substantially similar to the Chromium implementation. // If the Chromium implementation diverges the below implementation should be diff --git a/include/capi/cef_base_capi.h b/include/capi/cef_base_capi.h index dbd0b9f339..fefc95d00f 100644 --- a/include/capi/cef_base_capi.h +++ b/include/capi/cef_base_capi.h @@ -44,62 +44,54 @@ extern "C" { #endif /// -// All ref-counted framework structures must include this structure first. +/// All ref-counted framework structures must include this structure first. /// typedef struct _cef_base_ref_counted_t { /// - // Size of the data structure. + /// Size of the data structure. /// size_t size; /// - // Called to increment the reference count for the object. Should be called - // for every new copy of a pointer to a given object. + /// Called to increment the reference count for the object. Should be called + /// for every new copy of a pointer to a given object. /// void(CEF_CALLBACK* add_ref)(struct _cef_base_ref_counted_t* self); /// - // Called to decrement the reference count for the object. If the reference - // count falls to 0 the object should self-delete. Returns true (1) if the - // resulting reference count is 0. + /// Called to decrement the reference count for the object. If the reference + /// count falls to 0 the object should self-delete. Returns true (1) if the + /// resulting reference count is 0. /// int(CEF_CALLBACK* release)(struct _cef_base_ref_counted_t* self); /// - // Returns true (1) if the current reference count is 1. + /// Returns true (1) if the current reference count is 1. /// int(CEF_CALLBACK* has_one_ref)(struct _cef_base_ref_counted_t* self); /// - // Returns true (1) if the current reference count is at least 1. + /// Returns true (1) if the current reference count is at least 1. /// int(CEF_CALLBACK* has_at_least_one_ref)(struct _cef_base_ref_counted_t* self); } cef_base_ref_counted_t; /// -// All scoped framework structures must include this structure first. +/// All scoped framework structures must include this structure first. /// typedef struct _cef_base_scoped_t { /// - // Size of the data structure. + /// Size of the data structure. /// size_t size; /// - // Called to delete this object. May be NULL if the object is not owned. + /// Called to delete this object. May be NULL if the object is not owned. /// void(CEF_CALLBACK* del)(struct _cef_base_scoped_t* self); } cef_base_scoped_t; -// Check that the structure |s|, which is defined with a size_t member at the -// top, is large enough to contain the specified member |f|. -#define CEF_MEMBER_EXISTS(s, f) \ - ((intptr_t) & \ - ((s)->f) - (intptr_t)(s) + sizeof((s)->f) <= *reinterpret_cast(s)) - -#define CEF_MEMBER_MISSING(s, f) (!CEF_MEMBER_EXISTS(s, f) || !((s)->f)) - #ifdef __cplusplus } #endif diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 8070b56014..af903d4b92 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -27,45 +27,124 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// --------------------------------------------------------------------------- -// -// This file was generated by the make_api_hash_header.py tool. +// Versions are managed using the version_manager.py tool. For usage details +// see https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md // -#ifndef CEF_INCLUDE_API_HASH_H_ -#define CEF_INCLUDE_API_HASH_H_ +#ifndef CEF_INCLUDE_CEF_API_HASH_H_ +#define CEF_INCLUDE_CEF_API_HASH_H_ #include "include/internal/cef_export.h" -// The API hash is created by analyzing CEF header files for C API type -// definitions. The hash value will change when header files are modified in a -// way that may cause binary incompatibility with other builds. The universal -// hash value will change if any platform is affected whereas the platform hash -// values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "e52a54bfb4cfc13366fe52766756180fc7d3d1b2" -#if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "ce7bfedc905e90407eb975eebf2f419a347b27cd" -#elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "8bb21c09270905fe64b8c31f744e0cbf7bc39ff5" -#elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "a8198f66f731c8ac91bba1e45847807bffca7b01" +#if !defined(GENERATING_CEF_API_HASH) +#include "include/cef_api_versions.h" +#endif + +// Experimental CEF API. Experimental API is unversioned, meaning that it is +// excluded (compiled out) when clients explicitly set the CEF_API_VERSION +// value in their project configuration. Experimental API is not back/forward +// compatible with different CEF versions. +#define CEF_API_VERSION_EXPERIMENTAL 999999 + +// Placeholder for the next CEF version currently under development. This is a +// temporary value that must be replaced with the actual next version number +// (output of running `version_manager.py -n`) prior to upstream merge. As an +// added reminder, use of this value will cause version_manager.py to fail when +// computing hashes for explicit API versions. When adding new API consider +// using CEF_API_VERSION_EXPERIMENTAL instead. +#if !defined(CEF_API_VERSION_NEXT) +#define CEF_API_VERSION_NEXT 999998 +#endif + +// Shorter versions of the above for convenience use in comparison macros. +#define CEF_NEXT CEF_API_VERSION_NEXT +#define CEF_EXPERIMENTAL CEF_API_VERSION_EXPERIMENTAL + +// API version that will be compiled client-side. The experimental (unversioned) +// API is selected by default. Clients can set the CEF_API_VERSION value in +// their project configuration to configure an explicit API version. Unlike +// the experimental API, explicit API versions are back/forward compatible with +// a specific range of CEF versions. +#if !defined(CEF_API_VERSION) +#define CEF_API_VERSION CEF_API_VERSION_EXPERIMENTAL +#endif + +#if !defined(GENERATING_CEF_API_HASH) +#if CEF_API_VERSION < CEF_API_VERSION_MIN || \ + (CEF_API_VERSION > CEF_API_VERSION_LAST && CEF_API_VERSION != CEF_NEXT && \ + CEF_API_VERSION != CEF_EXPERIMENTAL) +#error Building with unsupported CEF_API_VERSION value +#endif #endif +#define _CEF_AH_PASTE(a, b, c) a##_##b##_##c +#define _CEF_AH_EVAL(a, b, c) _CEF_AH_PASTE(a, b, c) +#define _CEF_AH_DECLARE(version, suffix) \ + _CEF_AH_EVAL(CEF_API_HASH, version, suffix) + +// API hashes for the selected CEF_API_VERSION. API hashes are created for +// each version by analyzing CEF header files for C API type definitions. The +// hash value will change when header files are modified in a way that may +// cause binary incompatibility with other builds. The universal hash value +// will change if any platform is affected whereas the platform hash values +// will change only if that particular platform is affected. +#define CEF_API_HASH_UNIVERSAL _CEF_AH_DECLARE(CEF_API_VERSION, UNIVERSAL) +#define CEF_API_HASH_PLATFORM _CEF_AH_DECLARE(CEF_API_VERSION, PLATFORM) + +#if defined(BUILDING_CEF_SHARED) + +#define _CEF_AV_LT(v) 1 +#define _CEF_AV_GE(v) 1 + +#else // !defined(BUILDING_CEF_SHARED) + +#define _CEF_AV_CMP(v, op) (CEF_API_VERSION op v) + +#define _CEF_AV_LT(v) _CEF_AV_CMP(v, <) +#define _CEF_AV_GE(v) _CEF_AV_CMP(v, >=) + +#endif // !defined(BUILDING_CEF_SHARED) + +/// +/// API was added in the specified version. +/// +#define CEF_API_ADDED(v) _CEF_AV_GE(v) + +/// +/// API was removed in the specified version. +/// +#define CEF_API_REMOVED(v) _CEF_AV_LT(v) + +/// +/// API exists only in the specified version range. +/// +#define CEF_API_RANGE(va, vr) (_CEF_AV_GE(va) && _CEF_AV_LT(vr)) + #ifdef __cplusplus extern "C" { #endif /// -// Returns CEF API hashes for the libcef library. The returned string is owned -// by the library and should not be freed. The |entry| parameter describes which -// hash value will be returned: -// 0 - CEF_API_HASH_PLATFORM -// 1 - CEF_API_HASH_UNIVERSAL -// 2 - CEF_COMMIT_HASH (from cef_version.h) +/// Configures the CEF API version and returns API hashes for the libcef +/// library. The returned string is owned by the library and should not be +/// freed. The |version| parameter should be CEF_API_VERSION and any changes to +/// this value will be ignored after the first call to this method. The |entry| +/// parameter describes which hash value will be returned: +/// +/// 0 - CEF_API_HASH_PLATFORM +/// 1 - CEF_API_HASH_UNIVERSAL +/// 2 - CEF_COMMIT_HASH (from cef_version.h) +/// +CEF_EXPORT const char* cef_api_hash(int version, int entry); + +/// +/// Returns the CEF API version that was configured by the first call to +/// cef_api_hash(). /// -CEF_EXPORT const char* cef_api_hash(int entry); +CEF_EXPORT int cef_api_version(); #ifdef __cplusplus } #endif -#endif // CEF_INCLUDE_API_HASH_H_ + +#endif // CEF_INCLUDE_CEF_API_HASH_H_ diff --git a/include/cef_browser.h b/include/cef_browser.h index f8749aeace..70f684d8fc 100644 --- a/include/cef_browser.h +++ b/include/cef_browser.h @@ -1022,17 +1022,21 @@ class CefBrowserHost : public virtual CefBaseRefCounted { virtual void ExitFullscreen(bool will_cause_resize) = 0; /// - /// Returns true if a Chrome command is supported and enabled. Values for - /// |command_id| can be found in the cef_command_ids.h file. This method can - /// only be called on the UI thread. Only used with Chrome style. + /// Returns true if a Chrome command is supported and enabled. Use the + /// cef_id_for_command_id_name() function for version-safe mapping of command + /// IDC names from cef_command_ids.h to version-specific numerical + /// |command_id| values. This method can only be called on the UI thread. Only + /// used with Chrome style. /// /*--cef()--*/ virtual bool CanExecuteChromeCommand(int command_id) = 0; /// - /// Execute a Chrome command. Values for |command_id| can be found in the - /// cef_command_ids.h file. |disposition| provides information about the - /// intended command target. Only used with Chrome style. + /// Execute a Chrome command. Use the cef_id_for_command_id_name() + /// function for version-safe mapping of command IDC names from + /// cef_command_ids.h to version-specific numerical |command_id| values. + /// |disposition| provides information about the intended command target. Only + /// used with Chrome style. /// /*--cef()--*/ virtual void ExecuteChromeCommand( diff --git a/include/cef_command_handler.h b/include/cef_command_handler.h index e20749d97c..ea8d59d9ff 100644 --- a/include/cef_command_handler.h +++ b/include/cef_command_handler.h @@ -50,12 +50,13 @@ class CefCommandHandler : public virtual CefBaseRefCounted { public: /// /// Called to execute a Chrome command triggered via menu selection or - /// keyboard shortcut. Values for |command_id| can be found in the - /// cef_command_ids.h file. |disposition| provides information about the - /// intended command target. Return true if the command was handled or false - /// for the default implementation. For context menu commands this will be - /// called after CefContextMenuHandler::OnContextMenuCommand. Only used with - /// Chrome style. + /// keyboard shortcut. Use the cef_id_for_command_id_name() + /// function for version-safe mapping of command IDC names from + /// cef_command_ids.h to version-specific numerical |command_id| values. + /// |disposition| provides information about the intended command target. + /// Return true if the command was handled or false for the default + /// implementation. For context menu commands this will be called after + /// CefContextMenuHandler::OnContextMenuCommand. Only used with Chrome style. /// /*--cef()--*/ virtual bool OnChromeCommand(CefRefPtr browser, @@ -65,9 +66,11 @@ class CefCommandHandler : public virtual CefBaseRefCounted { } /// - /// Called to check if a Chrome app menu item should be visible. Values for - /// |command_id| can be found in the cef_command_ids.h file. Only called for - /// menu items that would be visible by default. Only used with Chrome style. + /// Called to check if a Chrome app menu item should be visible. Use the + /// cef_id_for_command_id_name() function for version-safe mapping of command + /// IDC names from cef_command_ids.h to version-specific numerical + /// |command_id| values. Only called for menu items that would be visible by + /// default. Only used with Chrome style. /// /*--cef()--*/ virtual bool IsChromeAppMenuItemVisible(CefRefPtr browser, @@ -76,9 +79,11 @@ class CefCommandHandler : public virtual CefBaseRefCounted { } /// - /// Called to check if a Chrome app menu item should be enabled. Values for - /// |command_id| can be found in the cef_command_ids.h file. Only called for - /// menu items that would be enabled by default. Only used with Chrome style. + /// Called to check if a Chrome app menu item should be enabled. Use the + /// cef_id_for_command_id_name() function for version-safe mapping of command + /// IDC names from cef_command_ids.h to version-specific numerical + /// |command_id| values. Only called for menu items that would be enabled by + /// default. Only used with Chrome style. /// /*--cef()--*/ virtual bool IsChromeAppMenuItemEnabled(CefRefPtr browser, diff --git a/include/cef_id_mappers.h b/include/cef_id_mappers.h new file mode 100644 index 0000000000..75e4e698f2 --- /dev/null +++ b/include/cef_id_mappers.h @@ -0,0 +1,89 @@ +// Copyright (c) 2025 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CEF_INCLUDE_CEF_ID_MAPPERS_H_ +#define CEF_INCLUDE_CEF_ID_MAPPERS_H_ +#pragma once + +/// +/// Helper for declaring a static IDR variable. +/// +#define CEF_DECLARE_PACK_RESOURCE_ID(name) \ + static const int name = cef_id_for_pack_resource_name(#name) + +/// +/// Helper for declaring a static IDS variable. +/// +#define CEF_DECLARE_PACK_STRING_ID(name) \ + static const int name = cef_id_for_pack_string_name(#name) + +/// +/// Helper for declaring a static IDC variable. +/// +#define CEF_DECLARE_COMMAND_ID(name) \ + static const int name = cef_id_for_command_id_name(#name) + +#ifdef __cplusplus +extern "C" { +#endif + +#include "include/internal/cef_export.h" + +/// +/// Returns the numeric ID value for an IDR |name| from cef_pack_resources.h or +/// -1 if |name| is unrecognized by the current CEF/Chromium build. This +/// function provides version-safe mapping of resource IDR names to +/// version-specific numeric ID values. Numeric ID values are likely to change +/// across CEF/Chromium versions but names generally remain the same. +/// +CEF_EXPORT int cef_id_for_pack_resource_name(const char* name); + +/// +/// Returns the numeric ID value for an IDS |name| from cef_pack_strings.h or -1 +/// if |name| is unrecognized by the current CEF/Chromium build. This function +/// provides version-safe mapping of string IDS names to version-specific +/// numeric ID values. Numeric ID values are likely to change across +/// CEF/Chromium versions but names generally remain the same. +/// +CEF_EXPORT int cef_id_for_pack_string_name(const char* name); + +/// +/// Returns the numeric ID value for an IDC |name| from cef_command_ids.h or -1 +/// if |name| is unrecognized by the current CEF/Chromium build. This function +/// provides version-safe mapping of command IDC names to version-specific +/// numeric ID values. Numeric ID values are likely to change across +/// CEF/Chromium versions but names generally remain the same. +/// +CEF_EXPORT int cef_id_for_command_id_name(const char* name); + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CEF_ID_MAPPERS_H_ diff --git a/include/cef_resource_bundle.h b/include/cef_resource_bundle.h index d235e1c4a5..7e3e083682 100644 --- a/include/cef_resource_bundle.h +++ b/include/cef_resource_bundle.h @@ -61,16 +61,19 @@ class CefResourceBundle : public virtual CefBaseRefCounted { /// /// Returns the localized string for the specified |string_id| or an empty - /// string if the value is not found. Include cef_pack_strings.h for a listing - /// of valid string ID values. + /// string if the value is not found. Use the cef_id_for_pack_string_name() + /// function for version-safe mapping of string IDS names from + /// cef_pack_strings.h to version-specific numerical |string_id| values. /// /*--cef()--*/ virtual CefString GetLocalizedString(int string_id) = 0; /// /// Returns a CefBinaryValue containing the decompressed contents of the - /// specified scale independent |resource_id| or NULL if not found. Include - /// cef_pack_resources.h for a listing of valid resource ID values. + /// specified scale independent |resource_id| or NULL if not found. Use the + /// cef_id_for_pack_resource_name() function for version-safe mapping of + /// resource IDR names from cef_pack_resources.h to version-specific numerical + /// |resource_id| values. /// /*--cef()--*/ virtual CefRefPtr GetDataResource(int resource_id) = 0; @@ -79,8 +82,10 @@ class CefResourceBundle : public virtual CefBaseRefCounted { /// Returns a CefBinaryValue containing the decompressed contents of the /// specified |resource_id| nearest the scale factor |scale_factor| or NULL if /// not found. Use a |scale_factor| value of SCALE_FACTOR_NONE for scale - /// independent resources or call GetDataResource instead.Include - /// cef_pack_resources.h for a listing of valid resource ID values. + /// independent resources or call GetDataResource instead. Use the + /// cef_id_for_pack_resource_name() function for version-safe mapping of + /// resource IDR names from cef_pack_resources.h to version-specific numerical + /// |resource_id| values. /// /*--cef()--*/ virtual CefRefPtr GetDataResourceForScale( diff --git a/include/cef_resource_bundle_handler.h b/include/cef_resource_bundle_handler.h index 4743482c1b..c77b762a1b 100644 --- a/include/cef_resource_bundle_handler.h +++ b/include/cef_resource_bundle_handler.h @@ -53,8 +53,10 @@ class CefResourceBundleHandler : public virtual CefBaseRefCounted { /// /// Called to retrieve a localized translation for the specified |string_id|. /// To provide the translation set |string| to the translation string and - /// return true. To use the default translation return false. Include - /// cef_pack_strings.h for a listing of valid string ID values. + /// return true. To use the default translation return false. Use the + /// cef_id_for_pack_string_name() function for version-safe mapping of string + /// IDS names from cef_pack_strings.h to version-specific numerical + /// |string_id| values. /// /*--cef()--*/ virtual bool GetLocalizedString(int string_id, CefString& string) = 0; @@ -64,8 +66,9 @@ class CefResourceBundleHandler : public virtual CefBaseRefCounted { /// To provide the resource data set |data| and |data_size| to the data /// pointer and size respectively and return true. To use the default resource /// data return false. The resource data will not be copied and must remain - /// resident in memory. Include cef_pack_resources.h for a listing of valid - /// resource ID values. + /// resident in memory. Use the cef_id_for_pack_resource_name() function for + /// version-safe mapping of resource IDR names from cef_pack_resources.h to + /// version-specific numerical |resource_id| values. /// /*--cef()--*/ virtual bool GetDataResource(int resource_id, @@ -77,8 +80,10 @@ class CefResourceBundleHandler : public virtual CefBaseRefCounted { /// factor |scale_factor|. To provide the resource data set |data| and /// |data_size| to the data pointer and size respectively and return true. To /// use the default resource data return false. The resource data will not be - /// copied and must remain resident in memory. Include cef_pack_resources.h - /// for a listing of valid resource ID values. + /// copied and must remain resident in memory. Use the + /// cef_id_for_pack_resource_name() function for version-safe mapping of + /// resource IDR names from cef_pack_resources.h to version-specific numerical + /// |resource_id| values. /// /*--cef()--*/ virtual bool GetDataResourceForScale(int resource_id, diff --git a/include/cef_version_info.h b/include/cef_version_info.h new file mode 100644 index 0000000000..31723af51f --- /dev/null +++ b/include/cef_version_info.h @@ -0,0 +1,62 @@ +// Copyright (c) 2024 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CEF_INCLUDE_CEF_VERSION_INFO_H_ +#define CEF_INCLUDE_CEF_VERSION_INFO_H_ + +#include "include/internal/cef_export.h" + +#if !defined(GENERATING_CEF_API_HASH) +#include "include/cef_version.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Returns CEF version information for the libcef library. The |entry| +/// parameter describes which version component will be returned: +/// +/// 0 - CEF_VERSION_MAJOR +/// 1 - CEF_VERSION_MINOR +/// 2 - CEF_VERSION_PATCH +/// 3 - CEF_COMMIT_NUMBER +/// 4 - CHROME_VERSION_MAJOR +/// 5 - CHROME_VERSION_MINOR +/// 6 - CHROME_VERSION_BUILD +/// 7 - CHROME_VERSION_PATCH +/// +CEF_EXPORT int cef_version_info(int entry); + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CEF_VERSION_INFO_H_ diff --git a/include/internal/cef_export.h b/include/internal/cef_export.h index 1915f5e5ef..e268b290be 100644 --- a/include/internal/cef_export.h +++ b/include/internal/cef_export.h @@ -36,9 +36,9 @@ #if defined(COMPILER_MSVC) -#ifdef BUILDING_CEF_SHARED +#if defined(BUILDING_CEF_SHARED) #define CEF_EXPORT __declspec(dllexport) -#elif USING_CEF_SHARED +#elif defined(USING_CEF_SHARED) #define CEF_EXPORT __declspec(dllimport) #else #define CEF_EXPORT diff --git a/include/test/cef_api_version_test.h b/include/test/cef_api_version_test.h new file mode 100644 index 0000000000..e27426d301 --- /dev/null +++ b/include/test/cef_api_version_test.h @@ -0,0 +1,1172 @@ +// Copyright (c) 2024 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// The contents of this file must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// +// THIS FILE IS FOR TESTING PURPOSES ONLY. +// +// The APIs defined in this file are for testing purposes only. They should only +// be included from unit test targets. +// + +#ifndef CEF_INCLUDE_TEST_CEF_API_VERSION_TEST_H_ +#define CEF_INCLUDE_TEST_CEF_API_VERSION_TEST_H_ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \ + !defined(UNIT_TEST) +#error This file can be included for unit tests only +#endif + +#include + +#include "include/cef_api_hash.h" +#include "include/cef_base.h" + +// NOTE: This test implements an example of API version changes over time. +// It is basically the same as the RefPtr/OwnPtr/RawPtr portion of +// CefTranslatorTest but with API version changes applied. +// - Legacy API has no version suffix on class/method names. +// - Changed to API v1 in CEF version 13301 (added 'V1' suffix). +// - Changed to API v2 in CEF version 13302 (changed to 'V2' suffix). + +class CefApiVersionTestRefPtrClient; +#if CEF_API_REMOVED(13302) +class CefApiVersionTestRefPtrClientChild; +#endif +#if CEF_API_ADDED(13302) +class CefApiVersionTestRefPtrClientChildV2; +#endif +class CefApiVersionTestRefPtrLibrary; +class CefApiVersionTestRefPtrLibraryChild; +class CefApiVersionTestScopedClient; +#if CEF_API_REMOVED(13302) +class CefApiVersionTestScopedClientChild; +#endif +#if CEF_API_ADDED(13302) +class CefApiVersionTestScopedClientChildV2; +#endif +class CefApiVersionTestScopedLibrary; +class CefApiVersionTestScopedLibraryChild; + +/// +/// Class for testing versioned object transfer. +/// +/*--cef(source=library)--*/ +class CefApiVersionTest : public CefBaseRefCounted { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefRefPtr Create(); + + // LIBRARY-SIDE REFPTR VALUES + + /// + /// Return an new library-side object. + /// + /*--cef()--*/ + virtual CefRefPtr GetRefPtrLibrary( + int val) = 0; + + /// + /// Set an object. Returns the value from + /// CefApiVersionTestRefPtrLibrary::GetValue(). + /// This tests input and execution of a library-side object type. + /// + /*--cef()--*/ + virtual int SetRefPtrLibrary( + CefRefPtr val) = 0; + + /// + /// Set an object. Returns the object passed in. This tests input and output + /// of a library-side object type. + /// + /*--cef()--*/ + virtual CefRefPtr SetRefPtrLibraryAndReturn( + CefRefPtr val) = 0; + + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestRefPtrLibrary::GetValue(). This tests input of a library- + /// side child object type and execution as the parent type. + /// + /*--cef()--*/ + virtual int SetChildRefPtrLibrary( + CefRefPtr val) = 0; + + /// + /// Set a child object. Returns the object as the parent type. This tests + /// input of a library-side child object type and return as the parent type. + /// + /*--cef()--*/ + virtual CefRefPtr + SetChildRefPtrLibraryAndReturnParent( + CefRefPtr val) = 0; + + // LIBRARY-SIDE REFPTR LIST VALUES + + // Test both with and without a typedef. + typedef std::vector> + RefPtrLibraryList; + + /// + /// Set an object list vlaue. + /// + /*--cef()--*/ + virtual bool SetRefPtrLibraryList( + const std::vector>& val, + int val1, + int val2) = 0; + + /// + /// Return an object list value by out-param. + /// + /*--cef(count_func=val:GetRefPtrLibraryListSize)--*/ + virtual bool GetRefPtrLibraryListByRef(RefPtrLibraryList& val, + int val1, + int val2) = 0; + + /// + /// Return the number of object that will be output above. + /// + /*--cef()--*/ + virtual size_t GetRefPtrLibraryListSize() = 0; + + // CLIENT-SIDE REFPTR VALUES + + /// + /// Set an object. Returns the value from + /// CefApiVersionTestRefPtrClient::GetValue(). + /// This tests input and execution of a client-side object type. + /// + /*--cef()--*/ + virtual int SetRefPtrClient(CefRefPtr val) = 0; + + /// + /// Set an object. Returns the handler passed in. This tests input and output + /// of a client-side object type. + /// + /*--cef()--*/ + virtual CefRefPtr SetRefPtrClientAndReturn( + CefRefPtr val) = 0; + +#if CEF_API_REMOVED(13302) + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestRefPtrClient::GetValue(). This tests input of a client- + /// side child object type and execution as the parent type. + /// + /*--cef(removed=13302)--*/ + virtual int SetChildRefPtrClient( + CefRefPtr val) = 0; + + /// + /// Set a child object. Returns the object as the parent type. This tests + /// input of a client-side child object type and return as the parent type. + /// + /*--cef(removed=13302)--*/ + virtual CefRefPtr + SetChildRefPtrClientAndReturnParent( + CefRefPtr val) = 0; +#endif // CEF_API_REMOVED(13302) + +#if CEF_API_ADDED(13302) + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestRefPtrClient::GetValue(). This tests input of a client- + /// side child object type and execution as the parent type. + /// + /*--cef(added=13302,capi_name=set_child_ref_ptr_client_v2)--*/ + virtual int SetChildRefPtrClient( + CefRefPtr val) = 0; + + /// + /// Set a child object. Returns the object as the parent type. This tests + /// input of a client-side child object type and return as the parent type. + /// + /*--cef(added=13302, + capi_name=set_child_ref_ptr_client_and_return_parent_v2)--*/ + virtual CefRefPtr + SetChildRefPtrClientAndReturnParent( + CefRefPtr val) = 0; +#endif // CEF_API_ADDED(13302) + + // CLIENT-SIDE REFPTR LIST VALUES + + // Test both with and without a typedef. + typedef std::vector> + RefPtrClientList; + + /// + /// Set an object list vlaue. + /// + /*--cef()--*/ + virtual bool SetRefPtrClientList( + const std::vector>& val, + int val1, + int val2) = 0; + + /// + /// Return an object list value by out-param. + /// + /*--cef(count_func=val:GetRefPtrLibraryListSize)--*/ + virtual bool GetRefPtrClientListByRef( + RefPtrClientList& val, + CefRefPtr val1, + CefRefPtr val2) = 0; + + /// + /// Return the number of object that will be output above. + /// + /*--cef()--*/ + virtual size_t GetRefPtrClientListSize() = 0; + + // LIBRARY-SIDE OWNPTR VALUES + + /// + /// Return an new library-side object. + /// + /*--cef()--*/ + virtual CefOwnPtr GetOwnPtrLibrary( + int val) = 0; + + /// + /// Set an object. Returns the value from + /// CefApiVersionTestScopedLibrary::GetValue(). + /// This tests input and execution of a library-side object type. + /// + /*--cef()--*/ + virtual int SetOwnPtrLibrary( + CefOwnPtr val) = 0; + + /// + /// Set an object. Returns the object passed in. This tests input and output + /// of a library-side object type. + /// + /*--cef()--*/ + virtual CefOwnPtr SetOwnPtrLibraryAndReturn( + CefOwnPtr val) = 0; + + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestScopedLibrary::GetValue(). This tests input of a library- + /// side child object type and execution as the parent type. + /// + /*--cef()--*/ + virtual int SetChildOwnPtrLibrary( + CefOwnPtr val) = 0; + + /// + /// Set a child object. Returns the object as the parent type. This tests + /// input of a library-side child object type and return as the parent type. + /// + /*--cef()--*/ + virtual CefOwnPtr + SetChildOwnPtrLibraryAndReturnParent( + CefOwnPtr val) = 0; + + // CLIENT-SIDE OWNPTR VALUES + + /// + /// Set an object. Returns the value from + /// CefApiVersionTestScopedClient::GetValue(). + /// This tests input and execution of a client-side object type. + /// + /*--cef()--*/ + virtual int SetOwnPtrClient(CefOwnPtr val) = 0; + + /// + /// Set an object. Returns the handler passed in. This tests input and output + /// of a client-side object type. + /// + /*--cef()--*/ + virtual CefOwnPtr SetOwnPtrClientAndReturn( + CefOwnPtr val) = 0; + +#if CEF_API_REMOVED(13302) + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestScopedClient::GetValue(). This tests input of a client- + /// side child object type and execution as the parent type. + /// + /*--cef(removed=13302)--*/ + virtual int SetChildOwnPtrClient( + CefOwnPtr val) = 0; + + /// + /// Set a child object. Returns the object as the parent type. This tests + /// input of a client-side child object type and return as the parent type. + /// + /*--cef(removed=13302)--*/ + virtual CefOwnPtr + SetChildOwnPtrClientAndReturnParent( + CefOwnPtr val) = 0; +#endif // CEF_API_REMOVED(13302) + +#if CEF_API_ADDED(13302) + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestScopedClient::GetValue(). This tests input of a client- + /// side child object type and execution as the parent type. + /// + /*--cef(added=13302,capi_name=set_child_own_ptr_client_v2)--*/ + virtual int SetChildOwnPtrClient( + CefOwnPtr val) = 0; + + /// + /// Set a child object. Returns the object as the parent type. This tests + /// input of a client-side child object type and return as the parent type. + /// + /*--cef(added=13302, + capi_name=set_child_own_ptr_client_and_return_parent_v2)--*/ + virtual CefOwnPtr + SetChildOwnPtrClientAndReturnParent( + CefOwnPtr val) = 0; +#endif // CEF_API_ADDED(13302) + + // LIBRARY-SIDE RAWPTR VALUES + + /// + /// Set an object. Returns the value from + /// CefApiVersionTestScopedLibrary::GetValue(). + /// This tests input and execution of a library-side object type. + /// + /*--cef()--*/ + virtual int SetRawPtrLibrary( + CefRawPtr val) = 0; + + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestScopedLibrary::GetValue(). This tests input of a library- + /// side child object type and execution as the parent type. + /// + /*--cef()--*/ + virtual int SetChildRawPtrLibrary( + CefRawPtr val) = 0; + + // LIBRARY-SIDE RAWPTR LIST VALUES + + // Test both with and without a typedef. + typedef std::vector> + RawPtrLibraryList; + + /// + /// Set an object list vlaue. + /// + /*--cef()--*/ + virtual bool SetRawPtrLibraryList( + const std::vector>& val, + int val1, + int val2) = 0; + + // CLIENT-SIDE RAWPTR VALUES + + /// + /// Set an object. Returns the value from + /// CefApiVersionTestScopedClient::GetValue(). + /// This tests input and execution of a client-side object type. + /// + /*--cef()--*/ + virtual int SetRawPtrClient(CefRawPtr val) = 0; + +#if CEF_API_REMOVED(13302) + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestScopedClient::GetValue(). This tests input of a client- + /// side child object type and execution as the parent type. + /// + /*--cef(removed=13302)--*/ + virtual int SetChildRawPtrClient( + CefRawPtr val) = 0; +#endif + +#if CEF_API_ADDED(13302) + /// + /// Set a child object. Returns the value from + /// CefApiVersionTestScopedClient::GetValue(). This tests input of a client- + /// side child object type and execution as the parent type. + /// + /*--cef(added=13302,capi_name=set_child_raw_ptr_client_v2)--*/ + virtual int SetChildRawPtrClient( + CefRawPtr val) = 0; +#endif + + // CLIENT-SIDE RAWPTR LIST VALUES + + // Test both with and without a typedef. + typedef std::vector> + RawPtrClientList; + + /// + /// Set an object list vlaue. + /// + /*--cef()--*/ + virtual bool SetRawPtrClientList( + const std::vector>& val, + int val1, + int val2) = 0; +}; + +// REFPTR TYPES + +/// +/// Library-side test object for RefPtr. +/// +/*--cef(source=library)--*/ +class CefApiVersionTestRefPtrLibrary : public CefBaseRefCounted { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefRefPtr Create(); + + // NOTE: Example of static method versioning. + +#if CEF_API_ADDED(13301) + /// + /// Create the test object with default value. + /// + /*--cef(added=13301,capi_name=create_with_default)--*/ + static CefRefPtr Create(int value); +#endif + + // NOTE: API that has remained unchanged from the original version. + // This will be ordered first in the C API struct. + + /// + /// Return a legacy value. + /// + /*--cef()--*/ + virtual int GetValueLegacy() = 0; + + /// + /// Set a legacy value. + /// + /*--cef()--*/ + virtual void SetValueLegacy(int value) = 0; + + // NOTE: Experimental API that is only available when CEF_API_VERSION is + // undefined. This will be ordered last in the C API struct. + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + /// + /// Return an experimental value. + /// + /*--cef(added=experimental)--*/ + virtual int GetValueExp() = 0; + + /// + /// Set an experimental value. + /// + /*--cef(added=experimental)--*/ + virtual void SetValueExp(int value) = 0; +#endif + + // NOTE: Example of API changing over time. Name needs to change because the + // return value is the same. Will be ordered by 'added' version in the C API + // struct. + +#if CEF_API_REMOVED(13301) + /// + /// Return a value. This is replaced by GetValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual int GetValue() = 0; + + /// + /// Set a value. This is replaced by SetValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual void SetValue(int value) = 0; +#endif + +#if CEF_API_RANGE(13301, 13302) + /// + /// Return a value (V1). This replaces GetValue in version 13301 and is + /// replaced by CefValueV2 in version 13302. + /// + /*--cef(added=13301,removed=13302)--*/ + virtual int GetValueV1() = 0; + + /// + /// Set a value (V1). This replaces SetValue in version 13301 and is replaced + /// by SefValueV2 in version 13302. + /// + /*--cef(added=13301,removed=13302)--*/ + virtual void SetValueV1(int value) = 0; +#endif + +#if CEF_API_ADDED(13302) + /// + /// Return a value (V2). This replaces GetValueV1 in version 13302. + /// + /*--cef(added=13302)--*/ + virtual int GetValueV2() = 0; + + /// + /// Set a value (V2). This replaces SetValueV1 in version 13302. + /// + /*--cef(added=13302)--*/ + virtual void SetValueV2(int value) = 0; +#endif +}; + +// NOTE: Child has 1 version, parent has multiple versions. + +/// +/// Library-side child test object for RefPtr. +/// +/*--cef(source=library)--*/ +class CefApiVersionTestRefPtrLibraryChild + : public CefApiVersionTestRefPtrLibrary { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefRefPtr Create(); + +#if CEF_API_ADDED(13301) + /// + /// Create the test object with default value. + /// + /*--cef(added=13301,capi_name=create_with_default)--*/ + static CefRefPtr Create(int value, + int other_value); +#endif + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherValue() = 0; + + /// + /// Set a value. + /// + /*--cef()--*/ + virtual void SetOtherValue(int value) = 0; +}; + +// NOTE: Child changes at each version. + +#if CEF_API_REMOVED(13301) +/// +/// Another library-side child test object for RefPtr. This is replaced +/// by CefApiVersionTestRefPtrLibraryChildChildV1 in version 13301. +/// +/*--cef(source=library,removed=13301)--*/ +class CefApiVersionTestRefPtrLibraryChildChild + : public CefApiVersionTestRefPtrLibraryChild { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefRefPtr Create(); + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherOtherValue() = 0; + + /// + /// Set a value. + /// + /*--cef()--*/ + virtual void SetOtherOtherValue(int value) = 0; +}; +#endif // CEF_API_REMOVED(13301) + +#if CEF_API_RANGE(13301, 13302) +/// +/// Another library-side child test object for RefPtr. This replaces +/// CefApiVersionTestRefPtrLibraryChildChild in version 13301 and is +/// replaced by CefApiVersionTestRefPtrLibraryChildChildV2 in version 13302. +/// +/*--cef(source=library,added=13301,removed=13302)--*/ +class CefApiVersionTestRefPtrLibraryChildChildV1 + : public CefApiVersionTestRefPtrLibraryChild { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefRefPtr Create(); + + /// + /// Create the test object with default value. + /// + /*--cef(capi_name=create_with_default)--*/ + static CefRefPtr + Create(int value, int other_value, int other_other_value); + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherOtherValue() = 0; + + /// + /// Set a value. + /// + /*--cef()--*/ + virtual void SetOtherOtherValue(int value) = 0; +}; +#endif // CEF_API_RANGE(13301, 13302) + +#if CEF_API_ADDED(13302) +/// +/// Another library-side child test object for RefPtr. This replaces +/// CefApiVersionTestRefPtrLibraryChildChildV1 in version 13302. +/// +/*--cef(source=library,added=13302)--*/ +class CefApiVersionTestRefPtrLibraryChildChildV2 + : public CefApiVersionTestRefPtrLibraryChild { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefRefPtr Create(); + + // NOTE: Redundant use of 'added' here will be ignored. + + /// + /// Create the test object with default value. + /// + /*--cef(added=13302,capi_name=create_with_default)--*/ + static CefRefPtr + Create(int value, int other_value, int other_other_value); + + // NOTE: Class name changed so we don't need to also change the method name. + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherOtherValue() = 0; + + /// + /// Set a value (v3). + /// + /*--cef()--*/ + virtual void SetOtherOtherValue(int value) = 0; +}; +#endif // CEF_API_ADDED(13302) + +/// +/// Client-side test object for RefPtr. +/// +/*--cef(source=client)--*/ +class CefApiVersionTestRefPtrClient : public virtual CefBaseRefCounted { + public: + // NOTE: API that has remained unchanged from the original version. + // This will be ordered first in the C API struct. + + /// + /// Return a legacy value. + /// + /*--cef()--*/ + virtual int GetValueLegacy() = 0; + + // NOTE: Experimental API that is only available when CEF_API_VERSION is + // undefined. This will be ordered last in the C API struct. + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + /// + /// Return an experimental value. + /// + /*--cef(added=experimental)--*/ + virtual int GetValueExp() = 0; +#endif + + // NOTE: Example of API changing over time. Name needs to change because the + // return value is the same. Will be ordered by 'added' version in the C API + // struct. + + // Available in version < 13301 +#if CEF_API_REMOVED(13301) + /// + /// Return a value. This is replaced with GetValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual int GetValue() = 0; +#endif + +#if CEF_API_RANGE(13301, 13302) + /// + /// Return a value (V1). This replaces GetValue in version 13301 and is + /// replaced with GetValueV2 in version 13302. + /// + /*--cef(added=13301,removed=13302)--*/ + virtual int GetValueV1() = 0; +#endif + +#if CEF_API_ADDED(13302) + /// + /// Return a value (V2). This replaces GetValueV1 in version 13302. + /// + /*--cef(added=13302)--*/ + virtual int GetValueV2() = 0; +#endif +}; + +// NOTE: Children have multiple versions, parent has multiple versions. +// NOTE: Example of both class and method versioning. + +#if CEF_API_REMOVED(13302) +/// +/// Client-side child test object for RefPtr. This is replaced with +/// CefApiVersionTestRefPtrClientChildV2 in version 13302. +/// +/*--cef(source=client,removed=13302)--*/ +class CefApiVersionTestRefPtrClientChild + : public CefApiVersionTestRefPtrClient { + public: + // NOTE: Order of added/removed definitions doesn't matter, provided the order + // doesn't change in the future. + +#if CEF_API_ADDED(13301) + /// + /// Return a value (V1). This replaces GetOtherValue in version 13301. + /// + /*--cef(added=13301)--*/ + virtual int GetOtherValueV1() = 0; +#endif + +#if CEF_API_REMOVED(13301) + /// + /// Return a value. This is replaced with GetOtherValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual int GetOtherValue() = 0; +#endif +}; +#endif // CEF_API_REMOVED(13302) + +#if CEF_API_ADDED(13302) +/// +/// Client-side child test object for RefPtr. This replaces +/// CefApiVersionTestRefPtrClientChild in version 13302. +/// +/*--cef(source=client,added=13302)--*/ +class CefApiVersionTestRefPtrClientChildV2 + : public CefApiVersionTestRefPtrClient { + public: + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherValue() = 0; + +#if CEF_API_ADDED(13303) + /// + /// Return another value. + /// + /*--cef(added=13303)--*/ + virtual int GetAnotherValue() = 0; +#endif +}; +#endif // CEF_API_ADDED(13302) + +// OWNPTR/RAWPTR TYPES + +/// +/// Library-side test object for OwnPtr/RawPtr. +/// +/*--cef(source=library)--*/ +class CefApiVersionTestScopedLibrary : public CefBaseScoped { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefOwnPtr Create(); + + // NOTE: Example of static method versioning. + +#if CEF_API_ADDED(13301) + /// + /// Create the test object with default value. + /// + /*--cef(added=13301,capi_name=create_with_default)--*/ + static CefOwnPtr Create(int value); +#endif + + // NOTE: API that has remained unchanged from the original version. + // This will be ordered first in the C API struct. + + /// + /// Return a legacy value. + /// + /*--cef()--*/ + virtual int GetValueLegacy() = 0; + + /// + /// Set a legacy value. + /// + /*--cef()--*/ + virtual void SetValueLegacy(int value) = 0; + + // NOTE: Experimental API that is only available when CEF_API_VERSION is + // undefined. This will be ordered last in the C API struct. + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + /// + /// Return an experimental value. + /// + /*--cef(added=experimental)--*/ + virtual int GetValueExp() = 0; + + /// + /// Set an experimental value. + /// + /*--cef(added=experimental)--*/ + virtual void SetValueExp(int value) = 0; +#endif + + // NOTE: Example of API changing over time. Name needs to change because the + // return value is the same. Will be ordered by 'added' version in the C API + // struct. + +#if CEF_API_REMOVED(13301) + /// + /// Return a value. This is replaced by GetValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual int GetValue() = 0; + + /// + /// Set a value. This is replaced by SetValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual void SetValue(int value) = 0; +#endif + +#if CEF_API_RANGE(13301, 13302) + /// + /// Return a value (V1). This replaces GetValue in version 13301 and is + /// replaced by CefValueV2 in version 13302. + /// + /*--cef(added=13301,removed=13302)--*/ + virtual int GetValueV1() = 0; + + /// + /// Set a value (V1). This replaces SetValue in version 13301 and is replaced + /// by SefValueV2 in version 13302. + /// + /*--cef(added=13301,removed=13302)--*/ + virtual void SetValueV1(int value) = 0; +#endif + +#if CEF_API_ADDED(13302) + /// + /// Return a value (V2). This replaces GetValueV1 in version 13302. + /// + /*--cef(added=13302)--*/ + virtual int GetValueV2() = 0; + + /// + /// Set a value (V2). This replaces SetValueV1 in version 13302. + /// + /*--cef(added=13302)--*/ + virtual void SetValueV2(int value) = 0; +#endif +}; + +// NOTE: Child has 1 version, parent has multiple versions. + +/// +/// Library-side child test object for OwnPtr/RawPtr. +/// +/*--cef(source=library)--*/ +class CefApiVersionTestScopedLibraryChild + : public CefApiVersionTestScopedLibrary { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefOwnPtr Create(); + +#if CEF_API_ADDED(13301) + /// + /// Create the test object with default value. + /// + /*--cef(added=13301,capi_name=create_with_default)--*/ + static CefOwnPtr Create(int value, + int other_value); +#endif + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherValue() = 0; + + /// + /// Set a value. + /// + /*--cef()--*/ + virtual void SetOtherValue(int value) = 0; +}; + +// NOTE: Child changes at each version. + +#if CEF_API_REMOVED(13301) +/// +/// Another library-side child test object for OwnPtr/RawPtr. This is replaced +/// by CefApiVersionTestScopedLibraryChildChildV1 in version 13301. +/// +/*--cef(source=library,removed=13301)--*/ +class CefApiVersionTestScopedLibraryChildChild + : public CefApiVersionTestScopedLibraryChild { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefOwnPtr Create(); + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherOtherValue() = 0; + + /// + /// Set a value. + /// + /*--cef()--*/ + virtual void SetOtherOtherValue(int value) = 0; +}; +#endif // CEF_API_REMOVED(13301) + +#if CEF_API_RANGE(13301, 13302) +/// +/// Another library-side child test object for OwnPtr/RawPtr. This replaces +/// CefApiVersionTestScopedLibraryChildChild in version 13301 and is +/// replaced by CefApiVersionTestScopedLibraryChildChildV2 in version 13302. +/// +/*--cef(source=library,added=13301,removed=13302)--*/ +class CefApiVersionTestScopedLibraryChildChildV1 + : public CefApiVersionTestScopedLibraryChild { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefOwnPtr Create(); + + /// + /// Create the test object with default value. + /// + /*--cef(capi_name=create_with_default)--*/ + static CefOwnPtr + Create(int value, int other_value, int other_other_value); + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherOtherValue() = 0; + + /// + /// Set a value. + /// + /*--cef()--*/ + virtual void SetOtherOtherValue(int value) = 0; +}; +#endif // CEF_API_RANGE(13301, 13302) + +#if CEF_API_ADDED(13302) +/// +/// Another library-side child test object for OwnPtr/RawPtr. This replaces +/// CefApiVersionTestScopedLibraryChildChildV1 in version 13302. +/// +/*--cef(source=library,added=13302)--*/ +class CefApiVersionTestScopedLibraryChildChildV2 + : public CefApiVersionTestScopedLibraryChild { + public: + /// + /// Create the test object. + /// + /*--cef()--*/ + static CefOwnPtr Create(); + + // NOTE: Redundant use of 'added' here will be ignored. + + /// + /// Create the test object with default value. + /// + /*--cef(added=13302,capi_name=create_with_default)--*/ + static CefOwnPtr + Create(int value, int other_value, int other_other_value); + + // NOTE: Class name changed so we don't need to also change the method name. + + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherOtherValue() = 0; + + /// + /// Set a value (v3). + /// + /*--cef()--*/ + virtual void SetOtherOtherValue(int value) = 0; +}; +#endif // CEF_API_ADDED(13302) + +/// +/// Client-side test object for OwnPtr/RawPtr. +/// +/*--cef(source=client)--*/ +class CefApiVersionTestScopedClient : public virtual CefBaseScoped { + public: + // NOTE: API that has remained unchanged from the original version. + // This will be ordered first in the C API struct. + + /// + /// Return a legacy value. + /// + /*--cef()--*/ + virtual int GetValueLegacy() = 0; + + // NOTE: Experimental API that is only available when CEF_API_VERSION is + // undefined. This will be ordered last in the C API struct. + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + /// + /// Return an experimental value. + /// + /*--cef(added=experimental)--*/ + virtual int GetValueExp() = 0; +#endif + + // NOTE: Example of API changing over time. Name needs to change because the + // return value is the same. Will be ordered by 'added' version in the C API + // struct. + + // Available in version < 13301 +#if CEF_API_REMOVED(13301) + /// + /// Return a value. This is replaced with GetValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual int GetValue() = 0; +#endif + +#if CEF_API_RANGE(13301, 13302) + /// + /// Return a value (V1). This replaces GetValue in version 13301 and is + /// replaced with GetValueV2 in version 13302. + /// + /*--cef(added=13301,removed=13302)--*/ + virtual int GetValueV1() = 0; +#endif + +#if CEF_API_ADDED(13302) + /// + /// Return a value (V2). This replaces GetValueV1 in version 13302. + /// + /*--cef(added=13302)--*/ + virtual int GetValueV2() = 0; +#endif +}; + +// NOTE: Children have multiple versions, parent has multiple versions. +// NOTE: Example of both class and method versioning. + +#if CEF_API_REMOVED(13302) +/// +/// Client-side child test object for OwnPtr/RawPtr. This is replaced with +/// CefApiVersionTestScopedClientChildV2 in version 13302. +/// +/*--cef(source=client,removed=13302)--*/ +class CefApiVersionTestScopedClientChild + : public CefApiVersionTestScopedClient { + public: + // NOTE: Order of added/removed definitions doesn't matter, provided the order + // doesn't change in the future. + +#if CEF_API_ADDED(13301) + /// + /// Return a value (V1). This replaces GetOtherValue in version 13301. + /// + /*--cef(added=13301)--*/ + virtual int GetOtherValueV1() = 0; +#endif + +#if CEF_API_REMOVED(13301) + /// + /// Return a value. This is replaced with GetOtherValueV1 in version 13301. + /// + /*--cef(removed=13301)--*/ + virtual int GetOtherValue() = 0; +#endif +}; +#endif // CEF_API_REMOVED(13302) + +#if CEF_API_ADDED(13302) +/// +/// Client-side child test object for OwnPtr/RawPtr. This replaces +/// CefApiVersionTestScopedClientChild in version 13302. +/// +/*--cef(source=client,added=13302)--*/ +class CefApiVersionTestScopedClientChildV2 + : public CefApiVersionTestScopedClient { + public: + /// + /// Return a value. + /// + /*--cef()--*/ + virtual int GetOtherValue() = 0; + +#if CEF_API_ADDED(13303) + /// + /// Return another value. + /// + /*--cef(added=13303)--*/ + virtual int GetAnotherValue() = 0; +#endif +}; +#endif // CEF_API_ADDED(13302) + +#endif // CEF_INCLUDE_TEST_CEF_API_VERSION_TEST_H_ diff --git a/include/test/cef_translator_test.h b/include/test/cef_translator_test.h index 8ac49343b8..d4ed6db4f2 100644 --- a/include/test/cef_translator_test.h +++ b/include/test/cef_translator_test.h @@ -39,8 +39,8 @@ // be included from unit test targets. // -#ifndef CEF_INCLUDE_TEST_CEF_TEST_H_ -#define CEF_INCLUDE_TEST_CEF_TEST_H_ +#ifndef CEF_INCLUDE_TEST_CEF_TRANSLATOR_TEST_H_ +#define CEF_INCLUDE_TEST_CEF_TRANSLATOR_TEST_H_ #pragma once #if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \ @@ -805,4 +805,4 @@ class CefTranslatorTestScopedClientChild virtual int GetOtherValue() = 0; }; -#endif // CEF_INCLUDE_TEST_CEF_TEST_H_ +#endif // CEF_INCLUDE_TEST_CEF_TRANSLATOR_TEST_H_ diff --git a/libcef/common/api_version_util.h b/libcef/common/api_version_util.h new file mode 100644 index 0000000000..650c6d8ddc --- /dev/null +++ b/libcef/common/api_version_util.h @@ -0,0 +1,54 @@ +// Copyright (c) 2024 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_COMMON_API_VERSION_UTIL_H_ +#define CEF_LIBCEF_COMMON_API_VERSION_UTIL_H_ +#pragma once + +#include "base/logging.h" +#include "base/notreached.h" +#include "cef/include/cef_api_hash.h" + +#define _CEF_RA_CMP(v, op) (cef_api_version() op v) + +#define _CEF_RA_LT(v) _CEF_RA_CMP(v, <) +#define _CEF_RA_GE(v) _CEF_RA_CMP(v, >=) + +#define _CEF_RA_CHECK(check) \ + check << __func__ << " called for invalid API version " << cef_api_version() + +// Helpers for fatally asserting (with [[noreturn]]) that the configured +// CEF API version matches expectations. + +// Assert that the specified version-related condition is true. +#define CEF_API_ASSERT(condition) _CEF_RA_CHECK(LOG_IF(FATAL, (condition))) + +// Annotate should-be unreachable version-related code. +#define CEF_API_NOTREACHED() _CEF_RA_CHECK(NOTREACHED()) + +// Assert that API was added in the specified version. +// Pair with CEF_API_ADDED usage in CEF header files. +#define CEF_API_REQUIRE_ADDED(v) CEF_API_ASSERT(_CEF_RA_LT(v)) + +// Assert that API was removed in the specified version. +// Pair with CEF_API_REMOVED usage in CEF header files. +#define CEF_API_REQUIRE_REMOVED(v) CEF_API_ASSERT(_CEF_RA_GE(v)) + +// Assert that API exists only in the specified version range. +// Pair with CEF_API_RANGE usage in CEF header files. +#define CEF_API_REQUIRE_RANGE(va, vr) \ + CEF_API_ASSERT(_CEF_RA_LT(va) || _CEF_RA_GE(vr)) + +// Helpers for testing the configured CEF API version in conditionals. + +// True if API was added in the specified version. +#define CEF_API_IS_ADDED(v) _CEF_RA_GE(v) + +// True if API was removed in the specified version. +#define CEF_API_IS_REMOVED(v) _CEF_RA_LT(v) + +// True if API exists only in the specified version range. +#define CEF_API_IS_RANGE(va, vr) (_CEF_RA_GE(va) && _CEF_RA_LT(vr)) + +#endif // CEF_LIBCEF_COMMON_API_VERSION_UTIL_H_ diff --git a/libcef/common/test/api_version_test_impl.cc b/libcef/common/test/api_version_test_impl.cc new file mode 100644 index 0000000000..1f8864c901 --- /dev/null +++ b/libcef/common/test/api_version_test_impl.cc @@ -0,0 +1,1136 @@ +// Copyright (c) 2024 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 "cef/include/test/cef_api_version_test.h" +#include "cef/libcef/common/api_version_util.h" + +namespace { + +class CefApiVersionTestRefPtrLibraryImpl + : public CefApiVersionTestRefPtrLibrary { + public: + CefApiVersionTestRefPtrLibraryImpl() = default; + + CefApiVersionTestRefPtrLibraryImpl( + const CefApiVersionTestRefPtrLibraryImpl&) = delete; + CefApiVersionTestRefPtrLibraryImpl& operator=( + const CefApiVersionTestRefPtrLibraryImpl&) = delete; + + // Helper for creating this object at all supported API versions. + static CefApiVersionTestRefPtrLibrary* Create(int value) { + auto* obj = new CefApiVersionTestRefPtrLibraryImpl(); + + if (CEF_API_IS_REMOVED(13301)) { + obj->SetValue(value); + } else if (CEF_API_IS_RANGE(13301, 13302)) { + obj->SetValueV1(value); + } else if (CEF_API_IS_ADDED(13302)) { + obj->SetValueV2(value); + } else { + CEF_API_NOTREACHED(); + } + + return obj; + } + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { + CEF_API_REQUIRE_REMOVED(13301); + return value_; + } + + void SetValue(int value) override { + CEF_API_REQUIRE_REMOVED(13301); + value_ = value; + } + + int GetValueV1() override { + CEF_API_REQUIRE_RANGE(13301, 13302); + return value_; + } + + void SetValueV1(int value) override { + CEF_API_REQUIRE_RANGE(13301, 13302); + value_ = value; + } + + int GetValueV2() override { + CEF_API_REQUIRE_ADDED(13302); + return value_; + } + + void SetValueV2(int value) override { + CEF_API_REQUIRE_ADDED(13302); + value_ = value; + } + + int GetValueExp() override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + return value_exp_; + } + + void SetValueExp(int value) override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + value_exp_ = value; + } + + private: + int value_ = -1; + int value_legacy_ = -1; + int value_exp_ = -1; + + IMPLEMENT_REFCOUNTING(CefApiVersionTestRefPtrLibraryImpl); +}; + +} // namespace + +// static +CefRefPtr +CefApiVersionTestRefPtrLibrary::Create() { + return new CefApiVersionTestRefPtrLibraryImpl(); +} + +// static +CefRefPtr +CefApiVersionTestRefPtrLibrary::Create(int value) { + CEF_API_REQUIRE_ADDED(13301); + return CefApiVersionTestRefPtrLibraryImpl::Create(value); +} + +namespace { + +class CefApiVersionTestRefPtrLibraryChildImpl + : public CefApiVersionTestRefPtrLibraryChild { + public: + CefApiVersionTestRefPtrLibraryChildImpl() = default; + + CefApiVersionTestRefPtrLibraryChildImpl( + const CefApiVersionTestRefPtrLibraryChildImpl&) = delete; + CefApiVersionTestRefPtrLibraryChildImpl& operator=( + const CefApiVersionTestRefPtrLibraryChildImpl&) = delete; + + // Helper for creating this object at all supported API versions. + static CefApiVersionTestRefPtrLibraryChild* Create(int value, + int other_value) { + auto* obj = new CefApiVersionTestRefPtrLibraryChildImpl(); + + if (CEF_API_IS_REMOVED(13301)) { + obj->SetValue(value); + } else if (CEF_API_IS_RANGE(13301, 13302)) { + obj->SetValueV1(value); + } else if (CEF_API_IS_ADDED(13302)) { + obj->SetValueV2(value); + } else { + CEF_API_NOTREACHED(); + } + + obj->SetOtherValue(other_value); + + return obj; + } + + // CefApiVersionTestRefPtrLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { + CEF_API_REQUIRE_REMOVED(13301); + return value_; + } + + void SetValue(int value) override { + CEF_API_REQUIRE_REMOVED(13301); + value_ = value; + } + + int GetValueV1() override { + CEF_API_REQUIRE_RANGE(13301, 13302); + return value_; + } + + void SetValueV1(int value) override { + CEF_API_REQUIRE_RANGE(13301, 13302); + value_ = value; + } + + int GetValueV2() override { + CEF_API_REQUIRE_ADDED(13302); + return value_; + } + + void SetValueV2(int value) override { + CEF_API_REQUIRE_ADDED(13302); + value_ = value; + } + + int GetValueExp() override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + return value_exp_; + } + + void SetValueExp(int value) override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + value_exp_ = value; + } + + // CefApiVersionTestRefPtrLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int value_exp_ = -1; + int other_value_ = -1; + + IMPLEMENT_REFCOUNTING(CefApiVersionTestRefPtrLibraryChildImpl); +}; + +} // namespace + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChild::Create() { + return new CefApiVersionTestRefPtrLibraryChildImpl(); +} + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChild::Create(int value, int other_value) { + CEF_API_REQUIRE_ADDED(13301); + return CefApiVersionTestRefPtrLibraryChildImpl::Create(value, other_value); +} + +namespace { + +// This object will only be created at the required API version, so +// API checks are simplified. +class CefApiVersionTestRefPtrLibraryChildChildImpl + : public CefApiVersionTestRefPtrLibraryChildChild { + public: + CefApiVersionTestRefPtrLibraryChildChildImpl() = default; + + CefApiVersionTestRefPtrLibraryChildChildImpl( + const CefApiVersionTestRefPtrLibraryChildChildImpl&) = delete; + CefApiVersionTestRefPtrLibraryChildChildImpl& operator=( + const CefApiVersionTestRefPtrLibraryChildChildImpl&) = delete; + + // CefApiVersionTestRefPtrLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { return value_; } + + void SetValue(int value) override { value_ = value; } + + int GetValueV1() override { CEF_API_NOTREACHED(); } + + void SetValueV1(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV2() override { CEF_API_NOTREACHED(); } + + void SetValueV2(int value) override { CEF_API_NOTREACHED(); } + + // CefApiVersionTestRefPtrLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + int GetValueExp() override { CEF_API_NOTREACHED(); } + + void SetValueExp(int value) override { CEF_API_NOTREACHED(); } + + // CefApiVersionTestRefPtrLibraryChildChild methods: + + int GetOtherOtherValue() override { return other_other_value_; } + + void SetOtherOtherValue(int value) override { other_other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int other_value_ = -1; + int other_other_value_ = -1; + + IMPLEMENT_REFCOUNTING(CefApiVersionTestRefPtrLibraryChildChildImpl); +}; + +} // namespace + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChildChild::Create() { + CEF_API_REQUIRE_REMOVED(13301); + return new CefApiVersionTestRefPtrLibraryChildChildImpl(); +} + +namespace { + +// This object will only be created at the required API version, so +// API checks are simplified. +class CefApiVersionTestRefPtrLibraryChildChildV1Impl + : public CefApiVersionTestRefPtrLibraryChildChildV1 { + public: + CefApiVersionTestRefPtrLibraryChildChildV1Impl() = default; + + CefApiVersionTestRefPtrLibraryChildChildV1Impl( + const CefApiVersionTestRefPtrLibraryChildChildV1Impl&) = delete; + CefApiVersionTestRefPtrLibraryChildChildV1Impl& operator=( + const CefApiVersionTestRefPtrLibraryChildChildV1Impl&) = delete; + + // CefApiVersionTestRefPtrLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { CEF_API_NOTREACHED(); } + + void SetValue(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV1() override { return value_; } + + void SetValueV1(int value) override { value_ = value; } + + int GetValueV2() override { CEF_API_NOTREACHED(); } + + void SetValueV2(int value) override { CEF_API_NOTREACHED(); } + + int GetValueExp() override { CEF_API_NOTREACHED(); } + + void SetValueExp(int value) override { CEF_API_NOTREACHED(); } + + // CefApiVersionTestRefPtrLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + // CefApiVersionTestRefPtrLibraryChildChildV1 methods: + + int GetOtherOtherValue() override { return other_other_value_; } + + void SetOtherOtherValue(int value) override { other_other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int other_value_ = -1; + int other_other_value_ = -1; + + IMPLEMENT_REFCOUNTING(CefApiVersionTestRefPtrLibraryChildChildV1Impl); +}; + +} // namespace + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChildChildV1::Create() { + CEF_API_REQUIRE_RANGE(13301, 13302); + return new CefApiVersionTestRefPtrLibraryChildChildV1Impl(); +} + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChildChildV1::Create(int value, + int other_value, + int other_other_value) { + CEF_API_REQUIRE_RANGE(13301, 13302); + auto* obj = new CefApiVersionTestRefPtrLibraryChildChildV1Impl(); + obj->SetValueV1(value); + obj->SetOtherValue(other_value); + obj->SetOtherOtherValue(other_other_value); + return obj; +} + +namespace { + +// This object will only be created at the required API version, so +// API checks are simplified. +class CefApiVersionTestRefPtrLibraryChildChildV2Impl + : public CefApiVersionTestRefPtrLibraryChildChildV2 { + public: + CefApiVersionTestRefPtrLibraryChildChildV2Impl() = default; + + CefApiVersionTestRefPtrLibraryChildChildV2Impl( + const CefApiVersionTestRefPtrLibraryChildChildV1Impl&) = delete; + CefApiVersionTestRefPtrLibraryChildChildV1Impl& operator=( + const CefApiVersionTestRefPtrLibraryChildChildV1Impl&) = delete; + + // CefApiVersionTestRefPtrLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { CEF_API_NOTREACHED(); } + + void SetValue(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV1() override { CEF_API_NOTREACHED(); } + + void SetValueV1(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV2() override { return value_; } + + void SetValueV2(int value) override { value_ = value; } + + int GetValueExp() override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + return value_exp_; + } + + void SetValueExp(int value) override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + value_exp_ = value; + } + + // CefApiVersionTestRefPtrLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + // CefApiVersionTestRefPtrLibraryChildChildV2 methods: + + int GetOtherOtherValue() override { return other_other_value_; } + + void SetOtherOtherValue(int value) override { other_other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int value_exp_ = -1; + int other_value_ = -1; + int other_other_value_ = -1; + + IMPLEMENT_REFCOUNTING(CefApiVersionTestRefPtrLibraryChildChildV2Impl); +}; + +} // namespace + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChildChildV2::Create() { + CEF_API_REQUIRE_ADDED(13302); + return new CefApiVersionTestRefPtrLibraryChildChildV2Impl(); +} + +// static +CefRefPtr +CefApiVersionTestRefPtrLibraryChildChildV2::Create(int value, + int other_value, + int other_other_value) { + CEF_API_REQUIRE_ADDED(13302); + auto* obj = new CefApiVersionTestRefPtrLibraryChildChildV2Impl(); + obj->SetValueV2(value); + obj->SetOtherValue(other_value); + obj->SetOtherOtherValue(other_other_value); + return obj; +} + +namespace { + +class CefApiVersionTestScopedLibraryImpl + : public CefApiVersionTestScopedLibrary { + public: + CefApiVersionTestScopedLibraryImpl() = default; + + CefApiVersionTestScopedLibraryImpl( + const CefApiVersionTestScopedLibraryImpl&) = delete; + CefApiVersionTestScopedLibraryImpl& operator=( + const CefApiVersionTestScopedLibraryImpl&) = delete; + + // Helper for creating this object at all supported API versions. + static CefApiVersionTestScopedLibrary* Create(int value) { + auto* obj = new CefApiVersionTestScopedLibraryImpl(); + + if (CEF_API_IS_REMOVED(13301)) { + obj->SetValue(value); + } else if (CEF_API_IS_RANGE(13301, 13302)) { + obj->SetValueV1(value); + } else if (CEF_API_IS_ADDED(13302)) { + obj->SetValueV2(value); + } else { + CEF_API_NOTREACHED(); + } + + return obj; + } + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { + CEF_API_REQUIRE_REMOVED(13301); + return value_; + } + + void SetValue(int value) override { + CEF_API_REQUIRE_REMOVED(13301); + value_ = value; + } + + int GetValueV1() override { + CEF_API_REQUIRE_RANGE(13301, 13302); + return value_; + } + + void SetValueV1(int value) override { + CEF_API_REQUIRE_RANGE(13301, 13302); + value_ = value; + } + + int GetValueV2() override { + CEF_API_REQUIRE_ADDED(13302); + return value_; + } + + void SetValueV2(int value) override { + CEF_API_REQUIRE_ADDED(13302); + value_ = value; + } + + int GetValueExp() override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + return value_exp_; + } + + void SetValueExp(int value) override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + value_exp_ = value; + } + + private: + int value_ = -1; + int value_legacy_ = -1; + int value_exp_ = -1; +}; + +} // namespace + +// static +CefOwnPtr +CefApiVersionTestScopedLibrary::Create() { + return CefOwnPtr( + new CefApiVersionTestScopedLibraryImpl()); +} + +// static +CefOwnPtr +CefApiVersionTestScopedLibrary::Create(int value) { + CEF_API_REQUIRE_ADDED(13301); + return CefOwnPtr( + CefApiVersionTestScopedLibraryImpl::Create(value)); +} + +namespace { + +class CefApiVersionTestScopedLibraryChildImpl + : public CefApiVersionTestScopedLibraryChild { + public: + CefApiVersionTestScopedLibraryChildImpl() = default; + + CefApiVersionTestScopedLibraryChildImpl( + const CefApiVersionTestScopedLibraryChildImpl&) = delete; + CefApiVersionTestScopedLibraryChildImpl& operator=( + const CefApiVersionTestScopedLibraryChildImpl&) = delete; + + // Helper for creating this object at all supported API versions. + static CefApiVersionTestScopedLibraryChild* Create(int value, + int other_value) { + auto* obj = new CefApiVersionTestScopedLibraryChildImpl(); + + if (CEF_API_IS_REMOVED(13301)) { + obj->SetValue(value); + } else if (CEF_API_IS_RANGE(13301, 13302)) { + obj->SetValueV1(value); + } else if (CEF_API_IS_ADDED(13302)) { + obj->SetValueV2(value); + } else { + CEF_API_NOTREACHED(); + } + + obj->SetOtherValue(other_value); + + return obj; + } + + // CefApiVersionTestScopedLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { + CEF_API_REQUIRE_REMOVED(13301); + return value_; + } + + void SetValue(int value) override { + CEF_API_REQUIRE_REMOVED(13301); + value_ = value; + } + + int GetValueV1() override { + CEF_API_REQUIRE_RANGE(13301, 13302); + return value_; + } + + void SetValueV1(int value) override { + CEF_API_REQUIRE_RANGE(13301, 13302); + value_ = value; + } + + int GetValueV2() override { + CEF_API_REQUIRE_ADDED(13302); + return value_; + } + + void SetValueV2(int value) override { + CEF_API_REQUIRE_ADDED(13302); + value_ = value; + } + + int GetValueExp() override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + return value_exp_; + } + + void SetValueExp(int value) override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + value_exp_ = value; + } + + // CefApiVersionTestScopedLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int value_exp_ = -1; + int other_value_ = -1; +}; + +} // namespace + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChild::Create() { + return CefOwnPtr( + new CefApiVersionTestScopedLibraryChildImpl()); +} + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChild::Create(int value, int other_value) { + CEF_API_REQUIRE_ADDED(13301); + return CefOwnPtr( + CefApiVersionTestScopedLibraryChildImpl::Create(value, other_value)); +} + +namespace { + +// This object will only be created at the required API version, so +// API checks are simplified. +class CefApiVersionTestScopedLibraryChildChildImpl + : public CefApiVersionTestScopedLibraryChildChild { + public: + CefApiVersionTestScopedLibraryChildChildImpl() = default; + + CefApiVersionTestScopedLibraryChildChildImpl( + const CefApiVersionTestScopedLibraryChildChildImpl&) = delete; + CefApiVersionTestScopedLibraryChildChildImpl& operator=( + const CefApiVersionTestScopedLibraryChildChildImpl&) = delete; + + // CefApiVersionTestScopedLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { return value_; } + + void SetValue(int value) override { value_ = value; } + + int GetValueV1() override { CEF_API_NOTREACHED(); } + + void SetValueV1(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV2() override { CEF_API_NOTREACHED(); } + + void SetValueV2(int value) override { CEF_API_NOTREACHED(); } + + // CefApiVersionTestScopedLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + int GetValueExp() override { CEF_API_NOTREACHED(); } + + void SetValueExp(int value) override { CEF_API_NOTREACHED(); } + + // CefApiVersionTestScopedLibraryChildChild methods: + + int GetOtherOtherValue() override { return other_other_value_; } + + void SetOtherOtherValue(int value) override { other_other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int other_value_ = -1; + int other_other_value_ = -1; +}; + +} // namespace + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChildChild::Create() { + CEF_API_REQUIRE_REMOVED(13301); + return CefOwnPtr( + new CefApiVersionTestScopedLibraryChildChildImpl()); +} + +namespace { + +// This object will only be created at the required API version, so +// API checks are simplified. +class CefApiVersionTestScopedLibraryChildChildV1Impl + : public CefApiVersionTestScopedLibraryChildChildV1 { + public: + CefApiVersionTestScopedLibraryChildChildV1Impl() = default; + + CefApiVersionTestScopedLibraryChildChildV1Impl( + const CefApiVersionTestScopedLibraryChildChildV1Impl&) = delete; + CefApiVersionTestScopedLibraryChildChildV1Impl& operator=( + const CefApiVersionTestScopedLibraryChildChildV1Impl&) = delete; + + // CefApiVersionTestScopedLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { CEF_API_NOTREACHED(); } + + void SetValue(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV1() override { return value_; } + + void SetValueV1(int value) override { value_ = value; } + + int GetValueV2() override { CEF_API_NOTREACHED(); } + + void SetValueV2(int value) override { CEF_API_NOTREACHED(); } + + int GetValueExp() override { CEF_API_NOTREACHED(); } + + void SetValueExp(int value) override { CEF_API_NOTREACHED(); } + + // CefApiVersionTestScopedLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + // CefApiVersionTestScopedLibraryChildChildV1 methods: + + int GetOtherOtherValue() override { return other_other_value_; } + + void SetOtherOtherValue(int value) override { other_other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int other_value_ = -1; + int other_other_value_ = -1; +}; + +} // namespace + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChildChildV1::Create() { + CEF_API_REQUIRE_RANGE(13301, 13302); + return CefOwnPtr( + new CefApiVersionTestScopedLibraryChildChildV1Impl()); +} + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChildChildV1::Create(int value, + int other_value, + int other_other_value) { + CEF_API_REQUIRE_RANGE(13301, 13302); + auto* obj = new CefApiVersionTestScopedLibraryChildChildV1Impl(); + obj->SetValueV1(value); + obj->SetOtherValue(other_value); + obj->SetOtherOtherValue(other_other_value); + return CefOwnPtr(obj); +} + +namespace { + +// This object will only be created at the required API version, so +// API checks are simplified. +class CefApiVersionTestScopedLibraryChildChildV2Impl + : public CefApiVersionTestScopedLibraryChildChildV2 { + public: + CefApiVersionTestScopedLibraryChildChildV2Impl() = default; + + CefApiVersionTestScopedLibraryChildChildV2Impl( + const CefApiVersionTestScopedLibraryChildChildV1Impl&) = delete; + CefApiVersionTestScopedLibraryChildChildV1Impl& operator=( + const CefApiVersionTestScopedLibraryChildChildV1Impl&) = delete; + + // CefApiVersionTestScopedLibrary methods: + + int GetValueLegacy() override { return value_legacy_; } + + void SetValueLegacy(int value) override { value_legacy_ = value; } + + int GetValue() override { CEF_API_NOTREACHED(); } + + void SetValue(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV1() override { CEF_API_NOTREACHED(); } + + void SetValueV1(int value) override { CEF_API_NOTREACHED(); } + + int GetValueV2() override { return value_; } + + void SetValueV2(int value) override { value_ = value; } + + int GetValueExp() override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + return value_exp_; + } + + void SetValueExp(int value) override { + CEF_API_REQUIRE_ADDED(CEF_EXPERIMENTAL); + value_exp_ = value; + } + + // CefApiVersionTestScopedLibraryChild methods: + + int GetOtherValue() override { return other_value_; } + + void SetOtherValue(int value) override { other_value_ = value; } + + // CefApiVersionTestScopedLibraryChildChildV2 methods: + + int GetOtherOtherValue() override { return other_other_value_; } + + void SetOtherOtherValue(int value) override { other_other_value_ = value; } + + private: + int value_ = -1; + int value_legacy_ = -1; + int value_exp_ = -1; + int other_value_ = -1; + int other_other_value_ = -1; +}; + +} // namespace + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChildChildV2::Create() { + CEF_API_REQUIRE_ADDED(13302); + return CefOwnPtr( + new CefApiVersionTestScopedLibraryChildChildV2Impl()); +} + +// static +CefOwnPtr +CefApiVersionTestScopedLibraryChildChildV2::Create(int value, + int other_value, + int other_other_value) { + CEF_API_REQUIRE_ADDED(13302); + auto* obj = new CefApiVersionTestScopedLibraryChildChildV2Impl(); + obj->SetValueV2(value); + obj->SetOtherValue(other_value); + obj->SetOtherOtherValue(other_other_value); + return CefOwnPtr(obj); +} + +namespace { + +class CefApiVersionTestImpl : public CefApiVersionTest { + public: + CefApiVersionTestImpl() = default; + + CefApiVersionTestImpl(const CefApiVersionTestImpl&) = delete; + CefApiVersionTestImpl& operator=(const CefApiVersionTestImpl&) = delete; + + // LIBRARY-SIDE REFPTR VALUES + + CefRefPtr GetRefPtrLibrary(int val) override { + return CefApiVersionTestRefPtrLibraryChildImpl::Create(val, 0); + } + + int SetRefPtrLibrary(CefRefPtr val) override { + return GetValue(val); + } + + CefRefPtr SetRefPtrLibraryAndReturn( + CefRefPtr val) override { + return val; + } + + int SetChildRefPtrLibrary( + CefRefPtr val) override { + return GetValue(val); + } + + CefRefPtr + SetChildRefPtrLibraryAndReturnParent( + CefRefPtr val) override { + return val; + } + + // LIBRARY-SIDE REFPTR LIST VALUES + + bool SetRefPtrLibraryList( + const std::vector>& val, + int val1, + int val2) override { + if (val.size() != 2U) { + return false; + } + return GetValue(val[0]) == val1 && GetValue(val[1]) == val2; + } + + bool GetRefPtrLibraryListByRef(RefPtrLibraryList& val, + int val1, + int val2) override { + if (val.size() != GetRefPtrLibraryListSize()) { + return false; + } + val.clear(); + val.push_back(CefApiVersionTestRefPtrLibraryChildImpl::Create(val1, 0)); + val.push_back(CefApiVersionTestRefPtrLibraryImpl::Create(val2)); + return true; + } + + size_t GetRefPtrLibraryListSize() override { return 2U; } + + // CLIENT-SIDE REFPTR VALUES + + int SetRefPtrClient(CefRefPtr val) override { + return GetValue(val); + } + + CefRefPtr SetRefPtrClientAndReturn( + CefRefPtr val) override { + return val; + } + + int SetChildRefPtrClient( + CefRefPtr val) override { + CEF_API_REQUIRE_REMOVED(13302); + return GetValue(val); + } + + CefRefPtr SetChildRefPtrClientAndReturnParent( + CefRefPtr val) override { + CEF_API_REQUIRE_REMOVED(13302); + return val; + } + + int SetChildRefPtrClient( + CefRefPtr val) override { + CEF_API_REQUIRE_ADDED(13302); + return GetValue(val); + } + + CefRefPtr SetChildRefPtrClientAndReturnParent( + CefRefPtr val) override { + CEF_API_REQUIRE_ADDED(13302); + return val; + } + + // CLIENT-SIDE REFPTR LIST VALUES + + bool SetRefPtrClientList( + const std::vector>& val, + int val1, + int val2) override { + if (val.size() != 2U) { + return false; + } + return GetValue(val[0]) == val1 && GetValue(val[1]) == val2; + } + + bool GetRefPtrClientListByRef( + RefPtrClientList& val, + CefRefPtr val1, + CefRefPtr val2) override { + if (val.size() != GetRefPtrClientListSize()) { + return false; + } + val.clear(); + val.push_back(val1); + val.push_back(val2); + return true; + } + + size_t GetRefPtrClientListSize() override { return 2U; } + + // LIBRARY-SIDE OWNPTR VALUES + + CefOwnPtr GetOwnPtrLibrary(int val) override { + return CefOwnPtr( + CefApiVersionTestScopedLibraryChildImpl::Create(val, 0)); + } + + int SetOwnPtrLibrary(CefOwnPtr val) override { + return GetValue(val); + } + + CefOwnPtr SetOwnPtrLibraryAndReturn( + CefOwnPtr val) override { + return val; + } + + int SetChildOwnPtrLibrary( + CefOwnPtr val) override { + return GetValue(val); + } + + CefOwnPtr + SetChildOwnPtrLibraryAndReturnParent( + CefOwnPtr val) override { + return CefOwnPtr(val.release()); + } + + // CLIENT-SIDE OWNPTR VALUES + + int SetOwnPtrClient(CefOwnPtr val) override { + return GetValue(val); + } + + CefOwnPtr SetOwnPtrClientAndReturn( + CefOwnPtr val) override { + return val; + } + + int SetChildOwnPtrClient( + CefOwnPtr val) override { + CEF_API_REQUIRE_REMOVED(13302); + return GetValue(val); + } + + CefOwnPtr SetChildOwnPtrClientAndReturnParent( + CefOwnPtr val) override { + CEF_API_REQUIRE_REMOVED(13302); + return CefOwnPtr(val.release()); + } + + int SetChildOwnPtrClient( + CefOwnPtr val) override { + CEF_API_REQUIRE_ADDED(13302); + return GetValue(val); + } + + CefOwnPtr SetChildOwnPtrClientAndReturnParent( + CefOwnPtr val) override { + CEF_API_REQUIRE_ADDED(13302); + return CefOwnPtr(val.release()); + } + + // LIBRARY-SIDE RAWPTR VALUES + + int SetRawPtrLibrary(CefRawPtr val) override { + return GetValue(val); + } + + int SetChildRawPtrLibrary( + CefRawPtr val) override { + return GetValue(val); + } + + // LIBRARY-SIDE RAWPTR LIST VALUES + + bool SetRawPtrLibraryList( + const std::vector>& val, + int val1, + int val2) override { + if (val.size() != 2U) { + return false; + } + return GetValue(val[0]) == val1 && GetValue(val[1]) == val2; + } + + // CLIENT-SIDE RAWPTR VALUES + + int SetRawPtrClient(CefRawPtr val) override { + return GetValue(val); + } + + int SetChildRawPtrClient( + CefRawPtr val) override { + CEF_API_REQUIRE_REMOVED(13302); + return GetValue(val); + } + + int SetChildRawPtrClient( + CefRawPtr val) override { + CEF_API_REQUIRE_ADDED(13302); + return GetValue(val); + } + + // CLIENT-SIDE RAWPTR LIST VALUES + + bool SetRawPtrClientList( + const std::vector>& val, + int val1, + int val2) override { + if (val.size() != 2U) { + return false; + } + return GetValue(val[0]) == val1 && GetValue(val[1]) == val2; + } + + private: + template + static int GetValue(T& obj) { + if (CEF_API_IS_REMOVED(13301)) { + return obj->GetValue(); + } + if (CEF_API_IS_RANGE(13301, 13302)) { + return obj->GetValueV1(); + } + if (CEF_API_IS_ADDED(13302)) { + return obj->GetValueV2(); + } + CEF_API_NOTREACHED(); + } + + IMPLEMENT_REFCOUNTING(CefApiVersionTestImpl); +}; + +} // namespace + +// static +CefRefPtr CefApiVersionTest::Create() { + return new CefApiVersionTestImpl(); +} diff --git a/libcef/common/test/translator_test_impl.cc b/libcef/common/test/translator_test_impl.cc index 75e37b429e..3c20741d8c 100644 --- a/libcef/common/test/translator_test_impl.cc +++ b/libcef/common/test/translator_test_impl.cc @@ -4,6 +4,8 @@ #include "cef/include/test/cef_translator_test.h" +namespace { + class CefTranslatorTestRefPtrLibraryImpl : public CefTranslatorTestRefPtrLibrary { public: @@ -18,19 +20,22 @@ class CefTranslatorTestRefPtrLibraryImpl void SetValue(int value) override { value_ = value; } - protected: + private: int value_; - private: IMPLEMENT_REFCOUNTING(CefTranslatorTestRefPtrLibraryImpl); }; +} // namespace + // static CefRefPtr CefTranslatorTestRefPtrLibrary::Create(int value) { return new CefTranslatorTestRefPtrLibraryImpl(value); } +namespace { + class CefTranslatorTestRefPtrLibraryChildImpl : public CefTranslatorTestRefPtrLibraryChild { public: @@ -50,20 +55,23 @@ class CefTranslatorTestRefPtrLibraryChildImpl void SetOtherValue(int value) override { other_value_ = value; } - protected: + private: int value_; int other_value_; - private: IMPLEMENT_REFCOUNTING(CefTranslatorTestRefPtrLibraryChildImpl); }; +} // namespace + // static CefRefPtr CefTranslatorTestRefPtrLibraryChild::Create(int value, int other_value) { return new CefTranslatorTestRefPtrLibraryChildImpl(value, other_value); } +namespace { + class CefTranslatorTestRefPtrLibraryChildChildImpl : public CefTranslatorTestRefPtrLibraryChildChild { public: @@ -91,15 +99,16 @@ class CefTranslatorTestRefPtrLibraryChildChildImpl void SetOtherOtherValue(int value) override { other_other_value_ = value; } - protected: + private: int value_; int other_value_; int other_other_value_; - private: IMPLEMENT_REFCOUNTING(CefTranslatorTestRefPtrLibraryChildChildImpl); }; +} // namespace + // static CefRefPtr CefTranslatorTestRefPtrLibraryChildChild::Create(int value, @@ -109,6 +118,8 @@ CefTranslatorTestRefPtrLibraryChildChild::Create(int value, other_other_value); } +namespace { + class CefTranslatorTestScopedLibraryImpl : public CefTranslatorTestScopedLibrary { public: @@ -123,10 +134,12 @@ class CefTranslatorTestScopedLibraryImpl void SetValue(int value) override { value_ = value; } - protected: + private: int value_; }; +} // namespace + // static CefOwnPtr CefTranslatorTestScopedLibrary::Create(int value) { @@ -134,6 +147,8 @@ CefTranslatorTestScopedLibrary::Create(int value) { new CefTranslatorTestScopedLibraryImpl(value)); } +namespace { + class CefTranslatorTestScopedLibraryChildImpl : public CefTranslatorTestScopedLibraryChild { public: @@ -153,11 +168,13 @@ class CefTranslatorTestScopedLibraryChildImpl void SetOtherValue(int value) override { other_value_ = value; } - protected: + private: int value_; int other_value_; }; +} // namespace + // static CefOwnPtr CefTranslatorTestScopedLibraryChild::Create(int value, int other_value) { @@ -165,6 +182,8 @@ CefTranslatorTestScopedLibraryChild::Create(int value, int other_value) { new CefTranslatorTestScopedLibraryChildImpl(value, other_value)); } +namespace { + class CefTranslatorTestScopedLibraryChildChildImpl : public CefTranslatorTestScopedLibraryChildChild { public: @@ -192,12 +211,14 @@ class CefTranslatorTestScopedLibraryChildChildImpl void SetOtherOtherValue(int value) override { other_other_value_ = value; } - protected: + private: int value_; int other_value_; int other_other_value_; }; +} // namespace + // static CefOwnPtr CefTranslatorTestScopedLibraryChildChild::Create(int value, @@ -208,6 +229,8 @@ CefTranslatorTestScopedLibraryChildChild::Create(int value, other_other_value)); } +namespace { + class CefTranslatorTestImpl : public CefTranslatorTest { public: CefTranslatorTestImpl() = default; @@ -595,6 +618,8 @@ class CefTranslatorTestImpl : public CefTranslatorTest { IMPLEMENT_REFCOUNTING(CefTranslatorTestImpl); }; +} // namespace + // static CefRefPtr CefTranslatorTest::Create() { return new CefTranslatorTestImpl(); diff --git a/libcef/features/features.gni b/libcef/features/features.gni index 7d7ae49716..3fc4e80a7a 100644 --- a/libcef/features/features.gni +++ b/libcef/features/features.gni @@ -11,4 +11,7 @@ declare_args() { # FOR OTHER CHROMIUM/CEF BUILD CONFIGURATIONS AS ITS USE MAY HAVE SIGNIFICANT # PERFORMANCE AND/OR SECURITY IMPLICATIONS. is_cef_sandbox_build = false + + # Optionally configure the CEF API version. This impacts wrapper-side only. + cef_api_version = "" } diff --git a/libcef_dll/cpptoc/base_ref_counted_cpptoc.h b/libcef_dll/cpptoc/base_ref_counted_cpptoc.h index a8e1eb1ffb..b940e72e83 100644 --- a/libcef_dll/cpptoc/base_ref_counted_cpptoc.h +++ b/libcef_dll/cpptoc/base_ref_counted_cpptoc.h @@ -23,4 +23,7 @@ class CefBaseRefCountedCppToC CefBaseRefCountedCppToC(); }; +constexpr auto CefBaseRefCountedCppToC_Wrap = CefBaseRefCountedCppToC::Wrap; +constexpr auto CefBaseRefCountedCppToC_Unwrap = CefBaseRefCountedCppToC::Unwrap; + #endif // CEF_LIBCEF_DLL_CPPTOC_BASE_REF_COUNTED_CPPTOC_H_ diff --git a/libcef_dll/cpptoc/base_scoped_cpptoc.h b/libcef_dll/cpptoc/base_scoped_cpptoc.h index 63a66883da..0a78be0f8a 100644 --- a/libcef_dll/cpptoc/base_scoped_cpptoc.h +++ b/libcef_dll/cpptoc/base_scoped_cpptoc.h @@ -22,4 +22,17 @@ class CefBaseScopedCppToC : public CefCppToCScoped c) { + auto [ownerPtr, structPtr] = CefBaseScopedCppToC_WrapRaw(c); + ownerPtr.release(); + return structPtr; +} + #endif // CEF_LIBCEF_DLL_CPPTOC_BASE_CPPTOC_H_ diff --git a/libcef_dll/cpptoc/cpptoc_ref_counted.h b/libcef_dll/cpptoc/cpptoc_ref_counted.h index d7a04e7691..34370df59d 100644 --- a/libcef_dll/cpptoc/cpptoc_ref_counted.h +++ b/libcef_dll/cpptoc/cpptoc_ref_counted.h @@ -45,7 +45,8 @@ class CefCppToCRefCounted : public CefBaseRefCounted { } // Cast our structure to the wrapper structure type. - WrapperStruct* wrapperStruct = GetWrapperStruct(s); + WrapperStruct* wrapperStruct = + GetWrapperStruct(s, /*require_exact_type=*/false); // If the type does not match this object then we need to unwrap as the // derived type. @@ -68,8 +69,6 @@ class CefCppToCRefCounted : public CefBaseRefCounted { static CefRefPtr Get(StructName* s) { DCHECK(s); WrapperStruct* wrapperStruct = GetWrapperStruct(s); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); return wrapperStruct->object_; } @@ -122,13 +121,21 @@ class CefCppToCRefCounted : public CefBaseRefCounted { StructName struct_; }; - static WrapperStruct* GetWrapperStruct(StructName* s) { + static WrapperStruct* GetWrapperStruct(StructName* s, + bool require_exact_type = true) { // Offset using the WrapperStruct size instead of individual member sizes // to avoid problems due to platform/compiler differences in structure // padding. - return reinterpret_cast( + auto* wrapperStruct = reinterpret_cast( reinterpret_cast(s) - (sizeof(WrapperStruct) - sizeof(StructName))); + + if (require_exact_type) { + // Verify that the wrapper offset was calculated correctly. + CHECK_EQ(kWrapperType, wrapperStruct->type_); + } + + return wrapperStruct; } // Unwrap as the derived type. @@ -152,9 +159,6 @@ class CefCppToCRefCounted : public CefBaseRefCounted { WrapperStruct* wrapperStruct = GetWrapperStruct(reinterpret_cast(base)); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); - wrapperStruct->wrapper_->AddRef(); } @@ -166,9 +170,6 @@ class CefCppToCRefCounted : public CefBaseRefCounted { WrapperStruct* wrapperStruct = GetWrapperStruct(reinterpret_cast(base)); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); - return wrapperStruct->wrapper_->Release(); } @@ -180,9 +181,6 @@ class CefCppToCRefCounted : public CefBaseRefCounted { WrapperStruct* wrapperStruct = GetWrapperStruct(reinterpret_cast(base)); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); - return wrapperStruct->wrapper_->HasOneRef(); } @@ -195,9 +193,6 @@ class CefCppToCRefCounted : public CefBaseRefCounted { WrapperStruct* wrapperStruct = GetWrapperStruct(reinterpret_cast(base)); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); - return wrapperStruct->wrapper_->HasAtLeastOneRef(); } diff --git a/libcef_dll/cpptoc/cpptoc_scoped.h b/libcef_dll/cpptoc/cpptoc_scoped.h index 11fe3d9643..b8347b8af9 100644 --- a/libcef_dll/cpptoc/cpptoc_scoped.h +++ b/libcef_dll/cpptoc/cpptoc_scoped.h @@ -6,6 +6,8 @@ #define CEF_LIBCEF_DLL_CPPTOC_CPPTOC_SCOPED_H_ #pragma once +#include + #include "include/base/cef_logging.h" #include "include/capi/cef_base_capi.h" #include "include/cef_base.h" @@ -52,17 +54,18 @@ class CefCppToCScoped : public CefBaseScoped { // For example: // // void MyMethod(MyType* obj) { - // CefOwnPtr MyTypeWrapper = MyTypeCppToC::WrapRaw(obj); - // my_method(MyTypeWrapper->GetStruct()); + // [MyTypeWrapper, MyTypeStruct] = MyTypeCppToC::WrapRaw(obj); + // my_method(MyTypeStruct); // // MyTypeWrapper is deleted when MyMethod() goes out of scope. // } // // void my_method(my_type_t* struct) { // // Access |struct| here but you can't delete it. // } - static CefOwnPtr WrapRaw(CefRawPtr c) { + static std::pair, StructName*> WrapRaw( + CefRawPtr c) { if (!c) { - return CefOwnPtr(); + return std::make_pair(nullptr, nullptr); } // Wrap our object with the CefCppToC class. @@ -70,7 +73,9 @@ class CefCppToCScoped : public CefBaseScoped { wrapper->Initialize(c, false); // Return the owned wrapper object. - return CefOwnPtr(wrapper); + CefOwnPtr wrapperPtr(wrapper); + auto* wrapperStruct = wrapperPtr->GetStruct(); + return std::make_pair(std::move(wrapperPtr), wrapperStruct); } // Retrieve the underlying object instance for a structure reference passed @@ -87,7 +92,8 @@ class CefCppToCScoped : public CefBaseScoped { } // Cast our structure to the wrapper structure type. - WrapperStruct* wrapperStruct = GetWrapperStruct(s); + WrapperStruct* wrapperStruct = + GetWrapperStruct(s, /*require_exact_type=*/false); // If the type does not match this object then we need to unwrap as the // derived type. @@ -96,8 +102,8 @@ class CefCppToCScoped : public CefBaseScoped { } // We should own the underlying object currently. - DCHECK(wrapperStruct->wrapper_->owned_); - DCHECK(wrapperStruct->object_); + CHECK(wrapperStruct->wrapper_->owned_); + CHECK(wrapperStruct->object_); // We're giving up ownership of the underlying object. Clear the pointer so // it doesn't get deleted. @@ -123,7 +129,8 @@ class CefCppToCScoped : public CefBaseScoped { } // Cast our structure to the wrapper structure type. - WrapperStruct* wrapperStruct = GetWrapperStruct(s); + WrapperStruct* wrapperStruct = + GetWrapperStruct(s, /*require_exact_type=*/false); // If the type does not match this object then we need to unwrap as the // derived type. @@ -137,12 +144,10 @@ class CefCppToCScoped : public CefBaseScoped { // Retrieve the same side wrapper associated with the structure. Ownership // does not change. - static ClassName* GetWrapper(StructName* s) { + static CefBaseScoped* GetWrapper(StructName* s) { DCHECK(s); WrapperStruct* wrapperStruct = GetWrapperStruct(s); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); - return static_cast(wrapperStruct->wrapper_); + return wrapperStruct->wrapper_; } // Retrieve the underlying object instance from our own structure reference @@ -151,8 +156,6 @@ class CefCppToCScoped : public CefBaseScoped { static BaseName* Get(StructName* s) { DCHECK(s); WrapperStruct* wrapperStruct = GetWrapperStruct(s); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); return wrapperStruct->object_; } @@ -196,13 +199,21 @@ class CefCppToCScoped : public CefBaseScoped { } } - static WrapperStruct* GetWrapperStruct(StructName* s) { + static WrapperStruct* GetWrapperStruct(StructName* s, + bool require_exact_type = true) { // Offset using the WrapperStruct size instead of individual member sizes // to avoid problems due to platform/compiler differences in structure // padding. - return reinterpret_cast( + auto* wrapperStruct = reinterpret_cast( reinterpret_cast(s) - (sizeof(WrapperStruct) - sizeof(StructName))); + + if (require_exact_type) { + // Verify that the wrapper offset was calculated correctly. + CHECK_EQ(kWrapperType, wrapperStruct->type_); + } + + return wrapperStruct; } // Unwrap as the derived type. @@ -219,11 +230,9 @@ class CefCppToCScoped : public CefBaseScoped { WrapperStruct* wrapperStruct = GetWrapperStruct(reinterpret_cast(base)); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); // Should only be deleting wrappers that own the underlying object. - DCHECK(wrapperStruct->wrapper_->owned_); + CHECK(wrapperStruct->wrapper_->owned_); delete wrapperStruct->wrapper_; } diff --git a/libcef_dll/ctocpp/base_ref_counted_ctocpp.h b/libcef_dll/ctocpp/base_ref_counted_ctocpp.h index dfff39e26f..27deab3b5b 100644 --- a/libcef_dll/ctocpp/base_ref_counted_ctocpp.h +++ b/libcef_dll/ctocpp/base_ref_counted_ctocpp.h @@ -23,4 +23,7 @@ class CefBaseRefCountedCToCpp CefBaseRefCountedCToCpp(); }; +constexpr auto CefBaseRefCountedCToCpp_Wrap = CefBaseRefCountedCToCpp::Wrap; +constexpr auto CefBaseRefCountedCToCpp_Unwrap = CefBaseRefCountedCToCpp::Unwrap; + #endif // CEF_LIBCEF_DLL_CTOCPP_BASE_REF_COUNTED_CTOCPP_H_ diff --git a/libcef_dll/ctocpp/base_scoped_ctocpp.h b/libcef_dll/ctocpp/base_scoped_ctocpp.h index 6e7c0852a9..5af36d1141 100644 --- a/libcef_dll/ctocpp/base_scoped_ctocpp.h +++ b/libcef_dll/ctocpp/base_scoped_ctocpp.h @@ -22,4 +22,8 @@ class CefBaseScopedCToCpp : public CefCToCppScopedtype_); return wrapperStruct->struct_; } @@ -55,7 +54,8 @@ class CefCToCppRefCounted : public BaseName { // from the other side. struct WrapperStruct; - static WrapperStruct* GetWrapperStruct(const BaseName* obj); + static WrapperStruct* GetWrapperStruct(const BaseName* obj, + bool require_exact_type = true); // Unwrap as the derived type. static StructName* UnwrapDerived(CefWrapperType type, BaseName* c); @@ -119,6 +119,18 @@ CefRefPtr CefCToCppRefCounted::Wrap( return nullptr; } + const auto size = reinterpret_cast(s)->size; + if (size != sizeof(StructName)) { + LOG(FATAL) << "Cannot wrap struct with invalid base.size value (got " + << size << ", expected " << sizeof(StructName) + << ") at API version " +#if defined(WRAPPING_CEF_SHARED) + << CEF_API_VERSION; +#else + << cef_api_version(); +#endif + } + // Wrap their structure with the CefCToCppRefCounted object. WrapperStruct* wrapperStruct = new WrapperStruct; wrapperStruct->type_ = kWrapperType; @@ -140,7 +152,8 @@ StructName* CefCToCppRefCounted::Unwrap( return nullptr; } - WrapperStruct* wrapperStruct = GetWrapperStruct(c.get()); + WrapperStruct* wrapperStruct = + GetWrapperStruct(c.get(), /*require_exact_type=*/false); // If the type does not match this object then we need to unwrap as the // derived type. @@ -160,8 +173,6 @@ bool CefCToCppRefCounted::Release() const { UnderlyingRelease(); if (ref_count_.Release()) { WrapperStruct* wrapperStruct = GetWrapperStruct(this); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); delete wrapperStruct; return true; } @@ -171,12 +182,20 @@ bool CefCToCppRefCounted::Release() const { template typename CefCToCppRefCounted::WrapperStruct* CefCToCppRefCounted::GetWrapperStruct( - const BaseName* obj) { + const BaseName* obj, + bool require_exact_type) { // Offset using the WrapperStruct size instead of individual member sizes to // avoid problems due to platform/compiler differences in structure padding. - return reinterpret_cast( + auto* wrapperStruct = reinterpret_cast( reinterpret_cast(const_cast(obj)) - (sizeof(WrapperStruct) - sizeof(ClassName))); + + if (require_exact_type) { + // Verify that the wrapper offset was calculated correctly. + CHECK_EQ(kWrapperType, wrapperStruct->type_); + } + + return wrapperStruct; } #endif // CEF_LIBCEF_DLL_CTOCPP_CTOCPP_REF_COUNTED_H_ diff --git a/libcef_dll/ctocpp/ctocpp_scoped.h b/libcef_dll/ctocpp/ctocpp_scoped.h index be1bb84805..ab0fabed8e 100644 --- a/libcef_dll/ctocpp/ctocpp_scoped.h +++ b/libcef_dll/ctocpp/ctocpp_scoped.h @@ -8,6 +8,7 @@ #include "include/base/cef_logging.h" #include "include/capi/cef_base_capi.h" +#include "include/cef_api_hash.h" #include "include/cef_base.h" #include "libcef_dll/wrapper_types.h" @@ -75,8 +76,6 @@ class CefCToCppScoped : public BaseName { // If returning the structure across the DLL boundary use Unwrap() instead. StructName* GetStruct() const { WrapperStruct* wrapperStruct = GetWrapperStruct(this); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); return wrapperStruct->struct_; } @@ -85,7 +84,8 @@ class CefCToCppScoped : public BaseName { // from the other side. struct WrapperStruct; - static WrapperStruct* GetWrapperStruct(const BaseName* obj); + static WrapperStruct* GetWrapperStruct(const BaseName* obj, + bool require_exact_type = true); // Unwrap as the derived type. static StructName* UnwrapDerivedOwn(CefWrapperType type, @@ -107,7 +107,19 @@ template CefOwnPtr CefCToCppScoped::Wrap( StructName* s) { if (!s) { - return CefOwnPtr(); + return nullptr; + } + + const auto size = reinterpret_cast(s)->size; + if (size != sizeof(StructName)) { + LOG(FATAL) << "Cannot wrap struct with invalid base.size value (got " + << size << ", expected " << sizeof(StructName) + << ") at API version " +#if defined(WRAPPING_CEF_SHARED) + << CEF_API_VERSION; +#else + << cef_api_version(); +#endif } // Wrap their structure with the CefCToCpp object. @@ -125,7 +137,8 @@ StructName* CefCToCppScoped::UnwrapOwn( return nullptr; } - WrapperStruct* wrapperStruct = GetWrapperStruct(c.get()); + WrapperStruct* wrapperStruct = + GetWrapperStruct(c.get(), /*require_exact_type=*/false); // If the type does not match this object then we need to unwrap as the // derived type. @@ -156,7 +169,8 @@ StructName* CefCToCppScoped::UnwrapRaw( return nullptr; } - WrapperStruct* wrapperStruct = GetWrapperStruct(c); + WrapperStruct* wrapperStruct = + GetWrapperStruct(c, /*require_exact_type=*/false); // If the type does not match this object then we need to unwrap as the // derived type. @@ -173,8 +187,6 @@ NO_SANITIZE("cfi-icall") void CefCToCppScoped::operator delete( void* ptr) { WrapperStruct* wrapperStruct = GetWrapperStruct(static_cast(ptr)); - // Verify that the wrapper offset was calculated correctly. - DCHECK_EQ(kWrapperType, wrapperStruct->type_); // May be NULL if UnwrapOwn() was called. cef_base_scoped_t* base = @@ -194,12 +206,20 @@ void CefCToCppScoped::operator delete( template typename CefCToCppScoped::WrapperStruct* CefCToCppScoped::GetWrapperStruct( - const BaseName* obj) { + const BaseName* obj, + bool require_exact_type) { // Offset using the WrapperStruct size instead of individual member sizes to // avoid problems due to platform/compiler differences in structure padding. - return reinterpret_cast( + auto* wrapperStruct = reinterpret_cast( reinterpret_cast(const_cast(obj)) - (sizeof(WrapperStruct) - sizeof(ClassName))); + + if (require_exact_type) { + // Verify that the wrapper offset was calculated correctly. + CHECK_EQ(kWrapperType, wrapperStruct->type_); + } + + return wrapperStruct; } #endif // CEF_LIBCEF_DLL_CTOCPP_CTOCPP_SCOPED_H_ diff --git a/libcef_dll/libcef_dll2.cc b/libcef_dll/libcef_dll2.cc index 94ecd77e56..39b7755e0c 100644 --- a/libcef_dll/libcef_dll2.cc +++ b/libcef_dll/libcef_dll2.cc @@ -4,15 +4,24 @@ // #include +#include +#include -#include "include/base/cef_build.h" -#include "include/cef_api_hash.h" -#include "include/cef_version.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "cef/include/base/cef_build.h" +#include "cef/include/cef_api_hash.h" +#include "cef/include/cef_id_mappers.h" +#include "cef/include/cef_version_info.h" #if defined(OS_WIN) -#include "include/internal/cef_win.h" +#include "cef/include/internal/cef_win.h" #endif +namespace { +int g_version = -1; +} + CEF_EXPORT int cef_version_info(int entry) { switch (entry) { case 0: @@ -36,19 +45,117 @@ CEF_EXPORT int cef_version_info(int entry) { } } -CEF_EXPORT const char* cef_api_hash(int entry) { +#include "cef/libcef_dll/cef_api_versions.inc" + +CEF_EXPORT const char* cef_api_hash(int version, int entry) { + static const ApiVersionHash* hash = nullptr; + + // Initialize on the first successful lookup. + if (!hash) { + for (size_t i = 0; i < kApiVersionHashesSize; ++i) { + if (version == kApiVersionHashes[i].version) { + hash = &kApiVersionHashes[i]; + break; + } + } + + if (hash) { + g_version = version; + } + } + + if (!hash) { + LOG(ERROR) << "Request for unsupported CEF API version " << version; + return nullptr; + } + + if (version != g_version) { + LOG(ERROR) << "CEF API version cannot be configured multiple times"; + return nullptr; + } + switch (entry) { case 0: - return CEF_API_HASH_PLATFORM; + return hash->platform; case 1: - return CEF_API_HASH_UNIVERSAL; + return hash->universal; case 2: return CEF_COMMIT_HASH; default: - return NULL; + return nullptr; } } +CEF_EXPORT int cef_api_version() { + return g_version; +} + +#include "cef/libcef_dll/cef_pack_resources.inc" + +CEF_EXPORT int cef_id_for_pack_resource_name(const char* name) { + static base::NoDestructor> string_to_id_map; + + // Initialize on the first call. + if (string_to_id_map->empty()) { + for (size_t i = 0; i < kIdNamesPackResourcesSize; ++i) { + (*string_to_id_map)[kIdNamesPackResources[i].name] = + kIdNamesPackResources[i].id; + } + } + + const auto& it = string_to_id_map->find(name); + if (it != string_to_id_map->end()) { + return it->second; + } + + LOG(WARNING) << __func__ << " called with unsupported value " << name; + return -1; +} + +#include "cef/libcef_dll/cef_pack_strings.inc" + +CEF_EXPORT int cef_id_for_pack_string_name(const char* name) { + static base::NoDestructor> string_to_id_map; + + // Initialize on the first call. + if (string_to_id_map->empty()) { + for (size_t i = 0; i < kIdNamesPackStringsSize; ++i) { + (*string_to_id_map)[kIdNamesPackStrings[i].name] = + kIdNamesPackStrings[i].id; + } + } + + const auto& it = string_to_id_map->find(name); + if (it != string_to_id_map->end()) { + return it->second; + } + + LOG(WARNING) << __func__ << " called with unsupported value " << name; + return -1; +} + +#include "cef/libcef_dll/cef_command_ids.inc" + +CEF_EXPORT int cef_id_for_command_id_name(const char* name) { + static base::NoDestructor> string_to_id_map; + + // Initialize on the first call. + if (string_to_id_map->empty()) { + for (size_t i = 0; i < kIdNamesCommandIdsSize; ++i) { + (*string_to_id_map)[kIdNamesCommandIds[i].name] = + kIdNamesCommandIds[i].id; + } + } + + const auto& it = string_to_id_map->find(name); + if (it != string_to_id_map->end()) { + return it->second; + } + + LOG(WARNING) << __func__ << " called with unsupported value " << name; + return -1; +} + #if defined(OS_WIN) #if defined(ARCH_CPU_32_BITS) diff --git a/libcef_dll/wrapper/libcef_dll_wrapper2.cc b/libcef_dll/wrapper/libcef_dll_wrapper2.cc index 4e6c48a12c..760818648f 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper2.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper2.cc @@ -19,12 +19,9 @@ int CefRunWinMainWithPreferredStackSize(wWinMainPtr wWinMain, int nCmdShow) { CHECK(wWinMain && hInstance); - const char* api_hash = cef_api_hash(0); - if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) { - // The libcef API hash does not match the current header API hash. - DCHECK(false); - return 0; - } + const char* api_hash = cef_api_hash(CEF_API_VERSION, 0); + CHECK(!strcmp(api_hash, CEF_API_HASH_PLATFORM)) << + "API hashes for libcef and libcef_dll_wrapper do not match."; return cef_run_winmain_with_preferred_stack_size(wWinMain, hInstance, lpCmdLine, nCmdShow); @@ -33,12 +30,9 @@ int CefRunWinMainWithPreferredStackSize(wWinMainPtr wWinMain, int CefRunMainWithPreferredStackSize(mainPtr main, int argc, char* argv[]) { CHECK(main); - const char* api_hash = cef_api_hash(0); - if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) { - // The libcef API hash does not match the current header API hash. - DCHECK(false); - return 0; - } + const char* api_hash = cef_api_hash(CEF_API_VERSION, 0); + CHECK(!strcmp(api_hash, CEF_API_HASH_PLATFORM)) << + "API hashes for libcef and libcef_dll_wrapper do not match."; return cef_run_main_with_preferred_stack_size(main, argc, argv); } diff --git a/patch/patches/chrome_browser_browser.patch b/patch/patches/chrome_browser_browser.patch index 692cc95b43..d0cdb8925f 100644 --- a/patch/patches/chrome_browser_browser.patch +++ b/patch/patches/chrome_browser_browser.patch @@ -88,7 +88,7 @@ index 4aaba212926f4..e8aadb2c97fe9 100644 } diff --git chrome/browser/ui/BUILD.gn chrome/browser/ui/BUILD.gn -index 977b3222c8f7c..ee71bf3a1a463 100644 +index 977b3222c8f7c..e5d36a4e5921e 100644 --- chrome/browser/ui/BUILD.gn +++ chrome/browser/ui/BUILD.gn @@ -8,6 +8,7 @@ import("//build/config/compiler/compiler.gni") @@ -118,18 +118,21 @@ index 977b3222c8f7c..ee71bf3a1a463 100644 "//chrome:resources", "//chrome:strings", "//chrome/app:chrome_dll_resources", -@@ -699,6 +705,10 @@ static_library("ui") { +@@ -699,6 +705,13 @@ static_library("ui") { deps += [ "//components/plus_addresses/resources:vector_icons" ] } + if (enable_cef) { -+ deps += [ "//cef:cef_resources" ] ++ deps += [ ++ "//cef:cef_resources", ++ "//cef:make_version_header", ++ ] + } + # TODO(crbug.com/41437292): Remove this circular dependency. # Any circular includes must depend on the target "//chrome/browser:browser_public_dependencies". # These are all-platform circular includes. -@@ -5487,6 +5497,7 @@ static_library("ui") { +@@ -5487,6 +5500,7 @@ static_library("ui") { if (enable_printing) { deps += [ "//components/printing/browser", diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index 7b17560a71..2d305a18b0 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -13,8 +13,8 @@ #include "include/base/cef_callback.h" #include "include/cef_browser.h" -#include "include/cef_command_ids.h" #include "include/cef_frame.h" +#include "include/cef_id_mappers.h" #include "include/cef_parser.h" #include "include/cef_shared_process_message_builder.h" #include "include/cef_ssl_status.h" @@ -318,6 +318,32 @@ bool IsAllowedToolbarButton(cef_chrome_toolbar_button_type_t button_type) { } bool IsAllowedAppMenuCommandId(int command_id) { + // Version-safe static declarations of IDC variables using names from + // cef_command_ids.h. + CEF_DECLARE_COMMAND_ID(IDC_NEW_WINDOW); + CEF_DECLARE_COMMAND_ID(IDC_NEW_INCOGNITO_WINDOW); + CEF_DECLARE_COMMAND_ID(IDC_ZOOM_MENU); + CEF_DECLARE_COMMAND_ID(IDC_ZOOM_PLUS); + CEF_DECLARE_COMMAND_ID(IDC_ZOOM_NORMAL); + CEF_DECLARE_COMMAND_ID(IDC_ZOOM_MINUS); + CEF_DECLARE_COMMAND_ID(IDC_FULLSCREEN); + CEF_DECLARE_COMMAND_ID(IDC_PRINT); + CEF_DECLARE_COMMAND_ID(IDC_FIND); + CEF_DECLARE_COMMAND_ID(IDC_FIND_NEXT); + CEF_DECLARE_COMMAND_ID(IDC_FIND_PREVIOUS); + CEF_DECLARE_COMMAND_ID(IDC_MORE_TOOLS_MENU); + CEF_DECLARE_COMMAND_ID(IDC_CLEAR_BROWSING_DATA); + CEF_DECLARE_COMMAND_ID(IDC_MANAGE_EXTENSIONS); + CEF_DECLARE_COMMAND_ID(IDC_PERFORMANCE); + CEF_DECLARE_COMMAND_ID(IDC_TASK_MANAGER); + CEF_DECLARE_COMMAND_ID(IDC_DEV_TOOLS); + CEF_DECLARE_COMMAND_ID(IDC_EDIT_MENU); + CEF_DECLARE_COMMAND_ID(IDC_CUT); + CEF_DECLARE_COMMAND_ID(IDC_COPY); + CEF_DECLARE_COMMAND_ID(IDC_PASTE); + CEF_DECLARE_COMMAND_ID(IDC_OPTIONS); + CEF_DECLARE_COMMAND_ID(IDC_EXIT); + // Only the commands in this array will be allowed. static const int kAllowedCommandIds[] = { IDC_NEW_WINDOW, @@ -361,6 +387,28 @@ bool IsAllowedAppMenuCommandId(int command_id) { } bool IsAllowedContextMenuCommandId(int command_id) { + // Version-safe static declarations of IDC variables using names from + // cef_command_ids.h. + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_CUSTOM_FIRST); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_CUSTOM_LAST); + CEF_DECLARE_COMMAND_ID(IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST); + CEF_DECLARE_COMMAND_ID(IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST); + CEF_DECLARE_COMMAND_ID(IDC_BACK); + CEF_DECLARE_COMMAND_ID(IDC_FORWARD); + CEF_DECLARE_COMMAND_ID(IDC_RELOAD); + CEF_DECLARE_COMMAND_ID(IDC_RELOAD_BYPASSING_CACHE); + CEF_DECLARE_COMMAND_ID(IDC_RELOAD_CLEARING_CACHE); + CEF_DECLARE_COMMAND_ID(IDC_STOP); + CEF_DECLARE_COMMAND_ID(IDC_PRINT); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_CUT); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_COPY); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_PASTE); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_DELETE); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_SELECTALL); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_UNDO); + CEF_DECLARE_COMMAND_ID(IDC_CONTENT_CONTEXT_REDO); + // Allow commands added by web content. if (command_id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && command_id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { diff --git a/tests/cefclient/cefclient_mac.mm b/tests/cefclient/cefclient_mac.mm index 9c09f6a83f..3670c3b0d7 100644 --- a/tests/cefclient/cefclient_mac.mm +++ b/tests/cefclient/cefclient_mac.mm @@ -7,7 +7,7 @@ #include "include/cef_app.h" #import "include/cef_application_mac.h" -#include "include/cef_command_ids.h" +#import "include/cef_id_mappers.h" #import "include/wrapper/cef_library_loader.h" #include "tests/cefclient/browser/main_context_impl.h" #include "tests/cefclient/browser/resource.h" @@ -404,6 +404,24 @@ - (BOOL)keyWindowIsModal { // // This implementation is based on Chromium's AppController class. - (BOOL)validateUserInterfaceItem:(id)item { + // Version-safe static declarations of IDC variables using names from + // cef_command_ids.h. + CEF_DECLARE_COMMAND_ID(IDC_OPEN_FILE); + CEF_DECLARE_COMMAND_ID(IDC_NEW_TAB); + CEF_DECLARE_COMMAND_ID(IDC_FOCUS_LOCATION); + CEF_DECLARE_COMMAND_ID(IDC_FOCUS_SEARCH); + CEF_DECLARE_COMMAND_ID(IDC_SHOW_HISTORY); + CEF_DECLARE_COMMAND_ID(IDC_SHOW_BOOKMARK_MANAGER); + CEF_DECLARE_COMMAND_ID(IDC_CLEAR_BROWSING_DATA); + CEF_DECLARE_COMMAND_ID(IDC_SHOW_DOWNLOADS); + CEF_DECLARE_COMMAND_ID(IDC_IMPORT_SETTINGS); + CEF_DECLARE_COMMAND_ID(IDC_MANAGE_EXTENSIONS); + CEF_DECLARE_COMMAND_ID(IDC_HELP_PAGE_VIA_MENU); + CEF_DECLARE_COMMAND_ID(IDC_OPTIONS); + CEF_DECLARE_COMMAND_ID(IDC_NEW_WINDOW); + CEF_DECLARE_COMMAND_ID(IDC_TASK_MANAGER); + CEF_DECLARE_COMMAND_ID(IDC_NEW_INCOGNITO_WINDOW); + SEL action = [item action]; BOOL enable = NO; // Whether opening a new browser window is allowed. @@ -413,37 +431,26 @@ - (BOOL)validateUserInterfaceItem:(id)item { // no key window. if (action == @selector(commandDispatch:) || action == @selector(commandDispatchUsingKeyModifiers:)) { - switch ([item tag]) { + const auto tag = [item tag]; + if (tag == IDC_OPEN_FILE || tag == IDC_NEW_TAB || + tag == IDC_FOCUS_LOCATION || tag == IDC_FOCUS_SEARCH || + tag == IDC_SHOW_HISTORY || tag == IDC_SHOW_BOOKMARK_MANAGER || + tag == IDC_CLEAR_BROWSING_DATA || tag == IDC_SHOW_DOWNLOADS || + tag == IDC_IMPORT_SETTINGS || tag == IDC_MANAGE_EXTENSIONS || + tag == IDC_HELP_PAGE_VIA_MENU || tag == IDC_OPTIONS) { // Browser-level items that open in new tabs or perform an action in a // current tab should not open if there's a window- or app-modal dialog. - case IDC_OPEN_FILE: - case IDC_NEW_TAB: - case IDC_FOCUS_LOCATION: - case IDC_FOCUS_SEARCH: - case IDC_SHOW_HISTORY: - case IDC_SHOW_BOOKMARK_MANAGER: - case IDC_CLEAR_BROWSING_DATA: - case IDC_SHOW_DOWNLOADS: - case IDC_IMPORT_SETTINGS: - case IDC_MANAGE_EXTENSIONS: - case IDC_HELP_PAGE_VIA_MENU: - case IDC_OPTIONS: - enable = canOpenNewBrowser && ![self keyWindowIsModal]; - break; + enable = canOpenNewBrowser && ![self keyWindowIsModal]; + } else if (tag == IDC_NEW_WINDOW) { // Browser-level items that open in new windows: allow the user to open // a new window even if there's a window-modal dialog. - case IDC_NEW_WINDOW: - enable = canOpenNewBrowser; - break; - case IDC_TASK_MANAGER: - enable = YES; - break; - case IDC_NEW_INCOGNITO_WINDOW: - enable = canOpenNewBrowser; - break; - default: - enable = ![self keyWindowIsModal]; - break; + enable = canOpenNewBrowser; + } else if (tag == IDC_TASK_MANAGER) { + enable = YES; + } else if (tag == IDC_NEW_INCOGNITO_WINDOW) { + enable = canOpenNewBrowser; + } else { + enable = ![self keyWindowIsModal]; } } else if ([self respondsToSelector:action]) { // All other selectors that this class implements. @@ -469,22 +476,23 @@ - (void)commandDispatch:(id)sender { } } + // Version-safe static declarations of IDC variables using names from + // cef_command_ids.h. + CEF_DECLARE_COMMAND_ID(IDC_FIND); + CEF_DECLARE_COMMAND_ID(IDC_FIND_NEXT); + CEF_DECLARE_COMMAND_ID(IDC_FIND_PREVIOUS); + // Handle specific commands where we want to make the last active browser // frontmost and then re-execute the command. - switch ([sender tag]) { - case IDC_FIND: - case IDC_FIND_NEXT: - case IDC_FIND_PREVIOUS: - if (id window = [self getActiveBrowserNSWindow]) { - [window makeKeyAndOrderFront:nil]; - if ([window respondsToSelector:@selector(commandDispatch:)]) { - [window commandDispatch:sender]; - return; - } + const auto tag = [sender tag]; + if (tag == IDC_FIND || tag == IDC_FIND_NEXT || tag == IDC_FIND_PREVIOUS) { + if (id window = [self getActiveBrowserNSWindow]) { + [window makeKeyAndOrderFront:nil]; + if ([window respondsToSelector:@selector(commandDispatch:)]) { + [window commandDispatch:sender]; + return; } - break; - default: - break; + } } LOG(INFO) << "Unhandled commandDispatch: for tag " << [sender tag]; diff --git a/tests/ceftests/api_version_unittest.cc b/tests/ceftests/api_version_unittest.cc new file mode 100644 index 0000000000..7fca1262d3 --- /dev/null +++ b/tests/ceftests/api_version_unittest.cc @@ -0,0 +1,868 @@ +// Copyright (c) 2024 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 + +#include "include/test/cef_api_version_test.h" +#include "tests/ceftests/test_handler.h" +#include "tests/gtest/include/gtest/gtest.h" + +namespace { + +template +static int GetValue(T& obj) { +#if CEF_API_REMOVED(13301) + return obj->GetValue(); +#elif CEF_API_RANGE(13301, 13302) + return obj->GetValueV1(); +#elif CEF_API_ADDED(13302) + return obj->GetValueV2(); +#endif +} + +CefRefPtr CreateRefPtrLibrary(int val) { +#if CEF_API_ADDED(13301) + return CefApiVersionTestRefPtrLibrary::Create(val); +#else + auto obj = CefApiVersionTestRefPtrLibrary::Create(); + obj->SetValue(val); + return obj; +#endif +} + +CefRefPtr CreateRefPtrLibraryChild( + int val1, + int val2) { +#if CEF_API_ADDED(13301) + return CefApiVersionTestRefPtrLibraryChild::Create(val1, val2); +#else + auto obj = CefApiVersionTestRefPtrLibraryChild::Create(); + obj->SetValue(val1); + obj->SetOtherValue(val2); + return obj; +#endif +} + +#if CEF_API_REMOVED(13301) +CefRefPtr +#elif CEF_API_RANGE(13301, 13302) +CefRefPtr +#elif CEF_API_ADDED(13302) +CefRefPtr +#endif +CreateRefPtrLibraryChildChild(int val1, int val2, int val3) { +#if CEF_API_REMOVED(13301) + auto obj = CefApiVersionTestRefPtrLibraryChildChild::Create(); + obj->SetValue(val1); + obj->SetOtherValue(val2); + obj->SetOtherOtherValue(val3); + return obj; +#elif CEF_API_RANGE(13301, 13302) + return CefApiVersionTestRefPtrLibraryChildChildV1::Create(val1, val2, val3); +#elif CEF_API_ADDED(13302) + return CefApiVersionTestRefPtrLibraryChildChildV2::Create(val1, val2, val3); +#endif +} + +} // namespace + +// Test getting/setting library-side RefPtr types. +TEST(ApiVersionTest, RefPtrLibrary) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + CefRefPtr test_obj = + CreateRefPtrLibrary(kTestVal); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + int retval = obj->SetRefPtrLibrary(test_obj); + EXPECT_EQ(kTestVal, retval); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + + const int kTestVal2 = 30; + CefRefPtr test_obj2 = + obj->GetRefPtrLibrary(kTestVal2); + EXPECT_EQ(kTestVal2, GetValue(test_obj2)); + int retval2 = obj->SetRefPtrLibrary(test_obj2); + EXPECT_EQ(kTestVal2, retval2); + EXPECT_EQ(kTestVal2, GetValue(test_obj2)); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); + EXPECT_TRUE(test_obj->HasOneRef()); + EXPECT_TRUE(test_obj2->HasOneRef()); +} + +// Test getting/setting inherited library-side RefPtr types. +TEST(ApiVersionTest, RefPtrLibraryInherit) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + const int kTestVal2 = 40; + auto test_obj = CreateRefPtrLibraryChild(kTestVal, kTestVal2); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, test_obj->GetOtherValue()); + int retval = obj->SetRefPtrLibrary(test_obj); + EXPECT_EQ(kTestVal, retval); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, test_obj->GetOtherValue()); + + EXPECT_EQ(kTestVal, obj->SetChildRefPtrLibrary(test_obj)); + auto parent = obj->SetChildRefPtrLibraryAndReturnParent(test_obj); + EXPECT_EQ(kTestVal, GetValue(parent)); + parent = nullptr; + + const int kTestVal3 = 100; + auto test_obj2 = + CreateRefPtrLibraryChildChild(kTestVal, kTestVal2, kTestVal3); + EXPECT_EQ(kTestVal, GetValue(test_obj2)); + EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue()); + EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue()); + int retval2 = obj->SetRefPtrLibrary(test_obj2); + EXPECT_EQ(kTestVal, retval2); + EXPECT_EQ(kTestVal, GetValue(test_obj2)); + EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue()); + EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue()); + + EXPECT_EQ(kTestVal, obj->SetChildRefPtrLibrary(test_obj2)); + auto parent2 = obj->SetChildRefPtrLibraryAndReturnParent(test_obj2); + EXPECT_EQ(kTestVal, GetValue(parent2)); + parent2 = nullptr; + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); + EXPECT_TRUE(test_obj->HasOneRef()); + EXPECT_TRUE(test_obj2->HasOneRef()); +} + +// Test getting/setting library-side RefPtr list types. +TEST(ApiVersionTest, RefPtrLibraryList) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kVal1 = 34; + const int kVal2 = 10; + + CefRefPtr val1 = CreateRefPtrLibrary(kVal1); + CefRefPtr val2 = + CreateRefPtrLibraryChild(kVal2, 0); + + std::vector> list; + list.push_back(val1); + list.push_back(val2); + EXPECT_TRUE(obj->SetRefPtrLibraryList(list, kVal1, kVal2)); + + list.clear(); + EXPECT_TRUE(obj->GetRefPtrLibraryListByRef(list, kVal1, kVal2)); + EXPECT_EQ(2U, list.size()); + EXPECT_EQ(kVal1, GetValue(list[0])); + EXPECT_EQ(kVal2, GetValue(list[1])); + + list.clear(); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); + EXPECT_TRUE(val1->HasOneRef()); + EXPECT_TRUE(val2->HasOneRef()); +} + +namespace { + +class ApiVersionTestRefPtrClient : public CefApiVersionTestRefPtrClient { + public: + explicit ApiVersionTestRefPtrClient(int val) : val_(val) {} + + int GetValueLegacy() override { return val_legacy_; } + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int GetValueExp() override { return val_exp_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetValue() override { return val_; } +#elif CEF_API_RANGE(13301, 13302) + int GetValueV1() override { return val_; } +#elif CEF_API_ADDED(13302) + int GetValueV2() override { return val_; } +#endif + + private: + const int val_; + int val_legacy_ = -1; +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int val_exp_ = -1; +#endif + + IMPLEMENT_REFCOUNTING(ApiVersionTestRefPtrClient); + DISALLOW_COPY_AND_ASSIGN(ApiVersionTestRefPtrClient); +}; + +#if CEF_API_REMOVED(13302) + +class ApiVersionTestRefPtrClientChild + : public CefApiVersionTestRefPtrClientChild { + public: + ApiVersionTestRefPtrClientChild(int val, int other_val) + : val_(val), other_val_(other_val) {} + + int GetValueLegacy() override { return val_legacy_; } + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int GetValueExp() override { return val_exp_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetValue() override { return val_; } +#elif CEF_API_RANGE(13301, 13302) + int GetValueV1() override { return val_; } +#elif CEF_API_ADDED(13302) + int GetValueV2() override { return val_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetOtherValue() override { return other_val_; } +#else + int GetOtherValueV1() override { return other_val_; } +#endif + + private: + const int val_; + const int other_val_; + int val_legacy_ = -1; +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int val_exp_ = -1; +#endif + + IMPLEMENT_REFCOUNTING(ApiVersionTestRefPtrClientChild); + DISALLOW_COPY_AND_ASSIGN(ApiVersionTestRefPtrClientChild); +}; + +using ApiVersionTestRefPtrClientChildType = ApiVersionTestRefPtrClientChild; + +#else // CEF_API_ADDED(13302) + +class ApiVersionTestRefPtrClientChildV2 + : public CefApiVersionTestRefPtrClientChildV2 { + public: + ApiVersionTestRefPtrClientChildV2(int val, int other_val) + : val_(val), other_val_(other_val) {} + + int GetValueLegacy() override { return val_legacy_; } + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int GetValueExp() override { return val_exp_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetValue() override { return val_; } +#elif CEF_API_RANGE(13301, 13302) + int GetValueV1() override { return val_; } +#elif CEF_API_ADDED(13302) + int GetValueV2() override { return val_; } +#endif + + int GetOtherValue() override { return other_val_; } + +#if CEF_API_ADDED(13303) + int GetAnotherValue() override { return another_val_; } +#endif + + private: + const int val_; + const int other_val_; +#if CEF_API_ADDED(13303) + int another_val_ = -1; +#endif + int val_legacy_ = -1; +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int val_exp_ = -1; +#endif + + IMPLEMENT_REFCOUNTING(ApiVersionTestRefPtrClientChildV2); + DISALLOW_COPY_AND_ASSIGN(ApiVersionTestRefPtrClientChildV2); +}; + +using ApiVersionTestRefPtrClientChildType = ApiVersionTestRefPtrClientChildV2; + +#endif // CEF_API_ADDED(13302) + +template +static int GetOtherValue(T& obj) { +#if CEF_API_REMOVED(13301) + // ApiVersionTestRefPtrClientChild + return obj->GetOtherValue(); +#elif CEF_API_RANGE(13301, 13302) + // ApiVersionTestRefPtrClientChild + return obj->GetOtherValueV1(); +#elif CEF_API_ADDED(13302) + // ApiVersionTestRefPtrClientChildV2 + return obj->GetOtherValue(); +#endif +} + +} // namespace + +// Test getting/setting client-side RefPtr types. +TEST(ApiVersionTest, RefPtrClient) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + + CefRefPtr test_obj = + new ApiVersionTestRefPtrClient(kTestVal); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal, obj->SetRefPtrClient(test_obj.get())); + CefRefPtr handler = + obj->SetRefPtrClientAndReturn(test_obj.get()); + EXPECT_EQ(test_obj.get(), handler.get()); + EXPECT_EQ(kTestVal, GetValue(handler)); + handler = nullptr; + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); + EXPECT_TRUE(test_obj->HasOneRef()); +} + +// Test getting/setting inherited client-side RefPtr types. +TEST(ApiVersionTest, RefPtrClientInherit) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + const int kTestVal2 = 86; + + CefRefPtr test_obj = + new ApiVersionTestRefPtrClientChildType(kTestVal, kTestVal2); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, GetOtherValue(test_obj)); + int retval = obj->SetRefPtrClient(test_obj); + EXPECT_EQ(kTestVal, retval); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, GetOtherValue(test_obj)); + + EXPECT_EQ(kTestVal, obj->SetChildRefPtrClient(test_obj)); + CefRefPtr handler = + obj->SetChildRefPtrClientAndReturnParent(test_obj); + EXPECT_EQ(kTestVal, GetValue(handler)); + EXPECT_EQ(test_obj.get(), handler.get()); + handler = nullptr; + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); + EXPECT_TRUE(test_obj->HasOneRef()); +} + +// Test getting/setting client-side RefPtr list types. +TEST(ApiVersionTest, RefPtrClientList) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kVal1 = 34; + const int kVal2 = 10; + + CefRefPtr val1 = + new ApiVersionTestRefPtrClient(kVal1); + CefRefPtr val2 = + new ApiVersionTestRefPtrClientChildType(kVal2, 0); + + std::vector> list; + list.push_back(val1); + list.push_back(val2); + EXPECT_TRUE(obj->SetRefPtrClientList(list, kVal1, kVal2)); + + list.clear(); + EXPECT_TRUE(obj->GetRefPtrClientListByRef(list, val1, val2)); + EXPECT_EQ(2U, list.size()); + EXPECT_EQ(kVal1, GetValue(list[0])); + EXPECT_EQ(val1.get(), list[0].get()); + EXPECT_EQ(kVal2, GetValue(list[1])); + EXPECT_EQ(val2.get(), list[1].get()); + + list.clear(); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); + EXPECT_TRUE(val1->HasOneRef()); + EXPECT_TRUE(val2->HasOneRef()); +} + +namespace { + +CefOwnPtr CreateScopedLibrary(int val) { +#if CEF_API_ADDED(13301) + return CefApiVersionTestScopedLibrary::Create(val); +#else + auto obj = CefApiVersionTestScopedLibrary::Create(); + obj->SetValue(val); + return obj; +#endif +} + +CefOwnPtr CreateScopedLibraryChild( + int val1, + int val2) { +#if CEF_API_ADDED(13301) + return CefApiVersionTestScopedLibraryChild::Create(val1, val2); +#else + auto obj = CefApiVersionTestScopedLibraryChild::Create(); + obj->SetValue(val1); + obj->SetOtherValue(val2); + return obj; +#endif +} + +#if CEF_API_REMOVED(13301) +CefOwnPtr +#elif CEF_API_RANGE(13301, 13302) +CefOwnPtr +#elif CEF_API_ADDED(13302) +CefOwnPtr +#endif +CreateScopedLibraryChildChild(int val1, int val2, int val3) { +#if CEF_API_REMOVED(13301) + auto obj = CefApiVersionTestScopedLibraryChildChild::Create(); + obj->SetValue(val1); + obj->SetOtherValue(val2); + obj->SetOtherOtherValue(val3); + return obj; +#elif CEF_API_RANGE(13301, 13302) + return CefApiVersionTestScopedLibraryChildChildV1::Create(val1, val2, val3); +#elif CEF_API_ADDED(13302) + return CefApiVersionTestScopedLibraryChildChildV2::Create(val1, val2, val3); +#endif +} + +} // namespace + +// Test getting/setting library-side OwnPtr types. +TEST(ApiVersionTest, OwnPtrLibrary) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + CefOwnPtr test_obj = + CreateScopedLibrary(kTestVal); + EXPECT_TRUE(test_obj.get()); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + int retval = obj->SetOwnPtrLibrary(std::move(test_obj)); + EXPECT_EQ(kTestVal, retval); + EXPECT_FALSE(test_obj.get()); + + const int kTestVal2 = 30; + CefOwnPtr test_obj2 = + obj->GetOwnPtrLibrary(kTestVal2); + EXPECT_TRUE(test_obj2.get()); + EXPECT_EQ(kTestVal2, GetValue(test_obj2)); + int retval2 = obj->SetOwnPtrLibrary(std::move(test_obj2)); + EXPECT_EQ(kTestVal2, retval2); + EXPECT_FALSE(test_obj2.get()); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting inherited library-side OwnPtr types. +TEST(ApiVersionTest, OwnPtrLibraryInherit) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + const int kTestVal2 = 40; + auto test_obj = CreateScopedLibraryChild(kTestVal, kTestVal2); + EXPECT_TRUE(test_obj.get()); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, test_obj->GetOtherValue()); + int retval = obj->SetOwnPtrLibrary(std::move(test_obj)); + EXPECT_EQ(kTestVal, retval); + EXPECT_FALSE(test_obj.get()); + + test_obj = CreateScopedLibraryChild(kTestVal, kTestVal2); + EXPECT_TRUE(test_obj.get()); + EXPECT_EQ(kTestVal, obj->SetChildOwnPtrLibrary(std::move(test_obj))); + EXPECT_FALSE(test_obj.get()); + + test_obj = CreateScopedLibraryChild(kTestVal, kTestVal2); + EXPECT_TRUE(test_obj.get()); + CefOwnPtr test_obj_parent = + obj->SetChildOwnPtrLibraryAndReturnParent(std::move(test_obj)); + EXPECT_FALSE(test_obj.get()); + EXPECT_TRUE(test_obj_parent.get()); + EXPECT_EQ(kTestVal, GetValue(test_obj_parent)); + test_obj_parent.reset(nullptr); + + const int kTestVal3 = 100; + auto test_obj2 = + CreateScopedLibraryChildChild(kTestVal, kTestVal2, kTestVal3); + EXPECT_EQ(kTestVal, GetValue(test_obj2)); + EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue()); + EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue()); + int retval2 = obj->SetOwnPtrLibrary(std::move(test_obj2)); + EXPECT_EQ(kTestVal, retval2); + EXPECT_FALSE(test_obj2.get()); + + test_obj2 = CreateScopedLibraryChildChild(kTestVal, kTestVal2, kTestVal3); + EXPECT_EQ(kTestVal, obj->SetChildOwnPtrLibrary(std::move(test_obj2))); + EXPECT_FALSE(test_obj2.get()); + + test_obj2 = CreateScopedLibraryChildChild(kTestVal, kTestVal2, kTestVal3); + test_obj_parent = + obj->SetChildOwnPtrLibraryAndReturnParent(std::move(test_obj2)); + EXPECT_FALSE(test_obj2.get()); + EXPECT_TRUE(test_obj_parent.get()); + EXPECT_EQ(kTestVal, GetValue(test_obj_parent)); + test_obj_parent.reset(nullptr); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +namespace { + +class ApiVersionTestScopedClient : public CefApiVersionTestScopedClient { + public: + ApiVersionTestScopedClient(int val, TrackCallback* got_delete) + : val_(val), got_delete_(got_delete) {} + ~ApiVersionTestScopedClient() override { got_delete_->yes(); } + + int GetValueLegacy() override { return val_legacy_; } + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int GetValueExp() override { return val_exp_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetValue() override { return val_; } +#elif CEF_API_RANGE(13301, 13302) + int GetValueV1() override { return val_; } +#elif CEF_API_ADDED(13302) + int GetValueV2() override { return val_; } +#endif + + private: + const int val_; + int val_legacy_ = -1; +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int val_exp_ = -1; +#endif + + TrackCallback* got_delete_; + + DISALLOW_COPY_AND_ASSIGN(ApiVersionTestScopedClient); +}; + +#if CEF_API_REMOVED(13302) + +class ApiVersionTestScopedClientChild + : public CefApiVersionTestScopedClientChild { + public: + ApiVersionTestScopedClientChild(int val, + int other_val, + TrackCallback* got_delete) + : val_(val), other_val_(other_val), got_delete_(got_delete) {} + ~ApiVersionTestScopedClientChild() override { got_delete_->yes(); } + + int GetValueLegacy() override { return val_legacy_; } + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int GetValueExp() override { return val_exp_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetValue() override { return val_; } +#elif CEF_API_RANGE(13301, 13302) + int GetValueV1() override { return val_; } +#elif CEF_API_ADDED(13302) + int GetValueV2() override { return val_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetOtherValue() override { return other_val_; } +#else + int GetOtherValueV1() override { return other_val_; } +#endif + + private: + const int val_; + const int other_val_; + int val_legacy_ = -1; +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int val_exp_ = -1; +#endif + + TrackCallback* got_delete_; + + DISALLOW_COPY_AND_ASSIGN(ApiVersionTestScopedClientChild); +}; + +using ApiVersionTestScopedClientChildType = ApiVersionTestScopedClientChild; + +#else // CEF_API_ADDED(13302) + +class ApiVersionTestScopedClientChildV2 + : public CefApiVersionTestScopedClientChildV2 { + public: + ApiVersionTestScopedClientChildV2(int val, + int other_val, + TrackCallback* got_delete) + : val_(val), other_val_(other_val), got_delete_(got_delete) {} + ~ApiVersionTestScopedClientChildV2() override { got_delete_->yes(); } + + int GetValueLegacy() override { return val_legacy_; } + +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int GetValueExp() override { return val_exp_; } +#endif + +#if CEF_API_REMOVED(13301) + int GetValue() override { return val_; } +#elif CEF_API_RANGE(13301, 13302) + int GetValueV1() override { return val_; } +#elif CEF_API_ADDED(13302) + int GetValueV2() override { return val_; } +#endif + + int GetOtherValue() override { return other_val_; } + +#if CEF_API_ADDED(13303) + int GetAnotherValue() override { return another_val_; } +#endif + + private: + const int val_; + const int other_val_; +#if CEF_API_ADDED(13303) + int another_val_ = -1; +#endif + int val_legacy_ = -1; +#if CEF_API_ADDED(CEF_EXPERIMENTAL) + int val_exp_ = -1; +#endif + + TrackCallback* got_delete_; + + DISALLOW_COPY_AND_ASSIGN(ApiVersionTestScopedClientChildV2); +}; + +using ApiVersionTestScopedClientChildType = ApiVersionTestScopedClientChildV2; + +#endif // CEF_API_ADDED(13302) + +} // namespace + +// Test getting/setting client-side OwnPtr types. +TEST(ApiVersionTest, OwnPtrClient) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + TrackCallback got_delete; + + CefOwnPtr test_obj( + new ApiVersionTestScopedClient(kTestVal, &got_delete)); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal, obj->SetOwnPtrClient(std::move(test_obj))); + EXPECT_FALSE(test_obj.get()); + EXPECT_TRUE(got_delete); + + got_delete.reset(); + test_obj = + std::make_unique(kTestVal, &got_delete); + CefOwnPtr handler = + obj->SetOwnPtrClientAndReturn(std::move(test_obj)); + EXPECT_FALSE(test_obj.get()); + EXPECT_TRUE(handler.get()); + EXPECT_FALSE(got_delete); + EXPECT_EQ(kTestVal, GetValue(handler)); + handler.reset(nullptr); + EXPECT_TRUE(got_delete); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting inherited client-side OwnPtr types. +TEST(ApiVersionTest, OwnPtrClientInherit) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + const int kTestVal2 = 86; + TrackCallback got_delete; + + CefOwnPtr test_obj( + new ApiVersionTestScopedClientChildType(kTestVal, kTestVal2, + &got_delete)); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, GetOtherValue(test_obj)); + EXPECT_EQ(kTestVal, obj->SetOwnPtrClient(std::move(test_obj))); + EXPECT_FALSE(test_obj.get()); + EXPECT_TRUE(got_delete); + + got_delete.reset(); + test_obj = std::make_unique( + kTestVal, kTestVal2, &got_delete); + EXPECT_EQ(kTestVal, obj->SetChildOwnPtrClient(std::move(test_obj))); + EXPECT_FALSE(test_obj.get()); + EXPECT_TRUE(got_delete); + + got_delete.reset(); + test_obj = std::make_unique( + kTestVal, kTestVal2, &got_delete); + CefOwnPtr handler( + obj->SetChildOwnPtrClientAndReturnParent(std::move(test_obj))); + EXPECT_EQ(kTestVal, GetValue(handler)); + EXPECT_FALSE(test_obj.get()); + EXPECT_FALSE(got_delete); + handler.reset(nullptr); + EXPECT_TRUE(got_delete); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting library-side RawPtr types. +TEST(ApiVersionTest, RawPtrLibrary) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + auto test_obj = CreateScopedLibrary(kTestVal); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + int retval = obj->SetRawPtrLibrary(test_obj.get()); + EXPECT_EQ(kTestVal, retval); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + + const int kTestVal2 = 30; + auto test_obj2 = obj->GetOwnPtrLibrary(kTestVal2); + EXPECT_EQ(kTestVal2, GetValue(test_obj2)); + int retval2 = obj->SetRawPtrLibrary(test_obj2.get()); + EXPECT_EQ(kTestVal2, retval2); + EXPECT_EQ(kTestVal2, GetValue(test_obj2)); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting inherited library-side RawPtr types. +TEST(ApiVersionTest, RawPtrLibraryInherit) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + const int kTestVal2 = 40; + auto test_obj = CreateScopedLibraryChild(kTestVal, kTestVal2); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, test_obj->GetOtherValue()); + int retval = obj->SetRawPtrLibrary(test_obj.get()); + EXPECT_EQ(kTestVal, retval); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, test_obj->GetOtherValue()); + + EXPECT_EQ(kTestVal, obj->SetChildRawPtrLibrary(test_obj.get())); + + const int kTestVal3 = 100; + auto test_obj2 = + CreateScopedLibraryChildChild(kTestVal, kTestVal2, kTestVal3); + EXPECT_EQ(kTestVal, GetValue(test_obj2)); + EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue()); + EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue()); + int retval2 = obj->SetRawPtrLibrary(test_obj2.get()); + EXPECT_EQ(kTestVal, retval2); + EXPECT_EQ(kTestVal, GetValue(test_obj2)); + EXPECT_EQ(kTestVal2, test_obj2->GetOtherValue()); + EXPECT_EQ(kTestVal3, test_obj2->GetOtherOtherValue()); + + EXPECT_EQ(kTestVal, obj->SetChildRawPtrLibrary(test_obj2.get())); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting library-side RawPtr list types. +TEST(ApiVersionTest, RawPtrLibraryList) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kVal1 = 34; + const int kVal2 = 10; + + auto val1 = CreateScopedLibrary(kVal1); + auto val2 = CreateScopedLibraryChild(kVal2, 0); + + std::vector> list; + list.push_back(val1.get()); + list.push_back(val2.get()); + EXPECT_TRUE(obj->SetRawPtrLibraryList(list, kVal1, kVal2)); + list.clear(); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting client-side RawPtr types. +TEST(ApiVersionTest, RawPtrClient) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + TrackCallback got_delete; + + CefOwnPtr test_obj( + new ApiVersionTestScopedClient(kTestVal, &got_delete)); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal, obj->SetRawPtrClient(test_obj.get())); + EXPECT_FALSE(got_delete); + test_obj.reset(nullptr); + EXPECT_TRUE(got_delete); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting inherited client-side RawPtr types. +TEST(ApiVersionTest, RawPtrClientInherit) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kTestVal = 12; + const int kTestVal2 = 86; + TrackCallback got_delete; + + CefOwnPtr test_obj( + new ApiVersionTestScopedClientChildType(kTestVal, kTestVal2, + &got_delete)); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, GetOtherValue(test_obj)); + int retval = obj->SetRawPtrClient(test_obj.get()); + EXPECT_EQ(kTestVal, retval); + EXPECT_EQ(kTestVal, GetValue(test_obj)); + EXPECT_EQ(kTestVal2, GetOtherValue(test_obj)); + EXPECT_FALSE(got_delete); + + EXPECT_EQ(kTestVal, obj->SetChildRawPtrClient(test_obj.get())); + EXPECT_FALSE(got_delete); + test_obj.reset(nullptr); + EXPECT_TRUE(got_delete); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} + +// Test getting/setting client-side RawPtr list types. +TEST(ApiVersionTest, RawPtrClientList) { + CefRefPtr obj = CefApiVersionTest::Create(); + + const int kVal1 = 34; + const int kVal2 = 10; + TrackCallback got_delete1, got_delete2; + + CefOwnPtr val1( + new ApiVersionTestScopedClient(kVal1, &got_delete1)); + CefOwnPtr val2( + new ApiVersionTestScopedClientChildType(kVal2, 0, &got_delete2)); + + std::vector> list; + list.push_back(val1.get()); + list.push_back(val2.get()); + EXPECT_TRUE(obj->SetRawPtrClientList(list, kVal1, kVal2)); + list.clear(); + + EXPECT_FALSE(got_delete1); + val1.reset(nullptr); + EXPECT_TRUE(got_delete1); + + EXPECT_FALSE(got_delete2); + val2.reset(nullptr); + EXPECT_TRUE(got_delete2); + + // Only one reference to the object should exist. + EXPECT_TRUE(obj->HasOneRef()); +} diff --git a/tests/ceftests/pdf_viewer_unittest.cc b/tests/ceftests/pdf_viewer_unittest.cc index 442dc768f3..e0b9cf29f0 100644 --- a/tests/ceftests/pdf_viewer_unittest.cc +++ b/tests/ceftests/pdf_viewer_unittest.cc @@ -3,7 +3,6 @@ // can be found in the LICENSE file. #include "include/base/cef_callback.h" -#include "include/cef_pack_resources.h" #include "include/cef_request_context_handler.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_stream_resource_handler.h" diff --git a/tests/ceftests/run_all_unittests.cc b/tests/ceftests/run_all_unittests.cc index c20c1bfd80..26146d0792 100644 --- a/tests/ceftests/run_all_unittests.cc +++ b/tests/ceftests/run_all_unittests.cc @@ -18,6 +18,7 @@ #endif #include "include/base/cef_callback.h" +#include "include/cef_api_hash.h" #include "include/cef_app.h" #include "include/cef_task.h" #include "include/cef_thread.h" @@ -136,6 +137,10 @@ class ScopedPlatformSetup final { int main(int argc, char* argv[]) { int exit_code; +#if CEF_API_VERSION != CEF_EXPERIMENTAL + printf("Running with configured CEF API version %d\n", CEF_API_VERSION); +#endif + #if defined(OS_WIN) && defined(ARCH_CPU_32_BITS) // Run the main thread on 32-bit Windows using a fiber with the preferred 4MiB // stack size. This function must be called at the top of the executable entry diff --git a/tests/ceftests/version_unittest.cc b/tests/ceftests/version_unittest.cc index 54776e5cfd..50621e17ce 100644 --- a/tests/ceftests/version_unittest.cc +++ b/tests/ceftests/version_unittest.cc @@ -3,7 +3,7 @@ // can be found in the LICENSE file. #include "include/cef_api_hash.h" -#include "include/cef_version.h" +#include "include/cef_version_info.h" #include "tests/gtest/include/gtest/gtest.h" TEST(VersionTest, VersionInfo) { @@ -18,7 +18,7 @@ TEST(VersionTest, VersionInfo) { } TEST(VersionTest, ApiHash) { - EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(0)); - EXPECT_STREQ(CEF_API_HASH_UNIVERSAL, cef_api_hash(1)); - EXPECT_STREQ(CEF_COMMIT_HASH, cef_api_hash(2)); + EXPECT_STREQ(CEF_API_HASH_PLATFORM, cef_api_hash(CEF_API_VERSION, 0)); + EXPECT_STREQ(CEF_API_HASH_UNIVERSAL, cef_api_hash(CEF_API_VERSION, 1)); + EXPECT_STREQ(CEF_COMMIT_HASH, cef_api_hash(CEF_API_VERSION, 2)); } diff --git a/tests/ceftests/views/scroll_view_unittest.cc b/tests/ceftests/views/scroll_view_unittest.cc index 72849fa217..dc337ec7c8 100644 --- a/tests/ceftests/views/scroll_view_unittest.cc +++ b/tests/ceftests/views/scroll_view_unittest.cc @@ -3,7 +3,6 @@ // can be found in the LICENSE file. #include "include/base/cef_callback.h" -#include "include/cef_pack_strings.h" #include "include/views/cef_panel.h" #include "include/views/cef_panel_delegate.h" #include "include/views/cef_scroll_view.h" diff --git a/tests/ceftests/views/textfield_unittest.cc b/tests/ceftests/views/textfield_unittest.cc index 57de034f35..d2405fe9e0 100644 --- a/tests/ceftests/views/textfield_unittest.cc +++ b/tests/ceftests/views/textfield_unittest.cc @@ -3,7 +3,6 @@ // can be found in the LICENSE file. #include "include/base/cef_callback.h" -#include "include/cef_pack_strings.h" #include "include/views/cef_textfield.h" #include "include/views/cef_textfield_delegate.h" #include "include/wrapper/cef_closure_task.h" diff --git a/tools/cef_api_hash.py b/tools/cef_api_hash.py index b036fb961e..949266feee 100644 --- a/tools/cef_api_hash.py +++ b/tools/cef_api_hash.py @@ -4,16 +4,16 @@ from __future__ import absolute_import from __future__ import print_function +from clang_util import clang_eval from file_util import * +import hashlib +import itertools import os import re -import shutil import string import sys -import textwrap import time -import itertools -import hashlib +from version_util import EXP_VERSION # Determines string type for python 2 and python 3. if sys.version_info[0] == 3: @@ -22,93 +22,172 @@ string_type = basestring +def _run_clang_eval(filename, content, api_version, added_defines, verbose): + # Add a tag so we know where the header-specific output begins. + tag = 'int begin_includes_tag;\n' + find = '#ifdef __cplusplus\nextern "C" {' + pos = content.find(find) + assert pos > 0, filename + content = content[0:pos] + tag + content[pos:] + + defines = [ + # Makes sure CEF_EXPORT is defined. + 'USING_CEF_SHARED', + + # Avoid include of generated headers. + 'GENERATING_CEF_API_HASH', + ] + + if filename.find('test/') >= 0: + # Avoids errors parsing test includes. + defines.append('UNIT_TEST') + + # Not the experimental version. + api_version = int(api_version) + if api_version != EXP_VERSION: + # Specify the exact version. + defines.append('CEF_API_VERSION=%d' % api_version) + + if not added_defines is None: + defines.extend(added_defines) + + includes = [ + # Includes relative to the 'src/cef' directory. + '.', + # Includes relative to the 'src' directory. + '..', + ] + + result = clang_eval( + filename, + content, + defines=defines, + includes=includes, + as_cpp=False, + verbose=verbose) + if result is None: + return None + + pos = result.find(tag) + assert pos > 0, filename + result = result[pos + len(tag):] + + replacements = [ + # Undo substitutions from cef_export.h + ['__declspec(dllimport)', 'CEF_EXPORT'], + ['__attribute__((visibility("default")))', 'CEF_EXPORT'], + ['__stdcall', ''], + ] + + for find, replace in replacements: + result = result.replace(find, replace) + + return result + + class cef_api_hash: """ CEF API hash calculator """ - def __init__(self, headerdir, debugdir=None, verbose=False): + def __init__(self, headerdir, verbose=False): if headerdir is None or len(headerdir) == 0: raise AssertionError("headerdir is not specified") self.__headerdir = headerdir - self.__debugdir = debugdir self.__verbose = verbose - self.__debug_enabled = not (self.__debugdir is - None) and len(self.__debugdir) > 0 self.platforms = ["windows", "mac", "linux"] + cef_dir = os.path.abspath(os.path.join(self.__headerdir, os.pardir)) + + # Read the variables list from the autogenerated cef_paths.gypi file. + cef_paths = eval_file(os.path.join(cef_dir, 'cef_paths.gypi')) + cef_paths = cef_paths['variables'] + + # Read the variables list from the manually edited cef_paths2.gypi file. + cef_paths2 = eval_file(os.path.join(cef_dir, 'cef_paths2.gypi')) + cef_paths2 = cef_paths2['variables'] + + # Excluded files (paths relative to the include/ directory). + excluded_files = [] + + # List of platform-specific C API include/ files. self.platform_files = { - # List of includes_win_capi from cef_paths2.gypi. - "windows": [ - "internal/cef_app_win.h", - "internal/cef_types_win.h", - ], - # List of includes_mac_capi from cef_paths2.gypi. - "mac": [ - "internal/cef_types_mac.h", - ], - # List of includes_linux_capi from cef_paths2.gypi. - "linux": [ - "internal/cef_types_linux.h", - ] + "windows": + self.__get_filenames(cef_dir, cef_paths2['includes_win_capi'], + excluded_files), + "mac": + self.__get_filenames(cef_dir, cef_paths2['includes_mac_capi'], + excluded_files), + "linux": + self.__get_filenames(cef_dir, cef_paths2['includes_linux_capi'], + excluded_files) } - self.included_files = [] - - # List of include/ and include/internal/ files from cef_paths2.gypi. - self.excluded_files = [ - # includes_common - "cef_api_hash.h", - "cef_base.h", - "cef_version.h", - "internal/cef_export.h", - "internal/cef_ptr.h", - "internal/cef_string_wrappers.h", - "internal/cef_time_wrappers.h", - "internal/cef_types_wrappers.h", - # includes_win - "cef_sandbox_win.h", - "internal/cef_win.h", - # includes_mac - "cef_application_mac.h", - "cef_sandbox_mac.h", - "internal/cef_mac.h", - # includes_linux - "internal/cef_linux.h", - ] + # List of all C API include/ files. + paths = cef_paths2['includes_capi'] + cef_paths2['includes_common_capi'] + \ + cef_paths2['includes_linux_capi'] + cef_paths2['includes_mac_capi'] + \ + cef_paths2['includes_win_capi'] + cef_paths['autogen_capi_includes'] + self.filenames = self.__get_filenames(cef_dir, paths, excluded_files) - def calculate(self): - filenames = [ - filename for filename in self.__get_filenames() - if not filename in self.excluded_files - ] + self.filecontents = {} + self.filecontentobjs = {} - objects = [] - for filename in filenames: + # Cache values that will not change between calls to calculate(). + for filename in self.filenames: if self.__verbose: print("Processing " + filename + "...") + + assert not filename in self.filecontents, filename + assert not filename in self.filecontentobjs, filename + content = read_file(os.path.join(self.__headerdir, filename), True) - platforms = list([ - p for p in self.platforms if self.__is_platform_filename(filename, p) - ]) + content_objects = None # Parse cef_string.h happens in special case: grab only defined CEF_STRING_TYPE_xxx declaration - content_objects = None if filename == "internal/cef_string.h": content_objects = self.__parse_string_type(content) + elif content.find('#if CEF_API') >= 0: + # Needs to be passed to clang with version-specific defines. + self.filecontents[filename] = content else: content_objects = self.__parse_objects(content) - for o in content_objects: - o["text"] = self.__prepare_text(o["text"]) - o["platforms"] = platforms - o["filename"] = filename - objects.append(o) + if not content_objects is None: + self.__prepare_objects(filename, content_objects) + self.filecontentobjs[filename] = content_objects + + def calculate(self, api_version, debug_dir=None, added_defines=None): + debug_enabled = not (debug_dir is None) and len(debug_dir) > 0 + + objects = [] + for filename in self.filenames: + if self.__verbose: + print("Processing " + filename + "...") + + content = self.filecontents.get(filename, None) + if not content is None: + assert content.find('#if CEF_API') >= 0, filename + content = _run_clang_eval(filename, content, api_version, added_defines, + self.__verbose) + if content is None: + sys.stderr.write( + 'ERROR: Failed to compute API hash for %s\n' % filename) + return False + if debug_enabled: + self.__write_debug_file( + debug_dir, 'clang-' + filename.replace('/', '-'), content) + content_objects = self.__parse_objects(content) + self.__prepare_objects(filename, content_objects) + else: + content_objects = self.filecontentobjs.get(filename, None) + + assert not content_objects is None, filename + objects.extend(content_objects) # objects will be sorted including filename, to make stable universal hashes objects = sorted(objects, key=lambda o: o["name"] + "@" + o["filename"]) - if self.__debug_enabled: + if debug_enabled: namelen = max([len(o["name"]) for o in objects]) filenamelen = max([len(o["filename"]) for o in objects]) dumpsig = [] @@ -116,14 +195,14 @@ def calculate(self): dumpsig.append( format(o["name"], str(namelen) + "s") + "|" + format( o["filename"], "" + str(filenamelen) + "s") + "|" + o["text"]) - self.__write_debug_file("objects.txt", dumpsig) + self.__write_debug_file(debug_dir, "objects.txt", dumpsig) revisions = {} for platform in itertools.chain(["universal"], self.platforms): sig = self.__get_final_sig(objects, platform) - if self.__debug_enabled: - self.__write_debug_file(platform + ".sig", sig) + if debug_enabled: + self.__write_debug_file(debug_dir, platform + ".sig", sig) revstr = hashlib.sha1(sig.encode('utf-8')).hexdigest() revisions[platform] = revstr @@ -152,7 +231,8 @@ def __parse_objects(self, content): # enums for m in re.finditer( - r"\ntypedef\s+?enum\s+?\{.*?\}\s+?(\w+)\s*?;", content, flags=re.DOTALL): + r"\ntypedef\s+?enum\s+?\{.*?\}\s+?(\w+)\s*?;", content, + flags=re.DOTALL): object = {"name": m.group(1), "text": m.group(0).strip()} objects.append(object) @@ -163,11 +243,20 @@ def __parse_objects(self, content): return objects + def __prepare_objects(self, filename, objects): + platforms = list( + [p for p in self.platforms if self.__is_platform_filename(filename, p)]) + for o in objects: + o["text"] = self.__prepare_text(o["text"]) + o["platforms"] = platforms + o["filename"] = filename + def __parse_string_type(self, content): """ Grab defined CEF_STRING_TYPE_xxx """ objects = [] for m in re.finditer( - r"\n\s*?#\s*?define\s+?(CEF_STRING_TYPE_\w+)\s+?.*?\n", content, + r"\n\s*?#\s*?define\s+?(CEF_STRING_TYPE_\w+)\s+?.*?\n", + content, flags=0): object = { "name": m.group(1), @@ -191,35 +280,20 @@ def __get_final_sig(self, objects, platform): return "\n".join(sig) - def __get_filenames(self): + def __get_filenames(self, cef_dir, paths, excluded_files): """ Returns file names to be processed, relative to headerdir """ - headers = [ - os.path.join(self.__headerdir, filename) - for filename in self.included_files + filenames = [ + os.path.relpath(os.path.join(cef_dir, filename), + self.__headerdir).replace('\\', '/').lower() + for filename in paths ] - capi_dir = os.path.join(self.__headerdir, "capi") - headers = itertools.chain(headers, get_files(os.path.join(capi_dir, "*.h"))) - - # Also include capi sub-directories. - for root, dirs, files in os.walk(capi_dir): - for name in dirs: - headers = itertools.chain(headers, - get_files(os.path.join(root, name, "*.h"))) + if len(excluded_files) == 0: + return filenames - headers = itertools.chain( - headers, get_files(os.path.join(self.__headerdir, "internal", "*.h"))) - - for v in self.platform_files.values(): - headers = itertools.chain(headers, - [os.path.join(self.__headerdir, f) for f in v]) - - normalized = [ - os.path.relpath(filename, self.__headerdir) for filename in headers + return [ + filename for filename in filenames if not filename in excluded_files ] - normalized = [f.replace('\\', '/').lower() for f in normalized] - - return list(set(normalized)) def __is_platform_filename(self, filename, platform): if platform == "universal": @@ -235,9 +309,9 @@ def __is_platform_filename(self, filename, platform): listed = True return not listed - def __write_debug_file(self, filename, content): - make_dir(self.__debugdir) - outfile = os.path.join(self.__debugdir, filename) + def __write_debug_file(self, debug_dir, filename, content): + make_dir(debug_dir) + outfile = os.path.join(debug_dir, filename) dir = os.path.dirname(outfile) make_dir(dir) if not isinstance(content, string_type): @@ -282,14 +356,16 @@ def __write_debug_file(self, filename, content): c_start_time = time.time() calc = cef_api_hash(options.cppheaderdir, options.debugdir, options.verbose) - revisions = calc.calculate() + revisions = calc.calculate(api_version=EXP_VERSION) c_completed_in = time.time() - c_start_time - print("{") - for k in sorted(revisions.keys()): - print(format("\"" + k + "\"", ">12s") + ": \"" + revisions[k] + "\"") - print("}") + if bool(revisions): + print("{") + for k in sorted(revisions.keys()): + print(format("\"" + k + "\"", ">12s") + ": \"" + revisions[k] + "\"") + print("}") + # print # print 'Completed in: ' + str(c_completed_in) # print diff --git a/tools/cef_parser.py b/tools/cef_parser.py index 094ee5b3ea..93990450fb 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -3,6 +3,7 @@ # can be found in the LICENSE file. from __future__ import absolute_import +import bisect from date_util import * from file_util import * import os @@ -12,11 +13,26 @@ import sys import textwrap import time +from version_util import version_as_numeric, version_as_variable + +_NOTIFY_CONTEXT = None +_NOTIFY_CONTEXT_LAST = None + + +def set_notify_context(context): + global _NOTIFY_CONTEXT + _NOTIFY_CONTEXT = context def notify(msg): """ Display a message. """ - sys.stdout.write(' NOTE: ' + msg + '\n') + global _NOTIFY_CONTEXT_LAST + + if not _NOTIFY_CONTEXT is None and _NOTIFY_CONTEXT != _NOTIFY_CONTEXT_LAST: + print('In %s:' % _NOTIFY_CONTEXT) + _NOTIFY_CONTEXT_LAST = _NOTIFY_CONTEXT + + print(' NOTE: ' + msg) def wrap_text(text, indent='', maxchars=80, listitem=False): @@ -44,25 +60,28 @@ def is_base_class(clsname): return clsname == 'CefBaseRefCounted' or clsname == 'CefBaseScoped' -def get_capi_file_name(cppname): +def get_capi_file_name(cppname, versions=False): """ Convert a C++ header file name to a C API header file name. """ - return cppname[:-2] + '_capi.h' + return cppname[:-2] + ('_capi_versions.h' if versions else '_capi.h') -def get_capi_name(cppname, isclassname, prefix=None): +def get_capi_name(cppname, isclassname, prefix=None, version=None): """ Convert a C++ CamelCaps name to a C API underscore name. """ result = '' lastchr = '' for chr in cppname: # add an underscore if the current character is an upper case letter - # and the last character was a lower case letter - if len(result) > 0 and not chr.isdigit() \ + # and the last character was a lower case letter or number. + if len(result) > 0 and chr.isalpha() \ and chr.upper() == chr \ - and not lastchr.upper() == lastchr: + and lastchr.isalnum() and lastchr.lower() == lastchr: result += '_' result += chr.lower() lastchr = chr + if isclassname and not version is None: + result += '_%d' % version + if isclassname: result += '_t' @@ -259,7 +278,10 @@ def format_translation_changes(old, new): return result -def format_translation_includes(header, body): +def format_translation_includes(header, + body, + with_versions=False, + other_includes=None): """ Return the necessary list of includes based on the contents of the body. """ @@ -269,47 +291,187 @@ def format_translation_includes(header, body): if body.find('std::min') > 0 or body.find('std::max') > 0: result += '#include \n' - if body.find('cef_api_hash(') > 0: - result += '#include "include/cef_api_hash.h"\n' + paths = set() + + if body.find('cef_api_hash(') > 0 or body.find('cef_api_version(') > 0: + paths.add('include/cef_api_hash.h') if body.find('template_util::has_valid_size(') > 0: - result += '#include "libcef_dll/template_util.h"\n' - - # identify what CppToC classes are being used - p = re.compile(r'([A-Za-z0-9_]{1,})CppToC') - list = sorted(set(p.findall(body))) - for item in list: - directory = '' - if not is_base_class(item): - cls = header.get_class(item) - dir = cls.get_file_directory() - if not dir is None: - directory = dir + '/' - result += '#include "libcef_dll/cpptoc/'+directory+ \ - get_capi_name(item[3:], False)+'_cpptoc.h"\n' - - # identify what CToCpp classes are being used - p = re.compile(r'([A-Za-z0-9_]{1,})CToCpp') - list = sorted(set(p.findall(body))) - for item in list: - directory = '' - if not is_base_class(item): - cls = header.get_class(item) - dir = cls.get_file_directory() - if not dir is None: - directory = dir + '/' - result += '#include "libcef_dll/ctocpp/'+directory+ \ - get_capi_name(item[3:], False)+'_ctocpp.h"\n' + paths.add('libcef_dll/template_util.h') + + search = ((True, True, r'([A-Za-z0-9_]{1,})_[0-9]{1,}_CppToC'), + (True, False, r'([A-Za-z0-9_]{1,})CppToC'), + (False, True, r'([A-Za-z0-9_]{1,})_[0-9]{1,}_CToCpp'), + (False, False, r'([A-Za-z0-9_]{1,})CToCpp')) + for cpptoc, versioned, regex in search: + # identify what classes are being used + p = re.compile(regex) + items = set(p.findall(body)) + for item in items: + if item == 'Cef': + continue + if not versioned and item[-1] == '_': + # skip versioned names that are picked up by the unversioned regex + continue + directory = '' + if not is_base_class(item): + cls = header.get_class(item) + if cls is None: + raise Exception('Class does not exist: ' + item) + dir = cls.get_file_directory() + if not dir is None: + directory = dir + '/' + type = 'cpptoc' if cpptoc else 'ctocpp' + paths.add('libcef_dll/' + type + '/'+directory+ \ + get_capi_name(item[3:], False)+'_' + type + '.h') if body.find('shutdown_checker') > 0: - result += '#include "libcef_dll/shutdown_checker.h"\n' + paths.add('libcef_dll/shutdown_checker.h') if body.find('transfer_') > 0: - result += '#include "libcef_dll/transfer_util.h"\n' + paths.add('libcef_dll/transfer_util.h') + + if not other_includes is None: + paths.update(other_includes) + + if len(paths) > 0: + if len(result) > 0: + result += '\n' + + paths = sorted(list(paths)) + result += '\n'.join(['#include "%s"' % p for p in paths]) + '\n' + + return result + + +def format_notreached(library_side, msg, default_retval='', indent=' '): + if library_side: + return 'NOTREACHED() << __func__ << ' + msg + ';' + return 'CHECK(false) << __func__ << ' + msg + ';\n' + \ + indent + 'return%s;' % ((' ' + default_retval) if len(default_retval) > 0 else '') + + +def _has_version_added(attribs): + return 'added' in attribs + + +def _has_version_removed(attribs): + return 'removed' in attribs + + +def _has_version(attribs): + return _has_version_added(attribs) or _has_version_removed(attribs) + + +def get_version_check(attribs): + assert _has_version(attribs) + + added = attribs.get('added', None) + if not added is None: + added = version_as_variable(added) + removed = attribs.get('removed', None) + if not removed is None: + removed = version_as_variable(removed) + + if not added is None and not removed is None: + return 'CEF_API_RANGE(%s, %s)' % (added, removed) + elif not added is None: + return 'CEF_API_ADDED(%s)' % added + return 'CEF_API_REMOVED(%s)' % removed + +def _get_version_attrib(attribs, key): + value = attribs.get(key, None) + if not value is None: + return version_as_numeric(value) + + # Unversioned is always the first value. + return 0 + + +def _get_version_added(attribs): + """ Returns a numeric 'added' value used for sorting purposes. """ + return _get_version_attrib(attribs, 'added') + + +def _get_version_removed(attribs): + """ Returns a numeric 'removed' value used for sorting purposes. """ + return _get_version_attrib(attribs, 'removed') + + +def get_version_surround(obj, long=False): + """ Returns (pre,post) strings for a version check. """ + version_check = obj.get_version_check() if obj.has_version() else None + + # Don't duplicate the surrounding class version check for a virtual method. + if not version_check is None and \ + isinstance(obj, obj_function) and isinstance(obj.parent, obj_class) and \ + obj.parent.has_version() and obj.parent.get_version_check() == version_check: + version_check = None + + if not version_check is None: + return ('#if %s\n' % version_check, ('#endif // %s\n' % version_check) + if long else '#endif\n') + return ('', '') + + +def get_clsname(cls, version): + name = cls.get_name() + + if not version is None: + # Select the appropriate version for this class. + closest_version = cls.get_closest_version(version) + if closest_version is None: + raise Exception('Cannot find version <= %d for %s' % (version, name)) + return '%s_%d_' % (name, closest_version) + + return name + + +def _version_order_funcs(funcs, max_version=None): + """ Applies version-based ordering to a list of funcs. """ + versions = {0: []} + + for func in funcs: + if func.has_version(): + added = func.get_version_added() + if not added in versions: + versions[added] = [func] + else: + versions[added].append(func) + else: + # Unversioned funcs. + versions[0].append(func) + + result = [] + for version in sorted(versions.keys()): + if not max_version is None and version > max_version: + break + result.extend(versions[version]) return result +def _get_all_versions(funcs): + # Using a set to ensure uniqueness. + versions = set({0}) + + for func in funcs: + if func.has_version(): + versions.add(func.get_version_added()) + versions.add(func.get_version_removed()) + + return versions + + +def _find_closest_not_greater(lst, target): + assert isinstance(lst, list), lst + assert isinstance(target, int), target + idx = bisect.bisect_right(lst, target) - 1 + if idx < 0: + return None + return lst[idx] + + def str_to_dict(str): """ Convert a string to a dictionary. If the same key has multiple values the values will be stored in a list. """ @@ -357,6 +519,13 @@ def dict_to_str(dict): return ','.join(str) +# Attribute keys allowed in CEF metadata comments. +COMMON_ATTRIB_KEYS = ('added', 'removed') +CLASS_ATTRIB_KEYS = COMMON_ATTRIB_KEYS + ('no_debugct_check', 'source') +FUNCTION_ATTRIB_KEYS = COMMON_ATTRIB_KEYS + ('api_hash_check', 'capi_name', + 'count_func', 'default_retval', + 'index_param', 'optional_param') + # regex for matching comment-formatted attributes _cre_attrib = r'/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/' # regex for matching class and function names @@ -747,13 +916,18 @@ def get_classes(self, filename=None): res.append(cls) return res - def get_class(self, classname, defined_structs=None): + def get_class(self, classname): """ Return the specified class or None if not found. """ for cls in self.classes: if cls.get_name() == classname: return cls - elif not defined_structs is None: - defined_structs.append(cls.get_capi_name()) + return None + + def get_capi_class(self, classname): + """ Return the specified class or None if not found. """ + for cls in self.classes: + if cls.get_capi_name() == classname: + return cls return None def get_class_names(self): @@ -856,6 +1030,8 @@ def __init__(self, parent, filename, attrib, name, parent_name, body, comment, self.includes = includes self.forward_declares = forward_declares + self._validate_attribs() + # extract typedefs p = re.compile( r'\n' + _cre_space + r'typedef' + _cre_space + _cre_typedef + r';', @@ -895,13 +1071,19 @@ def __init__(self, parent, filename, attrib, name, parent_name, body, comment, # build the virtual function objects self.virtualfuncs = [] + self.has_versioned_funcs = False for attrib, retval, argval, vfmod in list: comment = get_comment(body, retval + '(' + argval + ')') validate_comment(filename, retval, comment) + if not self.has_versioned_funcs and _has_version(attrib): + self.has_versioned_funcs = True self.virtualfuncs.append( obj_function_virtual(self, attrib, retval, argval, comment, vfmod.strip())) + self.virtualfuncs_ordered = None + self.allversions = None + def __repr__(self): result = '/* ' + dict_to_str( self.attribs) + ' */ class ' + self.name + "\n{" @@ -930,15 +1112,21 @@ def __repr__(self): result += "\n};\n" return result + def _validate_attribs(self): + for key in self.attribs.keys(): + if not key in CLASS_ATTRIB_KEYS: + raise Exception('Invalid attribute key \"%s\" for class %s' % + (key, self.get_name())) + def get_file_name(self): """ Return the C++ header file name. Includes the directory component, if any. """ return self.filename - def get_capi_file_name(self): + def get_capi_file_name(self, versions=False): """ Return the CAPI header file name. Includes the directory component, if any. """ - return get_capi_file_name(self.filename) + return get_capi_file_name(self.filename, versions) def get_file_directory(self): """ Return the file directory component, if any. """ @@ -947,21 +1135,44 @@ def get_file_directory(self): return self.filename[:pos] return None - def get_name(self): + def get_name(self, version=None): """ Return the class name. """ + if not version is None: + # Select the appropriate version for this class. + closest_version = self.get_closest_version(version) + if closest_version is None: + raise Exception('Cannot find version <= %d for %s' % (version, + self.name)) + return '%s_%d_' % (self.name, closest_version) return self.name - def get_capi_name(self): + def get_capi_name(self, version=None, first_version=False): """ Return the CAPI structure name for this class. """ - return get_capi_name(self.name, True) + # Select the appropriate version for this class. + if first_version: + version = self.get_first_version() + elif not version is None: + closest_version = self.get_closest_version(version) + if closest_version is None: + raise Exception('Cannot find version <= %d for %s' % (version, + self.name)) + version = closest_version + return get_capi_name(self.name, True, version=version) def get_parent_name(self): """ Return the parent class name. """ return self.parent_name - def get_parent_capi_name(self): + def get_parent_capi_name(self, version=None): """ Return the CAPI structure name for the parent class. """ - return get_capi_name(self.parent_name, True) + if not version is None: + # Select the appropriate version for the parent class. + if is_base_class(self.parent_name): + version = None + else: + parent_cls = self.parent.get_class(self.parent_name) + version = parent_cls.get_closest_version(version) + return get_capi_name(self.parent_name, True, version=version) def has_parent(self, parent_name): """ Returns true if this class has the specified class anywhere in its @@ -1043,8 +1254,17 @@ def get_static_funcs(self): """ Return the array of static function objects. """ return self.staticfuncs - def get_virtual_funcs(self): + def get_virtual_funcs(self, version_order=False, version=None): """ Return the array of virtual function objects. """ + if version_order and self.has_versioned_funcs: + if version is None: + # Cache the ordering result for future use. + if self.virtualfuncs_ordered is None: + self.virtualfuncs_ordered = _version_order_funcs(self.virtualfuncs) + return self.virtualfuncs_ordered + + # Need to order each time to apply the max version. + return _version_order_funcs(self.virtualfuncs, version) return self.virtualfuncs def get_types(self, list): @@ -1078,6 +1298,75 @@ def is_client_side(self): """ Returns true if the class is implemented by the client. """ return self.attribs['source'] == 'client' + def has_version(self): + """ Returns true if the class has an associated version. """ + return _has_version(self.attribs) + + def has_version_added(self): + """ Returns true if the class has an associated 'added' version. """ + return _has_version_added(self.attribs) + + def get_version_added(self): + """ Returns the associated 'added' version. """ + return _get_version_added(self.attribs) + + def has_version_removed(self): + """ Returns true if the class has an associated 'removed' version. """ + return _has_version_removed(self.attribs) + + def get_version_removed(self): + """ Returns the associated 'removed' version. """ + return _get_version_removed(self.attribs) + + def removed_at_version(self, version): + """ Returns true if this class is removed at the specified version. """ + return self.has_version_removed() and self.get_version_removed() <= version + + def exists_at_version(self, version): + """ Returns true if this class exists at the specified version. """ + if self.has_version_added() and self.get_version_added() > version: + return False + return not self.removed_at_version(version) + + def get_version_check(self): + """ Returns the #if check for the associated version. """ + return get_version_check(self.attribs) + + def get_all_versions(self): + """ Returns all distinct versions of this class. """ + if not self.allversions is None: + return self.allversions + + # Using a set to ensure uniqueness. + versions = set() + + # Versions from class inheritance. + if not is_base_class(self.parent_name): + versions.update( + self.parent.get_class(self.parent_name).get_all_versions()) + + # Versions from virtual methods. + versions.update(_get_all_versions(self.virtualfuncs)) + + versions = list(versions) + + # Clamp to class versions, if specified. + if self.has_version_added(): + versions = [x for x in versions if x >= self.get_version_added()] + if self.has_version_removed(): + versions = [x for x in versions if x < self.get_version_removed()] + + self.allversions = sorted(versions) + return self.allversions + + def get_first_version(self): + """ Returns the first version. """ + return self.get_all_versions()[0] + + def get_closest_version(self, version): + """ Returns the closest version to |version| that is not greater, or None. """ + return _find_closest_not_greater(self.get_all_versions(), version) + class obj_typedef: """ Class representing a typedef statement. """ @@ -1099,9 +1388,9 @@ def get_file_name(self): """ Return the C++ header file name. """ return self.filename - def get_capi_file_name(self): + def get_capi_file_name(self, versions=False): """ Return the CAPI header file name. """ - return get_capi_file_name(self.filename) + return get_capi_file_name(self.filename, versions) def get_alias(self): """ Return the alias. """ @@ -1131,6 +1420,8 @@ def __init__(self, parent, filename, attrib, retval, argval, comment): self.name = self.retval.remove_name() self.comment = comment + self._validate_attribs() + # build the argument objects self.arguments = [] arglist = argval.split(',') @@ -1163,13 +1454,19 @@ def __init__(self, parent, filename, attrib, retval, argval, comment): def __repr__(self): return '/* ' + dict_to_str(self.attribs) + ' */ ' + self.get_cpp_proto() + def _validate_attribs(self): + for key in self.attribs.keys(): + if not key in FUNCTION_ATTRIB_KEYS: + raise Exception('Invalid attribute key \"%s\" for %s' % + (key, self.get_qualified_name())) + def get_file_name(self): """ Return the C++ header file name. """ return self.filename - def get_capi_file_name(self): + def get_capi_file_name(self, versions=False): """ Return the CAPI header file name. """ - return get_capi_file_name(self.filename) + return get_capi_file_name(self.filename, versions) def get_name(self): """ Return the function name. """ @@ -1186,9 +1483,7 @@ def get_qualified_name(self): def get_capi_name(self, prefix=None): """ Return the CAPI function name. """ - if 'capi_name' in self.attribs: - return self.attribs['capi_name'] - return get_capi_name(self.name, False, prefix) + return get_capi_name(self.get_attrib('capi_name', self.name), False, prefix) def get_comment(self): """ Return the function comment as an array of lines. """ @@ -1202,7 +1497,7 @@ def has_attrib(self, name): """ Return true if the specified attribute exists. """ return name in self.attribs - def get_attrib(self, name): + def get_attrib(self, name, default=None): """ Return the first or only value for specified attribute. """ if name in self.attribs: if isinstance(self.attribs[name], list): @@ -1211,7 +1506,7 @@ def get_attrib(self, name): else: # the value is a string return self.attribs[name] - return None + return default def get_attrib_list(self, name): """ Return all values for specified attribute as a list. """ @@ -1237,10 +1532,15 @@ def get_types(self, list): for cls in self.arguments: cls.get_types(list) - def get_capi_parts(self, defined_structs=[], isimpl=False, prefix=None): + def get_capi_parts(self, + defined_structs=[], + isimpl=False, + prefix=None, + version=None, + version_finder=None): """ Return the parts of the C API function definition. """ retval = '' - dict = self.retval.get_type().get_capi(defined_structs) + dict = self.retval.get_type().get_capi(defined_structs, version_finder) if dict['format'] == 'single': retval = dict['value'] @@ -1249,7 +1549,7 @@ def get_capi_parts(self, defined_structs=[], isimpl=False, prefix=None): if isinstance(self, obj_function_virtual): # virtual functions get themselves as the first argument - str = 'struct _' + self.parent.get_capi_name() + '* self' + str = 'struct _' + self.parent.get_capi_name(version=version) + '* self' if isinstance(self, obj_function_virtual) and self.is_const(): # const virtual functions get const self pointers str = 'const ' + str @@ -1260,7 +1560,7 @@ def get_capi_parts(self, defined_structs=[], isimpl=False, prefix=None): if len(self.arguments) > 0: for cls in self.arguments: type = cls.get_type() - dict = type.get_capi(defined_structs) + dict = type.get_capi(defined_structs, version_finder) if dict['format'] == 'single': args.append(dict['value']) elif dict['format'] == 'multi-arg': @@ -1276,9 +1576,15 @@ def get_capi_parts(self, defined_structs=[], isimpl=False, prefix=None): return {'retval': retval, 'name': name, 'args': args} - def get_capi_proto(self, defined_structs=[], isimpl=False, prefix=None): + def get_capi_proto(self, + defined_structs=[], + isimpl=False, + prefix=None, + version=None, + version_finder=None): """ Return the prototype of the C API function. """ - parts = self.get_capi_parts(defined_structs, isimpl, prefix) + parts = self.get_capi_parts(defined_structs, isimpl, prefix, version, + version_finder) result = parts['retval']+' '+parts['name']+ \ '('+', '.join(parts['args'])+')' return result @@ -1336,6 +1642,14 @@ def is_same_side(self, other_class_name): return other_is_library_side == this_is_library_side + def has_version(self): + """ Returns true if the class has an associated version. """ + return _has_version(self.attribs) + + def get_version_check(self): + """ Returns the #if check for the associated version. """ + return get_version_check(self.attribs) + class obj_function_static(obj_function): """ Class representing a static function. """ @@ -1377,6 +1691,34 @@ def is_const(self): """ Returns true if the method declaration is const. """ return self.isconst + def has_version_added(self): + """ Returns true if a 'added' value was specified. """ + return _has_version_added(self.attribs) + + def get_version_added(self): + """ Returns the numeric 'added' value, or 0 if unspecified. + Used for sorting purposes only. """ + return _get_version_added(self.attribs) + + def has_version_removed(self): + """ Returns true if a 'removed' value was specified. """ + return _has_version_removed(self.attribs) + + def get_version_removed(self): + """ Returns the numeric 'removed' value, or 0 if unspecified. + Used for sorting purposes only. """ + return _get_version_removed(self.attribs) + + def removed_at_version(self, version): + """ Returns true if this function is removed at the specified version. """ + return self.has_version_removed() and self.get_version_removed() <= version + + def exists_at_version(self, version): + """ Returns true if this function exists at the specified version. """ + if self.has_version_added() and self.get_version_added() > version: + return False + return not self.removed_at_version(version) + class obj_argument: """ Class representing a function argument. """ @@ -1907,12 +2249,15 @@ def get_result_ptr_type_root(self): """ Return the *Ptr type structure name. """ return self.result_value[:-1] - def get_result_ptr_type(self, defined_structs=[]): + def get_result_ptr_type(self, defined_structs=[], version_finder=None): """ Return the *Ptr type. """ result = '' - if not self.result_value[:-1] in defined_structs: + name = self.result_value[:-1] + if not version_finder is None: + name = version_finder(name) + if not name in defined_structs: result += 'struct _' - result += self.result_value + result += name + self.result_value[-1] if self.is_byref() or self.is_byaddr(): result += '*' return result @@ -1951,16 +2296,22 @@ def is_result_struct_enum(self): return True return False - def get_result_struct_type(self, defined_structs=[]): + def get_result_struct_type(self, defined_structs=[], version_finder=None): """ Return the structure or enumeration type. """ result = '' + + name = self.result_value + is_enum = self.is_result_struct_enum() if not is_enum: if self.is_const(): result += 'const ' if not self.result_value in defined_structs: result += 'struct _' - result += self.result_value + if not version_finder is None: + name = version_finder(name) + + result += name if not is_enum: result += '*' return result @@ -2026,7 +2377,7 @@ def get_result_vector_type_root(self): """ Return the vector structure or basic type name. """ return self.result_value[0]['result_value'] - def get_result_vector_type(self, defined_structs=[]): + def get_result_vector_type(self, defined_structs=[], version_finder=None): """ Return the vector type. """ if not self.has_name(): raise Exception('Cannot use vector as a return type') @@ -2048,9 +2399,15 @@ def get_result_vector_type(self, defined_structs=[]): result['value'] = str elif type == 'refptr' or type == 'ownptr' or type == 'rawptr': str = '' - if not value[:-1] in defined_structs: + + # remove the * suffix + name = value[:-1] + if not version_finder is None: + name = version_finder(name) + + if not name in defined_structs: str += 'struct _' - str += value + str += name + value[-1] if self.is_const(): str += ' const' str += '*' @@ -2087,16 +2444,16 @@ def get_result_map_type(self, defined_structs=[]): return {'value': 'cef_string_multimap_t', 'format': 'multi'} raise Exception('Only mappings of strings to strings are supported') - def get_capi(self, defined_structs=[]): + def get_capi(self, defined_structs=[], version_finder=None): """ Format the value for the C API. """ result = '' format = 'single' if self.is_result_simple(): result += self.get_result_simple_type() elif self.is_result_ptr(): - result += self.get_result_ptr_type(defined_structs) + result += self.get_result_ptr_type(defined_structs, version_finder) elif self.is_result_struct(): - result += self.get_result_struct_type(defined_structs) + result += self.get_result_struct_type(defined_structs, version_finder) elif self.is_result_string(): result += self.get_result_string_type() elif self.is_result_map(): @@ -2106,7 +2463,7 @@ def get_capi(self, defined_structs=[]): else: raise Exception('Unsupported map type') elif self.is_result_vector(): - resdict = self.get_result_vector_type(defined_structs) + resdict = self.get_result_vector_type(defined_structs, version_finder) if resdict['format'] != 'single': format = resdict['format'] result += resdict['value'] diff --git a/tools/cef_version.py b/tools/cef_version.py index 7bc5026792..4e8d143c03 100644 --- a/tools/cef_version.py +++ b/tools/cef_version.py @@ -7,6 +7,7 @@ from file_util import * import git_util as git import os +from version_util import read_version_last, version_parse, VERSIONS_JSON_FILE class VersionFormatter: @@ -46,15 +47,18 @@ def get_chrome_version_components(self): if not bool(self._chrome_version): file_path = os.path.join(self.src_path, 'chrome', 'VERSION') assert os.path.isfile(file_path), file_path - read_version_file(file_path, self._chrome_version) + assert read_version_file(file_path, self._chrome_version), file_path return self._chrome_version + def get_chrome_major_version(self): + return self.get_chrome_version_components()['MAJOR'] + def get_cef_version_components(self): """ Returns CEF version components. """ if not bool(self._cef_version): file_path = os.path.join(self.cef_path, 'VERSION.in') assert os.path.isfile(file_path), file_path - read_version_file(file_path, self._cef_version) + assert read_version_file(file_path, self._cef_version), file_path return self._cef_version def get_cef_commit_components(self): @@ -75,11 +79,11 @@ def get_cef_branch_version_components(self): # branch since branching from origin/master. hashes = git.get_branch_hashes(self.cef_path) for hash in hashes: - # Determine if the API hash file was modified by the commit. + # Determine if the API versions file was modified by the commit. found = False files = git.get_changed_files(self.cef_path, hash) for file in files: - if file.find('cef_api_hash.h') >= 0: + if file.find(VERSIONS_JSON_FILE) >= 0: found = True break @@ -89,6 +93,14 @@ def get_cef_branch_version_components(self): else: bugfix += 1 + last_version = read_version_last( + os.path.join(self.cef_path, VERSIONS_JSON_FILE)) + if not last_version is None: + major, revision = version_parse(last_version) + if major == int(self.get_chrome_major_version()) and revision > minor: + # Override the computed minor version with the last specified API version. + minor = revision + self._branch_version = {'MINOR': minor, 'PATCH': bugfix} return self._branch_version @@ -149,8 +161,8 @@ def _get_old_version_parts(self): # - "X" is the Chromium major version (e.g. 74). # - "Y" is an incremental number that starts at 0 when a release branch is # created and changes only when the CEF C/C++ API changes (similar to how - # the CEF_API_HASH_UNIVERSAL value behaves in cef_version.h) (release branch - # only). + # the CEF_API_HASH_UNIVERSAL value behaves in cef_api_hash.h) (release + # branch only). # - "Z" is an incremental number that starts at 0 when a release branch is # created and changes on each commit, with reset to 0 when "Y" changes # (release branch only). @@ -186,9 +198,15 @@ def _compute_version(self): # if we can get the name of the branch we are on (may be just "HEAD"). cef_branch_name = git.get_branch_name(self.cef_path).split('/')[-1] + cef_minor = cef_patch = 0 + if cef_branch_name != 'HEAD' and cef_branch_name != 'master': + cef_branch = self.get_cef_branch_version_components() + cef_minor = cef_branch['MINOR'] + cef_patch = cef_branch['PATCH'] + self._version_parts = {'MAJOR': int(chrome_major), 'MINOR': 0, 'PATCH': 0} - self._version_string = '%s.0.0-%s.%s+%s+%s' % \ - (chrome_major, cef_branch_name, cef_commit['NUMBER'], + self._version_string = '%s.%d.%d-%s.%s+%s+%s' % \ + (chrome_major, cef_minor, cef_patch, cef_branch_name, cef_commit['NUMBER'], cef_commit_hash, chrome_version_part) else: cef_branch = self.get_cef_branch_version_components() diff --git a/tools/clang_util.py b/tools/clang_util.py index 4835472121..4ef09f75f7 100644 --- a/tools/clang_util.py +++ b/tools/clang_util.py @@ -10,22 +10,66 @@ # Script directory. script_dir = os.path.dirname(__file__) -root_dir = os.path.join(script_dir, os.pardir) +cef_dir = os.path.join(script_dir, os.pardir) +src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir)) +llvm_bin_dir = os.path.join(src_dir, + 'third_party/llvm-build/Release+Asserts/bin') if sys.platform == 'win32': # Force use of the clang-format version bundled with depot_tools. clang_format_exe = 'clang-format.bat' + clang_exe = os.path.join(llvm_bin_dir, 'clang-cl.exe') else: clang_format_exe = 'clang-format' + clang_exe = os.path.join(llvm_bin_dir, 'clang') def clang_format(file_name, file_contents): # -assume-filename is necessary to find the .clang-format file and determine # the language when specifying contents via stdin. result = exec_cmd("%s -assume-filename=%s" % (clang_format_exe, file_name), \ - root_dir, file_contents.encode('utf-8')) + cef_dir, file_contents.encode('utf-8')) if result['err'] != '': - print("clang-format error: %s" % result['err']) + sys.stderr.write("clang-format error: %s\n" % result['err']) + if result['out'] != '': + output = result['out'] + if sys.platform == 'win32': + # Convert to Unix line endings. + output = output.replace("\r", "") + return output + return None + + +def clang_format_inplace(file_name): + result = exec_cmd("%s -i %s" % (clang_format_exe, file_name), cef_dir) + if result['err'] != '': + sys.stderr.write("clang-format error: %s\n" % result['err']) + return False + return True + + +def clang_eval(file_name, + file_contents, + defines=[], + includes=[], + as_cpp=True, + verbose=False): + lang = 'c++' if as_cpp else 'c' + if file_name.lower().endswith('.h'): + lang += '-header' + # The -P option removes unnecessary line markers and whitespace. + format = '/EP' if sys.platform == 'win32' else '-E -P' + cmd = "%s -x %s %s %s %s -" % (clang_exe, lang, format, + ' '.join(['-D' + v for v in defines]), + ' '.join(['-I' + v for v in includes])) + if verbose: + print('--- Running "%s" in "%s"' % (cmd, cef_dir)) + + result = exec_cmd(cmd, cef_dir, file_contents.encode('utf-8')) + if result['err'] != '': + err = result['err'].replace('', file_name) + sys.stderr.write("clang error: %s\n" % err) + return None if result['out'] != '': output = result['out'] if sys.platform == 'win32': diff --git a/tools/file_util.py b/tools/file_util.py index cbddafd8c7..3a3bfcc36e 100644 --- a/tools/file_util.py +++ b/tools/file_util.py @@ -3,35 +3,45 @@ # can be found in the LICENSE file. from __future__ import absolute_import +import codecs +import fnmatch from glob import iglob from io import open +import json import os -import fnmatch import shutil import sys import time -def read_file(name, normalize=True): +def read_file(path, normalize=True): """ Read a file. """ - try: - with open(name, 'r', encoding='utf-8') as f: - # read the data - data = f.read() - if normalize: - # normalize line endings - data = data.replace("\r\n", "\n") - return data - except IOError as e: - (errno, strerror) = e.args - sys.stderr.write('Failed to read file ' + name + ': ' + strerror) - raise + if os.path.isfile(path): + try: + with open(path, 'r', encoding='utf-8') as f: + # read the data + data = f.read() + if normalize: + # normalize line endings + data = data.replace("\r\n", "\n") + return data + except IOError as e: + (errno, strerror) = e.args + sys.stderr.write('ERROR: Failed to read file ' + path + ': ' + strerror) + raise + return None + + +def write_file(path, data, overwrite=True, quiet=True): + """ Write a file. """ + if not overwrite and os.path.exists(path): + return False + if not quiet: + print('Writing file %s' % path) -def write_file(name, data): - """ Write a file. """ try: - with open(name, 'w', encoding='utf-8', newline='\n') as f: + with open(path, 'w', encoding='utf-8', newline='\n') as f: # write the data if sys.version_info.major == 2: f.write(data.decode('utf-8')) @@ -39,57 +49,67 @@ def write_file(name, data): f.write(data) except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to write file ' + name + ': ' + strerror) + sys.stderr.write('ERROR: Failed to write file ' + path + ': ' + strerror) raise + return True -def path_exists(name): +def path_exists(path): """ Returns true if the path currently exists. """ - return os.path.exists(name) + return os.path.exists(path) -def write_file_if_changed(name, data): +def write_file_if_changed(path, data, quiet=True): """ Write a file if the contents have changed. Returns True if the file was written. """ - if path_exists(name): - old_contents = read_file(name) + if path_exists(path): + old_contents = read_file(path) + assert not old_contents is None, path else: old_contents = '' if (data != old_contents): - write_file(name, data) + write_file(path, data, quiet=quiet) return True return False -def backup_file(name): +def backup_file(path): """ Rename the file to a name that includes the current time stamp. """ - move_file(name, name + '.' + time.strftime('%Y-%m-%d-%H-%M-%S')) + move_file(path, path + '.' + time.strftime('%Y-%m-%d-%H-%M-%S')) def copy_file(src, dst, quiet=True): """ Copy a file. """ + if not os.path.isfile(src): + return False + try: - shutil.copy2(src, dst) if not quiet: sys.stdout.write('Transferring ' + src + ' file.\n') + shutil.copy2(src, dst) except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to copy file from ' + src + ' to ' + dst + ': ' + - strerror) + sys.stderr.write('ERROR: Failed to copy file from ' + src + ' to ' + dst + + ': ' + strerror) raise + return True def move_file(src, dst, quiet=True): """ Move a file. """ + if not os.path.isfile(src): + return False + try: - shutil.move(src, dst) if not quiet: - sys.stdout.write('Moving ' + src + ' file.\n') + print('Moving ' + src + ' file.') + shutil.move(src, dst) except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to move file from ' + src + ' to ' + dst + ': ' + - strerror) + sys.stderr.write('ERROR: Failed to move file from ' + src + ' to ' + dst + + ': ' + strerror) raise + return True def copy_files(src_glob, dst_folder, quiet=True): @@ -102,56 +122,62 @@ def copy_files(src_glob, dst_folder, quiet=True): copy_file(fname, dst, quiet) -def remove_file(name, quiet=True): +def remove_file(path, quiet=True): """ Remove the specified file. """ try: - if path_exists(name): - os.remove(name) + if path_exists(path): if not quiet: - sys.stdout.write('Removing ' + name + ' file.\n') + print('Removing ' + path + ' file.') + os.remove(path) + return True except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to remove file ' + name + ': ' + strerror) + sys.stderr.write('ERROR: Failed to remove file ' + path + ': ' + strerror) raise + return False def copy_dir(src, dst, quiet=True): """ Copy a directory tree. """ try: remove_dir(dst, quiet) - shutil.copytree(src, dst) if not quiet: - sys.stdout.write('Transferring ' + src + ' directory.\n') + print('Transferring ' + src + ' directory.') + shutil.copytree(src, dst) except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to copy directory from ' + src + ' to ' + dst + - ': ' + strerror) + sys.stderr.write('ERROR: Failed to copy directory from ' + src + ' to ' + + dst + ': ' + strerror) raise -def remove_dir(name, quiet=True): +def remove_dir(path, quiet=True): """ Remove the specified directory. """ try: - if path_exists(name): - shutil.rmtree(name) + if path_exists(path): if not quiet: - sys.stdout.write('Removing ' + name + ' directory.\n') + print('Removing ' + path + ' directory.') + shutil.rmtree(path) + return True except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to remove directory ' + name + ': ' + strerror) + sys.stderr.write('ERROR: Failed to remove directory ' + path + ': ' + + strerror) raise + return False -def make_dir(name, quiet=True): +def make_dir(path, quiet=True): """ Create the specified directory. """ try: - if not path_exists(name): + if not path_exists(path): if not quiet: - sys.stdout.write('Creating ' + name + ' directory.\n') - os.makedirs(name) + print('Creating ' + path + ' directory.') + os.makedirs(path) except IOError as e: (errno, strerror) = e.args - sys.stderr.write('Failed to create directory ' + name + ': ' + strerror) + sys.stderr.write('ERROR: Failed to create directory ' + path + ': ' + + strerror) raise @@ -180,13 +206,17 @@ def get_files_recursive(directory, pattern): yield filename -def read_version_file(file, args): +def read_version_file(path, args): """ Read and parse a version file (key=value pairs, one per line). """ - lines = read_file(file).split("\n") + contents = read_file(path) + if contents is None: + return False + lines = contents.split("\n") for line in lines: parts = line.split('=', 1) if len(parts) == 2: args[parts[0]] = parts[1] + return True def eval_file(src): @@ -199,3 +229,48 @@ def normalize_path(path): if sys.platform == 'win32': return path.replace('\\', '/') return path + + +def read_json_file(path): + """ Read and parse a JSON file. Returns a list/dictionary or None. """ + if os.path.isfile(path): + try: + with codecs.open(path, 'r', encoding='utf-8') as fp: + return json.load(fp) + except IOError as e: + (errno, strerror) = e.args + sys.stderr.write('ERROR: Failed to read file ' + path + ': ' + strerror) + raise + return None + + +def _bytes_encoder(z): + if isinstance(z, bytes): + return str(z, 'utf-8') + else: + type_name = z.__class__.__name__ + raise TypeError(f"Object of type {type_name} is not serializable") + + +def write_json_file(path, data, overwrite=True, quiet=True): + """ Serialize and write a JSON file. Returns True on success. """ + if not overwrite and os.path.exists(path): + return False + + if not quiet: + print('Writing file %s' % path) + + try: + with open(path, 'w', encoding='utf-8') as fp: + json.dump( + data, + fp, + ensure_ascii=False, + indent=2, + sort_keys=True, + default=_bytes_encoder) + except IOError as e: + (errno, strerror) = e.args + sys.stderr.write('ERROR: Failed to write file ' + path + ': ' + strerror) + raise + return True diff --git a/tools/gclient_hook.py b/tools/gclient_hook.py index 85d59f1fe5..5000474728 100644 --- a/tools/gclient_hook.py +++ b/tools/gclient_hook.py @@ -28,8 +28,8 @@ print('Unknown operating system platform') sys.exit() -print("\nGenerating CEF version header file...") -cmd = [sys.executable, 'tools/make_version_header.py', 'include/cef_version.h'] +print("\nGenerating CEF translated files...") +cmd = [sys.executable, 'tools/version_manager.py', '-u', '--fast-check'] RunAction(cef_dir, cmd) print("\nPatching build configuration and source files for CEF...") diff --git a/tools/git_util.py b/tools/git_util.py index 886d8278dc..3cdb2b9414 100644 --- a/tools/git_util.py +++ b/tools/git_util.py @@ -26,85 +26,80 @@ def is_ancestor(path='.', commit1='HEAD', commit2='master'): return result['ret'] == 0 -def get_hash(path='.', branch='HEAD'): - """ Returns the git hash for the specified branch/tag/hash. """ - cmd = "%s rev-parse %s" % (git_exe, branch) +def exec_git_cmd(args, path='.'): + """ Executes a git command with the specified |args|. """ + cmd = "%s %s" % (git_exe, args) result = exec_cmd(cmd, path) if result['out'] != '': - return result['out'].strip() - return 'Unknown' + out = result['out'].strip() + if sys.platform == 'win32': + # Convert to Unix line endings. + out = out.replace('\r\n', '\n') + return out + return None + + +def get_hash(path='.', branch='HEAD'): + """ Returns the git hash for the specified branch/tag/hash. """ + cmd = "rev-parse %s" % branch + result = exec_git_cmd(cmd, path) + return 'Unknown' if result is None else result def get_branch_name(path='.', branch='HEAD'): """ Returns the branch name for the specified branch/tag/hash. """ # Returns the branch name if not in detached HEAD state, else an empty string # or "HEAD". - cmd = "%s rev-parse --abbrev-ref %s" % (git_exe, branch) - result = exec_cmd(cmd, path) - if result['out'] != '': - name = result['out'].strip() - if len(name) > 0 and name != 'HEAD': - return name + cmd = "rev-parse --abbrev-ref %s" % branch + result = exec_git_cmd(cmd, path) + if result is None: + return 'Unknown' + if result != 'HEAD': + return result - # Returns a value like "(HEAD, origin/3729, 3729)". - # Ubuntu 14.04 uses Git version 1.9.1 which does not support %D (which - # provides the same output but without the parentheses). - cmd = "%s log -n 1 --pretty=%%d %s" % (git_exe, branch) - result = exec_cmd(cmd, path) - if result['out'] != '': - return result['out'].strip()[1:-1].split(', ')[-1] - return 'Unknown' + # Returns a value like "(HEAD, origin/3729, 3729)". + # Ubuntu 14.04 uses Git version 1.9.1 which does not support %D (which + # provides the same output but without the parentheses). + cmd = "log -n 1 --pretty=%%d %s" % branch + result = exec_git_cmd(cmd, path) + return 'Unknown' if result is None else result[1:-1].split(', ')[-1] def get_url(path='.'): """ Returns the origin url for the specified path. """ - cmd = "%s config --get remote.origin.url" % git_exe - result = exec_cmd(cmd, path) - if result['out'] != '': - return result['out'].strip() - return 'Unknown' + cmd = "config --get remote.origin.url" + result = exec_git_cmd(cmd, path) + return 'Unknown' if result is None else result def get_commit_number(path='.', branch='HEAD'): """ Returns the number of commits in the specified branch/tag/hash. """ - cmd = "%s rev-list --count %s" % (git_exe, branch) - result = exec_cmd(cmd, path) - if result['out'] != '': - return result['out'].strip() - return '0' + cmd = "rev-list --count %s" % (branch) + result = exec_git_cmd(cmd, path) + return '0' if result is None else result def get_changed_files(path, hash): """ Retrieves the list of changed files. """ if hash == 'unstaged': - cmd = "%s diff --name-only" % git_exe + cmd = "diff --name-only" elif hash == 'staged': - cmd = "%s diff --name-only --cached" % git_exe + cmd = "diff --name-only --cached" else: - cmd = "%s diff-tree --no-commit-id --name-only -r %s" % (git_exe, hash) - result = exec_cmd(cmd, path) - if result['out'] != '': - files = result['out'] - if sys.platform == 'win32': - # Convert to Unix line endings. - files = files.replace('\r\n', '\n') - return files.strip().split("\n") - return [] + cmd = "diff-tree --no-commit-id --name-only -r %s" % hash + result = exec_git_cmd(cmd, path) + return [] if result is None else result.split("\n") def get_branch_hashes(path='.', branch='HEAD', ref='origin/master'): """ Returns an ordered list of hashes for commits that have been applied since branching from ref. """ - cmd = "%s cherry %s %s" % (git_exe, ref, branch) - result = exec_cmd(cmd, path) - if result['out'] != '': - hashes = result['out'] - if sys.platform == 'win32': - # Convert to Unix line endings. - hashes = hashes.replace('\r\n', '\n') - # Remove the "+ " or "- " prefix. - return [line[2:] for line in hashes.strip().split('\n')] - return [] + cmd = "cherry %s %s" % (ref, branch) + result = exec_git_cmd(cmd, path) + if result is None: + return [] + # Remove the "+ " or "- " prefix. + return [line[2:] for line in result.split('\n')] def write_indented_output(output): diff --git a/tools/gn_args.py b/tools/gn_args.py index 6d0c3a73c1..4c55a09aec 100644 --- a/tools/gn_args.py +++ b/tools/gn_args.py @@ -87,9 +87,12 @@ print('Unknown operating system platform') sys.exit() +_QUIET = False + def msg(msg): - print('NOTE: ' + msg) + if not _QUIET: + print('NOTE: ' + msg) def NameValueListToDict(name_value_list): @@ -591,12 +594,16 @@ def LinuxSysrootExists(cpu): return os.path.isdir(os.path.join(sysroot_root, sysroot_name)) -def GetAllPlatformConfigs(build_args): +def GetAllPlatformConfigs(build_args, quiet=False): """ Return a map of directory name to GN args for the current platform. """ result = {} + if quiet: + global _QUIET + _QUIET = True + # Merged args without validation. args = GetMergedArgs(build_args) diff --git a/tools/make_api_hash_header.py b/tools/make_api_hash_header.py deleted file mode 100644 index 6c79efcdba..0000000000 --- a/tools/make_api_hash_header.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 2019 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. - -from __future__ import absolute_import -from cef_api_hash import cef_api_hash -from cef_parser import get_copyright -from file_util import * -import os -import sys - - -def make_api_hash_header(cpp_header_dir): - # calculate api hashes - api_hash_calculator = cef_api_hash(cpp_header_dir, verbose=False) - api_hash = api_hash_calculator.calculate() - - result = get_copyright(full=True, translator=False) + \ -"""// -// --------------------------------------------------------------------------- -// -// This file was generated by the make_api_hash_header.py tool. -// - -#ifndef CEF_INCLUDE_API_HASH_H_ -#define CEF_INCLUDE_API_HASH_H_ - -#include "include/internal/cef_export.h" - -// The API hash is created by analyzing CEF header files for C API type -// definitions. The hash value will change when header files are modified in a -// way that may cause binary incompatibility with other builds. The universal -// hash value will change if any platform is affected whereas the platform hash -// values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "$UNIVERSAL$" -#if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "$WINDOWS$" -#elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "$MAC$" -#elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "$LINUX$" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/// -// Returns CEF API hashes for the libcef library. The returned string is owned -// by the library and should not be freed. The |entry| parameter describes which -// hash value will be returned: -// 0 - CEF_API_HASH_PLATFORM -// 1 - CEF_API_HASH_UNIVERSAL -// 2 - CEF_COMMIT_HASH (from cef_version.h) -/// -CEF_EXPORT const char* cef_api_hash(int entry); - -#ifdef __cplusplus -} -#endif -#endif // CEF_INCLUDE_API_HASH_H_ -""" - - # Substitute hash values for placeholders. - for platform, value in api_hash.items(): - result = result.replace('$%s$' % platform.upper(), value) - - return result - - -def write_api_hash_header(output, cpp_header_dir): - output = os.path.abspath(output) - result = make_api_hash_header(cpp_header_dir) - ret = write_file_if_changed(output, result) - - # Also write to |cpp_header_dir| if a different path from |output|, since we - # need to commit the hash header for cef_version.py to correctly calculate the - # version number based on git history. - header_path = os.path.abspath( - os.path.join(cpp_header_dir, os.path.basename(output))) - if (output != header_path): - write_file_if_changed(header_path, result) - - return ret - - -def main(argv): - if len(argv) < 3: - print(("Usage:\n %s " % argv[0])) - sys.exit(-1) - write_api_hash_header(argv[1], argv[2]) - - -if '__main__' == __name__: - main(sys.argv) diff --git a/tools/make_api_versions_header.py b/tools/make_api_versions_header.py new file mode 100644 index 0000000000..529c087983 --- /dev/null +++ b/tools/make_api_versions_header.py @@ -0,0 +1,130 @@ +# Copyright (c) 2024 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. + +from __future__ import absolute_import +from cef_parser import get_copyright +from file_util import write_file_if_changed +from version_util import read_version_files +import os +import sys + + +def make_api_versions_header(json): + result = get_copyright(full=True, translator=False) + \ +"""// +// --------------------------------------------------------------------------- +// +// This file was generated by the make_api_versions_header.py tool. Versions +// are managed using the version_manager.py tool. For usage details see +// https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md +// + +#ifndef CEF_INCLUDE_CEF_API_VERSIONS_H_ +#define CEF_INCLUDE_CEF_API_VERSIONS_H_ + +#include "include/base/cef_build.h" +""" + + for version, hashes in json['hashes'].items(): + version_part = """ +// $COMMENT$ +#define CEF_API_VERSION_$VER$ $VER$ +#define CEF_API_HASH_$VER$_UNIVERSAL "$UNIVERSAL$" +#if defined(OS_WIN) +#define CEF_API_HASH_$VER$_PLATFORM "$WINDOWS$" +#elif defined(OS_MAC) +#define CEF_API_HASH_$VER$_PLATFORM "$MAC$" +#elif defined(OS_LINUX) +#define CEF_API_HASH_$VER$_PLATFORM "$LINUX$" +#endif +""".replace('$VER$', version) + + # Substitute hash values for placeholders. + for key, value in hashes.items(): + version_part = version_part.replace('$%s$' % key.upper(), value) + + result += version_part + + result += \ +""" +// Oldest supported CEF version. +#define CEF_API_VERSION_MIN CEF_API_VERSION_$MIN$ + +// Newest supported CEF version. +#define CEF_API_VERSION_LAST CEF_API_VERSION_$LAST$ + +#endif // CEF_INCLUDE_CEF_API_VERSIONS_H_ +""".replace('$LAST$', json['last']).replace('$MIN$', json['min']) + + return result + + +def make_api_versions_inc(json): + result = get_copyright(full=False, translator=False) + \ +"""// +// --------------------------------------------------------------------------- +// +// This file was generated by the make_api_versions_header.py tool. +// + +namespace { + +struct ApiVersionHash { + int version; + const char* const universal; + const char* const platform; +}; + +const ApiVersionHash kApiVersionHashes[] = {""" + + for version, hashes in json['hashes'].items(): + result += """ + {$VER$, CEF_API_HASH_$VER$_UNIVERSAL, CEF_API_HASH_$VER$_PLATFORM},""".replace( + '$VER$', version) + + result += \ +""" +}; + +const size_t kApiVersionHashesSize = std::size(kApiVersionHashes); + +} // namespace +""" + + return result + + +def write_api_versions(out_header_file, out_inc_file, json): + out_file = os.path.abspath(out_header_file) + result = make_api_versions_header(json) + if not bool(result): + sys.stderr.write('Failed to create %s\n' % out_file) + sys.exit(1) + retval1 = write_file_if_changed(out_file, result) + + out_file = os.path.abspath(out_inc_file) + result = make_api_versions_inc(json) + if not bool(result): + sys.stderr.write('Failed to create %s\n' % out_file) + sys.exit(1) + retval2 = write_file_if_changed(out_file, result) + + return retval1 or retval2 + + +def main(argv): + if len(argv) < 5: + print( + "Usage:\n %s " + % argv[0]) + sys.exit(-1) + + json, initialized = \ + read_version_files(argv[3], argv[4], True, combine=True) + if not write_api_versions(argv[1], argv[2], json): + print('Nothing done') + + +if '__main__' == __name__: + main(sys.argv) diff --git a/tools/make_capi_header.py b/tools/make_capi_header.py index 5e270d0be5..a0e7cba5f1 100644 --- a/tools/make_capi_header.py +++ b/tools/make_capi_header.py @@ -8,33 +8,41 @@ def make_capi_global_funcs(funcs, defined_names, translate_map, indent): result = '' - first = True for func in funcs: comment = func.get_comment() - if first or len(comment) > 0: - result += '\n' + format_comment(comment, indent, translate_map) + pre, post = get_version_surround(func) + result += '\n' + pre + if len(comment) > 0: + result += format_comment(comment, indent, translate_map) if func.get_retval().get_type().is_result_string(): result += indent + '// The resulting string must be freed by calling cef_string_userfree_free().\n' - result += indent + 'CEF_EXPORT ' + func.get_capi_proto(defined_names) + ';\n' - if first: - first = False + result += indent + 'CEF_EXPORT ' + func.get_capi_proto( + defined_names) + ';\n' + post return result def make_capi_member_funcs(funcs, defined_names, translate_map, indent): result = '' - first = True for func in funcs: comment = func.get_comment() - if first or len(comment) > 0: - result += '\n' + format_comment(comment, indent, translate_map) + pre, post = get_version_surround(func) + result += '\n' + pre + if len(comment) > 0: + result += format_comment(comment, indent, translate_map) if func.get_retval().get_type().is_result_string(): result += indent + '// The resulting string must be freed by calling cef_string_userfree_free().\n' parts = func.get_capi_parts() - result += indent+parts['retval']+' (CEF_CALLBACK *'+parts['name']+ \ - ')('+', '.join(parts['args'])+');\n' - if first: - first = False + result += indent + parts['retval'] + ' (CEF_CALLBACK *' + parts['name'] + \ + ')(' + ', '.join(parts['args']) + ');\n' + if len(post) > 0 and func.has_version_removed(): + if func.has_version_added(): + result += '#elif ' + get_version_check({ + 'added': func.get_attrib('removed') + }) + else: + result += '#else' + result += '\n' + indent + 'uintptr_t ' + parts['name'] + '_removed;\n' + result += post return result @@ -61,13 +69,16 @@ def make_capi_header(header, filename): #define $GUARD$ #pragma once +#if defined(BUILDING_CEF_SHARED) +#error This file cannot be included DLL-side +#endif + """ # Protect against incorrect use of test headers. if filename.startswith('test/'): result += \ -"""#if !defined(BUILDING_CEF_SHARED) && !defined(WRAPPING_CEF_SHARED) && \\ - !defined(UNIT_TEST) +"""#if !defined(WRAPPING_CEF_SHARED) && !defined(UNIT_TEST) #error This file can be included for unit tests only #endif @@ -78,7 +89,7 @@ def make_capi_header(header, filename): # identify all includes and forward declarations translated_includes = set([]) internal_includes = set([]) - all_declares = set([]) + all_declares = {} for cls in classes: includes = cls.get_includes() for include in includes: @@ -87,7 +98,7 @@ def make_capi_header(header, filename): # translated CEF API headers. raise Exception('Disallowed include of %s.h from %s' % (include, filename)) - elif include.startswith('internal/'): + elif include.startswith('internal/') or include == 'cef_api_hash': # internal/ headers may be C or C++. Include them as-is. internal_includes.add(include) else: @@ -97,7 +108,10 @@ def make_capi_header(header, filename): declare_cls = header.get_class(declare) if declare_cls is None: raise Exception('Unknown class: %s' % declare) - all_declares.add(declare_cls.get_capi_name()) + capi_name = declare_cls.get_capi_name() + if not capi_name in all_declares: + all_declares[capi_name] = declare_cls.get_version_check() \ + if declare_cls.has_version() else None # output translated includes if len(translated_includes) > 0: @@ -122,22 +136,39 @@ def make_capi_header(header, filename): """ # output forward declarations - if len(all_declares) > 0: - sorted_declares = sorted(all_declares) + if bool(all_declares): + sorted_declares = sorted(all_declares.keys()) for declare in sorted_declares: + cls_version_check = all_declares[declare] + if not cls_version_check is None: + result += '#if ' + cls_version_check + '\n' result += 'struct _' + declare + ';\n' + if not cls_version_check is None: + result += '#endif\n' # output classes for cls in classes: + pre, post = get_version_surround(cls, long=True) + if len(pre) > 0: + result += '\n' + pre + + comment = cls.get_comment() + add_comment = [] + if comment[-1] != '': + add_comment.append('') + add_comment.append('NOTE: This struct is allocated %s-side.' % \ + ('client' if cls.is_client_side() else 'DLL')) + add_comment.append('') + # virtual functions are inside the structure classname = cls.get_capi_name() - result += '\n' + format_comment(cls.get_comment(), '', translate_map) + result += '\n' + format_comment(comment + add_comment, '', translate_map) result += 'typedef struct _'+classname+' {\n'+\ ' ///\n'+\ ' /// Base structure.\n'+\ ' ///\n'+\ ' '+cls.get_parent_capi_name()+' base;\n' - funcs = cls.get_virtual_funcs() + funcs = cls.get_virtual_funcs(version_order=True) result += make_capi_member_funcs(funcs, defined_names, translate_map, ' ') result += '} ' + classname + ';\n\n' @@ -149,6 +180,8 @@ def make_capi_header(header, filename): result += make_capi_global_funcs(funcs, defined_names, translate_map, '') + '\n' + result += post + # output global functions funcs = header.get_funcs(filename) if len(funcs) > 0: diff --git a/tools/make_capi_versions_header.py b/tools/make_capi_versions_header.py new file mode 100644 index 0000000000..3c79b4f849 --- /dev/null +++ b/tools/make_capi_versions_header.py @@ -0,0 +1,189 @@ +# Copyright (c) 2024 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. + +from __future__ import absolute_import +from cef_parser import * +import functools + + +def _version_finder(header, name): + cls = header.get_capi_class(name) + if not cls is None: + return cls.get_capi_name(first_version=True) + return name + + +def make_capi_global_funcs(version, version_finder, funcs, defined_names): + result = '' + for func in funcs: + result += 'CEF_EXPORT ' + func.get_capi_proto( + defined_names, version=version, version_finder=version_finder) + ';\n' + return result + + +def make_capi_member_funcs(version, version_finder, funcs, defined_names, + translate_map, indent): + result = '' + for func in funcs: + parts = func.get_capi_parts(version=version, version_finder=version_finder) + if func.removed_at_version(version): + result += indent + 'uintptr_t ' + parts['name'] + '_removed;\n' + else: + result += indent + parts['retval'] + ' (CEF_CALLBACK *' + parts['name'] + \ + ')(' + ', '.join(parts['args']) + ');\n' + return result + + +def make_capi_versions_header(header, filename): + # structure names that have already been defined + defined_names = header.get_defined_structs() + + # map of strings that will be changed in C++ comments + translate_map = header.get_capi_translations() + + # header string + result = get_copyright(full=True, translator=False) + \ +"""// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +// $hash=$$HASH$$$ +// + +#ifndef $GUARD$ +#define $GUARD$ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) +#error This file can be included DLL-side only +#endif + +""" + + classes = header.get_classes(filename) + + # identify all includes and forward declarations + translated_includes = set() + internal_includes = set() + all_declares = set() + for cls in classes: + includes = cls.get_includes() + for include in includes: + if include.startswith('base/'): + # base/ headers are C++. They should not be included by + # translated CEF API headers. + raise Exception('Disallowed include of %s.h from %s' % (include, + filename)) + elif include.startswith('internal/') or include == 'cef_api_hash': + # internal/ headers may be C or C++. Include them as-is. + internal_includes.add(include) + else: + translated_includes.add(include) + declares = cls.get_forward_declares() + for declare in declares: + declare_cls = header.get_class(declare) + if declare_cls is None: + raise Exception('Unknown class: %s' % declare) + for version in declare_cls.get_all_versions(): + all_declares.add(declare_cls.get_capi_name(version=version)) + + # output translated includes + if len(translated_includes) > 0: + sorted_includes = sorted(translated_includes) + for include in sorted_includes: + suffix = '_versions' if not include in ('cef_base',) else '' + result += '#include "include/capi/' + include + '_capi' + suffix + '.h"\n' + else: + result += '#include "include/capi/cef_base_capi.h"\n' + + # output internal includes + if len(internal_includes) > 0: + sorted_includes = sorted(internal_includes) + for include in sorted_includes: + result += '#include "include/' + include + '.h"\n' + + result += \ +""" +#ifdef __cplusplus +extern "C" { +#endif + +""" + + # output forward declarations + if bool(all_declares): + for declare in sorted(all_declares): + result += 'struct _' + declare + ';\n' + result += '\n' + + version_finder = functools.partial(_version_finder, header) + + # output classes + for cls in classes: + for version in cls.get_all_versions(): + # virtual functions are inside the structure + classname = cls.get_capi_name(version=version) + result += 'typedef struct _'+classname+' {\n'+\ + ' '+cls.get_parent_capi_name(version=version)+' base;\n' + funcs = cls.get_virtual_funcs(version_order=True, version=version) + result += make_capi_member_funcs(version, version_finder, funcs, + defined_names, translate_map, ' ') + result += '} ' + classname + ';\n\n' + + defined_names.append(classname) + + # static functions become global + funcs = cls.get_static_funcs() + if len(funcs) > 0: + result += make_capi_global_funcs(version, version_finder, funcs, + defined_names) + '\n' + + # output global functions + funcs = header.get_funcs(filename) + if len(funcs) > 0: + result += make_capi_global_funcs(None, version_finder, funcs, + defined_names) + '\n' + + # footer string + result += \ +"""#ifdef __cplusplus +} +#endif + +#endif // $GUARD$ +""" + + # add the guard string + guard = 'CEF_INCLUDE_CAPI_' + \ + filename.replace('/', '_').replace('.', '_capi_versions_').upper() + '_' + result = result.replace('$GUARD$', guard) + + return result + + +def write_capi_versions_header(header, header_dir, filename): + file = get_capi_file_name(os.path.join(header_dir, filename), versions=True) + newcontents = make_capi_versions_header(header, filename) + return (file, newcontents) + + +# test the module +if __name__ == "__main__": + import sys + + # verify that the correct number of command-line arguments are provided + if len(sys.argv) < 2: + sys.stderr.write('Usage: ' + sys.argv[0] + ' \n') + sys.exit() + + # create the header object + header = obj_header() + header.add_file(sys.argv[1]) + + # dump the result to stdout + filename = os.path.split(sys.argv[1])[1] + sys.stdout.write(make_capi_versions_header(header, filename)) diff --git a/tools/make_cpptoc_header.py b/tools/make_cpptoc_header.py index 8375553759..a8b2f9a4c1 100644 --- a/tools/make_cpptoc_header.py +++ b/tools/make_cpptoc_header.py @@ -20,8 +20,6 @@ def make_cpptoc_header(header, clsname): defname += get_capi_name(clsname[3:], False) defname = defname.upper() - capiname = cls.get_capi_name() - result = get_copyright() result += '#ifndef CEF_LIBCEF_DLL_CPPTOC_'+defname+'_CPPTOC_H_\n'+ \ @@ -41,9 +39,11 @@ def make_cpptoc_header(header, clsname): #endif """ + with_versions = dllside + # include the headers for this class result += '\n#include "include/'+cls.get_file_name()+'"\n' \ - '#include "include/capi/'+cls.get_capi_file_name()+'"\n' + '#include "include/capi/'+cls.get_capi_file_name(versions=with_versions)+'"\n' # include headers for any forward declared classes that are not in the same file declares = cls.get_forward_declares() @@ -51,7 +51,7 @@ def make_cpptoc_header(header, clsname): dcls = header.get_class(declare) if dcls.get_file_name() != cls.get_file_name(): result += '#include "include/'+dcls.get_file_name()+'"\n' \ - '#include "include/capi/'+dcls.get_capi_file_name()+'"\n' + '#include "include/capi/'+dcls.get_capi_file_name(versions=with_versions)+'"\n' base_class_name = header.get_base_class_name(clsname) base_scoped = True if base_class_name == 'CefBaseScoped' else False @@ -63,19 +63,78 @@ def make_cpptoc_header(header, clsname): template_class = 'CefCppToCRefCounted' result += '#include "libcef_dll/cpptoc/' + template_file + '"' - result += '\n\n// Wrap a C++ class with a C structure.\n' - if dllside: - result += '// This class may be instantiated and accessed DLL-side only.\n' + if with_versions: + pre = post = '' else: - result += '// This class may be instantiated and accessed wrapper-side only.\n' - - result += 'class '+clsname+'CppToC\n'+ \ - ' : public ' + template_class + '<'+clsname+'CppToC, '+clsname+', '+capiname+'> {\n'+ \ - ' public:\n'+ \ - ' '+clsname+'CppToC();\n'+ \ - ' virtual ~'+clsname+'CppToC();\n'+ \ - '};\n\n' + pre, post = get_version_surround(cls, long=True) + if len(pre) > 0: + result += '\n\n' + pre.strip() + + result += '\n\n' + + versions = cls.get_all_versions() if with_versions else (None,) + + for version in versions: + result += '// Wrap a C++ class with a C structure%s.\n' % \ + ('' if version is None else ' at API version %d' % version) + if dllside: + result += '// This class may be instantiated and accessed DLL-side only.\n' + else: + result += '// This class may be instantiated and accessed wrapper-side only.\n' + + capiname = cls.get_capi_name(version=version) + if version is None: + typename = clsname + 'CppToC' + else: + typename = clsname + '_%d_CppToC' % version + + result += 'class '+typename+'\n'+ \ + ' : public ' + template_class + '<'+typename+', '+clsname+', '+capiname+'> {\n'+ \ + ' public:\n'+ \ + ' '+typename+'();\n'+ \ + ' virtual ~'+typename+'();\n'+ \ + '};\n\n' + + typename = clsname + 'CppToC' + if len(versions) > 1: + result += '// Helpers to return objects at the globally configured API version.\n' + structname = cls.get_capi_name(version=versions[0]) + if base_scoped: + result += structname + '* ' + typename + '_WrapOwn(CefOwnPtr<' + clsname + '> c);\n' + \ + 'std::pair, '+ structname + '*> ' + typename + '_WrapRaw(CefRawPtr<' + clsname + '> c);\n' + \ + 'CefOwnPtr<' + clsname + '> ' + typename + '_UnwrapOwn('+ structname + '* s);\n' + \ + 'CefRawPtr<' + clsname + '> ' + typename + '_UnwrapRaw('+ structname + '* s);\n' + \ + 'CefBaseScoped* ' + typename + '_GetWrapper('+ structname + '* s);\n\n' + else: + result += structname + '* ' + typename + '_Wrap(CefRefPtr<' + clsname + '> c);\n' + \ + 'CefRefPtr<' + clsname + '> ' + typename + '_Unwrap('+ structname + '* s);\n\n' + else: + if versions[0] is None: + targetname = clsname + 'CppToC' + structname = cls.get_capi_name() + else: + targetname = clsname + '_%d_CppToC' % versions[0] + structname = cls.get_capi_name(version=versions[0]) + if base_scoped: + result += 'constexpr auto ' + typename + '_WrapOwn = ' + targetname + '::WrapOwn;\n' + \ + 'constexpr auto ' + typename + '_WrapRaw = ' + targetname + '::WrapRaw;\n' + \ + 'constexpr auto ' + typename + '_UnwrapOwn = ' + targetname + '::UnwrapOwn;\n' + \ + 'constexpr auto ' + typename + '_UnwrapRaw = ' + targetname + '::UnwrapRaw;\n' + \ + 'constexpr auto ' + typename + '_GetWrapper = ' + targetname + '::GetWrapper;\n\n' + else: + result += 'constexpr auto ' + typename + '_Wrap = ' + targetname + '::Wrap;\n' + \ + 'constexpr auto ' + typename + '_Unwrap = ' + targetname + '::Unwrap;\n\n' + + if base_scoped: + result += 'inline ' + structname + '* ' + typename + '_WrapRawAndRelease(CefRawPtr<' + clsname + '> c) {\n' + \ + ' auto [ownerPtr, structPtr] = ' + typename + '_WrapRaw(c);\n' + \ + ' ownerPtr.release();\n' + \ + ' return structPtr;\n' + \ + '}\n\n' + + if len(post) > 0: + result += post + '\n' result += '#endif // CEF_LIBCEF_DLL_CPPTOC_' + defname + '_CPPTOC_H_' diff --git a/tools/make_cpptoc_impl.py b/tools/make_cpptoc_impl.py index 5284d38137..f73a99025c 100644 --- a/tools/make_cpptoc_impl.py +++ b/tools/make_cpptoc_impl.py @@ -4,6 +4,8 @@ from __future__ import absolute_import from cef_parser import * +import copy +import functools def make_cpptoc_impl_proto(name, func, parts): @@ -16,27 +18,37 @@ def make_cpptoc_impl_proto(name, func, parts): return proto -def make_cpptoc_function_impl_existing(cls, name, func, impl, defined_names): +def make_cpptoc_function_impl_existing(cls, name, func, impl, defined_names, + version, version_finder): notify(name + ' has manual edits') # retrieve the C API prototype parts - parts = func.get_capi_parts(defined_names, True) + parts = func.get_capi_parts( + defined_names, True, version=version, version_finder=version_finder) changes = format_translation_changes(impl, parts) if len(changes) > 0: notify(name + ' prototype changed') - return make_cpptoc_impl_proto( - name, func, parts) + '{' + changes + impl['body'] + '\n}\n\n' + return make_cpptoc_impl_proto(name, func, + parts) + '{' + changes + impl['body'] + '\n}\n' -def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): +def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped, + version, version_finder): + if not version is None and isinstance(func, obj_function_virtual) and \ + not func.exists_at_version(version): + raise Exception( + 'Attempting to generate non-existing function %s at version %d' % + (name, version)) + # Special handling for the cef_shutdown global function. is_cef_shutdown = name == 'cef_shutdown' and isinstance( func.parent, obj_header) # retrieve the C API prototype parts - parts = func.get_capi_parts(defined_names, True) + parts = func.get_capi_parts( + defined_names, True, version=version, version_finder=version_finder) result = make_cpptoc_impl_proto(name, func, parts) + ' {' if isinstance(func.parent, obj_class) and \ @@ -219,31 +231,31 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): elif arg_type == 'refptr_same' or arg_type == 'refptr_diff': ptr_class = arg.get_type().get_ptr_type() if arg_type == 'refptr_same': - params.append(ptr_class + 'CppToC::Unwrap(' + arg_name + ')') + params.append(ptr_class + 'CppToC_Unwrap(' + arg_name + ')') else: - params.append(ptr_class + 'CToCpp::Wrap(' + arg_name + ')') + params.append(ptr_class + 'CToCpp_Wrap(' + arg_name + ')') elif arg_type == 'ownptr_same' or arg_type == 'rawptr_same': ptr_class = arg.get_type().get_ptr_type() if arg_type == 'ownptr_same': - params.append(ptr_class + 'CppToC::UnwrapOwn(' + arg_name + ')') + params.append(ptr_class + 'CppToC_UnwrapOwn(' + arg_name + ')') else: - params.append(ptr_class + 'CppToC::UnwrapRaw(' + arg_name + ')') + params.append(ptr_class + 'CppToC_UnwrapRaw(' + arg_name + ')') elif arg_type == 'ownptr_diff' or arg_type == 'rawptr_diff': ptr_class = arg.get_type().get_ptr_type() result += comment+\ - '\n CefOwnPtr<'+ptr_class+'> '+arg_name+'Ptr('+ptr_class+'CToCpp::Wrap('+arg_name+'));' + '\n CefOwnPtr<'+ptr_class+'> '+arg_name+'Ptr('+ptr_class+'CToCpp_Wrap('+arg_name+'));' if arg_type == 'ownptr_diff': params.append('std::move(' + arg_name + 'Ptr)') else: params.append(arg_name + 'Ptr.get()') elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref': - ptr_class = arg.get_type().get_ptr_type() + ptr_class = ptr_class_u = arg.get_type().get_ptr_type() if arg_type == 'refptr_same_byref': - assign = ptr_class + 'CppToC::Unwrap(*' + arg_name + ')' + assign = ptr_class + 'CppToC_Unwrap(*' + arg_name + ')' else: - assign = ptr_class + 'CToCpp::Wrap(*' + arg_name + ')' + assign = ptr_class + 'CToCpp_Wrap(*' + arg_name + ')' result += comment+\ - '\n CefRefPtr<'+ptr_class+'> '+arg_name+'Ptr;'\ + '\n CefRefPtr<'+ptr_class_u+'> '+arg_name+'Ptr;'\ '\n if ('+arg_name+' && *'+arg_name+') {'\ '\n '+arg_name+'Ptr = '+assign+';'\ '\n }'\ @@ -273,10 +285,10 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): assign = arg_name + '[i]?true:false' elif arg_type == 'refptr_vec_same_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CppToC::Unwrap(' + arg_name + '[i])' + assign = ptr_class + 'CppToC_Unwrap(' + arg_name + '[i])' elif arg_type == 'refptr_vec_diff_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CToCpp::Wrap(' + arg_name + '[i])' + assign = ptr_class + 'CToCpp_Wrap(' + arg_name + '[i])' result += comment+\ '\n std::vector<'+vec_type+' > '+arg_name+'List;'\ '\n if ('+arg_name+'Count && *'+arg_name+'Count > 0 && '+arg_name+') {'\ @@ -296,13 +308,13 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): else: ptr_class = arg.get_type().get_ptr_type() if arg_type == 'refptr_vec_same_byref_const': - assign = ptr_class + 'CppToC::Unwrap(' + arg_name + '[i])' + assign = ptr_class + 'CppToC_Unwrap(' + arg_name + '[i])' elif arg_type == 'refptr_vec_diff_byref_const': - assign = ptr_class + 'CToCpp::Wrap(' + arg_name + '[i])' + assign = ptr_class + 'CToCpp_Wrap(' + arg_name + '[i])' elif arg_type == 'rawptr_vec_same_byref_const': - assign = ptr_class + 'CppToC::UnwrapRaw(' + arg_name + '[i])' + assign = ptr_class + 'CppToC_UnwrapRaw(' + arg_name + '[i])' elif arg_type == 'rawptr_vec_diff_byref_const': - assign = ptr_class + 'CToCpp::Wrap(' + arg_name + '[i]).release()' + assign = ptr_class + 'CToCpp_Wrap(' + arg_name + '[i]).release()' result += comment+\ '\n std::vector<'+vec_type+' > '+arg_name+'List;'\ '\n if ('+arg_name+'Count > 0) {'\ @@ -334,13 +346,16 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): if isinstance(func.parent, obj_class): # virtual and static class methods if isinstance(func, obj_function_virtual): + ptr_class = cls.get_name() + if not version_finder is None: + ptr_class = version_finder(ptr_class, as_cpp=True) if cls.get_name() == func.parent.get_name(): - # virtual method for the current class - result += func.parent.get_name() + 'CppToC::Get(self)->' + # virtual method called for the current class + result += ptr_class + 'CppToC::Get(self)->' else: - # virtual method for a parent class - result += cls.get_name( - ) + 'CppToC::Get(reinterpret_cast<' + cls.get_capi_name() + '*>(self))->' + # virtual method called for a parent class + result += ptr_class + 'CppToC::Get(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(self))->' else: result += func.parent.get_name() + '::' result += func.get_name() + '(' @@ -382,9 +397,9 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref': ptr_class = arg.get_type().get_ptr_type() if arg_type == 'refptr_same_byref': - assign = ptr_class + 'CppToC::Wrap(' + arg_name + 'Ptr)' + assign = ptr_class + 'CppToC_Wrap(' + arg_name + 'Ptr)' else: - assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + 'Ptr)' + assign = ptr_class + 'CToCpp_Unwrap(' + arg_name + 'Ptr)' result += comment+\ '\n if ('+arg_name+') {'\ '\n if ('+arg_name+'Ptr.get()) {'\ @@ -413,10 +428,10 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): assign = arg_name + 'List[i]' elif arg_type == 'refptr_vec_same_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CppToC::Wrap(' + arg_name + 'List[i])' + assign = ptr_class + 'CppToC_Wrap(' + arg_name + 'List[i])' elif arg_type == 'refptr_vec_diff_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + 'List[i])' + assign = ptr_class + 'CToCpp_Unwrap(' + arg_name + 'List[i])' result += comment+\ '\n if ('+arg_name+'Count && '+arg_name+') {'\ '\n *'+arg_name+'Count = std::min('+arg_name+'List.size(), *'+arg_name+'Count);'\ @@ -452,80 +467,121 @@ def make_cpptoc_function_impl_new(cls, name, func, defined_names, base_scoped): result += '\n return _retval.DetachToUserFree();' elif retval_type == 'refptr_same': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CppToC::Wrap(_retval);' + result += '\n return ' + ptr_class + 'CppToC_Wrap(_retval);' elif retval_type == 'refptr_diff': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CToCpp::Unwrap(_retval);' + result += '\n return ' + ptr_class + 'CToCpp_Unwrap(_retval);' elif retval_type == 'ownptr_same': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CppToC::WrapOwn(std::move(_retval));' + result += '\n return ' + ptr_class + 'CppToC_WrapOwn(std::move(_retval));' elif retval_type == 'ownptr_diff': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CToCpp::UnwrapOwn(std::move(_retval));' + result += '\n return ' + ptr_class + 'CToCpp_UnwrapOwn(std::move(_retval));' else: raise Exception('Unsupported return type %s in %s' % (retval_type, name)) if len(result) != result_len: result += '\n' - result += '}\n\n' + result += '}\n' return result -def make_cpptoc_function_impl(cls, funcs, existing, prefixname, defined_names, - base_scoped): +def make_cpptoc_function_impl(cls, funcs, existing, prefixname, suffixname, + defined_names, base_scoped, version, + version_finder): impl = '' + customized = False + for func in funcs: + if not version is None and isinstance(func, obj_function_virtual) and \ + not func.exists_at_version(version): + continue + + name = func.get_capi_name() if not prefixname is None: - name = prefixname + '_' + func.get_capi_name() + name = prefixname + '_' + name + if not suffixname is None: + name += '_' + suffixname + + if version is None: + pre, post = get_version_surround(func, long=True) else: - name = func.get_capi_name() + pre = post = '' + value = get_next_function_impl(existing, name) if not value is None \ and value['body'].find('// AUTO-GENERATED CONTENT') < 0: # an implementation exists that was not auto-generated - impl += make_cpptoc_function_impl_existing(cls, name, func, value, - defined_names) + customized = True + impl += pre + make_cpptoc_function_impl_existing( + cls, name, func, value, defined_names, version, + version_finder) + post + '\n' else: - impl += make_cpptoc_function_impl_new(cls, name, func, defined_names, - base_scoped) + impl += pre + make_cpptoc_function_impl_new( + cls, name, func, defined_names, base_scoped, version, + version_finder) + post + '\n' - return impl + if not customized and impl.find('// COULD NOT IMPLEMENT') >= 0: + customized = True + + return (impl, customized) def make_cpptoc_virtual_function_impl(header, cls, existing, prefixname, - defined_names, base_scoped): - funcs = [] - funcs.extend(cls.get_virtual_funcs()) + suffixname, defined_names, base_scoped, + version, version_finder): + # don't modify the original list + funcs = copy.copy(cls.get_virtual_funcs(version=version)) cur_cls = cls while True: parent_name = cur_cls.get_parent_name() if is_base_class(parent_name): break else: - parent_cls = header.get_class(parent_name, defined_names) + parent_cls = header.get_class(parent_name) if parent_cls is None: raise Exception('Class does not exist: ' + parent_name) - funcs.extend(parent_cls.get_virtual_funcs()) - cur_cls = header.get_class(parent_name, defined_names) + defined_names.append(parent_cls.get_capi_name(version)) + funcs.extend(parent_cls.get_virtual_funcs(version=version)) + cur_cls = header.get_class(parent_name) + defined_names.append(cur_cls.get_capi_name(version)) - return make_cpptoc_function_impl(cls, funcs, existing, prefixname, - defined_names, base_scoped) + return make_cpptoc_function_impl(cls, funcs, existing, prefixname, suffixname, + defined_names, base_scoped, version, + version_finder) -def make_cpptoc_virtual_function_assignment_block(funcs, offset, prefixname): +def make_cpptoc_virtual_function_assignment_block(cls, offset, prefixname, + suffixname, version): impl = '' + + funcs = cls.get_virtual_funcs(version_order=True, version=version) + for func in funcs: - name = func.get_capi_name() - impl += ' GetStruct()->' + offset + name + ' = ' + prefixname + '_' + name + ';\n' + # should only include methods directly declared in the current class + assert func.parent.get_name() == cls.get_name(), func.get_name() + if version is None: + pre, post = get_version_surround(func) + else: + if func.removed_at_version(version): + continue + pre = post = '' + name = oname = func.get_capi_name() + if not prefixname is None: + name = prefixname + '_' + name + if not suffixname is None: + name += '_' + suffixname + impl += pre + ' GetStruct()->' + offset + oname + ' = ' + name + ';\n' + post return impl -def make_cpptoc_virtual_function_assignment(header, cls, prefixname, - defined_names): - impl = make_cpptoc_virtual_function_assignment_block(cls.get_virtual_funcs(), - '', prefixname) +def make_cpptoc_virtual_function_assignment(header, cls, prefixname, suffixname, + defined_names, version, + version_finder): + impl = make_cpptoc_virtual_function_assignment_block(cls, '', prefixname, + suffixname, version) cur_cls = cls offset = '' @@ -535,17 +591,19 @@ def make_cpptoc_virtual_function_assignment(header, cls, prefixname, if is_base_class(parent_name): break else: - parent_cls = header.get_class(parent_name, defined_names) + parent_cls = header.get_class(parent_name) if parent_cls is None: raise Exception('Class does not exist: ' + parent_name) + defined_names.append(parent_cls.get_capi_name(version)) impl += make_cpptoc_virtual_function_assignment_block( - parent_cls.get_virtual_funcs(), offset, prefixname) - cur_cls = header.get_class(parent_name, defined_names) + parent_cls, offset, prefixname, suffixname, version) + cur_cls = header.get_class(parent_name) + defined_names.append(cur_cls.get_capi_name(version)) return impl -def make_cpptoc_unwrap_derived(header, cls, base_scoped): +def make_cpptoc_unwrap_derived(header, cls, base_scoped, version): # identify all classes that derive from cls derived_classes = [] cur_clsname = cls.get_name() @@ -561,39 +619,193 @@ def make_cpptoc_unwrap_derived(header, cls, base_scoped): if base_scoped: impl = ['', ''] for clsname in derived_classes: - impl[0] += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ - ' return '+clsname+'CppToC::UnwrapOwn(reinterpret_cast<'+\ - get_capi_name(clsname, True)+'*>(s));\n'+\ - ' }\n' - impl[1] += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ - ' return '+clsname+'CppToC::UnwrapRaw(reinterpret_cast<'+\ - get_capi_name(clsname, True)+'*>(s));\n'+\ - ' }\n' + derived_cls = header.get_class(clsname) + if version is None: + capiname = derived_cls.get_capi_name() + pre, post = get_version_surround(derived_cls) + else: + if not derived_cls.exists_at_version(version): + continue + capiname = derived_cls.get_capi_name(first_version=True) + pre = post = '' + + impl[0] += pre + ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ + ' return '+clsname+'CppToC_UnwrapOwn(reinterpret_cast<'+capiname+'*>(s));\n'+\ + ' }\n' + post + impl[1] += pre + ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ + ' return '+clsname+'CppToC_UnwrapRaw(reinterpret_cast<'+capiname+'*>(s));\n'+\ + ' }\n' + post else: impl = '' for clsname in derived_classes: - impl += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ - ' return '+clsname+'CppToC::Unwrap(reinterpret_cast<'+\ - get_capi_name(clsname, True)+'*>(s));\n'+\ - ' }\n' + derived_cls = header.get_class(clsname) + if version is None: + capiname = derived_cls.get_capi_name() + pre, post = get_version_surround(derived_cls) + else: + if not derived_cls.exists_at_version(version): + continue + capiname = derived_cls.get_capi_name(first_version=True) + pre = post = '' + + impl += pre + ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ + ' return '+clsname+'CppToC_Unwrap(reinterpret_cast<'+capiname+'*>(s));\n'+\ + ' }\n' + post return impl +def make_cpptoc_version_wrappers(header, cls, base_scoped, versions): + assert len(versions) > 0 + + clsname = cls.get_name() + typename = clsname + 'CppToC' + structname = cls.get_capi_name(version=versions[0]) + + rversions = sorted(versions, reverse=True) + + notreached = format_notreached( + True, + '" called with invalid version " << version', + default_retval='nullptr') + + impl = '' + + if base_scoped: + impl += structname + '* ' + typename + '_WrapOwn(CefOwnPtr<' + clsname + '> c) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::WrapOwn(std::move(c));\n' + else: + impl += ' return reinterpret_cast<' + structname + '*>(' + clsname + '_' + vstr + '_CppToC::WrapOwn(std::move(c)));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'std::pair, '+ structname + '*> ' + typename + '_WrapRaw(CefRawPtr<' + clsname + '> c) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::WrapRaw(std::move(c));\n' + else: + impl += ' auto [ownPtr, structPtr] = ' + clsname + '_' + vstr + '_CppToC::WrapRaw(std::move(c));\n' + \ + ' return std::make_pair(std::move(ownPtr), reinterpret_cast<' + structname + '*>(structPtr));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'CefOwnPtr<' + clsname + '> ' + typename + '_UnwrapOwn('+ structname + '* s) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::UnwrapOwn(s);\n' + else: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::UnwrapOwn(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(s));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'CefRawPtr<' + clsname + '> ' + typename + '_UnwrapRaw('+ structname + '* s) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::UnwrapRaw(s);\n' + else: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::UnwrapRaw(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(s));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'CefBaseScoped* ' + typename + '_GetWrapper('+ structname + '* s) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::GetWrapper(s);\n' + else: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::GetWrapper(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(s));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n' + else: + impl += structname + '* ' + typename + '_Wrap(CefRefPtr<' + clsname + '> c) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::Wrap(c);\n' + else: + impl += ' return reinterpret_cast<' + structname + '*>(' + clsname + '_' + vstr + '_CppToC::Wrap(c));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'CefRefPtr<' + clsname + '> ' + typename + '_Unwrap('+ structname + '* s) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::Unwrap(s);\n' + else: + impl += ' return ' + clsname + '_' + vstr + '_CppToC::Unwrap(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(s));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n' + + return impl + '\n' + + +def _version_finder(header, version, name, as_cpp=False): + assert version is None or isinstance(version, int), version + assert name[-1] != '*', name + + if as_cpp: + cls = header.get_class(name) + if cls is None: + return name + return cls.get_name(version=version) + + cls = header.get_capi_class(name) + if not cls is None: + return cls.get_capi_name(first_version=True) + return name + + def make_cpptoc_class_impl(header, clsname, impl): # structure names that have already been defined defined_names = header.get_defined_structs() # retrieve the class and populate the defined names - cls = header.get_class(clsname, defined_names) + cls = header.get_class(clsname) if cls is None: raise Exception('Class does not exist: ' + clsname) - capiname = cls.get_capi_name() prefixname = get_capi_name(clsname[3:], False) - # retrieve the existing virtual function implementations - existing = get_function_impls(impl, 'CEF_CALLBACK') - base_class_name = header.get_base_class_name(clsname) base_scoped = True if base_class_name == 'CefBaseScoped' else False if base_scoped: @@ -601,80 +813,143 @@ def make_cpptoc_class_impl(header, clsname, impl): else: template_class = 'CefCppToCRefCounted' - # generate virtual functions - virtualimpl = make_cpptoc_virtual_function_impl( - header, cls, existing, prefixname, defined_names, base_scoped) - if len(virtualimpl) > 0: - virtualimpl = '\nnamespace {\n\n// MEMBER FUNCTIONS - Body may be edited by hand.\n\n' + virtualimpl + '} // namespace' - - # the current class is already defined for static functions - defined_names.append(cls.get_capi_name()) + with_versions = cls.is_library_side() + versions = list(cls.get_all_versions()) if with_versions else (None,) # retrieve the existing static function implementations - existing = get_function_impls(impl, 'CEF_EXPORT') - - # generate static functions - staticimpl = make_cpptoc_function_impl(cls, - cls.get_static_funcs(), existing, None, - defined_names, base_scoped) - if len(staticimpl) > 0: - staticimpl = '\n// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n' + staticimpl + existing_static = get_function_impls(impl, 'CEF_EXPORT') - resultingimpl = staticimpl + virtualimpl - - # any derived classes can be unwrapped - unwrapderived = make_cpptoc_unwrap_derived(header, cls, base_scoped) + # retrieve the existing virtual function implementations + existing_virtual = get_function_impls(impl, 'CEF_CALLBACK') + + staticout = virtualout = '' + customized = False + first = True + idx = 0 + + for version in versions: + version_finder = functools.partial(_version_finder, header, + version) if with_versions else None + defined_names.append(cls.get_capi_name(version=version)) + + if first: + first = False + + # generate static functions + staticimpl, scustomized = make_cpptoc_function_impl( + cls, + cls.get_static_funcs(), existing_static, None, None, defined_names, + base_scoped, version, version_finder) + if len(staticimpl) > 0: + staticout += '// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n' + staticimpl + if scustomized: + customized = True + + if len(versions) > 1: + staticout += '// HELPER FUNCTIONS - Do not edit by hand.\n\n' + staticout += make_cpptoc_version_wrappers(header, cls, base_scoped, + versions) + + comment = '' if version is None else (' FOR VERSION %d' % version) + + suffixname = str(version) if (len(versions) > 1 and version > 0) else None + + # generate virtual functions + virtualimpl, vcustomized = make_cpptoc_virtual_function_impl( + header, cls, existing_virtual, prefixname, suffixname, defined_names, + base_scoped, version, version_finder) + if len(virtualimpl) > 0: + virtualout += 'namespace {\n\n// MEMBER FUNCTIONS' + comment + ' - Body may be edited by hand.\n\n' + \ + virtualimpl + '} // namespace\n\n' + if vcustomized: + customized = True + + # any derived classes can be unwrapped + unwrapderived = make_cpptoc_unwrap_derived(header, cls, base_scoped, + version) + + capiname = cls.get_capi_name(version=version) + typename = cls.get_name(version=version) + 'CppToC' + + const = '// CONSTRUCTOR' + comment + ' - Do not edit by hand.\n\n'+ \ + typename+'::'+typename+'() {\n' + + if not version is None: + if idx < len(versions) - 1: + condition = 'version < %d || version >= %d' % (version, versions[idx + + 1]) + else: + condition = 'version < %d' % version + + const += ' const int version = cef_api_version();\n' + \ + ' LOG_IF(FATAL, ' + condition + ') << __func__ << " called with invalid version " << version;\n\n' + + const += make_cpptoc_virtual_function_assignment(header, cls, prefixname, + suffixname, defined_names, + version, version_finder) + const += '}\n\n'+ \ + '// DESTRUCTOR' + comment + ' - Do not edit by hand.\n\n'+ \ + typename+'::~'+typename+'() {\n' + + if not cls.has_attrib('no_debugct_check') and not base_scoped: + const += ' shutdown_checker::AssertNotShutdown();\n' + + const += '}\n\n' + + parent_sig = template_class + '<' + typename + ', ' + clsname + ', ' + capiname + '>' + notreached = format_notreached( + with_versions, + '" called with unexpected class type " << type', + default_retval='nullptr') + + if base_scoped: + const += 'template<> CefOwnPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, '+capiname+'* s) {\n' + \ + unwrapderived[0] + \ + ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'template<> CefRawPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedRaw(CefWrapperType type, '+capiname+'* s) {\n' + \ + unwrapderived[1] + \ + ' ' + notreached + '\n'+ \ + '}\n\n' + else: + const += 'template<> CefRefPtr<'+clsname+'> '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+capiname+'* s) {\n' + \ + unwrapderived + \ + ' ' + notreached + '\n'+ \ + '}\n\n' - const = '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \ - clsname+'CppToC::'+clsname+'CppToC() {\n' - const += make_cpptoc_virtual_function_assignment(header, cls, prefixname, - defined_names) - const += '}\n\n'+ \ - '// DESTRUCTOR - Do not edit by hand.\n\n'+ \ - clsname+'CppToC::~'+clsname+'CppToC() {\n' + const += 'template<> CefWrapperType ' + parent_sig + '::kWrapperType = ' + get_wrapper_type_enum( + clsname) + ';\n\n' - if not cls.has_attrib('no_debugct_check') and not base_scoped: - const += ' shutdown_checker::AssertNotShutdown();\n' + virtualout += const + idx += 1 - const += '}\n\n' + out = staticout + virtualout # determine what includes are required by identifying what translation # classes are being used - includes = format_translation_includes(header, const + resultingimpl + - (unwrapderived[0] - if base_scoped else unwrapderived)) + includes = format_translation_includes( + header, + out + (unwrapderived[0] if base_scoped else unwrapderived), + with_versions=with_versions) # build the final output result = get_copyright() - result += includes + '\n' + resultingimpl + '\n' - - parent_sig = template_class + '<' + clsname + 'CppToC, ' + clsname + ', ' + capiname + '>' + result += includes + '\n' - if base_scoped: - const += 'template<> CefOwnPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, '+capiname+'* s) {\n' + \ - unwrapderived[0] + \ - ' DCHECK(false) << "Unexpected class type: " << type;\n'+ \ - ' return CefOwnPtr<'+clsname+'>();\n'+ \ - '}\n\n' + \ - 'template<> CefRawPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedRaw(CefWrapperType type, '+capiname+'* s) {\n' + \ - unwrapderived[1] + \ - ' DCHECK(false) << "Unexpected class type: " << type;\n'+ \ - ' return nullptr;\n'+ \ - '}\n\n' + if with_versions: + pre = post = '' else: - const += 'template<> CefRefPtr<'+clsname+'> '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+capiname+'* s) {\n' + \ - unwrapderived + \ - ' DCHECK(false) << "Unexpected class type: " << type;\n'+ \ - ' return nullptr;\n'+ \ - '}\n\n' + pre, post = get_version_surround(cls, long=True) + if len(pre) > 0: + result += pre + '\n' - const += 'template<> CefWrapperType ' + parent_sig + '::kWrapperType = ' + get_wrapper_type_enum( - clsname) + ';' + result += out + '\n' - result += '\n\n' + const + if len(post) > 0: + result += post + '\n' - return result + return (result, customized) def make_cpptoc_global_impl(header, impl): @@ -684,34 +959,36 @@ def make_cpptoc_global_impl(header, impl): # retrieve the existing global function implementations existing = get_function_impls(impl, 'CEF_EXPORT') + version_finder = functools.partial(_version_finder, header, None) + # generate global functions - impl = make_cpptoc_function_impl(None, - header.get_funcs(), existing, None, - defined_names, False) + impl, customized = make_cpptoc_function_impl(None, + header.get_funcs(), existing, + None, None, defined_names, False, + None, version_finder) if len(impl) > 0: impl = '\n// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n' + impl includes = '' # include required headers for global functions - filenames = [] + paths = set() for func in header.get_funcs(): filename = func.get_file_name() - if not filename in filenames: - includes += '#include "include/'+func.get_file_name()+'"\n' \ - '#include "include/capi/'+func.get_capi_file_name()+'"\n' - filenames.append(filename) + paths.add('include/' + func.get_file_name()) + paths.add('include/capi/' + func.get_capi_file_name(versions=True)) # determine what includes are required by identifying what translation # classes are being used - includes += format_translation_includes(header, impl) + includes += format_translation_includes( + header, impl, with_versions=True, other_includes=paths) # build the final output result = get_copyright() result += includes + '\n' + impl - return result + return (result, customized) def write_cpptoc_impl(header, clsname, dir): @@ -725,16 +1002,22 @@ def write_cpptoc_impl(header, clsname, dir): dir = os.path.dirname(os.path.join(dir, cls.get_file_name())) file = os.path.join(dir, get_capi_name(clsname[3:], False) + '_cpptoc.cc') + set_notify_context(file) + if path_exists(file): oldcontents = read_file(file) else: oldcontents = '' if clsname is None: - newcontents = make_cpptoc_global_impl(header, oldcontents) + newcontents, customized = make_cpptoc_global_impl(header, oldcontents) else: - newcontents = make_cpptoc_class_impl(header, clsname, oldcontents) - return (file, newcontents) + newcontents, customized = make_cpptoc_class_impl(header, clsname, + oldcontents) + + set_notify_context(None) + + return (file, newcontents, customized) # test the module diff --git a/tools/make_ctocpp_header.py b/tools/make_ctocpp_header.py index 708d7d61c1..2682851dfa 100644 --- a/tools/make_ctocpp_header.py +++ b/tools/make_ctocpp_header.py @@ -6,22 +6,25 @@ from cef_parser import * -def make_function_body_block(cls): +def make_function_body_block(cls, with_versions): impl = ' // ' + cls.get_name() + ' methods.\n' funcs = cls.get_virtual_funcs() for func in funcs: - impl += ' ' + func.get_cpp_proto() - if cls.is_client_side(): - impl += ' override;\n' + if func.parent.get_name() != cls.get_name(): + # skip methods that are not directly declared in the current class + continue + if with_versions: + pre = post = '' else: - impl += ' override;\n' + pre, post = get_version_surround(func) + impl += pre + ' ' + func.get_cpp_proto() + ' override;\n' + post return impl -def make_function_body(header, cls): - impl = make_function_body_block(cls) +def make_function_body(header, cls, with_versions): + impl = make_function_body_block(cls, with_versions) cur_cls = cls while True: @@ -34,7 +37,7 @@ def make_function_body(header, cls): raise Exception('Class does not exist: ' + parent_name) if len(impl) > 0: impl += '\n' - impl += make_function_body_block(parent_cls) + impl += make_function_body_block(parent_cls, with_versions) cur_cls = header.get_class(parent_name) return impl @@ -54,8 +57,6 @@ def make_ctocpp_header(header, clsname): defname += get_capi_name(clsname[3:], False) defname = defname.upper() - capiname = cls.get_capi_name() - result = get_copyright() result += '#ifndef CEF_LIBCEF_DLL_CTOCPP_'+defname+'_CTOCPP_H_\n'+ \ @@ -75,8 +76,10 @@ def make_ctocpp_header(header, clsname): #endif """ + with_versions = clientside + # build the function body - func_body = make_function_body(header, cls) + func_body = make_function_body(header, cls, with_versions) # include standard headers if func_body.find('std::map') > 0 or func_body.find('std::multimap') > 0: @@ -86,7 +89,7 @@ def make_ctocpp_header(header, clsname): # include the headers for this class result += '\n#include "include/'+cls.get_file_name()+'"'+ \ - '\n#include "include/capi/'+cls.get_capi_file_name()+'"\n' + '\n#include "include/capi/'+cls.get_capi_file_name(versions=with_versions)+'"\n' # include headers for any forward declared classes that are not in the same file declares = cls.get_forward_declares() @@ -94,7 +97,7 @@ def make_ctocpp_header(header, clsname): dcls = header.get_class(declare) if dcls.get_file_name() != cls.get_file_name(): result += '#include "include/'+dcls.get_file_name()+'"\n' \ - '#include "include/capi/'+dcls.get_capi_file_name()+'"\n' + '#include "include/capi/'+dcls.get_capi_file_name(versions=with_versions)+'"\n' base_class_name = header.get_base_class_name(clsname) base_scoped = True if base_class_name == 'CefBaseScoped' else False @@ -106,21 +109,66 @@ def make_ctocpp_header(header, clsname): template_class = 'CefCToCppRefCounted' result += '#include "libcef_dll/ctocpp/' + template_file + '"' - result += '\n\n// Wrap a C structure with a C++ class.\n' - if clientside: - result += '// This class may be instantiated and accessed DLL-side only.\n' + if with_versions: + pre = post = '' else: - result += '// This class may be instantiated and accessed wrapper-side only.\n' + pre, post = get_version_surround(cls, long=True) + if len(pre) > 0: + result += '\n\n' + pre.strip() - result += 'class '+clsname+'CToCpp\n'+ \ - ' : public ' + template_class + '<'+clsname+'CToCpp, '+clsname+', '+capiname+'> {\n'+ \ - ' public:\n'+ \ - ' '+clsname+'CToCpp();\n'+ \ - ' virtual ~'+clsname+'CToCpp();\n\n' + result += '\n\n' + + versions = cls.get_all_versions() if with_versions else (None,) + + for version in versions: + result += '// Wrap a C structure with a C++ class%s.\n' % \ + ('' if version is None else ' at API version %d' % version) + if clientside: + result += '// This class may be instantiated and accessed DLL-side only.\n' + else: + result += '// This class may be instantiated and accessed wrapper-side only.\n' - result += func_body - result += '};\n\n' + capiname = cls.get_capi_name(version=version) + if version is None: + typename = clsname + 'CToCpp' + else: + typename = clsname + '_%d_CToCpp' % version + + result += 'class '+typename+'\n'+ \ + ' : public ' + template_class + '<'+typename+', '+clsname+', '+capiname+'> {\n'+ \ + ' public:\n'+ \ + ' '+typename+'();\n'+ \ + ' virtual ~'+typename+'();\n\n' + + result += func_body + result += '};\n\n' + + typename = clsname + 'CToCpp' + if len(versions) > 1: + result += '// Helpers to return objects at the globally configured API version.\n' + structname = cls.get_capi_name(version=versions[0]) + if base_scoped: + result += 'CefOwnPtr<' + clsname + '> ' + typename + '_Wrap(' + structname + '* s);\n' + \ + structname + '* ' + typename + '_UnwrapOwn(CefOwnPtr<' + clsname + '> c);\n' + \ + structname + '* ' + typename + '_UnwrapRaw(CefRawPtr<' + clsname + '> c);\n\n' + else: + result += 'CefRefPtr<' + clsname + '> ' + typename + '_Wrap(' + structname + '* s);\n' + \ + structname + '* ' + typename + '_Unwrap(CefRefPtr<' + clsname + '> c);\n\n' + else: + if versions[0] is None: + targetname = clsname + 'CToCpp' + else: + targetname = clsname + '_%d_CToCpp' % versions[0] + if base_scoped: + result += 'constexpr auto ' + typename + '_Wrap = ' + targetname + '::Wrap;\n' + \ + 'constexpr auto ' + typename + '_UnwrapOwn = ' + targetname + '::UnwrapOwn;\n' + \ + 'constexpr auto ' + typename + '_UnwrapRaw = ' + targetname + '::UnwrapRaw;\n\n' + else: + result += 'constexpr auto ' + typename + '_Wrap = ' + targetname + '::Wrap;\n' + \ + 'constexpr auto ' + typename + '_Unwrap = ' + targetname + '::Unwrap;\n\n' + if len(post) > 0: + result += post + '\n' result += '#endif // CEF_LIBCEF_DLL_CTOCPP_' + defname + '_CTOCPP_H_' diff --git a/tools/make_ctocpp_impl.py b/tools/make_ctocpp_impl.py index 0d3e66b35c..e3acf2cecb 100644 --- a/tools/make_ctocpp_impl.py +++ b/tools/make_ctocpp_impl.py @@ -4,6 +4,7 @@ from __future__ import absolute_import from cef_parser import * +import functools def make_ctocpp_impl_proto(clsname, name, func, parts): @@ -25,42 +26,31 @@ def make_ctocpp_impl_proto(clsname, name, func, parts): return proto -def make_ctocpp_function_impl_existing(clsname, name, func, impl): - notify(name + ' has manual edits') +def make_ctocpp_function_impl_existing(cls, name, func, impl, version): + clsname = None if cls is None else cls.get_name(version=version) + notify_name = name if clsname is None else '%sCToCpp::%s' % (clsname, name) + + notify(notify_name + ' has manual edits') # retrieve the C++ prototype parts parts = func.get_cpp_parts(True) changes = format_translation_changes(impl, parts) if len(changes) > 0: - notify(name + ' prototype changed') + notify(notify_name + ' prototype changed') return make_ctocpp_impl_proto(clsname, name, func, parts)+'{'+ \ - changes+impl['body']+'\n}\n\n' + changes+impl['body']+'\n}\n' -def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): +def make_ctocpp_function_impl_new(cls, name, func, base_scoped, version, + version_finder): # Special handling for the CefShutdown global function. is_cef_shutdown = name == 'CefShutdown' and isinstance( func.parent, obj_header) - # build the C++ prototype - parts = func.get_cpp_parts(True) - result = make_ctocpp_impl_proto(clsname, name, func, parts) + ' {' - - if isinstance(func.parent, obj_class) and \ - not func.parent.has_attrib('no_debugct_check') and \ - not base_scoped: - result += '\n shutdown_checker::AssertNotShutdown();\n' - - if isinstance(func, obj_function_virtual): - # determine how the struct should be referenced - if clsname == func.parent.get_name(): - result += '\n ' + get_capi_name(clsname, - True) + '* _struct = GetStruct();' - else: - result += '\n '+func.parent.get_capi_name()+'* _struct = reinterpret_cast<'+\ - func.parent.get_capi_name()+'*>(GetStruct());' + clsname = None if cls is None else cls.get_name(version=version) + notify_name = name if clsname is None else '%sCToCpp::%s' % (clsname, name) invalid = [] @@ -83,23 +73,43 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): if len(retval_default) > 0: retval_default = ' ' + retval_default + # build the C++ prototype + parts = func.get_cpp_parts(True) + result = make_ctocpp_impl_proto(clsname, name, func, parts) + ' {' + + is_virtual = isinstance(func, obj_function_virtual) + + if is_virtual and not version is None and not func.exists_at_version(version): + notreached = format_notreached( + True, + '" should not be called at version %d"' % version, + default_retval=retval_default.strip()) + result += '\n // AUTO-GENERATED CONTENT' + \ + '\n ' + notreached + \ + '\n}\n' + return result + + if isinstance(func.parent, obj_class) and \ + not func.parent.has_attrib('no_debugct_check') and \ + not base_scoped: + result += '\n shutdown_checker::AssertNotShutdown();\n' + + if is_virtual: + # determine how the struct should be referenced + if cls.get_name() == func.parent.get_name(): + result += '\n auto* _struct = GetStruct();' + else: + result += '\n auto* _struct = reinterpret_cast<'+\ + func.parent.get_capi_name(version=version)+'*>(GetStruct());' + # add API hash check if func.has_attrib('api_hash_check'): - result += '\n const char* api_hash = cef_api_hash(0);'\ - '\n if (strcmp(api_hash, CEF_API_HASH_PLATFORM)) {'\ - '\n // The libcef API hash does not match the current header API hash.'\ - '\n DCHECK(false);'\ - '\n return'+retval_default+';'\ - '\n }\n' - - if isinstance(func, obj_function_virtual): - # add the structure size check - result += '\n if (CEF_MEMBER_MISSING(_struct, ' + func.get_capi_name() + ')) {'\ - '\n return' + retval_default + ';\n'\ - '\n }\n' + result += '\n const char* api_hash = cef_api_hash(CEF_API_VERSION, 0);'\ + '\n CHECK(!strcmp(api_hash, CEF_API_HASH_PLATFORM)) <<'\ + '\n "API hashes for libcef and libcef_dll_wrapper do not match.";\n' if len(invalid) > 0: - notify(name + ' could not be autogenerated') + notify(notify_name + ' could not be autogenerated') # code could not be auto-generated result += '\n // BEGIN DELETE BEFORE MODIFYING' result += '\n // AUTO-GENERATED CONTENT' @@ -209,38 +219,39 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): params.append(arg_name + '.GetWritableStruct()') elif arg_type == 'refptr_same': ptr_class = arg.get_type().get_ptr_type() - params.append(ptr_class + 'CToCpp::Unwrap(' + arg_name + ')') + params.append(ptr_class + 'CToCpp_Unwrap(' + arg_name + ')') elif arg_type == 'ownptr_same': ptr_class = arg.get_type().get_ptr_type() - params.append(ptr_class + 'CToCpp::UnwrapOwn(std::move(' + arg_name + - '))') + params.append(ptr_class + 'CToCpp_UnwrapOwn(std::move(' + arg_name + '))') elif arg_type == 'rawptr_same': ptr_class = arg.get_type().get_ptr_type() - params.append(ptr_class + 'CToCpp::UnwrapRaw(' + arg_name + ')') + params.append(ptr_class + 'CToCpp_UnwrapRaw(' + arg_name + ')') elif arg_type == 'refptr_diff': ptr_class = arg.get_type().get_ptr_type() - params.append(ptr_class + 'CppToC::Wrap(' + arg_name + ')') + params.append(ptr_class + 'CppToC_Wrap(' + arg_name + ')') elif arg_type == 'ownptr_diff': ptr_class = arg.get_type().get_ptr_type() - params.append(ptr_class + 'CppToC::WrapOwn(std::move(' + arg_name + '))') + params.append(ptr_class + 'CppToC_WrapOwn(std::move(' + arg_name + '))') elif arg_type == 'rawptr_diff': ptr_class = arg.get_type().get_ptr_type() result += comment+\ - '\n CefOwnPtr<'+ptr_class+'CppToC> '+arg_name+'Ptr('+ptr_class+'CppToC::WrapRaw('+arg_name+'));' - params.append(arg_name + 'Ptr->GetStruct()') + '\n auto ['+arg_name+'Ptr, '+arg_name+'Struct] = '+ptr_class+'CppToC_WrapRaw('+arg_name+');' + params.append(arg_name + 'Struct') elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref': ptr_class = arg.get_type().get_ptr_type() ptr_struct = arg.get_type().get_result_ptr_type_root() + if not version_finder is None: + ptr_struct = version_finder(ptr_struct) if arg_type == 'refptr_same_byref': - assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + ')' + assign = ptr_class + 'CToCpp_Unwrap(' + arg_name + ')' else: - assign = ptr_class + 'CppToC::Wrap(' + arg_name + ')' + assign = ptr_class + 'CppToC_Wrap(' + arg_name + ')' result += comment+\ '\n '+ptr_struct+'* '+arg_name+'Struct = NULL;'\ '\n if ('+arg_name+'.get()) {'\ '\n '+arg_name+'Struct = '+assign+';'\ '\n }'\ - '\n '+ptr_struct+'* '+arg_name+'Orig = '+arg_name+'Struct;' + '\n auto* '+arg_name+'Orig = '+arg_name+'Struct;' params.append('&' + arg_name + 'Struct') elif arg_type == 'string_vec_byref' or arg_type == 'string_vec_byref_const': result += comment+\ @@ -270,12 +281,15 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref': count_func = arg.get_attrib_count_func() vec_type = arg.get_type().get_result_vector_type_root() + if not version_finder is None: + vec_type = version_finder(vec_type) + if arg_type == 'refptr_vec_same_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + '[i])' + assign = ptr_class + 'CToCpp_Unwrap(' + arg_name + '[i])' elif arg_type == 'refptr_vec_diff_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CppToC::Wrap(' + arg_name + '[i])' + assign = ptr_class + 'CppToC_Wrap(' + arg_name + '[i])' else: assign = arg_name + '[i]' result += comment+\ @@ -301,18 +315,21 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): arg_type == 'rawptr_vec_same_byref_const' or arg_type == 'rawptr_vec_diff_byref_const': count_func = arg.get_attrib_count_func() vec_type = arg.get_type().get_result_vector_type_root() + if not version_finder is None: + vec_type = version_finder(vec_type) + if arg_type == 'simple_vec_byref_const' or arg_type == 'bool_vec_byref_const': assign = arg_name + '[i]' else: ptr_class = arg.get_type().get_ptr_type() if arg_type == 'refptr_vec_same_byref_const': - assign = ptr_class + 'CToCpp::Unwrap(' + arg_name + '[i])' + assign = ptr_class + 'CToCpp_Unwrap(' + arg_name + '[i])' elif arg_type == 'refptr_vec_diff_byref_const': - assign = ptr_class + 'CppToC::Wrap(' + arg_name + '[i])' + assign = ptr_class + 'CppToC_Wrap(' + arg_name + '[i])' elif arg_type == 'rawptr_vec_same_byref_const': - assign = ptr_class + 'CToCpp::UnwrapRaw(' + arg_name + '[i])' + assign = ptr_class + 'CToCpp_UnwrapRaw(' + arg_name + '[i])' elif arg_type == 'rawptr_vec_diff_byref_const': - assign = ptr_class + 'CppToC::WrapRaw(' + arg_name + '[i]).release()->GetStruct()' + assign = ptr_class + 'CppToC_WrapRawAndRelease(' + arg_name + '[i])' result += comment+\ '\n const size_t '+arg_name+'Count = '+arg_name+'.size();'\ '\n '+vec_type+'* '+arg_name+'List = NULL;'\ @@ -348,8 +365,7 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): result += 'cef_string_userfree_t' elif retval_type == 'refptr_same' or retval_type == 'refptr_diff' or \ retval_type == 'ownptr_same' or retval_type == 'ownptr_diff': - ptr_struct = retval.get_type().get_result_ptr_type_root() - result += ptr_struct + '*' + result += 'auto*' else: raise Exception('Unsupported return type %s in %s' % (retval_type, name)) @@ -390,11 +406,10 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): '\n }' elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref': ptr_class = arg.get_type().get_ptr_type() - ptr_struct = arg.get_type().get_result_ptr_type_root() if arg_type == 'refptr_same_byref': - assign = ptr_class + 'CToCpp::Wrap(' + arg_name + 'Struct)' + assign = ptr_class + 'CToCpp_Wrap(' + arg_name + 'Struct)' else: - assign = ptr_class + 'CppToC::Unwrap(' + arg_name + 'Struct)' + assign = ptr_class + 'CppToC_Unwrap(' + arg_name + 'Struct)' result += comment+\ '\n if ('+arg_name+'Struct) {'\ '\n if ('+arg_name+'Struct != '+arg_name+'Orig) {'\ @@ -442,13 +457,13 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): elif arg_type == 'simple_vec_byref' or arg_type == 'bool_vec_byref' or \ arg_type == 'refptr_vec_same_byref' or arg_type == 'refptr_vec_diff_byref': count_func = arg.get_attrib_count_func() - vec_type = arg.get_type().get_result_vector_type_root() + if arg_type == 'refptr_vec_same_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CToCpp::Wrap(' + arg_name + 'List[i])' + assign = ptr_class + 'CToCpp_Wrap(' + arg_name + 'List[i])' elif arg_type == 'refptr_vec_diff_byref': ptr_class = arg.get_type().get_ptr_type() - assign = ptr_class + 'CppToC::Unwrap(' + arg_name + 'List[i])' + assign = ptr_class + 'CppToC_Unwrap(' + arg_name + 'List[i])' elif arg_type == 'bool_vec_byref': assign = arg_name + 'List[i]?true:false' else: @@ -468,7 +483,7 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): if arg_type == 'rawptr_vec_diff_byref_const': result += '\n if ('+arg_name+'Count > 0) {'\ '\n for (size_t i = 0; i < '+arg_name+'Count; ++i) {'\ - '\n delete '+ptr_class+'CppToC::GetWrapper('+arg_name+'List[i]);'\ + '\n delete '+ptr_class+'CppToC_GetWrapper('+arg_name+'List[i]);'\ '\n }'\ '\n }' result += '\n if ('+arg_name+'List) {'\ @@ -497,43 +512,59 @@ def make_ctocpp_function_impl_new(clsname, name, func, base_scoped): '\n return _retvalStr;' elif retval_type == 'refptr_same' or retval_type == 'ownptr_same': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CToCpp::Wrap(_retval);' + result += '\n return ' + ptr_class + 'CToCpp_Wrap(_retval);' elif retval_type == 'refptr_diff': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CppToC::Unwrap(_retval);' + result += '\n return ' + ptr_class + 'CppToC_Unwrap(_retval);' elif retval_type == 'ownptr_diff': ptr_class = retval.get_type().get_ptr_type() - result += '\n return ' + ptr_class + 'CppToC::UnwrapOwn(_retval);' + result += '\n return ' + ptr_class + 'CppToC_UnwrapOwn(_retval);' else: raise Exception('Unsupported return type %s in %s' % (retval_type, name)) if len(result) != result_len: result += '\n' - result += '}\n\n' + result += '}\n' return result -def make_ctocpp_function_impl(clsname, funcs, existing, base_scoped): +def make_ctocpp_function_impl(cls, funcs, existing, base_scoped, version, + version_finder): impl = '' + customized = False + for func in funcs: name = func.get_name() value = get_next_function_impl(existing, name) + + if version is None: + pre, post = get_version_surround(func, long=True) + else: + pre = post = '' + if not value is None \ and value['body'].find('// AUTO-GENERATED CONTENT') < 0: # an implementation exists that was not auto-generated - impl += make_ctocpp_function_impl_existing(clsname, name, func, value) + customized = True + impl += pre + make_ctocpp_function_impl_existing(cls, name, func, value, + version) + post + '\n' else: - impl += make_ctocpp_function_impl_new(clsname, name, func, base_scoped) + impl += pre + make_ctocpp_function_impl_new( + cls, name, func, base_scoped, version, version_finder) + post + '\n' - return impl + if not customized and impl.find('// COULD NOT IMPLEMENT') >= 0: + customized = True + + return (impl, customized) -def make_ctocpp_virtual_function_impl(header, cls, existing, base_scoped): - impl = make_ctocpp_function_impl(cls.get_name(), - cls.get_virtual_funcs(), existing, - base_scoped) +def make_ctocpp_virtual_function_impl(header, cls, existing, base_scoped, + version, version_finder): + impl, customized = make_ctocpp_function_impl( + cls, + cls.get_virtual_funcs(), existing, base_scoped, version, version_finder) cur_cls = cls while True: @@ -544,18 +575,28 @@ def make_ctocpp_virtual_function_impl(header, cls, existing, base_scoped): parent_cls = header.get_class(parent_name) if parent_cls is None: raise Exception('Class does not exist: ' + parent_name) - impl += make_ctocpp_function_impl(cls.get_name(), - parent_cls.get_virtual_funcs(), - existing, base_scoped) + pimpl, pcustomized = make_ctocpp_function_impl( + cls, + parent_cls.get_virtual_funcs(), existing, base_scoped, version, + version_finder) + impl += pimpl + if pcustomized and not customized: + customized = True cur_cls = header.get_class(parent_name) - return impl + return (impl, customized) -def make_ctocpp_unwrap_derived(header, cls, base_scoped): +def make_ctocpp_unwrap_derived(header, cls, base_scoped, version): # identify all classes that derive from cls derived_classes = [] clsname = cls.get_name() + + if version is None: + capiname = cls.get_capi_name() + else: + capiname = cls.get_capi_name(version=version) + allclasses = header.get_classes() for cur_cls in allclasses: if cur_cls.get_name() == clsname: @@ -568,34 +609,156 @@ def make_ctocpp_unwrap_derived(header, cls, base_scoped): if base_scoped: impl = ['', ''] for clsname in derived_classes: - impl[0] += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ - ' return reinterpret_cast<'+get_capi_name(cls.get_name(), True)+'*>('+\ - clsname+'CToCpp::UnwrapOwn(CefOwnPtr<'+clsname+'>(reinterpret_cast<'+clsname+'*>(c.release()))));\n'+\ - ' }\n' - impl[1] += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ - ' return reinterpret_cast<'+get_capi_name(cls.get_name(), True)+'*>('+\ - clsname+'CToCpp::UnwrapRaw(CefRawPtr<'+clsname+'>(reinterpret_cast<'+clsname+'*>(c))));\n'+\ - ' }\n' + derived_cls = header.get_class(clsname) + if version is None: + pre, post = get_version_surround(derived_cls) + else: + if not derived_cls.exists_at_version(version): + continue + pre = post = '' + + impl[0] += pre + ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ + ' return reinterpret_cast<'+capiname+'*>('+\ + clsname+'CToCpp_UnwrapOwn(CefOwnPtr<'+clsname+'>(reinterpret_cast<'+clsname+'*>(c.release()))));\n'+\ + ' }\n' + post + impl[1] += pre + ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ + ' return reinterpret_cast<'+capiname+'*>('+\ + clsname+'CToCpp_UnwrapRaw(CefRawPtr<'+clsname+'>(reinterpret_cast<'+clsname+'*>(c))));\n'+\ + ' }\n' + post else: impl = '' for clsname in derived_classes: - impl += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ - ' return reinterpret_cast<'+get_capi_name(cls.get_name(), True)+'*>('+\ - clsname+'CToCpp::Unwrap(reinterpret_cast<'+clsname+'*>(c)));\n'+\ - ' }\n' + derived_cls = header.get_class(clsname) + if version is None: + pre, post = get_version_surround(derived_cls) + else: + if not derived_cls.exists_at_version(version): + continue + pre = post = '' + + impl += pre + ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\ + ' return reinterpret_cast<'+capiname+'*>('+\ + clsname+'CToCpp_Unwrap(reinterpret_cast<'+clsname+'*>(c)));\n'+\ + ' }\n' + post return impl +def make_ctocpp_version_wrappers(header, cls, base_scoped, versions): + assert len(versions) > 0 + + clsname = cls.get_name() + typename = clsname + 'CToCpp' + structname = cls.get_capi_name(version=versions[0]) + + rversions = sorted(versions, reverse=True) + + notreached = format_notreached( + True, + '" called with invalid version " << version', + default_retval='nullptr') + + impl = '' + + if base_scoped: + impl += 'CefOwnPtr<' + clsname + '> ' + typename + '_Wrap(' + structname + '* s) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::Wrap(s);\n' + else: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::Wrap(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(s));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + structname + '* ' + typename + '_UnwrapOwn(CefOwnPtr<' + clsname + '> c) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::UnwrapOwn(std::move(c));\n' + else: + impl += ' return reinterpret_cast<' + structname + '*>(' + clsname + '_' + vstr + '_CToCpp::UnwrapOwn(std::move(c)));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + structname + '* ' + typename + '_UnwrapRaw(CefRawPtr<' + clsname + '> c) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::UnwrapRaw(std::move(c));\n' + else: + impl += ' return reinterpret_cast<' + structname + '*>(' + clsname + '_' + vstr + '_CToCpp::UnwrapRaw(std::move(c)));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n' + else: + impl += 'CefRefPtr<' + clsname + '> ' + typename + '_Wrap(' + structname + '* s) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::Wrap(s);\n' + else: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::Wrap(reinterpret_cast<' + cls.get_capi_name( + version) + '*>(s));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n\n' + \ + structname + '* ' + typename + '_Unwrap(CefRefPtr<' + clsname + '> c) {\n' + \ + ' const int version = cef_api_version();\n' + + for version in rversions: + vstr = str(version) + impl += ' if (version >= ' + vstr + ') {\n' + if versions[0] == version: + impl += ' return ' + clsname + '_' + vstr + '_CToCpp::Unwrap(c);\n' + else: + impl += ' return reinterpret_cast<' + structname + '*>(' + clsname + '_' + vstr + '_CToCpp::Unwrap(c));\n' + impl += ' }\n' + + impl += ' ' + notreached + '\n'+ \ + '}\n' + + return impl + '\n' + + +def _version_finder(header, version, name): + assert version is None or isinstance(version, int), version + + # normalize ptr values + if name[-1] == '*': + name = name[0:-1] + suffix = '*' + else: + suffix = '' + + cls = header.get_capi_class(name) + if not cls is None: + name = cls.get_capi_name(first_version=True) + + return name + suffix + + def make_ctocpp_class_impl(header, clsname, impl): cls = header.get_class(clsname) if cls is None: raise Exception('Class does not exist: ' + clsname) - capiname = cls.get_capi_name() - - # retrieve the existing virtual function implementations - existing = get_function_impls(impl, clsname + 'CToCpp::') - base_class_name = header.get_base_class_name(clsname) base_scoped = True if base_class_name == 'CefBaseScoped' else False if base_scoped: @@ -603,75 +766,135 @@ def make_ctocpp_class_impl(header, clsname, impl): else: template_class = 'CefCToCppRefCounted' - # generate virtual functions - virtualimpl = make_ctocpp_virtual_function_impl(header, cls, existing, - base_scoped) - if len(virtualimpl) > 0: - virtualimpl = '\n// VIRTUAL METHODS - Body may be edited by hand.\n\n' + virtualimpl + with_versions = cls.is_client_side() + versions = list(cls.get_all_versions()) if with_versions else (None,) # retrieve the existing static function implementations - existing = get_function_impls(impl, clsname + '::') - - # generate static functions - staticimpl = make_ctocpp_function_impl(clsname, - cls.get_static_funcs(), existing, - base_scoped) - if len(staticimpl) > 0: - staticimpl = '\n// STATIC METHODS - Body may be edited by hand.\n\n' + staticimpl - - resultingimpl = staticimpl + virtualimpl - - # any derived classes can be unwrapped - unwrapderived = make_ctocpp_unwrap_derived(header, cls, base_scoped) + existing_static = get_function_impls(impl, clsname + '::') + + staticout = virtualout = '' + customized = False + first = True + idx = 0 + + for version in versions: + version_finder = functools.partial(_version_finder, header, + version) if with_versions else None + + if first: + first = False + + # generate static functions + staticimpl, scustomized = make_ctocpp_function_impl( + cls, + cls.get_static_funcs(), existing_static, base_scoped, version, + version_finder) + if len(staticimpl) > 0: + staticout += '\n// STATIC METHODS - Body may be edited by hand.\n\n' + staticimpl + if scustomized: + customized = True + + if len(versions) > 1: + staticout += '// HELPER FUNCTIONS - Do not edit by hand.\n\n' + staticout += make_ctocpp_version_wrappers(header, cls, base_scoped, + versions) + + comment = '' if version is None else (' FOR VERSION %d' % version) + typename = cls.get_name(version=version) + 'CToCpp' + + # retrieve the existing virtual function implementations + existing_virtual = get_function_impls(impl, typename + '::') + + # generate virtual functions + virtualimpl, vcustomized = make_ctocpp_virtual_function_impl( + header, cls, existing_virtual, base_scoped, version, version_finder) + if len(virtualimpl) > 0: + virtualout += '\n// VIRTUAL METHODS' + comment + ' - Body may be edited by hand.\n\n' + virtualimpl + if vcustomized: + customized = True + + # any derived classes can be unwrapped + unwrapderived = make_ctocpp_unwrap_derived(header, cls, base_scoped, + version) + + capiname = cls.get_capi_name(version=version) + + const = '// CONSTRUCTOR' + comment + ' - Do not edit by hand.\n\n'+ \ + typename+'::'+typename+'() {\n' + + if not version is None: + if idx < len(versions) - 1: + condition = 'version < %d || version >= %d' % (version, versions[idx + + 1]) + else: + condition = 'version < %d' % version + + const += ' const int version = cef_api_version();\n' + \ + ' LOG_IF(FATAL, ' + condition + ') << __func__ << " called with invalid version " << version;\n' + + const += '}\n\n'+ \ + '// DESTRUCTOR' + comment + ' - Do not edit by hand.\n\n'+ \ + typename+'::~'+typename+'() {\n' + + if not cls.has_attrib('no_debugct_check') and not base_scoped: + const += ' shutdown_checker::AssertNotShutdown();\n' + + const += '}\n\n' + + parent_sig = template_class + '<' + typename + ', ' + clsname + ', ' + capiname + '>' + notreached = format_notreached( + with_versions, + '" called with unexpected class type " << type', + default_retval='nullptr') + + if base_scoped: + const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, CefOwnPtr<'+clsname+'> c) {\n'+ \ + unwrapderived[0] + \ + ' ' + notreached + '\n'+ \ + '}\n\n' + \ + 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedRaw(CefWrapperType type, CefRawPtr<'+clsname+'> c) {\n'+ \ + unwrapderived[1] + \ + ' ' + notreached + '\n'+ \ + '}\n\n' + else: + const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+clsname+'* c) {\n'+ \ + unwrapderived + \ + ' ' + notreached + '\n'+ \ + '}\n\n' - const = '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \ - clsname+'CToCpp::'+clsname+'CToCpp() {\n'+ \ - '}\n\n'+ \ - '// DESTRUCTOR - Do not edit by hand.\n\n'+ \ - clsname+'CToCpp::~'+clsname+'CToCpp() {\n' + const += 'template<> CefWrapperType ' + parent_sig + '::kWrapperType = ' + get_wrapper_type_enum( + clsname) + ';\n\n' - if not cls.has_attrib('no_debugct_check') and not base_scoped: - const += ' shutdown_checker::AssertNotShutdown();\n' + virtualout += const + idx += 1 - const += '}\n\n' + out = staticout + virtualout # determine what includes are required by identifying what translation # classes are being used - includes = format_translation_includes(header, const + resultingimpl + - (unwrapderived[0] - if base_scoped else unwrapderived)) + includes = format_translation_includes( + header, + out + (unwrapderived[0] if base_scoped else unwrapderived), + with_versions=with_versions) # build the final output result = get_copyright() - result += includes + '\n' + resultingimpl + '\n' + result += includes + '\n' - parent_sig = template_class + '<' + clsname + 'CToCpp, ' + clsname + ', ' + capiname + '>' - - if base_scoped: - const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, CefOwnPtr<'+clsname+'> c) {\n'+ \ - unwrapderived[0] + \ - ' DCHECK(false) << "Unexpected class type: " << type;\n'+ \ - ' return nullptr;\n'+ \ - '}\n\n' + \ - 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedRaw(CefWrapperType type, CefRawPtr<'+clsname+'> c) {\n'+ \ - unwrapderived[1] + \ - ' DCHECK(false) << "Unexpected class type: " << type;\n'+ \ - ' return nullptr;\n'+ \ - '}\n\n' + if with_versions: + pre = post = '' else: - const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+clsname+'* c) {\n'+ \ - unwrapderived + \ - ' DCHECK(false) << "Unexpected class type: " << type;\n'+ \ - ' return nullptr;\n'+ \ - '}\n\n' + pre, post = get_version_surround(cls, long=True) + if len(pre) > 0: + result += pre + '\n' - const += 'template<> CefWrapperType ' + parent_sig + '::kWrapperType = ' + get_wrapper_type_enum( - clsname) + ';' + result += out + '\n' - result += const + if len(post) > 0: + result += post + '\n' - return result + return (result, customized) def make_ctocpp_global_impl(header, impl): @@ -679,31 +902,31 @@ def make_ctocpp_global_impl(header, impl): existing = get_function_impls(impl, 'CEF_GLOBAL') # generate static functions - impl = make_ctocpp_function_impl(None, header.get_funcs(), existing, False) + impl, customized = make_ctocpp_function_impl(None, + header.get_funcs(), existing, + False, None, None) if len(impl) > 0: impl = '\n// GLOBAL METHODS - Body may be edited by hand.\n\n' + impl includes = '' # include required headers for global functions - filenames = [] + paths = set() for func in header.get_funcs(): filename = func.get_file_name() - if not filename in filenames: - includes += '#include "include/'+func.get_file_name()+'"\n' \ - '#include "include/capi/'+func.get_capi_file_name()+'"\n' - filenames.append(filename) + paths.add('include/' + func.get_file_name()) + paths.add('include/capi/' + func.get_capi_file_name()) # determine what includes are required by identifying what translation # classes are being used - includes += format_translation_includes(header, impl) + includes += format_translation_includes(header, impl, other_includes=paths) # build the final output result = get_copyright() result += includes + '\n// Define used to facilitate parsing.\n#define CEF_GLOBAL\n\n' + impl - return result + return (result, customized) def write_ctocpp_impl(header, clsname, dir): @@ -717,16 +940,22 @@ def write_ctocpp_impl(header, clsname, dir): dir = os.path.dirname(os.path.join(dir, cls.get_file_name())) file = os.path.join(dir, get_capi_name(clsname[3:], False) + '_ctocpp.cc') + set_notify_context(file) + if path_exists(file): oldcontents = read_file(file) else: oldcontents = '' if clsname is None: - newcontents = make_ctocpp_global_impl(header, oldcontents) + newcontents, customized = make_ctocpp_global_impl(header, oldcontents) else: - newcontents = make_ctocpp_class_impl(header, clsname, oldcontents) - return (file, newcontents) + newcontents, customized = make_ctocpp_class_impl(header, clsname, + oldcontents) + + set_notify_context(None) + + return (file, newcontents, customized) # test the module diff --git a/tools/make_distrib.py b/tools/make_distrib.py index 883966fb0f..74c24030cb 100644 --- a/tools/make_distrib.py +++ b/tools/make_distrib.py @@ -6,6 +6,7 @@ from __future__ import print_function from bazel_util import bazel_substitute, bazel_last_error, bazel_set_quiet from cef_version import VersionFormatter +from clang_util import clang_format_inplace from date_util import * from exec_util import exec_cmd from file_util import * @@ -226,7 +227,12 @@ def transfer_doxyfile(dst_dir, quiet): sys.stdout.write('Creating Doxyfile file.\n') -def transfer_gypi_files(src_dir, gypi_paths, gypi_path_prefix, dst_dir, quiet): +def transfer_gypi_files(src_dir, + gypi_paths, + gypi_path_prefix, + dst_dir, + quiet, + format=False): """ Transfer files from one location to another. """ for path in gypi_paths: src = os.path.join(src_dir, path) @@ -235,6 +241,11 @@ def transfer_gypi_files(src_dir, gypi_paths, gypi_path_prefix, dst_dir, quiet): make_dir(dst_path, quiet) copy_file(src, dst, quiet) + # Apply clang-format for C/C++ files. + if format and os.path.splitext(dst)[1][1:] in ('c', 'cc', 'cpp', 'h'): + print(dst) + clang_format_inplace(dst) + def extract_toolchain_cmd(build_dir, exe_name, @@ -925,17 +936,19 @@ def print_error(msg): transfer_gypi_files(cef_dir, cef_paths2['includes_wrapper'], \ 'include/', include_dir, options.quiet) transfer_gypi_files(cef_dir, cef_paths['autogen_cpp_includes'], \ - 'include/', include_dir, options.quiet) + 'include/', include_dir, options.quiet, format=True) transfer_gypi_files(cef_dir, cef_paths['autogen_capi_includes'], \ - 'include/', include_dir, options.quiet) + 'include/', include_dir, options.quiet, format=True) # Transfer generated include files. generated_includes = [ + 'cef_api_versions.h', 'cef_color_ids.h', 'cef_command_ids.h', 'cef_config.h', 'cef_pack_resources.h', 'cef_pack_strings.h', + 'cef_version.h', ] for include in generated_includes: # Debug and Release build should be the same so grab whichever exists. @@ -953,7 +966,7 @@ def print_error(msg): transfer_gypi_files(cef_dir, cef_paths2['libcef_dll_wrapper_sources_common'], \ 'libcef_dll/', libcef_dll_dir, options.quiet) transfer_gypi_files(cef_dir, cef_paths['autogen_client_side'], \ - 'libcef_dll/', libcef_dll_dir, options.quiet) + 'libcef_dll/', libcef_dll_dir, options.quiet, format=True) if mode == 'standard' or mode == 'minimal': # transfer additional files diff --git a/tools/make_gypi_file.py b/tools/make_gypi_file.py index da66bc0057..c41f2e6630 100644 --- a/tools/make_gypi_file.py +++ b/tools/make_gypi_file.py @@ -37,6 +37,13 @@ def make_gypi_file(header): result += " 'include/capi/" + get_capi_file_name(filename) + "',\n" result += " ],\n" + # capi version includes + result += " 'autogen_capi_versions_includes': [\n" + for filename in filenames: + result += " 'include/capi/" + get_capi_file_name( + filename, versions=True) + "',\n" + result += " ],\n" + classes = sorted(header.get_class_names()) # library side includes diff --git a/tools/make_libcef_dll_dylib_impl.py b/tools/make_libcef_dll_dylib_impl.py index 59f788835a..941bc01185 100644 --- a/tools/make_libcef_dll_dylib_impl.py +++ b/tools/make_libcef_dll_dylib_impl.py @@ -10,7 +10,8 @@ # Other headers that export C API functions. OTHER_HEADERS = [ 'cef_api_hash.h', - 'cef_version.h', + 'cef_id_mappers.h', + 'cef_version_info.h', 'internal/cef_dump_without_crashing_internal.h', 'internal/cef_logging_internal.h', 'internal/cef_string_list.h', @@ -37,12 +38,11 @@ def make_libcef_dll_dylib_impl_parts(name, retval, args): declare = 'decltype(&%s) %s;\n' % (name, name) - init = ' INIT_ENTRY(%s);' % name + init = 'INIT_ENTRY(%s);\n' % name impl = """NO_SANITIZE("cfi-icall") %s %s(%s) { %sg_libcef_pointers.%s(%s); } - """ % (retval, name, ', '.join(args), 'return ' if retval != 'void' else '', name, arg_names) @@ -70,9 +70,10 @@ def make_libcef_dll_dylib_impl(header): # Include required headers for global functions. for func in header.get_funcs(): declare, init, impl = make_libcef_dll_dylib_impl_func(func) - ptr_declare += declare - ptr_init += init - ptr_impl += impl + pre, post = get_version_surround(func) + ptr_declare += pre + declare + post + ptr_init += pre + init + post + ptr_impl += pre + impl + post + '\n' filename = func.get_file_name() if not filename in filenames: @@ -85,9 +86,13 @@ def make_libcef_dll_dylib_impl(header): funcs = cls.get_static_funcs() for func in funcs: declare, init, impl = make_libcef_dll_dylib_impl_func(func) - ptr_declare += declare - ptr_init += init - ptr_impl += impl + pre1, post1 = get_version_surround(func) + pre2, post2 = get_version_surround(cls) + pre = pre1 + pre2 + post = post1 + post2 + ptr_declare += pre + declare + post + ptr_init += pre + init + post + ptr_impl += pre + impl + post + '\n' if len(funcs) > 0: filename = cls.get_file_name() @@ -106,7 +111,7 @@ def make_libcef_dll_dylib_impl(header): func['name'], func['retval'], func['args']) ptr_declare += declare ptr_init += init - ptr_impl += impl + ptr_impl += impl + '\n' includes.append('#include "include/%s"' % other) @@ -202,7 +207,9 @@ def write_libcef_dll_dylib_impl(header, file): # Create the header object. Should match the logic in translator.py. header = obj_header() header.set_root_directory(cpp_header_dir) - excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h'] + excluded_files = [ + 'cef_api_hash.h', 'cef_application_mac.h', 'cef_version_info.h' + ] header.add_directory(cpp_header_dir, excluded_files) header.add_directory(os.path.join(cpp_header_dir, 'test')) header.add_directory(os.path.join(cpp_header_dir, 'views')) diff --git a/tools/make_pack_header.py b/tools/make_pack_header.py index 9d371bc828..c7a300c41c 100644 --- a/tools/make_pack_header.py +++ b/tools/make_pack_header.py @@ -16,7 +16,7 @@ import sys -def MakeFileSegment(input, all_names): +def _make_pack_header_segment(input, ids): result = """ // --------------------------------------------------------------------------- @@ -26,8 +26,93 @@ def MakeFileSegment(input, all_names): filename = os.path.split(input)[1] result = result.replace('$FILE$', filename) + for name, id in ids.items(): + result += "\n#define %s %s" % (name, id) + + return result + + +def make_pack_header(output, all_files): + # header string + result = get_copyright(full=True, translator=False) + \ +"""// +// --------------------------------------------------------------------------- +// +// This file is generated by the make_pack_header.py tool. +// + +#ifndef $GUARD$ +#define $GUARD$ +#pragma once""" + + # generate the file segments + for file, ids in all_files.items(): + result += _make_pack_header_segment(file, ids) + + # footer string + result += \ +""" + +#endif // $GUARD$ +""" + + # add the guard string + filename = os.path.split(output)[1] + guard = 'CEF_INCLUDE_' + filename.replace('.', '_').upper() + '_' + result = result.replace('$GUARD$', guard) + + return result + + +def _get_cpp_var_name(output): + filename_no_ext = os.path.splitext(os.path.split(output)[1])[0] + + # Convert to CamelCase after removing the 'cef_' prefix. + parts = filename_no_ext.split('_')[1:] + return "".join([p[0].upper() + p[1:] for p in parts]) + + +def make_pack_inc(output, all_files): + var = 'IdNames' + _get_cpp_var_name(output) + + result = get_copyright(full=False, translator=False) + \ +"""// +// --------------------------------------------------------------------------- +// +// This file was generated by the make_pack_header.py tool. +// + +namespace { + +struct $var$ { + int id; + const char* const name; +}; + +const $var$ k$var$[] = {""".replace('$var$', var) + + for file, ids in all_files.items(): + result += '\n // From %s:' % file + for name, id in ids.items(): + result += '\n {%s, "%s"},' % (id, name) + + result += \ +""" +}; + +const size_t k$var$Size = std::size(k$var$); + +} // namespace +""".replace('$var$', var) + + return result + + +def _get_defines(input, all_names): contents = read_file(input) + ids = {} + # Format for Windows builds with resource whitelisting enabled [1]: # #define IDR_RESOURCE_NAME (::ui::WhitelistedResource<12345>(), 12345) # Format for other builds: @@ -50,54 +135,48 @@ def MakeFileSegment(input, all_names): else: all_names[name] = 1 - result += "\n#define %s %s" % (name, id) - - return result - + ids[name] = id -def MakeFile(output, input): - # header string - result = get_copyright(full=True, translator=False) + \ -"""// -// --------------------------------------------------------------------------- -// -// This file is generated by the make_pack_header.py tool. -// + return ids -#ifndef $GUARD$ -#define $GUARD$ -#pragma once""" +def write_pack_header(out_header_file, out_inc_file, inputs): # sort the input files by name - input = sorted(input, key=lambda path: os.path.split(path)[1]) + inputs = sorted(inputs, key=lambda path: os.path.split(path)[1]) all_names = {} + all_files = {} # generate the file segments - for file in input: - result += MakeFileSegment(file, all_names) + for file in inputs: + filename = os.path.split(file)[1] + assert not filename in all_files, filename + all_files[filename] = _get_defines(file, all_names) - # footer string - result += \ -""" - -#endif // $GUARD$ -""" + out_file = os.path.abspath(out_header_file) + result = make_pack_header(out_file, all_files) + if not bool(result): + sys.stderr.write('Failed to create %s\n' % out_file) + sys.exit(1) + retval1 = write_file_if_changed(out_file, result) - # add the guard string - filename = os.path.split(output)[1] - guard = 'CEF_INCLUDE_' + filename.replace('.', '_').upper() + '_' - result = result.replace('$GUARD$', guard) + out_file = os.path.abspath(out_inc_file) + result = make_pack_inc(out_file, all_files) + if not bool(result): + sys.stderr.write('Failed to create %s\n' % out_file) + sys.exit(1) + retval2 = write_file_if_changed(out_file, result) - write_file_if_changed(output, result) + return retval1 #or retval2 def main(argv): - if len(argv) < 3: - print(("Usage:\n %s [input_file2] ... " % - argv[0])) + if len(argv) < 4: + print( + "Usage:\n %s [input_file2] ... " + % argv[0]) sys.exit(-1) - MakeFile(argv[1], argv[2:]) + write_pack_header(argv[1], argv[2], argv[3:]) if '__main__' == __name__: diff --git a/tools/make_version_header.bat b/tools/make_version_header.bat deleted file mode 100644 index 15cdbc7c80..0000000000 --- a/tools/make_version_header.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -python3.bat tools\make_version_header.py include\cef_version.h diff --git a/tools/make_version_header.py b/tools/make_version_header.py index fa2325aa35..78dd088f8d 100644 --- a/tools/make_version_header.py +++ b/tools/make_version_header.py @@ -12,9 +12,6 @@ def make_version_header(header): - if not git.is_checkout('.'): - raise Exception('Not a valid checkout') - result = get_copyright(full=True, translator=False) + \ """// // --------------------------------------------------------------------------- @@ -41,33 +38,6 @@ def make_version_header(header): #define DO_MAKE_STRING(p) #p #define MAKE_STRING(p) DO_MAKE_STRING(p) -#ifndef APSTUDIO_HIDDEN_SYMBOLS - -#include "include/internal/cef_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Returns CEF version information for the libcef library. The |entry| -// parameter describes which version component will be returned: -// 0 - CEF_VERSION_MAJOR -// 1 - CEF_VERSION_MINOR -// 2 - CEF_VERSION_PATCH -// 3 - CEF_COMMIT_NUMBER -// 4 - CHROME_VERSION_MAJOR -// 5 - CHROME_VERSION_MINOR -// 6 - CHROME_VERSION_BUILD -// 7 - CHROME_VERSION_PATCH -/// -CEF_EXPORT int cef_version_info(int entry); - -#ifdef __cplusplus -} -#endif - -#endif // APSTUDIO_HIDDEN_SYMBOLS - #endif // CEF_INCLUDE_CEF_VERSION_H_ """ diff --git a/tools/make_version_header.sh b/tools/make_version_header.sh deleted file mode 100755 index 30e00b2667..0000000000 --- a/tools/make_version_header.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -python3 tools/make_version_header.py include/cef_version.h diff --git a/tools/make_wrapper_types_header.py b/tools/make_wrapper_types_header.py index 213c75f949..ca229d4fbc 100644 --- a/tools/make_wrapper_types_header.py +++ b/tools/make_wrapper_types_header.py @@ -43,7 +43,9 @@ def write_wrapper_types_header(header, file): # create the header object header = obj_header() - excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h'] + excluded_files = [ + 'cef_api_hash.h', 'cef_application_mac.h', 'cef_version_info.h' + ] header.add_directory(sys.argv[1], excluded_files) # dump the result to stdout diff --git a/tools/translator.README.txt b/tools/translator.README.txt index 4199d325e4..af9aad2d21 100644 --- a/tools/translator.README.txt +++ b/tools/translator.README.txt @@ -285,10 +285,6 @@ follows. void CefClassCToCpp::Function(cpp_params) { - // Structure Verification - if (CEF_MEMBER_MISSING(struct_, function)) - return; - // Parameter Verification (Optional) // Verify the C++ parameter values. // ... @@ -309,10 +305,6 @@ follows. cpp_retval CefClassCToCpp::Function(cpp_params) { - // Structure Verification - if (CEF_MEMBER_MISSING(struct_, function)) - return default_retval; // Configured or defaulted automatically. - // Parameter Verification (Optional) // Verify the C++ parameter values. // ... diff --git a/tools/translator.py b/tools/translator.py index 1d8552ce78..54797dd7af 100644 --- a/tools/translator.py +++ b/tools/translator.py @@ -3,111 +3,66 @@ # can be found in the LICENSE file. from __future__ import absolute_import -import sys -from cef_parser import * +from cef_parser import obj_header +from cef_version import VersionFormatter from clang_util import clang_format from file_util import * import hashlib -from make_api_hash_header import * -from make_capi_header import * -from make_cpptoc_header import * -from make_cpptoc_impl import * -from make_ctocpp_header import * -from make_ctocpp_impl import * -from make_gypi_file import * -from make_libcef_dll_dylib_impl import * -from make_wrapper_types_header import * +from make_capi_header import write_capi_header +from make_capi_versions_header import write_capi_versions_header +from make_cpptoc_header import write_cpptoc_header +from make_cpptoc_impl import write_cpptoc_impl +from make_ctocpp_header import write_ctocpp_header +from make_ctocpp_impl import write_ctocpp_impl +from make_gypi_file import write_gypi_file +from make_libcef_dll_dylib_impl import write_libcef_dll_dylib_impl +from make_wrapper_types_header import write_wrapper_types_header from optparse import OptionParser +import sys -# cannot be loaded as a module -if __name__ != "__main__": - sys.stderr.write('This file cannot be loaded as a module!') - sys.exit() +FILE_HEADER = """# +# This file was generated by the CEF translator tool and should not edited +# by hand. +# +# $hash=$$HASH$$$ +# -# parse command-line options -disc = """ -This utility generates files for the CEF C++ to C API translation layer. """ -parser = OptionParser(description=disc) -parser.add_option( - '--root-dir', - dest='rootdir', - metavar='DIR', - help='CEF root directory [required]') -parser.add_option( - '--backup', - action='store_true', - dest='backup', - default=False, - help='create a backup of modified files') -parser.add_option( - '--force', - action='store_true', - dest='force', - default=False, - help='force rewrite of the file') -parser.add_option( - '-c', - '--classes', - dest='classes', - action='append', - help='only translate the specified classes') -parser.add_option( - '-q', - '--quiet', - action='store_true', - dest='quiet', - default=False, - help='do not output detailed status information') -(options, args) = parser.parse_args() - -# the rootdir option is required -if options.rootdir is None: - parser.print_help(sys.stdout) - sys.exit() - -# determine the paths -root_dir = os.path.abspath(options.rootdir) -cpp_header_dir = os.path.join(root_dir, 'include') -cpp_header_test_dir = os.path.join(cpp_header_dir, 'test') -cpp_header_views_dir = os.path.join(cpp_header_dir, 'views') -capi_header_dir = os.path.join(cpp_header_dir, 'capi') -api_hash_header = os.path.join(cpp_header_dir, 'cef_api_hash.h') -libcef_dll_dir = os.path.join(root_dir, 'libcef_dll') -cpptoc_global_impl = os.path.join(libcef_dll_dir, 'libcef_dll.cc') -ctocpp_global_impl = os.path.join(libcef_dll_dir, 'wrapper', - 'libcef_dll_wrapper.cc') -wrapper_types_header = os.path.join(libcef_dll_dir, 'wrapper_types.h') -cpptoc_dir = os.path.join(libcef_dll_dir, 'cpptoc') -ctocpp_dir = os.path.join(libcef_dll_dir, 'ctocpp') -gypi_file = os.path.join(root_dir, 'cef_paths.gypi') -libcef_dll_dylib_impl = os.path.join(libcef_dll_dir, 'wrapper', - 'libcef_dll_dylib.cc') - -# make sure the header directory exists -if not path_exists(cpp_header_dir): - sys.stderr.write('Directory ' + cpp_header_dir + ' does not exist.') - sys.exit() - -# create the header object -if not options.quiet: - sys.stdout.write('Parsing C++ headers from ' + cpp_header_dir + '...\n') -header = obj_header() - -# add include files to be processed -header.set_root_directory(cpp_header_dir) -excluded_files = ['cef_api_hash.h', 'cef_application_mac.h', 'cef_version.h'] -header.add_directory(cpp_header_dir, excluded_files) -header.add_directory(cpp_header_test_dir) -header.add_directory(cpp_header_views_dir) - -# Track the number of files that were written. -writect = 0 - - -def update_file(file, newcontents): + +def _write_version(): + return FILE_HEADER + VersionFormatter().get_version_string() + + +def _write_gitignore(gitignore, gitignore_file, root_dir): + contents = FILE_HEADER + + in_file = gitignore_file + '.in' + if os.path.isfile(in_file): + contents += read_file(in_file) + + # Include ourselves in generated files. + gitignore.append(gitignore_file) + + root_dir_len = len(root_dir) + contents += '\n'.join( + [p[root_dir_len:].replace('\\', '/') for p in sorted(gitignore)]) + + return contents + + +def _update_file(file, newcontents, customized, force, clean, backup, + gitignore): """ Replaces the contents of |file| with |newcontents| if necessary. """ + if clean: + if not customized: + return 1 if remove_file(file, quiet=False) else 0 + print('File %s has customizations and will not be removed' % file) + return 0 + + if not customized and not gitignore is None: + gitignore.append(file) + oldcontents = '' oldhash = '' @@ -122,7 +77,7 @@ def update_file(file, newcontents): hash_end = "$" hash_token = "$$HASH$$" - if not options.force and path_exists(file): + if not force and path_exists(file): oldcontents = read_file(file) # Extract the existing hash. @@ -137,106 +92,264 @@ def update_file(file, newcontents): if oldhash == newhash: # Pre-formatted contents have not changed. - return + return 0 newcontents = newcontents.replace(hash_token, newhash, 1) - # Apply clang-format for C/C++ files. - if os.path.splitext(file)[1][1:] in ('c', 'cc', 'cpp', 'h'): + # Apply clang-format for C/C++ files. This is slow, so we only do it for + # customized files. + if customized and os.path.splitext(file)[1][1:] in ('c', 'cc', 'cpp', 'h'): result = clang_format(file, newcontents) if result != None: newcontents = result else: - raise Exception("Call to clang-format failed") + raise Exception("Call to clang-format failed for %s" % file) - if options.backup and oldcontents != '': + if backup and oldcontents != '': backup_file(file) filedir = os.path.split(file)[0] if not os.path.isdir(filedir): make_dir(filedir) + print('Writing file %s' % file) write_file(file, newcontents) - - global writect - writect += 1 - - -# output the C API header -if not options.quiet: - sys.stdout.write('In C API header directory ' + capi_header_dir + '...\n') -filenames = sorted(header.get_file_names()) -for filename in filenames: - if not options.quiet: - sys.stdout.write('Generating ' + filename + ' C API header...\n') - update_file(*write_capi_header(header, capi_header_dir, filename)) - -# output the wrapper types header -if not options.quiet: - sys.stdout.write('Generating wrapper types header...\n') -update_file(*write_wrapper_types_header(header, wrapper_types_header)) - -# build the list of classes to parse -allclasses = header.get_class_names() -if not options.classes is None: - for cls in options.classes: - if not cls in allclasses: - sys.stderr.write('ERROR: Unknown class: ' + cls) - sys.exit() - classes = options.classes -else: - classes = allclasses - -classes = sorted(classes) - -# output CppToC global file -if not options.quiet: - sys.stdout.write('Generating CppToC global implementation...\n') -update_file(*write_cpptoc_impl(header, None, cpptoc_global_impl)) - -# output CToCpp global file -if not options.quiet: - sys.stdout.write('Generating CToCpp global implementation...\n') -update_file(*write_ctocpp_impl(header, None, ctocpp_global_impl)) - -# output CppToC class files -if not options.quiet: - sys.stdout.write('In CppToC directory ' + cpptoc_dir + '...\n') -for cls in classes: - if not options.quiet: - sys.stdout.write('Generating ' + cls + 'CppToC class header...\n') - update_file(*write_cpptoc_header(header, cls, cpptoc_dir)) - if not options.quiet: - sys.stdout.write('Generating ' + cls + 'CppToC class implementation...\n') - update_file(*write_cpptoc_impl(header, cls, cpptoc_dir)) - -# output CppToC class files -if not options.quiet: - sys.stdout.write('In CToCpp directory ' + ctocpp_dir + '...\n') -for cls in classes: - if not options.quiet: - sys.stdout.write('Generating ' + cls + 'CToCpp class header...\n') - update_file(*write_ctocpp_header(header, cls, ctocpp_dir)) - if not options.quiet: - sys.stdout.write('Generating ' + cls + 'CToCpp class implementation...\n') - update_file(*write_ctocpp_impl(header, cls, ctocpp_dir)) - -# output the gypi file -if not options.quiet: - sys.stdout.write('Generating ' + gypi_file + ' file...\n') -update_file(*write_gypi_file(header, gypi_file)) - -# output the libcef dll dylib file -if not options.quiet: - sys.stdout.write('Generating ' + libcef_dll_dylib_impl + ' file...\n') -update_file(*write_libcef_dll_dylib_impl(header, libcef_dll_dylib_impl)) - -# Update the API hash header file if necessary. This must be done last because -# it reads files that were potentially written by proceeding operations. -if not options.quiet: - sys.stdout.write('Generating API hash header...\n') -if write_api_hash_header(api_hash_header, cpp_header_dir): - writect += 1 - -if not options.quiet: - sys.stdout.write('Done - Wrote ' + str(writect) + ' files.\n') + return 1 + + +def translate(cef_dir, + force=False, + clean=False, + backup=False, + verbose=False, + selected_classes=None): + # determine the paths + root_dir = os.path.abspath(cef_dir) + cpp_header_dir = os.path.join(root_dir, 'include') + cpp_header_test_dir = os.path.join(cpp_header_dir, 'test') + cpp_header_views_dir = os.path.join(cpp_header_dir, 'views') + capi_header_dir = os.path.join(cpp_header_dir, 'capi') + libcef_dll_dir = os.path.join(root_dir, 'libcef_dll') + cpptoc_global_impl = os.path.join(libcef_dll_dir, 'libcef_dll.cc') + ctocpp_global_impl = os.path.join(libcef_dll_dir, 'wrapper', + 'libcef_dll_wrapper.cc') + wrapper_types_header = os.path.join(libcef_dll_dir, 'wrapper_types.h') + cpptoc_dir = os.path.join(libcef_dll_dir, 'cpptoc') + ctocpp_dir = os.path.join(libcef_dll_dir, 'ctocpp') + gypi_file = os.path.join(root_dir, 'cef_paths.gypi') + libcef_dll_dylib_impl = os.path.join(libcef_dll_dir, 'wrapper', + 'libcef_dll_dylib.cc') + version_file = os.path.join(root_dir, 'VERSION.stamp') + gitignore_file = os.path.join(root_dir, '.gitignore') + + # make sure the header directory exists + if not path_exists(cpp_header_dir): + sys.stderr.write('ERROR: Directory ' + cpp_header_dir + + ' does not exist.\n') + sys.exit(1) + + # create the header object + if verbose: + print('Parsing C++ headers from ' + cpp_header_dir + '...') + header = obj_header() + + # add include files to be processed + header.set_root_directory(cpp_header_dir) + excluded_files = [ + 'cef_api_hash.h', 'cef_application_mac.h', 'cef_version_info.h' + ] + header.add_directory(cpp_header_dir, excluded_files) + header.add_directory(cpp_header_test_dir) + header.add_directory(cpp_header_views_dir) + + # Track the number of files that were written. + writect = 0 + + # Track files that are not customized. + gitignore = [] + + debug_string = '' + + try: + + # output the C API header + if verbose: + print('In C API header directory ' + capi_header_dir + '...') + filenames = sorted(header.get_file_names()) + for filename in filenames: + if verbose: + print('Generating ' + filename + ' C API headers...') + debug_string = 'CAPI header for ' + filename + writect += _update_file(*write_capi_header(header, capi_header_dir, + filename), False, force, clean, + backup, gitignore) + debug_string = 'CAPI versions header for ' + filename + writect += _update_file(*write_capi_versions_header( + header, capi_header_dir, filename), False, force, clean, backup, + gitignore) + + # output the wrapper types header + if verbose: + print('Generating wrapper types header...') + debug_string = 'wrapper types header' + writect += _update_file(*write_wrapper_types_header( + header, wrapper_types_header), False, force, clean, backup, gitignore) + + # build the list of classes to parse + allclasses = header.get_class_names() + if not selected_classes is None: + for cls in selected_classes: + if not cls in allclasses: + sys.stderr.write('ERROR: Unknown class: %s\n' % cls) + sys.exit(1) + classes = selected_classes + else: + classes = allclasses + + classes = sorted(classes) + + # output CppToC global file + if verbose: + print('Generating CppToC global implementation...') + debug_string = 'CppToC global implementation' + writect += _update_file(*write_cpptoc_impl( + header, None, cpptoc_global_impl), force, clean, backup, gitignore) + + # output CToCpp global file + if verbose: + print('Generating CToCpp global implementation...') + debug_string = 'CToCpp global implementation' + writect += _update_file(*write_ctocpp_impl( + header, None, ctocpp_global_impl), force, clean, backup, gitignore) + + # output CppToC class files + if verbose: + print('In CppToC directory ' + cpptoc_dir + '...') + for cls in classes: + if verbose: + print('Generating ' + cls + 'CppToC class header...') + debug_string = 'CppToC class header for ' + cls + writect += _update_file(*write_cpptoc_header(header, cls, cpptoc_dir), + False, force, clean, backup, gitignore) + + if verbose: + print('Generating ' + cls + 'CppToC class implementation...') + debug_string = 'CppToC class implementation for ' + cls + writect += _update_file(*write_cpptoc_impl(header, cls, cpptoc_dir), + force, clean, backup, gitignore) + + # output CppToC class files + if verbose: + print('In CToCpp directory ' + ctocpp_dir + '...') + for cls in classes: + if verbose: + print('Generating ' + cls + 'CToCpp class header...') + debug_string = 'CToCpp class header for ' + cls + writect += _update_file(*write_ctocpp_header(header, cls, ctocpp_dir), + False, force, clean, backup, gitignore) + if verbose: + print('Generating ' + cls + 'CToCpp class implementation...') + debug_string = 'CToCpp class implementation for ' + cls + writect += _update_file(*write_ctocpp_impl(header, cls, ctocpp_dir), + force, clean, backup, gitignore) + + # output the gypi file + if verbose: + print('Generating ' + gypi_file + ' file...') + debug_string = gypi_file + writect += _update_file(*write_gypi_file(header, gypi_file), False, force, + clean, backup, gitignore) + + # output the libcef dll dylib file + if verbose: + print('Generating ' + libcef_dll_dylib_impl + ' file...') + debug_string = libcef_dll_dylib_impl + writect += _update_file(*write_libcef_dll_dylib_impl( + header, libcef_dll_dylib_impl), False, force, clean, backup, gitignore) + + # output the VERSION.stamp file that triggers cef_version.h regen at build time + if verbose: + print('Generating ' + version_file + ' file...') + debug_string = version_file + writect += _update_file(version_file, + _write_version(), False, force, clean, backup, + gitignore) + + # output the top-level .gitignore file that lists uncustomized files + if verbose: + print('Generating ' + gitignore_file + ' file...') + debug_string = gitignore_file + writect += _update_file(gitignore_file, + _write_gitignore(gitignore, gitignore_file, + root_dir), False, force, clean, + backup, None) + + except (AssertionError, Exception) as e: + sys.stderr.write('ERROR: while processing %s\n' % debug_string) + raise + + if verbose or writect > 0: + print('Done translating - %s %d files.' % ('Removed' + if clean else 'Wrote', writect)) + + return writect + + +if __name__ == "__main__": + from optparse import OptionParser + + # parse command-line options + disc = """ + This utility generates files for the CEF C++ to C API translation layer. + """ + + parser = OptionParser(description=disc) + parser.add_option( + '--root-dir', + dest='rootdir', + metavar='DIR', + help='CEF root directory [required]') + parser.add_option( + '--backup', + action='store_true', + dest='backup', + default=False, + help='create a backup of modified files') + parser.add_option( + '--force', + action='store_true', + dest='force', + default=False, + help='force rewrite of the file') + parser.add_option( + '--clean', + action='store_true', + dest='clean', + default=False, + help='clean all files without custom modifications') + parser.add_option( + '-c', + '--classes', + dest='classes', + action='append', + help='only translate the specified classes') + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + default=False, + help='output detailed status information') + (options, args) = parser.parse_args() + + # the rootdir option is required + if options.rootdir is None: + parser.print_help(sys.stdout) + sys.exit() + + if translate(options.rootdir, options.force, options.clean, options.backup, + options.verbose, options.classes) == 0: + if not options.verbose: + print('Nothing to do.') + elif not options.clean: + print('WARNING: You must run version_manager.py to update API hashes.') diff --git a/tools/version_manager.py b/tools/version_manager.py new file mode 100644 index 0000000000..705b3843b2 --- /dev/null +++ b/tools/version_manager.py @@ -0,0 +1,549 @@ +# Copyright (c) 2024 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. + +from __future__ import absolute_import +from __future__ import print_function +from cef_api_hash import cef_api_hash +from cef_version import VersionFormatter +from date_util import get_date +from file_util import read_file, read_json_file, write_file, write_json_file +from git_util import exec_git_cmd +import os +import sys +from translator import translate +from version_util import * + + +def get_next_api_revision(api_versions_file, major_version): + """ Returns the next available API revision for |major_version|. + """ + json = read_json_file(api_versions_file) + if not bool(json): + return 0 + + assert 'last' in json, api_versions_file + last_version, last_revision = version_parse(json['last']) + + if major_version < last_version: + sys.stderr.write( + 'ERROR: Cannot add new API versions on old branches/checkouts ' + '(found %d, expected >= %d)\b' % (major_version, last_version)) + return -1 + + if major_version == last_version: + # Increment the revision for the current major version. + new_revision = last_revision + 1 + assert new_revision <= 99, new_revision + return new_revision + + # Reset the revision for a new major version. + return 0 + + +_CALC = None + + +def compute_api_hashes(cpp_header_dir, api_version, next_allowed, debug_dir, + verbose): + """ Computes API hashes for the specified |api_version|. + """ + if not debug_dir is None: + debug_dir = os.path.join(debug_dir, api_version) + + if not next_allowed: + # Next usage is banned with explicit API versions. + assert not api_version in UNTRACKED_VERSIONS, api_version + added_defines = [ + # Using CEF_API_VERSION_NEXT is an error. + 'CEF_API_VERSION_NEXT="Please_specify_an_exact_CEF_version"', + ] + else: + added_defines = None + + global _CALC + if _CALC is None: + _CALC = cef_api_hash(cpp_header_dir, verbose=verbose) + + hashes = _CALC.calculate(api_version, debug_dir, added_defines) + if bool(hashes): + if api_version in UNTRACKED_VERSIONS: + label = version_label(api_version) + label = label[0:1].upper() + label[1:] + hashes['comment'] = '%s last updated %s.' % (label, get_date()) + else: + hashes['comment'] = 'Added %s.' % get_date() + return hashes + + +def same_api_hashes(hashes1, hashes2): + for key in ('universal', 'linux', 'mac', 'windows'): + if hashes1[key] != hashes2[key]: + return False + return True + + +def compute_next_api_verson(api_versions_file): + """ Computes the next available API version number. + """ + major_version = int(VersionFormatter().get_chrome_major_version()) + next_revision = get_next_api_revision(api_versions_file, major_version) + if next_revision < 0: + return None + + return version_make(major_version, next_revision) + + +def git_grep_next(cef_dir): + cmd = "grep --no-color -n -E (CEF_NEXT|CEF_NEXT)|=next -- :!include/cef_api_hash.h *.h" + return exec_git_cmd(cmd, cef_dir) + + +def find_next_usage(cpp_header_dir): + cef_dir = os.path.abspath(os.path.join(cpp_header_dir, os.pardir)) + result = git_grep_next(cef_dir) + if result is None: + return False + + sys.stderr.write('ERROR: NEXT usage found in CEF headers:\n\n' + result + + '\n\nFix manually or run with --replace-next.\n') + return True + + +def replace_next_usage(file, linenums, as_variable, as_metadata): + assert len(linenums) > 0 + + contents = read_file(file) + if contents is None: + sys.stderr.write('ERROR: Failed to read file %s\n' % file) + return 0 + + lines = contents.split('\n') + changect = 0 + + messages = [] + + for num in linenums: + idx = num - 1 + if idx < 0 or idx >= len(lines): + sys.stderr.write('ERROR: Invalid line number %d in file %s\n' % (num, + file)) + return 0 + + line = lines[idx] + + replaced = False + if line.find('CEF_NEXT') >= 0: + line = line.replace('CEF_NEXT', as_variable) + replaced = True + if line.find('=next') >= 0: + line = line.replace('=next', '=' + as_metadata) + replaced = True + + if replaced: + lines[idx] = line + changect += 1 + else: + messages.append( + 'WARNING: No NEXT instances found on line number %d' % num) + + if changect > 0 and write_file(file, '\n'.join(lines)): + messages.append('Replaced %d of %d NEXT instances' % (changect, + len(linenums))) + else: + changect = 0 + + if len(messages) > 0: + print('For file %s:' % file) + for msg in messages: + print(' %s' % msg) + print() + + return changect + + +def find_replace_next_usage(cpp_header_dir, next_version): + cef_dir = os.path.abspath(os.path.join(cpp_header_dir, os.pardir)) + result = git_grep_next(cef_dir) + if result is None: + return 0 + + print('Attempting to replace NEXT usage with %s in CEF headers:\n' % + next_version) + print(result + '\n') + + as_variable = version_as_variable(next_version) + as_metadata = version_as_metadata(next_version) + + files = {} + + # Parse values like: + # include/test/cef_translator_test.h:879:#if CEF_API_ADDED(CEF_NEXT) + # include/test/cef_translator_test.h:883: /*--cef(added=next)--*/ + for line in result.split('\n'): + parts = line.split(':', maxsplit=2) + name = parts[0] + linenum = int(parts[1]) + if not name in files: + files[name] = [linenum] + else: + files[name].append(linenum) + + for file, linenums in files.items(): + if replace_next_usage( + os.path.join(cef_dir, file), linenums, as_variable, + as_metadata) != len(linenums): + sys.stderr.write('ERROR: Failed to replace all NEXT usage in %s\n' % file) + return 1 + + # Sanity-check that all instances were fixed. + if find_next_usage(cpp_header_dir): + return 1 + + print('All NEXT instances successfully replaced.') + return 0 + + +def exec_apply(cpp_header_dir, api_versions_file, api_untracked_file, + next_version, debug_dir, apply_next, verbose): + """ Updates untracked API hashes if necessary. + Saves the hash for the next API version if |apply_next| is true. + """ + json_versions, json_untracked, initialized = \ + read_version_files(api_versions_file, api_untracked_file, True) + if initialized: + # Also need to generate hashes for the first version. + apply_next = True + json_versions['min'] = next_version + + untracked_changed = False + for version in UNTRACKED_VERSIONS: + label = version_label(version) + hashes = compute_api_hashes(cpp_header_dir, version, True, debug_dir, + verbose) + if not bool(hashes): + sys.stderr.write('ERROR: Failed to process %s\n' % label) + return 1 + + if version in json_untracked['hashes'] and same_api_hashes( + hashes, json_untracked['hashes'][version]): + print('Hashes for %s are unchanged.' % label) + else: + untracked_changed = True + print('Updating hashes for %s.' % label) + json_untracked['hashes'][version] = hashes + + next_changed = apply_next + if apply_next: + next_label = version_label(next_version) + + hashes = compute_api_hashes(cpp_header_dir, next_version, False, debug_dir, + verbose) + if not bool(hashes): + sys.stderr.write('ERROR: Failed to process %s\n' % next_label) + return 1 + + last_version = json_versions.get('last', None) + if not last_version is None and last_version in json_versions['hashes']: + if same_api_hashes(hashes, json_versions['hashes'][last_version]): + print('Hashes for last %s are unchanged.' % version_label(last_version)) + next_changed = False + + if next_changed: + print('Adding hashes for %s.' % next_label) + json_versions['last'] = next_version + json_versions['hashes'][next_version] = hashes + + if NEXT_VERSION in json_untracked['hashes'] and not \ + same_api_hashes(hashes, json_untracked['hashes'][NEXT_VERSION]): + print('NOTE: Additional versions are available to generate.') + + write_versions = next_changed or not os.path.isfile(api_versions_file) + write_untracked = untracked_changed or not os.path.isfile(api_untracked_file) + + if not write_versions and not write_untracked: + print('No hash updates required.') + return -1 + + if write_versions and not write_json_file( + api_versions_file, json_versions, quiet=False): + return 1 + if write_untracked and not write_json_file( + api_untracked_file, json_untracked, quiet=False): + return 1 + + return 0 + + +def exec_check(cpp_header_dir, api_versions_file, api_untracked_file, debug_dir, + fast_check, force_update, skip_untracked, verbose): + """ Checks existing API version hashes. + Resaves all API hashes if |force_update| is true. Otherwise, hash + changes are considered an error. + """ + assert not (fast_check and force_update) + + json_versions, json_untracked, initialized = \ + read_version_files(api_versions_file, api_untracked_file, False) + assert not initialized + + versions = [] + len_versioned_existing = len_versioned_checked = len_versioned_failed = 0 + len_untracked_existing = len_untracked_checked = len_untracked_failed = 0 + + if not json_versions is None: + keys = json_versions['hashes'].keys() + len_versioned_existing = len(keys) + if len_versioned_existing > 0: + if fast_check: + # Only checking a subset of versions. + for key in ('last', 'min'): + if key in json_versions: + version = json_versions[key] + assert version in json_versions['hashes'], version + versions.append(version) + len_versioned_checked += 1 + else: + versions.extend(keys) + len_versioned_checked = len_versioned_existing + + if not json_untracked is None: + keys = json_untracked['hashes'].keys() + len_untracked_existing = len(keys) + if len_untracked_existing > 0 and not skip_untracked: + versions.extend(keys) + len_untracked_checked = len_untracked_existing + + if len(versions) == 0: + print('No hashes to check.') + return 0 + + write_versions = False + write_untracked = False + + for version in versions: + untracked = version in UNTRACKED_VERSIONS + if untracked: + stored_hashes = json_untracked['hashes'][version] + else: + stored_hashes = json_versions['hashes'][version] + label = version_label(version) + computed_hashes = compute_api_hashes(cpp_header_dir, version, True, + debug_dir, verbose) + if not bool(computed_hashes): + sys.stderr.write('ERROR: Failed to process %s\n' % label) + return 1 + if not same_api_hashes(computed_hashes, stored_hashes): + if force_update: + print('Updating hashes for %s' % label) + if untracked: + json_untracked['hashes'][version] = computed_hashes + write_untracked = True + else: + json_versions['hashes'][version] = computed_hashes + write_versions = True + else: + sys.stderr.write('ERROR: Hashes for %s do not match!\n' % label) + if untracked: + len_untracked_failed += 1 + else: + len_versioned_failed += 1 + + len_failed = len_untracked_failed + len_versioned_failed + if len_failed == 0: + if write_versions and not write_json_file( + api_versions_file, json_versions, quiet=False): + return 1 + if write_untracked and not write_json_file( + api_untracked_file, json_untracked, quiet=False): + return 1 + + if write_versions: + print('WARNING: This change can break back/forward binary compatibility.') + else: + sys.stderr.write('ERROR: %d hashes checked and failed\n' % len_failed) + + print('%d hashes checked and match (%d/%d versioned, %d/%d untracked).' % + (len(versions) - len_failed, + len_versioned_checked - len_versioned_failed, len_versioned_existing, + len_untracked_checked - len_untracked_failed, len_untracked_existing)) + + return 0 if len_failed == 0 else 1 + + +if __name__ == "__main__": + from optparse import OptionParser + + desc = """ + This utility manages CEF API versions. +""" + + epilog = """ +Call this utility without arguments after modifying header files in the CEF +include/ directory. Translated files will be updated if necessary. + +If translated files have changed, or when running with -u, unversioned API +hashes (next and experimental) will be checked and potentially updated. + +If translated files have changed, or when running with -c, versioned and +unversioned API hashes will be checked. Any changes to versioned API hashes +can break back/forward binary compatibility and are considered an error. + +API under development will use placeholder values like CEF_NEXT, added=next, +removed=next in CEF header files. This utility can replace those placeholders +with an actual new version and generate the associated versioned API hashes. + +Run with -n to output the next available API version. + +Run with -a to apply the next available API version. + +For complete usage details see +https://bitbucket.org/chromiumembedded/cef/wiki/ApiVersioning.md + +""" + + class CustomParser(OptionParser): + + def format_epilog(self, formatter): + return self.epilog + + parser = CustomParser(description=desc, epilog=epilog) + parser.add_option( + '--debug-dir', + dest='debugdir', + metavar='DIR', + help='intermediate directory for easy debugging') + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + default=False, + help='output detailed status information') + parser.add_option( + '-u', + '--update', + action='store_true', + dest='update', + default=False, + help='update next and unversioned API hashes') + parser.add_option( + '-n', + '--next', + action='store_true', + dest='next', + default=False, + help='output the next available API version') + parser.add_option( + '-a', + '--apply-next', + action='store_true', + dest='apply', + default=False, + help='add a hash for the next available API version') + parser.add_option( + '-c', + '--check', + action='store_true', + dest='check', + default=False, + help='check hashes for existing API versions') + parser.add_option( + '--fast-check', + action='store_true', + dest='fastcheck', + default=False, + help= + 'only check minimum, last, next and experimental API hashes (use with -u, -a or -c)' + ) + parser.add_option( + '--replace-next', + action='store_true', + dest='replacenext', + default=False, + help='replace NEXT usage in CEF headers (use with -a)') + parser.add_option( + '--replace-next-version', + dest='replacenextversion', + metavar='VERSION', + help='replace NEXT usage with this value (use with --replace-next)') + parser.add_option( + '--force-update', + action='store_true', + dest='forceupdate', + default=False, + help='force update all API hashes (use with -c)') + (options, args) = parser.parse_args() + + script_dir = os.path.dirname(__file__) + cef_dir = os.path.abspath(os.path.join(script_dir, os.pardir)) + + cpp_header_dir = os.path.join(cef_dir, 'include') + if not os.path.isdir(cpp_header_dir): + sys.stderr.write( + 'ERROR: Missing %s directory is required\n' % cpp_header_dir) + sys.exit(1) + + api_versions_file = os.path.join(cef_dir, VERSIONS_JSON_FILE) + api_untracked_file = os.path.join(cef_dir, UNTRACKED_JSON_FILE) + + mode_ct = options.update + options.next + options.apply + options.check + if mode_ct > 1: + sys.stderr.write( + 'ERROR: Choose a single execution mode (-u, -n, -a or -c)\n') + parser.print_help(sys.stdout) + sys.exit(1) + + next_version = compute_next_api_verson(api_versions_file) + if next_version is None: + sys.exit(1) + + if options.next: + print(next_version) + sys.exit(0) + + will_apply_next = options.apply or not os.path.isfile(api_versions_file) + if will_apply_next: + if options.replacenext: + replace_version = options.replacenextversion + if replace_version is None: + replace_version = next_version + elif not version_valid_for_next(replace_version, next_version): + sys.stderr.write('ERROR: Invalid value for --replace-next-version\n') + sys.exit(1) + result = find_replace_next_usage(cpp_header_dir, replace_version) + if result != 0: + sys.exit(result) + elif find_next_usage(cpp_header_dir): + sys.exit(1) + + changed = translate(cef_dir, verbose=options.verbose) > 0 + skip_untracked = False + + if options.update or will_apply_next or changed or not os.path.isfile( + api_untracked_file): + skip_untracked = True + if exec_apply( + cpp_header_dir, + api_versions_file, + api_untracked_file, + next_version, + options.debugdir, + apply_next=options.apply, + verbose=options.verbose) > 0: + # Apply failed. + sys.exit(1) + elif not options.check: + print('Nothing to do.') + sys.exit(0) + + sys.exit( + exec_check( + cpp_header_dir, + api_versions_file, + api_untracked_file, + options.debugdir, + options.fastcheck and not options.forceupdate, + options.check and options.forceupdate, + skip_untracked, + verbose=options.verbose)) diff --git a/tools/version_util.py b/tools/version_util.py new file mode 100644 index 0000000000..9ad17f5674 --- /dev/null +++ b/tools/version_util.py @@ -0,0 +1,154 @@ +# Copyright (c) 2024 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. + +from __future__ import absolute_import +from __future__ import print_function +from file_util import read_json_file + +# Experimental version. Always the last value. +EXP_VERSION = '999999' +EXP_NAME = 'EXPERIMENTAL' + +# Next version. Always the next to last value. +NEXT_VERSION = '999998' +NEXT_NAME = 'NEXT' + +UNTRACKED_VERSIONS = (EXP_VERSION, NEXT_VERSION) +UNTRACKED_NAMES = (EXP_NAME, NEXT_NAME) + +VERSIONS_JSON_FILE = 'cef_api_versions.json' +UNTRACKED_JSON_FILE = 'cef_api_untracked.json' + + +def version_make(major_version, revision): + """ Make a tracked version from components. """ + # 9999 is reserved for untracked placeholder values. This allows for ~898 years + # of Chromium major versions at the current (end of 2024) rate of 11 per year. + # If this breaks for you please file an issue via Ouija board and/or psychic medium. + assert major_version > 0 and major_version < 9999, major_version + assert revision >= 0 and revision <= 99, revision + if major_version < 1000: + return '%03d%02d' % (major_version, revision) + return '%d%02d' % (major_version, revision) + + +def version_tracked(version): + """ Returns true if version is in tracked format. """ + return (len(version) == 5 or len(version) == 6) and version.isnumeric() and \ + not version in UNTRACKED_VERSIONS + + +def version_parse(version): + """ Parse a tracked version into components. """ + assert version_tracked(version), version + split = 3 if len(version) == 5 else 4 + return (int(version[0:split]), int(version[split:])) + + +def version_valid(version): + """ Returns true if version is valid. """ + # Untracked versions must be referenced by name instead of number. + return version in UNTRACKED_NAMES or version_tracked(version) + + +def version_valid_for_next(version, ref_version, allow_exp=True): + """ Returns true if version is valid as a replacement for NEXT. """ + version = version.upper() + if allow_exp and version == EXP_NAME: + return True + # Must be valid and not NEXT. + if not version_valid(version) or version == NEXT_NAME: + return False + # Must be >= ref_version. + if version_as_numeric(version) < int(ref_version): + return False + # Must have the same major version number as ref_version. + return version_parse(version)[0] == version_parse(ref_version)[0] + + +def read_version_last(api_versions_file): + json_versions = read_json_file(api_versions_file) + if not bool(json_versions): + return None + assert 'last' in json_versions, api_versions_file + return json_versions['last'] + + +def read_version_files(api_versions_file, + api_untracked_file, + initialize, + combine=False): + initialized = False + + if combine: + initialize = True + + json_versions = read_json_file(api_versions_file) + if not bool(json_versions): + if initialize: + json_versions = { + 'hashes': {}, + } + initialized = True + else: + json_version = None + else: + assert 'hashes' in json_versions, api_versions_file + assert 'last' in json_versions, api_versions_file + assert 'min' in json_versions, api_versions_file + + json_untracked = read_json_file(api_untracked_file) + if not bool(json_untracked): + if initialize: + json_untracked = { + 'hashes': {}, + } + else: + json_untracked = None + else: + assert 'hashes' in json_untracked, api_untracked_file + for version in json_untracked['hashes']: + assert version in UNTRACKED_VERSIONS, api_untracked_file + + if combine: + if bool(json_untracked['hashes']): + json_versions['hashes'].update(json_untracked['hashes']) + return (json_versions, initialized) + + return (json_versions, json_untracked, initialized) + + +def version_label(version): + if version == EXP_VERSION or version == EXP_NAME: + return 'experimental version' + if version == NEXT_VERSION or version == NEXT_NAME: + return 'next version' + return 'version ' + version + + +def version_as_numeric(version): + """ Returns version as a numeric value. """ + version = version.upper() + assert version_valid(version), version + if version == EXP_NAME: + version = EXP_VERSION + elif version == NEXT_NAME: + version = NEXT_VERSION + return int(version) + + +def version_as_variable(version): + """ Returns version as a variable for use in C/C++ files. """ + version = version.upper() + assert version_valid(version), version + if not version.isnumeric(): + return 'CEF_' + version + return version + + +def version_as_metadata(version): + """ Returns version as metadata for comments in C++ header files. """ + version = version.upper() + assert version_valid(version), version + return version.lower()