mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	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:
		
							
								
								
									
										131
									
								
								libcef/browser/devtools/devtools_util.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								libcef/browser/devtools/devtools_util.cc
									
									
									
									
									
										Normal 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 | ||||
		Reference in New Issue
	
	Block a user