Add support for direct DevTools protocol messaging (fixes issue #2961).

This change allows the client to directly send and receive DevTools
protocol messages (send method calls, and receive method results and
events) without requiring a DevTools front-end or remote-debugging
session.

This change includes additional supporting changes:
- Add a new CefRequestHandler::OnDocumentAvailableInMainFrame
  callback (see issue #1454).
- Add a CefParseJSON variant that accepts a UTF8-encoded buffer.
- Add a `--devtools-protocol-log-file=<path>` command-line flag for
  logging protocol messages sent to/from the DevTools front-end
  while it is displayed. This is useful for understanding existing
  DevTools protocol usage.
- Add a new "libcef_static_unittests" executable target to support
  light-weight unit tests of libcef_static internals (e.g. without
  requiring exposure via the CEF API). Files to be unittested are
  placed in the new "libcef_static_unittested" source_set which is
  then included by both the existing libcef_static library and the
  new unittests executable target.
- Linux: Remove use_bundled_fontconfig=false, which is no longer
  required and causes unittest build errors (see issue #2424).

This change also adds a cefclient demo for configuring offline mode
using the DevTools protocol (fixes issue #245). This is controlled
by the "Offline mode" context menu option and the `--offline`
command-line switch which will launch cefclient in offline mode. When
cefclient is offline all network requests will fail with
ERR_INTERNET_DISCONNECTED and navigator.onLine will return false when
called from JavaScript in any frame. This mode is per-browser so
newly created browser windows will have the default mode. Note that
configuring offline mode in this way will not update the Network tab
UI ("Throtting" option) in a displayed DevTools front-end instance.
This commit is contained in:
Marshall Greenblatt
2020-06-12 20:54:08 -04:00
parent a9aef28966
commit 39aed35644
46 changed files with 2824 additions and 146 deletions

View File

@ -106,6 +106,7 @@ import("//media/media_options.gni")
import("//mojo/public/tools/bindings/mojom.gni") import("//mojo/public/tools/bindings/mojom.gni")
import("//ppapi/buildflags/buildflags.gni") import("//ppapi/buildflags/buildflags.gni")
import("//printing/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni")
import("//testing/test.gni")
import("//third_party/icu/config.gni") import("//third_party/icu/config.gni")
import("//third_party/widevine/cdm/widevine.gni") import("//third_party/widevine/cdm/widevine.gni")
import("//tools/grit/repack.gni") import("//tools/grit/repack.gni")
@ -262,12 +263,6 @@ if (is_clang) {
assert(!clang_use_chrome_plugins) assert(!clang_use_chrome_plugins)
} }
if (is_linux) {
# Use system fontconfig. This avoids a startup hang on Ubuntu 16.04.4 (see
# issue #2424).
assert(!use_bundled_fontconfig)
}
if (is_mac) { if (is_mac) {
# Always generate dSYM files. The make_distrib script will fail if # Always generate dSYM files. The make_distrib script will fail if
# enable_dsyms=true is not explicitly set when is_official_build=false. # enable_dsyms=true is not explicitly set when is_official_build=false.
@ -316,6 +311,7 @@ group("cef") {
deps = [ deps = [
":cefsimple", ":cefsimple",
":ceftests", ":ceftests",
":libcef_static_unittests",
] ]
if (!is_linux || use_x11) { if (!is_linux || use_x11) {
@ -373,6 +369,41 @@ if (is_win) {
} }
} }
# libcef_static source files that have unit tests.
source_set("libcef_static_unittested") {
sources = [
"libcef/browser/devtools/devtools_util.cc",
"libcef/browser/devtools/devtools_util.h",
]
deps = [
"//base",
]
configs += [
"libcef/features:config",
"//build/config:precompiled_headers",
]
}
# Executable target for libcef_static unit tests.
test("libcef_static_unittests") {
sources = [
"libcef/browser/devtools/devtools_util_unittest.cc",
]
deps = [
":libcef_static_unittested",
"//base/test:run_all_unittests",
"//testing/gtest",
]
configs += [
"libcef/features:config",
"//build/config:precompiled_headers",
]
}
static_library("libcef_static") { static_library("libcef_static") {
sources = includes_common + sources = includes_common +
gypi_paths.autogen_cpp_includes + [ gypi_paths.autogen_cpp_includes + [
@ -412,10 +443,14 @@ static_library("libcef_static") {
"libcef/browser/context.h", "libcef/browser/context.h",
"libcef/browser/context_menu_params_impl.cc", "libcef/browser/context_menu_params_impl.cc",
"libcef/browser/context_menu_params_impl.h", "libcef/browser/context_menu_params_impl.h",
"libcef/browser/devtools/devtools_controller.cc",
"libcef/browser/devtools/devtools_controller.h",
"libcef/browser/devtools/devtools_file_manager.cc", "libcef/browser/devtools/devtools_file_manager.cc",
"libcef/browser/devtools/devtools_file_manager.h", "libcef/browser/devtools/devtools_file_manager.h",
"libcef/browser/devtools/devtools_frontend.cc", "libcef/browser/devtools/devtools_frontend.cc",
"libcef/browser/devtools/devtools_frontend.h", "libcef/browser/devtools/devtools_frontend.h",
"libcef/browser/devtools/devtools_manager.cc",
"libcef/browser/devtools/devtools_manager.h",
"libcef/browser/devtools/devtools_manager_delegate.cc", "libcef/browser/devtools/devtools_manager_delegate.cc",
"libcef/browser/devtools/devtools_manager_delegate.h", "libcef/browser/devtools/devtools_manager_delegate.h",
"libcef/browser/download_item_impl.cc", "libcef/browser/download_item_impl.cc",
@ -739,6 +774,8 @@ static_library("libcef_static") {
":cef_make_headers", ":cef_make_headers",
":cef_service_manifests", ":cef_service_manifests",
":libcef_static_unittested",
# Generate API bindings for extensions. # Generate API bindings for extensions.
# TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See # TODO(cef): Enable if/when CEF exposes its own Mojo APIs. See
# libcef/common/extensions/api/README.txt for details. # libcef/common/extensions/api/README.txt for details.

View File

@ -8,7 +8,7 @@
# by hand. See the translator.README.txt file in the tools directory for # by hand. See the translator.README.txt file in the tools directory for
# more information. # more information.
# #
# $hash=21f0ab1e9902e4a47bf2893a4a383d33bd8161e2$ # $hash=30eed8c81da55c640eb6a491283d1c00fb59d635$
# #
{ {
@ -26,6 +26,7 @@
'include/cef_context_menu_handler.h', 'include/cef_context_menu_handler.h',
'include/cef_cookie.h', 'include/cef_cookie.h',
'include/cef_crash_util.h', 'include/cef_crash_util.h',
'include/cef_devtools_message_observer.h',
'include/cef_dialog_handler.h', 'include/cef_dialog_handler.h',
'include/cef_display_handler.h', 'include/cef_display_handler.h',
'include/cef_dom.h', 'include/cef_dom.h',
@ -122,6 +123,7 @@
'include/capi/cef_context_menu_handler_capi.h', 'include/capi/cef_context_menu_handler_capi.h',
'include/capi/cef_cookie_capi.h', 'include/capi/cef_cookie_capi.h',
'include/capi/cef_crash_util_capi.h', 'include/capi/cef_crash_util_capi.h',
'include/capi/cef_devtools_message_observer_capi.h',
'include/capi/cef_dialog_handler_capi.h', 'include/capi/cef_dialog_handler_capi.h',
'include/capi/cef_display_handler_capi.h', 'include/capi/cef_display_handler_capi.h',
'include/capi/cef_dom_capi.h', 'include/capi/cef_dom_capi.h',
@ -260,6 +262,8 @@
'libcef_dll/ctocpp/domvisitor_ctocpp.h', 'libcef_dll/ctocpp/domvisitor_ctocpp.h',
'libcef_dll/ctocpp/delete_cookies_callback_ctocpp.cc', 'libcef_dll/ctocpp/delete_cookies_callback_ctocpp.cc',
'libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h', 'libcef_dll/ctocpp/delete_cookies_callback_ctocpp.h',
'libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.cc',
'libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h',
'libcef_dll/ctocpp/dialog_handler_ctocpp.cc', 'libcef_dll/ctocpp/dialog_handler_ctocpp.cc',
'libcef_dll/ctocpp/dialog_handler_ctocpp.h', 'libcef_dll/ctocpp/dialog_handler_ctocpp.h',
'libcef_dll/cpptoc/dictionary_value_cpptoc.cc', 'libcef_dll/cpptoc/dictionary_value_cpptoc.cc',
@ -566,6 +570,8 @@
'libcef_dll/cpptoc/domvisitor_cpptoc.h', 'libcef_dll/cpptoc/domvisitor_cpptoc.h',
'libcef_dll/cpptoc/delete_cookies_callback_cpptoc.cc', 'libcef_dll/cpptoc/delete_cookies_callback_cpptoc.cc',
'libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h', 'libcef_dll/cpptoc/delete_cookies_callback_cpptoc.h',
'libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.cc',
'libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h',
'libcef_dll/cpptoc/dialog_handler_cpptoc.cc', 'libcef_dll/cpptoc/dialog_handler_cpptoc.cc',
'libcef_dll/cpptoc/dialog_handler_cpptoc.h', 'libcef_dll/cpptoc/dialog_handler_cpptoc.h',
'libcef_dll/ctocpp/dictionary_value_ctocpp.cc', 'libcef_dll/ctocpp/dictionary_value_ctocpp.cc',

View File

@ -470,6 +470,7 @@
'tests/ceftests/browser_info_map_unittest.cc', 'tests/ceftests/browser_info_map_unittest.cc',
'tests/ceftests/command_line_unittest.cc', 'tests/ceftests/command_line_unittest.cc',
'tests/ceftests/cookie_unittest.cc', 'tests/ceftests/cookie_unittest.cc',
'tests/ceftests/devtools_message_unittest.cc',
'tests/ceftests/dialog_unittest.cc', 'tests/ceftests/dialog_unittest.cc',
'tests/ceftests/display_unittest.cc', 'tests/ceftests/display_unittest.cc',
'tests/ceftests/dom_unittest.cc', 'tests/ceftests/dom_unittest.cc',

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=ba4033eaf40a8ee24408b89b78496bf1381e7e6b$ // $hash=6cb00a0fa3631a46903abb3a783f315895511db2$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
@ -41,10 +41,12 @@
#pragma once #pragma once
#include "include/capi/cef_base_capi.h" #include "include/capi/cef_base_capi.h"
#include "include/capi/cef_devtools_message_observer_capi.h"
#include "include/capi/cef_drag_data_capi.h" #include "include/capi/cef_drag_data_capi.h"
#include "include/capi/cef_frame_capi.h" #include "include/capi/cef_frame_capi.h"
#include "include/capi/cef_image_capi.h" #include "include/capi/cef_image_capi.h"
#include "include/capi/cef_navigation_entry_capi.h" #include "include/capi/cef_navigation_entry_capi.h"
#include "include/capi/cef_registration_capi.h"
#include "include/capi/cef_request_context_capi.h" #include "include/capi/cef_request_context_capi.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -484,6 +486,71 @@ typedef struct _cef_browser_host_t {
/// ///
int(CEF_CALLBACK* has_dev_tools)(struct _cef_browser_host_t* self); int(CEF_CALLBACK* has_dev_tools)(struct _cef_browser_host_t* self);
///
// Send a function call message over the DevTools protocol. |message| must be
// a UTF8-encoded JSON dictionary that contains "id" (int), "function"
// (string) and "params" (dictionary, optional) values. See the DevTools
// protocol documentation at https://chromedevtools.github.io/devtools-
// protocol/ for details of supported functions and the expected "params"
// dictionary contents. |message| will be copied if necessary. This function
// will return true (1) if called on the UI thread and the message was
// successfully submitted for validation, otherwise false (0). Validation will
// be applied asynchronously and any messages that fail due to formatting
// errors or missing parameters may be discarded without notification. Prefer
// ExecuteDevToolsMethod if a more structured approach to message formatting
// is desired.
//
// Every valid function call will result in an asynchronous function result or
// error message that references the sent message "id". Event messages are
// received while notifications are enabled (for example, between function
// calls for "Page.enable" and "Page.disable"). All received messages will be
// delivered to the observer(s) registered with AddDevToolsMessageObserver.
// See cef_dev_tools_message_observer_t::OnDevToolsMessage documentation for
// details of received message contents.
//
// Usage of the SendDevToolsMessage, ExecuteDevToolsMethod and
// AddDevToolsMessageObserver functions does not require an active DevTools
// front-end or remote-debugging session. Other active DevTools sessions will
// continue to function independently. However, any modification of global
// browser state by one session may not be reflected in the UI of other
// sessions.
//
// Communication with the DevTools front-end (when displayed) can be logged
// for development purposes by passing the `--devtools-protocol-log-
// file=<path>` command-line flag.
///
int(CEF_CALLBACK* send_dev_tools_message)(struct _cef_browser_host_t* self,
const void* message,
size_t message_size);
///
// Execute a function call over the DevTools protocol. This is a more
// structured version of SendDevToolsMessage. |message_id| is an incremental
// number that uniquely identifies the message (pass 0 to have the next number
// assigned automatically based on previous values). |function| is the
// function name. |params| are the function parameters, which may be NULL. See
// the DevTools protocol documentation (linked above) for details of supported
// functions and the expected |params| dictionary contents. This function will
// return the assigned message ID if called on the UI thread and the message
// was successfully submitted for validation, otherwise 0. See the
// SendDevToolsMessage documentation for additional usage information.
///
int(CEF_CALLBACK* execute_dev_tools_method)(
struct _cef_browser_host_t* self,
int message_id,
const cef_string_t* method,
struct _cef_dictionary_value_t* params);
///
// Add an observer for DevTools protocol messages (function results and
// events). The observer will remain registered until the returned
// Registration object is destroyed. See the SendDevToolsMessage documentation
// for additional usage information.
///
struct _cef_registration_t*(CEF_CALLBACK* add_dev_tools_message_observer)(
struct _cef_browser_host_t* self,
struct _cef_dev_tools_message_observer_t* observer);
/// ///
// Retrieve a snapshot of current navigation entries as values sent to the // Retrieve a snapshot of current navigation entries as values sent to the
// specified visitor. If |current_only| is true (1) only the current // specified visitor. If |current_only| is true (1) only the current

View File

@ -0,0 +1,147 @@
// Copyright (c) 2020 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=86906c2e971fea7e479738f59bbf85d71ce31953$
//
#ifndef CEF_INCLUDE_CAPI_CEF_DEVTOOLS_MESSAGE_OBSERVER_CAPI_H_
#define CEF_INCLUDE_CAPI_CEF_DEVTOOLS_MESSAGE_OBSERVER_CAPI_H_
#pragma once
#include "include/capi/cef_base_capi.h"
#ifdef __cplusplus
extern "C" {
#endif
struct _cef_browser_t;
///
// Callback structure for cef_browser_host_t::AddDevToolsMessageObserver. The
// functions of this structure will be called on the browser process UI thread.
///
typedef struct _cef_dev_tools_message_observer_t {
///
// Base structure.
///
cef_base_ref_counted_t base;
///
// Method that will be called on receipt of a DevTools protocol message.
// |browser| is the originating browser instance. |message| is a UTF8-encoded
// JSON dictionary representing either a function result or an event.
// |message| is only valid for the scope of this callback and should be copied
// if necessary. Return true (1) if the message was handled or false (0) if
// the message should be further processed and passed to the
// OnDevToolsMethodResult or OnDevToolsEvent functions as appropriate.
//
// Method result dictionaries include an "id" (int) value that identifies the
// orginating function call sent from cef_browser_host_t::SendDevToolsMessage,
// and optionally either a "result" (dictionary) or "error" (dictionary)
// value. The "error" dictionary will contain "code" (int) and "message"
// (string) values. Event dictionaries include a "function" (string) value and
// optionally a "params" (dictionary) value. See the DevTools protocol
// documentation at https://chromedevtools.github.io/devtools-protocol/ for
// details of supported function calls and the expected "result" or "params"
// dictionary contents. JSON dictionaries can be parsed using the CefParseJSON
// function if desired, however be aware of performance considerations when
// parsing large messages (some of which may exceed 1MB in size).
///
int(CEF_CALLBACK* on_dev_tools_message)(
struct _cef_dev_tools_message_observer_t* self,
struct _cef_browser_t* browser,
const void* message,
size_t message_size);
///
// Method that will be called after attempted execution of a DevTools protocol
// function. |browser| is the originating browser instance. |message_id| is
// the "id" value that identifies the originating function call message. If
// the function succeeded |success| will be true (1) and |result| will be the
// UTF8-encoded JSON "result" dictionary value (which may be NULL). If the
// function failed |success| will be false (0) and |result| will be the
// UTF8-encoded JSON "error" dictionary value. |result| is only valid for the
// scope of this callback and should be copied if necessary. See the
// OnDevToolsMessage documentation for additional details on |result|
// contents.
///
void(CEF_CALLBACK* on_dev_tools_method_result)(
struct _cef_dev_tools_message_observer_t* self,
struct _cef_browser_t* browser,
int message_id,
int success,
const void* result,
size_t result_size);
///
// Method that will be called on receipt of a DevTools protocol event.
// |browser| is the originating browser instance. |function| is the "function"
// value. |params| is the UTF8-encoded JSON "params" dictionary value (which
// may be NULL). |params| is only valid for the scope of this callback and
// should be copied if necessary. See the OnDevToolsMessage documentation for
// additional details on |params| contents.
///
void(CEF_CALLBACK* on_dev_tools_event)(
struct _cef_dev_tools_message_observer_t* self,
struct _cef_browser_t* browser,
const cef_string_t* method,
const void* params,
size_t params_size);
///
// Method that will be called when the DevTools agent has attached. |browser|
// is the originating browser instance. This will generally occur in response
// to the first message sent while the agent is detached.
///
void(CEF_CALLBACK* on_dev_tools_agent_attached)(
struct _cef_dev_tools_message_observer_t* self,
struct _cef_browser_t* browser);
///
// Method that will be called when the DevTools agent has detached. |browser|
// is the originating browser instance. Any function results that were pending
// before the agent became detached will not be delivered, and any active
// event subscriptions will be canceled.
///
void(CEF_CALLBACK* on_dev_tools_agent_detached)(
struct _cef_dev_tools_message_observer_t* self,
struct _cef_browser_t* browser);
} cef_dev_tools_message_observer_t;
#ifdef __cplusplus
}
#endif
#endif // CEF_INCLUDE_CAPI_CEF_DEVTOOLS_MESSAGE_OBSERVER_CAPI_H_

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=3bc4225f43428d8a3a24dcac1830dafac18b0caf$ // $hash=14cf03e02d8ca3416e65f756470afd8185c7bc78$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_PARSER_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_PARSER_CAPI_H_
@ -140,6 +140,16 @@ CEF_EXPORT struct _cef_value_t* cef_parse_json(
const cef_string_t* json_string, const cef_string_t* json_string,
cef_json_parser_options_t options); cef_json_parser_options_t options);
///
// Parses the specified UTF8-encoded |json| buffer of size |json_size| and
// returns a dictionary or list representation. If JSON parsing fails this
// function returns NULL.
///
CEF_EXPORT struct _cef_value_t* cef_parse_json_buffer(
const void* json,
size_t json_size,
cef_json_parser_options_t options);
/// ///
// Parses the specified |json_string| and returns a dictionary or list // Parses the specified |json_string| and returns a dictionary or list
// representation. If JSON parsing fails this function returns NULL and // representation. If JSON parsing fails this function returns NULL and

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=a28219cc8c1cb53faacaf236374c3cf2c0c45bef$ // $hash=0167eb1abe614bd6391d273a8085fa3e53e7c217$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_
@ -253,6 +253,14 @@ typedef struct _cef_request_handler_t {
struct _cef_request_handler_t* self, struct _cef_request_handler_t* self,
struct _cef_browser_t* browser, struct _cef_browser_t* browser,
cef_termination_status_t status); cef_termination_status_t status);
///
// Called on the browser process UI thread when the window.document object of
// the main frame has been created.
///
void(CEF_CALLBACK* on_document_available_in_main_frame)(
struct _cef_request_handler_t* self,
struct _cef_browser_t* browser);
} cef_request_handler_t; } cef_request_handler_t;
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -42,13 +42,13 @@
// way that may cause binary incompatibility with other builds. The universal // way that may cause binary incompatibility with other builds. The universal
// hash value will change if any platform is affected whereas the platform hash // hash value will change if any platform is affected whereas the platform hash
// values will change only if that particular platform is affected. // values will change only if that particular platform is affected.
#define CEF_API_HASH_UNIVERSAL "a48c939496261acf27f45293484f2c9e4065ca6f" #define CEF_API_HASH_UNIVERSAL "eb44c98407c9286a64b8d872f49c772b3fa3eb63"
#if defined(OS_WIN) #if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "7ab8365e6f76e376342247954be2f90f69014776" #define CEF_API_HASH_PLATFORM "365b52117c7adf0a8bb6d96638954eb313168ff7"
#elif defined(OS_MACOSX) #elif defined(OS_MACOSX)
#define CEF_API_HASH_PLATFORM "01cc20ae8dbd5ce63ff50cac215855c4c58059f4" #define CEF_API_HASH_PLATFORM "1d1bca66ca61d742f507c08d4efda9884c94baff"
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "112ef821732253dfda925d4a1a6d490396bac6d2" #define CEF_API_HASH_PLATFORM "a87fd1ea68d4e79c838538fa78d79f8dd05acab2"
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -40,10 +40,12 @@
#include <vector> #include <vector>
#include "include/cef_base.h" #include "include/cef_base.h"
#include "include/cef_devtools_message_observer.h"
#include "include/cef_drag_data.h" #include "include/cef_drag_data.h"
#include "include/cef_frame.h" #include "include/cef_frame.h"
#include "include/cef_image.h" #include "include/cef_image.h"
#include "include/cef_navigation_entry.h" #include "include/cef_navigation_entry.h"
#include "include/cef_registration.h"
#include "include/cef_request_context.h" #include "include/cef_request_context.h"
class CefBrowserHost; class CefBrowserHost;
@ -517,6 +519,69 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
/*--cef()--*/ /*--cef()--*/
virtual bool HasDevTools() = 0; virtual bool HasDevTools() = 0;
///
// Send a method call message over the DevTools protocol. |message| must be a
// UTF8-encoded JSON dictionary that contains "id" (int), "method" (string)
// and "params" (dictionary, optional) values. See the DevTools protocol
// documentation at https://chromedevtools.github.io/devtools-protocol/ for
// details of supported methods and the expected "params" dictionary contents.
// |message| will be copied if necessary. This method will return true if
// called on the UI thread and the message was successfully submitted for
// validation, otherwise false. Validation will be applied asynchronously and
// any messages that fail due to formatting errors or missing parameters may
// be discarded without notification. Prefer ExecuteDevToolsMethod if a more
// structured approach to message formatting is desired.
//
// Every valid method call will result in an asynchronous method result or
// error message that references the sent message "id". Event messages are
// received while notifications are enabled (for example, between method calls
// for "Page.enable" and "Page.disable"). All received messages will be
// delivered to the observer(s) registered with AddDevToolsMessageObserver.
// See CefDevToolsMessageObserver::OnDevToolsMessage documentation for details
// of received message contents.
//
// Usage of the SendDevToolsMessage, ExecuteDevToolsMethod and
// AddDevToolsMessageObserver methods does not require an active DevTools
// front-end or remote-debugging session. Other active DevTools sessions will
// continue to function independently. However, any modification of global
// browser state by one session may not be reflected in the UI of other
// sessions.
//
// Communication with the DevTools front-end (when displayed) can be logged
// for development purposes by passing the
// `--devtools-protocol-log-file=<path>` command-line flag.
///
/*--cef()--*/
virtual bool SendDevToolsMessage(const void* message,
size_t message_size) = 0;
///
// Execute a method call over the DevTools protocol. This is a more structured
// version of SendDevToolsMessage. |message_id| is an incremental number that
// uniquely identifies the message (pass 0 to have the next number assigned
// automatically based on previous values). |method| is the method name.
// |params| are the method parameters, which may be empty. See the DevTools
// protocol documentation (linked above) for details of supported methods and
// the expected |params| dictionary contents. This method will return the
// assigned message ID if called on the UI thread and the message was
// successfully submitted for validation, otherwise 0. See the
// SendDevToolsMessage documentation for additional usage information.
///
/*--cef(optional_param=params)--*/
virtual int ExecuteDevToolsMethod(int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) = 0;
///
// Add an observer for DevTools protocol messages (method results and events).
// The observer will remain registered until the returned Registration object
// is destroyed. See the SendDevToolsMessage documentation for additional
// usage information.
///
/*--cef()--*/
virtual CefRefPtr<CefRegistration> AddDevToolsMessageObserver(
CefRefPtr<CefDevToolsMessageObserver> observer) = 0;
/// ///
// Retrieve a snapshot of current navigation entries as values sent to the // Retrieve a snapshot of current navigation entries as values sent to the
// specified visitor. If |current_only| is true only the current navigation // specified visitor. If |current_only| is true only the current navigation

View File

@ -0,0 +1,130 @@
// Copyright (c) 2020 Marshall A. Greenblatt. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ---------------------------------------------------------------------------
//
// The contents of this file must follow a specific format in order to
// support the CEF translator tool. See the translator.README.txt file in the
// tools directory for more information.
//
#ifndef CEF_INCLUDE_CEF_DEVTOOLS_MESSAGE_OBSERVER_H_
#define CEF_INCLUDE_CEF_DEVTOOLS_MESSAGE_OBSERVER_H_
#pragma once
#include "include/cef_base.h"
class CefBrowser;
///
// Callback interface for CefBrowserHost::AddDevToolsMessageObserver. The
// methods of this class will be called on the browser process UI thread.
///
/*--cef(source=client)--*/
class CefDevToolsMessageObserver : public virtual CefBaseRefCounted {
public:
///
// Method that will be called on receipt of a DevTools protocol message.
// |browser| is the originating browser instance. |message| is a UTF8-encoded
// JSON dictionary representing either a method result or an event. |message|
// is only valid for the scope of this callback and should be copied if
// necessary. Return true if the message was handled or false if the message
// should be further processed and passed to the OnDevToolsMethodResult or
// OnDevToolsEvent methods as appropriate.
//
// Method result dictionaries include an "id" (int) value that identifies the
// orginating method call sent from CefBrowserHost::SendDevToolsMessage, and
// optionally either a "result" (dictionary) or "error" (dictionary) value.
// The "error" dictionary will contain "code" (int) and "message" (string)
// values. Event dictionaries include a "method" (string) value and optionally
// a "params" (dictionary) value. See the DevTools protocol documentation at
// https://chromedevtools.github.io/devtools-protocol/ for details of
// supported method calls and the expected "result" or "params" dictionary
// contents. JSON dictionaries can be parsed using the CefParseJSON function
// if desired, however be aware of performance considerations when parsing
// large messages (some of which may exceed 1MB in size).
///
/*--cef()--*/
virtual bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
const void* message,
size_t message_size) {
return false;
}
///
// Method that will be called after attempted execution of a DevTools protocol
// method. |browser| is the originating browser instance. |message_id| is the
// "id" value that identifies the originating method call message. If the
// method succeeded |success| will be true and |result| will be the
// UTF8-encoded JSON "result" dictionary value (which may be empty). If the
// method failed |success| will be false and |result| will be the UTF8-encoded
// JSON "error" dictionary value. |result| is only valid for the scope of this
// callback and should be copied if necessary. See the OnDevToolsMessage
// documentation for additional details on |result| contents.
///
/*--cef(optional_param=result)--*/
virtual void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
int message_id,
bool success,
const void* result,
size_t result_size) {}
///
// Method that will be called on receipt of a DevTools protocol event.
// |browser| is the originating browser instance. |method| is the "method"
// value. |params| is the UTF8-encoded JSON "params" dictionary value (which
// may be empty). |params| is only valid for the scope of this callback and
// should be copied if necessary. See the OnDevToolsMessage documentation for
// additional details on |params| contents.
///
/*--cef(optional_param=params)--*/
virtual void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
const CefString& method,
const void* params,
size_t params_size) {}
///
// Method that will be called when the DevTools agent has attached. |browser|
// is the originating browser instance. This will generally occur in response
// to the first message sent while the agent is detached.
///
/*--cef()--*/
virtual void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) {}
///
// Method that will be called when the DevTools agent has detached. |browser|
// is the originating browser instance. Any method results that were pending
// before the agent became detached will not be delivered, and any active
// event subscriptions will be canceled.
///
/*--cef()--*/
virtual void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) {}
};
#endif // CEF_INCLUDE_CEF_DEVTOOLS_MESSAGE_OBSERVER_H_

View File

@ -133,6 +133,16 @@ CefString CefURIDecode(const CefString& text,
CefRefPtr<CefValue> CefParseJSON(const CefString& json_string, CefRefPtr<CefValue> CefParseJSON(const CefString& json_string,
cef_json_parser_options_t options); cef_json_parser_options_t options);
///
// Parses the specified UTF8-encoded |json| buffer of size |json_size| and
// returns a dictionary or list representation. If JSON parsing fails this
// method returns NULL.
///
/*--cef(capi_name=cef_parse_json_buffer)--*/
CefRefPtr<CefValue> CefParseJSON(const void* json,
size_t json_size,
cef_json_parser_options_t options);
/// ///
// Parses the specified |json_string| and returns a dictionary or list // Parses the specified |json_string| and returns a dictionary or list
// representation. If JSON parsing fails this method returns NULL and populates // representation. If JSON parsing fails this method returns NULL and populates

View File

@ -252,6 +252,13 @@ class CefRequestHandler : public virtual CefBaseRefCounted {
/*--cef()--*/ /*--cef()--*/
virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) {} TerminationStatus status) {}
///
// Called on the browser process UI thread when the window.document object of
// the main frame has been created.
///
/*--cef()--*/
virtual void OnDocumentAvailableInMainFrame(CefRefPtr<CefBrowser> browser) {}
}; };
#endif // CEF_INCLUDE_CEF_REQUEST_HANDLER_H_ #endif // CEF_INCLUDE_CEF_REQUEST_HANDLER_H_

View File

@ -16,8 +16,7 @@
#include "libcef/browser/browser_util.h" #include "libcef/browser/browser_util.h"
#include "libcef/browser/content_browser_client.h" #include "libcef/browser/content_browser_client.h"
#include "libcef/browser/context.h" #include "libcef/browser/context.h"
#include "libcef/browser/devtools/devtools_frontend.h" #include "libcef/browser/devtools/devtools_manager.h"
#include "libcef/browser/devtools/devtools_manager_delegate.h"
#include "libcef/browser/extensions/browser_extensions_util.h" #include "libcef/browser/extensions/browser_extensions_util.h"
#include "libcef/browser/extensions/extension_background_host.h" #include "libcef/browser/extensions/extension_background_host.h"
#include "libcef/browser/extensions/extension_system.h" #include "libcef/browser/extensions/extension_system.h"
@ -594,26 +593,6 @@ CefRefPtr<CefBrowserHostImpl> CefBrowserHostImpl::GetBrowserForFrameRoute(
// CefBrowserHostImpl methods. // CefBrowserHostImpl methods.
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// WebContentsObserver that will be notified when the frontend WebContents is
// destroyed so that the inspected browser can clear its DevTools references.
class CefBrowserHostImpl::DevToolsWebContentsObserver
: public content::WebContentsObserver {
public:
DevToolsWebContentsObserver(CefBrowserHostImpl* browser,
content::WebContents* frontend_web_contents)
: WebContentsObserver(frontend_web_contents), browser_(browser) {}
// WebContentsObserver methods:
void WebContentsDestroyed() override {
browser_->OnDevToolsWebContentsDestroyed();
}
private:
CefBrowserHostImpl* browser_;
DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver);
};
CefBrowserHostImpl::~CefBrowserHostImpl() {} CefBrowserHostImpl::~CefBrowserHostImpl() {}
CefRefPtr<CefBrowser> CefBrowserHostImpl::GetBrowser() { CefRefPtr<CefBrowser> CefBrowserHostImpl::GetBrowser() {
@ -906,39 +885,29 @@ void CefBrowserHostImpl::ShowDevTools(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client, CefRefPtr<CefClient> client,
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
const CefPoint& inspect_element_at) { const CefPoint& inspect_element_at) {
if (CEF_CURRENTLY_ON_UIT()) { if (!CEF_CURRENTLY_ON_UIT()) {
if (!web_contents())
return;
if (devtools_frontend_) {
if (!inspect_element_at.IsEmpty()) {
devtools_frontend_->InspectElementAt(inspect_element_at.x,
inspect_element_at.y);
}
devtools_frontend_->Focus();
return;
}
devtools_frontend_ = CefDevToolsFrontend::Show(
this, windowInfo, client, settings, inspect_element_at);
devtools_observer_.reset(new DevToolsWebContentsObserver(
this, devtools_frontend_->frontend_browser()->web_contents()));
} else {
ShowDevToolsHelper* helper = new ShowDevToolsHelper( ShowDevToolsHelper* helper = new ShowDevToolsHelper(
this, windowInfo, client, settings, inspect_element_at); this, windowInfo, client, settings, inspect_element_at);
CEF_POST_TASK(CEF_UIT, base::BindOnce(ShowDevToolsWithHelper, helper)); CEF_POST_TASK(CEF_UIT, base::BindOnce(ShowDevToolsWithHelper, helper));
return;
} }
if (!EnsureDevToolsManager())
return;
devtools_manager_->ShowDevTools(windowInfo, client, settings,
inspect_element_at);
} }
void CefBrowserHostImpl::CloseDevTools() { void CefBrowserHostImpl::CloseDevTools() {
if (CEF_CURRENTLY_ON_UIT()) { if (!CEF_CURRENTLY_ON_UIT()) {
if (!devtools_frontend_)
return;
devtools_frontend_->Close();
} else {
CEF_POST_TASK(CEF_UIT, CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefBrowserHostImpl::CloseDevTools, this)); base::BindOnce(&CefBrowserHostImpl::CloseDevTools, this));
return;
} }
if (!devtools_manager_)
return;
devtools_manager_->CloseDevTools();
} }
bool CefBrowserHostImpl::HasDevTools() { bool CefBrowserHostImpl::HasDevTools() {
@ -947,7 +916,84 @@ bool CefBrowserHostImpl::HasDevTools() {
return false; return false;
} }
return (devtools_frontend_ != nullptr); if (!devtools_manager_)
return false;
return devtools_manager_->HasDevTools();
}
bool CefBrowserHostImpl::SendDevToolsMessage(const void* message,
size_t message_size) {
if (!message || message_size == 0)
return false;
if (!CEF_CURRENTLY_ON_UIT()) {
std::string message_str(static_cast<const char*>(message), message_size);
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(
[](CefRefPtr<CefBrowserHostImpl> self, std::string message_str) {
self->SendDevToolsMessage(message_str.data(), message_str.size());
},
CefRefPtr<CefBrowserHostImpl>(this), std::move(message_str)));
return false;
}
if (!EnsureDevToolsManager())
return false;
return devtools_manager_->SendDevToolsMessage(message, message_size);
}
int CefBrowserHostImpl::ExecuteDevToolsMethod(
int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(
CEF_UIT, base::BindOnce(base::IgnoreResult(
&CefBrowserHostImpl::ExecuteDevToolsMethod),
this, message_id, method, params));
return 0;
}
if (!EnsureDevToolsManager())
return 0;
return devtools_manager_->ExecuteDevToolsMethod(message_id, method, params);
}
CefRefPtr<CefRegistration> CefBrowserHostImpl::AddDevToolsMessageObserver(
CefRefPtr<CefDevToolsMessageObserver> observer) {
if (!observer)
return nullptr;
auto registration = CefDevToolsManager::CreateRegistration(observer);
InitializeDevToolsRegistrationOnUIThread(registration);
return registration.get();
}
bool CefBrowserHostImpl::EnsureDevToolsManager() {
CEF_REQUIRE_UIT();
if (!web_contents())
return false;
if (!devtools_manager_) {
devtools_manager_.reset(new CefDevToolsManager(this));
}
return true;
}
void CefBrowserHostImpl::InitializeDevToolsRegistrationOnUIThread(
CefRefPtr<CefRegistration> registration) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(
&CefBrowserHostImpl::InitializeDevToolsRegistrationOnUIThread, this,
registration));
return;
}
if (!EnsureDevToolsManager())
return;
devtools_manager_->InitializeRegistrationOnUIThread(registration);
} }
void CefBrowserHostImpl::GetNavigationEntries( void CefBrowserHostImpl::GetNavigationEntries(
@ -1611,6 +1657,8 @@ void CefBrowserHostImpl::DestroyBrowser() {
recently_audible_timer_.Stop(); recently_audible_timer_.Stop();
audio_capturer_.reset(nullptr); audio_capturer_.reset(nullptr);
devtools_manager_.reset(nullptr);
// Delete the platform delegate. // Delete the platform delegate.
platform_delegate_.reset(nullptr); platform_delegate_.reset(nullptr);
@ -2704,16 +2752,24 @@ void CefBrowserHostImpl::DidStopLoading() {
} }
void CefBrowserHostImpl::DocumentAvailableInMainFrame() { void CefBrowserHostImpl::DocumentAvailableInMainFrame() {
base::AutoLock lock_scope(state_lock_); {
has_document_ = true; base::AutoLock lock_scope(state_lock_);
has_document_ = true;
}
if (client_) {
CefRefPtr<CefRequestHandler> handler = client_->GetRequestHandler();
if (handler)
handler->OnDocumentAvailableInMainFrame(this);
}
} }
void CefBrowserHostImpl::DidFailLoad( void CefBrowserHostImpl::DidFailLoad(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const GURL& validated_url, const GURL& validated_url,
int error_code) { int error_code) {
// The navigation failed after commit. OnLoadStart was called so we also call // The navigation failed after commit. OnLoadStart was called so we also
// OnLoadEnd. // call OnLoadEnd.
auto frame = browser_info_->GetFrameForHost(render_frame_host); auto frame = browser_info_->GetFrameForHost(render_frame_host);
frame->RefreshAttributes(); frame->RefreshAttributes();
OnLoadError(frame, validated_url, error_code); OnLoadError(frame, validated_url, error_code);
@ -2949,18 +3005,6 @@ CefBrowserHostImpl::CefBrowserHostImpl(
platform_delegate_(std::move(platform_delegate)), platform_delegate_(std::move(platform_delegate)),
is_windowless_(platform_delegate_->IsWindowless()), is_windowless_(platform_delegate_->IsWindowless()),
is_views_hosted_(platform_delegate_->IsViewsHosted()), is_views_hosted_(platform_delegate_->IsViewsHosted()),
host_window_handle_(kNullWindowHandle),
is_loading_(false),
can_go_back_(false),
can_go_forward_(false),
has_document_(false),
is_fullscreen_(false),
destruction_state_(DESTRUCTION_STATE_NONE),
window_destroyed_(false),
is_in_onsetfocus_(false),
focus_on_editable_field_(false),
mouse_cursor_change_disabled_(false),
devtools_frontend_(nullptr),
extension_(extension) { extension_(extension) {
if (opener.get() && !platform_delegate_->IsViewsHosted()) { if (opener.get() && !platform_delegate_->IsViewsHosted()) {
// GetOpenerWindowHandle() only returns a value for non-views-hosted // GetOpenerWindowHandle() only returns a value for non-views-hosted
@ -3171,11 +3215,6 @@ void CefBrowserHostImpl::OnTitleChange(const base::string16& title) {
} }
} }
void CefBrowserHostImpl::OnDevToolsWebContentsDestroyed() {
devtools_observer_.reset();
devtools_frontend_ = nullptr;
}
void CefBrowserHostImpl::EnsureFileDialogManager() { void CefBrowserHostImpl::EnsureFileDialogManager() {
CEF_REQUIRE_UIT(); CEF_REQUIRE_UIT();
if (!file_dialog_manager_.get() && platform_delegate_) { if (!file_dialog_manager_.get() && platform_delegate_) {

View File

@ -51,7 +51,7 @@ class Widget;
class CefAudioCapturer; class CefAudioCapturer;
class CefBrowserInfo; class CefBrowserInfo;
class CefBrowserPlatformDelegate; class CefBrowserPlatformDelegate;
class CefDevToolsFrontend; class CefDevToolsManager;
class SiteInstance; class SiteInstance;
// Implementation of CefBrowser. // Implementation of CefBrowser.
@ -192,6 +192,12 @@ class CefBrowserHostImpl : public CefBrowserHost,
const CefPoint& inspect_element_at) override; const CefPoint& inspect_element_at) override;
void CloseDevTools() override; void CloseDevTools() override;
bool HasDevTools() override; bool HasDevTools() override;
bool SendDevToolsMessage(const void* message, size_t message_size) override;
int ExecuteDevToolsMethod(int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) override;
CefRefPtr<CefRegistration> AddDevToolsMessageObserver(
CefRefPtr<CefDevToolsMessageObserver> observer) override;
void GetNavigationEntries(CefRefPtr<CefNavigationEntryVisitor> visitor, void GetNavigationEntries(CefRefPtr<CefNavigationEntryVisitor> visitor,
bool current_only) override; bool current_only) override;
void SetMouseCursorChangeDisabled(bool disabled) override; void SetMouseCursorChangeDisabled(bool disabled) override;
@ -523,8 +529,6 @@ class CefBrowserHostImpl : public CefBrowserHost,
std::unique_ptr<NavigationLock> CreateNavigationLock(); std::unique_ptr<NavigationLock> CreateNavigationLock();
private: private:
class DevToolsWebContentsObserver;
static CefRefPtr<CefBrowserHostImpl> CreateInternal( static CefRefPtr<CefBrowserHostImpl> CreateInternal(
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
CefRefPtr<CefClient> client, CefRefPtr<CefClient> client,
@ -581,8 +585,6 @@ class CefBrowserHostImpl : public CefBrowserHost,
void OnFullscreenModeChange(bool fullscreen); void OnFullscreenModeChange(bool fullscreen);
void OnTitleChange(const base::string16& title); void OnTitleChange(const base::string16& title);
void OnDevToolsWebContentsDestroyed();
// Create the CefFileDialogManager if it doesn't already exist. // Create the CefFileDialogManager if it doesn't already exist.
void EnsureFileDialogManager(); void EnsureFileDialogManager();
@ -591,6 +593,10 @@ class CefBrowserHostImpl : public CefBrowserHost,
void StartAudioCapturer(); void StartAudioCapturer();
void OnRecentlyAudibleTimerFired(); void OnRecentlyAudibleTimerFired();
bool EnsureDevToolsManager();
void InitializeDevToolsRegistrationOnUIThread(
CefRefPtr<CefRegistration> registration);
CefBrowserSettings settings_; CefBrowserSettings settings_;
CefRefPtr<CefClient> client_; CefRefPtr<CefClient> client_;
scoped_refptr<CefBrowserInfo> browser_info_; scoped_refptr<CefBrowserInfo> browser_info_;
@ -599,7 +605,7 @@ class CefBrowserHostImpl : public CefBrowserHost,
std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate_; std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate_;
const bool is_windowless_; const bool is_windowless_;
const bool is_views_hosted_; const bool is_views_hosted_;
CefWindowHandle host_window_handle_; CefWindowHandle host_window_handle_ = kNullWindowHandle;
// Non-nullptr if this object owns the WebContents. Will be nullptr for popup // Non-nullptr if this object owns the WebContents. Will be nullptr for popup
// browsers between the calls to WebContentsCreated() and AddNewContents(), // browsers between the calls to WebContentsCreated() and AddNewContents(),
@ -609,18 +615,18 @@ class CefBrowserHostImpl : public CefBrowserHost,
// Volatile state information. All access must be protected by the state lock. // Volatile state information. All access must be protected by the state lock.
base::Lock state_lock_; base::Lock state_lock_;
bool is_loading_; bool is_loading_ = false;
bool can_go_back_; bool can_go_back_ = false;
bool can_go_forward_; bool can_go_forward_ = false;
bool has_document_; bool has_document_ = false;
bool is_fullscreen_; bool is_fullscreen_ = false;
// The currently focused frame, or nullptr if the main frame is focused. // The currently focused frame, or nullptr if the main frame is focused.
CefRefPtr<CefFrameHostImpl> focused_frame_; CefRefPtr<CefFrameHostImpl> focused_frame_;
// Represents the current browser destruction state. Only accessed on the UI // Represents the current browser destruction state. Only accessed on the UI
// thread. // thread.
DestructionState destruction_state_; DestructionState destruction_state_ = DESTRUCTION_STATE_NONE;
// Navigation will not occur while |navigation_lock_count_| > 0. // Navigation will not occur while |navigation_lock_count_| > 0.
// |pending_navigation_action_| will be executed when the lock is released. // |pending_navigation_action_| will be executed when the lock is released.
@ -630,18 +636,18 @@ class CefBrowserHostImpl : public CefBrowserHost,
// True if the OS window hosting the browser has been destroyed. Only accessed // True if the OS window hosting the browser has been destroyed. Only accessed
// on the UI thread. // on the UI thread.
bool window_destroyed_; bool window_destroyed_ = false;
// True if currently in the OnSetFocus callback. Only accessed on the UI // True if currently in the OnSetFocus callback. Only accessed on the UI
// thread. // thread.
bool is_in_onsetfocus_; bool is_in_onsetfocus_ = false;
// True if the focus is currently on an editable field on the page. Only // True if the focus is currently on an editable field on the page. Only
// accessed on the UI thread. // accessed on the UI thread.
bool focus_on_editable_field_; bool focus_on_editable_field_ = false;
// True if mouse cursor change is disabled. // True if mouse cursor change is disabled.
bool mouse_cursor_change_disabled_; bool mouse_cursor_change_disabled_ = false;
// Used for managing notification subscriptions. // Used for managing notification subscriptions.
std::unique_ptr<content::NotificationRegistrar> registrar_; std::unique_ptr<content::NotificationRegistrar> registrar_;
@ -655,12 +661,8 @@ class CefBrowserHostImpl : public CefBrowserHost,
// Used for creating and managing context menus. // Used for creating and managing context menus.
std::unique_ptr<CefMenuManager> menu_manager_; std::unique_ptr<CefMenuManager> menu_manager_;
// Track the lifespan of the frontend WebContents associated with this // Used for creating and managing DevTools instances.
// browser. std::unique_ptr<CefDevToolsManager> devtools_manager_;
std::unique_ptr<DevToolsWebContentsObserver> devtools_observer_;
// CefDevToolsFrontend will delete itself when the frontend WebContents is
// destroyed.
CefDevToolsFrontend* devtools_frontend_;
// Observers that want to be notified of changes to this object. // Observers that want to be notified of changes to this object.
base::ObserverList<Observer>::Unchecked observers_; base::ObserverList<Observer>::Unchecked observers_;

View File

@ -0,0 +1,137 @@
// Copyright (c) 2020 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 "libcef/browser/devtools/devtools_controller.h"
#include "libcef/browser/devtools/devtools_util.h"
#include "libcef/browser/thread_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "content/public/browser/devtools_agent_host.h"
CefDevToolsController::CefDevToolsController(
content::WebContents* inspected_contents)
: inspected_contents_(inspected_contents), weak_ptr_factory_(this) {
DCHECK(inspected_contents_);
}
CefDevToolsController::~CefDevToolsController() {
if (agent_host_) {
agent_host_->DetachClient(this);
AgentHostClosed(agent_host_.get());
}
for (auto& observer : observers_) {
observer.OnDevToolsControllerDestroyed();
}
}
bool CefDevToolsController::SendDevToolsMessage(
const base::StringPiece& message) {
CEF_REQUIRE_UIT();
if (!EnsureAgentHost())
return false;
agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(message)));
return true;
}
int CefDevToolsController::ExecuteDevToolsMethod(
int suggested_message_id,
const std::string& method,
const base::DictionaryValue* params) {
CEF_REQUIRE_UIT();
if (!EnsureAgentHost())
return 0;
// Message IDs must always be increasing and unique.
int message_id = suggested_message_id;
if (message_id < next_message_id_)
message_id = next_message_id_++;
else
next_message_id_ = message_id + 1;
base::DictionaryValue message;
message.SetIntKey("id", message_id);
message.SetStringKey("method", method);
if (params)
message.SetKey("params", params->Clone());
std::string protocol_message;
if (!base::JSONWriter::Write(message, &protocol_message))
return 0;
agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(protocol_message)));
return message_id;
}
void CefDevToolsController::AgentHostClosed(
content::DevToolsAgentHost* agent_host) {
DCHECK(agent_host == agent_host_.get());
agent_host_ = nullptr;
for (auto& observer : observers_) {
observer.OnDevToolsAgentDetached();
}
}
void CefDevToolsController::AddObserver(Observer* observer) {
CEF_REQUIRE_UIT();
observers_.AddObserver(observer);
}
void CefDevToolsController::RemoveObserver(Observer* observer) {
CEF_REQUIRE_UIT();
observers_.RemoveObserver(observer);
}
void CefDevToolsController::DispatchProtocolMessage(
content::DevToolsAgentHost* agent_host,
base::span<const uint8_t> message) {
if (!observers_.might_have_observers())
return;
base::StringPiece str_message(reinterpret_cast<const char*>(message.data()),
message.size());
if (!devtools_util::ProtocolParser::IsValidMessage(str_message)) {
LOG(WARNING) << "Invalid message: " << str_message.substr(0, 100);
return;
}
devtools_util::ProtocolParser parser;
for (auto& observer : observers_) {
if (observer.OnDevToolsMessage(str_message)) {
continue;
}
// Only perform parsing a single time.
if (parser.Initialize(str_message) && parser.IsFailure()) {
LOG(WARNING) << "Failed to parse message: " << str_message.substr(0, 100);
}
if (parser.IsEvent()) {
observer.OnDevToolsEvent(parser.method_, parser.params_);
} else if (parser.IsResult()) {
observer.OnDevToolsMethodResult(parser.message_id_, parser.success_,
parser.params_);
}
}
}
bool CefDevToolsController::EnsureAgentHost() {
if (!agent_host_) {
agent_host_ =
content::DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
if (agent_host_) {
agent_host_->AttachClient(this);
for (auto& observer : observers_) {
observer.OnDevToolsAgentAttached();
}
}
}
return !!agent_host_;
}

View File

@ -0,0 +1,79 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_CONTROLLER_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_CONTROLLER_H_
#include <memory>
#include "content/public/browser/devtools_agent_host_client.h"
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/values.h"
namespace content {
class WebContents;
}
class CefDevToolsController : public content::DevToolsAgentHostClient {
public:
class Observer : public base::CheckedObserver {
public:
// See CefDevToolsMessageObserver documentation.
virtual bool OnDevToolsMessage(const base::StringPiece& message) = 0;
virtual void OnDevToolsMethodResult(int message_id,
bool success,
const base::StringPiece& result) = 0;
virtual void OnDevToolsEvent(const base::StringPiece& method,
const base::StringPiece& params) = 0;
virtual void OnDevToolsAgentAttached() = 0;
virtual void OnDevToolsAgentDetached() = 0;
virtual void OnDevToolsControllerDestroyed() = 0;
protected:
~Observer() override {}
};
// |inspected_contents| will outlive this object.
explicit CefDevToolsController(content::WebContents* inspected_contents);
~CefDevToolsController() override;
// See CefBrowserHost methods of the same name for documentation.
bool SendDevToolsMessage(const base::StringPiece& message);
int ExecuteDevToolsMethod(int message_id,
const std::string& method,
const base::DictionaryValue* params);
// |observer| must outlive this object or be removed.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
base::WeakPtr<CefDevToolsController> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
// content::DevToolsAgentHostClient implementation:
void AgentHostClosed(content::DevToolsAgentHost* agent_host) override;
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
base::span<const uint8_t> message) override;
bool EnsureAgentHost();
content::WebContents* const inspected_contents_;
scoped_refptr<content::DevToolsAgentHost> agent_host_;
int next_message_id_ = 1;
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<CefDevToolsController> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CefDevToolsController);
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_CONTROLLER_H_

View File

@ -6,13 +6,18 @@
#include <stddef.h> #include <stddef.h>
#include <iomanip>
#include <utility> #include <utility>
#include "libcef/browser/browser_context.h" #include "libcef/browser/browser_context.h"
#include "libcef/browser/content_browser_client.h"
#include "libcef/browser/devtools/devtools_manager_delegate.h" #include "libcef/browser/devtools/devtools_manager_delegate.h"
#include "libcef/browser/net/devtools_scheme_handler.h" #include "libcef/browser/net/devtools_scheme_handler.h"
#include "libcef/common/cef_switches.h"
#include "base/base64.h" #include "base/base64.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/guid.h" #include "base/guid.h"
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
@ -48,6 +53,13 @@
#include "services/network/public/cpp/simple_url_loader.h" #include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h" #include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/mojom/url_response_head.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h"
#include "storage/browser/file_system/native_file_util.h"
#if defined(OS_WIN)
#include <windows.h>
#elif defined(OS_POSIX)
#include <time.h>
#endif
namespace { namespace {
@ -86,6 +98,71 @@ std::unique_ptr<base::DictionaryValue> BuildObjectForResponse(
return response; return response;
} }
const int kMaxLogLineLength = 1024;
void WriteTimestamp(std::stringstream& stream) {
#if defined(OS_WIN)
SYSTEMTIME local_time;
GetLocalTime(&local_time);
stream << std::setfill('0') << std::setw(2) << local_time.wMonth
<< std::setw(2) << local_time.wDay << '/' << std::setw(2)
<< local_time.wHour << std::setw(2) << local_time.wMinute
<< std::setw(2) << local_time.wSecond << '.' << std::setw(3)
<< local_time.wMilliseconds;
#elif defined(OS_POSIX)
timeval tv;
gettimeofday(&tv, nullptr);
time_t t = tv.tv_sec;
struct tm local_time;
localtime_r(&t, &local_time);
struct tm* tm_time = &local_time;
stream << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
<< std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
<< tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
<< tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec;
#else
#error Unsupported platform
#endif
}
void LogProtocolMessage(const base::FilePath& log_file,
ProtocolMessageType type,
std::string to_log) {
// Track if logging has failed, in which case we don't keep trying.
static bool log_error = false;
if (log_error)
return;
if (storage::NativeFileUtil::EnsureFileExists(log_file, nullptr) !=
base::File::FILE_OK) {
LOG(ERROR) << "Failed to create file " << log_file.value();
log_error = true;
return;
}
std::string type_label;
switch (type) {
case ProtocolMessageType::METHOD:
type_label = "METHOD";
break;
case ProtocolMessageType::RESULT:
type_label = "RESULT";
break;
case ProtocolMessageType::EVENT:
type_label = "EVENT";
break;
}
std::stringstream stream;
WriteTimestamp(stream);
stream << ": " << type_label << ": " << to_log << "\n";
const std::string& str = stream.str();
if (!base::AppendToFile(log_file, str.c_str(), str.size())) {
LOG(ERROR) << "Failed to write file " << log_file.value();
log_error = true;
}
}
} // namespace } // namespace
class CefDevToolsFrontend::NetworkResourceLoader class CefDevToolsFrontend::NetworkResourceLoader
@ -156,11 +233,12 @@ const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
// static // static
CefDevToolsFrontend* CefDevToolsFrontend::Show( CefDevToolsFrontend* CefDevToolsFrontend::Show(
CefRefPtr<CefBrowserHostImpl> inspected_browser, CefBrowserHostImpl* inspected_browser,
const CefWindowInfo& windowInfo, const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client, CefRefPtr<CefClient> client,
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
const CefPoint& inspect_element_at) { const CefPoint& inspect_element_at,
base::OnceClosure frontend_destroyed_callback) {
CefBrowserSettings new_settings = settings; CefBrowserSettings new_settings = settings;
if (!windowInfo.windowless_rendering_enabled && if (!windowInfo.windowless_rendering_enabled &&
CefColorGetA(new_settings.background_color) != SK_AlphaOPAQUE) { CefColorGetA(new_settings.background_color) != SK_AlphaOPAQUE) {
@ -187,7 +265,8 @@ CefDevToolsFrontend* CefDevToolsFrontend::Show(
// destroyed. // destroyed.
CefDevToolsFrontend* devtools_frontend = new CefDevToolsFrontend( CefDevToolsFrontend* devtools_frontend = new CefDevToolsFrontend(
static_cast<CefBrowserHostImpl*>(frontend_browser.get()), static_cast<CefBrowserHostImpl*>(frontend_browser.get()),
inspected_contents, inspect_element_at); inspected_contents, inspect_element_at,
std::move(frontend_destroyed_callback));
// Need to load the URL after creating the DevTools objects. // Need to load the URL after creating the DevTools objects.
frontend_browser->GetMainFrame()->LoadURL(GetFrontendURL()); frontend_browser->GetMainFrame()->LoadURL(GetFrontendURL());
@ -216,23 +295,23 @@ void CefDevToolsFrontend::Close() {
frontend_browser_.get(), true)); frontend_browser_.get(), true));
} }
void CefDevToolsFrontend::DisconnectFromTarget() {
if (!agent_host_)
return;
agent_host_->DetachClient(this);
agent_host_ = nullptr;
}
CefDevToolsFrontend::CefDevToolsFrontend( CefDevToolsFrontend::CefDevToolsFrontend(
CefRefPtr<CefBrowserHostImpl> frontend_browser, CefBrowserHostImpl* frontend_browser,
content::WebContents* inspected_contents, content::WebContents* inspected_contents,
const CefPoint& inspect_element_at) const CefPoint& inspect_element_at,
base::OnceClosure frontend_destroyed_callback)
: content::WebContentsObserver(frontend_browser->web_contents()), : content::WebContentsObserver(frontend_browser->web_contents()),
frontend_browser_(frontend_browser), frontend_browser_(frontend_browser),
inspected_contents_(inspected_contents), inspected_contents_(inspected_contents),
inspect_element_at_(inspect_element_at), inspect_element_at_(inspect_element_at),
file_manager_(frontend_browser.get(), GetPrefs()), frontend_destroyed_callback_(std::move(frontend_destroyed_callback)),
weak_factory_(this) {} file_manager_(frontend_browser, GetPrefs()),
protocol_log_file_(
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kDevToolsProtocolLogFile)),
weak_factory_(this) {
DCHECK(!frontend_destroyed_callback_.is_null());
}
CefDevToolsFrontend::~CefDevToolsFrontend() {} CefDevToolsFrontend::~CefDevToolsFrontend() {}
@ -279,6 +358,7 @@ void CefDevToolsFrontend::WebContentsDestroyed() {
agent_host_->DetachClient(this); agent_host_->DetachClient(this);
agent_host_ = nullptr; agent_host_ = nullptr;
} }
std::move(frontend_destroyed_callback_).Run();
delete this; delete this;
} }
@ -300,6 +380,9 @@ void CefDevToolsFrontend::HandleMessageFromDevToolsFrontend(
std::string protocol_message; std::string protocol_message;
if (!agent_host_ || !params->GetString(0, &protocol_message)) if (!agent_host_ || !params->GetString(0, &protocol_message))
return; return;
if (ProtocolLoggingEnabled()) {
LogProtocolMessage(ProtocolMessageType::METHOD, protocol_message);
}
agent_host_->DispatchProtocolMessage( agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(protocol_message))); this, base::as_bytes(base::make_span(protocol_message)));
} else if (method == "loadCompleted") { } else if (method == "loadCompleted") {
@ -451,6 +534,14 @@ void CefDevToolsFrontend::DispatchProtocolMessage(
base::span<const uint8_t> message) { base::span<const uint8_t> message) {
base::StringPiece str_message(reinterpret_cast<const char*>(message.data()), base::StringPiece str_message(reinterpret_cast<const char*>(message.data()),
message.size()); message.size());
if (ProtocolLoggingEnabled()) {
// Quick check to avoid parsing the JSON object. Events begin with a
// "method" value whereas method results begin with an "id" value.
LogProtocolMessage(str_message.starts_with("{\"method\":")
? ProtocolMessageType::EVENT
: ProtocolMessageType::RESULT,
str_message);
}
if (str_message.length() < kMaxMessageChunkSize) { if (str_message.length() < kMaxMessageChunkSize) {
std::string param; std::string param;
base::EscapeJSONString(str_message, true, &param); base::EscapeJSONString(str_message, true, &param);
@ -504,6 +595,23 @@ void CefDevToolsFrontend::SendMessageAck(int request_id,
CallClientFunction("DevToolsAPI.embedderMessageAck", &id_value, arg, nullptr); CallClientFunction("DevToolsAPI.embedderMessageAck", &id_value, arg, nullptr);
} }
bool CefDevToolsFrontend::ProtocolLoggingEnabled() const {
return !protocol_log_file_.empty();
}
void CefDevToolsFrontend::LogProtocolMessage(ProtocolMessageType type,
const base::StringPiece& message) {
DCHECK(ProtocolLoggingEnabled());
std::string to_log = message.substr(0, kMaxLogLineLength).as_string();
// Execute in an ordered context that allows blocking.
auto task_runner = CefContentBrowserClient::Get()->background_task_runner();
task_runner->PostTask(
FROM_HERE, base::BindOnce(::LogProtocolMessage, protocol_log_file_, type,
std::move(to_log)));
}
void CefDevToolsFrontend::AgentHostClosed( void CefDevToolsFrontend::AgentHostClosed(
content::DevToolsAgentHost* agent_host) { content::DevToolsAgentHost* agent_host) {
DCHECK(agent_host == agent_host_.get()); DCHECK(agent_host == agent_host_.get());

View File

@ -11,6 +11,7 @@
#include "libcef/browser/devtools/devtools_file_manager.h" #include "libcef/browser/devtools/devtools_file_manager.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
@ -31,36 +32,38 @@ class WebContents;
class PrefService; class PrefService;
enum class ProtocolMessageType {
METHOD,
RESULT,
EVENT,
};
class CefDevToolsFrontend : public content::WebContentsObserver, class CefDevToolsFrontend : public content::WebContentsObserver,
public content::DevToolsAgentHostClient { public content::DevToolsAgentHostClient {
public: public:
static CefDevToolsFrontend* Show( static CefDevToolsFrontend* Show(
CefRefPtr<CefBrowserHostImpl> inspected_browser, CefBrowserHostImpl* inspected_browser,
const CefWindowInfo& windowInfo, const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client, CefRefPtr<CefClient> client,
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
const CefPoint& inspect_element_at); const CefPoint& inspect_element_at,
base::OnceClosure frontend_destroyed_callback);
void Activate(); void Activate();
void Focus(); void Focus();
void InspectElementAt(int x, int y); void InspectElementAt(int x, int y);
void Close(); void Close();
void DisconnectFromTarget();
void CallClientFunction(const std::string& function_name, void CallClientFunction(const std::string& function_name,
const base::Value* arg1, const base::Value* arg1,
const base::Value* arg2, const base::Value* arg2,
const base::Value* arg3); const base::Value* arg3);
CefRefPtr<CefBrowserHostImpl> frontend_browser() const {
return frontend_browser_;
}
private: private:
CefDevToolsFrontend(CefRefPtr<CefBrowserHostImpl> frontend_browser, CefDevToolsFrontend(CefBrowserHostImpl* frontend_browser,
content::WebContents* inspected_contents, content::WebContents* inspected_contents,
const CefPoint& inspect_element_at); const CefPoint& inspect_element_at,
base::OnceClosure destroyed_callback);
~CefDevToolsFrontend() override; ~CefDevToolsFrontend() override;
// content::DevToolsAgentHostClient implementation. // content::DevToolsAgentHostClient implementation.
@ -78,12 +81,17 @@ class CefDevToolsFrontend : public content::WebContentsObserver,
void SendMessageAck(int request_id, const base::Value* arg1); void SendMessageAck(int request_id, const base::Value* arg1);
bool ProtocolLoggingEnabled() const;
void LogProtocolMessage(ProtocolMessageType type,
const base::StringPiece& message);
PrefService* GetPrefs() const; PrefService* GetPrefs() const;
CefRefPtr<CefBrowserHostImpl> frontend_browser_; CefRefPtr<CefBrowserHostImpl> frontend_browser_;
content::WebContents* inspected_contents_; content::WebContents* inspected_contents_;
scoped_refptr<content::DevToolsAgentHost> agent_host_; scoped_refptr<content::DevToolsAgentHost> agent_host_;
CefPoint inspect_element_at_; CefPoint inspect_element_at_;
base::OnceClosure frontend_destroyed_callback_;
std::unique_ptr<content::DevToolsFrontendHost> frontend_host_; std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
class NetworkResourceLoader; class NetworkResourceLoader;
@ -93,6 +101,9 @@ class CefDevToolsFrontend : public content::WebContentsObserver,
using ExtensionsAPIs = std::map<std::string, std::string>; using ExtensionsAPIs = std::map<std::string, std::string>;
ExtensionsAPIs extensions_api_; ExtensionsAPIs extensions_api_;
CefDevToolsFileManager file_manager_; CefDevToolsFileManager file_manager_;
const base::FilePath protocol_log_file_;
base::WeakPtrFactory<CefDevToolsFrontend> weak_factory_; base::WeakPtrFactory<CefDevToolsFrontend> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CefDevToolsFrontend); DISALLOW_COPY_AND_ASSIGN(CefDevToolsFrontend);

View File

@ -0,0 +1,200 @@
// Copyright (c) 2020 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 "libcef/browser/devtools/devtools_manager.h"
#include "libcef/browser/devtools/devtools_controller.h"
#include "libcef/browser/devtools/devtools_frontend.h"
#include "content/public/browser/web_contents.h"
namespace {
// May be created on any thread but will be destroyed on the UI thread.
class CefDevToolsRegistrationImpl : public CefRegistration,
public CefDevToolsController::Observer {
public:
explicit CefDevToolsRegistrationImpl(
CefRefPtr<CefDevToolsMessageObserver> observer)
: observer_(observer) {
DCHECK(observer_);
}
~CefDevToolsRegistrationImpl() override {
CEF_REQUIRE_UIT();
// May be null if OnDevToolsControllerDestroyed was called.
if (!controller_)
return;
controller_->RemoveObserver(this);
}
void Initialize(CefBrowserHostImpl* browser,
base::WeakPtr<CefDevToolsController> controller) {
CEF_REQUIRE_UIT();
DCHECK(browser && controller);
DCHECK(!browser_ && !controller_);
browser_ = browser;
controller_ = controller;
controller_->AddObserver(this);
}
private:
// CefDevToolsController::Observer methods:
bool OnDevToolsMessage(const base::StringPiece& message) override {
CEF_REQUIRE_UIT();
return observer_->OnDevToolsMessage(browser_, message.data(),
message.size());
}
void OnDevToolsMethodResult(int message_id,
bool success,
const base::StringPiece& result) override {
CEF_REQUIRE_UIT();
observer_->OnDevToolsMethodResult(browser_, message_id, success,
result.data(), result.size());
}
void OnDevToolsEvent(const base::StringPiece& method,
const base::StringPiece& params) override {
CEF_REQUIRE_UIT();
observer_->OnDevToolsEvent(browser_, method.as_string(), params.data(),
params.size());
}
void OnDevToolsAgentAttached() override {
CEF_REQUIRE_UIT();
observer_->OnDevToolsAgentAttached(browser_);
}
void OnDevToolsAgentDetached() override {
CEF_REQUIRE_UIT();
observer_->OnDevToolsAgentDetached(browser_);
}
void OnDevToolsControllerDestroyed() override {
CEF_REQUIRE_UIT();
browser_ = nullptr;
controller_.reset();
}
CefRefPtr<CefDevToolsMessageObserver> observer_;
CefBrowserHostImpl* browser_ = nullptr;
base::WeakPtr<CefDevToolsController> controller_;
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefDevToolsRegistrationImpl);
DISALLOW_COPY_AND_ASSIGN(CefDevToolsRegistrationImpl);
};
} // namespace
CefDevToolsManager::CefDevToolsManager(CefBrowserHostImpl* inspected_browser)
: inspected_browser_(inspected_browser), weak_ptr_factory_(this) {
CEF_REQUIRE_UIT();
}
CefDevToolsManager::~CefDevToolsManager() {
CEF_REQUIRE_UIT();
}
void CefDevToolsManager::ShowDevTools(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at) {
CEF_REQUIRE_UIT();
if (devtools_frontend_) {
if (!inspect_element_at.IsEmpty()) {
devtools_frontend_->InspectElementAt(inspect_element_at.x,
inspect_element_at.y);
}
devtools_frontend_->Focus();
return;
}
devtools_frontend_ = CefDevToolsFrontend::Show(
inspected_browser_, windowInfo, client, settings, inspect_element_at,
base::BindOnce(&CefDevToolsManager::OnFrontEndDestroyed,
weak_ptr_factory_.GetWeakPtr()));
}
void CefDevToolsManager::CloseDevTools() {
CEF_REQUIRE_UIT();
if (!devtools_frontend_)
return;
devtools_frontend_->Close();
}
bool CefDevToolsManager::HasDevTools() {
CEF_REQUIRE_UIT();
return !!devtools_frontend_;
}
bool CefDevToolsManager::SendDevToolsMessage(const void* message,
size_t message_size) {
CEF_REQUIRE_UIT();
if (!message || message_size == 0)
return false;
if (!EnsureController())
return false;
return devtools_controller_->SendDevToolsMessage(
base::StringPiece(static_cast<const char*>(message), message_size));
}
int CefDevToolsManager::ExecuteDevToolsMethod(
int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) {
CEF_REQUIRE_UIT();
if (method.empty())
return 0;
if (!EnsureController())
return 0;
if (params && params->IsValid()) {
CefDictionaryValueImpl* impl =
static_cast<CefDictionaryValueImpl*>(params.get());
CefValueController::AutoLock lock_scope(impl->controller());
return devtools_controller_->ExecuteDevToolsMethod(message_id, method,
impl->GetValueUnsafe());
} else {
return devtools_controller_->ExecuteDevToolsMethod(message_id, method,
nullptr);
}
}
// static
CefRefPtr<CefRegistration> CefDevToolsManager::CreateRegistration(
CefRefPtr<CefDevToolsMessageObserver> observer) {
DCHECK(observer);
return new CefDevToolsRegistrationImpl(observer);
}
void CefDevToolsManager::InitializeRegistrationOnUIThread(
CefRefPtr<CefRegistration> registration) {
CEF_REQUIRE_UIT();
if (!EnsureController())
return;
static_cast<CefDevToolsRegistrationImpl*>(registration.get())
->Initialize(inspected_browser_, devtools_controller_->GetWeakPtr());
}
void CefDevToolsManager::OnFrontEndDestroyed() {
devtools_frontend_ = nullptr;
}
bool CefDevToolsManager::EnsureController() {
if (!devtools_controller_) {
devtools_controller_.reset(
new CefDevToolsController(inspected_browser_->web_contents()));
}
return true;
}

View File

@ -0,0 +1,69 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
#pragma once
#include "include/cef_browser.h"
#include "base/memory/weak_ptr.h"
class CefBrowserHostImpl;
class CefDevToolsController;
class CefDevToolsFrontend;
namespace content {
class WebContents;
}
// Manages DevTools instances. Methods must be called on the UI thread unless
// otherwise indicated.
class CefDevToolsManager {
public:
// |inspected_browser| will outlive this object.
explicit CefDevToolsManager(CefBrowserHostImpl* inspected_browser);
~CefDevToolsManager();
// See CefBrowserHost methods of the same name for documentation.
void ShowDevTools(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at);
void CloseDevTools();
bool HasDevTools();
bool SendDevToolsMessage(const void* message, size_t message_size);
int ExecuteDevToolsMethod(int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> param);
// These methods are used to implement
// CefBrowserHost::AddDevToolsMessageObserver. CreateRegistration is safe to
// call on any thread. InitializeRegistrationOnUIThread should be called
// immediately afterwards on the UI thread.
static CefRefPtr<CefRegistration> CreateRegistration(
CefRefPtr<CefDevToolsMessageObserver> observer);
void InitializeRegistrationOnUIThread(
CefRefPtr<CefRegistration> registration);
private:
void OnFrontEndDestroyed();
bool EnsureController();
CefBrowserHostImpl* const inspected_browser_;
// CefDevToolsFrontend will delete itself when the frontend WebContents is
// destroyed.
CefDevToolsFrontend* devtools_frontend_ = nullptr;
// Used for sending DevTools protocol messages without an active frontend.
std::unique_ptr<CefDevToolsController> devtools_controller_;
base::WeakPtrFactory<CefDevToolsManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CefDevToolsManager);
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_

View File

@ -0,0 +1,131 @@
// Copyright (c) 2020 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 "libcef/browser/devtools/devtools_util.h"
#include "base/strings/string_number_conversions.h"
namespace devtools_util {
namespace {
bool IsValidDictionary(const base::StringPiece& str, bool allow_empty) {
return str.length() >= (allow_empty ? 2 : 3) && str[0] == '{' &&
str[str.length() - 1] == '}';
}
// Example:
// {"method":"Target.targetDestroyed","params":{"targetId":"1234..."}}
bool ParseEvent(const base::StringPiece& message,
base::StringPiece& method,
base::StringPiece& params) {
static const char kMethodStart[] = "{\"method\":\"";
static const char kMethodEnd[] = "\"";
static const char kParamsStart[] = ",\"params\":";
if (!message.starts_with(kMethodStart))
return false;
const size_t method_start = sizeof(kMethodStart) - 1;
const size_t method_end = message.find(kMethodEnd, method_start);
if (method_end < 0U)
return false;
method = message.substr(method_start, method_end - method_start);
if (method.empty())
return false;
size_t remainder_start = method_end + sizeof(kMethodEnd) - 1;
if (remainder_start == message.size() - 1) {
// No more contents.
params = base::StringPiece();
} else {
const base::StringPiece& remainder = message.substr(remainder_start);
if (remainder.starts_with(kParamsStart)) {
// Stop immediately before the message closing bracket.
remainder_start += sizeof(kParamsStart) - 1;
params =
message.substr(remainder_start, message.size() - 1 - remainder_start);
} else {
// Invalid format.
return false;
}
if (!IsValidDictionary(params, /*allow_empty=*/true))
return false;
}
return true;
}
// Examples:
// {"id":3,"result":{}}
// {"id":4,"result":{"debuggerId":"-2193881606781505058.81393575456727957"}}
// {"id":5,"error":{"code":-32000,"message":"Not supported"}}
bool ParseResult(const base::StringPiece& message,
int& message_id,
bool& success,
base::StringPiece& result) {
static const char kIdStart[] = "{\"id\":";
static const char kIdEnd[] = ",";
static const char kResultStart[] = "\"result\":";
static const char kErrorStart[] = "\"error\":";
if (!message.starts_with(kIdStart))
return false;
const size_t id_start = sizeof(kIdStart) - 1;
const size_t id_end = message.find(kIdEnd, id_start);
if (id_end < 0U)
return false;
const base::StringPiece& id_str = message.substr(id_start, id_end - id_start);
if (id_str.empty() || !base::StringToInt(id_str, &message_id))
return false;
size_t remainder_start = id_end + sizeof(kIdEnd) - 1;
const base::StringPiece& remainder = message.substr(remainder_start);
if (remainder.starts_with(kResultStart)) {
// Stop immediately before the message closing bracket.
remainder_start += sizeof(kResultStart) - 1;
result =
message.substr(remainder_start, message.size() - 1 - remainder_start);
success = true;
} else if (remainder.starts_with(kErrorStart)) {
// Stop immediately before the message closing bracket.
remainder_start += sizeof(kErrorStart) - 1;
result =
message.substr(remainder_start, message.size() - 1 - remainder_start);
success = false;
} else {
// Invalid format.
return false;
}
if (!IsValidDictionary(result, /*allow_empty=*/true))
return false;
return true;
}
} // namespace
// static
bool ProtocolParser::IsValidMessage(const base::StringPiece& message) {
return IsValidDictionary(message, /*allow_empty=*/false);
}
bool ProtocolParser::Initialize(const base::StringPiece& message) {
if (status_ != UNINITIALIZED)
return false;
if (ParseEvent(message, method_, params_)) {
status_ = EVENT;
} else if (ParseResult(message, message_id_, success_, params_)) {
status_ = RESULT;
} else {
status_ = FAILURE;
}
return true;
}
} // namespace devtools_util

View File

@ -0,0 +1,72 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_UTIL_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_UTIL_H_
#pragma once
#include "base/strings/string_piece.h"
namespace devtools_util {
// Fast parser for DevTools JSON protocol messages. This implementation makes
// certain assumptions about the JSON object structure (value order and
// formatting) to avoid stateful parsing of messages that may be large
// (sometimes > 1MB in size). The message must be a JSON dictionary that starts
// with a "method" or "id" value which is non-empty and of the expected data
// type. Messages that have a "method" value (event message) may optionally have
// a "params" dictionary. Messages that have an "id" value (result message) must
// have a "result" or "error" dictionary. The dictionary contents are not
// validated and may be empty ("{}").
//
// Example event message:
// {"method":"Target.targetDestroyed","params":{"targetId":"1234..."}}
//
// Example result messages:
// {"id":3,"result":{}}
// {"id":4,"result":{"debuggerId":"-2193881606781505058.81393575456727957"}}
// {"id":5,"error":{"code":-32000,"message":"Not supported"}}
struct ProtocolParser {
ProtocolParser() = default;
// Checks for a non-empty JSON dictionary.
static bool IsValidMessage(const base::StringPiece& message);
// Returns false if already initialized.
bool Initialize(const base::StringPiece& message);
bool IsInitialized() const { return status_ != UNINITIALIZED; }
bool IsEvent() const { return status_ == EVENT; }
bool IsResult() const { return status_ == RESULT; }
bool IsFailure() const { return status_ == FAILURE; }
void Reset() { status_ = UNINITIALIZED; }
// For event messages:
// "method" string:
base::StringPiece method_;
// For result messages:
// "id" int:
int message_id_ = 0;
// true if "result" value, false if "error" value:
bool success_ = false;
// For both:
// "params", "result" or "error" dictionary:
base::StringPiece params_;
private:
enum Status {
UNINITIALIZED,
EVENT, // Event message.
RESULT, // Result message.
FAILURE, // Parsing failure.
};
Status status_ = UNINITIALIZED;
};
} // namespace devtools_util
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_UTIL_H_

View File

@ -0,0 +1,204 @@
// Copyright (c) 2020 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 "libcef/browser/devtools/devtools_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using namespace devtools_util;
TEST(DevToolsUtil, ProtocolParser_IsValidMessage) {
// Empty dictionary is not valid.
EXPECT_FALSE(ProtocolParser::IsValidMessage(""));
EXPECT_FALSE(ProtocolParser::IsValidMessage("{}"));
// Incorrectly formatted dictionary is not valid.
EXPECT_FALSE(ProtocolParser::IsValidMessage("{ ]"));
// Everything else is valid (we don't verify JSON structure).
EXPECT_TRUE(ProtocolParser::IsValidMessage("{ }"));
EXPECT_TRUE(ProtocolParser::IsValidMessage("{blah blah}"));
EXPECT_TRUE(ProtocolParser::IsValidMessage("{method:\"foobar\"}"));
}
TEST(DevToolsUtil, ProtocolParser_Initialize_IsFailure_Unknown) {
ProtocolParser parser;
EXPECT_FALSE(parser.IsInitialized());
// Empty message is invalid.
EXPECT_TRUE(parser.Initialize(""));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Empty dictionary is invalid.
EXPECT_TRUE(parser.Initialize("{}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Unrecognized dictionary type is invalid.
EXPECT_TRUE(parser.Initialize("{blah blah}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
}
TEST(DevToolsUtil, ProtocolParser_Initialize_IsFailure_EventMalformed) {
ProtocolParser parser;
EXPECT_FALSE(parser.IsInitialized());
// Empty method is invalid.
EXPECT_TRUE(parser.Initialize("{\"method\":\"\"}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Unrecognized value is invalid.
EXPECT_TRUE(parser.Initialize("{\"method\":\"foo\",oops:false}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Params must be a dictionary.
EXPECT_TRUE(parser.Initialize("{\"method\":\",params:[]}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
}
TEST(DevToolsUtil, ProtocolParser_Initialize_IsEvent) {
ProtocolParser parser;
EXPECT_FALSE(parser.IsInitialized());
// Method without params is valid.
std::string message = "{\"method\":\"Test.myMethod\"}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsEvent());
EXPECT_STREQ("Test.myMethod", parser.method_.as_string().data());
EXPECT_TRUE(parser.params_.empty());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Method with empty params dictionary is valid.
message = "{\"method\":\"Test.myMethod2\",\"params\":{}}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsEvent());
EXPECT_STREQ("Test.myMethod2", parser.method_.as_string().data());
EXPECT_STREQ("{}", parser.params_.as_string().data());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Method with non-empty params dictionary is valid.
message = "{\"method\":\"Test.myMethod3\",\"params\":{\"foo\":\"bar\"}}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsEvent());
EXPECT_STREQ("Test.myMethod3", parser.method_.as_string().data());
EXPECT_STREQ("{\"foo\":\"bar\"}", parser.params_.as_string().data());
}
TEST(DevToolsUtil, ProtocolParser_Initialize_IsFailure_ResultMalformed) {
ProtocolParser parser;
EXPECT_FALSE(parser.IsInitialized());
// Empty ID is invalid.
EXPECT_TRUE(parser.Initialize("{\"id\":,result:{}}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Missing result or error value is invalid.
EXPECT_TRUE(parser.Initialize("{\"id\":1}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Unrecognized value is invalid.
EXPECT_TRUE(parser.Initialize("{\"id\":1,oops:false}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Result must be a dictionary.
EXPECT_TRUE(parser.Initialize("{\"id\":1,\"result\":[]}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Error must be a dictionary.
EXPECT_TRUE(parser.Initialize("{\"id\":1,\"error\":[]}"));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsFailure());
}
TEST(DevToolsUtil, ProtocolParser_Initialize_IsResult_Result) {
ProtocolParser parser;
EXPECT_FALSE(parser.IsInitialized());
// Id with empty result dictionary is valid.
std::string message = "{\"id\":1,\"result\":{}}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsResult());
EXPECT_EQ(1, parser.message_id_);
EXPECT_TRUE(parser.success_);
EXPECT_STREQ("{}", parser.params_.as_string().data());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Id with non-empty result dictionary is valid.
message = "{\"id\":2,\"result\":{\"foo\":\"bar\"}}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsResult());
EXPECT_EQ(2, parser.message_id_);
EXPECT_TRUE(parser.success_);
EXPECT_STREQ("{\"foo\":\"bar\"}", parser.params_.as_string().data());
}
TEST(DevToolsUtil, ProtocolParser_Initialize_IsResult_Error) {
ProtocolParser parser;
EXPECT_FALSE(parser.IsInitialized());
// Id with empty error dictionary is valid.
std::string message = "{\"id\":1,\"error\":{}}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsResult());
EXPECT_EQ(1, parser.message_id_);
EXPECT_FALSE(parser.success_);
EXPECT_STREQ("{}", parser.params_.as_string().data());
parser.Reset();
EXPECT_FALSE(parser.IsInitialized());
// Id with non-empty error dictionary is valid.
message = "{\"id\":2,\"error\":{\"foo\":\"bar\"}}";
EXPECT_TRUE(parser.Initialize(message));
EXPECT_TRUE(parser.IsInitialized());
EXPECT_TRUE(parser.IsResult());
EXPECT_EQ(2, parser.message_id_);
EXPECT_FALSE(parser.success_);
EXPECT_STREQ("{\"foo\":\"bar\"}", parser.params_.as_string().data());
}

View File

@ -114,11 +114,13 @@ const char kPluginPolicy_Block[] = "block";
const char kEnablePreferenceTesting[] = "enable-preference-testing"; const char kEnablePreferenceTesting[] = "enable-preference-testing";
// Enable print preview. // Enable print preview.
extern const char kEnablePrintPreview[] = "enable-print-preview"; const char kEnablePrintPreview[] = "enable-print-preview";
// Disable the timeout for delivering new browser info to the renderer process. // Disable the timeout for delivering new browser info to the renderer process.
extern const char kDisableNewBrowserInfoTimeout[] = const char kDisableNewBrowserInfoTimeout[] = "disable-new-browser-info-timeout";
"disable-new-browser-info-timeout";
// File used for logging DevTools protocol messages.
const char kDevToolsProtocolLogFile[] = "devtools-protocol-log-file";
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
// Path to the framework directory. // Path to the framework directory.

View File

@ -53,6 +53,7 @@ extern const char kPluginPolicy_Block[];
extern const char kEnablePreferenceTesting[]; extern const char kEnablePreferenceTesting[];
extern const char kEnablePrintPreview[]; extern const char kEnablePrintPreview[];
extern const char kDisableNewBrowserInfoTimeout[]; extern const char kDisableNewBrowserInfoTimeout[];
extern const char kDevToolsProtocolLogFile[];
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
extern const char kFrameworkDirPath[]; extern const char kFrameworkDirPath[];

View File

@ -34,8 +34,17 @@ int GetJSONWriterOptions(cef_json_writer_options_t options) {
CefRefPtr<CefValue> CefParseJSON(const CefString& json_string, CefRefPtr<CefValue> CefParseJSON(const CefString& json_string,
cef_json_parser_options_t options) { cef_json_parser_options_t options) {
const std::string& json = json_string.ToString(); const std::string& json = json_string.ToString();
base::Optional<base::Value> parse_result = return CefParseJSON(json.data(), json.size(), options);
base::JSONReader::Read(json, GetJSONReaderOptions(options)); }
CefRefPtr<CefValue> CefParseJSON(const void* json,
size_t json_size,
cef_json_parser_options_t options) {
if (!json || json_size == 0)
return nullptr;
base::Optional<base::Value> parse_result = base::JSONReader::Read(
base::StringPiece(static_cast<const char*>(json), json_size),
GetJSONReaderOptions(options));
if (parse_result) { if (parse_result) {
return new CefValueImpl( return new CefValueImpl(
base::Value::ToUniquePtrValue(std::move(parse_result.value())) base::Value::ToUniquePtrValue(std::move(parse_result.value()))

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=a84451b4f759f2a4a0fe673d90be1fb5053bfd1a$ // $hash=516b55b7ea53e2de2b096e85ba0eb83f2a2693f3$
// //
#include "libcef_dll/cpptoc/browser_host_cpptoc.h" #include "libcef_dll/cpptoc/browser_host_cpptoc.h"
@ -18,8 +18,10 @@
#include "libcef_dll/cpptoc/drag_data_cpptoc.h" #include "libcef_dll/cpptoc/drag_data_cpptoc.h"
#include "libcef_dll/cpptoc/extension_cpptoc.h" #include "libcef_dll/cpptoc/extension_cpptoc.h"
#include "libcef_dll/cpptoc/navigation_entry_cpptoc.h" #include "libcef_dll/cpptoc/navigation_entry_cpptoc.h"
#include "libcef_dll/cpptoc/registration_cpptoc.h"
#include "libcef_dll/cpptoc/request_context_cpptoc.h" #include "libcef_dll/cpptoc/request_context_cpptoc.h"
#include "libcef_dll/ctocpp/client_ctocpp.h" #include "libcef_dll/ctocpp/client_ctocpp.h"
#include "libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h"
#include "libcef_dll/ctocpp/download_image_callback_ctocpp.h" #include "libcef_dll/ctocpp/download_image_callback_ctocpp.h"
#include "libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h" #include "libcef_dll/ctocpp/navigation_entry_visitor_ctocpp.h"
#include "libcef_dll/ctocpp/pdf_print_callback_ctocpp.h" #include "libcef_dll/ctocpp/pdf_print_callback_ctocpp.h"
@ -522,6 +524,81 @@ int CEF_CALLBACK browser_host_has_dev_tools(struct _cef_browser_host_t* self) {
return _retval; return _retval;
} }
int CEF_CALLBACK
browser_host_send_dev_tools_message(struct _cef_browser_host_t* self,
const void* message,
size_t message_size) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: message; type: simple_byaddr
DCHECK(message);
if (!message)
return 0;
// Execute
bool _retval = CefBrowserHostCppToC::Get(self)->SendDevToolsMessage(
message, message_size);
// Return type: bool
return _retval;
}
int CEF_CALLBACK
browser_host_execute_dev_tools_method(struct _cef_browser_host_t* self,
int message_id,
const cef_string_t* method,
struct _cef_dictionary_value_t* params) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: method; type: string_byref_const
DCHECK(method);
if (!method)
return 0;
// Unverified params: params
// Execute
int _retval = CefBrowserHostCppToC::Get(self)->ExecuteDevToolsMethod(
message_id, CefString(method), CefDictionaryValueCppToC::Unwrap(params));
// Return type: simple
return _retval;
}
struct _cef_registration_t* CEF_CALLBACK
browser_host_add_dev_tools_message_observer(
struct _cef_browser_host_t* self,
struct _cef_dev_tools_message_observer_t* observer) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return NULL;
// Verify param: observer; type: refptr_diff
DCHECK(observer);
if (!observer)
return NULL;
// Execute
CefRefPtr<CefRegistration> _retval =
CefBrowserHostCppToC::Get(self)->AddDevToolsMessageObserver(
CefDevToolsMessageObserverCToCpp::Wrap(observer));
// Return type: refptr_same
return CefRegistrationCppToC::Wrap(_retval);
}
void CEF_CALLBACK void CEF_CALLBACK
browser_host_get_navigation_entries(struct _cef_browser_host_t* self, browser_host_get_navigation_entries(struct _cef_browser_host_t* self,
cef_navigation_entry_visitor_t* visitor, cef_navigation_entry_visitor_t* visitor,
@ -1285,6 +1362,10 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() {
GetStruct()->show_dev_tools = browser_host_show_dev_tools; GetStruct()->show_dev_tools = browser_host_show_dev_tools;
GetStruct()->close_dev_tools = browser_host_close_dev_tools; GetStruct()->close_dev_tools = browser_host_close_dev_tools;
GetStruct()->has_dev_tools = browser_host_has_dev_tools; GetStruct()->has_dev_tools = browser_host_has_dev_tools;
GetStruct()->send_dev_tools_message = browser_host_send_dev_tools_message;
GetStruct()->execute_dev_tools_method = browser_host_execute_dev_tools_method;
GetStruct()->add_dev_tools_message_observer =
browser_host_add_dev_tools_message_observer;
GetStruct()->get_navigation_entries = browser_host_get_navigation_entries; GetStruct()->get_navigation_entries = browser_host_get_navigation_entries;
GetStruct()->set_mouse_cursor_change_disabled = GetStruct()->set_mouse_cursor_change_disabled =
browser_host_set_mouse_cursor_change_disabled; browser_host_set_mouse_cursor_change_disabled;

View File

@ -0,0 +1,184 @@
// Copyright (c) 2020 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.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool. If making changes by
// hand only do so within the body of existing method and function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=c4a7b4d679f1f2ed47fc31df3e2099962d5cb9d6$
//
#include "libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h"
#include "libcef_dll/ctocpp/browser_ctocpp.h"
#include "libcef_dll/shutdown_checker.h"
namespace {
// MEMBER FUNCTIONS - Body may be edited by hand.
int CEF_CALLBACK dev_tools_message_observer_on_dev_tools_message(
struct _cef_dev_tools_message_observer_t* self,
cef_browser_t* browser,
const void* message,
size_t message_size) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return 0;
// Verify param: message; type: simple_byaddr
DCHECK(message);
if (!message)
return 0;
// Execute
bool _retval = CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsMessage(
CefBrowserCToCpp::Wrap(browser), message, message_size);
// Return type: bool
return _retval;
}
void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_method_result(
struct _cef_dev_tools_message_observer_t* self,
cef_browser_t* browser,
int message_id,
int success,
const void* result,
size_t result_size) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Unverified params: result
// Execute
CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsMethodResult(
CefBrowserCToCpp::Wrap(browser), message_id, success ? true : false,
result, result_size);
}
void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_event(
struct _cef_dev_tools_message_observer_t* self,
cef_browser_t* browser,
const cef_string_t* method,
const void* params,
size_t params_size) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Verify param: method; type: string_byref_const
DCHECK(method);
if (!method)
return;
// Unverified params: params
// Execute
CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsEvent(
CefBrowserCToCpp::Wrap(browser), CefString(method), params, params_size);
}
void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_agent_attached(
struct _cef_dev_tools_message_observer_t* self,
cef_browser_t* browser) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Execute
CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsAgentAttached(
CefBrowserCToCpp::Wrap(browser));
}
void CEF_CALLBACK dev_tools_message_observer_on_dev_tools_agent_detached(
struct _cef_dev_tools_message_observer_t* self,
cef_browser_t* browser) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Execute
CefDevToolsMessageObserverCppToC::Get(self)->OnDevToolsAgentDetached(
CefBrowserCToCpp::Wrap(browser));
}
} // namespace
// CONSTRUCTOR - Do not edit by hand.
CefDevToolsMessageObserverCppToC::CefDevToolsMessageObserverCppToC() {
GetStruct()->on_dev_tools_message =
dev_tools_message_observer_on_dev_tools_message;
GetStruct()->on_dev_tools_method_result =
dev_tools_message_observer_on_dev_tools_method_result;
GetStruct()->on_dev_tools_event =
dev_tools_message_observer_on_dev_tools_event;
GetStruct()->on_dev_tools_agent_attached =
dev_tools_message_observer_on_dev_tools_agent_attached;
GetStruct()->on_dev_tools_agent_detached =
dev_tools_message_observer_on_dev_tools_agent_detached;
}
// DESTRUCTOR - Do not edit by hand.
CefDevToolsMessageObserverCppToC::~CefDevToolsMessageObserverCppToC() {
shutdown_checker::AssertNotShutdown();
}
template <>
CefRefPtr<CefDevToolsMessageObserver>
CefCppToCRefCounted<CefDevToolsMessageObserverCppToC,
CefDevToolsMessageObserver,
cef_dev_tools_message_observer_t>::
UnwrapDerived(CefWrapperType type, cef_dev_tools_message_observer_t* s) {
NOTREACHED() << "Unexpected class type: " << type;
return nullptr;
}
template <>
CefWrapperType
CefCppToCRefCounted<CefDevToolsMessageObserverCppToC,
CefDevToolsMessageObserver,
cef_dev_tools_message_observer_t>::kWrapperType =
WT_DEV_TOOLS_MESSAGE_OBSERVER;

View File

@ -0,0 +1,40 @@
// Copyright (c) 2020 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.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool. If making changes by
// hand only do so within the body of existing method and function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=d48f93f3817dab7daf0e30a6fa94b1a173356383$
//
#ifndef CEF_LIBCEF_DLL_CPPTOC_DEV_TOOLS_MESSAGE_OBSERVER_CPPTOC_H_
#define CEF_LIBCEF_DLL_CPPTOC_DEV_TOOLS_MESSAGE_OBSERVER_CPPTOC_H_
#pragma once
#if !defined(WRAPPING_CEF_SHARED)
#error This file can be included wrapper-side only
#endif
#include "include/capi/cef_browser_capi.h"
#include "include/capi/cef_devtools_message_observer_capi.h"
#include "include/cef_browser.h"
#include "include/cef_devtools_message_observer.h"
#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
// Wrap a C++ class with a C structure.
// This class may be instantiated and accessed wrapper-side only.
class CefDevToolsMessageObserverCppToC
: public CefCppToCRefCounted<CefDevToolsMessageObserverCppToC,
CefDevToolsMessageObserver,
cef_dev_tools_message_observer_t> {
public:
CefDevToolsMessageObserverCppToC();
virtual ~CefDevToolsMessageObserverCppToC();
};
#endif // CEF_LIBCEF_DLL_CPPTOC_DEV_TOOLS_MESSAGE_OBSERVER_CPPTOC_H_

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=68840d18a25efe4d749e480225d7ac3caacd4723$ // $hash=716501855da0203723eb18bfb9e518c7b155f110$
// //
#include "libcef_dll/cpptoc/request_handler_cpptoc.h" #include "libcef_dll/cpptoc/request_handler_cpptoc.h"
@ -396,6 +396,26 @@ void CEF_CALLBACK request_handler_on_render_process_terminated(
CefBrowserCToCpp::Wrap(browser), status); CefBrowserCToCpp::Wrap(browser), status);
} }
void CEF_CALLBACK request_handler_on_document_available_in_main_frame(
struct _cef_request_handler_t* self,
cef_browser_t* browser) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Execute
CefRequestHandlerCppToC::Get(self)->OnDocumentAvailableInMainFrame(
CefBrowserCToCpp::Wrap(browser));
}
} // namespace } // namespace
// CONSTRUCTOR - Do not edit by hand. // CONSTRUCTOR - Do not edit by hand.
@ -414,6 +434,8 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC() {
GetStruct()->on_render_view_ready = request_handler_on_render_view_ready; GetStruct()->on_render_view_ready = request_handler_on_render_view_ready;
GetStruct()->on_render_process_terminated = GetStruct()->on_render_process_terminated =
request_handler_on_render_process_terminated; request_handler_on_render_process_terminated;
GetStruct()->on_document_available_in_main_frame =
request_handler_on_document_available_in_main_frame;
} }
// DESTRUCTOR - Do not edit by hand. // DESTRUCTOR - Do not edit by hand.

View File

@ -9,11 +9,12 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=9073823898d373a435ccdc56f2914aad91cbba1e$ // $hash=a362e11e85ce68488bbb0f5232b01f53cffeec1d$
// //
#include "libcef_dll/ctocpp/browser_host_ctocpp.h" #include "libcef_dll/ctocpp/browser_host_ctocpp.h"
#include "libcef_dll/cpptoc/client_cpptoc.h" #include "libcef_dll/cpptoc/client_cpptoc.h"
#include "libcef_dll/cpptoc/dev_tools_message_observer_cpptoc.h"
#include "libcef_dll/cpptoc/download_image_callback_cpptoc.h" #include "libcef_dll/cpptoc/download_image_callback_cpptoc.h"
#include "libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h" #include "libcef_dll/cpptoc/navigation_entry_visitor_cpptoc.h"
#include "libcef_dll/cpptoc/pdf_print_callback_cpptoc.h" #include "libcef_dll/cpptoc/pdf_print_callback_cpptoc.h"
@ -23,6 +24,7 @@
#include "libcef_dll/ctocpp/drag_data_ctocpp.h" #include "libcef_dll/ctocpp/drag_data_ctocpp.h"
#include "libcef_dll/ctocpp/extension_ctocpp.h" #include "libcef_dll/ctocpp/extension_ctocpp.h"
#include "libcef_dll/ctocpp/navigation_entry_ctocpp.h" #include "libcef_dll/ctocpp/navigation_entry_ctocpp.h"
#include "libcef_dll/ctocpp/registration_ctocpp.h"
#include "libcef_dll/ctocpp/request_context_ctocpp.h" #include "libcef_dll/ctocpp/request_context_ctocpp.h"
#include "libcef_dll/shutdown_checker.h" #include "libcef_dll/shutdown_checker.h"
#include "libcef_dll/transfer_util.h" #include "libcef_dll/transfer_util.h"
@ -468,6 +470,81 @@ NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::HasDevTools() {
return _retval ? true : false; return _retval ? true : false;
} }
NO_SANITIZE("cfi-icall")
bool CefBrowserHostCToCpp::SendDevToolsMessage(const void* message,
size_t message_size) {
shutdown_checker::AssertNotShutdown();
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, send_dev_tools_message))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: message; type: simple_byaddr
DCHECK(message);
if (!message)
return false;
// Execute
int _retval = _struct->send_dev_tools_message(_struct, message, message_size);
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall")
int CefBrowserHostCToCpp::ExecuteDevToolsMethod(
int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) {
shutdown_checker::AssertNotShutdown();
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, execute_dev_tools_method))
return 0;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: method; type: string_byref_const
DCHECK(!method.empty());
if (method.empty())
return 0;
// Unverified params: params
// Execute
int _retval = _struct->execute_dev_tools_method(
_struct, message_id, method.GetStruct(),
CefDictionaryValueCToCpp::Unwrap(params));
// Return type: simple
return _retval;
}
NO_SANITIZE("cfi-icall")
CefRefPtr<CefRegistration> CefBrowserHostCToCpp::AddDevToolsMessageObserver(
CefRefPtr<CefDevToolsMessageObserver> observer) {
shutdown_checker::AssertNotShutdown();
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, add_dev_tools_message_observer))
return nullptr;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: observer; type: refptr_diff
DCHECK(observer.get());
if (!observer.get())
return nullptr;
// Execute
cef_registration_t* _retval = _struct->add_dev_tools_message_observer(
_struct, CefDevToolsMessageObserverCppToC::Wrap(observer));
// Return type: refptr_same
return CefRegistrationCToCpp::Wrap(_retval);
}
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
void CefBrowserHostCToCpp::GetNavigationEntries( void CefBrowserHostCToCpp::GetNavigationEntries(
CefRefPtr<CefNavigationEntryVisitor> visitor, CefRefPtr<CefNavigationEntryVisitor> visitor,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=a9c5f9224663a0c2f2a29c35c2dea3f47973b82f$ // $hash=c9abd1293472afbac964aac4cd7dd4cac9dd8e58$
// //
#ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_ #ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_
@ -76,6 +76,12 @@ class CefBrowserHostCToCpp : public CefCToCppRefCounted<CefBrowserHostCToCpp,
const CefPoint& inspect_element_at) OVERRIDE; const CefPoint& inspect_element_at) OVERRIDE;
void CloseDevTools() OVERRIDE; void CloseDevTools() OVERRIDE;
bool HasDevTools() OVERRIDE; bool HasDevTools() OVERRIDE;
bool SendDevToolsMessage(const void* message, size_t message_size) OVERRIDE;
int ExecuteDevToolsMethod(int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) OVERRIDE;
CefRefPtr<CefRegistration> AddDevToolsMessageObserver(
CefRefPtr<CefDevToolsMessageObserver> observer) OVERRIDE;
void GetNavigationEntries(CefRefPtr<CefNavigationEntryVisitor> visitor, void GetNavigationEntries(CefRefPtr<CefNavigationEntryVisitor> visitor,
bool current_only) OVERRIDE; bool current_only) OVERRIDE;
void SetMouseCursorChangeDisabled(bool disabled) OVERRIDE; void SetMouseCursorChangeDisabled(bool disabled) OVERRIDE;

View File

@ -0,0 +1,174 @@
// Copyright (c) 2020 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.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool. If making changes by
// hand only do so within the body of existing method and function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=e8d1eaf4b8cdfd79a260de33fb3be05a2948c81e$
//
#include "libcef_dll/ctocpp/dev_tools_message_observer_ctocpp.h"
#include "libcef_dll/cpptoc/browser_cpptoc.h"
#include "libcef_dll/shutdown_checker.h"
// VIRTUAL METHODS - Body may be edited by hand.
NO_SANITIZE("cfi-icall")
bool CefDevToolsMessageObserverCToCpp::OnDevToolsMessage(
CefRefPtr<CefBrowser> browser,
const void* message,
size_t message_size) {
shutdown_checker::AssertNotShutdown();
cef_dev_tools_message_observer_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_dev_tools_message))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return false;
// Verify param: message; type: simple_byaddr
DCHECK(message);
if (!message)
return false;
// Execute
int _retval = _struct->on_dev_tools_message(
_struct, CefBrowserCppToC::Wrap(browser), message, message_size);
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall")
void CefDevToolsMessageObserverCToCpp::OnDevToolsMethodResult(
CefRefPtr<CefBrowser> browser,
int message_id,
bool success,
const void* result,
size_t result_size) {
shutdown_checker::AssertNotShutdown();
cef_dev_tools_message_observer_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_dev_tools_method_result))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Unverified params: result
// Execute
_struct->on_dev_tools_method_result(_struct, CefBrowserCppToC::Wrap(browser),
message_id, success, result, result_size);
}
NO_SANITIZE("cfi-icall")
void CefDevToolsMessageObserverCToCpp::OnDevToolsEvent(
CefRefPtr<CefBrowser> browser,
const CefString& method,
const void* params,
size_t params_size) {
shutdown_checker::AssertNotShutdown();
cef_dev_tools_message_observer_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_dev_tools_event))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Verify param: method; type: string_byref_const
DCHECK(!method.empty());
if (method.empty())
return;
// Unverified params: params
// Execute
_struct->on_dev_tools_event(_struct, CefBrowserCppToC::Wrap(browser),
method.GetStruct(), params, params_size);
}
NO_SANITIZE("cfi-icall")
void CefDevToolsMessageObserverCToCpp::OnDevToolsAgentAttached(
CefRefPtr<CefBrowser> browser) {
shutdown_checker::AssertNotShutdown();
cef_dev_tools_message_observer_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_dev_tools_agent_attached))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Execute
_struct->on_dev_tools_agent_attached(_struct,
CefBrowserCppToC::Wrap(browser));
}
NO_SANITIZE("cfi-icall")
void CefDevToolsMessageObserverCToCpp::OnDevToolsAgentDetached(
CefRefPtr<CefBrowser> browser) {
shutdown_checker::AssertNotShutdown();
cef_dev_tools_message_observer_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_dev_tools_agent_detached))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Execute
_struct->on_dev_tools_agent_detached(_struct,
CefBrowserCppToC::Wrap(browser));
}
// CONSTRUCTOR - Do not edit by hand.
CefDevToolsMessageObserverCToCpp::CefDevToolsMessageObserverCToCpp() {}
// DESTRUCTOR - Do not edit by hand.
CefDevToolsMessageObserverCToCpp::~CefDevToolsMessageObserverCToCpp() {
shutdown_checker::AssertNotShutdown();
}
template <>
cef_dev_tools_message_observer_t* CefCToCppRefCounted<
CefDevToolsMessageObserverCToCpp,
CefDevToolsMessageObserver,
cef_dev_tools_message_observer_t>::UnwrapDerived(CefWrapperType type,
CefDevToolsMessageObserver*
c) {
NOTREACHED() << "Unexpected class type: " << type;
return nullptr;
}
template <>
CefWrapperType
CefCToCppRefCounted<CefDevToolsMessageObserverCToCpp,
CefDevToolsMessageObserver,
cef_dev_tools_message_observer_t>::kWrapperType =
WT_DEV_TOOLS_MESSAGE_OBSERVER;

View File

@ -0,0 +1,56 @@
// Copyright (c) 2020 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.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool. If making changes by
// hand only do so within the body of existing method and function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=26b1ef9d351b0ce4a7ca0d61c53a8374ba21168b$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_DEV_TOOLS_MESSAGE_OBSERVER_CTOCPP_H_
#define CEF_LIBCEF_DLL_CTOCPP_DEV_TOOLS_MESSAGE_OBSERVER_CTOCPP_H_
#pragma once
#if !defined(BUILDING_CEF_SHARED)
#error This file can be included DLL-side only
#endif
#include "include/capi/cef_browser_capi.h"
#include "include/capi/cef_devtools_message_observer_capi.h"
#include "include/cef_browser.h"
#include "include/cef_devtools_message_observer.h"
#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
// Wrap a C structure with a C++ class.
// This class may be instantiated and accessed DLL-side only.
class CefDevToolsMessageObserverCToCpp
: public CefCToCppRefCounted<CefDevToolsMessageObserverCToCpp,
CefDevToolsMessageObserver,
cef_dev_tools_message_observer_t> {
public:
CefDevToolsMessageObserverCToCpp();
virtual ~CefDevToolsMessageObserverCToCpp();
// CefDevToolsMessageObserver methods.
bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
const void* message,
size_t message_size) override;
void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
int message_id,
bool success,
const void* result,
size_t result_size) override;
void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
const CefString& method,
const void* params,
size_t params_size) override;
void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) override;
void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) override;
};
#endif // CEF_LIBCEF_DLL_CTOCPP_DEV_TOOLS_MESSAGE_OBSERVER_CTOCPP_H_

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=72e6ec9a5095a99eaa78ff867ca429f8cd28b101$ // $hash=fdcb73f79369fe74f65f75d54f08aaec0f0e4dbf$
// //
#include "libcef_dll/ctocpp/request_handler_ctocpp.h" #include "libcef_dll/ctocpp/request_handler_ctocpp.h"
@ -394,6 +394,27 @@ void CefRequestHandlerCToCpp::OnRenderProcessTerminated(
_struct, CefBrowserCppToC::Wrap(browser), status); _struct, CefBrowserCppToC::Wrap(browser), status);
} }
NO_SANITIZE("cfi-icall")
void CefRequestHandlerCToCpp::OnDocumentAvailableInMainFrame(
CefRefPtr<CefBrowser> browser) {
shutdown_checker::AssertNotShutdown();
cef_request_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_document_available_in_main_frame))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Execute
_struct->on_document_available_in_main_frame(_struct,
CefBrowserCppToC::Wrap(browser));
}
// CONSTRUCTOR - Do not edit by hand. // CONSTRUCTOR - Do not edit by hand.
CefRequestHandlerCToCpp::CefRequestHandlerCToCpp() {} CefRequestHandlerCToCpp::CefRequestHandlerCToCpp() {}

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=0d67e24bf6d8079bcdfd8b36ba48543a05a9d2fd$ // $hash=be2bb80e8816f0eaad34382f05e2bec59dfe7fa7$
// //
#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_ #ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_
@ -82,6 +82,7 @@ class CefRequestHandlerCToCpp
void OnRenderViewReady(CefRefPtr<CefBrowser> browser) override; void OnRenderViewReady(CefRefPtr<CefBrowser> browser) override;
void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) override; TerminationStatus status) override;
void OnDocumentAvailableInMainFrame(CefRefPtr<CefBrowser> browser) override;
}; };
#endif // CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_ #endif // CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=fbf992432bd4ff88bed34f803047f70b5b3400f7$ // $hash=c42cd0225d8e4286df471fcc622f9bbf9ed977d3$
// //
#include "include/capi/cef_app_capi.h" #include "include/capi/cef_app_capi.h"
@ -589,6 +589,24 @@ CEF_EXPORT struct _cef_value_t* cef_parse_json(
return CefValueCppToC::Wrap(_retval); return CefValueCppToC::Wrap(_retval);
} }
CEF_EXPORT struct _cef_value_t* cef_parse_json_buffer(
const void* json,
size_t json_size,
cef_json_parser_options_t options) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: json; type: simple_byaddr
DCHECK(json);
if (!json)
return NULL;
// Execute
CefRefPtr<CefValue> _retval = CefParseJSON(json, json_size, options);
// Return type: refptr_same
return CefValueCppToC::Wrap(_retval);
}
CEF_EXPORT struct _cef_value_t* cef_parse_jsonand_return_error( CEF_EXPORT struct _cef_value_t* cef_parse_jsonand_return_error(
const cef_string_t* json_string, const cef_string_t* json_string,
cef_json_parser_options_t options, cef_json_parser_options_t options,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=bf82965f02cafae5a1afc80ab0c976436be9712e$ // $hash=66b734973339eb27399083e978844f7d5a6a6c44$
// //
#include <dlfcn.h> #include <dlfcn.h>
@ -139,6 +139,8 @@ typedef cef_string_userfree_t (*cef_uridecode_ptr)(const cef_string_t*,
cef_uri_unescape_rule_t); cef_uri_unescape_rule_t);
typedef struct _cef_value_t* (*cef_parse_json_ptr)(const cef_string_t*, typedef struct _cef_value_t* (*cef_parse_json_ptr)(const cef_string_t*,
cef_json_parser_options_t); cef_json_parser_options_t);
typedef struct _cef_value_t* (
*cef_parse_json_buffer_ptr)(const void*, size_t, cef_json_parser_options_t);
typedef struct _cef_value_t* (*cef_parse_jsonand_return_error_ptr)( typedef struct _cef_value_t* (*cef_parse_jsonand_return_error_ptr)(
const cef_string_t*, const cef_string_t*,
cef_json_parser_options_t, cef_json_parser_options_t,
@ -547,6 +549,7 @@ struct libcef_pointers {
cef_uriencode_ptr cef_uriencode; cef_uriencode_ptr cef_uriencode;
cef_uridecode_ptr cef_uridecode; cef_uridecode_ptr cef_uridecode;
cef_parse_json_ptr cef_parse_json; cef_parse_json_ptr cef_parse_json;
cef_parse_json_buffer_ptr cef_parse_json_buffer;
cef_parse_jsonand_return_error_ptr cef_parse_jsonand_return_error; cef_parse_jsonand_return_error_ptr cef_parse_jsonand_return_error;
cef_write_json_ptr cef_write_json; cef_write_json_ptr cef_write_json;
cef_get_path_ptr cef_get_path; cef_get_path_ptr cef_get_path;
@ -763,6 +766,7 @@ int libcef_init_pointers(const char* path) {
INIT_ENTRY(cef_uriencode); INIT_ENTRY(cef_uriencode);
INIT_ENTRY(cef_uridecode); INIT_ENTRY(cef_uridecode);
INIT_ENTRY(cef_parse_json); INIT_ENTRY(cef_parse_json);
INIT_ENTRY(cef_parse_json_buffer);
INIT_ENTRY(cef_parse_jsonand_return_error); INIT_ENTRY(cef_parse_jsonand_return_error);
INIT_ENTRY(cef_write_json); INIT_ENTRY(cef_write_json);
INIT_ENTRY(cef_get_path); INIT_ENTRY(cef_get_path);
@ -1134,6 +1138,13 @@ struct _cef_value_t* cef_parse_json(const cef_string_t* json_string,
return g_libcef_pointers.cef_parse_json(json_string, options); return g_libcef_pointers.cef_parse_json(json_string, options);
} }
NO_SANITIZE("cfi-icall")
struct _cef_value_t* cef_parse_json_buffer(const void* json,
size_t json_size,
cef_json_parser_options_t options) {
return g_libcef_pointers.cef_parse_json_buffer(json, json_size, options);
}
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
struct _cef_value_t* cef_parse_jsonand_return_error( struct _cef_value_t* cef_parse_jsonand_return_error(
const cef_string_t* json_string, const cef_string_t* json_string,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=c565ea9030eeed15f24fe420b828453a23dbc6ba$ // $hash=7db8dbe24a2510d9ae0649f1569909711017c064$
// //
#include "include/capi/cef_app_capi.h" #include "include/capi/cef_app_capi.h"
@ -557,6 +557,24 @@ CEF_GLOBAL CefRefPtr<CefValue> CefParseJSON(const CefString& json_string,
return CefValueCToCpp::Wrap(_retval); return CefValueCToCpp::Wrap(_retval);
} }
NO_SANITIZE("cfi-icall")
CEF_GLOBAL CefRefPtr<CefValue> CefParseJSON(const void* json,
size_t json_size,
cef_json_parser_options_t options) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: json; type: simple_byaddr
DCHECK(json);
if (!json)
return nullptr;
// Execute
cef_value_t* _retval = cef_parse_json_buffer(json, json_size, options);
// Return type: refptr_same
return CefValueCToCpp::Wrap(_retval);
}
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
CEF_GLOBAL CefRefPtr<CefValue> CefParseJSONAndReturnError( CEF_GLOBAL CefRefPtr<CefValue> CefParseJSONAndReturnError(
const CefString& json_string, const CefString& json_string,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=9bfe176dfac4770800e95e2bbc0fafffbf0aeeaf$ // $hash=20268db646dcf8aa6d54b2342dbc70a6e624e202$
// //
#ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_ #ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_
@ -46,6 +46,7 @@ enum CefWrapperType {
WT_DOMNODE, WT_DOMNODE,
WT_DOMVISITOR, WT_DOMVISITOR,
WT_DELETE_COOKIES_CALLBACK, WT_DELETE_COOKIES_CALLBACK,
WT_DEV_TOOLS_MESSAGE_OBSERVER,
WT_DIALOG_HANDLER, WT_DIALOG_HANDLER,
WT_DICTIONARY_VALUE, WT_DICTIONARY_VALUE,
WT_DISPLAY, WT_DISPLAY,

View File

@ -41,6 +41,7 @@ enum client_menu_ids {
CLIENT_ID_INSPECT_ELEMENT, CLIENT_ID_INSPECT_ELEMENT,
CLIENT_ID_SHOW_SSL_INFO, CLIENT_ID_SHOW_SSL_INFO,
CLIENT_ID_CURSOR_CHANGE_DISABLED, CLIENT_ID_CURSOR_CHANGE_DISABLED,
CLIENT_ID_OFFLINE,
CLIENT_ID_TESTMENU_SUBMENU, CLIENT_ID_TESTMENU_SUBMENU,
CLIENT_ID_TESTMENU_CHECKITEM, CLIENT_ID_TESTMENU_CHECKITEM,
CLIENT_ID_TESTMENU_RADIOITEM1, CLIENT_ID_TESTMENU_RADIOITEM1,
@ -268,6 +269,7 @@ ClientHandler::ClientHandler(Delegate* delegate,
CefCommandLine::GetGlobalCommandLine(); CefCommandLine::GetGlobalCommandLine();
mouse_cursor_change_disabled_ = mouse_cursor_change_disabled_ =
command_line->HasSwitch(switches::kMouseCursorChangeDisabled); command_line->HasSwitch(switches::kMouseCursorChangeDisabled);
offline_ = command_line->HasSwitch(switches::kOffline);
} }
void ClientHandler::DetachDelegate() { void ClientHandler::DetachDelegate() {
@ -334,6 +336,11 @@ void ClientHandler::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
if (browser->GetHost()->IsMouseCursorChangeDisabled()) if (browser->GetHost()->IsMouseCursorChangeDisabled())
model->SetChecked(CLIENT_ID_CURSOR_CHANGE_DISABLED, true); model->SetChecked(CLIENT_ID_CURSOR_CHANGE_DISABLED, true);
model->AddSeparator();
model->AddItem(CLIENT_ID_OFFLINE, "Offline mode");
if (offline_)
model->SetChecked(CLIENT_ID_OFFLINE, true);
// Test context menu features. // Test context menu features.
BuildTestMenu(model); BuildTestMenu(model);
} }
@ -366,6 +373,10 @@ bool ClientHandler::OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
browser->GetHost()->SetMouseCursorChangeDisabled( browser->GetHost()->SetMouseCursorChangeDisabled(
!browser->GetHost()->IsMouseCursorChangeDisabled()); !browser->GetHost()->IsMouseCursorChangeDisabled());
return true; return true;
case CLIENT_ID_OFFLINE:
offline_ = !offline_;
SetOfflineState(browser, offline_);
return true;
default: // Allow default handling, if any. default: // Allow default handling, if any.
return ExecuteTestMenu(command_id); return ExecuteTestMenu(command_id);
} }
@ -588,6 +599,10 @@ void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
if (mouse_cursor_change_disabled_) if (mouse_cursor_change_disabled_)
browser->GetHost()->SetMouseCursorChangeDisabled(true); browser->GetHost()->SetMouseCursorChangeDisabled(true);
// Set offline mode if requested via the command-line flag.
if (offline_)
SetOfflineState(browser, true);
if (browser->GetHost()->GetExtension()) { if (browser->GetHost()->GetExtension()) {
// Browsers hosting extension apps should auto-resize. // Browsers hosting extension apps should auto-resize.
browser->GetHost()->SetAutoResizeEnabled(true, CefSize(20, 20), browser->GetHost()->SetAutoResizeEnabled(true, CefSize(20, 20),
@ -843,6 +858,16 @@ void ClientHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
frame->LoadURL(startup_url_); frame->LoadURL(startup_url_);
} }
void ClientHandler::OnDocumentAvailableInMainFrame(
CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Restore offline mode after main frame navigation. Otherwise, offline state
// (e.g. `navigator.onLine`) might be wrong in the renderer process.
if (offline_)
SetOfflineState(browser, true);
}
cef_return_value_t ClientHandler::OnBeforeResourceLoad( cef_return_value_t ClientHandler::OnBeforeResourceLoad(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefFrame> frame,
@ -1178,4 +1203,16 @@ bool ClientHandler::ExecuteTestMenu(int command_id) {
return false; return false;
} }
void ClientHandler::SetOfflineState(CefRefPtr<CefBrowser> browser,
bool offline) {
// See DevTools protocol docs for message format specification.
CefRefPtr<CefDictionaryValue> params = CefDictionaryValue::Create();
params->SetBool("offline", offline);
params->SetDouble("latency", 0);
params->SetDouble("downloadThroughput", 0);
params->SetDouble("uploadThroughput", 0);
browser->GetHost()->ExecuteDevToolsMethod(
/*message_id=*/0, "Network.emulateNetworkConditions", params);
}
} // namespace client } // namespace client

View File

@ -256,6 +256,7 @@ class ClientHandler : public CefClient,
CefRefPtr<CefSelectClientCertificateCallback> callback) OVERRIDE; CefRefPtr<CefSelectClientCertificateCallback> callback) OVERRIDE;
void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) OVERRIDE; TerminationStatus status) OVERRIDE;
void OnDocumentAvailableInMainFrame(CefRefPtr<CefBrowser> browser) OVERRIDE;
// CefResourceRequestHandler methods // CefResourceRequestHandler methods
cef_return_value_t OnBeforeResourceLoad( cef_return_value_t OnBeforeResourceLoad(
@ -344,6 +345,8 @@ class ClientHandler : public CefClient,
void BuildTestMenu(CefRefPtr<CefMenuModel> model); void BuildTestMenu(CefRefPtr<CefMenuModel> model);
bool ExecuteTestMenu(int command_id); bool ExecuteTestMenu(int command_id);
void SetOfflineState(CefRefPtr<CefBrowser> browser, bool offline);
// THREAD SAFE MEMBERS // THREAD SAFE MEMBERS
// The following members may be accessed from any thread. // The following members may be accessed from any thread.
@ -356,6 +359,9 @@ class ClientHandler : public CefClient,
// True if mouse cursor change is disabled. // True if mouse cursor change is disabled.
bool mouse_cursor_change_disabled_; bool mouse_cursor_change_disabled_;
// True if the browser is currently offline.
bool offline_;
// True if Favicon images should be downloaded. // True if Favicon images should be downloaded.
bool download_favicon_images_; bool download_favicon_images_;

View File

@ -0,0 +1,375 @@
// Copyright (c) 2020 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 <algorithm>
#include <sstream>
#include "include/base/cef_bind.h"
#include "include/cef_callback.h"
#include "include/cef_devtools_message_observer.h"
#include "include/cef_parser.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
namespace {
const char kTestUrl1[] = "http://tests/DevToolsMessage1";
const char kTestUrl2[] = "http://tests/DevToolsMessage2";
class DevToolsMessageTestHandler : public TestHandler {
public:
DevToolsMessageTestHandler() {}
void RunTest() override {
// Add HTML resources.
AddResource(kTestUrl1, "<html><body>Test1</body></html>", "text/html");
AddResource(kTestUrl2, "<html><body>Test2</body></html>", "text/html");
// Create the browser.
CreateBrowser(kTestUrl1);
// Time out the test after a reasonable period of time.
SetTestTimeout();
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
TestHandler::OnAfterCreated(browser);
// STEP 1: Add the DevTools observer. Wait for the 1st load.
registration_ = browser->GetHost()->AddDevToolsMessageObserver(
new TestMessageObserver(this));
EXPECT_TRUE(registration_);
}
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override {
if (!isLoading) {
load_ct_++;
if (load_ct_ == 1) {
// STEP 2: 1st load has completed. Now enable page domain notifications
// and wait for the method result.
ExecuteMethod("Page.enable", "",
base::Bind(&DevToolsMessageTestHandler::Navigate, this));
} else if (load_ct_ == 2) {
MaybeDestroyTest();
}
}
}
void DestroyTest() override {
// STEP 7: Remove the DevTools observer. This should result in the observer
// object being destroyed.
EXPECT_TRUE(registration_);
registration_ = nullptr;
EXPECT_TRUE(observer_destroyed_);
// Each send message variant should be called at least a single time.
EXPECT_GE(method_send_ct_, 1);
EXPECT_GE(method_execute_ct_, 1);
// All sent messages should receive a result callback.
EXPECT_EQ(expected_method_ct_, method_send_ct_ + method_execute_ct_);
EXPECT_EQ(expected_method_ct_, result_ct_);
EXPECT_EQ(expected_method_ct_, last_result_id_);
// Every received message should parse successfully to a result or event
// callback.
EXPECT_EQ(message_ct_, result_ct_ + event_ct_);
// Should receive 1 or more events (probably just 1, but who knows?).
EXPECT_GE(event_ct_, 1);
// OnLoadingStateChange(isLoading=false) should be called twice.
EXPECT_EQ(expected_load_ct_, load_ct_);
// Should get callbacks for agent attached but not detached.
EXPECT_EQ(1, attached_ct_);
EXPECT_EQ(0, detached_ct_);
TestHandler::DestroyTest();
}
private:
struct MethodResult {
int message_id;
bool success;
std::string result;
};
struct Event {
std::string method;
std::string params;
};
class TestMessageObserver : public CefDevToolsMessageObserver {
public:
explicit TestMessageObserver(DevToolsMessageTestHandler* handler)
: handler_(handler) {}
virtual ~TestMessageObserver() { handler_->observer_destroyed_.yes(); }
bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
const void* message,
size_t message_size) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->message_ct_++;
return false;
}
void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
int message_id,
bool success,
const void* result,
size_t result_size) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->result_ct_++;
MethodResult mr;
mr.message_id = message_id;
mr.success = success;
if (result) {
// Intentionally truncating at small size.
mr.result = std::string(static_cast<const char*>(result),
std::min(static_cast<int>(result_size), 80));
}
handler_->OnMethodResult(mr);
}
void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
const CefString& method,
const void* params,
size_t params_size) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->event_ct_++;
Event ev;
ev.method = method;
if (params) {
// Intentionally truncating at small size.
ev.params = std::string(static_cast<const char*>(params),
std::min(static_cast<int>(params_size), 80));
}
handler_->OnEvent(ev);
}
void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->attached_ct_++;
}
void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->detached_ct_++;
}
private:
DevToolsMessageTestHandler* handler_;
IMPLEMENT_REFCOUNTING(TestMessageObserver);
DISALLOW_COPY_AND_ASSIGN(TestMessageObserver);
};
// Execute a DevTools method. Expected results will be verified in
// OnMethodResult, and |next_step| will then be executed.
// |expected_result| can be a fragment that the result should start with.
void ExecuteMethod(const std::string& method,
const std::string& params,
const base::Closure& next_step,
const std::string& expected_result = "{}",
bool expected_success = true) {
CHECK(!method.empty());
CHECK(!next_step.is_null());
int message_id = next_message_id_++;
std::stringstream message;
message << "{\"id\":" << message_id << ",\"method\":\"" << method << "\"";
if (!params.empty()) {
message << ",\"params\":" << params;
}
message << "}";
// Set expected result state.
pending_message_ = message.str();
pending_result_next_ = next_step;
pending_result_ = {message_id, expected_success, expected_result};
if (message_id % 2 == 0) {
// Use the less structured method.
method_send_ct_++;
GetBrowser()->GetHost()->SendDevToolsMessage(pending_message_.data(),
pending_message_.size());
} else {
// Use the more structured method.
method_execute_ct_++;
CefRefPtr<CefDictionaryValue> dict;
if (!params.empty()) {
CefRefPtr<CefValue> value =
CefParseJSON(params.data(), params.size(), JSON_PARSER_RFC);
EXPECT_TRUE(value && value->GetType() == VTYPE_DICTIONARY)
<< "failed to parse: " << params;
if (value && value->GetType() == VTYPE_DICTIONARY) {
dict = value->GetDictionary();
}
}
GetBrowser()->GetHost()->ExecuteDevToolsMethod(message_id, method, dict);
}
}
// Every call to ExecuteMethod should result in a single call to this method
// with the same message_id.
void OnMethodResult(const MethodResult& result) {
EXPECT_EQ(pending_result_.message_id, result.message_id)
<< "with message=" << pending_message_;
if (result.message_id != pending_result_.message_id)
return;
EXPECT_EQ(pending_result_.success, result.success)
<< "with message=" << pending_message_;
EXPECT_TRUE(result.result.find(pending_result_.result) == 0)
<< "with message=" << pending_message_
<< "\nand actual result=" << result.result
<< "\nand expected result=" << pending_result_.result;
last_result_id_ = result.message_id;
// Continue asynchronously to allow the callstack to unwind.
CefPostTask(TID_UI, pending_result_next_);
// Clear expected result state.
pending_message_.clear();
pending_result_next_.Reset();
pending_result_ = {};
}
void OnEvent(const Event& event) {
if (event.method != pending_event_.method)
return;
EXPECT_TRUE(event.params.find(pending_event_.params) == 0)
<< "with method=" << event.method
<< "\nand actual params=" << event.params
<< "\nand expected params=" << pending_event_.params;
// Continue asynchronously to allow the callstack to unwind.
CefPostTask(TID_UI, pending_event_next_);
// Clear expected result state.
pending_event_ = {};
pending_event_next_.Reset();
}
void Navigate() {
pending_event_ = {"Page.frameNavigated", "{\"frame\":"};
pending_event_next_ =
base::Bind(&DevToolsMessageTestHandler::AfterNavigate, this);
std::stringstream params;
params << "{\"url\":\"" << kTestUrl2 << "\"}";
// STEP 3: Page domain notifications are enabled. Now start a new
// navigation (but do nothing on method result) and wait for the
// "Page.frameNavigated" event.
ExecuteMethod("Page.navigate", params.str(), base::Bind(base::DoNothing),
/*expected_result=*/"{\"frameId\":");
}
void AfterNavigate() {
// STEP 4: Got the "Page.frameNavigated" event. Now disable page domain
// notifications.
ExecuteMethod(
"Page.disable", "",
base::Bind(&DevToolsMessageTestHandler::AfterPageDisabled, this));
}
void AfterPageDisabled() {
// STEP 5: Got the the "Page.disable" method result. Now call a non-existant
// method to verify an error result, and then destroy the test when done.
ExecuteMethod(
"Foo.doesNotExist", "",
base::Bind(&DevToolsMessageTestHandler::MaybeDestroyTest, this),
/*expected_result=*/
"{\"code\":-32601,\"message\":\"'Foo.doesNotExist' wasn't found\"}",
/*expected_success=*/false);
}
void MaybeDestroyTest() {
if (result_ct_ == expected_method_ct_ && load_ct_ == expected_load_ct_) {
// STEP 6: Got confirmation of all expected method results and load
// events. Now destroy the test.
DestroyTest();
}
}
// Total # of times we're planning to call ExecuteMethod.
const int expected_method_ct_ = 4;
// Total # of times we're expecting OnLoadingStateChange(isLoading=false) to
// be called.
const int expected_load_ct_ = 2;
// In ExecuteMethod:
// Next message ID to use.
int next_message_id_ = 1;
// Last message that was sent (used for debug messages only).
std::string pending_message_;
// SendDevToolsMessage call count.
int method_send_ct_ = 0;
// ExecuteDevToolsMethod call count.
int method_execute_ct_ = 0;
// Expect |pending_result_.message_id| in OnMethodResult.
// The result should start with the |pending_result_.result| fragment.
MethodResult pending_result_;
// Tracks the last message ID received.
int last_result_id_ = -1;
// When received, execute this callback.
base::Closure pending_result_next_;
// Wait for |pending_event_.method| in OnEvent.
// The params should start with the |pending_event_.params| fragment.
Event pending_event_;
// When received, execute this callback.
base::Closure pending_event_next_;
CefRefPtr<CefRegistration> registration_;
// Observer callback count.
int message_ct_ = 0;
int result_ct_ = 0;
int event_ct_ = 0;
int attached_ct_ = 0;
int detached_ct_ = 0;
// OnLoadingStateChange(isLoading=false) count.
int load_ct_ = 0;
TrackCallback observer_destroyed_;
IMPLEMENT_REFCOUNTING(DevToolsMessageTestHandler);
DISALLOW_COPY_AND_ASSIGN(DevToolsMessageTestHandler);
};
} // namespace
// Test everything related to DevTools messages:
// - CefDevToolsMessageObserver registration and life span.
// - SendDevToolsMessage/ExecuteDevToolsMethod calls.
// - CefDevToolsMessageObserver callbacks for method results and events.
TEST(DevToolsMessageTest, Messages) {
CefRefPtr<DevToolsMessageTestHandler> handler =
new DevToolsMessageTestHandler();
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}

View File

@ -28,6 +28,7 @@ const char kShowUpdateRect[] = "show-update-rect";
const char kSharedTextureEnabled[] = "shared-texture-enabled"; const char kSharedTextureEnabled[] = "shared-texture-enabled";
const char kExternalBeginFrameEnabled[] = "external-begin-frame-enabled"; const char kExternalBeginFrameEnabled[] = "external-begin-frame-enabled";
const char kMouseCursorChangeDisabled[] = "mouse-cursor-change-disabled"; const char kMouseCursorChangeDisabled[] = "mouse-cursor-change-disabled";
const char kOffline[] = "offline";
const char kRequestContextPerBrowser[] = "request-context-per-browser"; const char kRequestContextPerBrowser[] = "request-context-per-browser";
const char kRequestContextSharedCache[] = "request-context-shared-cache"; const char kRequestContextSharedCache[] = "request-context-shared-cache";
const char kBackgroundColor[] = "background-color"; const char kBackgroundColor[] = "background-color";

View File

@ -22,6 +22,7 @@ extern const char kShowUpdateRect[];
extern const char kSharedTextureEnabled[]; extern const char kSharedTextureEnabled[];
extern const char kExternalBeginFrameEnabled[]; extern const char kExternalBeginFrameEnabled[];
extern const char kMouseCursorChangeDisabled[]; extern const char kMouseCursorChangeDisabled[];
extern const char kOffline[];
extern const char kRequestContextPerBrowser[]; extern const char kRequestContextPerBrowser[];
extern const char kRequestContextSharedCache[]; extern const char kRequestContextSharedCache[];
extern const char kBackgroundColor[]; extern const char kBackgroundColor[];

View File

@ -266,10 +266,6 @@ def GetRequiredArgs():
# can't be enforced by assert(). # can't be enforced by assert().
result['enable_linux_installer'] = False result['enable_linux_installer'] = False
# Use system fontconfig. This avoids a startup hang on Ubuntu 16.04.4 (see
# issue #2424).
result['use_bundled_fontconfig'] = False
# Build without GTK dependencies (see issue #2014). # Build without GTK dependencies (see issue #2014).
result['use_gtk'] = False result['use_gtk'] = False