diff --git a/BUILD.gn b/BUILD.gn index b1b57fc06..d4ee2490b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -374,12 +374,16 @@ static_library("libcef_static") { "libcef/browser/download_item_impl.h", "libcef/browser/download_manager_delegate.cc", "libcef/browser/download_manager_delegate.h", + "libcef/browser/extension_impl.cc", + "libcef/browser/extension_impl.h", "libcef/browser/extensions/api/tabs/tabs_api.cc", "libcef/browser/extensions/api/tabs/tabs_api.h", "libcef/browser/extensions/browser_context_keyed_service_factories.cc", "libcef/browser/extensions/browser_context_keyed_service_factories.h", "libcef/browser/extensions/browser_extensions_util.cc", "libcef/browser/extensions/browser_extensions_util.h", + "libcef/browser/extensions/browser_platform_delegate_background.cc", + "libcef/browser/extensions/browser_platform_delegate_background.h", "libcef/browser/extensions/chrome_api_registration.cc", "libcef/browser/extensions/chrome_api_registration.h", "libcef/browser/extensions/component_extension_resource_manager.cc", @@ -388,10 +392,18 @@ static_library("libcef_static") { "libcef/browser/extensions/extensions_api_client.h", "libcef/browser/extensions/extensions_browser_client.cc", "libcef/browser/extensions/extensions_browser_client.h", + "libcef/browser/extensions/extension_background_host.cc", + "libcef/browser/extensions/extension_background_host.h", + "libcef/browser/extensions/extension_function_details.cc", + "libcef/browser/extensions/extension_function_details.h", + "libcef/browser/extensions/extension_host_delegate.cc", + "libcef/browser/extensions/extension_host_delegate.h", "libcef/browser/extensions/extension_system.cc", "libcef/browser/extensions/extension_system.h", "libcef/browser/extensions/extension_system_factory.cc", "libcef/browser/extensions/extension_system_factory.h", + "libcef/browser/extensions/extension_view_host.cc", + "libcef/browser/extensions/extension_view_host.h", "libcef/browser/extensions/extension_web_contents_observer.cc", "libcef/browser/extensions/extension_web_contents_observer.h", "libcef/browser/extensions/mime_handler_view_guest_delegate.cc", @@ -1578,6 +1590,13 @@ if (is_mac) { ] } + bundle_data("cefclient_resources_bundle_data_extensions_set_page_color") { + sources = gypi_paths2.cefclient_sources_resources_extensions_set_page_color + outputs = [ + "{{bundle_resources_dir}}/extensions/set_page_color/{{source_file_part}}", + ] + } + bundle_data("cefclient_resources_bundle_data_english") { sources = [ "tests/cefclient/resources/mac/English.lproj/InfoPlist.strings", @@ -1622,6 +1641,7 @@ if (is_mac) { gypi_paths2.cefclient_sources_mac deps = [ ":cefclient_resources_bundle_data", + ":cefclient_resources_bundle_data_extensions_set_page_color", ":cefclient_resources_bundle_data_english", ":cefclient_xibs", ":libcef_dll_wrapper", @@ -1785,6 +1805,11 @@ if (is_mac) { gypi_paths2.cefclient_sources_resources outputs = [ "${root_out_dir}/cefclient_files/{{source_file_part}}" ] } + + copy("copy_cefclient_files_extensions_set_page_color") { + sources = gypi_paths2.cefclient_sources_resources_extensions_set_page_color + outputs = [ "${root_out_dir}/cefclient_files/extensions/set_page_color/{{source_file_part}}" ] + } } executable("cefclient") { @@ -1797,7 +1822,8 @@ if (is_mac) { gypi_paths2.cefclient_sources_browser + gypi_paths2.cefclient_sources_common + gypi_paths2.cefclient_sources_renderer + - gypi_paths2.cefclient_sources_resources + gypi_paths2.cefclient_sources_resources + + gypi_paths2.cefclient_sources_resources_extensions_set_page_color deps = [ ":libcef", @@ -1845,6 +1871,7 @@ if (is_mac) { deps += [ ":copy_cefclient_files", + ":copy_cefclient_files_extensions_set_page_color", ] libs = [ diff --git a/cef_paths.gypi b/cef_paths.gypi index 664cf5e22..e8861ded1 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -8,7 +8,7 @@ # by hand. See the translator.README.txt file in the tools directory for # more information. # -# $hash=20588ad142633d169a929c7631888e287ab66b9a$ +# $hash=2a6e7c37a8421d3c0d032cc907fb3058790ac71d$ # { @@ -32,6 +32,8 @@ 'include/cef_download_item.h', 'include/cef_drag_data.h', 'include/cef_drag_handler.h', + 'include/cef_extension.h', + 'include/cef_extension_handler.h', 'include/cef_file_util.h', 'include/cef_find_handler.h', 'include/cef_focus_handler.h', @@ -122,6 +124,8 @@ 'include/capi/cef_download_item_capi.h', 'include/capi/cef_drag_data_capi.h', 'include/capi/cef_drag_handler_capi.h', + 'include/capi/cef_extension_capi.h', + 'include/capi/cef_extension_handler_capi.h', 'include/capi/cef_file_util_capi.h', 'include/capi/cef_find_handler_capi.h', 'include/capi/cef_focus_handler_capi.h', @@ -266,6 +270,10 @@ 'libcef_dll/ctocpp/drag_handler_ctocpp.h', 'libcef_dll/ctocpp/end_tracing_callback_ctocpp.cc', 'libcef_dll/ctocpp/end_tracing_callback_ctocpp.h', + 'libcef_dll/cpptoc/extension_cpptoc.cc', + 'libcef_dll/cpptoc/extension_cpptoc.h', + 'libcef_dll/ctocpp/extension_handler_ctocpp.cc', + 'libcef_dll/ctocpp/extension_handler_ctocpp.h', 'libcef_dll/cpptoc/file_dialog_callback_cpptoc.cc', 'libcef_dll/cpptoc/file_dialog_callback_cpptoc.h', 'libcef_dll/cpptoc/views/fill_layout_cpptoc.cc', @@ -280,6 +288,8 @@ 'libcef_dll/cpptoc/geolocation_callback_cpptoc.h', 'libcef_dll/ctocpp/geolocation_handler_ctocpp.cc', 'libcef_dll/ctocpp/geolocation_handler_ctocpp.h', + 'libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.cc', + 'libcef_dll/cpptoc/get_extension_resource_callback_cpptoc.h', 'libcef_dll/ctocpp/get_geolocation_callback_ctocpp.cc', 'libcef_dll/ctocpp/get_geolocation_callback_ctocpp.h', 'libcef_dll/cpptoc/image_cpptoc.cc', @@ -304,6 +314,8 @@ 'libcef_dll/cpptoc/views/menu_button_cpptoc.h', 'libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.cc', 'libcef_dll/ctocpp/views/menu_button_delegate_ctocpp.h', + 'libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.cc', + 'libcef_dll/cpptoc/views/menu_button_pressed_lock_cpptoc.h', 'libcef_dll/cpptoc/menu_model_cpptoc.cc', 'libcef_dll/cpptoc/menu_model_cpptoc.h', 'libcef_dll/ctocpp/menu_model_delegate_ctocpp.cc', @@ -540,6 +552,10 @@ 'libcef_dll/cpptoc/drag_handler_cpptoc.h', 'libcef_dll/cpptoc/end_tracing_callback_cpptoc.cc', 'libcef_dll/cpptoc/end_tracing_callback_cpptoc.h', + 'libcef_dll/ctocpp/extension_ctocpp.cc', + 'libcef_dll/ctocpp/extension_ctocpp.h', + 'libcef_dll/cpptoc/extension_handler_cpptoc.cc', + 'libcef_dll/cpptoc/extension_handler_cpptoc.h', 'libcef_dll/ctocpp/file_dialog_callback_ctocpp.cc', 'libcef_dll/ctocpp/file_dialog_callback_ctocpp.h', 'libcef_dll/ctocpp/views/fill_layout_ctocpp.cc', @@ -554,6 +570,8 @@ 'libcef_dll/ctocpp/geolocation_callback_ctocpp.h', 'libcef_dll/cpptoc/geolocation_handler_cpptoc.cc', 'libcef_dll/cpptoc/geolocation_handler_cpptoc.h', + 'libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.cc', + 'libcef_dll/ctocpp/get_extension_resource_callback_ctocpp.h', 'libcef_dll/cpptoc/get_geolocation_callback_cpptoc.cc', 'libcef_dll/cpptoc/get_geolocation_callback_cpptoc.h', 'libcef_dll/ctocpp/image_ctocpp.cc', @@ -578,6 +596,8 @@ 'libcef_dll/ctocpp/views/menu_button_ctocpp.h', 'libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.cc', 'libcef_dll/cpptoc/views/menu_button_delegate_cpptoc.h', + 'libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.cc', + 'libcef_dll/ctocpp/views/menu_button_pressed_lock_ctocpp.h', 'libcef_dll/ctocpp/menu_model_ctocpp.cc', 'libcef_dll/ctocpp/menu_model_ctocpp.h', 'libcef_dll/cpptoc/menu_model_delegate_cpptoc.cc', diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 36ffbc142..e08af4bfd 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -146,6 +146,10 @@ 'shared_sources_browser': [ 'tests/shared/browser/client_app_browser.cc', 'tests/shared/browser/client_app_browser.h', + 'tests/shared/browser/extension_util.cc', + 'tests/shared/browser/extension_util.h', + 'tests/shared/browser/file_util.cc', + 'tests/shared/browser/file_util.h', 'tests/shared/browser/geometry_util.cc', 'tests/shared/browser/geometry_util.h', 'tests/shared/browser/main_message_loop.cc', @@ -155,7 +159,6 @@ 'tests/shared/browser/main_message_loop_std.cc', 'tests/shared/browser/main_message_loop_std.h', 'tests/shared/browser/resource_util.h', - 'tests/shared/browser/resource_util.cc', 'tests/shared/browser/resource_util.h', ], 'shared_sources_common': [ @@ -216,6 +219,8 @@ 'tests/cefclient/browser/dialog_test.h', 'tests/cefclient/browser/drm_test.cc', 'tests/cefclient/browser/drm_test.h', + 'tests/cefclient/browser/image_cache.cc', + 'tests/cefclient/browser/image_cache.h', 'tests/cefclient/browser/main_context.cc', 'tests/cefclient/browser/main_context.h', 'tests/cefclient/browser/main_context_impl.cc', @@ -278,6 +283,13 @@ 'tests/cefclient/resources/window.html', 'tests/cefclient/resources/xmlhttprequest.html', ], + 'cefclient_sources_resources_extensions_set_page_color': [ + 'tests/cefclient/resources/extensions/set_page_color/icon.png', + 'tests/cefclient/resources/extensions/set_page_color/manifest.json', + 'tests/cefclient/resources/extensions/set_page_color/popup.html', + 'tests/cefclient/resources/extensions/set_page_color/popup.js', + 'tests/cefclient/resources/extensions/set_page_color/README.md', + ], 'cefclient_sources_win': [ 'tests/cefclient/browser/browser_window_osr_win.cc', 'tests/cefclient/browser/browser_window_osr_win.h', @@ -417,8 +429,11 @@ 'tests/ceftests/dom_unittest.cc', 'tests/ceftests/download_unittest.cc', 'tests/ceftests/draggable_regions_unittest.cc', - 'tests/ceftests/file_util.cc', - 'tests/ceftests/file_util.h', + 'tests/ceftests/extensions/background_unittest.cc', + 'tests/ceftests/extensions/chrome_tabs_unittest.cc', + 'tests/ceftests/extensions/extension_test_handler.cc', + 'tests/ceftests/extensions/extension_test_handler.h', + 'tests/ceftests/extensions/view_unittest.cc', 'tests/ceftests/file_util_unittest.cc', 'tests/ceftests/frame_unittest.cc', 'tests/ceftests/geolocation_unittest.cc', @@ -494,15 +509,14 @@ 'tests/ceftests/run_all_unittests_mac.mm', ], 'ceftests_sources_mac_helper': [ - 'tests/shared/browser/resource_util.cc', + 'tests/shared/browser/file_util.cc', + 'tests/shared/browser/file_util.h', 'tests/shared/browser/resource_util.h', 'tests/shared/browser/resource_util_mac.mm', 'tests/shared/browser/resource_util_posix.cc', 'tests/ceftests/client_app_delegates.cc', 'tests/ceftests/cookie_unittest.cc', 'tests/ceftests/dom_unittest.cc', - 'tests/ceftests/file_util.cc', - 'tests/ceftests/file_util.h', 'tests/ceftests/frame_unittest.cc', 'tests/ceftests/message_router_unittest.cc', 'tests/ceftests/navigation_unittest.cc', diff --git a/include/capi/cef_browser_capi.h b/include/capi/cef_browser_capi.h index 3c830bc0e..721b99c2d 100644 --- a/include/capi/cef_browser_capi.h +++ b/include/capi/cef_browser_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=b6308ab5e97eb9f7af95f1f4c371fd6e7edbf852$ +// $hash=a4831deeb05bc0a022c1a0ee6f1c484b338c741c$ // #ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_ @@ -115,7 +115,8 @@ typedef struct _cef_browser_t { void(CEF_CALLBACK* stop_load)(struct _cef_browser_t* self); /// - // Returns the globally unique identifier for this browser. + // Returns the globally unique identifier for this browser. This value is also + // used as the tabId for extension APIs. /// int(CEF_CALLBACK* get_identifier)(struct _cef_browser_t* self); @@ -812,6 +813,30 @@ typedef struct _cef_browser_host_t { /// void(CEF_CALLBACK* set_accessibility_state)(struct _cef_browser_host_t* self, cef_state_t accessibility_state); + + /// + // Enable notifications of auto resize via + // cef_display_handler_t::OnAutoResize. Notifications are disabled by default. + // |min_size| and |max_size| define the range of allowed sizes. + /// + void(CEF_CALLBACK* set_auto_resize_enabled)(struct _cef_browser_host_t* self, + int enabled, + const cef_size_t* min_size, + const cef_size_t* max_size); + + /// + // Returns the extension hosted in this browser or NULL if no extension is + // hosted. See cef_request_tContext::LoadExtension for details. + /// + struct _cef_extension_t*(CEF_CALLBACK* get_extension)( + struct _cef_browser_host_t* self); + + /// + // Returns true (1) if this browser is hosting an extension background script. + // Background hosts do not have a window and are not displayable. See + // cef_request_tContext::LoadExtension for details. + /// + int(CEF_CALLBACK* is_background_host)(struct _cef_browser_host_t* self); } cef_browser_host_t; /// diff --git a/include/capi/cef_display_handler_capi.h b/include/capi/cef_display_handler_capi.h index 4a9907654..412b1abf6 100644 --- a/include/capi/cef_display_handler_capi.h +++ b/include/capi/cef_display_handler_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=f0f3fd4cab00c0eb11956e674a111cb30d3af100$ +// $hash=979968e494e9d7c4d5117a1753acade5d0e79215$ // #ifndef CEF_INCLUDE_CAPI_CEF_DISPLAY_HANDLER_CAPI_H_ @@ -121,6 +121,16 @@ typedef struct _cef_display_handler_t { const cef_string_t* message, const cef_string_t* source, int line); + + /// + // Called when auto-resize is enabled via + // cef_browser_host_t::SetAutoResizeEnabled and the contents have auto- + // resized. |new_size| will be the desired size in view coordinates. Return + // true (1) if the resize was handled or false (0) for default handling. + /// + int(CEF_CALLBACK* on_auto_resize)(struct _cef_display_handler_t* self, + struct _cef_browser_t* browser, + const cef_size_t* new_size); } cef_display_handler_t; #ifdef __cplusplus diff --git a/include/capi/cef_extension_capi.h b/include/capi/cef_extension_capi.h new file mode 100644 index 000000000..a97f19e88 --- /dev/null +++ b/include/capi/cef_extension_capi.h @@ -0,0 +1,130 @@ +// Copyright (c) 2017 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. +// +// --------------------------------------------------------------------------- +// +// 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=aef2f0bc7a2491b5745b5eae8d02e2e8bd7335af$ +// + +#ifndef CEF_INCLUDE_CAPI_CEF_EXTENSION_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_EXTENSION_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" +#include "include/capi/cef_values_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct _cef_extension_handler_t; +struct _cef_request_context_t; + +/// +// Object representing an extension. Methods may be called on any thread unless +// otherwise indicated. +/// +typedef struct _cef_extension_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Returns the unique extension identifier. This is calculated based on the + // extension public key, if available, or on the extension path. See + // https://developer.chrome.com/extensions/manifest/key for details. + /// + // The resulting string must be freed by calling cef_string_userfree_free(). + cef_string_userfree_t(CEF_CALLBACK* get_identifier)( + struct _cef_extension_t* self); + + /// + // Returns the absolute path to the extension directory on disk. This value + // will be prefixed with PK_DIR_RESOURCES if a relative path was passed to + // cef_request_tContext::LoadExtension. + /// + // The resulting string must be freed by calling cef_string_userfree_free(). + cef_string_userfree_t(CEF_CALLBACK* get_path)(struct _cef_extension_t* self); + + /// + // Returns the extension manifest contents as a cef_dictionary_value_t object. + // See https://developer.chrome.com/extensions/manifest for details. + /// + struct _cef_dictionary_value_t*(CEF_CALLBACK* get_manifest)( + struct _cef_extension_t* self); + + /// + // Returns true (1) if this object is the same extension as |that| object. + // Extensions are considered the same if identifier, path and loader context + // match. + /// + int(CEF_CALLBACK* is_same)(struct _cef_extension_t* self, + struct _cef_extension_t* that); + + /// + // Returns the handler for this extension. Will return NULL for internal + // extensions or if no handler was passed to + // cef_request_tContext::LoadExtension. + /// + struct _cef_extension_handler_t*(CEF_CALLBACK* get_handler)( + struct _cef_extension_t* self); + + /// + // Returns the request context that loaded this extension. Will return NULL + // for internal extensions or if the extension has been unloaded. See the + // cef_request_tContext::LoadExtension documentation for more information + // about loader contexts. Must be called on the browser process UI thread. + /// + struct _cef_request_context_t*(CEF_CALLBACK* get_loader_context)( + struct _cef_extension_t* self); + + /// + // Returns true (1) if this extension is currently loaded. Must be called on + // the browser process UI thread. + /// + int(CEF_CALLBACK* is_loaded)(struct _cef_extension_t* self); + + /// + // Unload this extension if it is not an internal extension and is currently + // loaded. Will result in a call to + // cef_extension_tHandler::OnExtensionUnloaded on success. + /// + void(CEF_CALLBACK* unload)(struct _cef_extension_t* self); +} cef_extension_t; + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_EXTENSION_CAPI_H_ diff --git a/include/capi/cef_extension_handler_capi.h b/include/capi/cef_extension_handler_capi.h new file mode 100644 index 000000000..b0328fa64 --- /dev/null +++ b/include/capi/cef_extension_handler_capi.h @@ -0,0 +1,183 @@ +// Copyright (c) 2017 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. +// +// --------------------------------------------------------------------------- +// +// 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=b49f4c91db8eccdfe9ded503d8bb32ee0e433f60$ +// + +#ifndef CEF_INCLUDE_CAPI_CEF_EXTENSION_HANDLER_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_EXTENSION_HANDLER_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" +#include "include/capi/cef_browser_capi.h" +#include "include/capi/cef_extension_capi.h" +#include "include/capi/cef_stream_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct _cef_client_t; + +/// +// Callback structure used for asynchronous continuation of +// cef_extension_tHandler::GetExtensionResource. +/// +typedef struct _cef_get_extension_resource_callback_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Continue the request. Read the resource contents from |stream|. + /// + void(CEF_CALLBACK* cont)(struct _cef_get_extension_resource_callback_t* self, + struct _cef_stream_reader_t* stream); + + /// + // Cancel the request. + /// + void(CEF_CALLBACK* cancel)( + struct _cef_get_extension_resource_callback_t* self); +} cef_get_extension_resource_callback_t; + +/// +// Implement this structure to handle events related to browser extensions. The +// functions of this structure will be called on the UI thread. See +// cef_request_tContext::LoadExtension for information about extension loading. +/// +typedef struct _cef_extension_handler_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Called if the cef_request_tContext::LoadExtension request fails. |result| + // will be the error code. + /// + void(CEF_CALLBACK* on_extension_load_failed)( + struct _cef_extension_handler_t* self, + cef_errorcode_t result); + + /// + // Called if the cef_request_tContext::LoadExtension request succeeds. + // |extension| is the loaded extension. + /// + void(CEF_CALLBACK* on_extension_loaded)(struct _cef_extension_handler_t* self, + struct _cef_extension_t* extension); + + /// + // Called after the cef_extension_t::Unload request has completed. + /// + void(CEF_CALLBACK* on_extension_unloaded)( + struct _cef_extension_handler_t* self, + struct _cef_extension_t* extension); + + /// + // Called when an extension needs a browser to host a background script + // specified via the "background" manifest key. The browser will have no + // visible window and cannot be displayed. |extension| is the extension that + // is loading the background script. |url| is an internally generated + // reference to an HTML page that will be used to load the background script + // via a + + +
red
+
blue
+
green
+
yellow
+ + diff --git a/tests/cefclient/resources/extensions/set_page_color/popup.js b/tests/cefclient/resources/extensions/set_page_color/popup.js new file mode 100644 index 000000000..6f627087c --- /dev/null +++ b/tests/cefclient/resources/extensions/set_page_color/popup.js @@ -0,0 +1,16 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +function click(e) { + chrome.tabs.executeScript(null, + {code:"document.body.style.backgroundColor='" + e.target.id + "'"}); +} + +document.addEventListener('DOMContentLoaded', function () { + var divs = document.querySelectorAll('div'); + for (var i = 0; i < divs.length; i++) { + divs[i].addEventListener('click', click); + } +}); diff --git a/tests/cefclient/resources/win/cefclient.rc b/tests/cefclient/resources/win/cefclient.rc index 6ae63b364..b284f7618 100644 --- a/tests/cefclient/resources/win/cefclient.rc +++ b/tests/cefclient/resources/win/cefclient.rc @@ -52,6 +52,11 @@ IDS_WINDOW_ICON_1X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.1x.png IDS_WINDOW_ICON_2X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.2x.png" IDS_XMLHTTPREQUEST_HTML BINARY "..\\xmlhttprequest.html" +IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG BINARY "..\\extensions\\set_page_color\\icon.png" +IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON BINARY "..\\extensions\\set_page_color\\manifest.json" +IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML BINARY "..\\extensions\\set_page_color\\popup.html" +IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS BINARY "..\\extensions\\set_page_color\\popup.js" + ///////////////////////////////////////////////////////////////////////////// // // Icon diff --git a/tests/cefsimple/CMakeLists.txt.in b/tests/cefsimple/CMakeLists.txt.in index 0f7b97147..9c825330e 100644 --- a/tests/cefsimple/CMakeLists.txt.in +++ b/tests/cefsimple/CMakeLists.txt.in @@ -108,7 +108,6 @@ if(OS_MACOSX) add_dependencies(${CEF_TARGET} libcef_dll_wrapper "${CEF_HELPER_TARGET}") target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS}) set_target_properties(${CEF_TARGET} PROPERTIES - RESOURCE "${CEFSIMPLE_RESOURCES_SRCS}" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist ) @@ -130,12 +129,12 @@ if(OS_MACOSX) # Fix the framework rpath in the main executable. FIX_MACOSX_MAIN_FRAMEWORK_RPATH(${CEF_TARGET}) - if(NOT ${CMAKE_GENERATOR} STREQUAL "Xcode") - # Manually process and copy over resource files. - # The Xcode generator handles this via the set_target_properties RESOURCE directive. - set(PREFIXES "mac/") # Remove these prefixes from input file paths. - COPY_MACOSX_RESOURCES("${CEFSIMPLE_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}") - endif() + # Manually process and copy over resource files. + # The Xcode generator can support this via the set_target_properties RESOURCE + # directive but that doesn't properly handle nested resource directories. + # Remove these prefixes from input file paths. + set(PREFIXES "mac/") + COPY_MACOSX_RESOURCES("${CEFSIMPLE_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}") endif() diff --git a/tests/ceftests/CMakeLists.txt.in b/tests/ceftests/CMakeLists.txt.in index 099bfca04..f8d719a93 100644 --- a/tests/ceftests/CMakeLists.txt.in +++ b/tests/ceftests/CMakeLists.txt.in @@ -128,7 +128,6 @@ if(OS_MACOSX) add_dependencies(${CEF_TARGET} libcef_dll_wrapper cef_gtest "${CEF_HELPER_TARGET}") target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper cef_gtest ${CEF_STANDARD_LIBS}) set_target_properties(${CEF_TARGET} PROPERTIES - RESOURCE "${UNITTESTS_RESOURCES_SRCS}" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/Info.plist ) @@ -150,16 +149,15 @@ if(OS_MACOSX) # Fix the framework rpath in the main executable. FIX_MACOSX_MAIN_FRAMEWORK_RPATH(${CEF_TARGET}) - if(NOT ${CMAKE_GENERATOR} STREQUAL "Xcode") - # Manually process and copy over resource files. - # The Xcode generator handles this via the set_target_properties RESOURCE directive. - # Remove these prefixes from input file paths. - set(PREFIXES - "resources/mac/" - "../shared/resources/" - ) - COPY_MACOSX_RESOURCES("${UNITTESTS_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}") - endif() + # Manually process and copy over resource files. + # The Xcode generator can support this via the set_target_properties RESOURCE + # directive but that doesn't properly handle nested resource directories. + # Remove these prefixes from input file paths. + set(PREFIXES + "resources/mac/" + "../shared/resources/" + ) + COPY_MACOSX_RESOURCES("${UNITTESTS_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}") endif() diff --git a/tests/ceftests/display_unittest.cc b/tests/ceftests/display_unittest.cc index 19aea6eba..0f72553a9 100644 --- a/tests/ceftests/display_unittest.cc +++ b/tests/ceftests/display_unittest.cc @@ -6,6 +6,7 @@ #include "include/base/cef_bind.h" #include "include/wrapper/cef_closure_task.h" +#include "tests/ceftests/routing_test_handler.h" #include "tests/ceftests/test_handler.h" #include "tests/gtest/include/gtest/gtest.h" @@ -143,3 +144,101 @@ TEST(DisplayTest, Title) { handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } + +namespace { + +const char kAutoResizeUrl[] = "http://tests-display/auto-resize.html"; + +class AutoResizeTestHandler : public RoutingTestHandler { + public: + AutoResizeTestHandler() {} + + void RunTest() override { + // Add the resources that we will navigate to/from. + AddResource(kAutoResizeUrl, + "
Content
", + "text/html"); + + // Create the browser. + CreateBrowser(kAutoResizeUrl); + + // Time out the test after a reasonable period of time. + SetTestTimeout(); + } + + void OnAfterCreated(CefRefPtr browser) override { + RoutingTestHandler::OnAfterCreated(browser); + browser->GetHost()->SetAutoResizeEnabled(true, CefSize(10, 10), + CefSize(500, 500)); + } + + bool OnAutoResize(CefRefPtr browser, + const CefSize& new_size) override { + if (!got_auto_resize1_) { + got_auto_resize1_.yes(); + EXPECT_EQ(50, new_size.width); + EXPECT_EQ(18, new_size.height); + + // Trigger a resize. + browser->GetMainFrame()->ExecuteJavaScript( + "document.getElementById('a').innerText='New Content';", + kAutoResizeUrl, 0); + } else if (!got_auto_resize2_) { + got_auto_resize2_.yes(); + EXPECT_EQ(50, new_size.width); + EXPECT_EQ(36, new_size.height); + + // Disable resize notifications. + browser->GetHost()->SetAutoResizeEnabled(false, CefSize(), CefSize()); + + // There should be no more resize notifications. End the test after a + // short delay. + browser->GetMainFrame()->ExecuteJavaScript( + "document.getElementById('a').innerText='New Content Again';" + "var interval = setInterval(function() {" + "window.testQuery({request:'done'});clearInterval(interval);}, 50);", + kAutoResizeUrl, 0); + } else { + EXPECT_TRUE(false); // Not reached. + } + return true; + } + + bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64 query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) override { + EXPECT_STREQ("done", request.ToString().c_str()); + EXPECT_FALSE(got_done_message_); + got_done_message_.yes(); + DestroyTest(); + return true; + } + + void DestroyTest() override { + EXPECT_TRUE(got_auto_resize1_); + EXPECT_TRUE(got_auto_resize2_); + EXPECT_TRUE(got_done_message_); + TestHandler::DestroyTest(); + } + + private: + TrackCallback got_auto_resize1_; + TrackCallback got_auto_resize2_; + TrackCallback got_done_message_; + + IMPLEMENT_REFCOUNTING(AutoResizeTestHandler); +}; + +} // namespace + +// Test OnAutoResize notification. +TEST(DisplayTest, AutoResize) { + CefRefPtr handler = new AutoResizeTestHandler(); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} diff --git a/tests/ceftests/download_unittest.cc b/tests/ceftests/download_unittest.cc index 9d874129c..6a24e39f5 100644 --- a/tests/ceftests/download_unittest.cc +++ b/tests/ceftests/download_unittest.cc @@ -5,9 +5,9 @@ #include "include/cef_scheme.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_scoped_temp_dir.h" -#include "tests/ceftests/file_util.h" #include "tests/ceftests/test_handler.h" #include "tests/gtest/include/gtest/gtest.h" +#include "tests/shared/browser/file_util.h" namespace { @@ -155,7 +155,8 @@ class DownloadTestHandler : public TestHandler { // Create a new temporary directory. EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); - test_path_ = file_util::JoinPath(temp_dir_.GetPath(), kTestFileName); + test_path_ = + client::file_util::JoinPath(temp_dir_.GetPath(), kTestFileName); if (test_mode_ == NAVIGATED) { // Add the resource that we'll navigate to. @@ -318,7 +319,7 @@ class DownloadTestHandler : public TestHandler { if (test_mode_ != PENDING) { // Verify the file contents. std::string contents; - EXPECT_TRUE(file_util::ReadFileToString(test_path_, &contents)); + EXPECT_TRUE(client::file_util::ReadFileToString(test_path_, &contents)); EXPECT_STREQ(kTestContent, contents.c_str()); } diff --git a/tests/ceftests/extensions/background_unittest.cc b/tests/ceftests/extensions/background_unittest.cc new file mode 100644 index 000000000..f61cac19b --- /dev/null +++ b/tests/ceftests/extensions/background_unittest.cc @@ -0,0 +1,291 @@ +// Copyright (c) 2017 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 "tests/ceftests/extensions/extension_test_handler.h" + +#include "tests/ceftests/test_util.h" +#include "tests/shared/browser/client_app_browser.h" +#include "tests/shared/browser/extension_util.h" + +using client::ClientAppBrowser; + +namespace { + +const char kExtensionPath[] = "background-extension"; +const char kBackgroundScript[] = "background.js"; +// HTML file created internally to load the background script. +const char kGeneratedBackgroundPage[] = "_generated_background_page.html"; + +// Test load/unload of an extension with a background script. +class BackgroundLoadUnloadTestHandler : public ExtensionTestHandler { + public: + explicit BackgroundLoadUnloadTestHandler( + RequestContextType request_context_type) + : ExtensionTestHandler(request_context_type) { + // Only creating the extension browser. + set_create_main_browser(false); + } + + // CefExtensionHandler methods: + void OnExtensionLoaded(CefRefPtr extension) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension->IsLoaded()); + EXPECT_TRUE(extension->GetLoaderContext()); + EXPECT_TRUE( + loader_request_context()->IsSame(extension->GetLoaderContext())); + VerifyExtension(extension); + + EXPECT_FALSE(got_loaded_); + got_loaded_.yes(); + + EXPECT_FALSE(extension_); + extension_ = extension; + + background_page_url_ = GetExtensionURL(extension, kGeneratedBackgroundPage); + + // Add extension resources. + script_url_ = GetExtensionURL(extension, kBackgroundScript); + AddResource(script_url_, GetMessageJS("extension_onload"), + "text/javascript"); + } + + void OnExtensionUnloaded(CefRefPtr extension) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension); + EXPECT_FALSE(extension->IsLoaded()); + EXPECT_FALSE(extension->GetLoaderContext()); + + EXPECT_FALSE(got_unloaded_); + got_unloaded_.yes(); + + EXPECT_TRUE(extension_); + EXPECT_TRUE(extension_->IsSame(extension)); + + // The extension should no longer be registered with the context. + if (loader_request_context()) + VerifyExtensionInContext(extension, loader_request_context(), false, + true); + if (request_context() && !request_context_same_loader()) + VerifyExtensionInContext(extension, request_context(), false, false); + + extension_ = NULL; + + // Execute asynchronously so call stacks have a chance to unwind. + // Will close the browser windows. + CefPostTask( + TID_UI, + base::Bind(&BackgroundLoadUnloadTestHandler::DestroyTest, this)); + } + + bool OnBeforeBackgroundBrowser(CefRefPtr extension, + const CefString& url, + CefRefPtr& client, + CefBrowserSettings& settings) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension->IsLoaded()); + EXPECT_TRUE(extension->GetLoaderContext()); + EXPECT_TRUE( + loader_request_context()->IsSame(extension->GetLoaderContext())); + VerifyExtension(extension); + + const std::string& background_page_url = + GetExtensionURL(extension, kGeneratedBackgroundPage); + EXPECT_STREQ(background_page_url.c_str(), url.ToString().c_str()); + + EXPECT_FALSE(client); + client = this; + + // Allow the browser creation. + return false; + } + + // CefLoadHandler methods: + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) override { + EXPECT_TRUE(browser->GetHost()->IsBackgroundHost()); + + CefRefPtr extension = browser->GetHost()->GetExtension(); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension_->IsSame(extension)); + + if (isLoading) { + EXPECT_FALSE(extension_browser_); + extension_browser_ = browser; + } else { + EXPECT_TRUE(browser->IsSame(extension_browser_)); + + const std::string& url = browser->GetMainFrame()->GetURL(); + EXPECT_STREQ(background_page_url_.c_str(), url.c_str()); + + EXPECT_FALSE(got_load_done_); + got_load_done_.yes(); + + TriggerDestroyTestIfDone(); + } + } + + // CefRequestHandler methods: + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + EXPECT_TRUE(browser->GetHost()->IsBackgroundHost()); + EXPECT_TRUE(browser->IsSame(extension_browser_)); + + CefRefPtr extension = browser->GetHost()->GetExtension(); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension_->IsSame(extension)); + + const std::string& url = request->GetURL(); + if (url == background_page_url_) { + EXPECT_FALSE(got_background_page_url_request_); + got_background_page_url_request_.yes(); + } else if (url == script_url_) { + EXPECT_FALSE(got_script_url_request_); + got_script_url_request_.yes(); + } else { + EXPECT_TRUE(false); // Not reached. + } + + // Handle the resource request. + return RoutingTestHandler::GetResourceHandler(browser, frame, request); + } + + CefRefPtr extension() const { return extension_; } + + // Verify |extension| contents. + void VerifyExtension(CefRefPtr extension) const { + EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(), + client::extension_util::GetInternalExtensionResourcePath( + extension->GetPath()) + .c_str()); + + CefRefPtr expected_manifest = CreateManifest(); + TestDictionaryEqual(expected_manifest, extension->GetManifest()); + + VerifyExtensionInContext(extension, loader_request_context(), true, true); + if (!request_context_same_loader()) + VerifyExtensionInContext(extension, request_context(), true, false); + } + + std::string GetExtensionURL(CefRefPtr extension, + const std::string& resource_path) const { + const std::string& identifier = extension->GetIdentifier(); + const std::string& origin = + client::extension_util::GetExtensionOrigin(identifier); + EXPECT_FALSE(origin.empty()); + return origin + resource_path; + } + + protected: + void OnLoadExtensions() override { + LoadExtension(kExtensionPath, CreateManifest()); + } + + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + EXPECT_STREQ("extension_onload", message.c_str()); + EXPECT_TRUE(browser->GetHost()->IsBackgroundHost()); + + CefRefPtr extension = browser->GetHost()->GetExtension(); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension_->IsSame(extension)); + + EXPECT_TRUE(browser->IsSame(extension_browser_)); + EXPECT_TRUE(browser->GetHost()->IsBackgroundHost()); + + EXPECT_FALSE(got_body_onload_); + got_body_onload_.yes(); + + TriggerDestroyTestIfDone(); + return true; + } + + void OnDestroyTest() override { + extension_browser_ = NULL; + + EXPECT_TRUE(got_loaded_); + EXPECT_TRUE(got_background_page_url_request_); + EXPECT_TRUE(got_script_url_request_); + EXPECT_TRUE(got_body_onload_); + EXPECT_TRUE(got_load_done_); + EXPECT_TRUE(got_unloaded_); + } + + // Create a manifest with background script. + CefRefPtr CreateManifest() const { + CefRefPtr manifest = + CreateDefaultManifest(ApiPermissionsList()); + + CefRefPtr background = CefDictionaryValue::Create(); + CefRefPtr scripts = CefListValue::Create(); + scripts->SetString(0, kBackgroundScript); + background->SetList("scripts", scripts); + manifest->SetDictionary("background", background); + + return manifest; + } + + void TriggerDestroyTestIfDone() { + if (got_body_onload_ && got_load_done_) { + TriggerDestroyTest(); + } + } + + virtual void TriggerDestroyTest() { + // Execute asynchronously so call stacks have a chance to unwind. + CefPostTask(TID_UI, + base::Bind(&BackgroundLoadUnloadTestHandler::UnloadExtension, + this, extension_)); + } + + CefRefPtr extension_; + std::string script_url_; + std::string background_page_url_; + CefRefPtr extension_browser_; + + TrackCallback got_loaded_; + TrackCallback got_background_page_url_request_; + TrackCallback got_script_url_request_; + TrackCallback got_body_onload_; + TrackCallback got_load_done_; + TrackCallback got_unloaded_; + + IMPLEMENT_REFCOUNTING(BackgroundLoadUnloadTestHandler); + DISALLOW_COPY_AND_ASSIGN(BackgroundLoadUnloadTestHandler); +}; + +} // namespace + +EXTENSION_TEST_GROUP_ALL(BackgroundLoadUnload, BackgroundLoadUnloadTestHandler); + +namespace { + +// Same as above but without the unload. Only do this with a custom context to +// avoid poluting the global context. +class BackgroundLoadNoUnloadTestHandler + : public BackgroundLoadUnloadTestHandler { + public: + explicit BackgroundLoadNoUnloadTestHandler( + RequestContextType request_context_type) + : BackgroundLoadUnloadTestHandler(request_context_type) {} + + protected: + void TriggerDestroyTest() override { + // Release everything that references the request context. This should + // trigger unload of the extension. + CloseBrowser(extension_browser_, false); + extension_browser_ = NULL; + ReleaseRequestContexts(); + } +}; + +} // namespace + +EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(BackgroundLoadNoUnload, + BackgroundLoadNoUnloadTestHandler); diff --git a/tests/ceftests/extensions/chrome_tabs_unittest.cc b/tests/ceftests/extensions/chrome_tabs_unittest.cc new file mode 100644 index 000000000..b7a8e72b4 --- /dev/null +++ b/tests/ceftests/extensions/chrome_tabs_unittest.cc @@ -0,0 +1,1048 @@ +// Copyright (c) 2017 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 "tests/ceftests/extensions/extension_test_handler.h" + +#include + +#include "tests/ceftests/test_util.h" +#include "tests/shared/browser/extension_util.h" + +#define TABS_TEST_GROUP_ALL(name, test_class) \ + EXTENSION_TEST_GROUP_ALL(ChromeTabs##name, test_class) +#define TABS_TEST_GROUP_MINIMAL(name, test_class) \ + EXTENSION_TEST_GROUP_MINIMAL(ChromeTabs##name, test_class) + +namespace { + +const char kMainBrowserURL[] = "https://test-extensions.com/chrome-tabs"; +const char kExtensionPath[] = "tabs-extension"; +const char kSuccessMessage[] = "success"; + +// Base class for testing chrome.tabs methods. +// See https://developer.chrome.com/extensions/tabs +class TabsTestHandler : public ExtensionTestHandler { + public: + explicit TabsTestHandler(RequestContextType request_context_type) + : ExtensionTestHandler(request_context_type), + create_main_browser_first_(false), + expect_get_active_browser_(true), + expect_success_in_main_browser_(true), + expected_api_call_count_(1), + got_get_active_browser_count_(0), + got_can_access_browser_count_(0) {} + + // CefExtensionHandler methods: + void OnExtensionLoaded(CefRefPtr extension) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_FALSE(got_extension_loaded_); + got_extension_loaded_.yes(); + + // Verify |extension| contents. + EXPECT_FALSE(extension->GetIdentifier().empty()); + EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(), + client::extension_util::GetInternalExtensionResourcePath( + extension->GetPath()) + .c_str()); + TestDictionaryEqual(CreateManifest(), extension->GetManifest()); + + EXPECT_FALSE(extension_); + extension_ = extension; + + if (create_main_browser_first_) + CreateBrowserForExtensionIfReady(); + else + CreateBrowserForExtension(); + } + + void OnExtensionUnloaded(CefRefPtr extension) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension_); + EXPECT_TRUE(extension_->IsSame(extension)); + EXPECT_FALSE(got_extension_unloaded_); + got_extension_unloaded_.yes(); + extension_ = NULL; + + // Execute asynchronously so call stacks have a chance to unwind. + // Will close the browser windows. + CefPostTask(TID_UI, base::Bind(&TabsTestHandler::DestroyTest, this)); + } + + CefRefPtr GetActiveBrowser(CefRefPtr extension, + CefRefPtr browser, + bool include_incognito) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension_); + EXPECT_TRUE(extension_->IsSame(extension)); + EXPECT_TRUE(main_browser_); + + EXPECT_LE(got_get_active_browser_count_, expected_api_call_count_); + got_get_active_browser_count_++; + + // Tabs APIs will operate on the main browser. + return main_browser_; + } + + bool CanAccessBrowser(CefRefPtr extension, + CefRefPtr browser, + bool include_incognito, + CefRefPtr target_browser) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension_); + EXPECT_TRUE(extension_->IsSame(extension)); + EXPECT_TRUE(main_browser_); + EXPECT_TRUE(main_browser_->IsSame(target_browser)); + + EXPECT_LE(got_can_access_browser_count_, expected_api_call_count_); + got_can_access_browser_count_++; + + return true; + } + + // CefLoadHandler methods: + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) override { + if (isLoading) { + // Keep a reference to both browsers. + if (browser->GetHost()->GetExtension()) { + EXPECT_FALSE(extension_browser_); + extension_browser_ = browser; + } else { + EXPECT_FALSE(main_browser_); + main_browser_ = browser; + } + } else { + const std::string& url = browser->GetMainFrame()->GetURL(); + if (browser->GetHost()->GetExtension()) { + EXPECT_TRUE(browser->IsSame(extension_browser_)); + EXPECT_STREQ(extension_url_.c_str(), url.c_str()); + } else { + EXPECT_TRUE(browser->IsSame(main_browser_)); + EXPECT_STREQ(kMainBrowserURL, url.c_str()); + } + } + } + + // CefRequestHandler methods: + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + const std::string& url = request->GetURL(); + if (url == kMainBrowserURL) { + EXPECT_TRUE(browser->IsSame(main_browser_)); + EXPECT_FALSE(got_main_url_request_); + got_main_url_request_.yes(); + } else if (url == extension_url_) { + EXPECT_TRUE(browser->IsSame(extension_browser_)); + EXPECT_FALSE(got_extension_url_request_); + got_extension_url_request_.yes(); + } + + // Handle the resource request. + return RoutingTestHandler::GetResourceHandler(browser, frame, request); + } + + protected: + void OnAddMainBrowserResources() override { + AddResource(kMainBrowserURL, GetMainBrowserHTML(), "text/html"); + } + + void OnCreateMainBrowser() override { + CreateBrowser(kMainBrowserURL, request_context()); + } + + void OnLoadExtensions() override { + LoadExtension(kExtensionPath, CreateManifest()); + } + + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + if (message == "main_onload") { + // From body onLoad in the main browser. + EXPECT_TRUE(browser->IsSame(main_browser_)); + EXPECT_FALSE(got_main_body_onload_); + got_main_body_onload_.yes(); + if (create_main_browser_first_) + CreateBrowserForExtensionIfReady(); + TriggerTabsApiJSFunctionIfReady(); + return true; + } + if (message == "extension_onload") { + // From body onLoad in the extension browser. + EXPECT_TRUE(browser->IsSame(extension_browser_)); + EXPECT_FALSE(got_extension_body_onload_); + got_extension_body_onload_.yes(); + TriggerTabsApiJSFunctionIfReady(); + return true; + } + + // The success message usually orginates from the logic in + // GetMainBrowserSuccessHEAD/BODY(). It may occasionally originate from the + // extension browser if we don't know how to detect success in the main + // browser. + if (expect_success_in_main_browser_) { + EXPECT_TRUE(browser->IsSame(main_browser_)); + } else { + EXPECT_TRUE(browser->IsSame(extension_browser_)); + } + EXPECT_FALSE(got_success_message_); + got_success_message_.yes(); + EXPECT_STREQ(kSuccessMessage, message.c_str()); + TriggerDestroyTest(); + return true; + } + + void OnDestroyTest() override { + main_browser_ = NULL; + extension_browser_ = NULL; + + EXPECT_TRUE(got_extension_loaded_); + EXPECT_TRUE(got_main_url_request_); + EXPECT_TRUE(got_extension_url_request_); + EXPECT_TRUE(got_main_body_onload_); + EXPECT_TRUE(got_extension_body_onload_); + EXPECT_TRUE(got_trigger_api_function_); + EXPECT_TRUE(got_success_message_); + EXPECT_TRUE(got_extension_unloaded_); + + if (expect_get_active_browser_) { + EXPECT_EQ(expected_api_call_count_, got_get_active_browser_count_); + EXPECT_EQ(0, got_can_access_browser_count_); + } else { + EXPECT_EQ(0, got_get_active_browser_count_); + EXPECT_EQ(expected_api_call_count_, got_can_access_browser_count_); + } + } + + // Create a manifest that grants access to the tabs API. + virtual CefRefPtr CreateManifest() const { + ApiPermissionsList api_permissions; + api_permissions.push_back("tabs"); + return CreateDefaultManifest(api_permissions); + } + + // Add resources in the extension browser. + virtual void OnAddExtensionResources(const std::string& origin) { + extension_url_ = origin + "extension.html"; + AddResource(extension_url_, GetExtensionHTML(), "text/html"); + } + + // Returns the target tabId (null, or value >= 0). + virtual std::string GetTargetTabId() const { return "null"; } + + // Returns the logic in the main browser that triggers on success. It should + // execute GetMessageJS(kSuccessMessage). + virtual std::string GetMainBrowserSuccessHEAD() const { + return std::string(); + } + virtual std::string GetMainBrowserSuccessBODY() const { + return std::string(); + } + + // Returns the HTML that will be loaded in the main browser. + virtual std::string GetMainBrowserHTML() const { + return "" + GetMainBrowserSuccessHEAD() + + "Main" + + GetMainBrowserSuccessBODY() + ""; + } + + // Returns the chrome.tabs.* JS that is executed in the extension browser + // when the triggerTabsApi() JS function is called. + virtual std::string GetTabsApiJS() const = 0; + + // Returns the JS that will be loaded in the extension browser. This + // implements the triggerTabsApi() JS function called from + // TriggerTabsApiJSFunction(). + virtual std::string GetExtensionJS() const { + return "function triggerTabsApi() {" + GetTabsApiJS() + "}"; + } + + // Returns the HTML that will be loaded in the extension browser. + virtual std::string GetExtensionHTML() const { + return "Extension"; + } + + virtual void TriggerDestroyTest() { + // Execute asynchronously so call stacks have a chance to unwind. + CefPostTask(TID_UI, base::Bind(&TabsTestHandler::UnloadExtension, this, + extension_)); + } + + CefRefPtr extension() const { return extension_; } + std::string extension_url() const { return extension_url_; } + CefRefPtr main_browser() const { return main_browser_; } + CefRefPtr extension_browser() const { return extension_browser_; } + + void set_create_main_browser_first(bool val) { + create_main_browser_first_ = val; + } + void set_expect_get_active_browser(bool val) { + expect_get_active_browser_ = val; + } + void set_expect_success_in_main_browser(bool val) { + expect_success_in_main_browser_ = val; + } + void set_expected_api_call_count(int val) { expected_api_call_count_ = val; } + + bool got_success_message() const { return got_success_message_; } + + private: + void CreateBrowserForExtensionIfReady() { + DCHECK(create_main_browser_first_); + if (extension_ && main_browser_) + CreateBrowserForExtension(); + } + + void CreateBrowserForExtension() { + const std::string& identifier = extension_->GetIdentifier(); + EXPECT_FALSE(identifier.empty()); + const std::string& origin = + client::extension_util::GetExtensionOrigin(identifier); + EXPECT_FALSE(origin.empty()); + + // Add extension resources. + OnAddExtensionResources(origin); + + // Create a browser to host the extension. + CreateBrowser(extension_url_, request_context()); + } + + void TriggerTabsApiJSFunctionIfReady() { + if (got_main_body_onload_ && got_extension_body_onload_) + TriggerTabsApiJSFunction(); + } + + void TriggerTabsApiJSFunction() { + EXPECT_FALSE(got_trigger_api_function_); + got_trigger_api_function_.yes(); + + extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerTabsApi();", + extension_url_, 0); + } + + // If true the main browser will be created before the extension browser. + // Otherwise the creation order is undefined. + bool create_main_browser_first_; + + // If true we expect GetActiveBrowser() but not CanAccessBrowser() to be + // called. Else visa-versa. + bool expect_get_active_browser_; + + // If true we expect the success message to be delivered in the main browser. + // Else expect it in the extension browser. + bool expect_success_in_main_browser_; + + // Number of expected calls to GetActiveBrowser or CanAccessBrowser. This + // should match the number of calls to chrome.tabs.* API functions in the + // test. + int expected_api_call_count_; + + CefRefPtr extension_; + std::string extension_url_; + CefRefPtr main_browser_; + CefRefPtr extension_browser_; + + TrackCallback got_extension_loaded_; + TrackCallback got_main_url_request_; + TrackCallback got_extension_url_request_; + TrackCallback got_main_body_onload_; + TrackCallback got_extension_body_onload_; + TrackCallback got_trigger_api_function_; + TrackCallback got_success_message_; + TrackCallback got_extension_unloaded_; + + int got_get_active_browser_count_; + int got_can_access_browser_count_; +}; + +// +// chrome.tabs.executeScript tests. +// + +// Base class for chrome.tabs.executeScript tests. +class ExecuteScriptTestHandler : public TabsTestHandler { + public: + explicit ExecuteScriptTestHandler(RequestContextType request_context_type) + : TabsTestHandler(request_context_type) {} + + protected: + std::string GetMainBrowserSuccessHEAD() const override { + return ""; + } + + // Returns the code that will be injected as a content script. + virtual std::string GetContentScriptJS() const { + // Execute the onTrigger() JS function. + return "var s = document.createElement('script');" + "s.textContent = 'onTrigger();';" + "document.head.appendChild(s);"; + } + + std::string GetTabsApiJS() const override { + return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" + + GetContentScriptJS() + "\"});"; + } +}; + +// Test for chrome.tabs.executeScript with a null tabId value. +class ExecuteScriptNullTabTestHandler : public ExecuteScriptTestHandler { + public: + explicit ExecuteScriptNullTabTestHandler( + RequestContextType request_context_type) + : ExecuteScriptTestHandler(request_context_type) {} + + private: + IMPLEMENT_REFCOUNTING(ExecuteScriptNullTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ExecuteScriptNullTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ExecuteScriptNullTab, ExecuteScriptNullTabTestHandler); + +namespace { + +// Test for chrome.tabs.executeScript with an explicit tabId value. +class ExecuteScriptExplicitTabTestHandler : public ExecuteScriptTestHandler { + public: + explicit ExecuteScriptExplicitTabTestHandler( + RequestContextType request_context_type) + : ExecuteScriptTestHandler(request_context_type) { + // Create the main browser first so we can retrieve the id. + set_create_main_browser_first(true); + // When a tabId is specified we should get a call to CanAccessBrowser + // instead of GetActiveBrowser. + set_expect_get_active_browser(false); + } + + protected: + std::string GetTargetTabId() const override { + DCHECK(main_browser()); + std::stringstream ss; + ss << main_browser()->GetIdentifier(); + return ss.str(); + } + + private: + IMPLEMENT_REFCOUNTING(ExecuteScriptExplicitTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ExecuteScriptExplicitTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ExecuteScriptExplicitTab, + ExecuteScriptExplicitTabTestHandler); + +namespace { + +// Test for chrome.tabs.executeScript with a file argument loading a content +// script. +class ExecuteScriptFileTestHandler : public ExecuteScriptTestHandler { + public: + explicit ExecuteScriptFileTestHandler(RequestContextType request_context_type) + : ExecuteScriptTestHandler(request_context_type) {} + + // CefExtensionHandler methods: + bool GetExtensionResource( + CefRefPtr extension, + CefRefPtr browser, + const CefString& file, + CefRefPtr callback) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(this->extension()); + EXPECT_TRUE(this->extension()->IsSame(extension)); + + if (file == "script.js") { + EXPECT_FALSE(got_get_extension_resource_); + got_get_extension_resource_.yes(); + + const std::string& content = GetContentScriptJS(); + CefRefPtr stream = CefStreamReader::CreateForData( + const_cast(content.data()), content.size()); + callback->Continue(stream); + return true; + } + + EXPECT_FALSE(true); // Not reached. + return false; + } + + protected: + std::string GetTabsApiJS() const override { + return "chrome.tabs.executeScript(" + GetTargetTabId() + + ", {file:\"script.js\"});"; + } + + void OnDestroyTest() override { + ExecuteScriptTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_get_extension_resource_); + } + + private: + TrackCallback got_get_extension_resource_; + + IMPLEMENT_REFCOUNTING(ExecuteScriptFileTestHandler); + DISALLOW_COPY_AND_ASSIGN(ExecuteScriptFileTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ExecuteScriptFile, ExecuteScriptFileTestHandler); + +namespace { + +// Test for chrome.tabs.executeScript with a callback argument. +class ExecuteScriptCallbackTestHandler : public ExecuteScriptTestHandler { + public: + explicit ExecuteScriptCallbackTestHandler( + RequestContextType request_context_type) + : ExecuteScriptTestHandler(request_context_type) {} + + protected: + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + if (message == "callback") { + EXPECT_FALSE(got_callback_message_); + got_callback_message_.yes(); + EXPECT_TRUE(browser->IsSame(extension_browser())); + TriggerDestroyTest(); + return true; + } + return ExecuteScriptTestHandler::OnMessage(browser, message); + } + + std::string GetTabsApiJS() const override { + return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" + + GetContentScriptJS() + "\"}, function(results) {" + + GetMessageJS("callback") + "});"; + } + + void TriggerDestroyTest() override { + // Only destroy the test if we got both callbacks. + if (got_callback_message_ && got_success_message()) { + ExecuteScriptTestHandler::TriggerDestroyTest(); + } + } + + void OnDestroyTest() override { + ExecuteScriptTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_callback_message_); + } + + private: + TrackCallback got_callback_message_; + + IMPLEMENT_REFCOUNTING(ExecuteScriptCallbackTestHandler); + DISALLOW_COPY_AND_ASSIGN(ExecuteScriptCallbackTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_MINIMAL(ExecuteScriptCallback, + ExecuteScriptCallbackTestHandler); + +namespace { + +// Test for chrome.tabs.executeScript with execution occuring from a separate +// resource script. +class ExecuteScriptResourceTabTestHandler : public ExecuteScriptTestHandler { + public: + explicit ExecuteScriptResourceTabTestHandler( + RequestContextType request_context_type) + : ExecuteScriptTestHandler(request_context_type) {} + + // CefRequestHandler methods: + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + const std::string& url = request->GetURL(); + if (url == resource_url_) { + EXPECT_TRUE(browser->IsSame(extension_browser())); + EXPECT_FALSE(got_resource_url_request_); + got_resource_url_request_.yes(); + } + + return ExecuteScriptTestHandler::GetResourceHandler(browser, frame, + request); + } + + protected: + void OnAddExtensionResources(const std::string& origin) override { + ExecuteScriptTestHandler::OnAddExtensionResources(origin); + resource_url_ = origin + "resource.js"; + AddResource(resource_url_, GetExtensionJS(), "text/javascript"); + } + + std::string GetExtensionHTML() const override { + return "Extension"; + } + + void OnDestroyTest() override { + ExecuteScriptTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_resource_url_request_); + } + + private: + std::string resource_url_; + TrackCallback got_resource_url_request_; + + IMPLEMENT_REFCOUNTING(ExecuteScriptResourceTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ExecuteScriptResourceTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_MINIMAL(ExecuteScriptResource, + ExecuteScriptResourceTabTestHandler); + +// +// chrome.tabs.insertCSS tests. +// + +namespace { + +// Base class for chrome.tabs.insertCSS tests. +class InsertCSSTestHandler : public TabsTestHandler { + public: + explicit InsertCSSTestHandler(RequestContextType request_context_type) + : TabsTestHandler(request_context_type) {} + + protected: + std::string GetMainBrowserSuccessBODY() const override { + // We can't use a MutationObserver here because insertCSS does not modify + // the style attribute. We could detect the change by tracking modifications + // to document.styleSheets but that's complicated. Use a simple timer loop + // implementation calling getComputedStyle instead. + return ""; + } + + // Returns the code that will be injected as a content script. + virtual std::string GetContentScriptCSS() const { + return "body{background-color:red}"; + } + + std::string GetTabsApiJS() const override { + return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" + + GetContentScriptCSS() + "\"});"; + } +}; + +// Test for chrome.tabs.insertCSS with a null tabId value. +class InsertCSSNullTabTestHandler : public InsertCSSTestHandler { + public: + explicit InsertCSSNullTabTestHandler(RequestContextType request_context_type) + : InsertCSSTestHandler(request_context_type) {} + + private: + IMPLEMENT_REFCOUNTING(InsertCSSNullTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(InsertCSSNullTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(InsertCSSNullTab, InsertCSSNullTabTestHandler); + +namespace { + +// Test for chrome.tabs.insertCSS with an explicit tabId value. +class InsertCSSExplicitTabTestHandler : public InsertCSSTestHandler { + public: + explicit InsertCSSExplicitTabTestHandler( + RequestContextType request_context_type) + : InsertCSSTestHandler(request_context_type) { + // Create the main browser first so we can retrieve the id. + set_create_main_browser_first(true); + // When a tabId is specified we should get a call to CanAccessBrowser + // instead of GetActiveBrowser. + set_expect_get_active_browser(false); + } + + protected: + std::string GetTargetTabId() const override { + DCHECK(main_browser()); + std::stringstream ss; + ss << main_browser()->GetIdentifier(); + return ss.str(); + } + + private: + IMPLEMENT_REFCOUNTING(InsertCSSExplicitTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(InsertCSSExplicitTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(InsertCSSExplicitTab, InsertCSSExplicitTabTestHandler); + +namespace { + +// Test for chrome.tabs.insertCSS with a file argument loading a content +// script. +class InsertCSSFileTestHandler : public InsertCSSTestHandler { + public: + explicit InsertCSSFileTestHandler(RequestContextType request_context_type) + : InsertCSSTestHandler(request_context_type) {} + + // CefExtensionHandler methods: + bool GetExtensionResource( + CefRefPtr extension, + CefRefPtr browser, + const CefString& file, + CefRefPtr callback) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(this->extension()); + EXPECT_TRUE(this->extension()->IsSame(extension)); + + if (file == "script.css") { + EXPECT_FALSE(got_get_extension_resource_); + got_get_extension_resource_.yes(); + + const std::string& content = GetContentScriptCSS(); + CefRefPtr stream = CefStreamReader::CreateForData( + const_cast(content.data()), content.size()); + callback->Continue(stream); + return true; + } + + EXPECT_FALSE(true); // Not reached. + return false; + } + + protected: + std::string GetTabsApiJS() const override { + return "chrome.tabs.insertCSS(" + GetTargetTabId() + + ", {file:\"script.css\"});"; + } + + void OnDestroyTest() override { + InsertCSSTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_get_extension_resource_); + } + + private: + TrackCallback got_get_extension_resource_; + + IMPLEMENT_REFCOUNTING(InsertCSSFileTestHandler); + DISALLOW_COPY_AND_ASSIGN(InsertCSSFileTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(InsertCSSFile, InsertCSSFileTestHandler); + +namespace { + +// Test for chrome.tabs.insertCSS with a callback argument. +class InsertCSSCallbackTestHandler : public InsertCSSTestHandler { + public: + explicit InsertCSSCallbackTestHandler(RequestContextType request_context_type) + : InsertCSSTestHandler(request_context_type) {} + + protected: + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + if (message == "callback") { + EXPECT_FALSE(got_callback_message_); + got_callback_message_.yes(); + EXPECT_TRUE(browser->IsSame(extension_browser())); + TriggerDestroyTest(); + return true; + } + return InsertCSSTestHandler::OnMessage(browser, message); + } + + std::string GetTabsApiJS() const override { + return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" + + GetContentScriptCSS() + "\"}, function(results) {" + + GetMessageJS("callback") + "});"; + } + + void TriggerDestroyTest() override { + // Only destroy the test if we got both callbacks. + if (got_callback_message_ && got_success_message()) { + InsertCSSTestHandler::TriggerDestroyTest(); + } + } + + void OnDestroyTest() override { + InsertCSSTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_callback_message_); + } + + private: + TrackCallback got_callback_message_; + + IMPLEMENT_REFCOUNTING(InsertCSSCallbackTestHandler); + DISALLOW_COPY_AND_ASSIGN(InsertCSSCallbackTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_MINIMAL(InsertCSSCallback, InsertCSSCallbackTestHandler); + +namespace { + +// Test for chrome.tabs.insertCSS with execution occuring from a separate +// resource script. +class InsertCSSResourceTabTestHandler : public InsertCSSTestHandler { + public: + explicit InsertCSSResourceTabTestHandler( + RequestContextType request_context_type) + : InsertCSSTestHandler(request_context_type) {} + + // CefRequestHandler methods: + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + const std::string& url = request->GetURL(); + if (url == resource_url_) { + EXPECT_TRUE(browser->IsSame(extension_browser())); + EXPECT_FALSE(got_resource_url_request_); + got_resource_url_request_.yes(); + } + + return InsertCSSTestHandler::GetResourceHandler(browser, frame, request); + } + + protected: + void OnAddExtensionResources(const std::string& origin) override { + InsertCSSTestHandler::OnAddExtensionResources(origin); + resource_url_ = origin + "resource.js"; + AddResource(resource_url_, GetExtensionJS(), "text/javascript"); + } + + std::string GetExtensionHTML() const override { + return "Extension"; + } + + void OnDestroyTest() override { + InsertCSSTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_resource_url_request_); + } + + private: + std::string resource_url_; + TrackCallback got_resource_url_request_; + + IMPLEMENT_REFCOUNTING(InsertCSSResourceTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(InsertCSSResourceTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_MINIMAL(InsertCSSResource, InsertCSSResourceTabTestHandler); + +// +// chrome.tabs.setZoom/getZoom tests. +// + +namespace { + +// Base class for chrome.tabs.setZoom/getZoom tests. +class ZoomTestHandler : public TabsTestHandler { + public: + explicit ZoomTestHandler(RequestContextType request_context_type) + : TabsTestHandler(request_context_type) { + // We call API functions three times in this handler. + set_expected_api_call_count(3); + } + + protected: + std::string GetMainBrowserSuccessHEAD() const override { + return ""; + } + + std::string GetMainBrowserSuccessBODY() const override { + // We can't directly detect zoom changes, so instead we look for changes + // in window.innerWidth. + return ""; + } + + std::string GetTabsApiJS() const override { + // Results in a change to window.innerWidth. + return "chrome.tabs.setZoom(" + GetTargetTabId() + ", 2.0);"; + } + + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + if (message == "restored") { + EXPECT_TRUE(browser->IsSame(extension_browser())); + EXPECT_FALSE(got_restored_message_); + got_restored_message_.yes(); + // Destroy the test for real. + TabsTestHandler::TriggerDestroyTest(); + return true; + } + return TabsTestHandler::OnMessage(browser, message); + } + + void TriggerDestroyTest() override { + // Before destroying the test we need to restore the zoom factor so that + // it doesn't persist in the RequestContext. This also tests the callback + // argument and the getZoom function so there's no need to do that + // separately. + extension_browser()->GetMainFrame()->ExecuteJavaScript( + "chrome.tabs.setZoom(" + GetTargetTabId() + ", 1.0, function() {" + + "chrome.tabs.getZoom(" + GetTargetTabId() + + ", function(zoomFactor) { if (zoomFactor == 1.0) {" + + GetMessageJS("restored") + "}})});", + extension_url(), 0); + } + + void OnDestroyTest() override { + TabsTestHandler::OnDestroyTest(); + EXPECT_TRUE(got_restored_message_); + } + + private: + TrackCallback got_restored_message_; +}; + +// Test for chrome.tabs.setZoom/getZoom with a null tabId value. +class ZoomNullTabTestHandler : public ZoomTestHandler { + public: + explicit ZoomNullTabTestHandler(RequestContextType request_context_type) + : ZoomTestHandler(request_context_type) {} + + private: + IMPLEMENT_REFCOUNTING(ZoomNullTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ZoomNullTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ZoomNullTab, ZoomNullTabTestHandler); + +namespace { + +// Test for chrome.tabs.setZoom/getZoom with an explicit tabId value. +class ZoomExplicitTabTestHandler : public ZoomTestHandler { + public: + explicit ZoomExplicitTabTestHandler(RequestContextType request_context_type) + : ZoomTestHandler(request_context_type) { + // Create the main browser first so we can retrieve the id. + set_create_main_browser_first(true); + // When a tabId is specified we should get a call to CanAccessBrowser + // instead of GetActiveBrowser. + set_expect_get_active_browser(false); + } + + protected: + std::string GetTargetTabId() const override { + DCHECK(main_browser()); + std::stringstream ss; + ss << main_browser()->GetIdentifier(); + return ss.str(); + } + + private: + IMPLEMENT_REFCOUNTING(ZoomExplicitTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ZoomExplicitTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ZoomExplicitTab, ZoomExplicitTabTestHandler); + +// +// chrome.tabs.setZoomSettings/getZoomSettings tests. +// + +namespace { + +// Base class for chrome.tabs.setZoomSettings/getZoomSettings tests. +class ZoomSettingsTestHandler : public TabsTestHandler { + public: + explicit ZoomSettingsTestHandler(RequestContextType request_context_type) + : TabsTestHandler(request_context_type) { + // We call API functions two times in this handler. + set_expected_api_call_count(2); + // Success message will be delivered in the extension browser because we + // don't know how to detect zoom settings changes in the main browser. + set_expect_success_in_main_browser(false); + } + + protected: + std::string GetTabsApiJS() const override { + // Set and restore the zoom settings. This also tests the callback argument + // and the getZoomSettings function so there's no need to do that + // separately. This is safe because zoom settings are not persisted in the + // RequestContext across navigations. + return "chrome.tabs.setZoomSettings(" + GetTargetTabId() + + ", {mode: 'manual', scope: 'per-tab'}, function() {" + + "chrome.tabs.getZoomSettings(" + GetTargetTabId() + + ", function(zoomSettings) { if (zoomSettings.mode == 'manual' " + "&& zoomSettings.scope == 'per-tab') {" + + GetMessageJS(kSuccessMessage) + "}})});"; + } +}; + +// Test for chrome.tabs.setZoomSettings/getZoomSettings with a null tabId value. +class ZoomSettingsNullTabTestHandler : public ZoomSettingsTestHandler { + public: + explicit ZoomSettingsNullTabTestHandler( + RequestContextType request_context_type) + : ZoomSettingsTestHandler(request_context_type) {} + + private: + IMPLEMENT_REFCOUNTING(ZoomSettingsNullTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ZoomSettingsNullTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ZoomSettingsNullTab, ZoomSettingsNullTabTestHandler); + +namespace { + +// Test for chrome.tabs.setZoomSettings/getZoomSettings with an explicit tabId +// value. +class ZoomSettingsExplicitTabTestHandler : public ZoomSettingsTestHandler { + public: + explicit ZoomSettingsExplicitTabTestHandler( + RequestContextType request_context_type) + : ZoomSettingsTestHandler(request_context_type) { + // Create the main browser first so we can retrieve the id. + set_create_main_browser_first(true); + // When a tabId is specified we should get a call to CanAccessBrowser + // instead of GetActiveBrowser. + set_expect_get_active_browser(false); + } + + protected: + std::string GetTargetTabId() const override { + DCHECK(main_browser()); + std::stringstream ss; + ss << main_browser()->GetIdentifier(); + return ss.str(); + } + + private: + IMPLEMENT_REFCOUNTING(ZoomSettingsExplicitTabTestHandler); + DISALLOW_COPY_AND_ASSIGN(ZoomSettingsExplicitTabTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(ZoomSettingsExplicitTab, + ZoomSettingsExplicitTabTestHandler); diff --git a/tests/ceftests/extensions/extension_test_handler.cc b/tests/ceftests/extensions/extension_test_handler.cc new file mode 100644 index 000000000..b8f36962d --- /dev/null +++ b/tests/ceftests/extensions/extension_test_handler.cc @@ -0,0 +1,240 @@ +// Copyright (c) 2017 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 "tests/ceftests/extensions/extension_test_handler.h" + +#include "tests/ceftests/test_suite.h" +#include "tests/ceftests/test_util.h" + +ExtensionTestHandler::ExtensionTestHandler( + RequestContextType request_context_type) + : request_context_type_(request_context_type), create_main_browser_(true) { + // Verify supported flag combinations. + if (request_context_on_disk()) { + EXPECT_TRUE(request_context_is_custom()); + } + if (request_context_load_with_handler()) { + EXPECT_FALSE(request_context_load_without_handler()); + } + if (request_context_load_without_handler()) { + EXPECT_TRUE(request_context_with_handler()); + EXPECT_FALSE(request_context_load_with_handler()); + } +} + +ExtensionTestHandler::~ExtensionTestHandler() { + if (!request_context_temp_dir_.IsEmpty()) { + // Delete temporary directories on shutdown. + CefTestSuite::GetInstance()->RegisterTempDirectory( + request_context_temp_dir_.Take()); + } +} + +void ExtensionTestHandler::RunTest() { + if (create_main_browser_) + OnAddMainBrowserResources(); + + CefRefPtr rc_handler; + if (request_context_with_handler()) { + class Handler : public CefRequestContextHandler { + public: + explicit Handler(ExtensionTestHandler* test_handler) + : test_handler_(test_handler) {} + + void OnRequestContextInitialized( + CefRefPtr request_context) override { + if (test_handler_->create_main_browser()) { + // Load extensions after the RequestContext has been initialized by + // creation of the main browser. + test_handler_->OnLoadExtensions(); + } + } + + private: + ExtensionTestHandler* test_handler_; + + IMPLEMENT_REFCOUNTING(Handler); + }; + rc_handler = new Handler(this); + } + + if (request_context_is_custom()) { + CefRequestContextSettings settings; + + if (request_context_on_disk()) { + // Create a new temporary directory. + EXPECT_TRUE(request_context_temp_dir_.CreateUniqueTempDir()); + CefString(&settings.cache_path) = request_context_temp_dir_.GetPath(); + } + + request_context_ = CefRequestContext::CreateContext(settings, rc_handler); + } else { + request_context_ = CefRequestContext::CreateContext( + CefRequestContext::GetGlobalContext(), rc_handler); + } + + if (request_context_load_with_handler()) { + class Handler : public CefRequestContextHandler { + public: + Handler() {} + + private: + IMPLEMENT_REFCOUNTING(Handler); + }; + loader_request_context_ = + CefRequestContext::CreateContext(request_context_, new Handler()); + } else if (request_context_load_without_handler()) { + loader_request_context_ = + CefRequestContext::CreateContext(request_context_, NULL); + } else { + loader_request_context_ = request_context_; + } + + if (create_main_browser_) { + OnCreateMainBrowser(); + } else { + // Creation of the extension browser will trigger initialization of the + // RequestContext, so just load the extensions now. + OnLoadExtensions(); + } + + // Time out the test after a reasonable period of time. + SetTestTimeout(); +} + +void ExtensionTestHandler::DestroyTest() { + OnDestroyTest(); + ReleaseRequestContexts(); + RoutingTestHandler::DestroyTest(); +} + +void ExtensionTestHandler::OnAfterCreated(CefRefPtr browser) { + RoutingTestHandler::OnAfterCreated(browser); + + if (create_main_browser() && !request_context_with_handler() && + GetBrowserId() == browser->GetIdentifier()) { + // When the RequestContext doesn't have a handler we won't get a + // notification for RequestContext initialization. Instead use main browser + // creation to indicate that the RequestContext has been initialized. + OnLoadExtensions(); + } +} + +void ExtensionTestHandler::OnExtensionLoadFailed(cef_errorcode_t result) { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(false); // Not reached. +} + +// CefMessageRouterBrowserSide::Handler methods: +bool ExtensionTestHandler::OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64 query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) { + if (OnMessage(browser, request)) + return true; + + EXPECT_FALSE(true) << "Unexpected message: " << request.ToString(); + return false; +} + +// static +CefRefPtr ExtensionTestHandler::CreateDefaultManifest( + const std::vector& api_permissions) { + CefRefPtr manifest = CefDictionaryValue::Create(); + manifest->SetString("name", "An extension"); + manifest->SetString("description", "An extension description"); + manifest->SetString("version", "1.0"); + manifest->SetInt("manifest_version", 2); + + CefRefPtr permissions = CefListValue::Create(); + permissions->SetSize(api_permissions.size() + 2); + size_t idx = 0; + for (; idx < api_permissions.size(); ++idx) + permissions->SetString(idx, api_permissions[idx]); + + // Allow access to all http/https origins. + permissions->SetString(idx++, "http://*/*"); + permissions->SetString(idx++, "https://*/*"); + + manifest->SetList("permissions", permissions); + + return manifest; +} + +// static +std::string ExtensionTestHandler::GetMessageJS(const std::string& message) { + EXPECT_TRUE(!message.empty()); + return "window.testQuery({request:'" + message + "'});"; +} + +// static +void ExtensionTestHandler::VerifyExtensionInContext( + CefRefPtr extension, + CefRefPtr context, + bool has_access, + bool is_loader) { + const CefString& extension_id = extension->GetIdentifier(); + EXPECT_FALSE(extension_id.empty()); + + if (has_access) { + if (is_loader) { + EXPECT_TRUE(context->DidLoadExtension(extension_id)); + } else { + EXPECT_FALSE(context->DidLoadExtension(extension_id)); + } + EXPECT_TRUE(context->HasExtension(extension_id)); + } else { + EXPECT_FALSE(context->DidLoadExtension(extension_id)); + EXPECT_FALSE(context->HasExtension(extension_id)); + } + + CefRefPtr extension2 = context->GetExtension(extension_id); + if (has_access) { + EXPECT_TRUE(extension2); + EXPECT_TRUE(extension->IsSame(extension2)); + TestDictionaryEqual(extension->GetManifest(), extension2->GetManifest()); + } else { + EXPECT_FALSE(extension2); + } + + std::vector extension_ids; + EXPECT_TRUE(context->GetExtensions(extension_ids)); + + // Should be our test extension and possibly the builtin PDF extension if it + // has finished loading (our extension may load first if the call to + // LoadExtension initializes the request context). + bool has_extension = false; + for (size_t i = 0; i < extension_ids.size(); ++i) { + if (extension_ids[i] == extension_id) { + has_extension = true; + break; + } + } + if (has_access) { + EXPECT_TRUE(has_extension); + } else { + EXPECT_FALSE(has_extension); + } +} + +void ExtensionTestHandler::LoadExtension( + const std::string& extension_path, + CefRefPtr manifest) { + EXPECT_TRUE(!extension_path.empty()); + loader_request_context_->LoadExtension(extension_path, manifest, this); +} + +void ExtensionTestHandler::UnloadExtension(CefRefPtr extension) { + EXPECT_TRUE(extension); + extension->Unload(); + EXPECT_FALSE(extension->IsLoaded()); + EXPECT_FALSE(extension->GetLoaderContext()); +} + +void ExtensionTestHandler::ReleaseRequestContexts() { + request_context_ = NULL; + loader_request_context_ = NULL; +} diff --git a/tests/ceftests/extensions/extension_test_handler.h b/tests/ceftests/extensions/extension_test_handler.h new file mode 100644 index 000000000..cc68d659d --- /dev/null +++ b/tests/ceftests/extensions/extension_test_handler.h @@ -0,0 +1,238 @@ +// Copyright (c) 2017 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_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_ +#define CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_ +#pragma once + +#include + +#include "include/cef_extension_handler.h" +#include "include/cef_values.h" +#include "include/wrapper/cef_scoped_temp_dir.h" +#include "tests/ceftests/routing_test_handler.h" +#include "tests/gtest/include/gtest/gtest.h" + +class ExtensionTestHandler : public RoutingTestHandler, + public CefExtensionHandler { + public: + // All tests must be able to run with all RequestContext combinations. See the + // EXTENSION_TEST_GROUP_* macros below. + enum RequestContextType { + // If set create a custom context. Otherwise, use the global context. + RC_TYPE_FLAG_CUSTOM = 1 << 0, + + // If set store data on disk. Otherwise, store data in memory. + // Requires RC_TYPE_FLAG_CUSTOM. + RC_TYPE_FLAG_ON_DISK = 1 << 1, + + // If set use a handler. Otherwise, don't. + RC_TYPE_FLAG_WITH_HANDLER = 1 << 2, + + // If set load extensions with a different context that shares the same + // storage but specifies a different handler. + // Excludes RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER. + RC_TYPE_FLAG_LOAD_WITH_HANDLER = 1 << 3, + + // If set load extensions with a different context that shares the same + // storage but doesn't specify a handler. + // Requires RC_TYPE_FLAG_WITH_HANDLER. + // Excludes RC_TYPE_FLAG_LOAD_WITH_HANDLER. + RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER = 1 << 4, + }; + + explicit ExtensionTestHandler(RequestContextType request_context_type); + virtual ~ExtensionTestHandler(); + + // TestHandler methods: + void RunTest() override; + void DestroyTest() override; + void OnAfterCreated(CefRefPtr browser) override; + + // CefExtensionHandler methods: + void OnExtensionLoadFailed(cef_errorcode_t result) override; + + // CefMessageRouterBrowserSide::Handler methods: + bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64 query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) override; + + CefRefPtr request_context() const { + return request_context_; + } + CefRefPtr loader_request_context() const { + return loader_request_context_; + } + + bool request_context_is_custom() const { + return !!(request_context_type_ & RC_TYPE_FLAG_CUSTOM); + } + bool request_context_on_disk() const { + return !!(request_context_type_ & RC_TYPE_FLAG_ON_DISK); + } + bool request_context_with_handler() const { + return !!(request_context_type_ & RC_TYPE_FLAG_WITH_HANDLER); + } + bool request_context_load_with_handler() const { + return !!(request_context_type_ & RC_TYPE_FLAG_LOAD_WITH_HANDLER); + } + bool request_context_load_without_handler() const { + return !!(request_context_type_ & RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER); + } + bool request_context_same_loader() const { + return !(request_context_load_with_handler() || + request_context_load_without_handler()); + } + + protected: + // Returns the default extension manifest. + typedef std::vector ApiPermissionsList; + static CefRefPtr CreateDefaultManifest( + const ApiPermissionsList& api_permissions); + + // Returns the JS code that, when executed, will deliver |message| to the + // OnMessage callback. + static std::string GetMessageJS(const std::string& message); + + // Run checks on the state of |extension| in |context|. If |has_access| is + // true then |context| is expected to have access to |extension|. If + // |is_loader| is true then |context| is expected to have loaded |extension|. + static void VerifyExtensionInContext(CefRefPtr extension, + CefRefPtr context, + bool has_access, + bool is_loader); + + // Helper for loading/unloading an extension. + void LoadExtension(const std::string& extension_path, + CefRefPtr manifest); + void UnloadExtension(CefRefPtr extension); + + // Release request contexts. This is normally called from DestroyTest(). + void ReleaseRequestContexts(); + + void set_create_main_browser(bool val) { create_main_browser_ = val; } + bool create_main_browser() const { return create_main_browser_; } + + // Called when its time to add resources for the main browser if + // |create_main_browser_| is true. + virtual void OnAddMainBrowserResources() {} + // Called when its time to create the main browser if + // |create_main_browser_| is true. + virtual void OnCreateMainBrowser() {} + + // Called when its time to load extensions. + virtual void OnLoadExtensions() = 0; + + // Called when |browser| receives |message|. Return true if the message is + // handled. The JS code that sends messages is created by GetMessageJS(). + virtual bool OnMessage(CefRefPtr browser, + const std::string& message) = 0; + + // Called to perform verification on test destruction. + virtual void OnDestroyTest() = 0; + + private: + const RequestContextType request_context_type_; + CefScopedTempDir request_context_temp_dir_; + + // Context used when creating browsers. + CefRefPtr request_context_; + + // Context used when loading extensions. + CefRefPtr loader_request_context_; + + // If true expect creation of a main browser. Default is true. + bool create_main_browser_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionTestHandler); +}; + +// Helper for implementing an extension test. +#define EXTENSION_TEST(name, test_class, rc_type) \ + TEST(ExtensionTest, name) { \ + CefRefPtr handler = new test_class( \ + static_cast(rc_type)); \ + handler->ExecuteTest(); \ + ReleaseAndWaitForDestructor(handler); \ + } + +// Helper for implementing extension tests that include all RequestContext +// combinations. When two or more extension tests significantly overlap in +// tested functionality the first test should use the ALL macro and the others +// should use the MINIMAL macro. +#define EXTENSION_TEST_GROUP_ALL(name, test_class) \ + EXTENSION_TEST(name##RCGlobal, test_class, 0); \ + EXTENSION_TEST(name##RCGlobalLoadWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \ + EXTENSION_TEST(name##RCGlobalWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) \ + EXTENSION_TEST(name##RCGlobalWithHandlerLoadWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \ + EXTENSION_TEST(name##RCGlobalWithHandlerLoadWithoutHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER) \ + EXTENSION_TEST(name##RCCustomInMemory, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM) \ + EXTENSION_TEST(name##RCCustomInMemoryLoadWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \ + EXTENSION_TEST(name##RCCustomInMemoryWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) \ + EXTENSION_TEST(name##RCCustomInMemoryWithHandlerLoadWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \ + EXTENSION_TEST(name##RCCustomInMemoryWithHandlerLoadWithoutHandler, \ + test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER) \ + EXTENSION_TEST(name##RCCustomOnDisk, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK) \ + EXTENSION_TEST(name##RCCustomOnDiskLoadWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \ + EXTENSION_TEST(name##RCCustomOnDiskWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) \ + EXTENSION_TEST(name##RCCustomOnDiskWithHandlerLoadWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \ + EXTENSION_TEST(name##RCCustomOnDiskWithHandlerLoadWithoutHandler, \ + test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \ + ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER) + +#define EXTENSION_TEST_GROUP_MINIMAL_GLOBAL(name, test_class) \ + EXTENSION_TEST(name##RCGlobal, test_class, 0); \ + EXTENSION_TEST(name##RCGlobalWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) + +#define EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(name, test_class) \ + EXTENSION_TEST(name##RCCustomInMemory, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM) \ + EXTENSION_TEST(name##RCCustomInMemoryWithHandler, test_class, \ + ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \ + ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) + +// Helper for implementing extension tests that include a minimal set of +// RequestContext combinations. This mostly just verifies that the test runs +// and doesn't leak state information in the context. +#define EXTENSION_TEST_GROUP_MINIMAL(name, test_class) \ + EXTENSION_TEST_GROUP_MINIMAL_GLOBAL(name, test_class) \ + EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(name, test_class) + +#endif // CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_ diff --git a/tests/ceftests/extensions/view_unittest.cc b/tests/ceftests/extensions/view_unittest.cc new file mode 100644 index 000000000..94744e526 --- /dev/null +++ b/tests/ceftests/extensions/view_unittest.cc @@ -0,0 +1,241 @@ +// Copyright (c) 2017 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 "tests/ceftests/extensions/extension_test_handler.h" + +#include "tests/ceftests/test_util.h" +#include "tests/shared/browser/extension_util.h" + +namespace { + +const char kExtensionPath[] = "view-extension"; + +// Test extension load/unload. +class ViewLoadUnloadTestHandler : public ExtensionTestHandler { + public: + explicit ViewLoadUnloadTestHandler(RequestContextType request_context_type) + : ExtensionTestHandler(request_context_type) { + // Only creating the extension browser. + set_create_main_browser(false); + } + + // CefExtensionHandler methods: + void OnExtensionLoaded(CefRefPtr extension) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension->IsLoaded()); + EXPECT_TRUE(extension->GetLoaderContext()); + EXPECT_TRUE( + loader_request_context()->IsSame(extension->GetLoaderContext())); + VerifyExtension(extension); + + EXPECT_FALSE(got_loaded_); + got_loaded_.yes(); + + EXPECT_FALSE(extension_); + extension_ = extension; + + CreateBrowserForExtension(); + } + + void OnExtensionUnloaded(CefRefPtr extension) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(extension); + EXPECT_FALSE(extension->IsLoaded()); + EXPECT_FALSE(extension->GetLoaderContext()); + + EXPECT_FALSE(got_unloaded_); + got_unloaded_.yes(); + + EXPECT_TRUE(extension_); + EXPECT_TRUE(extension_->IsSame(extension)); + + // The extension should no longer be registered with the context. + if (loader_request_context()) + VerifyExtensionInContext(extension, loader_request_context(), false, + true); + if (request_context() && !request_context_same_loader()) + VerifyExtensionInContext(extension, request_context(), false, false); + + extension_ = NULL; + + // Execute asynchronously so call stacks have a chance to unwind. + // Will close the browser windows. + CefPostTask(TID_UI, + base::Bind(&ViewLoadUnloadTestHandler::DestroyTest, this)); + } + + // CefLoadHandler methods: + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) override { + EXPECT_FALSE(browser->GetHost()->IsBackgroundHost()); + + CefRefPtr extension = browser->GetHost()->GetExtension(); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension_->IsSame(extension)); + + if (isLoading) { + EXPECT_FALSE(extension_browser_); + extension_browser_ = browser; + } else { + EXPECT_TRUE(browser->IsSame(extension_browser_)); + + const std::string& url = browser->GetMainFrame()->GetURL(); + EXPECT_STREQ(extension_url_.c_str(), url.c_str()); + + EXPECT_FALSE(got_load_done_); + got_load_done_.yes(); + + TriggerDestroyTestIfDone(); + } + } + + // CefRequestHandler methods: + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + EXPECT_FALSE(browser->GetHost()->IsBackgroundHost()); + EXPECT_TRUE(browser->IsSame(extension_browser_)); + + CefRefPtr extension = browser->GetHost()->GetExtension(); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension_->IsSame(extension)); + + const std::string& url = request->GetURL(); + EXPECT_STREQ(extension_url_.c_str(), url.c_str()); + + EXPECT_FALSE(got_url_request_); + got_url_request_.yes(); + + // Handle the resource request. + return RoutingTestHandler::GetResourceHandler(browser, frame, request); + } + + protected: + void OnLoadExtensions() override { + LoadExtension(kExtensionPath, CreateManifest()); + } + + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + EXPECT_FALSE(browser->GetHost()->IsBackgroundHost()); + EXPECT_STREQ("extension_onload", message.c_str()); + + CefRefPtr extension = browser->GetHost()->GetExtension(); + EXPECT_TRUE(extension); + EXPECT_TRUE(extension_->IsSame(extension)); + + EXPECT_TRUE(browser->IsSame(extension_browser_)); + + EXPECT_FALSE(got_body_onload_); + got_body_onload_.yes(); + + TriggerDestroyTestIfDone(); + return true; + } + + void OnDestroyTest() override { + extension_browser_ = NULL; + + EXPECT_TRUE(got_loaded_); + EXPECT_TRUE(got_url_request_); + EXPECT_TRUE(got_body_onload_); + EXPECT_TRUE(got_load_done_); + EXPECT_TRUE(got_unloaded_); + } + + // Create the default manifest. + CefRefPtr CreateManifest() const { + return CreateDefaultManifest(ApiPermissionsList()); + } + + // Verify |extension| contents. + void VerifyExtension(CefRefPtr extension) const { + EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(), + client::extension_util::GetInternalExtensionResourcePath( + extension->GetPath()) + .c_str()); + + CefRefPtr expected_manifest = CreateManifest(); + TestDictionaryEqual(expected_manifest, extension->GetManifest()); + + VerifyExtensionInContext(extension, loader_request_context(), true, true); + if (!request_context_same_loader()) + VerifyExtensionInContext(extension, request_context(), true, false); + } + + void CreateBrowserForExtension() { + const std::string& identifier = extension_->GetIdentifier(); + EXPECT_FALSE(identifier.empty()); + const std::string& origin = + client::extension_util::GetExtensionOrigin(identifier); + EXPECT_FALSE(origin.empty()); + + // Add extension resources. + extension_url_ = origin + "extension.html"; + AddResource(extension_url_, + "Extension", + "text/html"); + + // Create a browser to host the extension. + CreateBrowser(extension_url_, request_context()); + } + + void TriggerDestroyTestIfDone() { + if (got_body_onload_ && got_load_done_) { + TriggerDestroyTest(); + } + } + + virtual void TriggerDestroyTest() { + // Execute asynchronously so call stacks have a chance to unwind. + CefPostTask(TID_UI, base::Bind(&ViewLoadUnloadTestHandler::UnloadExtension, + this, extension_)); + } + + CefRefPtr extension_; + std::string extension_url_; + CefRefPtr extension_browser_; + + TrackCallback got_loaded_; + TrackCallback got_url_request_; + TrackCallback got_body_onload_; + TrackCallback got_load_done_; + TrackCallback got_unloaded_; + + IMPLEMENT_REFCOUNTING(ViewLoadUnloadTestHandler); + DISALLOW_COPY_AND_ASSIGN(ViewLoadUnloadTestHandler); +}; + +} // namespace + +EXTENSION_TEST_GROUP_ALL(ViewLoadUnload, ViewLoadUnloadTestHandler); + +namespace { + +// Same as above but without the unload. Only do this with a custom context to +// avoid poluting the global context. +class ViewLoadNoUnloadTestHandler : public ViewLoadUnloadTestHandler { + public: + explicit ViewLoadNoUnloadTestHandler(RequestContextType request_context_type) + : ViewLoadUnloadTestHandler(request_context_type) {} + + protected: + void TriggerDestroyTest() override { + // Release everything that references the request context. This should + // trigger unload of the extension. + CloseBrowser(extension_browser_, false); + extension_browser_ = NULL; + ReleaseRequestContexts(); + } +}; + +} // namespace + +EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(ViewLoadNoUnload, + ViewLoadNoUnloadTestHandler); diff --git a/tests/ceftests/file_util_unittest.cc b/tests/ceftests/file_util_unittest.cc index 463d620bb..9a54ba4cf 100644 --- a/tests/ceftests/file_util_unittest.cc +++ b/tests/ceftests/file_util_unittest.cc @@ -5,31 +5,33 @@ #include #include "include/wrapper/cef_scoped_temp_dir.h" -#include "tests/ceftests/file_util.h" #include "tests/gtest/include/gtest/gtest.h" +#include "tests/shared/browser/file_util.h" TEST(FileUtil, JoinPath) { // Should return whichever path component is non-empty. - EXPECT_STREQ("", file_util::JoinPath("", "").c_str()); - EXPECT_STREQ("path1", file_util::JoinPath("path1", "").c_str()); - EXPECT_STREQ("path2", file_util::JoinPath("", "path2").c_str()); + EXPECT_STREQ("", client::file_util::JoinPath("", "").c_str()); + EXPECT_STREQ("path1", client::file_util::JoinPath("path1", "").c_str()); + EXPECT_STREQ("path2", client::file_util::JoinPath("", "path2").c_str()); const std::string& expected = - std::string("path1") + file_util::kPathSep + std::string("path2"); + std::string("path1") + client::file_util::kPathSep + std::string("path2"); // Should always be 1 kPathSep character between paths. - EXPECT_STREQ(expected.c_str(), file_util::JoinPath("path1", "path2").c_str()); - EXPECT_STREQ( - expected.c_str(), - file_util::JoinPath(std::string("path1") + file_util::kPathSep, "path2") - .c_str()); - EXPECT_STREQ( - expected.c_str(), - file_util::JoinPath("path1", file_util::kPathSep + std::string("path2")) - .c_str()); EXPECT_STREQ(expected.c_str(), - file_util::JoinPath(std::string("path1") + file_util::kPathSep, - file_util::kPathSep + std::string("path2")) + client::file_util::JoinPath("path1", "path2").c_str()); + EXPECT_STREQ(expected.c_str(), + client::file_util::JoinPath( + std::string("path1") + client::file_util::kPathSep, "path2") + .c_str()); + EXPECT_STREQ(expected.c_str(), + client::file_util::JoinPath( + "path1", client::file_util::kPathSep + std::string("path2")) + .c_str()); + EXPECT_STREQ(expected.c_str(), + client::file_util::JoinPath( + std::string("path1") + client::file_util::kPathSep, + client::file_util::kPathSep + std::string("path2")) .c_str()); } @@ -38,13 +40,21 @@ TEST(FileUtil, WriteAndReadFile) { EXPECT_TRUE(dir.CreateUniqueTempDir()); const std::string& data = "Test contents to read/write"; - const std::string& path = file_util::JoinPath(dir.GetPath(), "test.txt"); + const std::string& path = + client::file_util::JoinPath(dir.GetPath(), "test.txt"); EXPECT_EQ(static_cast(data.size()), - file_util::WriteFile(path.c_str(), data.data(), - static_cast(data.size()))); + client::file_util::WriteFile(path.c_str(), data.data(), + static_cast(data.size()))); std::string read; - EXPECT_TRUE(file_util::ReadFileToString(path.c_str(), &read)); + EXPECT_TRUE(client::file_util::ReadFileToString(path.c_str(), &read)); EXPECT_STREQ(data.c_str(), read.c_str()); } + +TEST(FileUtil, GetFileExtension) { + EXPECT_TRUE(client::file_util::GetFileExtension(std::string()).empty()); + EXPECT_TRUE(client::file_util::GetFileExtension("/path/to/foo").empty()); + EXPECT_STREQ("ext", + client::file_util::GetFileExtension("/path/to/foo.ext").c_str()); +} diff --git a/tests/ceftests/os_rendering_unittest.cc b/tests/ceftests/os_rendering_unittest.cc index 82d060715..9819ed584 100644 --- a/tests/ceftests/os_rendering_unittest.cc +++ b/tests/ceftests/os_rendering_unittest.cc @@ -969,14 +969,14 @@ class OSRTestHandler : public RoutingTestHandler, return false; } - void OnTakeFocus(CefRefPtr browser, bool next) { + void OnTakeFocus(CefRefPtr browser, bool next) override { if (test_type_ == OSR_TEST_TAKE_FOCUS) { EXPECT_TRUE(true); DestroySucceededTestSoon(); } } - void OnGotFocus(CefRefPtr browser) { + void OnGotFocus(CefRefPtr browser) override { if (test_type_ == OSR_TEST_GOT_FOCUS) { EXPECT_TRUE(true); DestroySucceededTestSoon(); diff --git a/tests/ceftests/resource_manager_unittest.cc b/tests/ceftests/resource_manager_unittest.cc index 58b36582f..d6d040969 100644 --- a/tests/ceftests/resource_manager_unittest.cc +++ b/tests/ceftests/resource_manager_unittest.cc @@ -10,9 +10,9 @@ #include "include/wrapper/cef_resource_manager.h" #include "include/wrapper/cef_scoped_temp_dir.h" #include "include/wrapper/cef_stream_resource_handler.h" -#include "tests/ceftests/file_util.h" #include "tests/ceftests/routing_test_handler.h" #include "tests/gtest/include/gtest/gtest.h" +#include "tests/shared/browser/file_util.h" namespace { @@ -32,7 +32,8 @@ std::string CreateContents(const std::string& message) { void WriteFile(const std::string& path, const std::string& contents) { int contents_size = static_cast(contents.size()); - int write_ct = file_util::WriteFile(path, contents.data(), contents_size); + int write_ct = + client::file_util::WriteFile(path, contents.data(), contents_size); EXPECT_EQ(contents_size, write_ct); } @@ -682,15 +683,15 @@ TEST(ResourceManagerTest, DirectoryProvider) { // Write the files to disk. const std::string& temp_dir = scoped_dir.GetPath(); - WriteFile(file_util::JoinPath(temp_dir, kFile1), + WriteFile(client::file_util::JoinPath(temp_dir, kFile1), CreateContents(success1_message)); - WriteFile(file_util::JoinPath(temp_dir, kFile2), + WriteFile(client::file_util::JoinPath(temp_dir, kFile2), CreateContents(success2_message)); // Also include a subdirectory. - const std::string& sub_dir = file_util::JoinPath(temp_dir, "sub"); + const std::string& sub_dir = client::file_util::JoinPath(temp_dir, "sub"); EXPECT_TRUE(CefCreateDirectory(sub_dir)); - WriteFile(file_util::JoinPath(sub_dir, kFile3), + WriteFile(client::file_util::JoinPath(sub_dir, kFile3), CreateContents(success3_message)); state.manager_->AddDirectoryProvider(kUrlBase, temp_dir, 0, std::string()); @@ -742,21 +743,21 @@ TEST(ResourceManagerTest, ArchiveProvider) { const std::string& temp_dir = scoped_dir.GetPath(); // Write the files to disk. - const std::string& file_dir = file_util::JoinPath(temp_dir, "files"); + const std::string& file_dir = client::file_util::JoinPath(temp_dir, "files"); EXPECT_TRUE(CefCreateDirectory(file_dir)); - WriteFile(file_util::JoinPath(file_dir, kFile1), + WriteFile(client::file_util::JoinPath(file_dir, kFile1), CreateContents(success1_message)); - WriteFile(file_util::JoinPath(file_dir, kFile2), + WriteFile(client::file_util::JoinPath(file_dir, kFile2), CreateContents(success2_message)); // Also include a subdirectory. - const std::string& sub_dir = file_util::JoinPath(file_dir, "sub"); + const std::string& sub_dir = client::file_util::JoinPath(file_dir, "sub"); EXPECT_TRUE(CefCreateDirectory(sub_dir)); - WriteFile(file_util::JoinPath(sub_dir, kFile3), + WriteFile(client::file_util::JoinPath(sub_dir, kFile3), CreateContents(success3_message)); const std::string& archive_path = - file_util::JoinPath(temp_dir, "archive.zip"); + client::file_util::JoinPath(temp_dir, "archive.zip"); // Create the archive file. EXPECT_TRUE(CefZipDirectory(file_dir, archive_path, false)); diff --git a/tests/ceftests/run_all_unittests.cc b/tests/ceftests/run_all_unittests.cc index 42d05fa0b..826b8a7c1 100644 --- a/tests/ceftests/run_all_unittests.cc +++ b/tests/ceftests/run_all_unittests.cc @@ -201,6 +201,8 @@ int main(int argc, char* argv[]) { // Shut down CEF. CefShutdown(); + test_suite.DeleteTempDirectories(); + // Destroy the MessageLoop. message_loop.reset(nullptr); diff --git a/tests/ceftests/scheme_handler_unittest.cc b/tests/ceftests/scheme_handler_unittest.cc index b67dca444..49841f10c 100644 --- a/tests/ceftests/scheme_handler_unittest.cc +++ b/tests/ceftests/scheme_handler_unittest.cc @@ -161,7 +161,8 @@ class TestSchemeHandler : public TestHandler { #if defined(OS_LINUX) // CustomStandardXHR* tests are flaky on Linux, sometimes returning // ERR_ABORTED. Make the tests less flaky by also accepting that value. - if (!(test_results_->expected_error_code == 0 && errorCode == ERR_ABORTED)) { + if (!(test_results_->expected_error_code == 0 && + errorCode == ERR_ABORTED)) { EXPECT_EQ(test_results_->expected_error_code, errorCode); } #else diff --git a/tests/ceftests/test_suite.cc b/tests/ceftests/test_suite.cc index 22e311102..95b6400ac 100644 --- a/tests/ceftests/test_suite.cc +++ b/tests/ceftests/test_suite.cc @@ -4,6 +4,7 @@ #include "tests/ceftests/test_suite.h" +#include "include/cef_file_util.h" #include "tests/gtest/include/gtest/gtest.h" #include "tests/shared/common/client_switches.h" @@ -162,6 +163,19 @@ bool CefTestSuite::GetCachePath(std::string& path) const { return false; } +void CefTestSuite::RegisterTempDirectory(const CefString& directory) { + base::AutoLock lock_scope(temp_directories_lock_); + temp_directories_.push_back(directory); +} + +void CefTestSuite::DeleteTempDirectories() { + base::AutoLock lock_scope(temp_directories_lock_); + for (size_t i = 0U; i < temp_directories_.size(); ++i) { + CefDeleteFile(temp_directories_[i], true); + } + temp_directories_.clear(); +} + void CefTestSuite::PreInitialize() { #if defined(OS_WIN) testing::GTEST_FLAG(catch_exceptions) = false; diff --git a/tests/ceftests/test_suite.h b/tests/ceftests/test_suite.h index 0c0794349..77ce3330a 100644 --- a/tests/ceftests/test_suite.h +++ b/tests/ceftests/test_suite.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "include/cef_command_line.h" #include "include/wrapper/cef_helpers.h" @@ -26,6 +27,12 @@ class CefTestSuite { void GetSettings(CefSettings& settings) const; bool GetCachePath(std::string& path) const; + // Register a temp directory that should be deleted on shutdown. + void RegisterTempDirectory(const CefString& directory); + + // Called after shutdown to delete any registered temp directories. + void DeleteTempDirectories(); + CefRefPtr command_line() const { return command_line_; } // The return value from Run(). @@ -40,6 +47,9 @@ class CefTestSuite { CefScopedArgArray argv_; CefRefPtr command_line_; + std::vector temp_directories_; + base::Lock temp_directories_lock_; + int retval_; }; diff --git a/tests/ceftests/tracing_unittest.cc b/tests/ceftests/tracing_unittest.cc index cbed6d09e..d060b7054 100644 --- a/tests/ceftests/tracing_unittest.cc +++ b/tests/ceftests/tracing_unittest.cc @@ -8,9 +8,9 @@ #include "include/cef_trace.h" #include "include/cef_waitable_event.h" #include "include/wrapper/cef_closure_task.h" -#include "tests/ceftests/file_util.h" #include "tests/ceftests/test_handler.h" #include "tests/gtest/include/gtest/gtest.h" +#include "tests/shared/browser/file_util.h" // Use the CEF version of the TRACE_* macros instead of the Chromium version. #undef USING_CHROMIUM_INCLUDES @@ -81,7 +81,7 @@ class TracingTestHandler : public CefEndTracingCallback, void ReadTracingFile(const std::string& file_path) { EXPECT_FILE_THREAD(); - EXPECT_TRUE(file_util::ReadFileToString(file_path, &trace_data_)); + EXPECT_TRUE(client::file_util::ReadFileToString(file_path, &trace_data_)); EXPECT_TRUE(CefDeleteFile(file_path, false)); completion_event_->Signal(); diff --git a/tests/ceftests/urlrequest_unittest.cc b/tests/ceftests/urlrequest_unittest.cc index 42885fd5c..699906576 100644 --- a/tests/ceftests/urlrequest_unittest.cc +++ b/tests/ceftests/urlrequest_unittest.cc @@ -12,11 +12,11 @@ #include "include/cef_waitable_event.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_scoped_temp_dir.h" -#include "tests/ceftests/file_util.h" #include "tests/ceftests/test_handler.h" #include "tests/ceftests/test_suite.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" +#include "tests/shared/browser/file_util.h" #include "tests/shared/renderer/client_app_renderer.h" using client::ClientAppRenderer; @@ -777,9 +777,10 @@ class RequestTestRunner : public base::RefCountedThreadSafe { EXPECT_TRUE(post_file_tmpdir_.CreateUniqueTempDir()); const std::string& path = - file_util::JoinPath(post_file_tmpdir_.GetPath(), "example.txt"); + client::file_util::JoinPath(post_file_tmpdir_.GetPath(), "example.txt"); const char content[] = "HELLO FRIEND!"; - int write_ct = file_util::WriteFile(path, content, sizeof(content) - 1); + int write_ct = + client::file_util::WriteFile(path, content, sizeof(content) - 1); EXPECT_EQ(static_cast(sizeof(content) - 1), write_ct); SetUploadFile(settings_.request, path); diff --git a/tests/ceftests/views/button_unittest.cc b/tests/ceftests/views/button_unittest.cc index 7ce4d5ca1..45ce99b41 100644 --- a/tests/ceftests/views/button_unittest.cc +++ b/tests/ceftests/views/button_unittest.cc @@ -10,6 +10,7 @@ #include "include/views/cef_menu_button_delegate.h" #include "include/wrapper/cef_closure_task.h" #include "tests/ceftests/image_util.h" +#include "tests/ceftests/test_handler.h" #include "tests/ceftests/thread_helper.h" #include "tests/ceftests/views/test_window_delegate.h" #include "tests/gtest/include/gtest/gtest.h" @@ -98,8 +99,10 @@ class EmptyMenuButtonDelegate : public CefMenuButtonDelegate { public: EmptyMenuButtonDelegate() {} - void OnMenuButtonPressed(CefRefPtr menu_button, - const CefPoint& screen_point) override { + void OnMenuButtonPressed( + CefRefPtr menu_button, + const CefPoint& screen_point, + CefRefPtr button_pressed_lock) override { EXPECT_TRUE(false); // Not reached. } @@ -377,8 +380,10 @@ class TestMenuButtonDelegate : public CefMenuButtonDelegate, public: TestMenuButtonDelegate() {} - void OnMenuButtonPressed(CefRefPtr menu_button, - const CefPoint& screen_point) override { + void OnMenuButtonPressed( + CefRefPtr menu_button, + const CefPoint& screen_point, + CefRefPtr button_pressed_lock) override { window_ = menu_button->GetWindow(); CefRefPtr model = CefMenuModel::CreateMenuModel(this); @@ -752,3 +757,123 @@ BUTTON_TEST_ASYNC( BUTTON_TEST_ASYNC(MenuButtonClickFramelessNoTextNoMarkerNoImageFramelessWindow); BUTTON_TEST_ASYNC( MenuButtonClickFramelessNoTextWithMarkerNoImageFramelessWindow); + +namespace { + +class TestMenuButtonCustomPopupDelegate : public CefMenuButtonDelegate, + public CefWindowDelegate { + public: + explicit TestMenuButtonCustomPopupDelegate(bool can_activate) + : can_activate_(can_activate) {} + + void OnMenuButtonPressed( + CefRefPtr menu_button, + const CefPoint& screen_point, + CefRefPtr button_pressed_lock) override { + parent_window_ = menu_button->GetWindow(); + button_pressed_lock_ = button_pressed_lock; + + popup_window_ = CefWindow::CreateTopLevelWindow(this); + popup_window_->SetBounds(CefRect(screen_point.x, screen_point.y, 100, 100)); + + CefRefPtr button = + CefLabelButton::CreateLabelButton(this, "Button", true); + button->SetFocusable(can_activate_); + popup_window_->AddChildView(button); + + popup_window_->Show(); + + // Wait a bit before trying to click the popup button. + CefPostDelayedTask(TID_UI, base::Bind(ClickMenuItem, menu_button), + kClickDelayMS); + } + + void OnButtonPressed(CefRefPtr button) override { + EXPECT_TRUE(button->GetWindow()->IsSame(popup_window_)); + popup_window_->Close(); + popup_window_ = nullptr; + button_pressed_lock_ = nullptr; + } + + CefRefPtr GetParentWindow(CefRefPtr window, + bool* is_menu, + bool* can_activate_menu) override { + EXPECT_TRUE(parent_window_); + *is_menu = true; + *can_activate_menu = can_activate_; + return parent_window_; + } + + bool IsFrameless(CefRefPtr window) override { + return true; + } + + void OnFocus(CefRefPtr view) override { + if (popup_window_ && view->GetWindow()->IsSame(popup_window_)) { + EXPECT_TRUE(can_activate_); + got_focus_.yes(); + } + } + + void OnWindowDestroyed(CefRefPtr window) override { + if (can_activate_) + EXPECT_TRUE(got_focus_); + else + EXPECT_FALSE(got_focus_); + + // Complete the test by closing the parent window. + parent_window_->Close(); + parent_window_ = nullptr; + } + + private: + const bool can_activate_; + + CefRefPtr parent_window_; + CefRefPtr popup_window_; + CefRefPtr button_pressed_lock_; + + TrackCallback got_focus_; + + IMPLEMENT_REFCOUNTING(TestMenuButtonCustomPopupDelegate); + DISALLOW_COPY_AND_ASSIGN(TestMenuButtonCustomPopupDelegate); +}; + +void RunMenuButtonCustomPopupClick(bool can_activate, + CefRefPtr window) { + CefRefPtr button = CefMenuButton::CreateMenuButton( + new TestMenuButtonCustomPopupDelegate(can_activate), "Custom", true, + false); + button->SetID(kButtonID); + + window->AddChildView(button); + window->Layout(); + + window->Show(); + + // Wait a bit before trying to click the button. + CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID), + kClickDelayMS); +} + +void MenuButtonCustomPopupClick(CefRefPtr event, + bool can_activate) { + TestWindowDelegate::Config config; + config.on_window_created = + base::Bind(RunMenuButtonCustomPopupClick, can_activate); + config.close_window = false; + TestWindowDelegate::RunTest(event, config); +} + +void MenuButtonCustomPopupActivateImpl(CefRefPtr event) { + MenuButtonCustomPopupClick(event, true); +} + +void MenuButtonCustomPopupNoActivateImpl(CefRefPtr event) { + MenuButtonCustomPopupClick(event, false); +} + +} // namespace + +BUTTON_TEST_ASYNC(MenuButtonCustomPopupActivate); +BUTTON_TEST_ASYNC(MenuButtonCustomPopupNoActivate); diff --git a/tests/ceftests/webui_unittest.cc b/tests/ceftests/webui_unittest.cc index 66a13f7ce..48a3c1a39 100644 --- a/tests/ceftests/webui_unittest.cc +++ b/tests/ceftests/webui_unittest.cc @@ -166,6 +166,7 @@ WEBUI_TEST(appcache_internals); WEBUI_TEST(accessibility); WEBUI_TEST(blob_internals); WEBUI_TEST(credits); +WEBUI_TEST(extensions_support); WEBUI_TEST(gpu); WEBUI_TEST(histograms); WEBUI_TEST(indexeddb_internals); diff --git a/tests/shared/browser/extension_util.cc b/tests/shared/browser/extension_util.cc new file mode 100644 index 000000000..9c5edde8d --- /dev/null +++ b/tests/shared/browser/extension_util.cc @@ -0,0 +1,239 @@ +// Copyright (c) 2017 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 "tests/shared/browser/extension_util.h" + +#include "include/base/cef_bind.h" +#include "include/cef_parser.h" +#include "include/cef_path_util.h" +#include "include/wrapper/cef_closure_task.h" +#include "tests/shared/browser/file_util.h" +#include "tests/shared/browser/resource_util.h" + +namespace client { +namespace extension_util { + +namespace { + +std::string GetResourcesPath() { + CefString resources_dir; + if (CefGetPath(PK_DIR_RESOURCES, resources_dir) && !resources_dir.empty()) { + return resources_dir.ToString() + file_util::kPathSep; + } + return std::string(); +} + +// Internal extension paths may be prefixed with PK_DIR_RESOURCES and always +// use forward slash as path separator. +std::string GetInternalPath(const std::string& extension_path) { + const std::string& resources_path = GetResourcesPath(); + std::string internal_path; + if (!resources_path.empty() && extension_path.find(resources_path) == 0U) { + internal_path = extension_path.substr(resources_path.size()); + } else { + internal_path = extension_path; + } + +#if defined(OS_WIN) + // Normalize path separators. + std::replace(internal_path.begin(), internal_path.end(), '\\', '/'); +#endif + + return internal_path; +} + +typedef base::Callback /*manifest*/)> + ManifestCallback; + +void RunManifestCallback(const ManifestCallback& callback, + CefRefPtr manifest) { + if (!CefCurrentlyOn(TID_UI)) { + // Execute on the browser UI thread. + CefPostTask(TID_UI, base::Bind(RunManifestCallback, callback, manifest)); + return; + } + callback.Run(manifest); +} + +// Asynchronously reads the manifest and executes |callback| on the UI thread. +void GetInternalManifest(const std::string& extension_path, + const ManifestCallback& callback) { + if (!CefCurrentlyOn(TID_FILE)) { + // Execute on the browser FILE thread. + CefPostTask(TID_FILE, + base::Bind(GetInternalManifest, extension_path, callback)); + return; + } + + const std::string& manifest_path = GetInternalExtensionResourcePath( + file_util::JoinPath(extension_path, "manifest.json")); + std::string manifest_contents; + if (!LoadBinaryResource(manifest_path.c_str(), manifest_contents) || + manifest_contents.empty()) { + LOG(ERROR) << "Failed to load manifest from " << manifest_path; + RunManifestCallback(callback, NULL); + return; + } + + cef_json_parser_error_t error_code; + CefString error_msg; + CefRefPtr value = CefParseJSONAndReturnError( + manifest_contents, JSON_PARSER_RFC, error_code, error_msg); + if (!value || value->GetType() != VTYPE_DICTIONARY) { + if (error_msg.empty()) + error_msg = "Incorrectly formatted dictionary contents."; + LOG(ERROR) << "Failed to parse manifest from " << manifest_path << "; " + << error_msg.ToString(); + RunManifestCallback(callback, NULL); + return; + } + + RunManifestCallback(callback, value->GetDictionary()); +} + +void LoadExtensionWithManifest(CefRefPtr request_context, + const std::string& extension_path, + CefRefPtr handler, + CefRefPtr manifest) { + CEF_REQUIRE_UI_THREAD(); + + // Load the extension internally. Resource requests will be handled via + // AddInternalExtensionToResourceManager. + request_context->LoadExtension(extension_path, manifest, handler); +} + +} // namespace + +bool IsInternalExtension(const std::string& extension_path) { + // List of internally handled extensions. + static const char* extensions[] = {"set_page_color"}; + + const std::string& internal_path = GetInternalPath(extension_path); + for (size_t i = 0; i < arraysize(extensions); ++i) { + // Exact match or first directory component. + const std::string& extension = extensions[i]; + if (internal_path == extension || + internal_path.find(extension + '/') == 0) { + return true; + } + } + + return false; +} + +std::string GetInternalExtensionResourcePath( + const std::string& extension_path) { + return "extensions/" + GetInternalPath(extension_path); +} + +std::string GetExtensionResourcePath(const std::string& extension_path, + bool* internal) { + const bool is_internal = IsInternalExtension(extension_path); + if (internal) + *internal = is_internal; + if (is_internal) + return GetInternalExtensionResourcePath(extension_path); + return extension_path; +} + +bool GetExtensionResourceContents(const std::string& extension_path, + std::string& contents) { + CEF_REQUIRE_FILE_THREAD(); + + if (IsInternalExtension(extension_path)) { + const std::string& contents_path = + GetInternalExtensionResourcePath(extension_path); + return LoadBinaryResource(contents_path.c_str(), contents); + } + + return file_util::ReadFileToString(extension_path, &contents); +} + +void LoadExtension(CefRefPtr request_context, + const std::string& extension_path, + CefRefPtr handler) { + if (!CefCurrentlyOn(TID_UI)) { + // Execute on the browser UI thread. + CefPostTask(TID_UI, base::Bind(LoadExtension, request_context, + extension_path, handler)); + return; + } + + if (IsInternalExtension(extension_path)) { + // Read the extension manifest and load asynchronously. + GetInternalManifest(extension_path, + base::Bind(LoadExtensionWithManifest, request_context, + extension_path, handler)); + } else { + // Load the extension from disk. + request_context->LoadExtension(extension_path, NULL, handler); + } +} + +void AddInternalExtensionToResourceManager( + CefRefPtr extension, + CefRefPtr resource_manager) { + DCHECK(IsInternalExtension(extension->GetPath())); + + if (!CefCurrentlyOn(TID_IO)) { + // Execute on the browser IO thread. + CefPostTask(TID_IO, base::Bind(AddInternalExtensionToResourceManager, + extension, resource_manager)); + return; + } + + const std::string& origin = GetExtensionOrigin(extension->GetIdentifier()); + const std::string& resource_path = + GetInternalExtensionResourcePath(extension->GetPath()); + +// Add provider for bundled resource files. +#if defined(OS_WIN) + // Read resources from the binary. + resource_manager->AddProvider( + CreateBinaryResourceProvider(origin, resource_path), 50, std::string()); +#elif defined(OS_POSIX) + // Read resources from a directory on disk. + std::string resource_dir; + if (GetResourceDir(resource_dir)) { + resource_dir += "/" + resource_path; + resource_manager->AddDirectoryProvider(origin, resource_dir, 50, + std::string()); + } +#endif +} + +std::string GetExtensionOrigin(const std::string& extension_id) { + return "chrome-extension://" + extension_id + "/"; +} + +std::string GetExtensionURL(CefRefPtr extension) { + CefRefPtr browser_action = + extension->GetManifest()->GetDictionary("browser_action"); + if (browser_action) { + const std::string& default_popup = + browser_action->GetString("default_popup"); + if (!default_popup.empty()) + return GetExtensionOrigin(extension->GetIdentifier()) + default_popup; + } + + return std::string(); +} + +std::string GetExtensionIconPath(CefRefPtr extension, + bool* internal) { + CefRefPtr browser_action = + extension->GetManifest()->GetDictionary("browser_action"); + if (browser_action) { + const std::string& default_icon = browser_action->GetString("default_icon"); + if (!default_icon.empty()) { + return GetExtensionResourcePath( + file_util::JoinPath(extension->GetPath(), default_icon), internal); + } + } + + return std::string(); +} + +} // namespace extension_util +} // namespace client diff --git a/tests/shared/browser/extension_util.h b/tests/shared/browser/extension_util.h new file mode 100644 index 000000000..f3e643acb --- /dev/null +++ b/tests/shared/browser/extension_util.h @@ -0,0 +1,80 @@ +// Copyright (c) 2017 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_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_ +#define CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_ +#pragma once + +#include + +#include "include/cef_extension.h" +#include "include/cef_extension_handler.h" +#include "include/wrapper/cef_resource_manager.h" + +namespace client { +namespace extension_util { + +// Returns true if |extension_path| can be handled internally via +// LoadBinaryResource. This checks a hard-coded list of allowed extension path +// components. +bool IsInternalExtension(const std::string& extension_path); + +// Returns the path relative to the resource directory after removing the +// PK_DIR_RESOURCES prefix. This will be the relative path expected by +// LoadBinaryResource (uses '/' as path separator on all platforms). Only call +// this method for internal extensions, either when IsInternalExtension returns +// true or when the extension is handled internally through some means other +// than LoadBinaryResource. Use GetExtensionResourcePath instead if you are +// unsure whether the extension is internal or external. +std::string GetInternalExtensionResourcePath(const std::string& extension_path); + +// Returns the resource path for |extension_path|. For external extensions this +// will be the full file path on disk. For internal extensions this will be the +// relative path expected by LoadBinaryResource (uses '/' as path separator on +// all platforms). Internal extensions must be on the hard-coded list enforced +// by IsInternalExtension. If |internal| is non-NULL it will be set to true if +// the extension is handled internally. +std::string GetExtensionResourcePath(const std::string& extension_path, + bool* internal); + +// Read the contents of |extension_path| into |contents|. For external +// extensions this will read the file from disk. For internal extensions this +// will call LoadBinaryResource. Internal extensions must be on the hard-coded +// list enforced by IsInternalExtension. Returns true on success. Must be +// called on the FILE thread. +bool GetExtensionResourceContents(const std::string& extension_path, + std::string& contents); + +// Load |extension_path| in |request_context|. May be an internal or external +// extension. Internal extensions must be on the hard-coded list enforced by +// IsInternalExtension. +void LoadExtension(CefRefPtr request_context, + const std::string& extension_path, + CefRefPtr handler); + +// Register an internal handler for extension resources. Internal extensions +// must be on the hard-coded list enforced by IsInternalExtension. +void AddInternalExtensionToResourceManager( + CefRefPtr extension, + CefRefPtr resource_manager); + +// Returns the URL origin for |extension_id|. +std::string GetExtensionOrigin(const std::string& extension_id); + +// Parse browser_action manifest values as defined at +// https://developer.chrome.com/extensions/browserAction + +// Look for a browser_action.default_popup manifest value. +std::string GetExtensionURL(CefRefPtr extension); + +// Look for a browser_action.default_icon manifest value and return the resource +// path. If |internal| is non-NULL it will be set to true if the extension is +// handled internally. +std::string GetExtensionIconPath(CefRefPtr extension, + bool* internal); + +} // namespace extension_util +} // namespace client + +#endif // CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_ diff --git a/tests/ceftests/file_util.cc b/tests/shared/browser/file_util.cc similarity index 90% rename from tests/ceftests/file_util.cc rename to tests/shared/browser/file_util.cc index bb2e98af7..73c415929 100644 --- a/tests/ceftests/file_util.cc +++ b/tests/shared/browser/file_util.cc @@ -2,7 +2,7 @@ // 2012 The Chromium Authors. All rights reserved. Use of this source code is // governed by a BSD-style license that can be found in the LICENSE file. -#include "tests/ceftests/file_util.h" +#include "tests/shared/browser/file_util.h" #include "include/base/cef_build.h" #include "include/base/cef_scoped_ptr.h" @@ -12,6 +12,7 @@ #include #include +namespace client { namespace file_util { namespace { @@ -109,4 +110,12 @@ std::string JoinPath(const std::string& path1, const std::string& path2) { return result; } +std::string GetFileExtension(const std::string& path) { + size_t sep = path.find_last_of("."); + if (sep != std::string::npos) + return path.substr(sep + 1); + return std::string(); +} + } // namespace file_util +} // namespace client diff --git a/tests/ceftests/file_util.h b/tests/shared/browser/file_util.h similarity index 84% rename from tests/ceftests/file_util.h rename to tests/shared/browser/file_util.h index 89d656b67..cc484b7b3 100644 --- a/tests/ceftests/file_util.h +++ b/tests/shared/browser/file_util.h @@ -2,13 +2,14 @@ // 2012 The Chromium Authors. All rights reserved. Use of this source code is // governed by a BSD-style license that can be found in the LICENSE file. -#ifndef CEF_TESTS_UNITTESTS_FILE_UTIL_H_ -#define CEF_TESTS_UNITTESTS_FILE_UTIL_H_ +#ifndef CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_ +#define CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_ #pragma once #include #include +namespace client { namespace file_util { // Platform-specific path separator. @@ -35,6 +36,10 @@ int WriteFile(const std::string& path, const char* data, int size); // separator. std::string JoinPath(const std::string& path1, const std::string& path2); -} // namespace file_util +// Extracts the file extension from |path|. +std::string GetFileExtension(const std::string& path); -#endif // CEF_TESTS_UNITTESTS_FILE_UTIL_H_ +} // namespace file_util +} // namespace client + +#endif // CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_ diff --git a/tests/shared/browser/resource_util.cc b/tests/shared/browser/resource_util.cc deleted file mode 100644 index 094429f83..000000000 --- a/tests/shared/browser/resource_util.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2013 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 "tests/shared/browser/resource_util.h" - -#include -#include - -#include "include/base/cef_logging.h" - -namespace client { - -CefRefPtr LoadImageIcon(const char* icon_name) { - CefRefPtr icon = CefImage::CreateImage(); - std::string image, resource; - - resource = std::string(icon_name) + ".1x.png"; - if (LoadBinaryResource(resource.c_str(), image)) - icon->AddPNG(1.0f, image.c_str(), image.size()); - - resource = std::string(icon_name) + ".2x.png"; - if (LoadBinaryResource(resource.c_str(), image)) - icon->AddPNG(2.0f, image.c_str(), image.size()); - - // Icons must be 16 in DIP. - DCHECK_EQ(16U, std::max(icon->GetWidth(), icon->GetHeight())); - - return icon; -} - -} // namespace client diff --git a/tests/shared/browser/resource_util.h b/tests/shared/browser/resource_util.h index 587177038..cf4e5af0a 100644 --- a/tests/shared/browser/resource_util.h +++ b/tests/shared/browser/resource_util.h @@ -30,15 +30,10 @@ CefRefPtr GetBinaryResourceReader(const char* resource_name); #if defined(OS_WIN) // Create a new provider for loading binary resources. CefResourceManager::Provider* CreateBinaryResourceProvider( - const std::string& url_path); + const std::string& url_path, + const std::string& resource_path_prefix); #endif -// Load an image icon at different scale factors. The image representations are -// expected to be 16 DIP in size. For example, if |icon_name| is "image" then -// the expected file names are "image.1x.png" for the 1x scale image (16 pixels) -// and "image.2x.png" for the 2x scale image (32 pixels). -CefRefPtr LoadImageIcon(const char* icon_name); - } // namespace client #endif // CEF_TESTS_SHARED_BROWSER_RESOURCE_UTIL_H_ diff --git a/tests/shared/browser/resource_util_win.cc b/tests/shared/browser/resource_util_win.cc index 284526d2c..d5b30e555 100644 --- a/tests/shared/browser/resource_util_win.cc +++ b/tests/shared/browser/resource_util_win.cc @@ -33,9 +33,14 @@ bool LoadBinaryResource(int binaryId, DWORD& dwSize, LPBYTE& pBytes) { // Provider of binary resources. class BinaryResourceProvider : public CefResourceManager::Provider { public: - explicit BinaryResourceProvider(const std::string& url_path) - : url_path_(url_path) { + BinaryResourceProvider(const std::string& url_path, + const std::string& resource_path_prefix) + : url_path_(url_path), resource_path_prefix_(resource_path_prefix) { DCHECK(!url_path.empty()); + if (!resource_path_prefix_.empty() && + resource_path_prefix_[resource_path_prefix_.length() - 1] != '/') { + resource_path_prefix_ += "/"; + } } bool OnRequest(scoped_refptr request) OVERRIDE { @@ -49,8 +54,11 @@ class BinaryResourceProvider : public CefResourceManager::Provider { CefRefPtr handler; - const std::string& relative_path = url.substr(url_path_.length()); + std::string relative_path = url.substr(url_path_.length()); if (!relative_path.empty()) { + if (!resource_path_prefix_.empty()) + relative_path = resource_path_prefix_ + relative_path; + CefRefPtr stream = GetBinaryResourceReader(relative_path.data()); if (stream.get()) { @@ -65,6 +73,7 @@ class BinaryResourceProvider : public CefResourceManager::Provider { private: std::string url_path_; + std::string resource_path_prefix_; DISALLOW_COPY_AND_ASSIGN(BinaryResourceProvider); }; @@ -109,8 +118,9 @@ CefRefPtr GetBinaryResourceReader(const char* resource_name) { } CefResourceManager::Provider* CreateBinaryResourceProvider( - const std::string& url_path) { - return new BinaryResourceProvider(url_path); + const std::string& url_path, + const std::string& resource_path_prefix) { + return new BinaryResourceProvider(url_path, resource_path_prefix); } } // namespace client diff --git a/tests/shared/common/client_switches.cc b/tests/shared/common/client_switches.cc index 101a84574..b2ef3e7fc 100644 --- a/tests/shared/common/client_switches.cc +++ b/tests/shared/common/client_switches.cc @@ -38,6 +38,7 @@ const char kHideTopMenu[] = "hide-top-menu"; const char kWidevineCdmPath[] = "widevine-cdm-path"; const char kSslClientCertificate[] = "ssl-client-certificate"; const char kCRLSetsPath[] = "crl-sets-path"; +const char kLoadExtension[] = "load-extension"; } // namespace switches } // namespace client diff --git a/tests/shared/common/client_switches.h b/tests/shared/common/client_switches.h index 65e02cfd8..6499ae6cb 100644 --- a/tests/shared/common/client_switches.h +++ b/tests/shared/common/client_switches.h @@ -32,6 +32,7 @@ extern const char kHideTopMenu[]; extern const char kWidevineCdmPath[]; extern const char kSslClientCertificate[]; extern const char kCRLSetsPath[]; +extern const char kLoadExtension[]; } // namespace switches } // namespace client diff --git a/tools/cef_parser.py b/tools/cef_parser.py index afa42de80..843d893a3 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -99,7 +99,8 @@ def get_comment(body, name): if len(line) == 0: # check if the next previous line is a comment prevdata = get_prev_line(body, pos) - if string.strip(prevdata['line'])[0:2] == '//': + prevline = string.strip(prevdata['line']) + if prevline[0:2] == '//' and prevline[0:3] != '///': result.append(None) else: break @@ -538,26 +539,6 @@ class obj_header: p = re.compile('\nclass' + _cre_space + _cre_cfname + ';') forward_declares = p.findall(data) - # extract classes - p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname + - _cre_space + ':' + _cre_space + 'public' + _cre_virtual + - _cre_space + _cre_cfname + _cre_space + '{(.*?)\n};', - re.MULTILINE | re.DOTALL) - list = p.findall(data) - if len(list) > 0: - added = True - - # build the class objects - for attrib, name, parent_name, body in list: - # Style may place the ':' on the next line. - comment = get_comment(data, name + ' :') - if len(comment) == 0: - comment = get_comment(data, name + "\n") - validate_comment(filename, name, comment) - self.classes.append( - obj_class(self, filename, attrib, name, parent_name, body, comment, - includes, forward_declares)) - # extract empty classes p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname + _cre_space + ':' + _cre_space + 'public' + _cre_virtual + @@ -578,6 +559,30 @@ class obj_header: obj_class(self, filename, attrib, name, parent_name, "", comment, includes, forward_declares)) + # Remove empty classes from |data| so we don't mess up the non-empty + # class search that follows. + data = p.sub('', data) + + # extract classes + p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname + + _cre_space + ':' + _cre_space + 'public' + _cre_virtual + + _cre_space + _cre_cfname + _cre_space + '{(.*?)\n};', + re.MULTILINE | re.DOTALL) + list = p.findall(data) + if len(list) > 0: + added = True + + # build the class objects + for attrib, name, parent_name, body in list: + # Style may place the ':' on the next line. + comment = get_comment(data, name + ' :') + if len(comment) == 0: + comment = get_comment(data, name + "\n") + validate_comment(filename, name, comment) + self.classes.append( + obj_class(self, filename, attrib, name, parent_name, body, comment, + includes, forward_declares)) + if added: # a global function or class was read from the header file self.filenames.append(filename) diff --git a/tools/make_cpptoc_impl.py b/tools/make_cpptoc_impl.py index 539cefa38..f0eaf00ef 100644 --- a/tools/make_cpptoc_impl.py +++ b/tools/make_cpptoc_impl.py @@ -613,9 +613,15 @@ def make_cpptoc_class_impl(header, clsname, impl): # any derived classes can be unwrapped unwrapderived = make_cpptoc_unwrap_derived(header, cls, base_scoped) + 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' + # determine what includes are required by identifying what translation # classes are being used - includes = format_translation_includes(header, resultingimpl + + includes = format_translation_includes(header, const + resultingimpl + (unwrapderived[0] if base_scoped else unwrapderived)) @@ -626,12 +632,6 @@ def make_cpptoc_class_impl(header, clsname, impl): parent_sig = template_class + '<' + clsname + 'CppToC, ' + clsname + ', ' + capiname + '>' - 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' - if base_scoped: const += 'template<> CefOwnPtr<'+clsname+'> '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, '+capiname+'* s) {\n' + \ unwrapderived[0] + \ diff --git a/tools/make_ctocpp_impl.py b/tools/make_ctocpp_impl.py index 5610727eb..bcb0b9e3c 100644 --- a/tools/make_ctocpp_impl.py +++ b/tools/make_ctocpp_impl.py @@ -610,9 +610,13 @@ def make_ctocpp_class_impl(header, clsname, impl): # any derived classes can be unwrapped unwrapderived = make_ctocpp_unwrap_derived(header, cls, base_scoped) + const = '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \ + clsname+'CToCpp::'+clsname+'CToCpp() {\n'+ \ + '}\n\n' + # determine what includes are required by identifying what translation # classes are being used - includes = format_translation_includes(header, resultingimpl + + includes = format_translation_includes(header, const + resultingimpl + (unwrapderived[0] if base_scoped else unwrapderived)) @@ -623,10 +627,6 @@ def make_ctocpp_class_impl(header, clsname, impl): parent_sig = template_class + '<' + clsname + 'CToCpp, ' + clsname + ', ' + capiname + '>' - const = '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \ - clsname+'CToCpp::'+clsname+'CToCpp() {\n'+ \ - '}\n\n' - if base_scoped: const += 'template<> '+capiname+'* '+parent_sig+'::UnwrapDerivedOwn(CefWrapperType type, CefOwnPtr<'+clsname+'> c) {\n'+ \ unwrapderived[0] + \ diff --git a/tools/make_distrib.py b/tools/make_distrib.py index 13ad0d9c9..6859b10b3 100644 --- a/tools/make_distrib.py +++ b/tools/make_distrib.py @@ -618,6 +618,8 @@ if mode == 'standard': 'tests/cefclient/', cefclient_dir, options.quiet) transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_resources'], \ 'tests/cefclient/', cefclient_dir, options.quiet) + transfer_gypi_files(cef_dir, cef_paths2['cefclient_sources_resources_extensions_set_page_color'], \ + 'tests/cefclient/', cefclient_dir, options.quiet) # transfer common cefsimple files transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_common'], \