summaryrefslogtreecommitdiff
path: root/chromium/third_party/inspector_protocol
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/third_party/inspector_protocol
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/third_party/inspector_protocol')
-rw-r--r--chromium/third_party/inspector_protocol/BUILD.gn26
-rw-r--r--chromium/third_party/inspector_protocol/README.chromium2
-rwxr-xr-xchromium/third_party/inspector_protocol/code_generator.py7
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/README.md262
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/cbor.cc195
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/cbor.h26
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/cbor_test.cc273
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/dispatch.cc576
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/dispatch.h311
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/dispatch_test.cc445
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/error_support.cc59
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/error_support.h62
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/error_support_test.cc45
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/find_by_first.h58
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/find_by_first_test.cc76
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/frontend_channel.h47
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/json.cc36
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/json.h5
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/json_test.cc227
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/serializable.cc21
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/serializable.h12
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/serializable_test.cc5
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/serializer_traits.h158
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/serializer_traits_test.cc227
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/span.cc24
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/span.h65
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/span_test.cc11
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/status.cc106
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/status.h60
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/status_test.cc8
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/status_test_support.cc50
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/status_test_support.h32
-rw-r--r--chromium/third_party/inspector_protocol/crdtp/transcode.cc39
-rw-r--r--chromium/third_party/inspector_protocol/inspector_protocol.gni11
-rw-r--r--chromium/third_party/inspector_protocol/lib/DispatcherBase_cpp.template355
-rw-r--r--chromium/third_party/inspector_protocol/lib/DispatcherBase_h.template176
-rw-r--r--chromium/third_party/inspector_protocol/lib/ErrorSupport_cpp.template73
-rw-r--r--chromium/third_party/inspector_protocol/lib/ErrorSupport_h.template39
-rw-r--r--chromium/third_party/inspector_protocol/lib/Forward_h.template12
-rw-r--r--chromium/third_party/inspector_protocol/lib/FrontendChannel_h.template31
-rw-r--r--chromium/third_party/inspector_protocol/lib/Object_cpp.template7
-rw-r--r--chromium/third_party/inspector_protocol/lib/Object_h.template7
-rw-r--r--chromium/third_party/inspector_protocol/lib/Parser_cpp.template548
-rw-r--r--chromium/third_party/inspector_protocol/lib/Parser_h.template24
-rw-r--r--chromium/third_party/inspector_protocol/lib/ValueConversions_h.template38
-rw-r--r--chromium/third_party/inspector_protocol/lib/Values_cpp.template457
-rw-r--r--chromium/third_party/inspector_protocol/lib/Values_h.template39
-rw-r--r--chromium/third_party/inspector_protocol/lib/base_string_adapter_cc.template72
-rw-r--r--chromium/third_party/inspector_protocol/lib/base_string_adapter_h.template73
-rwxr-xr-xchromium/third_party/inspector_protocol/roll.py16
-rw-r--r--chromium/third_party/inspector_protocol/templates/Exported_h.template5
-rw-r--r--chromium/third_party/inspector_protocol/templates/Imported_h.template17
-rw-r--r--chromium/third_party/inspector_protocol/templates/TypeBuilder_cpp.template245
-rw-r--r--chromium/third_party/inspector_protocol/templates/TypeBuilder_h.template21
54 files changed, 3251 insertions, 2571 deletions
diff --git a/chromium/third_party/inspector_protocol/BUILD.gn b/chromium/third_party/inspector_protocol/BUILD.gn
index 645c422cd1a..4546b13afa1 100644
--- a/chromium/third_party/inspector_protocol/BUILD.gn
+++ b/chromium/third_party/inspector_protocol/BUILD.gn
@@ -8,13 +8,21 @@ jumbo_component("crdtp") {
sources = [
"crdtp/cbor.cc",
"crdtp/cbor.h",
+ "crdtp/dispatch.cc",
+ "crdtp/dispatch.h",
+ "crdtp/error_support.cc",
+ "crdtp/error_support.h",
"crdtp/export.h",
+ "crdtp/find_by_first.h",
+ "crdtp/frontend_channel.h",
"crdtp/glue.h",
"crdtp/json.cc",
"crdtp/json.h",
"crdtp/parser_handler.h",
"crdtp/serializable.cc",
"crdtp/serializable.h",
+ "crdtp/serializer_traits.h",
+ "crdtp/span.cc",
"crdtp/span.h",
"crdtp/status.cc",
"crdtp/status.h",
@@ -26,9 +34,7 @@ jumbo_component("crdtp") {
configs -= [ "//build/config/compiler:default_optimization" ]
configs += [ "//build/config/compiler:optimize_max" ]
}
- deps = [
- ":crdtp_platform",
- ]
+ deps = [ ":crdtp_platform" ]
}
# A small adapter library which only :crdtp may depend on.
@@ -38,9 +44,7 @@ source_set("crdtp_platform") {
"crdtp/chromium/json_platform_chromium.cc",
"crdtp/json_platform.h",
]
- deps = [
- "//base",
- ]
+ deps = [ "//base" ]
}
# These tests are linked into //content/test:content_unittests.
@@ -48,15 +52,19 @@ source_set("crdtp_test") {
testonly = true
sources = [
"crdtp/cbor_test.cc",
+ "crdtp/dispatch_test.cc",
+ "crdtp/error_support_test.cc",
+ "crdtp/find_by_first_test.cc",
"crdtp/glue_test.cc",
"crdtp/json_test.cc",
"crdtp/serializable_test.cc",
+ "crdtp/serializer_traits_test.cc",
"crdtp/span_test.cc",
"crdtp/status_test.cc",
+ "crdtp/status_test_support.cc",
+ "crdtp/status_test_support.h",
]
- deps = [
- ":crdtp_test_platform",
- ]
+ deps = [ ":crdtp_test_platform" ]
}
# A small adapter library which only :crdtp_test may depend on.
diff --git a/chromium/third_party/inspector_protocol/README.chromium b/chromium/third_party/inspector_protocol/README.chromium
index 9307d131e3e..4cfb4fa717c 100644
--- a/chromium/third_party/inspector_protocol/README.chromium
+++ b/chromium/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0
-Revision: 6e549cc623afedea19c7fb7c9511aa2e027fd191
+Revision: b7cda08cd6e522df2159413ba5f29d2a953cc1c4
License: BSD
License File: LICENSE
Security Critical: yes
diff --git a/chromium/third_party/inspector_protocol/code_generator.py b/chromium/third_party/inspector_protocol/code_generator.py
index 4260dbe5a41..92207b91593 100755
--- a/chromium/third_party/inspector_protocol/code_generator.py
+++ b/chromium/third_party/inspector_protocol/code_generator.py
@@ -655,26 +655,19 @@ def main():
# Note these should be sorted in the right order.
# TODO(dgozman): sort them programmatically based on commented includes.
protocol_h_templates = [
- "ErrorSupport_h.template",
"Values_h.template",
"Object_h.template",
"ValueConversions_h.template",
- "DispatcherBase_h.template",
- "Parser_h.template",
]
protocol_cpp_templates = [
"Protocol_cpp.template",
- "ErrorSupport_cpp.template",
"Values_cpp.template",
"Object_cpp.template",
- "DispatcherBase_cpp.template",
- "Parser_cpp.template",
]
forward_h_templates = [
"Forward_h.template",
- "FrontendChannel_h.template",
]
base_string_adapter_h_templates = [
diff --git a/chromium/third_party/inspector_protocol/crdtp/README.md b/chromium/third_party/inspector_protocol/crdtp/README.md
index 5e6d03357be..81957e1692e 100644
--- a/chromium/third_party/inspector_protocol/crdtp/README.md
+++ b/chromium/third_party/inspector_protocol/crdtp/README.md
@@ -1,148 +1,114 @@
-# Inspector Protocol Encoding Library
-
-This library implements the encoding layer for the inspector protocol.
-The following facilities are provided:
-
-- Some tools for portability, including a simple span implementation.
- The library itself does not depend on anything except the C++ standard
- libraries. For the JSON features, dependencies are injected via a
- json::Platform object. See the json namespace in encoding.h
-
-- CBOR-based encoding/decoding functionality for a binary format. See the
- comment at the top of the cbor namespace in encoding.h for details.
-
-- JSON encoder/decoder. See the json namespace in encoding.h for details.
-
-- CBOR and JSON parsers use a streaming API. See StreamingParserHandler
- in encoding.h.
-
-- Two-way conversions between CBOR-based binary and JSON. See the
- json::ConvertCBORToJSON / json::ConvertJSONToCBOR functions in encoding.h
-
-The library is designed for portability and users of the inspector protocol
-should link it into their programs. We do so in Chromium, V8, and
-internally at Google.
-
-TODO(johannes): Update the design documentation below.
-
-## Objective
-
-We're hoping for:
-
-- Support a binary protocol with a reasonably efficient wire format.
- Efficient means that:
- - We can send .png images and similar bytes over this wire.
- JSON currently requires us to base64 encode such pieces.
- - We can extract message ids etc. from the wire format without
- fully parsing the entire message. With JSON This is not very
- practical because strings etc. are not preceded with their length,
- so one is forced to read through them.
- - Perhaps also, we may want to append session ids and the like to a message.
-- Reduce some runtime overhead:
- - Reduce the overhead of conversions between utf8 / utf16 / etc.
- E.g., for keys in dictionaries, we may want to assume they're utf8
- and do this encoding once so that we can efficiently look them up.
- - Reduce the in-memory overhead, that is, don't require round-tripping
- every message via base::Value and similar structures.
-- Reduce / maintain code size: The current template approach duplicates some
- compiled code in multiple namespaces; linking against this as a third party
- library should allow us to reduce the code size a bit. E.g., we'll just have
- one copy of the json parser (not a great example because the json parser isn't
- quite used multiple times, I think the linker may eliminate the multiple
- copies already). But overall, compiled code size should come down a bit for
- existing functionality, and we'll try our best to keep the new functionality
- (binary protocol) from adding more code bloat.
-- Reduce complexity / make code easier to test / maintain / etc.: It should
- become possible to test the inspector protocol layer prior to rolling it into
- the various consuming projects. Having a library / code that's unittested,
- should help toward this direction. We may also add some integration test,
- e.g. some .pdl file with example protocol to drive the code generator
- all the way to round / tripping / handling some messages.
- The distinction between code that's part of the protocol layer and the other
- parts of the inspector / devtools should become somewhat easier to see.
-
-## Approach
-
-To begin, we wish to make a library for the core part of digesting
-inspector_protocol messages - the encoding library, in this directory. That is,
-encoding / decoding as well as extracting some values that are needed for
-dispatch (method, message id, session id).
-
-Our first input shall be JSON, the existing wire format. It's convenient for
-testing, but also we'll later be able to use it to convert the existing wire
-format as early as possible into binary, e.g. in the devtools pipe for headless,
-and therefore we won't need to maintain much separate code between json and
-binary inputs.
-
-With the approach described here, we will not parse the JSON into dictionaries
-or base::Value / protocol::Value nested structures. Instead, we'll convert it
-into a binary format, probably CBOR.
-
-For this, json_parser{.h,.cc} implements a JSON parser with a SAX style
-handler interface, which will drive conversion into a binary buffer via a
-specific handler. We'll call the abstraction for the binary format
-MessageBuffer from here (most likely we'll have a C++ class with that name).
-
-For parts of the Chromium code that doesn't need to inspect deep into the
-inspector_protocol messages, we'll be able to extract individual values (message
-id, session id, method name) from this MessageBuffer. This is because
-MessageBuffer owns the wire format bytes (e.g. std::vector<uint_t> in a private
-field) and can return values as string pieces or int64_t as appropriate.
-
-This library does not involve code generation - it's just checked in C++. The
-library will be unittested, and because we have a JSON parser, it's an excellent
-way to drive examples into the CBOR buffer and check that they round trip OK. It
-may be practical to develop a JSON serializer in the process as well, at first to
-make testing simpler, and then later, to use it for compatibility in production.
-
-## Dependencies
-
-There is strtod (for json), there is character encoding, and there is
-string / vector / etc. libraries. We'll try handle them to maximize
-portability while making it easy to test the library.
-
-For strtod, clients of the library will need to provide their
-implementation. We'll use virtual method dispatch (abstract class +
-implementation), they'll have to pass in an object, e.g. a singleton.
-
-For UTF8 / UTF16 Unicode encoding, we'll try to avoid adding dependencies and
-see how far we get with the approach, much like jsoncpp does it, basically very
-minimal / light and no dependency on ICU. We'll depend on mini_chromium for
-testing, so that gives us ICU there.
-
-strings. We'll try to keep this minimal as well, by primarily working with
-string pieces on the public surface on the library. E.g., for the message id,
-which can be extracted from the binary message, we'll return a string piece
-which is backed by our binary buffer.
-
-The above library should be usable at least from the browser side, blink, and
-v8, but likely also from other projects (e.g. Google internal).
-
-## Code Generation
-
-The code generator (not part of encoding library) will provide:
-
-* Types for parameters, results, notification objects. These are likely
- generated classes just like now.
-* It will be possible to instatiate these types directly from binary messages,
- and it will be possible to directly serialize to binary messages. This will
- be generated code, somewhat similar to the code that's in protocol buffers
- when ParseFromString / ToString is invoked. It will probably involve
- supporting return types / parameters in these routines that come from the
- third party library, or if not, we'll instantiate it under the hood. It should
- not involve protocol::Value except maybe for free-form parts of the messages
- ("any" in the .pdl).
-* The code generator may provide glue. E.g., convert between the
- aforementioned string piece with whatever is the appropriate string piece in
- blink / v8 / etc.
-* The code generator is responsible for significant parts of the dispatching
- logic.
-
-The code generator is currently implemented in Python / jinja2 templates; we can
-probably adapt this / bend it to these needs. The templates for the existing
-functionality should shrink, however. E.g., we will not have a json parser in a
-template but rather use the one in the new library, and we may also move other
-supporting code out of the templates and into the library. The benefit of this
-should be that it's easier to understand / test / maintain, especially it should
-become possible to unittest within this project and provide sufficient
-confidence.
+# CRDTP - Chrome DevTools Protocol Library.
+
+[Canonical location for this library.](https://chromium.googlesource.com/deps/inspector_protocol/+/refs/heads/master)
+
+This is a support library for the Chrome DevTools protocol implementation.
+
+It's used from within the Jinja templates which we use for code generation
+(see ../lib and ../templates) as well as from Chromium (headless,
+chrome, content, blink), V8, and other code bases that use the DevTools
+protocol.
+
+The library is designed to be portable. The only allowed dependencies are:
+
+- The C/C++ standard libraries, up to C++14.
+ The litmus test is that it compiles and passes tests for all platforms
+ supported by V8.
+
+- For testing, we depend on mini_chromium and gtest. This is isolated
+ into the `crdtp/test_platform.{h,cc}` library.
+
+We support 32 bit and 64 bit architectures.
+
+# Common types used in this library.
+
+- `uint8_t`: a byte, e.g. for raw bytes or UTF8 characters
+
+- `uint16_t`: two bytes, e.g. for UTF16 characters
+
+For input parameters:
+
+- `span<uint8_t>`: pointer to bytes and length
+
+- `span<uint16_t>`: pointer to UTF16 chars and length
+
+For output parameters:
+
+- `std::vector<uint8_t>` - Owned segment of bytes / utf8 characters and length.
+
+- `std::string` - Same, for compatibility, even though char is signed.
+
+# Building and running the tests.
+
+If you're familiar with
+[Chromium's development process](https://www.chromium.org/developers/contributing-code)
+and have the depot_tools installed, you may use these commands
+to fetch the package (and dependencies) and build and run the tests:
+
+ fetch inspector_protocol
+ cd src
+ gn gen out/Release
+ ninja -C out/Release crdtp_test
+ out/Release/crdtp_test
+
+You'll probably also need to install g++, since Clang uses this to find the
+standard C++ headers. E.g.,
+
+ sudo apt-get install g++-8
+
+# Purpose of the tests
+
+crdtp comes with unittest coverage.
+
+Upstream, in this standalone package, the unittests make development
+more pleasant because they are very fast and light (try the previous
+section to see).
+
+Downstream (in Chromium, V8, etc.), they ensure that the library behaves
+correctly within each specific code base. We have seen bugs from different
+architectures / compilers / etc. in the past. We have also seen
+that a tweaked downstream crdtp_platform library did not behave correctly,
+becaues V8's strtod routine interprets out of range literals as 'inf'.
+Thus, the unittests function as a conformance test suite for such code-base
+specific tweaks downstream.
+
+# Customization by downstream users (Chrome, V8, google3, etc.).
+
+Downstream users may need to customize the library. We isolate these typical
+customizations into two platform libraries (crdtp_plaform and
+crdtp_test_platform), to reduce the chance of merge conflicts and grief when
+rolling as much as possible. While customized platform libraries may
+depend on the downstream code base (e.g. abseil, Chromium's base, V8's utility
+functions, Boost, etc.), they are not exposed to the headers that
+downstream code depends on.
+
+## crdtp_platform
+
+This platform library is only used by the crdtp library; it is not part of the
+crdtp API. Thus far it consists only of json_platform.h and json_platform.cc,
+because conversion between a `std::string` and a double is tricky, and different
+code bases have different preferences in this regard. In this repository
+(upstream), json_platform.cc provides a reference implementation which uses the
+C++ standard library.
+
+Downstream, in Chromium, json_platform_chromium.cc has a different
+implementation that uses the routines in Chromium's //base, that is, it's a .cc
+file that's specific to Chromium. Similarly, in V8, json_platform_v8.cc uses
+V8's number conversion utilities, so it's a .cc file that's specific to V8. And
+in google3, we use the absl library. crdtp/json_platform.cc is designed to be
+easy to modify or replace, and the interface defined by its header is designed
+to be stable.
+
+## crdtp_test_platform
+
+This platform library is only used by the tests. Upstream, it's setup to
+use mini_chromium and gtest. Downstream, Chromium uses its //base libraries,
+and V8 uses theirs; and a small amount of tweaking is needed in each code
+base - e.g., Chromium, V8, and google3 each place `#include` declarations into
+test_platform.h that are specific to their code base, and they have their
+own routines in test_platform.cc which uses their own libraries.
+
+The purpose of crdtp_test_platform is to isolate the tweaking to this small,
+stable library (modifying test_platform.h and test_platform.cc). This avoids
+having to modify the actual tests (json_test.cc, cbor_test.cc, ...)
+when rolling changes downstream. We try to not use patch files.
diff --git a/chromium/third_party/inspector_protocol/crdtp/cbor.cc b/chromium/third_party/inspector_protocol/crdtp/cbor.cc
index 8acb6d41579..c6acf8777cd 100644
--- a/chromium/third_party/inspector_protocol/crdtp/cbor.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/cbor.cc
@@ -81,8 +81,8 @@ static constexpr uint8_t kExpectedConversionToBase64Tag =
// Writes the bytes for |v| to |out|, starting with the most significant byte.
// See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
-template <typename T, class C>
-void WriteBytesMostSignificantByteFirst(T v, C* out) {
+template <typename T>
+void WriteBytesMostSignificantByteFirst(T v, std::vector<uint8_t>* out) {
for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes)
out->push_back(0xff & (v >> (shift_bytes * 8)));
}
@@ -152,8 +152,9 @@ size_t ReadTokenStart(span<uint8_t> bytes, MajorType* type, uint64_t* value) {
// Writes the start of a token with |type|. The |value| may indicate the size,
// or it may be the payload if the value is an unsigned integer.
-template <typename C>
-void WriteTokenStartTmpl(MajorType type, uint64_t value, C* encoded) {
+void WriteTokenStart(MajorType type,
+ uint64_t value,
+ std::vector<uint8_t>* encoded) {
if (value < 24) {
// Values 0-23 are encoded directly into the additional info of the
// initial byte.
@@ -183,16 +184,6 @@ void WriteTokenStartTmpl(MajorType type, uint64_t value, C* encoded) {
encoded->push_back(EncodeInitialByte(type, kAdditionalInformation8Bytes));
WriteBytesMostSignificantByteFirst<uint64_t>(value, encoded);
}
-
-void WriteTokenStart(MajorType type,
- uint64_t value,
- std::vector<uint8_t>* encoded) {
- WriteTokenStartTmpl(type, value, encoded);
-}
-
-void WriteTokenStart(MajorType type, uint64_t value, std::string* encoded) {
- WriteTokenStartTmpl(type, value, encoded);
-}
} // namespace internals
// =============================================================================
@@ -212,6 +203,20 @@ bool IsCBORMessage(span<uint8_t> msg) {
msg[1] == InitialByteFor32BitLengthByteString();
}
+Status CheckCBORMessage(span<uint8_t> msg) {
+ if (msg.empty())
+ return Status(Error::CBOR_NO_INPUT, 0);
+ if (msg[0] != InitialByteForEnvelope())
+ return Status(Error::CBOR_INVALID_START_BYTE, 0);
+ if (msg.size() < 6 || msg[1] != InitialByteFor32BitLengthByteString())
+ return Status(Error::CBOR_INVALID_ENVELOPE, 1);
+ if (msg[2] == 0 && msg[3] == 0 && msg[4] == 0 && msg[5] == 0)
+ return Status(Error::CBOR_INVALID_ENVELOPE, 1);
+ if (msg.size() < 7 || msg[6] != EncodeIndefiniteLengthMapStart())
+ return Status(Error::CBOR_MAP_START_EXPECTED, 6);
+ return Status();
+}
+
// =============================================================================
// Encoding invidiual CBOR items
// =============================================================================
@@ -240,8 +245,7 @@ uint8_t EncodeStop() {
return kStopByte;
}
-template <typename C>
-void EncodeInt32Tmpl(int32_t value, C* out) {
+void EncodeInt32(int32_t value, std::vector<uint8_t>* out) {
if (value >= 0) {
internals::WriteTokenStart(MajorType::UNSIGNED, value, out);
} else {
@@ -250,16 +254,7 @@ void EncodeInt32Tmpl(int32_t value, C* out) {
}
}
-void EncodeInt32(int32_t value, std::vector<uint8_t>* out) {
- EncodeInt32Tmpl(value, out);
-}
-
-void EncodeInt32(int32_t value, std::string* out) {
- EncodeInt32Tmpl(value, out);
-}
-
-template <typename C>
-void EncodeString16Tmpl(span<uint16_t> in, C* out) {
+void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) {
uint64_t byte_length = static_cast<uint64_t>(in.size_bytes());
internals::WriteTokenStart(MajorType::BYTE_STRING, byte_length, out);
// When emitting UTF16 characters, we always write the least significant byte
@@ -277,31 +272,13 @@ void EncodeString16Tmpl(span<uint16_t> in, C* out) {
}
}
-void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) {
- EncodeString16Tmpl(in, out);
-}
-
-void EncodeString16(span<uint16_t> in, std::string* out) {
- EncodeString16Tmpl(in, out);
-}
-
-template <typename C>
-void EncodeString8Tmpl(span<uint8_t> in, C* out) {
+void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) {
internals::WriteTokenStart(MajorType::STRING,
static_cast<uint64_t>(in.size_bytes()), out);
out->insert(out->end(), in.begin(), in.end());
}
-void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) {
- EncodeString8Tmpl(in, out);
-}
-
-void EncodeString8(span<uint8_t> in, std::string* out) {
- EncodeString8Tmpl(in, out);
-}
-
-template <typename C>
-void EncodeFromLatin1Tmpl(span<uint8_t> latin1, C* out) {
+void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out) {
for (size_t ii = 0; ii < latin1.size(); ++ii) {
if (latin1[ii] <= 127)
continue;
@@ -322,16 +299,7 @@ void EncodeFromLatin1Tmpl(span<uint8_t> latin1, C* out) {
EncodeString8(latin1, out);
}
-void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out) {
- EncodeFromLatin1Tmpl(latin1, out);
-}
-
-void EncodeFromLatin1(span<uint8_t> latin1, std::string* out) {
- EncodeFromLatin1Tmpl(latin1, out);
-}
-
-template <typename C>
-void EncodeFromUTF16Tmpl(span<uint16_t> utf16, C* out) {
+void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out) {
// If there's at least one non-ASCII char, encode as STRING16 (UTF16).
for (uint16_t ch : utf16) {
if (ch <= 127)
@@ -345,30 +313,13 @@ void EncodeFromUTF16Tmpl(span<uint16_t> utf16, C* out) {
out->insert(out->end(), utf16.begin(), utf16.end());
}
-void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out) {
- EncodeFromUTF16Tmpl(utf16, out);
-}
-
-void EncodeFromUTF16(span<uint16_t> utf16, std::string* out) {
- EncodeFromUTF16Tmpl(utf16, out);
-}
-
-template <typename C>
-void EncodeBinaryTmpl(span<uint8_t> in, C* out) {
+void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) {
out->push_back(kExpectedConversionToBase64Tag);
uint64_t byte_length = static_cast<uint64_t>(in.size_bytes());
internals::WriteTokenStart(MajorType::BYTE_STRING, byte_length, out);
out->insert(out->end(), in.begin(), in.end());
}
-void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) {
- EncodeBinaryTmpl(in, out);
-}
-
-void EncodeBinary(span<uint8_t> in, std::string* out) {
- EncodeBinaryTmpl(in, out);
-}
-
// A double is encoded with a specific initial byte
// (kInitialByteForDouble) plus the 64 bits of payload for its value.
constexpr size_t kEncodedDoubleSize = 1 + sizeof(uint64_t);
@@ -378,8 +329,7 @@ constexpr size_t kEncodedDoubleSize = 1 + sizeof(uint64_t);
// bit wide length, plus a 32 bit length for that string.
constexpr size_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t);
-template <typename C>
-void EncodeDoubleTmpl(double value, C* out) {
+void EncodeDouble(double value, std::vector<uint8_t>* out) {
// The additional_info=27 indicates 64 bits for the double follow.
// See RFC 7049 Section 2.3, Table 1.
out->push_back(kInitialByteForDouble);
@@ -391,69 +341,43 @@ void EncodeDoubleTmpl(double value, C* out) {
WriteBytesMostSignificantByteFirst<uint64_t>(reinterpret.to_uint64, out);
}
-void EncodeDouble(double value, std::vector<uint8_t>* out) {
- EncodeDoubleTmpl(value, out);
-}
-
-void EncodeDouble(double value, std::string* out) {
- EncodeDoubleTmpl(value, out);
-}
-
// =============================================================================
// cbor::EnvelopeEncoder - for wrapping submessages
// =============================================================================
-template <typename C>
-void EncodeStartTmpl(C* out, size_t* byte_size_pos) {
- assert(*byte_size_pos == 0);
+void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) {
+ assert(byte_size_pos_ == 0);
out->push_back(kInitialByteForEnvelope);
out->push_back(kInitialByteFor32BitLengthByteString);
- *byte_size_pos = out->size();
+ byte_size_pos_ = out->size();
out->resize(out->size() + sizeof(uint32_t));
}
-void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) {
- EncodeStartTmpl<std::vector<uint8_t>>(out, &byte_size_pos_);
-}
-
-void EnvelopeEncoder::EncodeStart(std::string* out) {
- EncodeStartTmpl<std::string>(out, &byte_size_pos_);
-}
-
-template <typename C>
-bool EncodeStopTmpl(C* out, size_t* byte_size_pos) {
- assert(*byte_size_pos != 0);
+bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) {
+ assert(byte_size_pos_ != 0);
// The byte size is the size of the payload, that is, all the
// bytes that were written past the byte size position itself.
- uint64_t byte_size = out->size() - (*byte_size_pos + sizeof(uint32_t));
+ uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t));
// We store exactly 4 bytes, so at most INT32MAX, with most significant
// byte first.
if (byte_size > std::numeric_limits<uint32_t>::max())
return false;
for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0;
--shift_bytes) {
- (*out)[(*byte_size_pos)++] = 0xff & (byte_size >> (shift_bytes * 8));
+ (*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8));
}
return true;
}
-bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) {
- return EncodeStopTmpl(out, &byte_size_pos_);
-}
-
-bool EnvelopeEncoder::EncodeStop(std::string* out) {
- return EncodeStopTmpl(out, &byte_size_pos_);
-}
-
// =============================================================================
// cbor::NewCBOREncoder - for encoding from a streaming parser
// =============================================================================
namespace {
-template <typename C>
class CBOREncoder : public ParserHandler {
public:
- CBOREncoder(C* out, Status* status) : out_(out), status_(status) {
+ CBOREncoder(std::vector<uint8_t>* out, Status* status)
+ : out_(out), status_(status) {
*status_ = Status();
}
@@ -551,7 +475,7 @@ class CBOREncoder : public ParserHandler {
}
private:
- C* out_;
+ std::vector<uint8_t>* out_;
std::vector<EnvelopeEncoder> envelopes_;
Status* status_;
};
@@ -559,13 +483,7 @@ class CBOREncoder : public ParserHandler {
std::unique_ptr<ParserHandler> NewCBOREncoder(std::vector<uint8_t>* out,
Status* status) {
- return std::unique_ptr<ParserHandler>(
- new CBOREncoder<std::vector<uint8_t>>(out, status));
-}
-std::unique_ptr<ParserHandler> NewCBOREncoder(std::string* out,
- Status* status) {
- return std::unique_ptr<ParserHandler>(
- new CBOREncoder<std::string>(out, status));
+ return std::unique_ptr<ParserHandler>(new CBOREncoder(out, status));
}
// =============================================================================
@@ -916,19 +834,12 @@ bool ParseEnvelope(int32_t stack_depth,
return false;
break; // Continue to check pos_past_envelope below.
case CBORTokenTag::ARRAY_START:
- if (stack_depth == 0) { // Not allowed at the top level.
- out->HandleError(
- Status{Error::CBOR_MAP_START_EXPECTED, tokenizer->Status().pos});
- return false;
- }
if (!ParseArray(stack_depth + 1, tokenizer, out))
return false;
break; // Continue to check pos_past_envelope below.
default:
- out->HandleError(Status{
- stack_depth == 0 ? Error::CBOR_MAP_START_EXPECTED
- : Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE,
- tokenizer->Status().pos});
+ out->HandleError(Status{Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE,
+ tokenizer->Status().pos});
return false;
}
// The contents of the envelope parsed OK, now check that we're at
@@ -1073,19 +984,12 @@ void ParseCBOR(span<uint8_t> bytes, ParserHandler* out) {
out->HandleError(Status{Error::CBOR_NO_INPUT, 0});
return;
}
- if (bytes[0] != kInitialByteForEnvelope) {
- out->HandleError(Status{Error::CBOR_INVALID_START_BYTE, 0});
- return;
- }
CBORTokenizer tokenizer(bytes);
if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) {
out->HandleError(tokenizer.Status());
return;
}
- // We checked for the envelope start byte above, so the tokenizer
- // must agree here, since it's not an error.
- assert(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
- if (!ParseEnvelope(/*stack_depth=*/0, &tokenizer, out))
+ if (!ParseValue(/*stack_depth=*/0, &tokenizer, out))
return;
if (tokenizer.TokenTag() == CBORTokenTag::DONE)
return;
@@ -1100,10 +1004,9 @@ void ParseCBOR(span<uint8_t> bytes, ParserHandler* out) {
// cbor::AppendString8EntryToMap - for limited in-place editing of messages
// =============================================================================
-template <typename C>
-Status AppendString8EntryToCBORMapTmpl(span<uint8_t> string8_key,
- span<uint8_t> string8_value,
- C* cbor) {
+Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
+ span<uint8_t> string8_value,
+ std::vector<uint8_t>* cbor) {
// Careful below: Don't compare (*cbor)[idx] with a uint8_t, since
// it could be a char (signed!). Instead, use bytes.
span<uint8_t> bytes(reinterpret_cast<const uint8_t*>(cbor->data()),
@@ -1137,17 +1040,5 @@ Status AppendString8EntryToCBORMapTmpl(span<uint8_t> string8_key,
*(out) = new_envelope_size & 0xff;
return Status();
}
-
-Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
- span<uint8_t> string8_value,
- std::vector<uint8_t>* cbor) {
- return AppendString8EntryToCBORMapTmpl(string8_key, string8_value, cbor);
-}
-
-Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
- span<uint8_t> string8_value,
- std::string* cbor) {
- return AppendString8EntryToCBORMapTmpl(string8_key, string8_value, cbor);
-}
} // namespace cbor
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/cbor.h b/chromium/third_party/inspector_protocol/crdtp/cbor.h
index 8b520aa382d..416a2cf5190 100644
--- a/chromium/third_party/inspector_protocol/crdtp/cbor.h
+++ b/chromium/third_party/inspector_protocol/crdtp/cbor.h
@@ -53,6 +53,15 @@ CRDTP_EXPORT uint8_t InitialByteFor32BitLengthByteString();
// Checks whether |msg| is a cbor message.
CRDTP_EXPORT bool IsCBORMessage(span<uint8_t> msg);
+// Performs a leightweight check of |msg|.
+// Disallows:
+// - Empty message
+// - Not starting with the two bytes 0xd8, 0x5a
+// - Empty envelope (all length bytes are 0)
+// - Not starting with a map after the envelope stanza
+// DevTools messages should pass this check.
+CRDTP_EXPORT Status CheckCBORMessage(span<uint8_t> msg);
+
// =============================================================================
// Encoding individual CBOR items
// =============================================================================
@@ -68,41 +77,34 @@ CRDTP_EXPORT uint8_t EncodeStop();
// Encodes |value| as |UNSIGNED| (major type 0) iff >= 0, or |NEGATIVE|
// (major type 1) iff < 0.
CRDTP_EXPORT void EncodeInt32(int32_t value, std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeInt32(int32_t value, std::string* out);
// Encodes a UTF16 string as a BYTE_STRING (major type 2). Each utf16
// character in |in| is emitted with most significant byte first,
// appending to |out|.
CRDTP_EXPORT void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeString16(span<uint16_t> in, std::string* out);
// Encodes a UTF8 string |in| as STRING (major type 3).
CRDTP_EXPORT void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeString8(span<uint8_t> in, std::string* out);
// Encodes the given |latin1| string as STRING8.
// If any non-ASCII character is present, it will be represented
// as a 2 byte UTF8 sequence.
CRDTP_EXPORT void EncodeFromLatin1(span<uint8_t> latin1,
std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeFromLatin1(span<uint8_t> latin1, std::string* out);
// Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII.
// Otherwise, encodes as STRING16.
CRDTP_EXPORT void EncodeFromUTF16(span<uint16_t> utf16,
std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeFromUTF16(span<uint16_t> utf16, std::string* out);
// Encodes arbitrary binary data in |in| as a BYTE_STRING (major type 2) with
// definitive length, prefixed with tag 22 indicating expected conversion to
// base64 (see RFC 7049, Table 3 and Section 2.4.4.2).
CRDTP_EXPORT void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeBinary(span<uint8_t> in, std::string* out);
// Encodes / decodes a double as Major type 7 (SIMPLE_VALUE),
// with additional info = 27, followed by 8 bytes in big endian.
CRDTP_EXPORT void EncodeDouble(double value, std::vector<uint8_t>* out);
-CRDTP_EXPORT void EncodeDouble(double value, std::string* out);
// =============================================================================
// cbor::EnvelopeEncoder - for wrapping submessages
@@ -121,11 +123,9 @@ class CRDTP_EXPORT EnvelopeEncoder {
// byte size in |byte_size_pos_|. Also emits empty bytes for the
// byte sisze so that encoding can continue.
void EncodeStart(std::vector<uint8_t>* out);
- void EncodeStart(std::string* out);
// This records the current size in |out| at position byte_size_pos_.
// Returns true iff successful.
bool EncodeStop(std::vector<uint8_t>* out);
- bool EncodeStop(std::string* out);
private:
size_t byte_size_pos_ = 0;
@@ -142,8 +142,6 @@ class CRDTP_EXPORT EnvelopeEncoder {
CRDTP_EXPORT std::unique_ptr<ParserHandler> NewCBOREncoder(
std::vector<uint8_t>* out,
Status* status);
-CRDTP_EXPORT std::unique_ptr<ParserHandler> NewCBOREncoder(std::string* out,
- Status* status);
// =============================================================================
// cbor::CBORTokenizer - for parsing individual CBOR items
@@ -294,9 +292,6 @@ CRDTP_EXPORT void ParseCBOR(span<uint8_t> bytes, ParserHandler* out);
CRDTP_EXPORT Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
span<uint8_t> string8_value,
std::vector<uint8_t>* cbor);
-CRDTP_EXPORT Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
- span<uint8_t> string8_value,
- std::string* cbor);
namespace internals { // Exposed only for writing tests.
CRDTP_EXPORT size_t ReadTokenStart(span<uint8_t> bytes,
@@ -306,9 +301,6 @@ CRDTP_EXPORT size_t ReadTokenStart(span<uint8_t> bytes,
CRDTP_EXPORT void WriteTokenStart(cbor::MajorType type,
uint64_t value,
std::vector<uint8_t>* encoded);
-CRDTP_EXPORT void WriteTokenStart(cbor::MajorType type,
- uint64_t value,
- std::string* encoded);
} // namespace internals
} // namespace cbor
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/cbor_test.cc b/chromium/third_party/inspector_protocol/crdtp/cbor_test.cc
index 56b8b1042b1..a86ec549ec4 100644
--- a/chromium/third_party/inspector_protocol/crdtp/cbor_test.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/cbor_test.cc
@@ -17,6 +17,7 @@
#include "parser_handler.h"
#include "span.h"
#include "status.h"
+#include "status_test_support.h"
#include "test_platform.h"
using testing::ElementsAreArray;
@@ -39,6 +40,73 @@ TEST(IsCBORMessage, SomeSmokeTests) {
EXPECT_TRUE(IsCBORMessage(SpanFrom(one)));
}
+TEST(CheckCBORMessage, SmallestValidExample) {
+ // The smallest example that we consider valid for this lightweight check is
+ // an empty dictionary inside of an envelope.
+ std::vector<uint8_t> empty_dict = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
+ Status status = CheckCBORMessage(SpanFrom(empty_dict));
+ EXPECT_THAT(status, StatusIsOk());
+}
+
+TEST(CheckCBORMessage, ValidCBORButNotValidMessage) {
+ // The CBOR parser supports parsing values that aren't messages. E.g., this is
+ // the encoded unsigned int 7 (CBOR really encodes it as a single byte with
+ // value 7).
+ std::vector<uint8_t> not_a_message = {7};
+
+ // Show that the parser (happily) decodes it into JSON
+ std::string json;
+ Status status;
+ std::unique_ptr<ParserHandler> json_writer =
+ json::NewJSONEncoder(&json, &status);
+ ParseCBOR(SpanFrom(not_a_message), json_writer.get());
+ EXPECT_THAT(status, StatusIsOk());
+ EXPECT_EQ("7", json);
+
+ // ... but it's not a message.
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(not_a_message)),
+ StatusIs(Error::CBOR_INVALID_START_BYTE, 0));
+}
+
+TEST(CheckCBORMessage, EmptyMessage) {
+ std::vector<uint8_t> empty;
+ Status status = CheckCBORMessage(SpanFrom(empty));
+ EXPECT_THAT(status, StatusIs(Error::CBOR_NO_INPUT, 0));
+}
+
+TEST(CheckCBORMessage, InvalidStartByte) {
+ // Here we test that some actual json, which usually starts with {, is not
+ // considered CBOR. CBOR messages must start with 0xd8, 0x5a, the envelope
+ // start bytes.
+ Status status = CheckCBORMessage(SpanFrom("{\"msg\": \"Hello, world.\"}"));
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_START_BYTE, 0));
+}
+
+TEST(CheckCBORMessage, InvalidEnvelopes) {
+ std::vector<uint8_t> bytes = {0xd8, 0x5a};
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
+ StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
+ bytes = {0xd8, 0x5a, 0};
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
+ StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
+ bytes = {0xd8, 0x5a, 0, 0};
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
+ StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
+ bytes = {0xd8, 0x5a, 0, 0, 0};
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
+ StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
+ bytes = {0xd8, 0x5a, 0, 0, 0, 0};
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
+ StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
+}
+
+TEST(CheckCBORMessage, MapStartExpected) {
+ std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, 1};
+ EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
+ StatusIs(Error::CBOR_MAP_START_EXPECTED, 6));
+}
+
// =============================================================================
// Encoding individual CBOR items
// cbor::CBORTokenizer - for parsing individual CBOR items
@@ -164,7 +232,7 @@ TEST(EncodeDecodeInt32Test, CantRoundtripUint32) {
CBORTokenizer tokenizer(SpanFrom(encoded));
// 0xdeadbeef is > std::numerical_limits<int32_t>::max().
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
- EXPECT_EQ(Error::CBOR_INVALID_INT32, tokenizer.Status().error);
+ EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_INT32, 0u));
}
TEST(EncodeDecodeInt32Test, DecodeErrorCases) {
@@ -191,7 +259,7 @@ TEST(EncodeDecodeInt32Test, DecodeErrorCases) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
- EXPECT_EQ(Error::CBOR_INVALID_INT32, tokenizer.Status().error);
+ EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_INT32, 0u));
}
}
@@ -353,7 +421,7 @@ TEST(EncodeDecodeString16Test, ErrorCases) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
- EXPECT_EQ(Error::CBOR_INVALID_STRING16, tokenizer.Status().error);
+ EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_STRING16, 0u));
}
}
@@ -402,7 +470,7 @@ TEST(EncodeDecodeString8Test, ErrorCases) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
- EXPECT_EQ(Error::CBOR_INVALID_STRING8, tokenizer.Status().error);
+ EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_STRING8, 0u));
}
}
@@ -477,7 +545,7 @@ TEST(EncodeDecodeBinaryTest, RoundtripsHelloWorld) {
std::vector<uint8_t> decoded;
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::BINARY, tokenizer.TokenTag());
- EXPECT_EQ(0, static_cast<int>(tokenizer.Status().error));
+ EXPECT_THAT(tokenizer.Status(), StatusIsOk());
decoded = std::vector<uint8_t>(tokenizer.GetBinary().begin(),
tokenizer.GetBinary().end());
EXPECT_THAT(decoded, ElementsAreArray(binary));
@@ -499,7 +567,7 @@ TEST(EncodeDecodeBinaryTest, ErrorCases) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
- EXPECT_EQ(Error::CBOR_INVALID_BINARY, tokenizer.Status().error);
+ EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_BINARY, 0u));
}
}
@@ -631,7 +699,7 @@ TEST(JSONToCBOREncoderTest, SevenBitStrings) {
std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(&encoded, &status);
std::vector<uint16_t> utf16 = {'f', 'o', 'o'};
encoder->HandleString16(span<uint16_t>(utf16.data(), utf16.size()));
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
// Here we assert that indeed, seven bit strings are represented as
// bytes on the wire, "foo" is just "foo".
EXPECT_THAT(encoded,
@@ -702,7 +770,7 @@ TEST(JsonCborRoundtrip, EncodingDecoding) {
std::unique_ptr<ParserHandler> json_encoder =
json::NewJSONEncoder(&decoded, &status);
ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_encoder.get());
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ(json, decoded);
}
@@ -722,7 +790,7 @@ TEST(JsonCborRoundtrip, MoreRoundtripExamples) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&decoded, &status);
ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ(json, decoded);
}
}
@@ -747,15 +815,14 @@ TEST(JSONToCBOREncoderTest, HelloWorldBinary_WithTripToJson) {
encoder->HandleBinary(SpanFrom(std::vector<uint8_t>{
'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'}));
encoder->HandleMapEnd();
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
// Now drive the json writer via the CBOR decoder.
std::string decoded;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&decoded, &status);
ParseCBOR(SpanFrom(encoded), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
// "Hello, world." in base64 is "SGVsbG8sIHdvcmxkLg==".
EXPECT_EQ("{\"foo\":\"SGVsbG8sIHdvcmxkLg==\"}", decoded);
}
@@ -774,7 +841,7 @@ TEST(ParseCBORTest, ParseEmptyCBORMessage) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{}", out);
}
@@ -798,7 +865,7 @@ TEST(ParseCBORTest, ParseCBORHelloWorld) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"msg\":\"Hello, \\ud83c\\udf0e.\"}", out);
}
@@ -823,7 +890,7 @@ TEST(ParseCBORTest, UTF8IsSupportedInKeys) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
+ EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"\\ud83c\\udf0e\":\"\\u263e\"}", out);
}
@@ -834,21 +901,7 @@ TEST(ParseCBORTest, NoInputError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_NO_INPUT, status.error);
- EXPECT_EQ("", out);
-}
-
-TEST(ParseCBORTest, InvalidStartByteError) {
- // Here we test that some actual json, which usually starts with {,
- // is not considered CBOR. CBOR messages must start with 0x5a, the
- // envelope start byte.
- std::string json = "{\"msg\": \"Hello, world.\"}";
- std::string out;
- Status status;
- std::unique_ptr<ParserHandler> json_writer =
- json::NewJSONEncoder(&out, &status);
- ParseCBOR(SpanFrom(json), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_START_BYTE, status.error);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_NO_INPUT, 0u));
EXPECT_EQ("", out);
}
@@ -864,8 +917,8 @@ TEST(ParseCBORTest, UnexpectedEofExpectedValueError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE, status.error);
- EXPECT_EQ(bytes.size(), status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE,
+ bytes.size()));
EXPECT_EQ("", out);
}
@@ -882,8 +935,8 @@ TEST(ParseCBORTest, UnexpectedEofInArrayError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, status.error);
- EXPECT_EQ(bytes.size(), status.pos);
+ EXPECT_THAT(status,
+ StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, bytes.size()));
EXPECT_EQ("", out);
}
@@ -897,44 +950,19 @@ TEST(ParseCBORTest, UnexpectedEofInMapError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_IN_MAP, status.error);
- EXPECT_EQ(7u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_MAP, 7u));
EXPECT_EQ("", out);
}
-TEST(ParseCBORTest, TopLevelCantBeEmptyEnvelope) {
- // Normally, an array would be allowed inside an envelope, but
- // the top-level envelope is required to contain a map.
+TEST(ParseCBORTest, NoEmptyEnvelopesAllowed) {
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, 0}; // envelope
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_MAP_START_EXPECTED, status.error);
- EXPECT_EQ(bytes.size(), status.pos);
- EXPECT_EQ("", out);
-}
-
-TEST(ParseCBORTest, MapStartExpectedAtTopLevel) {
- // Normally, an array would be allowed inside an envelope, but
- // the top-level envelope is required to contain a map.
- constexpr uint8_t kPayloadLen = 1;
- std::vector<uint8_t> bytes = {0xd8,
- 0x5a,
- 0,
- 0,
- 0,
- kPayloadLen, // envelope
- EncodeIndefiniteLengthArrayStart()};
- EXPECT_EQ(kPayloadLen, bytes.size() - 6);
- std::string out;
- Status status;
- std::unique_ptr<ParserHandler> json_writer =
- json::NewJSONEncoder(&out, &status);
- ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_MAP_START_EXPECTED, status.error);
- EXPECT_EQ(6u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE,
+ bytes.size()));
EXPECT_EQ("", out);
}
@@ -963,8 +991,8 @@ TEST(ParseCBORTest, OnlyMapsAndArraysSupportedInsideEnvelopes) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE,
+ error_pos));
EXPECT_EQ("", out);
}
@@ -980,8 +1008,7 @@ TEST(ParseCBORTest, InvalidMapKeyError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_MAP_KEY, status.error);
- EXPECT_EQ(7u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_MAP_KEY, 7u));
EXPECT_EQ("", out);
}
@@ -1011,8 +1038,7 @@ TEST(ParseCBORTest, StackLimitExceededError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"key\":{\"key\":{\"key\":\"innermost_value\"}}}", out);
}
{ // Depth 300: no stack limit exceeded.
@@ -1022,8 +1048,7 @@ TEST(ParseCBORTest, StackLimitExceededError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
}
// We just want to know the length of one opening map so we can compute
@@ -1042,8 +1067,8 @@ TEST(ParseCBORTest, StackLimitExceededError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_STACK_LIMIT_EXCEEDED, status.error);
- EXPECT_EQ(opening_segment_size * 301, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_STACK_LIMIT_EXCEEDED,
+ opening_segment_size * 301));
}
{ // Depth 320: still limit exceeded, and at the same pos as for 1001
std::vector<uint8_t> bytes = MakeNestedCBOR(320);
@@ -1052,8 +1077,8 @@ TEST(ParseCBORTest, StackLimitExceededError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_STACK_LIMIT_EXCEEDED, status.error);
- EXPECT_EQ(opening_segment_size * 301, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_STACK_LIMIT_EXCEEDED,
+ opening_segment_size * 301));
}
}
@@ -1071,8 +1096,7 @@ TEST(ParseCBORTest, UnsupportedValueError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_UNSUPPORTED_VALUE, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_UNSUPPORTED_VALUE, error_pos));
EXPECT_EQ("", out);
}
@@ -1094,8 +1118,7 @@ TEST(ParseCBORTest, InvalidString16Error) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_STRING16, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_STRING16, error_pos));
EXPECT_EQ("", out);
}
@@ -1114,8 +1137,7 @@ TEST(ParseCBORTest, InvalidString8Error) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_STRING8, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_STRING8, error_pos));
EXPECT_EQ("", out);
}
@@ -1136,8 +1158,7 @@ TEST(ParseCBORTest, InvalidBinaryError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_BINARY, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_BINARY, error_pos));
EXPECT_EQ("", out);
}
@@ -1157,8 +1178,7 @@ TEST(ParseCBORTest, InvalidDoubleError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_DOUBLE, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_DOUBLE, error_pos));
EXPECT_EQ("", out);
}
@@ -1178,8 +1198,7 @@ TEST(ParseCBORTest, InvalidSignedError) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_INVALID_INT32, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_INT32, error_pos));
EXPECT_EQ("", out);
}
@@ -1201,8 +1220,7 @@ TEST(ParseCBORTest, TrailingJunk) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_TRAILING_JUNK, status.error);
- EXPECT_EQ(error_pos, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_TRAILING_JUNK, error_pos));
EXPECT_EQ("", out);
}
@@ -1224,8 +1242,8 @@ TEST(ParseCBORTest, EnvelopeContentsLengthMismatch) {
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
- EXPECT_EQ(Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH, status.error);
- EXPECT_EQ(bytes.size(), status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH,
+ bytes.size()));
EXPECT_EQ("", out);
}
@@ -1239,7 +1257,7 @@ class AppendString8EntryToMapTest : public ::testing::Test {};
using ContainerTestTypes = ::testing::Types<std::vector<uint8_t>, std::string>;
TYPED_TEST_SUITE(AppendString8EntryToMapTest, ContainerTestTypes);
-TYPED_TEST(AppendString8EntryToMapTest, AppendsEntrySuccessfully) {
+TEST(AppendString8EntryToMapTest, AppendsEntrySuccessfully) {
constexpr uint8_t kPayloadLen = 12;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen, // envelope
0xbf}; // map start
@@ -1249,33 +1267,31 @@ TYPED_TEST(AppendString8EntryToMapTest, AppendsEntrySuccessfully) {
bytes.push_back(0xff); // A perfectly fine cbor message.
EXPECT_EQ(kPayloadLen, bytes.size() - pos_before_payload);
- TypeParam msg(bytes.begin(), bytes.end());
+ std::vector<uint8_t> msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("foo"), SpanFrom("bar"), &msg);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
std::string out;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(SpanFrom(msg), json_writer.get());
EXPECT_EQ("{\"key\":\"value\",\"foo\":\"bar\"}", out);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
}
TYPED_TEST(AppendString8EntryToMapTest, AppendThreeEntries) {
std::vector<uint8_t> encoded = {
0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
- EXPECT_EQ(Error::OK, AppendString8EntryToCBORMap(SpanFrom("key"),
- SpanFrom("value"), &encoded)
- .error);
- EXPECT_EQ(Error::OK, AppendString8EntryToCBORMap(SpanFrom("key1"),
- SpanFrom("value1"), &encoded)
- .error);
- EXPECT_EQ(Error::OK, AppendString8EntryToCBORMap(SpanFrom("key2"),
- SpanFrom("value2"), &encoded)
- .error);
+ EXPECT_THAT(
+ AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &encoded),
+ StatusIsOk());
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key1"), SpanFrom("value1"),
+ &encoded),
+ StatusIsOk());
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key2"), SpanFrom("value2"),
+ &encoded),
+ StatusIsOk());
TypeParam msg(encoded.begin(), encoded.end());
std::string out;
Status status;
@@ -1283,68 +1299,55 @@ TYPED_TEST(AppendString8EntryToMapTest, AppendThreeEntries) {
json::NewJSONEncoder(&out, &status);
ParseCBOR(SpanFrom(msg), json_writer.get());
EXPECT_EQ("{\"key\":\"value\",\"key1\":\"value1\",\"key2\":\"value2\"}", out);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
}
-TYPED_TEST(AppendString8EntryToMapTest, MapStartExpected_Error) {
- std::vector<uint8_t> bytes = {
+TEST(AppendString8EntryToMapTest, MapStartExpected_Error) {
+ std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 1, EncodeIndefiniteLengthArrayStart()};
- TypeParam msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
- EXPECT_EQ(Error::CBOR_MAP_START_EXPECTED, status.error);
- EXPECT_EQ(6u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_START_EXPECTED, 6u));
}
-TYPED_TEST(AppendString8EntryToMapTest, MapStopExpected_Error) {
- std::vector<uint8_t> bytes = {
+TEST(AppendString8EntryToMapTest, MapStopExpected_Error) {
+ std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), 42};
- TypeParam msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
- EXPECT_EQ(Error::CBOR_MAP_STOP_EXPECTED, status.error);
- EXPECT_EQ(7u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_STOP_EXPECTED, 7u));
}
-TYPED_TEST(AppendString8EntryToMapTest, InvalidEnvelope_Error) {
+TEST(AppendString8EntryToMapTest, InvalidEnvelope_Error) {
{ // Second byte is wrong.
- std::vector<uint8_t> bytes = {
+ std::vector<uint8_t> msg = {
0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop(), 0};
- TypeParam msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
- EXPECT_EQ(Error::CBOR_INVALID_ENVELOPE, status.error);
- EXPECT_EQ(0u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
{ // Second byte is wrong.
- std::vector<uint8_t> bytes = {
+ std::vector<uint8_t> msg = {
0xd8, 0x7a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
- TypeParam msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
- EXPECT_EQ(Error::CBOR_INVALID_ENVELOPE, status.error);
- EXPECT_EQ(0u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
{ // Invalid envelope size example.
- std::vector<uint8_t> bytes = {
+ std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 3, EncodeIndefiniteLengthMapStart(), EncodeStop(),
};
- TypeParam msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
- EXPECT_EQ(Error::CBOR_INVALID_ENVELOPE, status.error);
- EXPECT_EQ(0u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
{ // Invalid envelope size example.
- std::vector<uint8_t> bytes = {
+ std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 1, EncodeIndefiniteLengthMapStart(), EncodeStop(),
};
- TypeParam msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
- EXPECT_EQ(Error::CBOR_INVALID_ENVELOPE, status.error);
- EXPECT_EQ(0u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
}
} // namespace cbor
diff --git a/chromium/third_party/inspector_protocol/crdtp/dispatch.cc b/chromium/third_party/inspector_protocol/crdtp/dispatch.cc
new file mode 100644
index 00000000000..598fd36cc4d
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/dispatch.cc
@@ -0,0 +1,576 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dispatch.h"
+
+#include <cassert>
+#include "cbor.h"
+#include "error_support.h"
+#include "find_by_first.h"
+#include "frontend_channel.h"
+
+namespace crdtp {
+// =============================================================================
+// DispatchResponse - Error status and chaining / fall through
+// =============================================================================
+
+// static
+DispatchResponse DispatchResponse::Success() {
+ DispatchResponse result;
+ result.code_ = DispatchCode::SUCCESS;
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::FallThrough() {
+ DispatchResponse result;
+ result.code_ = DispatchCode::FALL_THROUGH;
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::ParseError(std::string message) {
+ DispatchResponse result;
+ result.code_ = DispatchCode::PARSE_ERROR;
+ result.message_ = std::move(message);
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::InvalidRequest(std::string message) {
+ DispatchResponse result;
+ result.code_ = DispatchCode::INVALID_REQUEST;
+ result.message_ = std::move(message);
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::MethodNotFound(std::string message) {
+ DispatchResponse result;
+ result.code_ = DispatchCode::METHOD_NOT_FOUND;
+ result.message_ = std::move(message);
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::InvalidParams(std::string message) {
+ DispatchResponse result;
+ result.code_ = DispatchCode::INVALID_PARAMS;
+ result.message_ = std::move(message);
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::InternalError() {
+ DispatchResponse result;
+ result.code_ = DispatchCode::INTERNAL_ERROR;
+ result.message_ = "Internal error";
+ return result;
+}
+
+// static
+DispatchResponse DispatchResponse::ServerError(std::string message) {
+ DispatchResponse result;
+ result.code_ = DispatchCode::SERVER_ERROR;
+ result.message_ = std::move(message);
+ return result;
+}
+
+// =============================================================================
+// Dispatchable - a shallow parser for CBOR encoded DevTools messages
+// =============================================================================
+namespace {
+constexpr size_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t);
+} // namespace
+
+Dispatchable::Dispatchable(span<uint8_t> serialized) : serialized_(serialized) {
+ Status s = cbor::CheckCBORMessage(serialized);
+ if (!s.ok()) {
+ status_ = {Error::MESSAGE_MUST_BE_AN_OBJECT, s.pos};
+ return;
+ }
+ cbor::CBORTokenizer tokenizer(serialized);
+ if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) {
+ status_ = tokenizer.Status();
+ return;
+ }
+
+ // We checked for the envelope start byte above, so the tokenizer
+ // must agree here, since it's not an error.
+ assert(tokenizer.TokenTag() == cbor::CBORTokenTag::ENVELOPE);
+
+ // Before we enter the envelope, we save the position that we
+ // expect to see after we're done parsing the envelope contents.
+ // This way we can compare and produce an error if the contents
+ // didn't fit exactly into the envelope length.
+ const size_t pos_past_envelope = tokenizer.Status().pos +
+ kEncodedEnvelopeHeaderSize +
+ tokenizer.GetEnvelopeContents().size();
+ tokenizer.EnterEnvelope();
+ if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) {
+ status_ = tokenizer.Status();
+ return;
+ }
+ if (tokenizer.TokenTag() != cbor::CBORTokenTag::MAP_START) {
+ status_ = {Error::MESSAGE_MUST_BE_AN_OBJECT, tokenizer.Status().pos};
+ return;
+ }
+ assert(tokenizer.TokenTag() == cbor::CBORTokenTag::MAP_START);
+ tokenizer.Next(); // Now we should be pointed at the map key.
+ while (tokenizer.TokenTag() != cbor::CBORTokenTag::STOP) {
+ switch (tokenizer.TokenTag()) {
+ case cbor::CBORTokenTag::DONE:
+ status_ =
+ Status{Error::CBOR_UNEXPECTED_EOF_IN_MAP, tokenizer.Status().pos};
+ return;
+ case cbor::CBORTokenTag::ERROR_VALUE:
+ status_ = tokenizer.Status();
+ return;
+ case cbor::CBORTokenTag::STRING8:
+ if (!MaybeParseProperty(&tokenizer))
+ return;
+ break;
+ default:
+ // We require the top-level keys to be UTF8 (US-ASCII in practice).
+ status_ = Status{Error::CBOR_INVALID_MAP_KEY, tokenizer.Status().pos};
+ return;
+ }
+ }
+ tokenizer.Next();
+ if (!has_call_id_) {
+ status_ = Status{Error::MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY,
+ tokenizer.Status().pos};
+ return;
+ }
+ if (method_.empty()) {
+ status_ = Status{Error::MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY,
+ tokenizer.Status().pos};
+ return;
+ }
+ // The contents of the envelope parsed OK, now check that we're at
+ // the expected position.
+ if (pos_past_envelope != tokenizer.Status().pos) {
+ status_ = Status{Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH,
+ tokenizer.Status().pos};
+ return;
+ }
+ if (tokenizer.TokenTag() != cbor::CBORTokenTag::DONE) {
+ status_ = Status{Error::CBOR_TRAILING_JUNK, tokenizer.Status().pos};
+ return;
+ }
+}
+
+bool Dispatchable::ok() const {
+ return status_.ok();
+}
+
+DispatchResponse Dispatchable::DispatchError() const {
+ // TODO(johannes): Replace with DCHECK / similar?
+ if (status_.ok())
+ return DispatchResponse::Success();
+
+ if (status_.IsMessageError())
+ return DispatchResponse::InvalidRequest(status_.Message());
+ return DispatchResponse::ParseError(status_.ToASCIIString());
+}
+
+bool Dispatchable::MaybeParseProperty(cbor::CBORTokenizer* tokenizer) {
+ span<uint8_t> property_name = tokenizer->GetString8();
+ if (SpanEquals(SpanFrom("id"), property_name))
+ return MaybeParseCallId(tokenizer);
+ if (SpanEquals(SpanFrom("method"), property_name))
+ return MaybeParseMethod(tokenizer);
+ if (SpanEquals(SpanFrom("params"), property_name))
+ return MaybeParseParams(tokenizer);
+ if (SpanEquals(SpanFrom("sessionId"), property_name))
+ return MaybeParseSessionId(tokenizer);
+ status_ =
+ Status{Error::MESSAGE_HAS_UNKNOWN_PROPERTY, tokenizer->Status().pos};
+ return false;
+}
+
+bool Dispatchable::MaybeParseCallId(cbor::CBORTokenizer* tokenizer) {
+ if (has_call_id_) {
+ status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
+ return false;
+ }
+ tokenizer->Next();
+ if (tokenizer->TokenTag() != cbor::CBORTokenTag::INT32) {
+ status_ = Status{Error::MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY,
+ tokenizer->Status().pos};
+ return false;
+ }
+ call_id_ = tokenizer->GetInt32();
+ has_call_id_ = true;
+ tokenizer->Next();
+ return true;
+}
+
+bool Dispatchable::MaybeParseMethod(cbor::CBORTokenizer* tokenizer) {
+ if (!method_.empty()) {
+ status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
+ return false;
+ }
+ tokenizer->Next();
+ if (tokenizer->TokenTag() != cbor::CBORTokenTag::STRING8) {
+ status_ = Status{Error::MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY,
+ tokenizer->Status().pos};
+ return false;
+ }
+ method_ = tokenizer->GetString8();
+ tokenizer->Next();
+ return true;
+}
+
+bool Dispatchable::MaybeParseParams(cbor::CBORTokenizer* tokenizer) {
+ if (params_seen_) {
+ status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
+ return false;
+ }
+ params_seen_ = true;
+ tokenizer->Next();
+ if (tokenizer->TokenTag() == cbor::CBORTokenTag::NULL_VALUE) {
+ tokenizer->Next();
+ return true;
+ }
+ if (tokenizer->TokenTag() != cbor::CBORTokenTag::ENVELOPE) {
+ status_ = Status{Error::MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY,
+ tokenizer->Status().pos};
+ return false;
+ }
+ params_ = tokenizer->GetEnvelope();
+ tokenizer->Next();
+ return true;
+}
+
+bool Dispatchable::MaybeParseSessionId(cbor::CBORTokenizer* tokenizer) {
+ if (!session_id_.empty()) {
+ status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
+ return false;
+ }
+ tokenizer->Next();
+ if (tokenizer->TokenTag() != cbor::CBORTokenTag::STRING8) {
+ status_ = Status{Error::MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY,
+ tokenizer->Status().pos};
+ return false;
+ }
+ session_id_ = tokenizer->GetString8();
+ tokenizer->Next();
+ return true;
+}
+
+namespace {
+class ProtocolError : public Serializable {
+ public:
+ explicit ProtocolError(DispatchResponse dispatch_response)
+ : dispatch_response_(std::move(dispatch_response)) {}
+
+ void AppendSerialized(std::vector<uint8_t>* out) const override {
+ Status status;
+ std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(out, &status);
+ encoder->HandleMapBegin();
+ if (has_call_id_) {
+ encoder->HandleString8(SpanFrom("id"));
+ encoder->HandleInt32(call_id_);
+ }
+ encoder->HandleString8(SpanFrom("error"));
+ encoder->HandleMapBegin();
+ encoder->HandleString8(SpanFrom("code"));
+ encoder->HandleInt32(static_cast<int32_t>(dispatch_response_.Code()));
+ encoder->HandleString8(SpanFrom("message"));
+ encoder->HandleString8(SpanFrom(dispatch_response_.Message()));
+ if (!data_.empty()) {
+ encoder->HandleString8(SpanFrom("data"));
+ encoder->HandleString8(SpanFrom(data_));
+ }
+ encoder->HandleMapEnd();
+ encoder->HandleMapEnd();
+ assert(status.ok());
+ }
+
+ void SetCallId(int call_id) {
+ has_call_id_ = true;
+ call_id_ = call_id;
+ }
+ void SetData(std::string data) { data_ = std::move(data); }
+
+ private:
+ const DispatchResponse dispatch_response_;
+ std::string data_;
+ int call_id_ = 0;
+ bool has_call_id_ = false;
+};
+} // namespace
+
+// =============================================================================
+// Helpers for creating protocol cresponses and notifications.
+// =============================================================================
+
+std::unique_ptr<Serializable> CreateErrorResponse(
+ int call_id,
+ DispatchResponse dispatch_response,
+ const ErrorSupport* errors) {
+ auto protocol_error =
+ std::make_unique<ProtocolError>(std::move(dispatch_response));
+ protocol_error->SetCallId(call_id);
+ if (errors && !errors->Errors().empty()) {
+ protocol_error->SetData(
+ std::string(errors->Errors().begin(), errors->Errors().end()));
+ }
+ return protocol_error;
+}
+
+std::unique_ptr<Serializable> CreateErrorNotification(
+ DispatchResponse dispatch_response) {
+ return std::make_unique<ProtocolError>(std::move(dispatch_response));
+}
+
+namespace {
+class Response : public Serializable {
+ public:
+ Response(int call_id, std::unique_ptr<Serializable> params)
+ : call_id_(call_id), params_(std::move(params)) {}
+
+ void AppendSerialized(std::vector<uint8_t>* out) const override {
+ Status status;
+ std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(out, &status);
+ encoder->HandleMapBegin();
+ encoder->HandleString8(SpanFrom("id"));
+ encoder->HandleInt32(call_id_);
+ encoder->HandleString8(SpanFrom("result"));
+ if (params_) {
+ params_->AppendSerialized(out);
+ } else {
+ encoder->HandleMapBegin();
+ encoder->HandleMapEnd();
+ }
+ encoder->HandleMapEnd();
+ assert(status.ok());
+ }
+
+ private:
+ const int call_id_;
+ std::unique_ptr<Serializable> params_;
+};
+
+class Notification : public Serializable {
+ public:
+ Notification(const char* method, std::unique_ptr<Serializable> params)
+ : method_(method), params_(std::move(params)) {}
+
+ void AppendSerialized(std::vector<uint8_t>* out) const override {
+ Status status;
+ std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(out, &status);
+ encoder->HandleMapBegin();
+ encoder->HandleString8(SpanFrom("method"));
+ encoder->HandleString8(SpanFrom(method_));
+ encoder->HandleString8(SpanFrom("params"));
+ if (params_) {
+ params_->AppendSerialized(out);
+ } else {
+ encoder->HandleMapBegin();
+ encoder->HandleMapEnd();
+ }
+ encoder->HandleMapEnd();
+ assert(status.ok());
+ }
+
+ private:
+ const char* method_;
+ std::unique_ptr<Serializable> params_;
+};
+} // namespace
+
+std::unique_ptr<Serializable> CreateResponse(
+ int call_id,
+ std::unique_ptr<Serializable> params) {
+ return std::make_unique<Response>(call_id, std::move(params));
+}
+
+std::unique_ptr<Serializable> CreateNotification(
+ const char* method,
+ std::unique_ptr<Serializable> params) {
+ return std::make_unique<Notification>(method, std::move(params));
+}
+
+// =============================================================================
+// DomainDispatcher - Dispatching betwen protocol methods within a domain.
+// =============================================================================
+DomainDispatcher::WeakPtr::WeakPtr(DomainDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {}
+
+DomainDispatcher::WeakPtr::~WeakPtr() {
+ if (dispatcher_)
+ dispatcher_->weak_ptrs_.erase(this);
+}
+
+DomainDispatcher::Callback::~Callback() = default;
+
+void DomainDispatcher::Callback::dispose() {
+ backend_impl_ = nullptr;
+}
+
+DomainDispatcher::Callback::Callback(
+ std::unique_ptr<DomainDispatcher::WeakPtr> backend_impl,
+ int call_id,
+ span<uint8_t> method,
+ span<uint8_t> message)
+ : backend_impl_(std::move(backend_impl)),
+ call_id_(call_id),
+ method_(method),
+ message_(message.begin(), message.end()) {}
+
+void DomainDispatcher::Callback::sendIfActive(
+ std::unique_ptr<Serializable> partialMessage,
+ const DispatchResponse& response) {
+ if (!backend_impl_ || !backend_impl_->get())
+ return;
+ backend_impl_->get()->sendResponse(call_id_, response,
+ std::move(partialMessage));
+ backend_impl_ = nullptr;
+}
+
+void DomainDispatcher::Callback::fallThroughIfActive() {
+ if (!backend_impl_ || !backend_impl_->get())
+ return;
+ backend_impl_->get()->channel()->FallThrough(call_id_, method_,
+ SpanFrom(message_));
+ backend_impl_ = nullptr;
+}
+
+DomainDispatcher::DomainDispatcher(FrontendChannel* frontendChannel)
+ : frontend_channel_(frontendChannel) {}
+
+DomainDispatcher::~DomainDispatcher() {
+ clearFrontend();
+}
+
+void DomainDispatcher::sendResponse(int call_id,
+ const DispatchResponse& response,
+ std::unique_ptr<Serializable> result) {
+ if (!frontend_channel_)
+ return;
+ std::unique_ptr<Serializable> serializable;
+ if (response.IsError()) {
+ serializable = CreateErrorResponse(call_id, response);
+ } else {
+ serializable = CreateResponse(call_id, std::move(result));
+ }
+ frontend_channel_->SendProtocolResponse(call_id, std::move(serializable));
+}
+
+bool DomainDispatcher::MaybeReportInvalidParams(
+ const Dispatchable& dispatchable,
+ const ErrorSupport& errors) {
+ if (errors.Errors().empty())
+ return false;
+ if (frontend_channel_) {
+ frontend_channel_->SendProtocolResponse(
+ dispatchable.CallId(),
+ CreateErrorResponse(
+ dispatchable.CallId(),
+ DispatchResponse::InvalidParams("Invalid parameters"), &errors));
+ }
+ return true;
+}
+
+void DomainDispatcher::clearFrontend() {
+ frontend_channel_ = nullptr;
+ for (auto& weak : weak_ptrs_)
+ weak->dispose();
+ weak_ptrs_.clear();
+}
+
+std::unique_ptr<DomainDispatcher::WeakPtr> DomainDispatcher::weakPtr() {
+ auto weak = std::make_unique<DomainDispatcher::WeakPtr>(this);
+ weak_ptrs_.insert(weak.get());
+ return weak;
+}
+
+// =============================================================================
+// UberDispatcher - dispatches between domains (backends).
+// =============================================================================
+UberDispatcher::DispatchResult::DispatchResult(bool method_found,
+ std::function<void()> runnable)
+ : method_found_(method_found), runnable_(runnable) {}
+
+void UberDispatcher::DispatchResult::Run() {
+ if (!runnable_)
+ return;
+ runnable_();
+ runnable_ = nullptr;
+}
+
+UberDispatcher::UberDispatcher(FrontendChannel* frontend_channel)
+ : frontend_channel_(frontend_channel) {
+ assert(frontend_channel);
+}
+
+UberDispatcher::~UberDispatcher() = default;
+
+constexpr size_t kNotFound = std::numeric_limits<size_t>::max();
+
+namespace {
+size_t DotIdx(span<uint8_t> method) {
+ const void* p = memchr(method.data(), '.', method.size());
+ return p ? reinterpret_cast<const uint8_t*>(p) - method.data() : kNotFound;
+}
+} // namespace
+
+UberDispatcher::DispatchResult UberDispatcher::Dispatch(
+ const Dispatchable& dispatchable) const {
+ span<uint8_t> method = FindByFirst(redirects_, dispatchable.Method(),
+ /*default_value=*/dispatchable.Method());
+ size_t dot_idx = DotIdx(method);
+ if (dot_idx != kNotFound) {
+ span<uint8_t> domain = method.subspan(0, dot_idx);
+ span<uint8_t> command = method.subspan(dot_idx + 1);
+ DomainDispatcher* dispatcher = FindByFirst(dispatchers_, domain);
+ if (dispatcher) {
+ std::function<void(const Dispatchable&)> dispatched =
+ dispatcher->Dispatch(command);
+ if (dispatched) {
+ return DispatchResult(
+ true, [dispatchable, dispatched = std::move(dispatched)]() {
+ dispatched(dispatchable);
+ });
+ }
+ }
+ }
+ return DispatchResult(false, [this, dispatchable]() {
+ frontend_channel_->SendProtocolResponse(
+ dispatchable.CallId(),
+ CreateErrorResponse(dispatchable.CallId(),
+ DispatchResponse::MethodNotFound(
+ "'" +
+ std::string(dispatchable.Method().begin(),
+ dispatchable.Method().end()) +
+ "' wasn't found")));
+ });
+}
+
+template <typename T>
+struct FirstLessThan {
+ bool operator()(const std::pair<span<uint8_t>, T>& left,
+ const std::pair<span<uint8_t>, T>& right) {
+ return SpanLessThan(left.first, right.first);
+ }
+};
+
+void UberDispatcher::WireBackend(
+ span<uint8_t> domain,
+ const std::vector<std::pair<span<uint8_t>, span<uint8_t>>>&
+ sorted_redirects,
+ std::unique_ptr<DomainDispatcher> dispatcher) {
+ auto it = redirects_.insert(redirects_.end(), sorted_redirects.begin(),
+ sorted_redirects.end());
+ std::inplace_merge(redirects_.begin(), it, redirects_.end(),
+ FirstLessThan<span<uint8_t>>());
+ auto jt = dispatchers_.insert(dispatchers_.end(),
+ std::make_pair(domain, std::move(dispatcher)));
+ std::inplace_merge(dispatchers_.begin(), jt, dispatchers_.end(),
+ FirstLessThan<std::unique_ptr<DomainDispatcher>>());
+}
+
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/dispatch.h b/chromium/third_party/inspector_protocol/crdtp/dispatch.h
new file mode 100644
index 00000000000..2de889abcd9
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/dispatch.h
@@ -0,0 +1,311 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRDTP_DISPATCH_H_
+#define CRDTP_DISPATCH_H_
+
+#include <cassert>
+#include <cstdint>
+#include <functional>
+#include <string>
+#include <unordered_set>
+#include "export.h"
+#include "serializable.h"
+#include "span.h"
+#include "status.h"
+
+namespace crdtp {
+class FrontendChannel;
+class ErrorSupport;
+namespace cbor {
+class CBORTokenizer;
+} // namespace cbor
+
+// =============================================================================
+// DispatchResponse - Error status and chaining / fall through
+// =============================================================================
+enum class DispatchCode {
+ SUCCESS = 1,
+ FALL_THROUGH = 2,
+ // For historical reasons, these error codes correspond to commonly used
+ // XMLRPC codes (e.g. see METHOD_NOT_FOUND in
+ // https://github.com/python/cpython/blob/master/Lib/xmlrpc/client.py).
+ PARSE_ERROR = -32700,
+ INVALID_REQUEST = -32600,
+ METHOD_NOT_FOUND = -32601,
+ INVALID_PARAMS = -32602,
+ INTERNAL_ERROR = -32603,
+ SERVER_ERROR = -32000,
+};
+
+// Information returned by command handlers. Usually returned after command
+// execution attempts.
+class CRDTP_EXPORT DispatchResponse {
+ public:
+ const std::string& Message() const { return message_; }
+
+ DispatchCode Code() const { return code_; }
+
+ bool IsSuccess() const { return code_ == DispatchCode::SUCCESS; }
+ bool IsFallThrough() const { return code_ == DispatchCode::FALL_THROUGH; }
+ bool IsError() const { return code_ < DispatchCode::SUCCESS; }
+
+ static DispatchResponse Success();
+ static DispatchResponse FallThrough();
+
+ // Indicates that a message could not be parsed. E.g., malformed JSON.
+ static DispatchResponse ParseError(std::string message);
+
+ // Indicates that a request is lacking required top-level properties
+ // ('id', 'method'), has top-level properties of the wrong type, or has
+ // unknown top-level properties.
+ static DispatchResponse InvalidRequest(std::string message);
+
+ // Indicates that a protocol method such as "Page.bringToFront" could not be
+ // dispatched because it's not known to the (domain) dispatcher.
+ static DispatchResponse MethodNotFound(std::string message);
+
+ // Indicates that the params sent to a domain handler are invalid.
+ static DispatchResponse InvalidParams(std::string message);
+
+ // Used for application level errors, e.g. within protocol agents.
+ static DispatchResponse InternalError();
+
+ // Used for application level errors, e.g. within protocol agents.
+ static DispatchResponse ServerError(std::string message);
+
+ private:
+ DispatchResponse() = default;
+ DispatchCode code_;
+ std::string message_;
+};
+
+// =============================================================================
+// Dispatchable - a shallow parser for CBOR encoded DevTools messages
+// =============================================================================
+
+// This parser extracts only the known top-level fields from a CBOR encoded map;
+// method, id, sessionId, and params.
+class CRDTP_EXPORT Dispatchable {
+ public:
+ // This constructor parses the |serialized| message. If successful,
+ // |ok()| will yield |true|, and |Method()|, |SessionId()|, |CallId()|,
+ // |Params()| can be used to access, the extracted contents. Otherwise,
+ // |ok()| will yield |false|, and |DispatchError()| can be
+ // used to send a response or notification to the client.
+ explicit Dispatchable(span<uint8_t> serialized);
+
+ // The serialized message that we just parsed.
+ span<uint8_t> Serialized() const { return serialized_; }
+
+ // Yields true if parsing was successful. This is cheaper than calling
+ // ::DispatchError().
+ bool ok() const;
+
+ // If !ok(), returns a DispatchResponse with appropriate code and error
+ // which can be sent to the client as a response or notification.
+ DispatchResponse DispatchError() const;
+
+ // Top level field: the command to be executed, fully qualified by
+ // domain. E.g. "Page.createIsolatedWorld".
+ span<uint8_t> Method() const { return method_; }
+ // Used to identify protocol connections attached to a specific
+ // target. See Target.attachToTarget, Target.setAutoAttach.
+ span<uint8_t> SessionId() const { return session_id_; }
+ // The call id, a sequence number that's used in responses to indicate
+ // the request to which the response belongs.
+ int32_t CallId() const { return call_id_; }
+ bool HasCallId() const { return has_call_id_; }
+ // The payload of the request in CBOR format. The |Dispatchable| parser does
+ // not parse into this; it only provides access to its raw contents here.
+ span<uint8_t> Params() const { return params_; }
+
+ private:
+ bool MaybeParseProperty(cbor::CBORTokenizer* tokenizer);
+ bool MaybeParseCallId(cbor::CBORTokenizer* tokenizer);
+ bool MaybeParseMethod(cbor::CBORTokenizer* tokenizer);
+ bool MaybeParseParams(cbor::CBORTokenizer* tokenizer);
+ bool MaybeParseSessionId(cbor::CBORTokenizer* tokenizer);
+
+ span<uint8_t> serialized_;
+
+ Status status_;
+
+ bool has_call_id_ = false;
+ int32_t call_id_;
+ span<uint8_t> method_;
+ bool params_seen_ = false;
+ span<uint8_t> params_;
+ span<uint8_t> session_id_;
+};
+
+// =============================================================================
+// Helpers for creating protocol cresponses and notifications.
+// =============================================================================
+
+// The resulting notifications can be sent to a protocol client,
+// usually via a FrontendChannel (see frontend_channel.h).
+
+CRDTP_EXPORT std::unique_ptr<Serializable> CreateErrorResponse(
+ int callId,
+ DispatchResponse dispatch_response,
+ const ErrorSupport* errors = nullptr);
+
+CRDTP_EXPORT std::unique_ptr<Serializable> CreateErrorNotification(
+ DispatchResponse dispatch_response);
+
+CRDTP_EXPORT std::unique_ptr<Serializable> CreateResponse(
+ int callId,
+ std::unique_ptr<Serializable> params);
+
+CRDTP_EXPORT std::unique_ptr<Serializable> CreateNotification(
+ const char* method,
+ std::unique_ptr<Serializable> params = nullptr);
+
+// =============================================================================
+// DomainDispatcher - Dispatching betwen protocol methods within a domain.
+// =============================================================================
+
+// This class is subclassed by |DomainDispatcherImpl|, which we generate per
+// DevTools domain. It contains routines called from the generated code,
+// e.g. ::MaybeReportInvalidParams, which are optimized for small code size.
+// The most important method is ::Dispatch, which implements method dispatch
+// by command name lookup.
+class CRDTP_EXPORT DomainDispatcher {
+ public:
+ class CRDTP_EXPORT WeakPtr {
+ public:
+ explicit WeakPtr(DomainDispatcher*);
+ ~WeakPtr();
+ DomainDispatcher* get() { return dispatcher_; }
+ void dispose() { dispatcher_ = nullptr; }
+
+ private:
+ DomainDispatcher* dispatcher_;
+ };
+
+ class CRDTP_EXPORT Callback {
+ public:
+ virtual ~Callback();
+ void dispose();
+
+ protected:
+ // |method| must point at static storage (a C++ string literal in practice).
+ Callback(std::unique_ptr<WeakPtr> backend_impl,
+ int call_id,
+ span<uint8_t> method,
+ span<uint8_t> message);
+
+ void sendIfActive(std::unique_ptr<Serializable> partialMessage,
+ const DispatchResponse& response);
+ void fallThroughIfActive();
+
+ private:
+ std::unique_ptr<WeakPtr> backend_impl_;
+ int call_id_;
+ // Subclasses of this class are instantiated from generated code which
+ // passes a string literal for the method name to the constructor. So the
+ // storage for |method| is the binary of the running process.
+ span<uint8_t> method_;
+ std::vector<uint8_t> message_;
+ };
+
+ explicit DomainDispatcher(FrontendChannel*);
+ virtual ~DomainDispatcher();
+
+ // Given a |command_name| without domain qualification, looks up the
+ // corresponding method. If the method is not found, returns nullptr.
+ // Otherwise, Returns a closure that will parse the provided
+ // Dispatchable.params() to a protocol object and execute the
+ // apprpropriate method. If the parsing fails it will issue an
+ // error response on the frontend channel, otherwise it will execute the
+ // command.
+ virtual std::function<void(const Dispatchable&)> Dispatch(
+ span<uint8_t> command_name) = 0;
+
+ // Sends a response to the client via the channel.
+ void sendResponse(int call_id,
+ const DispatchResponse&,
+ std::unique_ptr<Serializable> result = nullptr);
+
+ // Returns true if |errors| contains errors *and* reports these errors
+ // as a response on the frontend channel. Called from generated code,
+ // optimized for code size of the callee.
+ bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
+ const ErrorSupport& errors);
+
+ FrontendChannel* channel() { return frontend_channel_; }
+
+ void clearFrontend();
+
+ std::unique_ptr<WeakPtr> weakPtr();
+
+ private:
+ FrontendChannel* frontend_channel_;
+ std::unordered_set<WeakPtr*> weak_ptrs_;
+};
+
+// =============================================================================
+// UberDispatcher - dispatches between domains (backends).
+// =============================================================================
+class CRDTP_EXPORT UberDispatcher {
+ public:
+ // Return type for ::Dispatch.
+ class CRDTP_EXPORT DispatchResult {
+ public:
+ DispatchResult(bool method_found, std::function<void()> runnable);
+
+ // Indicates whether the method was found, that is, it could be dispatched
+ // to a backend registered with this dispatcher.
+ bool MethodFound() const { return method_found_; }
+
+ // Runs the dispatched result. This will send the appropriate error
+ // responses if the method wasn't found or if something went wrong during
+ // parameter parsing.
+ void Run();
+
+ private:
+ bool method_found_;
+ std::function<void()> runnable_;
+ };
+
+ // |frontend_hannel| can't be nullptr.
+ explicit UberDispatcher(FrontendChannel* frontend_channel);
+ virtual ~UberDispatcher();
+
+ // Dispatches the provided |dispatchable| considering all redirects and domain
+ // handlers registered with this uber dispatcher. Also see |DispatchResult|.
+ // |dispatchable.ok()| must hold - callers must check this separately and
+ // deal with errors.
+ DispatchResult Dispatch(const Dispatchable& dispatchable) const;
+
+ // Invoked from generated code for wiring domain backends; that is,
+ // connecting domain handlers to an uber dispatcher.
+ // See <domain-namespace>::Dispatcher::Wire(UberDispatcher*,Backend*).
+ FrontendChannel* channel() const {
+ assert(frontend_channel_);
+ return frontend_channel_;
+ }
+
+ // Invoked from generated code for wiring domain backends; that is,
+ // connecting domain handlers to an uber dispatcher.
+ // See <domain-namespace>::Dispatcher::Wire(UberDispatcher*,Backend*).
+ void WireBackend(span<uint8_t> domain,
+ const std::vector<std::pair<span<uint8_t>, span<uint8_t>>>&,
+ std::unique_ptr<DomainDispatcher> dispatcher);
+
+ private:
+ DomainDispatcher* findDispatcher(span<uint8_t> method);
+ FrontendChannel* const frontend_channel_;
+ // Pairs of ascii strings of the form ("Domain1.method1","Domain2.method2")
+ // indicating that the first element of each pair redirects to the second.
+ // Sorted by first element.
+ std::vector<std::pair<span<uint8_t>, span<uint8_t>>> redirects_;
+ // Domain dispatcher instances, sorted by their domain name.
+ std::vector<std::pair<span<uint8_t>, std::unique_ptr<DomainDispatcher>>>
+ dispatchers_;
+};
+} // namespace crdtp
+
+#endif // CRDTP_DISPATCH_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/dispatch_test.cc b/chromium/third_party/inspector_protocol/crdtp/dispatch_test.cc
new file mode 100644
index 00000000000..d0c36dfed8a
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/dispatch_test.cc
@@ -0,0 +1,445 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "cbor.h"
+#include "dispatch.h"
+#include "error_support.h"
+#include "frontend_channel.h"
+#include "json.h"
+#include "test_platform.h"
+
+namespace crdtp {
+// =============================================================================
+// DispatchResponse - Error status and chaining / fall through
+// =============================================================================
+TEST(DispatchResponseTest, OK) {
+ EXPECT_EQ(DispatchCode::SUCCESS, DispatchResponse::Success().Code());
+ EXPECT_TRUE(DispatchResponse::Success().IsSuccess());
+}
+
+TEST(DispatchResponseTest, ServerError) {
+ DispatchResponse error = DispatchResponse::ServerError("Oops!");
+ EXPECT_FALSE(error.IsSuccess());
+ EXPECT_EQ(DispatchCode::SERVER_ERROR, error.Code());
+ EXPECT_EQ("Oops!", error.Message());
+}
+
+TEST(DispatchResponseTest, InternalError) {
+ DispatchResponse error = DispatchResponse::InternalError();
+ EXPECT_FALSE(error.IsSuccess());
+ EXPECT_EQ(DispatchCode::INTERNAL_ERROR, error.Code());
+ EXPECT_EQ("Internal error", error.Message());
+}
+
+TEST(DispatchResponseTest, InvalidParams) {
+ DispatchResponse error = DispatchResponse::InvalidParams("too cool");
+ EXPECT_FALSE(error.IsSuccess());
+ EXPECT_EQ(DispatchCode::INVALID_PARAMS, error.Code());
+ EXPECT_EQ("too cool", error.Message());
+}
+
+TEST(DispatchResponseTest, FallThrough) {
+ DispatchResponse error = DispatchResponse::FallThrough();
+ EXPECT_FALSE(error.IsSuccess());
+ EXPECT_TRUE(error.IsFallThrough());
+ EXPECT_EQ(DispatchCode::FALL_THROUGH, error.Code());
+}
+
+// =============================================================================
+// Dispatchable - a shallow parser for CBOR encoded DevTools messages
+// =============================================================================
+TEST(DispatchableTest, MessageMustBeAnObject) {
+ // Provide no input whatsoever.
+ span<uint8_t> empty_span;
+ Dispatchable empty(empty_span);
+ EXPECT_FALSE(empty.ok());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, empty.DispatchError().Code());
+ EXPECT_EQ("Message must be an object", empty.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageMustHaveIntegerIdProperty) {
+ // Construct an empty map inside of an envelope.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom("{}"), &cbor).ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_FALSE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ("Message must have integer 'id' property",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageMustHaveIntegerIdProperty_IncorrectType) {
+ // This time we set the id property, but fail to make it an int32.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(
+ json::ConvertJSONToCBOR(SpanFrom("{\"id\":\"foo\"}"), &cbor).ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_FALSE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ("Message must have integer 'id' property",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageMustHaveStringMethodProperty) {
+ // This time we set the id property, but not the method property.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom("{\"id\":42}"), &cbor).ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ("Message must have string 'method' property",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageMustHaveStringMethodProperty_IncorrectType) {
+ // This time we set the method property, but fail to make it a string.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(
+ json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":42}"), &cbor)
+ .ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ("Message must have string 'method' property",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageMayHaveStringSessionIdProperty) {
+ // This time, the session id is an int but it should be a string. Method and
+ // call id are present.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(json::ConvertJSONToCBOR(
+ SpanFrom("{\"id\":42,\"method\":\"Foo.executeBar\","
+ "\"sessionId\":42" // int32 is wrong type
+ "}"),
+ &cbor)
+ .ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ("Message may have string 'sessionId' property",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageMayHaveObjectParamsProperty) {
+ // This time, we fail to use the correct type for the params property.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(json::ConvertJSONToCBOR(
+ SpanFrom("{\"id\":42,\"method\":\"Foo.executeBar\","
+ "\"params\":42" // int32 is wrong type
+ "}"),
+ &cbor)
+ .ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ("Message may have object 'params' property",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, MessageWithUnknownProperty) {
+ // This time we set the 'unknown' property, so we are told what's allowed.
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(
+ json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"unknown\":42}"), &cbor)
+ .ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
+ EXPECT_EQ(
+ "Message has property other than 'id', 'method', 'sessionId', 'params'",
+ dispatchable.DispatchError().Message());
+}
+
+TEST(DispatchableTest, DuplicateMapKey) {
+ for (const std::string& json :
+ {"{\"id\":42,\"id\":42}", "{\"params\":null,\"params\":null}",
+ "{\"method\":\"foo\",\"method\":\"foo\"}",
+ "{\"sessionId\":\"42\",\"sessionId\":\"42\"}"}) {
+ SCOPED_TRACE("json = " + json);
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom(json), &cbor).ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_EQ(DispatchCode::PARSE_ERROR, dispatchable.DispatchError().Code());
+ EXPECT_THAT(dispatchable.DispatchError().Message(),
+ testing::StartsWith("CBOR: duplicate map key at position "));
+ }
+}
+
+TEST(DispatchableTest, ValidMessageParsesOK_NoParams) {
+ for (const std::string& json :
+ {"{\"id\":42,\"method\":\"Foo.executeBar\",\"sessionId\":"
+ "\"f421ssvaz4\"}",
+ "{\"id\":42,\"method\":\"Foo.executeBar\",\"sessionId\":\"f421ssvaz4\","
+ "\"params\":null}"}) {
+ SCOPED_TRACE("json = " + json);
+ std::vector<uint8_t> cbor;
+ ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom(json), &cbor).ok());
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_TRUE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(42, dispatchable.CallId());
+ EXPECT_EQ("Foo.executeBar", std::string(dispatchable.Method().begin(),
+ dispatchable.Method().end()));
+ EXPECT_EQ("f421ssvaz4", std::string(dispatchable.SessionId().begin(),
+ dispatchable.SessionId().end()));
+ EXPECT_TRUE(dispatchable.Params().empty());
+ }
+}
+
+TEST(DispatchableTest, ValidMessageParsesOK_WithParams) {
+ std::vector<uint8_t> cbor;
+ cbor::EnvelopeEncoder envelope;
+ envelope.EncodeStart(&cbor);
+ cbor.push_back(cbor::EncodeIndefiniteLengthMapStart());
+ cbor::EncodeString8(SpanFrom("id"), &cbor);
+ cbor::EncodeInt32(42, &cbor);
+ cbor::EncodeString8(SpanFrom("method"), &cbor);
+ cbor::EncodeString8(SpanFrom("Foo.executeBar"), &cbor);
+ cbor::EncodeString8(SpanFrom("params"), &cbor);
+ cbor::EnvelopeEncoder params_envelope;
+ params_envelope.EncodeStart(&cbor);
+ // The |Dispatchable| class does not parse into the "params" envelope,
+ // so we can stick anything into there for the purpose of this test.
+ // For convenience, we use a String8.
+ cbor::EncodeString8(SpanFrom("params payload"), &cbor);
+ params_envelope.EncodeStop(&cbor);
+ cbor::EncodeString8(SpanFrom("sessionId"), &cbor);
+ cbor::EncodeString8(SpanFrom("f421ssvaz4"), &cbor);
+ cbor.push_back(cbor::EncodeStop());
+ envelope.EncodeStop(&cbor);
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_TRUE(dispatchable.ok());
+ EXPECT_TRUE(dispatchable.HasCallId());
+ EXPECT_EQ(42, dispatchable.CallId());
+ EXPECT_EQ("Foo.executeBar", std::string(dispatchable.Method().begin(),
+ dispatchable.Method().end()));
+ EXPECT_EQ("f421ssvaz4", std::string(dispatchable.SessionId().begin(),
+ dispatchable.SessionId().end()));
+ cbor::CBORTokenizer params_tokenizer(dispatchable.Params());
+ ASSERT_EQ(cbor::CBORTokenTag::ENVELOPE, params_tokenizer.TokenTag());
+ params_tokenizer.EnterEnvelope();
+ ASSERT_EQ(cbor::CBORTokenTag::STRING8, params_tokenizer.TokenTag());
+ EXPECT_EQ("params payload", std::string(params_tokenizer.GetString8().begin(),
+ params_tokenizer.GetString8().end()));
+}
+
+TEST(DispatchableTest, FaultyCBORTrailingJunk) {
+ // In addition to the higher level parsing errors, we also catch CBOR
+ // structural corruption. E.g., in this case, the message would be
+ // OK but has some extra trailing bytes.
+ std::vector<uint8_t> cbor;
+ cbor::EnvelopeEncoder envelope;
+ envelope.EncodeStart(&cbor);
+ cbor.push_back(cbor::EncodeIndefiniteLengthMapStart());
+ cbor::EncodeString8(SpanFrom("id"), &cbor);
+ cbor::EncodeInt32(42, &cbor);
+ cbor::EncodeString8(SpanFrom("method"), &cbor);
+ cbor::EncodeString8(SpanFrom("Foo.executeBar"), &cbor);
+ cbor::EncodeString8(SpanFrom("sessionId"), &cbor);
+ cbor::EncodeString8(SpanFrom("f421ssvaz4"), &cbor);
+ cbor.push_back(cbor::EncodeStop());
+ envelope.EncodeStop(&cbor);
+ size_t trailing_junk_pos = cbor.size();
+ cbor.push_back('t');
+ cbor.push_back('r');
+ cbor.push_back('a');
+ cbor.push_back('i');
+ cbor.push_back('l');
+ Dispatchable dispatchable(SpanFrom(cbor));
+ EXPECT_FALSE(dispatchable.ok());
+ EXPECT_EQ(DispatchCode::PARSE_ERROR, dispatchable.DispatchError().Code());
+ EXPECT_EQ(56u, trailing_junk_pos);
+ EXPECT_EQ("CBOR: trailing junk at position 56",
+ dispatchable.DispatchError().Message());
+}
+
+// =============================================================================
+// Helpers for creating protocol cresponses and notifications.
+// =============================================================================
+TEST(CreateErrorResponseTest, SmokeTest) {
+ ErrorSupport errors;
+ errors.Push();
+ errors.SetName("foo");
+ errors.Push();
+ errors.SetName("bar");
+ errors.AddError("expected a string");
+ errors.SetName("baz");
+ errors.AddError("expected a surprise");
+ auto serializable = CreateErrorResponse(
+ 42, DispatchResponse::InvalidParams("invalid params message"), &errors);
+ std::string json;
+ auto status =
+ json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(
+ "{\"id\":42,\"error\":"
+ "{\"code\":-32602,"
+ "\"message\":\"invalid params message\","
+ "\"data\":\"foo.bar: expected a string; "
+ "foo.baz: expected a surprise\"}}",
+ json);
+}
+
+TEST(CreateErrorNotificationTest, SmokeTest) {
+ auto serializable =
+ CreateErrorNotification(DispatchResponse::InvalidRequest("oops!"));
+ std::string json;
+ auto status =
+ json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ("{\"error\":{\"code\":-32600,\"message\":\"oops!\"}}", json);
+}
+
+TEST(CreateResponseTest, SmokeTest) {
+ auto serializable = CreateResponse(42, nullptr);
+ std::string json;
+ auto status =
+ json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ("{\"id\":42,\"result\":{}}", json);
+}
+
+TEST(CreateNotificationTest, SmokeTest) {
+ auto serializable = CreateNotification("Foo.bar");
+ std::string json;
+ auto status =
+ json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ("{\"method\":\"Foo.bar\",\"params\":{}}", json);
+}
+
+// =============================================================================
+// UberDispatcher - dispatches between domains (backends).
+// =============================================================================
+class TestChannel : public FrontendChannel {
+ public:
+ std::string JSON() const {
+ std::string json;
+ json::ConvertCBORToJSON(SpanFrom(cbor_), &json);
+ return json;
+ }
+
+ private:
+ void SendProtocolResponse(int call_id,
+ std::unique_ptr<Serializable> message) override {
+ cbor_ = message->Serialize();
+ }
+
+ void SendProtocolNotification(
+ std::unique_ptr<Serializable> message) override {
+ cbor_ = message->Serialize();
+ }
+
+ void FallThrough(int call_id,
+ span<uint8_t> method,
+ span<uint8_t> message) override {}
+
+ void FlushProtocolNotifications() override {}
+
+ std::vector<uint8_t> cbor_;
+};
+
+TEST(UberDispatcherTest, MethodNotFound) {
+ // No domain dispatchers are registered, so unsuprisingly, we'll get a method
+ // not found error and can see that DispatchResult::MethodFound() yields
+ // false.
+ TestChannel channel;
+ UberDispatcher dispatcher(&channel);
+ std::vector<uint8_t> message;
+ json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":\"Foo.bar\"}"),
+ &message);
+ Dispatchable dispatchable(SpanFrom(message));
+ ASSERT_TRUE(dispatchable.ok());
+ UberDispatcher::DispatchResult dispatched = dispatcher.Dispatch(dispatchable);
+ EXPECT_FALSE(dispatched.MethodFound());
+ dispatched.Run();
+ EXPECT_EQ(
+ "{\"id\":42,\"error\":"
+ "{\"code\":-32601,\"message\":\"'Foo.bar' wasn't found\"}}",
+ channel.JSON());
+}
+
+// A domain dispatcher which captured dispatched and executed commands in fields
+// for testing.
+class TestDomain : public DomainDispatcher {
+ public:
+ explicit TestDomain(FrontendChannel* channel) : DomainDispatcher(channel) {}
+
+ std::function<void(const Dispatchable&)> Dispatch(
+ span<uint8_t> command_name) override {
+ dispatched_commands_.push_back(
+ std::string(command_name.begin(), command_name.end()));
+ return [this](const Dispatchable& dispatchable) {
+ executed_commands_.push_back(dispatchable.CallId());
+ };
+ }
+
+ // Command names of the dispatched commands.
+ std::vector<std::string> DispatchedCommands() const {
+ return dispatched_commands_;
+ }
+
+ // Call ids of the executed commands.
+ std::vector<int32_t> ExecutedCommands() const { return executed_commands_; }
+
+ private:
+ std::vector<std::string> dispatched_commands_;
+ std::vector<int32_t> executed_commands_;
+};
+
+TEST(UberDispatcherTest, DispatchingToDomainWithRedirects) {
+ // This time, we register two domain dispatchers (Foo and Bar) and issue one
+ // command 'Foo.execute' which executes on Foo and one command 'Foo.redirect'
+ // which executes as 'Bar.redirected'.
+ TestChannel channel;
+ UberDispatcher dispatcher(&channel);
+ auto foo_dispatcher = std::make_unique<TestDomain>(&channel);
+ TestDomain* foo = foo_dispatcher.get();
+ auto bar_dispatcher = std::make_unique<TestDomain>(&channel);
+ TestDomain* bar = bar_dispatcher.get();
+
+ dispatcher.WireBackend(
+ SpanFrom("Foo"), {{SpanFrom("Foo.redirect"), SpanFrom("Bar.redirected")}},
+ std::move(foo_dispatcher));
+ dispatcher.WireBackend(SpanFrom("Bar"), {}, std::move(bar_dispatcher));
+
+ {
+ std::vector<uint8_t> message;
+ json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":\"Foo.execute\"}"),
+ &message);
+ Dispatchable dispatchable(SpanFrom(message));
+ ASSERT_TRUE(dispatchable.ok());
+ UberDispatcher::DispatchResult dispatched =
+ dispatcher.Dispatch(dispatchable);
+ EXPECT_TRUE(dispatched.MethodFound());
+ dispatched.Run();
+ }
+ {
+ std::vector<uint8_t> message;
+ json::ConvertJSONToCBOR(SpanFrom("{\"id\":43,\"method\":\"Foo.redirect\"}"),
+ &message);
+ Dispatchable dispatchable(SpanFrom(message));
+ ASSERT_TRUE(dispatchable.ok());
+ UberDispatcher::DispatchResult dispatched =
+ dispatcher.Dispatch(dispatchable);
+ EXPECT_TRUE(dispatched.MethodFound());
+ dispatched.Run();
+ }
+ EXPECT_THAT(foo->DispatchedCommands(), testing::ElementsAre("execute"));
+ EXPECT_THAT(foo->ExecutedCommands(), testing::ElementsAre(42));
+ EXPECT_THAT(bar->DispatchedCommands(), testing::ElementsAre("redirected"));
+ EXPECT_THAT(bar->ExecutedCommands(), testing::ElementsAre(43));
+}
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/error_support.cc b/chromium/third_party/inspector_protocol/crdtp/error_support.cc
new file mode 100644
index 00000000000..9f177523d0c
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/error_support.cc
@@ -0,0 +1,59 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "error_support.h"
+
+#include <cassert>
+
+namespace crdtp {
+
+void ErrorSupport::Push() {
+ stack_.emplace_back();
+}
+
+void ErrorSupport::Pop() {
+ stack_.pop_back();
+}
+
+void ErrorSupport::SetName(const char* name) {
+ assert(!stack_.empty());
+ stack_.back().type = NAME;
+ stack_.back().name = name;
+}
+
+void ErrorSupport::SetIndex(size_t index) {
+ assert(!stack_.empty());
+ stack_.back().type = INDEX;
+ stack_.back().index = index;
+}
+
+void ErrorSupport::AddError(const char* error) {
+ assert(!stack_.empty());
+ if (!errors_.empty())
+ errors_ += "; ";
+ for (size_t ii = 0; ii < stack_.size(); ++ii) {
+ if (ii)
+ errors_ += ".";
+ const Segment& s = stack_[ii];
+ switch (s.type) {
+ case NAME:
+ errors_ += s.name;
+ continue;
+ case INDEX:
+ errors_ += std::to_string(s.index);
+ continue;
+ default:
+ assert(s.type != EMPTY);
+ continue;
+ }
+ }
+ errors_ += ": ";
+ errors_ += error;
+}
+
+span<uint8_t> ErrorSupport::Errors() const {
+ return SpanFrom(errors_);
+}
+
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/error_support.h b/chromium/third_party/inspector_protocol/crdtp/error_support.h
new file mode 100644
index 00000000000..989dc9c64eb
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/error_support.h
@@ -0,0 +1,62 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRDTP_ERROR_SUPPORT_H_
+#define CRDTP_ERROR_SUPPORT_H_
+
+#include <cstdint>
+#include <string>
+#include <vector>
+#include "export.h"
+#include "span.h"
+
+namespace crdtp {
+// =============================================================================
+// ErrorSupport - For tracking errors in tree structures.
+// =============================================================================
+
+// This abstraction is used when converting between Values and inspector
+// objects, e.g. in lib/ValueConversions_{h,cc}.template. As the processing
+// enters and exits a branch, we call Push / Pop. Within the branch,
+// we either set the name or an index (in case we're processing the element of a
+// list/vector). Only once an error is seen, the path which is now on the
+// stack is materialized and prefixes the error message. E.g.,
+// "foo.bar.2: some error". After error collection, ::Errors() is used to
+// access the message.
+class CRDTP_EXPORT ErrorSupport {
+ public:
+ // Push / Pop operations for the path segments; after Push, either SetName or
+ // SetIndex must be called exactly once.
+ void Push();
+ void Pop();
+
+ // Sets the name of the current segment on the stack; e.g. a field name.
+ // |name| must be a C++ string literal in 7 bit US-ASCII.
+ void SetName(const char* name);
+ // Sets the index of the current segment on the stack; e.g. an array index.
+ void SetIndex(size_t index);
+
+ // Materializes the error internally. |error| must be a C++ string literal
+ // in 7 bit US-ASCII.
+ void AddError(const char* error);
+
+ // Returns the semicolon-separated list of errors as in 7 bit ASCII.
+ span<uint8_t> Errors() const;
+
+ private:
+ enum SegmentType { EMPTY, NAME, INDEX };
+ struct Segment {
+ SegmentType type = EMPTY;
+ union {
+ const char* name;
+ size_t index;
+ };
+ };
+ std::vector<Segment> stack_;
+ std::string errors_;
+};
+
+} // namespace crdtp
+
+#endif // CRDTP_ERROR_SUPPORT_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/error_support_test.cc b/chromium/third_party/inspector_protocol/crdtp/error_support_test.cc
new file mode 100644
index 00000000000..fb7ec9954e6
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/error_support_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "error_support.h"
+
+#include <string>
+#include <vector>
+
+#include "test_platform.h"
+
+namespace crdtp {
+TEST(ErrorSupportTest, Empty) {
+ ErrorSupport errors;
+ EXPECT_TRUE(errors.Errors().empty());
+}
+
+TEST(ErrorSupportTest, Nesting) {
+ ErrorSupport errors;
+ // Enter field foo, inter element at index 42, enter field bar, and encounter
+ // an error there ("something wrong").
+ errors.Push();
+ errors.SetName("foo");
+ errors.Push();
+ errors.SetIndex(42);
+ errors.Push();
+ errors.SetName("bar_sibling");
+ errors.SetName("bar");
+ errors.AddError("something wrong");
+ errors.Pop(); // bar
+ errors.Pop(); // 42
+ // The common case is actually that we'll enter some field, set the name
+ // or index, and leave without ever producing an error.
+ errors.Push();
+ errors.SetName("no_error_here");
+ errors.Pop(); // no_error_here
+ errors.Push();
+ errors.SetName("bang");
+ errors.AddError("one last error");
+ errors.Pop(); // bang
+ errors.Pop(); // foo
+ std::string out(errors.Errors().begin(), errors.Errors().end());
+ EXPECT_EQ("foo.42.bar: something wrong; foo.bang: one last error", out);
+}
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/find_by_first.h b/chromium/third_party/inspector_protocol/crdtp/find_by_first.h
new file mode 100644
index 00000000000..836ac60e988
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/find_by_first.h
@@ -0,0 +1,58 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRDTP_FIND_BY_FIRST_H_
+#define CRDTP_FIND_BY_FIRST_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "export.h"
+#include "span.h"
+
+namespace crdtp {
+// =============================================================================
+// FindByFirst - Retrieval from a sorted vector that's keyed by span<uint8_t>.
+// =============================================================================
+
+// Given a vector of pairs sorted by the first element of each pair, find
+// the corresponding value given a key to be compared to the first element.
+// Together with std::inplace_merge and pre-sorting or std::sort, this can
+// be used to implement a minimalistic equivalent of Chromium's flat_map.
+
+// In this variant, the template parameter |T| is a value type and a
+// |default_value| is provided.
+template <typename T>
+T FindByFirst(const std::vector<std::pair<span<uint8_t>, T>>& sorted_by_first,
+ span<uint8_t> key,
+ T default_value) {
+ auto it = std::lower_bound(
+ sorted_by_first.begin(), sorted_by_first.end(), key,
+ [](const std::pair<span<uint8_t>, T>& left, span<uint8_t> right) {
+ return SpanLessThan(left.first, right);
+ });
+ return (it != sorted_by_first.end() && SpanEquals(it->first, key))
+ ? it->second
+ : default_value;
+}
+
+// In this variant, the template parameter |T| is a class or struct that's
+// instantiated in std::unique_ptr, and we return either a T* or a nullptr.
+template <typename T>
+T* FindByFirst(const std::vector<std::pair<span<uint8_t>, std::unique_ptr<T>>>&
+ sorted_by_first,
+ span<uint8_t> key) {
+ auto it = std::lower_bound(
+ sorted_by_first.begin(), sorted_by_first.end(), key,
+ [](const std::pair<span<uint8_t>, std::unique_ptr<T>>& left,
+ span<uint8_t> right) { return SpanLessThan(left.first, right); });
+ return (it != sorted_by_first.end() && SpanEquals(it->first, key))
+ ? it->second.get()
+ : nullptr;
+}
+} // namespace crdtp
+
+#endif // CRDTP_FIND_BY_FIRST_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/find_by_first_test.cc b/chromium/third_party/inspector_protocol/crdtp/find_by_first_test.cc
new file mode 100644
index 00000000000..8e00b3e298e
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/find_by_first_test.cc
@@ -0,0 +1,76 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "find_by_first.h"
+#include "test_platform.h"
+
+namespace crdtp {
+// =============================================================================
+// FindByFirst - Efficient retrieval from a sorted vector.
+// =============================================================================
+TEST(FindByFirst, SpanBySpan) {
+ std::vector<std::pair<span<uint8_t>, span<uint8_t>>> sorted_span_by_span = {
+ {SpanFrom("foo1"), SpanFrom("bar1")},
+ {SpanFrom("foo2"), SpanFrom("bar2")},
+ {SpanFrom("foo3"), SpanFrom("bar3")},
+ };
+ {
+ auto result = FindByFirst(sorted_span_by_span, SpanFrom("foo1"),
+ SpanFrom("not_found"));
+ EXPECT_EQ("bar1", std::string(result.begin(), result.end()));
+ }
+ {
+ auto result = FindByFirst(sorted_span_by_span, SpanFrom("foo3"),
+ SpanFrom("not_found"));
+ EXPECT_EQ("bar3", std::string(result.begin(), result.end()));
+ }
+ {
+ auto result = FindByFirst(sorted_span_by_span, SpanFrom("baz"),
+ SpanFrom("not_found"));
+ EXPECT_EQ("not_found", std::string(result.begin(), result.end()));
+ }
+}
+
+namespace {
+class TestObject {
+ public:
+ explicit TestObject(const std::string& message) : message_(message) {}
+
+ const std::string& message() const { return message_; }
+
+ private:
+ std::string message_;
+};
+} // namespace
+
+TEST(FindByFirst, ObjectBySpan) {
+ std::vector<std::pair<span<uint8_t>, std::unique_ptr<TestObject>>>
+ sorted_object_by_span;
+ sorted_object_by_span.push_back(
+ std::make_pair(SpanFrom("foo1"), std::make_unique<TestObject>("bar1")));
+ sorted_object_by_span.push_back(
+ std::make_pair(SpanFrom("foo2"), std::make_unique<TestObject>("bar2")));
+ sorted_object_by_span.push_back(
+ std::make_pair(SpanFrom("foo3"), std::make_unique<TestObject>("bar3")));
+ {
+ TestObject* result =
+ FindByFirst<TestObject>(sorted_object_by_span, SpanFrom("foo1"));
+ ASSERT_TRUE(result);
+ ASSERT_EQ("bar1", result->message());
+ }
+ {
+ TestObject* result =
+ FindByFirst<TestObject>(sorted_object_by_span, SpanFrom("foo3"));
+ ASSERT_TRUE(result);
+ ASSERT_EQ("bar3", result->message());
+ }
+ {
+ TestObject* result =
+ FindByFirst<TestObject>(sorted_object_by_span, SpanFrom("baz"));
+ ASSERT_FALSE(result);
+ }
+}
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/frontend_channel.h b/chromium/third_party/inspector_protocol/crdtp/frontend_channel.h
new file mode 100644
index 00000000000..143eeb48f3f
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/frontend_channel.h
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRDTP_FRONTEND_CHANNEL_H_
+#define CRDTP_FRONTEND_CHANNEL_H_
+
+#include <cstdint>
+#include <memory>
+#include "export.h"
+#include "serializable.h"
+#include "span.h"
+
+namespace crdtp {
+// =============================================================================
+// FrontendChannel - For sending notifications and responses to protocol clients
+// =============================================================================
+class CRDTP_EXPORT FrontendChannel {
+ public:
+ virtual ~FrontendChannel() = default;
+
+ // Sends protocol responses and notifications. The |call_id| parameter is
+ // seemingly redundant because it's also included in the message, but
+ // responses may be sent from an untrusted source to a trusted process (e.g.
+ // from Chromium's renderer (blink) to the browser process), which needs
+ // to be able to match the response to an earlier request without parsing the
+ // messsage.
+ virtual void SendProtocolResponse(int call_id,
+ std::unique_ptr<Serializable> message) = 0;
+ virtual void SendProtocolNotification(
+ std::unique_ptr<Serializable> message) = 0;
+
+ // FallThrough indicates that |message| should be handled in another layer.
+ // Usually this means the layer responding to the message didn't handle it,
+ // but in some cases messages are handled by multiple layers (e.g. both
+ // the embedder and the content layer in Chromium).
+ virtual void FallThrough(int call_id,
+ span<uint8_t> method,
+ span<uint8_t> message) = 0;
+
+ // Session implementations may queue notifications for performance or
+ // other considerations; this is a hook for domain handlers to manually flush.
+ virtual void FlushProtocolNotifications() = 0;
+};
+} // namespace crdtp
+
+#endif // CRDTP_FRONTEND_CHANNEL_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/json.cc b/chromium/third_party/inspector_protocol/crdtp/json.cc
index d35b57daac9..5b9357835bd 100644
--- a/chromium/third_party/inspector_protocol/crdtp/json.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/json.cc
@@ -225,14 +225,17 @@ class JSONEncoder : public ParserHandler {
// belonging to this Unicode character into |codepoint|.
if (ii + num_bytes_left >= chars.size())
continue;
+ bool invalid_byte_seen = false;
while (num_bytes_left > 0) {
c = chars[++ii];
--num_bytes_left;
// Check the next byte is a continuation byte, that is 10xx xxxx.
if ((c & 0xc0) != 0x80)
- continue;
+ invalid_byte_seen = true;
codepoint = (codepoint << 6) | (c & 0x3f);
}
+ if (invalid_byte_seen)
+ continue;
// Disallow overlong encodings for ascii characters, as these
// would include " and other characters significant to JSON
@@ -246,7 +249,7 @@ class JSONEncoder : public ParserHandler {
// So, now we transcode to UTF16,
// using the math described at https://en.wikipedia.org/wiki/UTF-16,
// for either one or two 16 bit characters.
- if (codepoint < 0xffff) {
+ if (codepoint <= 0xffff) {
Emit("\\u");
PrintHex(static_cast<uint16_t>(codepoint), out_);
continue;
@@ -282,7 +285,22 @@ class JSONEncoder : public ParserHandler {
Emit("null");
return;
}
+ // If |value| is a scalar, emit it as an int. Taken from json_writer.cc in
+ // Chromium.
+ if (value <= std::numeric_limits<int64_t>::max() &&
+ value >= std::numeric_limits<int64_t>::min() &&
+ std::floor(value) == value) {
+ Emit(std::to_string(static_cast<int64_t>(value)));
+ return;
+ }
std::string str_value = json::platform::DToStr(value);
+ // The following is somewhat paranoid, but also taken from json_writer.cc
+ // in Chromium:
+ // Ensure that the number has a .0 if there's no decimal or 'e'. This
+ // makes sure that when we read the JSON back, it's interpreted as a
+ // real rather than an int.
+ if (str_value.find_first_of(".eE") == std::string::npos)
+ str_value.append(".0");
// DToStr may fail to emit a 0 before the decimal dot. E.g. this is
// the case in base::NumberToString in Chromium (which is based on
@@ -752,7 +770,7 @@ class JsonParser {
// So, now we transcode to UTF16,
// using the math described at https://en.wikipedia.org/wiki/UTF-16,
// for either one or two 16 bit characters.
- if (codepoint < 0xffff) {
+ if (codepoint <= 0xffff) {
output->push_back(codepoint);
continue;
}
@@ -994,22 +1012,14 @@ Status ConvertCBORToJSON(span<uint8_t> cbor, std::string* json) {
return ConvertCBORToJSONTmpl(cbor, json);
}
-template <typename T, typename C>
-Status ConvertJSONToCBORTmpl(span<T> json, C* cbor) {
+template <typename T>
+Status ConvertJSONToCBORTmpl(span<T> json, std::vector<uint8_t>* cbor) {
Status status;
std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(cbor, &status);
ParseJSON(json, encoder.get());
return status;
}
-Status ConvertJSONToCBOR(span<uint8_t> json, std::string* cbor) {
- return ConvertJSONToCBORTmpl(json, cbor);
-}
-
-Status ConvertJSONToCBOR(span<uint16_t> json, std::string* cbor) {
- return ConvertJSONToCBORTmpl(json, cbor);
-}
-
Status ConvertJSONToCBOR(span<uint8_t> json, std::vector<uint8_t>* cbor) {
return ConvertJSONToCBORTmpl(json, cbor);
}
diff --git a/chromium/third_party/inspector_protocol/crdtp/json.h b/chromium/third_party/inspector_protocol/crdtp/json.h
index 716aab740d6..50309af1085 100644
--- a/chromium/third_party/inspector_protocol/crdtp/json.h
+++ b/chromium/third_party/inspector_protocol/crdtp/json.h
@@ -6,6 +6,7 @@
#define CRDTP_JSON_H_
#include <memory>
+#include <vector>
#include "export.h"
#include "parser_handler.h"
@@ -50,10 +51,6 @@ CRDTP_EXPORT Status ConvertJSONToCBOR(span<uint8_t> json,
CRDTP_EXPORT Status ConvertJSONToCBOR(span<uint16_t> json,
std::vector<uint8_t>* cbor);
-
-CRDTP_EXPORT Status ConvertJSONToCBOR(span<uint8_t> json, std::string* cbor);
-
-CRDTP_EXPORT Status ConvertJSONToCBOR(span<uint16_t> json, std::string* cbor);
} // namespace json
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/json_test.cc b/chromium/third_party/inspector_protocol/crdtp/json_test.cc
index e5fdbae24e6..1451d961224 100644
--- a/chromium/third_party/inspector_protocol/crdtp/json_test.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/json_test.cc
@@ -18,6 +18,7 @@
#include "parser_handler.h"
#include "span.h"
#include "status.h"
+#include "status_test_support.h"
#include "test_platform.h"
namespace crdtp {
@@ -48,6 +49,75 @@ TEST(JsonEncoder, OverlongEncodings) {
EXPECT_EQ("\"\"", out); // Empty string means that 0x7f was rejected (good).
}
+TEST(JsonEncoder, NotAContinuationByte) {
+ std::string out;
+ Status status;
+ std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status);
+
+ // |world| encodes the globe as a 4 byte UTF8 sequence. So, naturally, it'll
+ // have a start byte, followed by three continuation bytes.
+ std::string world = "🌎";
+ ASSERT_EQ(4u, world.size());
+ ASSERT_EQ(world[1] & 0xc0, 0x80); // checks for continuation byte
+ ASSERT_EQ(world[2] & 0xc0, 0x80);
+ ASSERT_EQ(world[3] & 0xc0, 0x80);
+
+ // Now create a corrupted UTF8 string, starting with the first two bytes from
+ // |world|, followed by an ASCII message. Upon encountering '!', our decoder
+ // will realize that it's not a continuation byte; it'll skip to the end of
+ // this UTF8 sequence and continue with the next character. In this case, the
+ // 'H', of "Hello".
+ std::vector<uint8_t> chars;
+ chars.push_back(world[0]);
+ chars.push_back(world[1]);
+ chars.push_back('!');
+ chars.push_back('?');
+ chars.push_back('H');
+ chars.push_back('e');
+ chars.push_back('l');
+ chars.push_back('l');
+ chars.push_back('o');
+ writer->HandleString8(SpanFrom(chars));
+ EXPECT_EQ("\"Hello\"", out); // "Hello" shows we restarted at 'H'.
+}
+
+TEST(JsonEncoder, EscapesLoneHighSurrogates) {
+ // This tests that the JSON encoder escapes lone high surrogates, i.e.
+ // invalid code points in the range from 0xD800 to 0xDBFF. In
+ // unescaped form, these cannot be represented in well-formed UTF-8 or
+ // UTF-16.
+ std::vector<uint16_t> chars = {'a', 0xd800, 'b', 0xdada, 'c', 0xdbff, 'd'};
+ std::string out;
+ Status status;
+ std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status);
+ writer->HandleString16(span<uint16_t>(chars.data(), chars.size()));
+ EXPECT_EQ("\"a\\ud800b\\udadac\\udbffd\"", out);
+}
+
+TEST(JsonEncoder, EscapesLoneLowSurrogates) {
+ // This tests that the JSON encoder escapes lone low surrogates, i.e.
+ // invalid code points in the range from 0xDC00 to 0xDFFF. In
+ // unescaped form, these cannot be represented in well-formed UTF-8 or
+ // UTF-16.
+ std::vector<uint16_t> chars = {'a', 0xdc00, 'b', 0xdede, 'c', 0xdfff, 'd'};
+ std::string out;
+ Status status;
+ std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status);
+ writer->HandleString16(span<uint16_t>(chars.data(), chars.size()));
+ EXPECT_EQ("\"a\\udc00b\\udedec\\udfffd\"", out);
+}
+
+TEST(JsonEncoder, EscapesFFFF) {
+ // This tests that the JSON encoder will escape the UTF16 input 0xffff as
+ // \uffff; useful to check this since it's an edge case.
+ std::vector<uint16_t> chars = {'a', 'b', 'c', 0xffff, 'd'};
+ std::string out;
+ Status status;
+ std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status);
+ writer->HandleString16(span<uint16_t>(chars.data(), chars.size()));
+ EXPECT_EQ("\"abc\\uffffd\"", out);
+}
+
TEST(JsonEncoder, IncompleteUtf8Sequence) {
std::string out;
Status status;
@@ -114,6 +184,32 @@ TEST(JsonStdStringWriterTest, HelloWorld) {
out);
}
+TEST(JsonStdStringWriterTest, ScalarsAreRenderedAsInt) {
+ // Test that Number.MIN_SAFE_INTEGER / Number.MAX_SAFE_INTEGER from Javascript
+ // are rendered as integers (no decimal point / rounding), even when we
+ // encode them from double. Javascript's Number is an IEE754 double, so
+ // it has 53 bits to represent integers.
+ std::string out;
+ Status status;
+ std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status);
+ writer->HandleMapBegin();
+
+ writer->HandleString8(SpanFrom("Number.MIN_SAFE_INTEGER"));
+ EXPECT_EQ(-0x1fffffffffffff, -9007199254740991); // 53 bits for integers.
+ writer->HandleDouble(-9007199254740991); // Note HandleDouble here.
+
+ writer->HandleString8(SpanFrom("Number.MAX_SAFE_INTEGER"));
+ EXPECT_EQ(0x1fffffffffffff, 9007199254740991); // 53 bits for integers.
+ writer->HandleDouble(9007199254740991); // Note HandleDouble here.
+
+ writer->HandleMapEnd();
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(
+ "{\"Number.MIN_SAFE_INTEGER\":-9007199254740991,"
+ "\"Number.MAX_SAFE_INTEGER\":9007199254740991}",
+ out);
+}
+
TEST(JsonStdStringWriterTest, RepresentingNonFiniteValuesAsNull) {
// JSON can't represent +Infinity, -Infinity, or NaN.
// So in practice it's mapped to null.
@@ -180,8 +276,7 @@ TEST(JsonStdStringWriterTest, HandlesErrors) {
writer->HandleMapBegin();
WriteUTF8AsUTF16(writer.get(), "msg1");
writer->HandleError(Status{Error::JSON_PARSER_VALUE_EXPECTED, 42});
- EXPECT_EQ(Error::JSON_PARSER_VALUE_EXPECTED, status.error);
- EXPECT_EQ(42u, status.pos);
+ EXPECT_THAT(status, StatusIs(Error::JSON_PARSER_VALUE_EXPECTED, 42u));
EXPECT_EQ("", out);
}
@@ -216,6 +311,7 @@ class Log : public ParserHandler {
}
void HandleString16(span<uint16_t> chars) override {
+ raw_log_string16_.emplace_back(chars.begin(), chars.end());
log_ << "string16: " << UTF16ToUTF8(chars) << "\n";
}
@@ -239,10 +335,15 @@ class Log : public ParserHandler {
std::string str() const { return status_.ok() ? log_.str() : ""; }
+ std::vector<std::vector<uint16_t>> raw_log_string16() const {
+ return raw_log_string16_;
+ }
+
Status status() const { return status_; }
private:
std::ostringstream log_;
+ std::vector<std::vector<uint16_t>> raw_log_string16_;
Status status_;
};
@@ -363,6 +464,31 @@ TEST_F(JsonParserTest, Unicode_ParseUtf16) {
log_.str());
}
+TEST_F(JsonParserTest, Unicode_ParseUtf16_SingleEscapeUpToFFFF) {
+ // 0xFFFF is the max codepoint that can be represented as a single \u escape.
+ // One way to write this is \uffff, another way is to encode it as a 3 byte
+ // UTF-8 sequence (0xef 0xbf 0xbf). Both are equivalent.
+
+ // Example with both ways of encoding code point 0xFFFF in a JSON string.
+ std::string json = "{\"escape\": \"\xef\xbf\xbf or \\uffff\"}";
+ ParseJSON(SpanFrom(json), &log_);
+ EXPECT_TRUE(log_.status().ok());
+
+ // Shows both inputs result in equivalent output once converted to UTF-8.
+ EXPECT_EQ(
+ "map begin\n"
+ "string16: escape\n"
+ "string16: \xEF\xBF\xBF or \xEF\xBF\xBF\n"
+ "map end\n",
+ log_.str());
+
+ // Make an even stronger assertion: The parser represents \xffff as a single
+ // UTF-16 char.
+ ASSERT_EQ(2u, log_.raw_log_string16().size());
+ std::vector<uint16_t> expected = {0xffff, ' ', 'o', 'r', ' ', 0xffff};
+ EXPECT_EQ(expected, log_.raw_log_string16()[1]);
+}
+
TEST_F(JsonParserTest, Unicode_ParseUtf8) {
// Used below:
// гласность - example for 2 byte utf8, Russian word "glasnost"
@@ -401,8 +527,8 @@ TEST_F(JsonParserTest, UnprocessedInputRemainsError) {
size_t junk_idx = json.find("junk");
EXPECT_NE(junk_idx, std::string::npos);
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_UNPROCESSED_INPUT_REMAINS, log_.status().error);
- EXPECT_EQ(junk_idx, log_.status().pos);
+ EXPECT_THAT(log_.status(),
+ StatusIs(Error::JSON_PARSER_UNPROCESSED_INPUT_REMAINS, junk_idx));
EXPECT_EQ("", log_.str());
}
@@ -442,38 +568,36 @@ TEST_F(JsonParserTest, StackLimitExceededError_AtLimit) {
ParseJSON(span<uint8_t>(reinterpret_cast<const uint8_t*>(json_limit.data()),
json_limit.size()),
&log_);
- EXPECT_TRUE(log_.status().ok());
+ EXPECT_THAT(log_.status(), StatusIsOk());
}
TEST_F(JsonParserTest, StackLimitExceededError_AboveLimit) {
// Now with kStackLimit + 1 (301) - it exceeds in the innermost instance.
std::string exceeded = MakeNestedJson(301);
ParseJSON(SpanFrom(exceeded), &log_);
- EXPECT_EQ(Error::JSON_PARSER_STACK_LIMIT_EXCEEDED, log_.status().error);
- EXPECT_EQ(strlen("{\"foo\":") * 301, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_STACK_LIMIT_EXCEEDED,
+ strlen("{\"foo\":") * 301));
}
TEST_F(JsonParserTest, StackLimitExceededError_WayAboveLimit) {
// Now way past the limit. Still, the point of exceeding is 301.
std::string far_out = MakeNestedJson(320);
ParseJSON(SpanFrom(far_out), &log_);
- EXPECT_EQ(Error::JSON_PARSER_STACK_LIMIT_EXCEEDED, log_.status().error);
- EXPECT_EQ(strlen("{\"foo\":") * 301, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_STACK_LIMIT_EXCEEDED,
+ strlen("{\"foo\":") * 301));
}
TEST_F(JsonParserTest, NoInputError) {
std::string json = "";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_NO_INPUT, log_.status().error);
- EXPECT_EQ(0u, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_NO_INPUT, 0u));
EXPECT_EQ("", log_.str());
}
TEST_F(JsonParserTest, InvalidTokenError) {
std::string json = "|";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_INVALID_TOKEN, log_.status().error);
- EXPECT_EQ(0u, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_INVALID_TOKEN, 0u));
EXPECT_EQ("", log_.str());
}
@@ -481,8 +605,7 @@ TEST_F(JsonParserTest, InvalidNumberError) {
// Mantissa exceeds max (the constant used here is int64_t max).
std::string json = "1E9223372036854775807";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_INVALID_NUMBER, log_.status().error);
- EXPECT_EQ(0u, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_INVALID_NUMBER, 0u));
EXPECT_EQ("", log_.str());
}
@@ -490,25 +613,23 @@ TEST_F(JsonParserTest, InvalidStringError) {
// \x22 is an unsupported escape sequence
std::string json = "\"foo\\x22\"";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_INVALID_STRING, log_.status().error);
- EXPECT_EQ(0u, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_INVALID_STRING, 0u));
EXPECT_EQ("", log_.str());
}
TEST_F(JsonParserTest, UnexpectedArrayEndError) {
std::string json = "[1,2,]";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_UNEXPECTED_ARRAY_END, log_.status().error);
- EXPECT_EQ(5u, log_.status().pos);
+ EXPECT_THAT(log_.status(),
+ StatusIs(Error::JSON_PARSER_UNEXPECTED_ARRAY_END, 5u));
EXPECT_EQ("", log_.str());
}
TEST_F(JsonParserTest, CommaOrArrayEndExpectedError) {
std::string json = "[1,2 2";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED,
- log_.status().error);
- EXPECT_EQ(5u, log_.status().pos);
+ EXPECT_THAT(log_.status(),
+ StatusIs(Error::JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED, 5u));
EXPECT_EQ("", log_.str());
}
@@ -516,24 +637,23 @@ TEST_F(JsonParserTest, StringLiteralExpectedError) {
// There's an error because the key bar, a string, is not terminated.
std::string json = "{\"foo\": 3.1415, \"bar: 31415e-4}";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_STRING_LITERAL_EXPECTED, log_.status().error);
- EXPECT_EQ(16u, log_.status().pos);
+ EXPECT_THAT(log_.status(),
+ StatusIs(Error::JSON_PARSER_STRING_LITERAL_EXPECTED, 16u));
EXPECT_EQ("", log_.str());
}
TEST_F(JsonParserTest, ColonExpectedError) {
std::string json = "{\"foo\", 42}";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_COLON_EXPECTED, log_.status().error);
- EXPECT_EQ(6u, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_COLON_EXPECTED, 6u));
EXPECT_EQ("", log_.str());
}
TEST_F(JsonParserTest, UnexpectedMapEndError) {
std::string json = "{\"foo\": 42, }";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_UNEXPECTED_MAP_END, log_.status().error);
- EXPECT_EQ(12u, log_.status().pos);
+ EXPECT_THAT(log_.status(),
+ StatusIs(Error::JSON_PARSER_UNEXPECTED_MAP_END, 12u));
EXPECT_EQ("", log_.str());
}
@@ -541,16 +661,15 @@ TEST_F(JsonParserTest, CommaOrMapEndExpectedError) {
// The second separator should be a comma.
std::string json = "{\"foo\": 3.1415: \"bar\": 0}";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_COMMA_OR_MAP_END_EXPECTED, log_.status().error);
- EXPECT_EQ(14u, log_.status().pos);
+ EXPECT_THAT(log_.status(),
+ StatusIs(Error::JSON_PARSER_COMMA_OR_MAP_END_EXPECTED, 14u));
EXPECT_EQ("", log_.str());
}
TEST_F(JsonParserTest, ValueExpectedError) {
std::string json = "}";
ParseJSON(SpanFrom(json), &log_);
- EXPECT_EQ(Error::JSON_PARSER_VALUE_EXPECTED, log_.status().error);
- EXPECT_EQ(0u, log_.status().pos);
+ EXPECT_THAT(log_.status(), StatusIs(Error::JSON_PARSER_VALUE_EXPECTED, 0u));
EXPECT_EQ("", log_.str());
}
@@ -561,21 +680,29 @@ using ContainerTestTypes = ::testing::Types<std::vector<uint8_t>, std::string>;
TYPED_TEST_SUITE(ConvertJSONToCBORTest, ContainerTestTypes);
TYPED_TEST(ConvertJSONToCBORTest, RoundTripValidJson) {
- std::string json_in = "{\"msg\":\"Hello, world.\",\"lst\":[1,2,3]}";
- TypeParam json(json_in.begin(), json_in.end());
- TypeParam cbor;
- {
- Status status = ConvertJSONToCBOR(SpanFrom(json), &cbor);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
- }
- TypeParam roundtrip_json;
- {
- Status status = ConvertCBORToJSON(SpanFrom(cbor), &roundtrip_json);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ for (const std::string& json_in : {
+ "{\"msg\":\"Hello, world.\",\"lst\":[1,2,3]}",
+ "3.1415",
+ "false",
+ "true",
+ "\"Hello, world.\"",
+ "[1,2,3]",
+ "[]",
+ }) {
+ SCOPED_TRACE(json_in);
+ TypeParam json(json_in.begin(), json_in.end());
+ std::vector<uint8_t> cbor;
+ {
+ Status status = ConvertJSONToCBOR(SpanFrom(json), &cbor);
+ EXPECT_THAT(status, StatusIsOk());
+ }
+ TypeParam roundtrip_json;
+ {
+ Status status = ConvertCBORToJSON(SpanFrom(cbor), &roundtrip_json);
+ EXPECT_THAT(status, StatusIsOk());
+ }
+ EXPECT_EQ(json, roundtrip_json);
}
- EXPECT_EQ(json, roundtrip_json);
}
TYPED_TEST(ConvertJSONToCBORTest, RoundTripValidJson16) {
@@ -583,18 +710,16 @@ TYPED_TEST(ConvertJSONToCBORTest, RoundTripValidJson16) {
'{', '"', 'm', 's', 'g', '"', ':', '"', 'H', 'e', 'l', 'l',
'o', ',', ' ', 0xd83c, 0xdf0e, '.', '"', ',', '"', 'l', 's', 't',
'"', ':', '[', '1', ',', '2', ',', '3', ']', '}'};
- TypeParam cbor;
+ std::vector<uint8_t> cbor;
{
Status status =
ConvertJSONToCBOR(span<uint16_t>(json16.data(), json16.size()), &cbor);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
}
TypeParam roundtrip_json;
{
Status status = ConvertCBORToJSON(SpanFrom(cbor), &roundtrip_json);
- EXPECT_EQ(Error::OK, status.error);
- EXPECT_EQ(Status::npos(), status.pos);
+ EXPECT_THAT(status, StatusIsOk());
}
std::string json = "{\"msg\":\"Hello, \\ud83c\\udf0e.\",\"lst\":[1,2,3]}";
TypeParam expected_json(json.begin(), json.end());
diff --git a/chromium/third_party/inspector_protocol/crdtp/serializable.cc b/chromium/third_party/inspector_protocol/crdtp/serializable.cc
index d5c48c25d4c..cb894656e6d 100644
--- a/chromium/third_party/inspector_protocol/crdtp/serializable.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/serializable.cc
@@ -9,9 +9,28 @@ namespace crdtp {
// Serializable - An object to be emitted as a sequence of bytes.
// =============================================================================
-std::vector<uint8_t> Serializable::TakeSerialized() && {
+std::vector<uint8_t> Serializable::Serialize() const {
std::vector<uint8_t> out;
AppendSerialized(&out);
return out;
}
+
+namespace {
+class PreSerialized : public Serializable {
+ public:
+ explicit PreSerialized(std::vector<uint8_t> bytes) : bytes_(bytes) {}
+
+ void AppendSerialized(std::vector<uint8_t>* out) const override {
+ out->insert(out->end(), bytes_.begin(), bytes_.end());
+ }
+
+ private:
+ std::vector<uint8_t> bytes_;
+};
+} // namespace
+
+// static
+std::unique_ptr<Serializable> Serializable::From(std::vector<uint8_t> bytes) {
+ return std::make_unique<PreSerialized>(std::move(bytes));
+}
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/serializable.h b/chromium/third_party/inspector_protocol/crdtp/serializable.h
index deb6a16cd0f..3b180b569a4 100644
--- a/chromium/third_party/inspector_protocol/crdtp/serializable.h
+++ b/chromium/third_party/inspector_protocol/crdtp/serializable.h
@@ -6,6 +6,7 @@
#define CRDTP_SERIALIZABLE_H_
#include <cstdint>
+#include <memory>
#include <vector>
#include "export.h"
@@ -13,17 +14,18 @@ namespace crdtp {
// =============================================================================
// Serializable - An object to be emitted as a sequence of bytes.
// =============================================================================
-
class CRDTP_EXPORT Serializable {
public:
- // The default implementation invokes AppendSerialized with an empty vector
- // and returns it; some subclasses may override and move out internal state
- // instead to avoid copying.
- virtual std::vector<uint8_t> TakeSerialized() &&;
+ // Convenience: Invokes |AppendSerialized| on an empty vector.
+ std::vector<uint8_t> Serialize() const;
virtual void AppendSerialized(std::vector<uint8_t>* out) const = 0;
virtual ~Serializable() = default;
+
+ // Wraps a vector of |bytes| into a Serializable for situations in which we
+ // eagerly serialize a structure.
+ static std::unique_ptr<Serializable> From(std::vector<uint8_t> bytes);
};
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/serializable_test.cc b/chromium/third_party/inspector_protocol/crdtp/serializable_test.cc
index eb8e5130a72..13b6d08ba6a 100644
--- a/chromium/third_party/inspector_protocol/crdtp/serializable_test.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/serializable_test.cc
@@ -14,8 +14,7 @@ namespace crdtp {
// =============================================================================
namespace {
-// Tests the default behavior for ::TakeSerialized (to invoke
-// ::AppendSerialized).
+// Tests ::Serialize (which invokes ::AppendSerialized).
class SimpleExample : public Serializable {
public:
explicit SimpleExample(const std::vector<uint8_t>& contents)
@@ -36,6 +35,6 @@ TEST(SerializableTest, YieldsContents) {
foo.AppendSerialized(&contents); // Yields contents by appending.
EXPECT_THAT(contents, testing::ElementsAre(1, 2, 3, 1, 2, 3));
// Yields contents by returning.
- EXPECT_THAT(std::move(foo).TakeSerialized(), testing::ElementsAre(1, 2, 3));
+ EXPECT_THAT(foo.Serialize(), testing::ElementsAre(1, 2, 3));
}
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/serializer_traits.h b/chromium/third_party/inspector_protocol/crdtp/serializer_traits.h
new file mode 100644
index 00000000000..5647bed2880
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/serializer_traits.h
@@ -0,0 +1,158 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRDTP_SERIALIZER_TRAITS_H_
+#define CRDTP_SERIALIZER_TRAITS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+#include "cbor.h"
+#include "glue.h"
+#include "span.h"
+
+namespace crdtp {
+// =============================================================================
+// SerializerTraits - Encodes field values of protocol objects in CBOR.
+// =============================================================================
+//
+// A family of serialization functions which are used by FieldSerializerTraits
+// (below) to encode field values in CBOR. Conceptually, it's this:
+//
+// Serialize(bool value, std::vector<uint8_t>* out);
+// Serialize(int32_t value, std::vector<uint8_t>* out);
+// Serialize(double value, std::vector<uint8_t>* out);
+// ...
+//
+// However, if this was to use straight-forward overloading, implicit
+// type conversions would lead to ambiguity - e.g., a bool could be
+// represented as an int32_t, but it should really be encoded as a bool.
+// The template parameterized / specialized structs accomplish this.
+//
+// SerializerTraits<bool>::Serialize(bool value, std::vector<uint8_t>* out);
+// SerializerTraits<int32>::Serialize(int32_t value, std::vector<uint8_t>* out);
+// SerializerTraits<double>::Serialize(double value, std::vector<uint8_t>* out);
+template <typename T>
+struct SerializerTraits {
+ // |Serializable| (defined in serializable.h) already knows how to serialize
+ // to CBOR, so we can just delegate. This covers domain specific types,
+ // protocol::Binary, etc.
+ // However, we use duck-typing here, because Exported, which is part of the V8
+ // headers also comes with AppendSerialized, and logically it's the same type,
+ // but it lives in a different namespace (v8_inspector::protocol::Exported).
+ template <
+ typename LikeSerializable,
+ typename std::enable_if_t<std::is_member_pointer<decltype(
+ &LikeSerializable::AppendSerialized)>{},
+ int> = 0>
+ static void Serialize(const LikeSerializable& value,
+ std::vector<uint8_t>* out) {
+ value.AppendSerialized(out);
+ }
+};
+
+// This covers std::string, which is assumed to be UTF-8.
+// The two other string implementations that are used in the protocol bindings:
+// - WTF::String, for which the SerializerTraits specialization is located
+// in third_party/blink/renderer/core/inspector/v8-inspector-string.h.
+// - v8_inspector::String16, implemented in v8/src/inspector/string-16.h
+// along with its SerializerTraits specialization.
+template <>
+struct SerializerTraits<std::string> {
+ static void Serialize(const std::string& str, std::vector<uint8_t>* out) {
+ cbor::EncodeString8(SpanFrom(str), out);
+ }
+};
+
+template <>
+struct SerializerTraits<bool> {
+ static void Serialize(bool value, std::vector<uint8_t>* out) {
+ out->push_back(value ? cbor::EncodeTrue() : cbor::EncodeFalse());
+ }
+};
+
+template <>
+struct SerializerTraits<int32_t> {
+ static void Serialize(int32_t value, std::vector<uint8_t>* out) {
+ cbor::EncodeInt32(value, out);
+ }
+};
+
+template <>
+struct SerializerTraits<double> {
+ static void Serialize(double value, std::vector<uint8_t>* out) {
+ cbor::EncodeDouble(value, out);
+ }
+};
+
+template <typename T>
+struct SerializerTraits<std::vector<T>> {
+ static void Serialize(const std::vector<T>& value,
+ std::vector<uint8_t>* out) {
+ out->push_back(cbor::EncodeIndefiniteLengthArrayStart());
+ for (const T& element : value)
+ SerializerTraits<T>::Serialize(element, out);
+ out->push_back(cbor::EncodeStop());
+ }
+};
+
+template <typename T>
+struct SerializerTraits<std::unique_ptr<T>> {
+ static void Serialize(const std::unique_ptr<T>& value,
+ std::vector<uint8_t>* out) {
+ SerializerTraits<T>::Serialize(*value, out);
+ }
+};
+
+// =============================================================================
+// FieldSerializerTraits - Encodes fields of protocol objects in CBOR
+// =============================================================================
+//
+// The generated code (see TypeBuilder_cpp.template) invokes SerializeField,
+// which then instantiates the FieldSerializerTraits to emit the appropriate
+// existence checks / dereference for the field value. This avoids emitting
+// the field name if the value for an optional field isn't set.
+template <typename T>
+struct FieldSerializerTraits {
+ static void Serialize(span<uint8_t> field_name,
+ const T& field_value,
+ std::vector<uint8_t>* out) {
+ cbor::EncodeString8(field_name, out);
+ SerializerTraits<T>::Serialize(field_value, out);
+ }
+};
+
+template <typename T>
+struct FieldSerializerTraits<glue::detail::PtrMaybe<T>> {
+ static void Serialize(span<uint8_t> field_name,
+ const glue::detail::PtrMaybe<T>& field_value,
+ std::vector<uint8_t>* out) {
+ if (!field_value.isJust())
+ return;
+ cbor::EncodeString8(field_name, out);
+ SerializerTraits<T>::Serialize(*field_value.fromJust(), out);
+ }
+};
+
+template <typename T>
+struct FieldSerializerTraits<glue::detail::ValueMaybe<T>> {
+ static void Serialize(span<uint8_t> field_name,
+ const glue::detail::ValueMaybe<T>& field_value,
+ std::vector<uint8_t>* out) {
+ if (!field_value.isJust())
+ return;
+ cbor::EncodeString8(field_name, out);
+ SerializerTraits<T>::Serialize(field_value.fromJust(), out);
+ }
+};
+
+template <typename T>
+void SerializeField(span<uint8_t> field_name,
+ const T& field_value,
+ std::vector<uint8_t>* out) {
+ FieldSerializerTraits<T>::Serialize(field_name, field_value, out);
+}
+} // namespace crdtp
+
+#endif // CRDTP_SERIALIZER_TRAITS_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/serializer_traits_test.cc b/chromium/third_party/inspector_protocol/crdtp/serializer_traits_test.cc
new file mode 100644
index 00000000000..ee4ec4c84cb
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/serializer_traits_test.cc
@@ -0,0 +1,227 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "serializer_traits.h"
+
+#include <array>
+#include "serializable.h"
+#include "test_platform.h"
+
+// The purpose of this test is to ensure that the
+// {Field}SerializerTraits<X>::Serialize methods invoke the appropriate
+// functions from cbor.h; so, it's usually sufficient to compare with what
+// cbor.h function invocations would produce, rather than making assertions on
+// the specific bytes emitted by the SerializerTraits code.
+
+namespace crdtp {
+namespace {
+// =============================================================================
+// SerializerTraits - Encodes field values of protocol objects in CBOR.
+// =============================================================================
+
+TEST(SerializerTraits, Bool) {
+ std::vector<uint8_t> out;
+ SerializerTraits<bool>::Serialize(true, &out);
+ SerializerTraits<bool>::Serialize(false, &out);
+ EXPECT_THAT(out,
+ testing::ElementsAre(cbor::EncodeTrue(), cbor::EncodeFalse()));
+}
+
+TEST(SerializerTraits, Double) {
+ std::vector<uint8_t> out;
+ SerializerTraits<double>::Serialize(1.00001, &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeDouble(1.00001, &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+TEST(SerializerTraits, Int32) {
+ std::vector<uint8_t> out;
+ SerializerTraits<int32_t>::Serialize(42, &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeInt32(42, &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+TEST(SerializerTraits, VectorOfInt32) {
+ std::vector<int32_t> ints = {1, 2, 3};
+
+ std::vector<uint8_t> out;
+ SerializerTraits<std::vector<int32_t>>::Serialize(ints, &out);
+
+ std::vector<uint8_t> expected;
+ expected.push_back(cbor::EncodeIndefiniteLengthArrayStart());
+ for (int32_t v : ints)
+ cbor::EncodeInt32(v, &expected);
+ expected.push_back(cbor::EncodeStop());
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+// Foo is an example for a domain specific type.
+class Foo : public Serializable {
+ public:
+ Foo(int32_t value) : value(value) {}
+
+ int32_t value;
+
+ void AppendSerialized(std::vector<uint8_t>* out) const override {
+ // In production, this would be generated code which emits a
+ // CBOR map that has STRING8 keys corresponding to the field names
+ // and field values encoded using SerializerTraits::Serialize.
+ //
+ // For the test we simplify this drastically and just emit the field
+ // value, for conveniently testing std::vector<std::unique_ptr<Foo>>,
+ // as well as the convenience methods for raw pointer and unique_ptr.
+ SerializerTraits<int32_t>::Serialize(value, out);
+ }
+};
+
+TEST(SerializerTraits, VectorOfDomainSpecificType) {
+ std::vector<std::unique_ptr<Foo>> foos;
+ foos.push_back(std::make_unique<Foo>(1));
+ foos.push_back(std::make_unique<Foo>(2));
+ foos.push_back(std::make_unique<Foo>(3));
+
+ std::vector<uint8_t> out;
+ SerializerTraits<std::vector<std::unique_ptr<Foo>>>::Serialize(foos, &out);
+
+ std::vector<uint8_t> expected;
+ expected.push_back(cbor::EncodeIndefiniteLengthArrayStart());
+ for (int32_t v : {1, 2, 3})
+ cbor::EncodeInt32(v, &expected);
+ expected.push_back(cbor::EncodeStop());
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+TEST(SerializerTraits, ConstRefAndUniquePtr) {
+ // Shows that SerializerTraits<Foo> allows unique_ptr.
+ Foo foo(42);
+ auto bar = std::make_unique<Foo>(21);
+
+ std::vector<uint8_t> out;
+ // In this case, |foo| is taken as a const Foo&.
+ SerializerTraits<Foo>::Serialize(foo, &out);
+ // In this case, |bar| is taken as a const std::unique_ptr<Foo>&.
+ SerializerTraits<std::unique_ptr<Foo>>::Serialize(bar, &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeInt32(42, &expected);
+ cbor::EncodeInt32(21, &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+TEST(SerializerTraits, UTF8String) {
+ std::string msg = "Hello, 🌎.";
+
+ std::vector<uint8_t> out;
+ SerializerTraits<std::string>::Serialize(msg, &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeString8(SpanFrom(msg), &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+// A trivial model of Exported.
+// (see
+// https://cs.chromium.org/chromium/src/out/Debug/gen/v8/include/inspector/Debugger.h).
+struct Exported {
+ std::string msg;
+ void AppendSerialized(std::vector<uint8_t>* out) const {
+ cbor::EncodeString8(SpanFrom(msg), out);
+ }
+};
+
+TEST(SerializerTraits, Exported) {
+ Exported exported;
+ exported.msg = "Hello, world.";
+
+ std::vector<uint8_t> out;
+ SerializerTraits<Exported>::Serialize(exported, &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeString8(SpanFrom(exported.msg), &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+// =============================================================================
+// FieldSerializerTraits - Encodes fields of protocol objects in CBOR
+// =============================================================================
+TEST(FieldSerializerTraits, RequiredField) {
+ std::string value = "Hello, world.";
+
+ std::vector<uint8_t> out;
+ SerializeField(SpanFrom("msg"), value, &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeString8(SpanFrom("msg"), &expected);
+ cbor::EncodeString8(SpanFrom(value), &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+template <typename T>
+class FieldSerializerTraits_MaybeTest : public ::testing::Test {};
+using MaybeTypes =
+ ::testing::Types<glue::detail::ValueMaybe<bool>,
+ glue::detail::ValueMaybe<double>,
+ glue::detail::ValueMaybe<int32_t>,
+ glue::detail::ValueMaybe<std::string>,
+ glue::detail::PtrMaybe<Foo>,
+ glue::detail::PtrMaybe<std::vector<std::unique_ptr<Foo>>>>;
+TYPED_TEST_SUITE(FieldSerializerTraits_MaybeTest, MaybeTypes);
+
+TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) {
+ std::vector<uint8_t> out;
+ SerializeField(SpanFrom("maybe"), TypeParam(), &out);
+ EXPECT_THAT(out, testing::ElementsAreArray(std::vector<uint8_t>()));
+}
+
+TEST(FieldSerializerTraits, MaybeBool) {
+ std::vector<uint8_t> out;
+ SerializeField(SpanFrom("true"), glue::detail::ValueMaybe<bool>(true), &out);
+ SerializeField(SpanFrom("false"), glue::detail::ValueMaybe<bool>(false),
+ &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeString8(SpanFrom("true"), &expected);
+ expected.push_back(cbor::EncodeTrue());
+ cbor::EncodeString8(SpanFrom("false"), &expected);
+ expected.push_back(cbor::EncodeFalse());
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+TEST(FieldSerializerTraits, MaybeDouble) {
+ std::vector<uint8_t> out;
+ SerializeField(SpanFrom("dbl"), glue::detail::ValueMaybe<double>(3.14), &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeString8(SpanFrom("dbl"), &expected);
+ cbor::EncodeDouble(3.14, &expected);
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+
+TEST(FieldSerializerTraits, MaybePtrFoo) {
+ std::vector<uint8_t> out;
+ SerializeField(SpanFrom("foo"),
+ glue::detail::PtrMaybe<Foo>(std::make_unique<Foo>(42)), &out);
+
+ std::vector<uint8_t> expected;
+ cbor::EncodeString8(SpanFrom("foo"), &expected);
+ cbor::EncodeInt32(42, &expected); // Simplified relative to production.
+
+ EXPECT_THAT(out, testing::ElementsAreArray(expected));
+}
+} // namespace
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/span.cc b/chromium/third_party/inspector_protocol/crdtp/span.cc
new file mode 100644
index 00000000000..05443e0e2b6
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/span.cc
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "span.h"
+
+#include <algorithm>
+
+namespace crdtp {
+
+bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept {
+ auto min_size = std::min(x.size(), y.size());
+ const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
+ return (r < 0) || (r == 0 && x.size() < y.size());
+}
+
+bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept {
+ auto len = x.size();
+ if (len != y.size())
+ return false;
+ return x.data() == y.data() || len == 0 ||
+ std::memcmp(x.data(), y.data(), len) == 0;
+}
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/span.h b/chromium/third_party/inspector_protocol/crdtp/span.h
index bdd808b7268..965a7847ef8 100644
--- a/chromium/third_party/inspector_protocol/crdtp/span.h
+++ b/chromium/third_party/inspector_protocol/crdtp/span.h
@@ -5,11 +5,11 @@
#ifndef CRDTP_SPAN_H_
#define CRDTP_SPAN_H_
-#include <algorithm>
#include <cstdint>
#include <cstring>
#include <string>
-#include <vector>
+
+#include "export.h"
namespace crdtp {
// =============================================================================
@@ -22,45 +22,40 @@ class span {
public:
using index_type = size_t;
- span() : data_(nullptr), size_(0) {}
- span(const T* data, index_type size) : data_(data), size_(size) {}
+ constexpr span() : data_(nullptr), size_(0) {}
+ constexpr span(const T* data, index_type size) : data_(data), size_(size) {}
- const T* data() const { return data_; }
+ constexpr const T* data() const { return data_; }
- const T* begin() const { return data_; }
- const T* end() const { return data_ + size_; }
+ constexpr const T* begin() const { return data_; }
+ constexpr const T* end() const { return data_ + size_; }
- const T& operator[](index_type idx) const { return data_[idx]; }
+ constexpr const T& operator[](index_type idx) const { return data_[idx]; }
- span<T> subspan(index_type offset, index_type count) const {
+ constexpr span<T> subspan(index_type offset, index_type count) const {
return span(data_ + offset, count);
}
- span<T> subspan(index_type offset) const {
+ constexpr span<T> subspan(index_type offset) const {
return span(data_ + offset, size_ - offset);
}
- bool empty() const { return size_ == 0; }
+ constexpr bool empty() const { return size_ == 0; }
- index_type size() const { return size_; }
- index_type size_bytes() const { return size_ * sizeof(T); }
+ constexpr index_type size() const { return size_; }
+ constexpr index_type size_bytes() const { return size_ * sizeof(T); }
private:
const T* data_;
index_type size_;
};
-template <typename T>
-span<T> SpanFrom(const std::vector<T>& v) {
- return span<T>(v.data(), v.size());
-}
-
template <size_t N>
-span<uint8_t> SpanFrom(const char (&str)[N]) {
+constexpr span<uint8_t> SpanFrom(const char (&str)[N]) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1);
}
-inline span<uint8_t> SpanFrom(const char* str) {
+constexpr inline span<uint8_t> SpanFrom(const char* str) {
return str ? span<uint8_t>(reinterpret_cast<const uint8_t*>(str), strlen(str))
: span<uint8_t>();
}
@@ -69,21 +64,27 @@ inline span<uint8_t> SpanFrom(const std::string& v) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size());
}
+// This SpanFrom routine works for std::vector<uint8_t> and
+// std::vector<uint16_t>, but also for base::span<const uint8_t> in Chromium.
+template <typename C,
+ typename = std::enable_if_t<
+ std::is_unsigned<typename C::value_type>{} &&
+ std::is_member_function_pointer<decltype(&C::size)>{}>>
+inline span<typename C::value_type> SpanFrom(const C& v) {
+ return span<typename C::value_type>(v.data(), v.size());
+}
+
// Less than / equality comparison functions for sorting / searching for byte
// spans. These are similar to absl::string_view's < and == operators.
-inline bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept {
- auto min_size = std::min(x.size(), y.size());
- const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
- return (r < 0) || (r == 0 && x.size() < y.size());
-}
+CRDTP_EXPORT bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept;
-inline bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept {
- auto len = x.size();
- if (len != y.size())
- return false;
- return x.data() == y.data() || len == 0 ||
- std::memcmp(x.data(), y.data(), len) == 0;
-}
+CRDTP_EXPORT bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept;
+
+struct SpanLt {
+ bool operator()(span<uint8_t> l, span<uint8_t> r) const {
+ return SpanLessThan(l, r);
+ }
+};
} // namespace crdtp
#endif // CRDTP_SPAN_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/span_test.cc b/chromium/third_party/inspector_protocol/crdtp/span_test.cc
index 166997f54d6..13c7ea441ae 100644
--- a/chromium/third_party/inspector_protocol/crdtp/span_test.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/span_test.cc
@@ -12,7 +12,6 @@ namespace crdtp {
// =============================================================================
// span - sequence of bytes
// =============================================================================
-
template <typename T>
class SpanTest : public ::testing::Test {};
@@ -76,6 +75,16 @@ TEST(SpanFromTest, FromConstCharAndLiteral) {
EXPECT_EQ(3u, SpanFrom("foo").size());
}
+TEST(SpanFromTest, FromVectorUint8AndUint16) {
+ std::vector<uint8_t> foo = {'f', 'o', 'o'};
+ span<uint8_t> foo_span = SpanFrom(foo);
+ EXPECT_EQ(foo.size(), foo_span.size());
+
+ std::vector<uint16_t> bar = {0xff, 0xef, 0xeb};
+ span<uint16_t> bar_span = SpanFrom(bar);
+ EXPECT_EQ(bar.size(), bar_span.size());
+}
+
TEST(SpanComparisons, ByteWiseLexicographicalOrder) {
// Compare the empty span.
EXPECT_FALSE(SpanLessThan(span<uint8_t>(), span<uint8_t>()));
diff --git a/chromium/third_party/inspector_protocol/crdtp/status.cc b/chromium/third_party/inspector_protocol/crdtp/status.cc
index 3d8cfecb860..f2d3f526dc6 100644
--- a/chromium/third_party/inspector_protocol/crdtp/status.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/status.cc
@@ -9,100 +9,118 @@ namespace crdtp {
// Status and Error codes
// =============================================================================
-std::string Status::ToASCIIString() const {
+std::string Status::Message() const {
switch (error) {
case Error::OK:
return "OK";
case Error::JSON_PARSER_UNPROCESSED_INPUT_REMAINS:
- return ToASCIIString("JSON: unprocessed input remains");
+ return "JSON: unprocessed input remains";
case Error::JSON_PARSER_STACK_LIMIT_EXCEEDED:
- return ToASCIIString("JSON: stack limit exceeded");
+ return "JSON: stack limit exceeded";
case Error::JSON_PARSER_NO_INPUT:
- return ToASCIIString("JSON: no input");
+ return "JSON: no input";
case Error::JSON_PARSER_INVALID_TOKEN:
- return ToASCIIString("JSON: invalid token");
+ return "JSON: invalid token";
case Error::JSON_PARSER_INVALID_NUMBER:
- return ToASCIIString("JSON: invalid number");
+ return "JSON: invalid number";
case Error::JSON_PARSER_INVALID_STRING:
- return ToASCIIString("JSON: invalid string");
+ return "JSON: invalid string";
case Error::JSON_PARSER_UNEXPECTED_ARRAY_END:
- return ToASCIIString("JSON: unexpected array end");
+ return "JSON: unexpected array end";
case Error::JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED:
- return ToASCIIString("JSON: comma or array end expected");
+ return "JSON: comma or array end expected";
case Error::JSON_PARSER_STRING_LITERAL_EXPECTED:
- return ToASCIIString("JSON: string literal expected");
+ return "JSON: string literal expected";
case Error::JSON_PARSER_COLON_EXPECTED:
- return ToASCIIString("JSON: colon expected");
+ return "JSON: colon expected";
case Error::JSON_PARSER_UNEXPECTED_MAP_END:
- return ToASCIIString("JSON: unexpected map end");
+ return "JSON: unexpected map end";
case Error::JSON_PARSER_COMMA_OR_MAP_END_EXPECTED:
- return ToASCIIString("JSON: comma or map end expected");
+ return "JSON: comma or map end expected";
case Error::JSON_PARSER_VALUE_EXPECTED:
- return ToASCIIString("JSON: value expected");
+ return "JSON: value expected";
case Error::CBOR_INVALID_INT32:
- return ToASCIIString("CBOR: invalid int32");
+ return "CBOR: invalid int32";
case Error::CBOR_INVALID_DOUBLE:
- return ToASCIIString("CBOR: invalid double");
+ return "CBOR: invalid double";
case Error::CBOR_INVALID_ENVELOPE:
- return ToASCIIString("CBOR: invalid envelope");
+ return "CBOR: invalid envelope";
case Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH:
- return ToASCIIString("CBOR: envelope contents length mismatch");
+ return "CBOR: envelope contents length mismatch";
case Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE:
- return ToASCIIString("CBOR: map or array expected in envelope");
+ return "CBOR: map or array expected in envelope";
case Error::CBOR_INVALID_STRING8:
- return ToASCIIString("CBOR: invalid string8");
+ return "CBOR: invalid string8";
case Error::CBOR_INVALID_STRING16:
- return ToASCIIString("CBOR: invalid string16");
+ return "CBOR: invalid string16";
case Error::CBOR_INVALID_BINARY:
- return ToASCIIString("CBOR: invalid binary");
+ return "CBOR: invalid binary";
case Error::CBOR_UNSUPPORTED_VALUE:
- return ToASCIIString("CBOR: unsupported value");
+ return "CBOR: unsupported value";
case Error::CBOR_NO_INPUT:
- return ToASCIIString("CBOR: no input");
+ return "CBOR: no input";
case Error::CBOR_INVALID_START_BYTE:
- return ToASCIIString("CBOR: invalid start byte");
+ return "CBOR: invalid start byte";
case Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE:
- return ToASCIIString("CBOR: unexpected eof expected value");
+ return "CBOR: unexpected eof expected value";
case Error::CBOR_UNEXPECTED_EOF_IN_ARRAY:
- return ToASCIIString("CBOR: unexpected eof in array");
+ return "CBOR: unexpected eof in array";
case Error::CBOR_UNEXPECTED_EOF_IN_MAP:
- return ToASCIIString("CBOR: unexpected eof in map");
+ return "CBOR: unexpected eof in map";
case Error::CBOR_INVALID_MAP_KEY:
- return ToASCIIString("CBOR: invalid map key");
+ return "CBOR: invalid map key";
+ case Error::CBOR_DUPLICATE_MAP_KEY:
+ return "CBOR: duplicate map key";
case Error::CBOR_STACK_LIMIT_EXCEEDED:
- return ToASCIIString("CBOR: stack limit exceeded");
+ return "CBOR: stack limit exceeded";
case Error::CBOR_TRAILING_JUNK:
- return ToASCIIString("CBOR: trailing junk");
+ return "CBOR: trailing junk";
case Error::CBOR_MAP_START_EXPECTED:
- return ToASCIIString("CBOR: map start expected");
+ return "CBOR: map start expected";
case Error::CBOR_MAP_STOP_EXPECTED:
- return ToASCIIString("CBOR: map stop expected");
+ return "CBOR: map stop expected";
case Error::CBOR_ARRAY_START_EXPECTED:
- return ToASCIIString("CBOR: array start expected");
+ return "CBOR: array start expected";
case Error::CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED:
- return ToASCIIString("CBOR: envelope size limit exceeded");
+ return "CBOR: envelope size limit exceeded";
+
+ case Error::MESSAGE_MUST_BE_AN_OBJECT:
+ return "Message must be an object";
+ case Error::MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY:
+ return "Message must have integer 'id' property";
+ case Error::MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY:
+ return "Message must have string 'method' property";
+ case Error::MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY:
+ return "Message may have string 'sessionId' property";
+ case Error::MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY:
+ return "Message may have object 'params' property";
+ case Error::MESSAGE_HAS_UNKNOWN_PROPERTY:
+ return "Message has property other than "
+ "'id', 'method', 'sessionId', 'params'";
case Error::BINDINGS_MANDATORY_FIELD_MISSING:
- return ToASCIIString("BINDINGS: mandatory field missing");
+ return "BINDINGS: mandatory field missing";
case Error::BINDINGS_BOOL_VALUE_EXPECTED:
- return ToASCIIString("BINDINGS: bool value expected");
+ return "BINDINGS: bool value expected";
case Error::BINDINGS_INT32_VALUE_EXPECTED:
- return ToASCIIString("BINDINGS: int32 value expected");
+ return "BINDINGS: int32 value expected";
case Error::BINDINGS_DOUBLE_VALUE_EXPECTED:
- return ToASCIIString("BINDINGS: double value expected");
+ return "BINDINGS: double value expected";
case Error::BINDINGS_STRING_VALUE_EXPECTED:
- return ToASCIIString("BINDINGS: string value expected");
+ return "BINDINGS: string value expected";
case Error::BINDINGS_STRING8_VALUE_EXPECTED:
- return ToASCIIString("BINDINGS: string8 value expected");
+ return "BINDINGS: string8 value expected";
case Error::BINDINGS_BINARY_VALUE_EXPECTED:
- return ToASCIIString("BINDINGS: binary value expected");
+ return "BINDINGS: binary value expected";
}
// Some compilers can't figure out that we can't get here.
return "INVALID ERROR CODE";
}
-std::string Status::ToASCIIString(const char* msg) const {
- return std::string(msg) + " at position " + std::to_string(pos);
+std::string Status::ToASCIIString() const {
+ if (ok())
+ return "OK";
+ return Message() + " at position " + std::to_string(pos);
}
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/status.h b/chromium/third_party/inspector_protocol/crdtp/status.h
index e65a1f23540..dc6c2e6c973 100644
--- a/chromium/third_party/inspector_protocol/crdtp/status.h
+++ b/chromium/third_party/inspector_protocol/crdtp/status.h
@@ -18,7 +18,9 @@ namespace crdtp {
enum class Error {
OK = 0,
- // JSON parsing errors - json_parser.{h,cc}.
+
+ // JSON parsing errors; checked when parsing / converting from JSON.
+ // See json.{h,cc}.
JSON_PARSER_UNPROCESSED_INPUT_REMAINS = 0x01,
JSON_PARSER_STACK_LIMIT_EXCEEDED = 0x02,
JSON_PARSER_NO_INPUT = 0x03,
@@ -33,6 +35,7 @@ enum class Error {
JSON_PARSER_COMMA_OR_MAP_END_EXPECTED = 0x0c,
JSON_PARSER_VALUE_EXPECTED = 0x0d,
+ // CBOR parsing errors; checked when parsing / converting from CBOR.
CBOR_INVALID_INT32 = 0x0e,
CBOR_INVALID_DOUBLE = 0x0f,
CBOR_INVALID_ENVELOPE = 0x10,
@@ -48,20 +51,31 @@ enum class Error {
CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x1a,
CBOR_UNEXPECTED_EOF_IN_MAP = 0x1b,
CBOR_INVALID_MAP_KEY = 0x1c,
- CBOR_STACK_LIMIT_EXCEEDED = 0x1d,
- CBOR_TRAILING_JUNK = 0x1e,
- CBOR_MAP_START_EXPECTED = 0x1f,
- CBOR_MAP_STOP_EXPECTED = 0x20,
- CBOR_ARRAY_START_EXPECTED = 0x21,
- CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x22,
-
- BINDINGS_MANDATORY_FIELD_MISSING = 0x23,
- BINDINGS_BOOL_VALUE_EXPECTED = 0x24,
- BINDINGS_INT32_VALUE_EXPECTED = 0x25,
- BINDINGS_DOUBLE_VALUE_EXPECTED = 0x26,
- BINDINGS_STRING_VALUE_EXPECTED = 0x27,
- BINDINGS_STRING8_VALUE_EXPECTED = 0x28,
- BINDINGS_BINARY_VALUE_EXPECTED = 0x29,
+ CBOR_DUPLICATE_MAP_KEY = 0x1d,
+ CBOR_STACK_LIMIT_EXCEEDED = 0x1e,
+ CBOR_TRAILING_JUNK = 0x1f,
+ CBOR_MAP_START_EXPECTED = 0x20,
+ CBOR_MAP_STOP_EXPECTED = 0x21,
+ CBOR_ARRAY_START_EXPECTED = 0x22,
+ CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x23,
+
+ // Message errors are constraints we place on protocol messages coming
+ // from a protocol client; these are checked in crdtp::Dispatchable
+ // (see dispatch.h) as it performs a shallow parse.
+ MESSAGE_MUST_BE_AN_OBJECT = 0x24,
+ MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY = 0x25,
+ MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY = 0x26,
+ MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY = 0x27,
+ MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY = 0x28,
+ MESSAGE_HAS_UNKNOWN_PROPERTY = 0x29,
+
+ BINDINGS_MANDATORY_FIELD_MISSING = 0x30,
+ BINDINGS_BOOL_VALUE_EXPECTED = 0x31,
+ BINDINGS_INT32_VALUE_EXPECTED = 0x32,
+ BINDINGS_DOUBLE_VALUE_EXPECTED = 0x33,
+ BINDINGS_STRING_VALUE_EXPECTED = 0x34,
+ BINDINGS_STRING8_VALUE_EXPECTED = 0x35,
+ BINDINGS_BINARY_VALUE_EXPECTED = 0x36,
};
// A status value with position that can be copied. The default status
@@ -76,12 +90,18 @@ struct CRDTP_EXPORT Status {
Status(Error error, size_t pos) : error(error), pos(pos) {}
Status() = default;
- // Returns a 7 bit US-ASCII string, either "OK" or an error message
- // that includes the position.
- std::string ToASCIIString() const;
+ bool IsMessageError() const {
+ return error >= Error::MESSAGE_MUST_BE_AN_OBJECT &&
+ error <= Error::MESSAGE_HAS_UNKNOWN_PROPERTY;
+ }
+
+ // Returns 7 bit US-ASCII string, either "OK" or an error message without
+ // position.
+ std::string Message() const;
- private:
- std::string ToASCIIString(const char* msg) const;
+ // Returns a 7 bit US-ASCII string, either "OK" or an error message that
+ // includes the position.
+ std::string ToASCIIString() const;
};
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/status_test.cc b/chromium/third_party/inspector_protocol/crdtp/status_test.cc
index 3d270b3e707..31462baa3db 100644
--- a/chromium/third_party/inspector_protocol/crdtp/status_test.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/status_test.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "status.h"
+#include "status_test_support.h"
#include "test_platform.h"
namespace crdtp {
@@ -18,4 +19,11 @@ TEST(StatusTest, StatusToASCIIString) {
Status cbor_error(Error::CBOR_TRAILING_JUNK, 21);
EXPECT_EQ("CBOR: trailing junk at position 21", cbor_error.ToASCIIString());
}
+
+TEST(StatusTest, StatusTestSupport) {
+ Status ok_status;
+ EXPECT_THAT(ok_status, StatusIsOk());
+ Status json_error(Error::JSON_PARSER_COLON_EXPECTED, 42);
+ EXPECT_THAT(json_error, StatusIs(Error::JSON_PARSER_COLON_EXPECTED, 42));
+}
} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/status_test_support.cc b/chromium/third_party/inspector_protocol/crdtp/status_test_support.cc
new file mode 100644
index 00000000000..a2c70157cfa
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/status_test_support.cc
@@ -0,0 +1,50 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "status_test_support.h"
+
+namespace crdtp {
+void PrintTo(const Status& status, std::ostream* os) {
+ *os << status.ToASCIIString() << " (error: 0x" << std::hex
+ << static_cast<int>(status.error) << ", "
+ << "pos: " << std::dec << status.pos << ")";
+}
+
+namespace {
+class StatusIsMatcher : public testing::MatcherInterface<Status> {
+ public:
+ explicit StatusIsMatcher(Status status) : expected_(status) {}
+
+ bool MatchAndExplain(Status status,
+ testing::MatchResultListener* listener) const override {
+ return status.error == expected_.error && status.pos == expected_.pos;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "equals to ";
+ PrintTo(expected_, os);
+ }
+
+ private:
+ Status expected_;
+};
+
+class StatusIsOkMatcher : public testing::MatcherInterface<Status> {
+ bool MatchAndExplain(Status status,
+ testing::MatchResultListener* listener) const override {
+ return status.ok();
+ }
+
+ void DescribeTo(std::ostream* os) const override { *os << "is ok"; }
+};
+} // namespace
+
+testing::Matcher<Status> StatusIsOk() {
+ return MakeMatcher(new StatusIsOkMatcher());
+}
+
+testing::Matcher<Status> StatusIs(Error error, size_t pos) {
+ return MakeMatcher(new StatusIsMatcher(Status(error, pos)));
+}
+} // namespace crdtp
diff --git a/chromium/third_party/inspector_protocol/crdtp/status_test_support.h b/chromium/third_party/inspector_protocol/crdtp/status_test_support.h
new file mode 100644
index 00000000000..fe53b310a44
--- /dev/null
+++ b/chromium/third_party/inspector_protocol/crdtp/status_test_support.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRDTP_STATUS_TEST_SUPPORT_H_
+#define CRDTP_STATUS_TEST_SUPPORT_H_
+
+#include <ostream>
+#include "status.h"
+#include "test_platform.h"
+
+namespace crdtp {
+// Supports gtest, to conveniently match Status objects and
+// get useful error messages when tests fail.
+// Typically used with EXPECT_THAT, e.g.
+//
+// EXPECT_THAT(status, StatusIs(Error::JSON_PARSER_COLON_EXPECTED, 42));
+//
+// EXPECT_THAT(status, StatusIsOk());
+
+// Prints a |status|, including its generated error message, error code, and
+// position. This is used by gtest for pretty printing actual vs. expected.
+void PrintTo(const Status& status, std::ostream* os);
+
+// Matches any status with |status.ok()|.
+testing::Matcher<Status> StatusIsOk();
+
+// Matches any status with |error| and |pos|.
+testing::Matcher<Status> StatusIs(Error error, size_t pos);
+} // namespace crdtp
+
+#endif // CRDTP_STATUS_TEST_SUPPORT_H_
diff --git a/chromium/third_party/inspector_protocol/crdtp/transcode.cc b/chromium/third_party/inspector_protocol/crdtp/transcode.cc
index 3b113e65a39..5642314531f 100644
--- a/chromium/third_party/inspector_protocol/crdtp/transcode.cc
+++ b/chromium/third_party/inspector_protocol/crdtp/transcode.cc
@@ -7,35 +7,10 @@
#include <sstream>
#include <string>
-#include "encoding.h"
+#include "json.h"
namespace crdtp {
-class SingleThreadedPlatform : public json::Platform {
- bool StrToD(const char* str, double* result) const override {
- const char* saved_locale = std::setlocale(LC_NUMERIC, nullptr);
- char* end;
- *result = std::strtod(str, &end);
- std::setlocale(LC_NUMERIC, saved_locale);
- if (errno == ERANGE) {
- // errno must be reset, e.g. see the example here:
- // https://en.cppreference.com/w/cpp/string/byte/strtof
- errno = 0;
- return false;
- }
- return end == str + strlen(str);
- }
-
- std::unique_ptr<char[]> DToStr(double value) const override {
- std::stringstream ss;
- ss.imbue(std::locale("C"));
- ss << value;
- std::string str = ss.str();
- std::unique_ptr<char[]> result(new char[str.size() + 1]);
- memcpy(result.get(), str.c_str(), str.size() + 1);
- return result;
- }
-};
-
+namespace {
int Transcode(const std::string& cmd,
const std::string& input_file_name,
const std::string& output_file_name) {
@@ -51,12 +26,11 @@ int Transcode(const std::string& cmd,
in += buffer.substr(0, input_file.gcount());
}
Status status;
- SingleThreadedPlatform platform;
- std::string out;
+ std::vector<uint8_t> out;
if (cmd == "--json-to-cbor") {
- status = json::ConvertJSONToCBOR(platform, SpanFrom(in), &out);
+ status = json::ConvertJSONToCBOR(SpanFrom(in), &out);
} else if (cmd == "--cbor-to-json") {
- status = json::ConvertCBORToJSON(platform, SpanFrom(in), &out);
+ status = json::ConvertCBORToJSON(SpanFrom(in), &out);
} else {
std::cerr << "unknown command " << cmd << "\n";
return 1;
@@ -70,9 +44,10 @@ int Transcode(const std::string& cmd,
std::cerr << "failed to open " << output_file_name << "\n";
return 1;
}
- output_file.write(out.data(), out.size());
+ output_file.write(reinterpret_cast<const char*>(out.data()), out.size());
return 0;
}
+} // namespace
} // namespace crdtp
int main(int argc, char** argv) {
diff --git a/chromium/third_party/inspector_protocol/inspector_protocol.gni b/chromium/third_party/inspector_protocol/inspector_protocol.gni
index 6e83e87d2a7..f4823847df5 100644
--- a/chromium/third_party/inspector_protocol/inspector_protocol.gni
+++ b/chromium/third_party/inspector_protocol/inspector_protocol.gni
@@ -33,16 +33,9 @@ template("inspector_protocol_generate") {
invoker.config_file,
"$inspector_protocol_dir/lib/base_string_adapter_cc.template",
"$inspector_protocol_dir/lib/base_string_adapter_h.template",
- "$inspector_protocol_dir/lib/DispatcherBase_cpp.template",
- "$inspector_protocol_dir/lib/DispatcherBase_h.template",
- "$inspector_protocol_dir/lib/ErrorSupport_cpp.template",
- "$inspector_protocol_dir/lib/ErrorSupport_h.template",
"$inspector_protocol_dir/lib/Forward_h.template",
- "$inspector_protocol_dir/lib/FrontendChannel_h.template",
"$inspector_protocol_dir/lib/Object_cpp.template",
"$inspector_protocol_dir/lib/Object_h.template",
- "$inspector_protocol_dir/lib/Parser_cpp.template",
- "$inspector_protocol_dir/lib/Parser_h.template",
"$inspector_protocol_dir/lib/Protocol_cpp.template",
"$inspector_protocol_dir/lib/ValueConversions_h.template",
"$inspector_protocol_dir/lib/Values_cpp.template",
@@ -58,7 +51,9 @@ template("inspector_protocol_generate") {
args = [
"--jinja_dir",
- rebase_path("//third_party/", root_build_dir), # jinja is in chromium's third_party
+ rebase_path("//third_party/", root_build_dir), # jinja is in chromium's
+ # third_party
+
"--output_base",
rebase_path(invoker.out_dir, root_build_dir),
"--config",
diff --git a/chromium/third_party/inspector_protocol/lib/DispatcherBase_cpp.template b/chromium/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
deleted file mode 100644
index 22fabf9bfb9..00000000000
--- a/chromium/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
+++ /dev/null
@@ -1,355 +0,0 @@
-// This file is generated by DispatcherBase_cpp.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//#include "DispatcherBase.h"
-//#include "Parser.h"
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-// static
-DispatchResponse DispatchResponse::OK()
-{
- DispatchResponse result;
- result.m_status = kSuccess;
- result.m_errorCode = kParseError;
- return result;
-}
-
-// static
-DispatchResponse DispatchResponse::Error(const String& error)
-{
- DispatchResponse result;
- result.m_status = kError;
- result.m_errorCode = kServerError;
- result.m_errorMessage = error;
- return result;
-}
-
-// static
-DispatchResponse DispatchResponse::InternalError()
-{
- DispatchResponse result;
- result.m_status = kError;
- result.m_errorCode = kInternalError;
- result.m_errorMessage = "Internal error";
- return result;
-}
-
-// static
-DispatchResponse DispatchResponse::InvalidParams(const String& error)
-{
- DispatchResponse result;
- result.m_status = kError;
- result.m_errorCode = kInvalidParams;
- result.m_errorMessage = error;
- return result;
-}
-
-// static
-DispatchResponse DispatchResponse::FallThrough()
-{
- DispatchResponse result;
- result.m_status = kFallThrough;
- result.m_errorCode = kParseError;
- return result;
-}
-
-// static
-const char DispatcherBase::kInvalidParamsString[] = "Invalid parameters";
-
-DispatcherBase::WeakPtr::WeakPtr(DispatcherBase* dispatcher) : m_dispatcher(dispatcher) { }
-
-DispatcherBase::WeakPtr::~WeakPtr()
-{
- if (m_dispatcher)
- m_dispatcher->m_weakPtrs.erase(this);
-}
-
-DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, const ProtocolMessage& message)
- : m_backendImpl(std::move(backendImpl))
- , m_callId(callId)
- , m_method(method)
- , m_message(message) { }
-
-DispatcherBase::Callback::~Callback() = default;
-
-void DispatcherBase::Callback::dispose()
-{
- m_backendImpl = nullptr;
-}
-
-void DispatcherBase::Callback::sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const DispatchResponse& response)
-{
- if (!m_backendImpl || !m_backendImpl->get())
- return;
- m_backendImpl->get()->sendResponse(m_callId, response, std::move(partialMessage));
- m_backendImpl = nullptr;
-}
-
-void DispatcherBase::Callback::fallThroughIfActive()
-{
- if (!m_backendImpl || !m_backendImpl->get())
- return;
- m_backendImpl->get()->channel()->fallThrough(m_callId, m_method, m_message);
- m_backendImpl = nullptr;
-}
-
-DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel)
- : m_frontendChannel(frontendChannel) { }
-
-DispatcherBase::~DispatcherBase()
-{
- clearFrontend();
-}
-
-void DispatcherBase::sendResponse(int callId, const DispatchResponse& response, std::unique_ptr<protocol::DictionaryValue> result)
-{
- if (!m_frontendChannel)
- return;
- if (response.status() == DispatchResponse::kError) {
- reportProtocolError(callId, response.errorCode(), response.errorMessage(), nullptr);
- return;
- }
- m_frontendChannel->sendProtocolResponse(callId, InternalResponse::createResponse(callId, std::move(result)));
-}
-
-void DispatcherBase::sendResponse(int callId, const DispatchResponse& response)
-{
- sendResponse(callId, response, DictionaryValue::create());
-}
-
-namespace {
-
-class ProtocolError : public Serializable {
-public:
- static std::unique_ptr<ProtocolError> createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors)
- {
- std::unique_ptr<ProtocolError> protocolError(new ProtocolError(code, errorMessage));
- protocolError->m_callId = callId;
- protocolError->m_hasCallId = true;
- if (errors && errors->hasErrors())
- protocolError->m_data = errors->errors();
- return protocolError;
- }
-
- static std::unique_ptr<ProtocolError> createErrorNotification(DispatchResponse::ErrorCode code, const String& errorMessage)
- {
- return std::unique_ptr<ProtocolError>(new ProtocolError(code, errorMessage));
- }
-
- void AppendSerialized(std::vector<uint8_t>* out) const override
- {
- toDictionary()->AppendSerialized(out);
- }
-
- ~ProtocolError() override {}
-
-private:
- ProtocolError(DispatchResponse::ErrorCode code, const String& errorMessage)
- : m_code(code)
- , m_errorMessage(errorMessage)
- {
- }
-
- std::unique_ptr<DictionaryValue> toDictionary() const {
- std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create();
- error->setInteger("code", m_code);
- error->setString("message", m_errorMessage);
- if (m_data.length())
- error->setString("data", m_data);
- std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create();
- message->setObject("error", std::move(error));
- if (m_hasCallId)
- message->setInteger("id", m_callId);
- return message;
- }
-
- DispatchResponse::ErrorCode m_code;
- String m_errorMessage;
- String m_data;
- int m_callId = 0;
- bool m_hasCallId = false;
-};
-
-} // namespace
-
-static void reportProtocolErrorTo(FrontendChannel* frontendChannel, int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors)
-{
- if (frontendChannel)
- frontendChannel->sendProtocolResponse(callId, ProtocolError::createErrorResponse(callId, code, errorMessage, errors));
-}
-
-static void reportProtocolErrorTo(FrontendChannel* frontendChannel, DispatchResponse::ErrorCode code, const String& errorMessage)
-{
- if (frontendChannel)
- frontendChannel->sendProtocolNotification(ProtocolError::createErrorNotification(code, errorMessage));
-}
-
-void DispatcherBase::reportProtocolError(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors)
-{
- reportProtocolErrorTo(m_frontendChannel, callId, code, errorMessage, errors);
-}
-
-void DispatcherBase::clearFrontend()
-{
- m_frontendChannel = nullptr;
- for (auto& weak : m_weakPtrs)
- weak->dispose();
- m_weakPtrs.clear();
-}
-
-std::unique_ptr<DispatcherBase::WeakPtr> DispatcherBase::weakPtr()
-{
- std::unique_ptr<DispatcherBase::WeakPtr> weak(new DispatcherBase::WeakPtr(this));
- m_weakPtrs.insert(weak.get());
- return weak;
-}
-
-UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel)
- : m_frontendChannel(frontendChannel) { }
-
-void UberDispatcher::registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase> dispatcher)
-{
- m_dispatchers[name] = std::move(dispatcher);
-}
-
-void UberDispatcher::setupRedirects(const std::unordered_map<String, String>& redirects)
-{
- for (const auto& pair : redirects)
- m_redirects[pair.first] = pair.second;
-}
-
-bool UberDispatcher::parseCommand(Value* parsedMessage, int* outCallId, String* outMethod) {
- if (!parsedMessage) {
- reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON");
- return false;
- }
- protocol::DictionaryValue* messageObject = DictionaryValue::cast(parsedMessage);
- if (!messageObject) {
- reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object");
- return false;
- }
-
- int callId = 0;
- protocol::Value* callIdValue = messageObject->get("id");
- bool success = callIdValue && callIdValue->asInteger(&callId);
- if (!success) {
- reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have integer 'id' property");
- return false;
- }
- if (outCallId)
- *outCallId = callId;
-
- protocol::Value* methodValue = messageObject->get("method");
- String method;
- success = methodValue && methodValue->asString(&method);
- if (!success) {
- reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kInvalidRequest, "Message must have string 'method' property", nullptr);
- return false;
- }
- if (outMethod)
- *outMethod = method;
- return true;
-}
-
-protocol::DispatcherBase* UberDispatcher::findDispatcher(const String& method) {
- size_t dotIndex = StringUtil::find(method, ".");
- if (dotIndex == StringUtil::kNotFound)
- return nullptr;
- String domain = StringUtil::substring(method, 0, dotIndex);
- auto it = m_dispatchers.find(domain);
- if (it == m_dispatchers.end())
- return nullptr;
- if (!it->second->canDispatch(method))
- return nullptr;
- return it->second.get();
-}
-
-bool UberDispatcher::canDispatch(const String& in_method)
-{
- String method = in_method;
- auto redirectIt = m_redirects.find(method);
- if (redirectIt != m_redirects.end())
- method = redirectIt->second;
- return !!findDispatcher(method);
-}
-
-void UberDispatcher::dispatch(int callId, const String& in_method, std::unique_ptr<Value> parsedMessage, const ProtocolMessage& rawMessage)
-{
- String method = in_method;
- auto redirectIt = m_redirects.find(method);
- if (redirectIt != m_redirects.end())
- method = redirectIt->second;
- protocol::DispatcherBase* dispatcher = findDispatcher(method);
- if (!dispatcher) {
- reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
- return;
- }
- std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage));
- dispatcher->dispatch(callId, method, rawMessage, std::move(messageObject));
-}
-
-UberDispatcher::~UberDispatcher() = default;
-
-// static
-std::unique_ptr<Serializable> InternalResponse::createResponse(int callId, std::unique_ptr<Serializable> params)
-{
- return std::unique_ptr<Serializable>(new InternalResponse(callId, nullptr, std::move(params)));
-}
-
-// static
-std::unique_ptr<Serializable> InternalResponse::createNotification(const char* method, std::unique_ptr<Serializable> params)
-{
- return std::unique_ptr<Serializable>(new InternalResponse(0, method, std::move(params)));
-}
-
-// static
-std::unique_ptr<Serializable> InternalResponse::createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& message)
-{
- return ProtocolError::createErrorResponse(callId, code, message, nullptr);
-}
-
-void InternalResponse::AppendSerialized(std::vector<uint8_t>* out) const
-{
- using {{config.crdtp.namespace}}::cbor::NewCBOREncoder;
- using {{config.crdtp.namespace}}::ParserHandler;
- using {{config.crdtp.namespace}}::Status;
- using {{config.crdtp.namespace}}::SpanFrom;
-
- Status status;
- std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(out, &status);
- encoder->HandleMapBegin();
- if (m_method) {
- encoder->HandleString8(SpanFrom("method"));
- encoder->HandleString8(SpanFrom(m_method));
- encoder->HandleString8(SpanFrom("params"));
- } else {
- encoder->HandleString8(SpanFrom("id"));
- encoder->HandleInt32(m_callId);
- encoder->HandleString8(SpanFrom("result"));
- }
- if (m_params) {
- m_params->AppendSerialized(out);
- } else {
- encoder->HandleMapBegin();
- encoder->HandleMapEnd();
- }
- encoder->HandleMapEnd();
- DCHECK(status.ok());
-}
-
-InternalResponse::InternalResponse(int callId, const char* method, std::unique_ptr<Serializable> params)
- : m_callId(callId)
- , m_method(method)
- , m_params(params ? std::move(params) : nullptr)
-{
-}
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/lib/DispatcherBase_h.template b/chromium/third_party/inspector_protocol/lib/DispatcherBase_h.template
deleted file mode 100644
index 8a7dfceec39..00000000000
--- a/chromium/third_party/inspector_protocol/lib/DispatcherBase_h.template
+++ /dev/null
@@ -1,176 +0,0 @@
-// This file is generated by DispatcherBase_h.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
-#define {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
-
-//#include "Forward.h"
-//#include "ErrorSupport.h"
-//#include "Values.h"
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-class WeakPtr;
-
-class {{config.lib.export_macro}} DispatchResponse {
-public:
- enum Status {
- kSuccess = 0,
- kError = 1,
- kFallThrough = 2,
- };
-
- // For historical reasons, these error codes correspond to commonly used
- // XMLRPC codes (e.g. see METHOD_NOT_FOUND in
- // https://github.com/python/cpython/blob/master/Lib/xmlrpc/client.py).
- enum ErrorCode {
- kParseError = -32700,
- kInvalidRequest = -32600,
- kMethodNotFound = -32601,
- kInvalidParams = -32602,
- kInternalError = -32603,
- kServerError = -32000,
- };
-
- Status status() const { return m_status; }
- const String& errorMessage() const { return m_errorMessage; }
- ErrorCode errorCode() const { return m_errorCode; }
- bool isSuccess() const { return m_status == kSuccess; }
-
- static DispatchResponse OK();
- static DispatchResponse Error(const String&);
- static DispatchResponse InternalError();
- static DispatchResponse InvalidParams(const String&);
- static DispatchResponse FallThrough();
-
-private:
- Status m_status;
- String m_errorMessage;
- ErrorCode m_errorCode;
-};
-
-class {{config.lib.export_macro}} DispatcherBase {
- PROTOCOL_DISALLOW_COPY(DispatcherBase);
-public:
- static const char kInvalidParamsString[];
- class {{config.lib.export_macro}} WeakPtr {
- public:
- explicit WeakPtr(DispatcherBase*);
- ~WeakPtr();
- DispatcherBase* get() { return m_dispatcher; }
- void dispose() { m_dispatcher = nullptr; }
-
- private:
- DispatcherBase* m_dispatcher;
- };
-
- class {{config.lib.export_macro}} Callback {
- public:
- Callback(std::unique_ptr<WeakPtr> backendImpl, int callId, const String& method, const ProtocolMessage& message);
- virtual ~Callback();
- void dispose();
-
- protected:
- void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const DispatchResponse& response);
- void fallThroughIfActive();
-
- private:
- std::unique_ptr<WeakPtr> m_backendImpl;
- int m_callId;
- String m_method;
- ProtocolMessage m_message;
- };
-
- explicit DispatcherBase(FrontendChannel*);
- virtual ~DispatcherBase();
-
- virtual bool canDispatch(const String& method) = 0;
- virtual void dispatch(int callId, const String& method, const ProtocolMessage& rawMessage, std::unique_ptr<protocol::DictionaryValue> messageObject) = 0;
- FrontendChannel* channel() { return m_frontendChannel; }
-
- void sendResponse(int callId, const DispatchResponse&, std::unique_ptr<protocol::DictionaryValue> result);
- void sendResponse(int callId, const DispatchResponse&);
-
- void reportProtocolError(int callId, DispatchResponse::ErrorCode, const String& errorMessage, ErrorSupport* errors);
- void clearFrontend();
-
- std::unique_ptr<WeakPtr> weakPtr();
-
-private:
- FrontendChannel* m_frontendChannel;
- std::unordered_set<WeakPtr*> m_weakPtrs;
-};
-
-class {{config.lib.export_macro}} UberDispatcher {
- PROTOCOL_DISALLOW_COPY(UberDispatcher);
-public:
- explicit UberDispatcher(FrontendChannel*);
- void registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase>);
- void setupRedirects(const std::unordered_map<String, String>&);
- bool parseCommand(Value* message, int* callId, String* method);
- bool canDispatch(const String& method);
- void dispatch(int callId, const String& method, std::unique_ptr<Value> message, const ProtocolMessage& rawMessage);
- FrontendChannel* channel() { return m_frontendChannel; }
- virtual ~UberDispatcher();
-
-private:
- protocol::DispatcherBase* findDispatcher(const String& method);
- FrontendChannel* m_frontendChannel;
- std::unordered_map<String, String> m_redirects;
- std::unordered_map<String, std::unique_ptr<protocol::DispatcherBase>> m_dispatchers;
-};
-
-class InternalResponse : public Serializable {
- PROTOCOL_DISALLOW_COPY(InternalResponse);
-public:
- static std::unique_ptr<Serializable> createResponse(int callId, std::unique_ptr<Serializable> params);
- static std::unique_ptr<Serializable> createNotification(const char* method, std::unique_ptr<Serializable> params = nullptr);
- static std::unique_ptr<Serializable> createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& message);
-
- void AppendSerialized(std::vector<uint8_t>* out) const override;
-
- ~InternalResponse() override {}
-
-private:
- InternalResponse(int callId, const char* method, std::unique_ptr<Serializable> params);
-
- int m_callId;
- const char* m_method = nullptr;
- std::unique_ptr<Serializable> m_params;
-};
-
-class InternalRawNotification : public Serializable {
-public:
- static std::unique_ptr<InternalRawNotification> fromBinary(std::vector<uint8_t> notification)
- {
- return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(std::move(notification)));
- }
-
- ~InternalRawNotification() override {}
-
- std::vector<uint8_t> TakeSerialized() && override {
- return std::move(m_binaryNotification);
- }
-
- void AppendSerialized(std::vector<uint8_t>* out) const override
- {
- out->insert(out->end(), m_binaryNotification.begin(), m_binaryNotification.end());
- }
-
-private:
- explicit InternalRawNotification(std::vector<uint8_t> notification)
- : m_binaryNotification(std::move(notification)) { }
-
- std::vector<uint8_t> m_binaryNotification;
-};
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
-
-#endif // !defined({{"_".join(config.protocol.namespace)}}_DispatcherBase_h)
diff --git a/chromium/third_party/inspector_protocol/lib/ErrorSupport_cpp.template b/chromium/third_party/inspector_protocol/lib/ErrorSupport_cpp.template
deleted file mode 100644
index a5c2a79bbd2..00000000000
--- a/chromium/third_party/inspector_protocol/lib/ErrorSupport_cpp.template
+++ /dev/null
@@ -1,73 +0,0 @@
-// This file is generated by ErrorSupport_cpp.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//#include "ErrorSupport.h"
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-ErrorSupport::ErrorSupport() { }
-ErrorSupport::~ErrorSupport() { }
-
-void ErrorSupport::setName(const char* name)
-{
- setName(String(name));
-}
-
-void ErrorSupport::setName(const String& name)
-{
- DCHECK(m_path.size());
- m_path[m_path.size() - 1] = name;
-}
-
-void ErrorSupport::push()
-{
- m_path.push_back(String());
-}
-
-void ErrorSupport::pop()
-{
- m_path.pop_back();
-}
-
-void ErrorSupport::addError(const char* error)
-{
- addError(String(error));
-}
-
-void ErrorSupport::addError(const String& error)
-{
- StringBuilder builder;
- for (size_t i = 0; i < m_path.size(); ++i) {
- if (i)
- StringUtil::builderAppend(builder, '.');
- StringUtil::builderAppend(builder, m_path[i]);
- }
- StringUtil::builderAppend(builder, ": ");
- StringUtil::builderAppend(builder, error);
- m_errors.push_back(StringUtil::builderToString(builder));
-}
-
-bool ErrorSupport::hasErrors()
-{
- return !!m_errors.size();
-}
-
-String ErrorSupport::errors()
-{
- StringBuilder builder;
- for (size_t i = 0; i < m_errors.size(); ++i) {
- if (i)
- StringUtil::builderAppend(builder, "; ");
- StringUtil::builderAppend(builder, m_errors[i]);
- }
- return StringUtil::builderToString(builder);
-}
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/lib/ErrorSupport_h.template b/chromium/third_party/inspector_protocol/lib/ErrorSupport_h.template
deleted file mode 100644
index f317a3cfb41..00000000000
--- a/chromium/third_party/inspector_protocol/lib/ErrorSupport_h.template
+++ /dev/null
@@ -1,39 +0,0 @@
-// This file is generated by ErrorSupport_h.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef {{"_".join(config.protocol.namespace)}}_ErrorSupport_h
-#define {{"_".join(config.protocol.namespace)}}_ErrorSupport_h
-
-#include {{format_include(config.protocol.package, "Forward")}}
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-class {{config.lib.export_macro}} ErrorSupport {
-public:
- ErrorSupport();
- ~ErrorSupport();
-
- void push();
- void setName(const char*);
- void setName(const String&);
- void pop();
- void addError(const char*);
- void addError(const String&);
- bool hasErrors();
- String errors();
-
-private:
- std::vector<String> m_path;
- std::vector<String> m_errors;
-};
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
-
-#endif // !defined({{"_".join(config.protocol.namespace)}}_ErrorSupport_h)
diff --git a/chromium/third_party/inspector_protocol/lib/Forward_h.template b/chromium/third_party/inspector_protocol/lib/Forward_h.template
index aefc1a16e80..e2eef3042f5 100644
--- a/chromium/third_party/inspector_protocol/lib/Forward_h.template
+++ b/chromium/third_party/inspector_protocol/lib/Forward_h.template
@@ -18,6 +18,9 @@
#include <unordered_map>
#include <unordered_set>
+#include "{{config.crdtp.dir}}/error_support.h"
+#include "{{config.crdtp.dir}}/dispatch.h"
+#include "{{config.crdtp.dir}}/frontend_channel.h"
#include "{{config.crdtp.dir}}/glue.h"
{% for namespace in config.protocol.namespace %}
@@ -25,15 +28,18 @@ namespace {{namespace}} {
{% endfor %}
class DictionaryValue;
-class DispatchResponse;
-class ErrorSupport;
+using DispatchResponse = {{config.crdtp.namespace}}::DispatchResponse;
+using ErrorSupport = {{config.crdtp.namespace}}::ErrorSupport;
+using Serializable = {{config.crdtp.namespace}}::Serializable;
+using FrontendChannel = {{config.crdtp.namespace}}::FrontendChannel;
+using DomainDispatcher = {{config.crdtp.namespace}}::DomainDispatcher;
+using UberDispatcher = {{config.crdtp.namespace}}::UberDispatcher;
class FundamentalValue;
class ListValue;
class Object;
using Response = DispatchResponse;
class SerializedValue;
class StringValue;
-class UberDispatcher;
class Value;
namespace detail {
diff --git a/chromium/third_party/inspector_protocol/lib/FrontendChannel_h.template b/chromium/third_party/inspector_protocol/lib/FrontendChannel_h.template
deleted file mode 100644
index 350c9a84dcc..00000000000
--- a/chromium/third_party/inspector_protocol/lib/FrontendChannel_h.template
+++ /dev/null
@@ -1,31 +0,0 @@
-// This file is generated by FrontendChannel_h.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef {{"_".join(config.protocol.namespace)}}_FrontendChannel_h
-#define {{"_".join(config.protocol.namespace)}}_FrontendChannel_h
-
-#include "{{config.crdtp.dir}}/serializable.h"
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-using {{config.crdtp.namespace}}::Serializable;
-
-class {{config.lib.export_macro}} FrontendChannel {
-public:
- virtual ~FrontendChannel() { }
- virtual void sendProtocolResponse(int callId, std::unique_ptr<Serializable> message) = 0;
- virtual void sendProtocolNotification(std::unique_ptr<Serializable> message) = 0;
- virtual void fallThrough(int callId, const String& method, const ProtocolMessage& message) = 0;
- virtual void flushProtocolNotifications() = 0;
-};
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
-
-#endif // !defined({{"_".join(config.protocol.namespace)}}_FrontendChannel_h)
diff --git a/chromium/third_party/inspector_protocol/lib/Object_cpp.template b/chromium/third_party/inspector_protocol/lib/Object_cpp.template
index 1640a11127b..b639b1bb776 100644
--- a/chromium/third_party/inspector_protocol/lib/Object_cpp.template
+++ b/chromium/third_party/inspector_protocol/lib/Object_cpp.template
@@ -14,13 +14,18 @@ std::unique_ptr<Object> Object::fromValue(protocol::Value* value, ErrorSupport*
{
protocol::DictionaryValue* dictionary = DictionaryValue::cast(value);
if (!dictionary) {
- errors->addError("object expected");
+ errors->AddError("object expected");
return nullptr;
}
dictionary = static_cast<protocol::DictionaryValue*>(dictionary->clone().release());
return std::unique_ptr<Object>(new Object(std::unique_ptr<DictionaryValue>(dictionary)));
}
+// Implements Serializable.
+void Object::AppendSerialized(std::vector<uint8_t>* out) const {
+ m_object->AppendSerialized(out);
+}
+
std::unique_ptr<protocol::DictionaryValue> Object::toValue() const
{
return DictionaryValue::cast(m_object->clone());
diff --git a/chromium/third_party/inspector_protocol/lib/Object_h.template b/chromium/third_party/inspector_protocol/lib/Object_h.template
index ec953d0d483..8a1a2e39f8a 100644
--- a/chromium/third_party/inspector_protocol/lib/Object_h.template
+++ b/chromium/third_party/inspector_protocol/lib/Object_h.template
@@ -11,16 +11,21 @@
//#include "Forward.h"
//#include "Values.h"
+#include "{{config.crdtp.dir}}/serializable.h"
+
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
-class {{config.lib.export_macro}} Object {
+class {{config.lib.export_macro}} Object : public {{config.crdtp.namespace}}::Serializable {
public:
static std::unique_ptr<Object> fromValue(protocol::Value*, ErrorSupport*);
explicit Object(std::unique_ptr<protocol::DictionaryValue>);
~Object();
+ // Implements Serializable.
+ void AppendSerialized(std::vector<uint8_t>* out) const override;
+
std::unique_ptr<protocol::DictionaryValue> toValue() const;
std::unique_ptr<Object> clone() const;
private:
diff --git a/chromium/third_party/inspector_protocol/lib/Parser_cpp.template b/chromium/third_party/inspector_protocol/lib/Parser_cpp.template
deleted file mode 100644
index ea7ecc5a1a4..00000000000
--- a/chromium/third_party/inspector_protocol/lib/Parser_cpp.template
+++ /dev/null
@@ -1,548 +0,0 @@
-// This file is generated by Parser_cpp.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-namespace {
-
-const int stackLimit = 1000;
-
-enum Token {
- ObjectBegin,
- ObjectEnd,
- ArrayBegin,
- ArrayEnd,
- StringLiteral,
- Number,
- BoolTrue,
- BoolFalse,
- NullToken,
- ListSeparator,
- ObjectPairSeparator,
- InvalidToken,
-};
-
-const char* const nullString = "null";
-const char* const trueString = "true";
-const char* const falseString = "false";
-
-bool isASCII(uint16_t c)
-{
- return !(c & ~0x7F);
-}
-
-bool isSpaceOrNewLine(uint16_t c)
-{
- return isASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9));
-}
-
-double charactersToDouble(const uint16_t* characters, size_t length, bool* ok)
-{
- std::vector<char> buffer;
- buffer.reserve(length + 1);
- for (size_t i = 0; i < length; ++i) {
- if (!isASCII(characters[i])) {
- *ok = false;
- return 0;
- }
- buffer.push_back(static_cast<char>(characters[i]));
- }
- buffer.push_back('\0');
- return StringUtil::toDouble(buffer.data(), length, ok);
-}
-
-double charactersToDouble(const uint8_t* characters, size_t length, bool* ok)
-{
- std::string buffer(reinterpret_cast<const char*>(characters), length);
- return StringUtil::toDouble(buffer.data(), length, ok);
-}
-
-template<typename Char>
-bool parseConstToken(const Char* start, const Char* end, const Char** tokenEnd, const char* token)
-{
- while (start < end && *token != '\0' && *start++ == *token++) { }
- if (*token != '\0')
- return false;
- *tokenEnd = start;
- return true;
-}
-
-template<typename Char>
-bool readInt(const Char* start, const Char* end, const Char** tokenEnd, bool canHaveLeadingZeros)
-{
- if (start == end)
- return false;
- bool haveLeadingZero = '0' == *start;
- int length = 0;
- while (start < end && '0' <= *start && *start <= '9') {
- ++start;
- ++length;
- }
- if (!length)
- return false;
- if (!canHaveLeadingZeros && length > 1 && haveLeadingZero)
- return false;
- *tokenEnd = start;
- return true;
-}
-
-template<typename Char>
-bool parseNumberToken(const Char* start, const Char* end, const Char** tokenEnd)
-{
- // We just grab the number here. We validate the size in DecodeNumber.
- // According to RFC4627, a valid number is: [minus] int [frac] [exp]
- if (start == end)
- return false;
- Char c = *start;
- if ('-' == c)
- ++start;
-
- if (!readInt(start, end, &start, false))
- return false;
- if (start == end) {
- *tokenEnd = start;
- return true;
- }
-
- // Optional fraction part
- c = *start;
- if ('.' == c) {
- ++start;
- if (!readInt(start, end, &start, true))
- return false;
- if (start == end) {
- *tokenEnd = start;
- return true;
- }
- c = *start;
- }
-
- // Optional exponent part
- if ('e' == c || 'E' == c) {
- ++start;
- if (start == end)
- return false;
- c = *start;
- if ('-' == c || '+' == c) {
- ++start;
- if (start == end)
- return false;
- }
- if (!readInt(start, end, &start, true))
- return false;
- }
-
- *tokenEnd = start;
- return true;
-}
-
-template<typename Char>
-bool readHexDigits(const Char* start, const Char* end, const Char** tokenEnd, int digits)
-{
- if (end - start < digits)
- return false;
- for (int i = 0; i < digits; ++i) {
- Char c = *start++;
- if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')))
- return false;
- }
- *tokenEnd = start;
- return true;
-}
-
-template<typename Char>
-bool parseStringToken(const Char* start, const Char* end, const Char** tokenEnd)
-{
- while (start < end) {
- Char c = *start++;
- if ('\\' == c) {
- if (start == end)
- return false;
- c = *start++;
- // Make sure the escaped char is valid.
- switch (c) {
- case 'x':
- if (!readHexDigits(start, end, &start, 2))
- return false;
- break;
- case 'u':
- if (!readHexDigits(start, end, &start, 4))
- return false;
- break;
- case '\\':
- case '/':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- case 'v':
- case '"':
- break;
- default:
- return false;
- }
- } else if ('"' == c) {
- *tokenEnd = start;
- return true;
- }
- }
- return false;
-}
-
-template<typename Char>
-bool skipComment(const Char* start, const Char* end, const Char** commentEnd)
-{
- if (start == end)
- return false;
-
- if (*start != '/' || start + 1 >= end)
- return false;
- ++start;
-
- if (*start == '/') {
- // Single line comment, read to newline.
- for (++start; start < end; ++start) {
- if (*start == '\n' || *start == '\r') {
- *commentEnd = start + 1;
- return true;
- }
- }
- *commentEnd = end;
- // Comment reaches end-of-input, which is fine.
- return true;
- }
-
- if (*start == '*') {
- Char previous = '\0';
- // Block comment, read until end marker.
- for (++start; start < end; previous = *start++) {
- if (previous == '*' && *start == '/') {
- *commentEnd = start + 1;
- return true;
- }
- }
- // Block comment must close before end-of-input.
- return false;
- }
-
- return false;
-}
-
-template<typename Char>
-void skipWhitespaceAndComments(const Char* start, const Char* end, const Char** whitespaceEnd)
-{
- while (start < end) {
- if (isSpaceOrNewLine(*start)) {
- ++start;
- } else if (*start == '/') {
- const Char* commentEnd;
- if (!skipComment(start, end, &commentEnd))
- break;
- start = commentEnd;
- } else {
- break;
- }
- }
- *whitespaceEnd = start;
-}
-
-template<typename Char>
-Token parseToken(const Char* start, const Char* end, const Char** tokenStart, const Char** tokenEnd)
-{
- skipWhitespaceAndComments(start, end, tokenStart);
- start = *tokenStart;
-
- if (start == end)
- return InvalidToken;
-
- switch (*start) {
- case 'n':
- if (parseConstToken(start, end, tokenEnd, nullString))
- return NullToken;
- break;
- case 't':
- if (parseConstToken(start, end, tokenEnd, trueString))
- return BoolTrue;
- break;
- case 'f':
- if (parseConstToken(start, end, tokenEnd, falseString))
- return BoolFalse;
- break;
- case '[':
- *tokenEnd = start + 1;
- return ArrayBegin;
- case ']':
- *tokenEnd = start + 1;
- return ArrayEnd;
- case ',':
- *tokenEnd = start + 1;
- return ListSeparator;
- case '{':
- *tokenEnd = start + 1;
- return ObjectBegin;
- case '}':
- *tokenEnd = start + 1;
- return ObjectEnd;
- case ':':
- *tokenEnd = start + 1;
- return ObjectPairSeparator;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '-':
- if (parseNumberToken(start, end, tokenEnd))
- return Number;
- break;
- case '"':
- if (parseStringToken(start + 1, end, tokenEnd))
- return StringLiteral;
- break;
- }
- return InvalidToken;
-}
-
-template<typename Char>
-int hexToInt(Char c)
-{
- if ('0' <= c && c <= '9')
- return c - '0';
- if ('A' <= c && c <= 'F')
- return c - 'A' + 10;
- if ('a' <= c && c <= 'f')
- return c - 'a' + 10;
- DCHECK(false);
- return 0;
-}
-
-template<typename Char>
-bool decodeString(const Char* start, const Char* end, StringBuilder* output)
-{
- while (start < end) {
- uint16_t c = *start++;
- if ('\\' != c) {
- StringUtil::builderAppend(*output, c);
- continue;
- }
- if (start == end)
- return false;
- c = *start++;
-
- if (c == 'x') {
- // \x is not supported.
- return false;
- }
-
- switch (c) {
- case '"':
- case '/':
- case '\\':
- break;
- case 'b':
- c = '\b';
- break;
- case 'f':
- c = '\f';
- break;
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 't':
- c = '\t';
- break;
- case 'v':
- c = '\v';
- break;
- case 'u':
- c = (hexToInt(*start) << 12) +
- (hexToInt(*(start + 1)) << 8) +
- (hexToInt(*(start + 2)) << 4) +
- hexToInt(*(start + 3));
- start += 4;
- break;
- default:
- return false;
- }
- StringUtil::builderAppend(*output, c);
- }
- return true;
-}
-
-template<typename Char>
-bool decodeString(const Char* start, const Char* end, String* output)
-{
- if (start == end) {
- *output = "";
- return true;
- }
- if (start > end)
- return false;
- StringBuilder buffer;
- StringUtil::builderReserve(buffer, end - start);
- if (!decodeString(start, end, &buffer))
- return false;
- *output = StringUtil::builderToString(buffer);
- return true;
-}
-
-template<typename Char>
-std::unique_ptr<Value> buildValue(const Char* start, const Char* end, const Char** valueTokenEnd, int depth)
-{
- if (depth > stackLimit)
- return nullptr;
-
- std::unique_ptr<Value> result;
- const Char* tokenStart;
- const Char* tokenEnd;
- Token token = parseToken(start, end, &tokenStart, &tokenEnd);
- switch (token) {
- case InvalidToken:
- return nullptr;
- case NullToken:
- result = Value::null();
- break;
- case BoolTrue:
- result = FundamentalValue::create(true);
- break;
- case BoolFalse:
- result = FundamentalValue::create(false);
- break;
- case Number: {
- bool ok;
- double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok);
- if (!ok)
- return nullptr;
- if (value >= INT_MIN && value <= INT_MAX && static_cast<int>(value) == value)
- result = FundamentalValue::create(static_cast<int>(value));
- else
- result = FundamentalValue::create(value);
- break;
- }
- case StringLiteral: {
- String value;
- bool ok = decodeString(tokenStart + 1, tokenEnd - 1, &value);
- if (!ok)
- return nullptr;
- result = StringValue::create(value);
- break;
- }
- case ArrayBegin: {
- std::unique_ptr<ListValue> array = ListValue::create();
- start = tokenEnd;
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- while (token != ArrayEnd) {
- std::unique_ptr<Value> arrayNode = buildValue(start, end, &tokenEnd, depth + 1);
- if (!arrayNode)
- return nullptr;
- array->pushValue(std::move(arrayNode));
-
- // After a list value, we expect a comma or the end of the list.
- start = tokenEnd;
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- if (token == ListSeparator) {
- start = tokenEnd;
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- if (token == ArrayEnd)
- return nullptr;
- } else if (token != ArrayEnd) {
- // Unexpected value after list value. Bail out.
- return nullptr;
- }
- }
- if (token != ArrayEnd)
- return nullptr;
- result = std::move(array);
- break;
- }
- case ObjectBegin: {
- std::unique_ptr<DictionaryValue> object = DictionaryValue::create();
- start = tokenEnd;
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- while (token != ObjectEnd) {
- if (token != StringLiteral)
- return nullptr;
- String key;
- if (!decodeString(tokenStart + 1, tokenEnd - 1, &key))
- return nullptr;
- start = tokenEnd;
-
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- if (token != ObjectPairSeparator)
- return nullptr;
- start = tokenEnd;
-
- std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, depth + 1);
- if (!value)
- return nullptr;
- object->setValue(key, std::move(value));
- start = tokenEnd;
-
- // After a key/value pair, we expect a comma or the end of the
- // object.
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- if (token == ListSeparator) {
- start = tokenEnd;
- token = parseToken(start, end, &tokenStart, &tokenEnd);
- if (token == ObjectEnd)
- return nullptr;
- } else if (token != ObjectEnd) {
- // Unexpected value after last object value. Bail out.
- return nullptr;
- }
- }
- if (token != ObjectEnd)
- return nullptr;
- result = std::move(object);
- break;
- }
-
- default:
- // We got a token that's not a value.
- return nullptr;
- }
-
- skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd);
- return result;
-}
-
-template<typename Char>
-std::unique_ptr<Value> parseJSONInternal(const Char* start, unsigned length)
-{
- const Char* end = start + length;
- const Char *tokenEnd;
- std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, 0);
- if (!value || tokenEnd != end)
- return nullptr;
- return value;
-}
-
-} // anonymous namespace
-
-std::unique_ptr<Value> parseJSONCharacters(const uint16_t* characters, unsigned length)
-{
- return parseJSONInternal<uint16_t>(characters, length);
-}
-
-std::unique_ptr<Value> parseJSONCharacters(const uint8_t* characters, unsigned length)
-{
- return parseJSONInternal<uint8_t>(characters, length);
-}
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/lib/Parser_h.template b/chromium/third_party/inspector_protocol/lib/Parser_h.template
deleted file mode 100644
index 1832c2e9724..00000000000
--- a/chromium/third_party/inspector_protocol/lib/Parser_h.template
+++ /dev/null
@@ -1,24 +0,0 @@
-// This file is generated by Parser_h.template.
-
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef {{"_".join(config.protocol.namespace)}}_Parser_h
-#define {{"_".join(config.protocol.namespace)}}_Parser_h
-
-//#include "Forward.h"
-//#include "Values.h"
-
-{% for namespace in config.protocol.namespace %}
-namespace {{namespace}} {
-{% endfor %}
-
-{{config.lib.export_macro}} std::unique_ptr<Value> parseJSONCharacters(const uint8_t*, unsigned);
-{{config.lib.export_macro}} std::unique_ptr<Value> parseJSONCharacters(const uint16_t*, unsigned);
-
-{% for namespace in config.protocol.namespace %}
-} // namespace {{namespace}}
-{% endfor %}
-
-#endif // !defined({{"_".join(config.protocol.namespace)}}_Parser_h)
diff --git a/chromium/third_party/inspector_protocol/lib/ValueConversions_h.template b/chromium/third_party/inspector_protocol/lib/ValueConversions_h.template
index 63baf689c6e..15961a63216 100644
--- a/chromium/third_party/inspector_protocol/lib/ValueConversions_h.template
+++ b/chromium/third_party/inspector_protocol/lib/ValueConversions_h.template
@@ -40,7 +40,7 @@ struct ValueConversions<bool> {
bool result = false;
bool success = value ? value->asBoolean(&result) : false;
if (!success)
- errors->addError("boolean value expected");
+ errors->AddError("boolean value expected");
return result;
}
@@ -57,7 +57,7 @@ struct ValueConversions<int> {
int result = 0;
bool success = value ? value->asInteger(&result) : false;
if (!success)
- errors->addError("integer value expected");
+ errors->AddError("integer value expected");
return result;
}
@@ -74,7 +74,7 @@ struct ValueConversions<double> {
double result = 0;
bool success = value ? value->asDouble(&result) : false;
if (!success)
- errors->addError("double value expected");
+ errors->AddError("double value expected");
return result;
}
@@ -91,7 +91,7 @@ struct ValueConversions<String> {
String result;
bool success = value ? value->asString(&result) : false;
if (!success)
- errors->addError("string value expected");
+ errors->AddError("string value expected");
return result;
}
@@ -107,7 +107,7 @@ struct ValueConversions<Binary> {
{
if (!value ||
(value->type() != Value::TypeBinary && value->type() != Value::TypeString)) {
- errors->addError("Either string base64 or binary value expected");
+ errors->AddError("Either string base64 or binary value expected");
return Binary();
}
Binary binary;
@@ -118,7 +118,7 @@ struct ValueConversions<Binary> {
bool success;
Binary out = Binary::fromBase64(result, &success);
if (!success)
- errors->addError("base64 decoding error");
+ errors->AddError("base64 decoding error");
return out;
}
@@ -133,20 +133,20 @@ struct ValueConversions<std::vector<std::unique_ptr<T>>> {
static std::unique_ptr<std::vector<std::unique_ptr<T>>> fromValue(protocol::Value* value, ErrorSupport* errors) {
protocol::ListValue* array = ListValue::cast(value);
if (!array) {
- errors->addError("array expected");
+ errors->AddError("array expected");
return nullptr;
}
- errors->push();
+ errors->Push();
std::unique_ptr<std::vector<std::unique_ptr<T>>> result(
new std::vector<std::unique_ptr<T>>());
result->reserve(array->size());
for (size_t i = 0; i < array->size(); ++i) {
- errors->setName(StringUtil::fromInteger(i));
+ errors->SetIndex(i);
auto item = ValueConversions<T>::fromValue(array->at(i), errors);
result->emplace_back(std::move(item));
}
- errors->pop();
- if (errors->hasErrors())
+ errors->Pop();
+ if (!errors->Errors().empty())
return nullptr;
return result;
}
@@ -167,19 +167,19 @@ struct ValueConversions<std::vector<T>> {
static std::unique_ptr<std::vector<T>> fromValue(protocol::Value* value, ErrorSupport* errors) {
protocol::ListValue* array = ListValue::cast(value);
if (!array) {
- errors->addError("array expected");
+ errors->AddError("array expected");
return nullptr;
}
- errors->push();
+ errors->Push();
std::unique_ptr<std::vector<T>> result(new std::vector<T>());
result->reserve(array->size());
for (size_t i = 0; i < array->size(); ++i) {
- errors->setName(StringUtil::fromInteger(i));
+ errors->SetIndex(i);
auto item = ValueConversions<T>::fromValue(array->at(i), errors);
result->emplace_back(std::move(item));
}
- errors->pop();
- if (errors->hasErrors())
+ errors->Pop();
+ if (!errors->Errors().empty())
return nullptr;
return result;
}
@@ -200,7 +200,7 @@ struct ValueConversions<Value> {
{
bool success = !!value;
if (!success) {
- errors->addError("value expected");
+ errors->AddError("value expected");
return nullptr;
}
return value->clone();
@@ -223,7 +223,7 @@ struct ValueConversions<DictionaryValue> {
{
bool success = value && value->type() == protocol::Value::TypeObject;
if (!success)
- errors->addError("object expected");
+ errors->AddError("object expected");
return DictionaryValue::cast(value->clone());
}
@@ -244,7 +244,7 @@ struct ValueConversions<ListValue> {
{
bool success = value && value->type() == protocol::Value::TypeArray;
if (!success)
- errors->addError("list expected");
+ errors->AddError("list expected");
return ListValue::cast(value->clone());
}
diff --git a/chromium/third_party/inspector_protocol/lib/Values_cpp.template b/chromium/third_party/inspector_protocol/lib/Values_cpp.template
index 09777b116b4..09f3bed1366 100644
--- a/chromium/third_party/inspector_protocol/lib/Values_cpp.template
+++ b/chromium/third_party/inspector_protocol/lib/Values_cpp.template
@@ -13,65 +13,11 @@ namespace {{namespace}} {
{% endfor %}
namespace {
-
-const char* const nullValueString = "null";
-const char* const trueValueString = "true";
-const char* const falseValueString = "false";
-
-inline bool escapeChar(uint16_t c, StringBuilder* dst)
-{
- switch (c) {
- case '\b': StringUtil::builderAppend(*dst, "\\b"); break;
- case '\f': StringUtil::builderAppend(*dst, "\\f"); break;
- case '\n': StringUtil::builderAppend(*dst, "\\n"); break;
- case '\r': StringUtil::builderAppend(*dst, "\\r"); break;
- case '\t': StringUtil::builderAppend(*dst, "\\t"); break;
- case '\\': StringUtil::builderAppend(*dst, "\\\\"); break;
- case '"': StringUtil::builderAppend(*dst, "\\\""); break;
- default:
- return false;
- }
- return true;
-}
-
-const char hexDigits[17] = "0123456789ABCDEF";
-
-void appendUnsignedAsHex(uint16_t number, StringBuilder* dst)
-{
- StringUtil::builderAppend(*dst, "\\u");
- for (size_t i = 0; i < 4; ++i) {
- uint16_t c = hexDigits[(number & 0xF000) >> 12];
- StringUtil::builderAppend(*dst, c);
- number <<= 4;
- }
-}
-
-template <typename Char>
-void escapeStringForJSONInternal(const Char* str, unsigned len,
- StringBuilder* dst)
-{
- for (unsigned i = 0; i < len; ++i) {
- Char c = str[i];
- if (escapeChar(c, dst))
- continue;
- if (c < 32 || c > 126) {
- appendUnsignedAsHex(c, dst);
- } else {
- StringUtil::builderAppend(*dst, c);
- }
- }
-}
-
-// When parsing CBOR, we limit recursion depth for objects and arrays
-// to this constant.
-static constexpr int kStackLimitValues = 1000;
-
-using {{config.crdtp.namespace}}::Error;
using {{config.crdtp.namespace}}::Status;
+using {{config.crdtp.namespace}}::ParserHandler;
using {{config.crdtp.namespace}}::span;
namespace cbor {
-using {{config.crdtp.namespace}}::cbor::CBORTokenTag;
-using {{config.crdtp.namespace}}::cbor::CBORTokenizer;
+using {{config.crdtp.namespace}}::cbor::ParseCBOR;
using {{config.crdtp.namespace}}::cbor::EncodeBinary;
using {{config.crdtp.namespace}}::cbor::EncodeDouble;
using {{config.crdtp.namespace}}::cbor::EncodeFalse;
@@ -88,165 +34,161 @@ using {{config.crdtp.namespace}}::cbor::EnvelopeEncoder;
using {{config.crdtp.namespace}}::cbor::InitialByteForEnvelope;
} // namespace cbor
-// Below are three parsing routines for CBOR, which cover enough
-// to roundtrip JSON messages.
-std::unique_ptr<DictionaryValue> parseMap(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
-std::unique_ptr<ListValue> parseArray(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
-std::unique_ptr<Value> parseValue(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
-
-// |bytes| must start with the indefinite length array byte, so basically,
-// ParseArray may only be called after an indefinite length array has been
-// detected.
-std::unique_ptr<ListValue> parseArray(int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
- DCHECK(tokenizer->TokenTag() == cbor::CBORTokenTag::ARRAY_START);
- tokenizer->Next();
- auto list = ListValue::create();
- while (tokenizer->TokenTag() != cbor::CBORTokenTag::STOP) {
- // Error::CBOR_UNEXPECTED_EOF_IN_ARRAY
- if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE) return nullptr;
- if (tokenizer->TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
- // Parse value.
- auto value = parseValue(stack_depth, tokenizer);
- if (!value) return nullptr;
- list->pushValue(std::move(value));
+// Uses the parsing events received from driver of |ParserHandler|
+// (e.g. cbor::ParseCBOR) into a protocol::Value instance.
+class ValueParserHandler : public ParserHandler {
+ public:
+ // Provides the parsed protocol::Value.
+ std::unique_ptr<Value> ReleaseRoot() { return std::move(root_); }
+
+ // The first parsing error encountered; |status().ok()| is the default.
+ Status status() const { return status_; }
+
+ private:
+ //
+ // Implementation of ParserHandler.
+ //
+ void HandleMapBegin() override {
+ if (!status_.ok()) return;
+ std::unique_ptr<DictionaryValue> dict = DictionaryValue::create();
+ DictionaryValue* dict_ptr = dict.get();
+ AddValueToParent(std::move(dict));
+ stack_.emplace_back(dict_ptr);
}
- tokenizer->Next();
- return list;
-}
-
-std::unique_ptr<Value> parseValue(
- int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
- // Error::CBOR_STACK_LIMIT_EXCEEDED
- if (stack_depth > kStackLimitValues) return nullptr;
- // Skip past the envelope to get to what's inside.
- if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE)
- tokenizer->EnterEnvelope();
- switch (tokenizer->TokenTag()) {
- case cbor::CBORTokenTag::ERROR_VALUE:
- return nullptr;
- case cbor::CBORTokenTag::DONE:
- // Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE
- return nullptr;
- case cbor::CBORTokenTag::TRUE_VALUE: {
- std::unique_ptr<Value> value = FundamentalValue::create(true);
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::FALSE_VALUE: {
- std::unique_ptr<Value> value = FundamentalValue::create(false);
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::NULL_VALUE: {
- std::unique_ptr<Value> value = FundamentalValue::null();
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::INT32: {
- std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetInt32());
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::DOUBLE: {
- std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetDouble());
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::STRING8: {
- span<uint8_t> str = tokenizer->GetString8();
- std::unique_ptr<Value> value =
- StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::STRING16: {
- span<uint8_t> wire = tokenizer->GetString16WireRep();
- DCHECK_EQ(wire.size() & 1, 0u);
- std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF16LE(
- reinterpret_cast<const uint16_t*>(wire.data()), wire.size() / 2));
- tokenizer->Next();
- return value;
- }
- case cbor::CBORTokenTag::BINARY: {
- span<uint8_t> payload = tokenizer->GetBinary();
- tokenizer->Next();
- return BinaryValue::create(Binary::fromSpan(payload.data(), payload.size()));
- }
- case cbor::CBORTokenTag::MAP_START:
- return parseMap(stack_depth + 1, tokenizer);
- case cbor::CBORTokenTag::ARRAY_START:
- return parseArray(stack_depth + 1, tokenizer);
- default:
- // Error::CBOR_UNSUPPORTED_VALUE
- return nullptr;
+
+ void HandleMapEnd() override {
+ if (!status_.ok()) return;
+ DCHECK(!stack_.empty());
+ DCHECK(stack_.back().is_dict);
+ stack_.pop_back();
+ }
+
+ void HandleArrayBegin() override {
+ if (!status_.ok()) return;
+ std::unique_ptr<ListValue> list = ListValue::create();
+ ListValue* list_ptr = list.get();
+ AddValueToParent(std::move(list));
+ stack_.emplace_back(list_ptr);
+ }
+
+ void HandleArrayEnd() override {
+ if (!status_.ok()) return;
+ DCHECK(!stack_.empty());
+ DCHECK(!stack_.back().is_dict);
+ stack_.pop_back();
+ }
+
+ void HandleString8(span<uint8_t> chars) override {
+ AddStringToParent(StringUtil::fromUTF8(chars.data(), chars.size()));
}
-}
-// |bytes| must start with the indefinite length array byte, so basically,
-// ParseArray may only be called after an indefinite length array has been
-// detected.
-std::unique_ptr<DictionaryValue> parseMap(
- int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
- auto dict = DictionaryValue::create();
- tokenizer->Next();
- while (tokenizer->TokenTag() != cbor::CBORTokenTag::STOP) {
- if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE) {
- // Error::CBOR_UNEXPECTED_EOF_IN_MAP
- return nullptr;
+ void HandleString16(span<uint16_t> chars) override {
+ AddStringToParent(
+ StringUtil::fromUTF16LE(chars.data(), chars.size()));
+ }
+
+ void HandleBinary(span<uint8_t> bytes) override {
+ AddValueToParent(
+ BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size())));
+ }
+
+ void HandleDouble(double value) override {
+ AddValueToParent(FundamentalValue::create(value));
+ }
+
+ void HandleInt32(int32_t value) override {
+ AddValueToParent(FundamentalValue::create(value));
+ }
+
+ void HandleBool(bool value) override {
+ AddValueToParent(FundamentalValue::create(value));
+ }
+
+ void HandleNull() override {
+ AddValueToParent(Value::null());
+ }
+
+ void HandleError(Status error) override {
+ status_ = error;
+ }
+
+ //
+ // Adding strings and values to the parent value.
+ // Strings are handled separately because they can be keys for
+ // dictionary values.
+ //
+ void AddStringToParent(String str) {
+ if (!status_.ok()) return;
+ if (!root_) {
+ DCHECK(!key_is_pending_);
+ root_ = StringValue::create(str);
+ } else if (stack_.back().is_dict) {
+ // If we already have a pending key, then this is the value of the
+ // key/value pair. Otherwise, it's the new pending key.
+ if (key_is_pending_) {
+ stack_.back().dict->setString(pending_key_, str);
+ key_is_pending_ = false;
+ } else {
+ pending_key_ = std::move(str);
+ key_is_pending_ = true;
+ }
+ } else { // Top of the stack is a list.
+ DCHECK(!key_is_pending_);
+ stack_.back().list->pushValue(StringValue::create(str));
}
- if (tokenizer->TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
- // Parse key.
- String key;
- if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
- span<uint8_t> key_span = tokenizer->GetString8();
- key = StringUtil::fromUTF8(key_span.data(), key_span.size());
- tokenizer->Next();
- } else if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING16) {
- span<uint8_t> key_span = tokenizer->GetString16WireRep();
- if (key_span.size() & 1) return nullptr; // UTF16 is 2 byte multiple.
- key = StringUtil::fromUTF16LE(
- reinterpret_cast<const uint16_t*>(key_span.data()),
- key_span.size() / 2);
- tokenizer->Next();
- } else {
- // Error::CBOR_INVALID_MAP_KEY
- return nullptr;
+ }
+
+ void AddValueToParent(std::unique_ptr<Value> value) {
+ if (!status_.ok()) return;
+ if (!root_) {
+ DCHECK(!key_is_pending_);
+ root_ = std::move(value);
+ } else if (stack_.back().is_dict) {
+ DCHECK(key_is_pending_);
+ stack_.back().dict->setValue(pending_key_, std::move(value));
+ key_is_pending_ = false;
+ } else { // Top of the stack is a list.
+ DCHECK(!key_is_pending_);
+ stack_.back().list->pushValue(std::move(value));
}
- // Parse value.
- auto value = parseValue(stack_depth, tokenizer);
- if (!value) return nullptr;
- dict->setValue(key, std::move(value));
}
- tokenizer->Next();
- return dict;
-}
+ // |status_.ok()| is the default; if we receive an error event
+ // we keep the first one and stop modifying any other state.
+ Status status_;
+
+ // The root of the parsed protocol::Value tree.
+ std::unique_ptr<Value> root_;
+
+ // If root_ is a list or a dictionary, this stack keeps track of
+ // the container we're currently parsing as well as its ancestors.
+ struct ContainerState {
+ ContainerState(DictionaryValue* dict) : is_dict(true), dict(dict) {}
+ ContainerState(ListValue* list) : is_dict(false), list(list) {}
+
+ bool is_dict;
+ union {
+ DictionaryValue* dict;
+ ListValue* list;
+ };
+ };
+ std::vector<ContainerState> stack_;
+
+ // For maps, keys and values are alternating events, so we keep the
+ // key around and process it when the value arrives.
+ bool key_is_pending_ = false;
+ String pending_key_;
+};
} // anonymous namespace
// static
std::unique_ptr<Value> Value::parseBinary(const uint8_t* data, size_t size) {
- span<uint8_t> bytes(data, size);
-
- // Error::CBOR_NO_INPUT
- if (bytes.empty()) return nullptr;
-
- // Error::CBOR_INVALID_START_BYTE
- if (bytes[0] != cbor::InitialByteForEnvelope()) return nullptr;
-
- cbor::CBORTokenizer tokenizer(bytes);
- if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
-
- // We checked for the envelope start byte above, so the tokenizer
- // must agree here, since it's not an error.
- DCHECK(tokenizer.TokenTag() == cbor::CBORTokenTag::ENVELOPE);
- tokenizer.EnterEnvelope();
- // Error::MAP_START_EXPECTED
- if (tokenizer.TokenTag() != cbor::CBORTokenTag::MAP_START) return nullptr;
- std::unique_ptr<Value> result = parseMap(/*stack_depth=*/1, &tokenizer);
- if (!result) return nullptr;
- if (tokenizer.TokenTag() == cbor::CBORTokenTag::DONE) return result;
- if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
- // Error::CBOR_TRAILING_JUNK
+ ValueParserHandler handler;
+ cbor::ParseCBOR(span<uint8_t>(data, size), &handler);
+ // TODO(johannes): We have decent error info in handler.status(); provide
+ // a richer interface that makes this available to client code.
+ if (handler.status().ok())
+ return handler.ReleaseRoot();
return nullptr;
}
@@ -275,12 +217,6 @@ bool Value::asBinary(Binary*) const
return false;
}
-void Value::writeJSON(StringBuilder* output) const
-{
- DCHECK(m_type == TypeNull);
- StringUtil::builderAppend(*output, nullValueString, 4);
-}
-
void Value::AppendSerialized(std::vector<uint8_t>* bytes) const {
DCHECK(m_type == TypeNull);
bytes->push_back(cbor::EncodeNull());
@@ -291,14 +227,6 @@ std::unique_ptr<Value> Value::clone() const
return Value::null();
}
-String Value::toJSONString() const
-{
- StringBuilder result;
- StringUtil::builderReserve(result, 512);
- writeJSON(&result);
- return StringUtil::builderToString(result);
-}
-
bool FundamentalValue::asBoolean(bool* output) const
{
if (type() != TypeBoolean)
@@ -328,25 +256,6 @@ bool FundamentalValue::asInteger(int* output) const
return true;
}
-void FundamentalValue::writeJSON(StringBuilder* output) const
-{
- DCHECK(type() == TypeBoolean || type() == TypeInteger || type() == TypeDouble);
- if (type() == TypeBoolean) {
- if (m_boolValue)
- StringUtil::builderAppend(*output, trueValueString, 4);
- else
- StringUtil::builderAppend(*output, falseValueString, 5);
- } else if (type() == TypeDouble) {
- if (!std::isfinite(m_doubleValue)) {
- StringUtil::builderAppend(*output, nullValueString, 4);
- return;
- }
- StringUtil::builderAppend(*output, StringUtil::fromDouble(m_doubleValue));
- } else if (type() == TypeInteger) {
- StringUtil::builderAppend(*output, StringUtil::fromInteger(m_integerValue));
- }
-}
-
void FundamentalValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
switch (type()) {
case TypeDouble:
@@ -381,12 +290,6 @@ bool StringValue::asString(String* output) const
return true;
}
-void StringValue::writeJSON(StringBuilder* output) const
-{
- DCHECK(type() == TypeString);
- StringUtil::builderAppendQuotedString(*output, m_stringValue);
-}
-
namespace {
// This routine distinguishes between the current encoding for a given
// string |s|, and calls encoding routines that will
@@ -431,12 +334,6 @@ bool BinaryValue::asBinary(Binary* output) const
return true;
}
-void BinaryValue::writeJSON(StringBuilder* output) const
-{
- DCHECK(type() == TypeBinary);
- StringUtil::builderAppendQuotedString(*output, m_binaryValue.toBase64());
-}
-
void BinaryValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
cbor::EncodeBinary(span<uint8_t>(m_binaryValue.data(),
m_binaryValue.size()), bytes);
@@ -447,26 +344,6 @@ std::unique_ptr<Value> BinaryValue::clone() const
return BinaryValue::create(m_binaryValue);
}
-void SerializedValue::writeJSON(StringBuilder* output) const
-{
- DCHECK(type() == TypeSerialized);
- StringUtil::builderAppend(*output, m_serializedJSON);
-}
-
-std::vector<uint8_t> SerializedValue::TakeSerialized() && {
- return std::move(m_serializedBinary);
-}
-
-void SerializedValue::AppendSerialized(std::vector<uint8_t>* output) const
-{
- DCHECK(type() == TypeSerialized);
- output->insert(output->end(), m_serializedBinary.begin(), m_serializedBinary.end());
-}
-
-std::unique_ptr<Value> SerializedValue::clone() const
-{
- return std::unique_ptr<SerializedValue>(new SerializedValue(m_serializedJSON, m_serializedBinary));
-}
DictionaryValue::~DictionaryValue()
{
@@ -590,21 +467,6 @@ void DictionaryValue::remove(const String& name)
m_order.erase(std::remove(m_order.begin(), m_order.end(), name), m_order.end());
}
-void DictionaryValue::writeJSON(StringBuilder* output) const
-{
- StringUtil::builderAppend(*output, '{');
- for (size_t i = 0; i < m_order.size(); ++i) {
- Dictionary::const_iterator it = m_data.find(m_order[i]);
- CHECK(it != m_data.end());
- if (i)
- StringUtil::builderAppend(*output, ',');
- StringUtil::builderAppendQuotedString(*output, it->first);
- StringUtil::builderAppend(*output, ':');
- it->second->writeJSON(output);
- }
- StringUtil::builderAppend(*output, '}');
-}
-
void DictionaryValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
cbor::EnvelopeEncoder encoder;
encoder.EncodeStart(bytes);
@@ -629,7 +491,7 @@ std::unique_ptr<Value> DictionaryValue::clone() const
DCHECK(value != m_data.cend() && value->second);
result->setValue(key, value->second->clone());
}
- return std::move(result);
+ return result;
}
DictionaryValue::DictionaryValue()
@@ -641,19 +503,6 @@ ListValue::~ListValue()
{
}
-void ListValue::writeJSON(StringBuilder* output) const
-{
- StringUtil::builderAppend(*output, '[');
- bool first = true;
- for (const std::unique_ptr<protocol::Value>& value : m_data) {
- if (!first)
- StringUtil::builderAppend(*output, ',');
- value->writeJSON(output);
- first = false;
- }
- StringUtil::builderAppend(*output, ']');
-}
-
void ListValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
cbor::EnvelopeEncoder encoder;
encoder.EncodeStart(bytes);
@@ -670,7 +519,7 @@ std::unique_ptr<Value> ListValue::clone() const
std::unique_ptr<ListValue> result = ListValue::create();
for (const std::unique_ptr<protocol::Value>& value : m_data)
result->pushValue(value->clone());
- return std::move(result);
+ return result;
}
ListValue::ListValue()
@@ -690,16 +539,6 @@ protocol::Value* ListValue::at(size_t index)
return m_data[index].get();
}
-void escapeLatinStringForJSON(const uint8_t* str, unsigned len, StringBuilder* dst)
-{
- escapeStringForJSONInternal<uint8_t>(str, len, dst);
-}
-
-void escapeWideStringForJSON(const uint16_t* str, unsigned len, StringBuilder* dst)
-{
- escapeStringForJSONInternal<uint16_t>(str, len, dst);
-}
-
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/lib/Values_h.template b/chromium/third_party/inspector_protocol/lib/Values_h.template
index caf7367e910..8514123fb8d 100644
--- a/chromium/third_party/inspector_protocol/lib/Values_h.template
+++ b/chromium/third_party/inspector_protocol/lib/Values_h.template
@@ -10,6 +10,8 @@
//#include "Allocator.h"
//#include "Forward.h"
+#include {{format_include(config.protocol.package, "Forward")}}
+
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
@@ -39,7 +41,6 @@ public:
TypeBinary,
TypeObject,
TypeArray,
- TypeSerialized,
TypeImported
};
@@ -53,10 +54,8 @@ public:
virtual bool asString(String* output) const;
virtual bool asBinary(Binary* output) const;
- virtual void writeJSON(StringBuilder* output) const;
virtual void AppendSerialized(std::vector<uint8_t>* bytes) const override;
virtual std::unique_ptr<Value> clone() const;
- String toJSONString() const;
protected:
Value() : m_type(TypeNull) { }
@@ -89,7 +88,6 @@ public:
bool asBoolean(bool* output) const override;
bool asDouble(double* output) const override;
bool asInteger(int* output) const override;
- void writeJSON(StringBuilder* output) const override;
void AppendSerialized(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
@@ -118,7 +116,6 @@ public:
}
bool asString(String* output) const override;
- void writeJSON(StringBuilder* output) const override;
void AppendSerialized(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
@@ -137,7 +134,6 @@ public:
}
bool asBinary(Binary* output) const override;
- void writeJSON(StringBuilder* output) const override;
void AppendSerialized(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
@@ -147,32 +143,6 @@ private:
Binary m_binaryValue;
};
-class {{config.lib.export_macro}} SerializedValue : public Value {
-public:
- static std::unique_ptr<SerializedValue> fromJSON(const String& value)
- {
- return std::unique_ptr<SerializedValue>(new SerializedValue(value));
- }
-
- static std::unique_ptr<SerializedValue> fromBinary(std::vector<uint8_t> value)
- {
- return std::unique_ptr<SerializedValue>(new SerializedValue(std::move(value)));
- }
-
- void writeJSON(StringBuilder* output) const override;
- std::vector<uint8_t> TakeSerialized() && override;
- void AppendSerialized(std::vector<uint8_t>* bytes) const override;
- std::unique_ptr<Value> clone() const override;
-
-private:
- explicit SerializedValue(const String& json) : Value(TypeSerialized), m_serializedJSON(json) { }
- explicit SerializedValue(std::vector<uint8_t> binary) : Value(TypeSerialized), m_serializedBinary(std::move(binary)) { }
- SerializedValue(const String& json, const std::vector<uint8_t>& binary)
- : Value(TypeSerialized), m_serializedJSON(json), m_serializedBinary(binary) { }
- String m_serializedJSON;
- std::vector<uint8_t> m_serializedBinary;
-};
-
class {{config.lib.export_macro}} DictionaryValue : public Value {
public:
using Entry = std::pair<String, Value*>;
@@ -193,7 +163,6 @@ public:
return std::unique_ptr<DictionaryValue>(DictionaryValue::cast(value.release()));
}
- void writeJSON(StringBuilder* output) const override;
void AppendSerialized(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
@@ -262,7 +231,6 @@ public:
~ListValue() override;
- void writeJSON(StringBuilder* output) const override;
void AppendSerialized(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
@@ -277,9 +245,6 @@ private:
std::vector<std::unique_ptr<Value>> m_data;
};
-void escapeLatinStringForJSON(const uint8_t* str, unsigned len, StringBuilder* dst);
-void escapeWideStringForJSON(const uint16_t* str, unsigned len, StringBuilder* dst);
-
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/lib/base_string_adapter_cc.template b/chromium/third_party/inspector_protocol/lib/base_string_adapter_cc.template
index 4619f32c307..b1ec1e475cb 100644
--- a/chromium/third_party/inspector_protocol/lib/base_string_adapter_cc.template
+++ b/chromium/third_party/inspector_protocol/lib/base_string_adapter_cc.template
@@ -1,4 +1,4 @@
-// This file is generated by DispatcherBase_cpp.template.
+// This file is generated by base_string_adapter_cc.template.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -15,6 +15,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "{{config.crdtp.dir}}/cbor.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
@@ -58,7 +59,7 @@ std::unique_ptr<protocol::Value> toProtocolValue(
if (converted)
result->pushValue(std::move(converted));
}
- return std::move(result);
+ return result;
}
if (value->is_dict()) {
const base::DictionaryValue* dictionary = nullptr;
@@ -72,7 +73,7 @@ std::unique_ptr<protocol::Value> toProtocolValue(
if (converted)
result->setValue(it.key(), std::move(converted));
}
- return std::move(result);
+ return result;
}
return nullptr;
}
@@ -111,7 +112,7 @@ std::unique_ptr<base::Value> toBaseValue(Value* value, int depth) {
if (converted)
result->Append(std::move(converted));
}
- return std::move(result);
+ return result;
}
if (value->type() == Value::TypeObject) {
DictionaryValue* dict = DictionaryValue::cast(value);
@@ -123,68 +124,11 @@ std::unique_ptr<base::Value> toBaseValue(Value* value, int depth) {
if (converted)
result->SetWithoutPathExpansion(entry.first, std::move(converted));
}
- return std::move(result);
+ return result;
}
return nullptr;
}
-// static
-std::unique_ptr<Value> StringUtil::parseMessage(
- const std::string& message, bool binary) {
- if (binary) {
- return Value::parseBinary(
- reinterpret_cast<const uint8_t*>(message.data()),
- message.length());
- }
- std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(message);
- return toProtocolValue(value.get(), 1000);
-}
-
-// static
-ProtocolMessage StringUtil::jsonToMessage(String message) {
- return message;
-}
-
-// static
-ProtocolMessage StringUtil::binaryToMessage(std::vector<uint8_t> message) {
- // TODO(pfeldman): figure out what to do with this copy.
- return std::string(reinterpret_cast<const char*>(message.data()), message.size());
-}
-
-StringBuilder::StringBuilder() {}
-
-StringBuilder::~StringBuilder() {}
-
-void StringBuilder::append(const std::string& s) {
- string_ += s;
-}
-
-void StringBuilder::append(char c) {
- string_ += c;
-}
-
-void StringBuilder::append(const char* characters, size_t length) {
- string_.append(characters, length);
-}
-
-// static
-void StringUtil::builderAppendQuotedString(StringBuilder& builder,
- const String& str) {
- builder.append('"');
- base::string16 str16 = base::UTF8ToUTF16(str);
- escapeWideStringForJSON(reinterpret_cast<const uint16_t*>(&str16[0]),
- str16.length(), &builder);
- builder.append('"');
-}
-
-std::string StringBuilder::toString() {
- return string_;
-}
-
-void StringBuilder::reserveCapacity(size_t capacity) {
- string_.reserve(capacity);
-}
-
// In Chromium, we do not support big endian architectures, so no conversion is needed
// to interpret UTF16LE.
// static
@@ -199,6 +143,10 @@ Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
Binary::~Binary() {}
+void Binary::AppendSerialized(std::vector<uint8_t>* out) const {
+ crdtp::cbor::EncodeBinary(crdtp::span<uint8_t>(data(), size()), out);
+}
+
String Binary::toBase64() const {
std::string encoded;
base::Base64Encode(
diff --git a/chromium/third_party/inspector_protocol/lib/base_string_adapter_h.template b/chromium/third_party/inspector_protocol/lib/base_string_adapter_h.template
index 6a9ba3867a5..ff40aba3637 100644
--- a/chromium/third_party/inspector_protocol/lib/base_string_adapter_h.template
+++ b/chromium/third_party/inspector_protocol/lib/base_string_adapter_h.template
@@ -1,4 +1,4 @@
-// This file is generated by Parser_h.template.
+// This file is generated by base_string_adapter_h.template.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -14,7 +14,8 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
-#include "base/strings/string_number_conversions.h"
+#include "{{config.crdtp.dir}}/serializable.h"
+
{% if config.lib.export_header %}
#include "{{config.lib.export_header}}"
{% endif %}
@@ -30,72 +31,9 @@ namespace {{namespace}} {
class Value;
using String = std::string;
-using ProtocolMessage = std::string;
-
-class {{config.lib.export_macro}} StringBuilder {
- public:
- StringBuilder();
- ~StringBuilder();
- void append(const String&);
- void append(char);
- void append(const char*, size_t);
- String toString();
- void reserveCapacity(size_t);
-
- private:
- std::string string_;
-};
class {{config.lib.export_macro}} StringUtil {
public:
- static String substring(const String& s, unsigned pos, unsigned len) {
- return s.substr(pos, len);
- }
- static String fromInteger(int number) { return base::NumberToString(number); }
- static String fromDouble(double number) {
- String s = base::NumberToString(number);
- if (!s.empty()) { // .123 -> 0.123; -.123 -> -0.123 for valid JSON.
- if (s[0] == '.')
- s.insert(/*index=*/ 0, /*count=*/ 1, /*ch=*/ '0');
- else if (s[0] == '-' && s.size() >= 2 && s[1] == '.')
- s.insert(/*index=*/ 1, /*count=*/ 1, /*ch=*/ '0');
- }
- return s;
- }
- static double toDouble(const char* s, size_t len, bool* ok) {
- double v = 0.0;
- *ok = base::StringToDouble(std::string(s, len), &v);
- return *ok ? v : 0.0;
- }
- static size_t find(const String& s, const char* needle) {
- return s.find(needle);
- }
- static size_t find(const String& s, const String& needle) {
- return s.find(needle);
- }
- static const size_t kNotFound = static_cast<size_t>(-1);
- static void builderAppend(StringBuilder& builder, const String& s) {
- builder.append(s);
- }
- static void builderAppend(StringBuilder& builder, char c) {
- builder.append(c);
- }
- static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
- builder.append(s, len);
- }
- static void builderAppendQuotedString(StringBuilder& builder,
- const String& str);
- static void builderReserve(StringBuilder& builder, unsigned capacity) {
- builder.reserveCapacity(capacity);
- }
- static String builderToString(StringBuilder& builder) {
- return builder.toString();
- }
-
- static std::unique_ptr<Value> parseMessage(const std::string& message, bool binary);
- static ProtocolMessage jsonToMessage(String message);
- static ProtocolMessage binaryToMessage(std::vector<uint8_t> message);
-
static String fromUTF8(const uint8_t* data, size_t length) {
return std::string(reinterpret_cast<const char*>(data), length);
}
@@ -111,12 +49,15 @@ class {{config.lib.export_macro}} StringUtil {
};
// A read-only sequence of uninterpreted bytes with reference-counted storage.
-class {{config.lib.export_macro}} Binary {
+class {{config.lib.export_macro}} Binary : public {{config.crdtp.namespace}}::Serializable {
public:
Binary(const Binary&);
Binary();
~Binary();
+ // Implements Serializable.
+ void AppendSerialized(std::vector<uint8_t>* out) const override;
+
const uint8_t* data() const { return bytes_->front(); }
size_t size() const { return bytes_->size(); }
scoped_refptr<base::RefCountedMemory> bytes() const { return bytes_; }
diff --git a/chromium/third_party/inspector_protocol/roll.py b/chromium/third_party/inspector_protocol/roll.py
index 4f21fb5980b..1e26ee3db68 100755
--- a/chromium/third_party/inspector_protocol/roll.py
+++ b/chromium/third_party/inspector_protocol/roll.py
@@ -18,10 +18,20 @@ FILES_TO_SYNC = [
'code_generator.py',
'concatenate_protocols.py',
'convert_protocol_to_json.py',
+ 'crdtp/README.md',
'crdtp/cbor.cc',
'crdtp/cbor.h',
'crdtp/cbor_test.cc',
+ 'crdtp/dispatch.h',
+ 'crdtp/dispatch.cc',
+ 'crdtp/dispatch_test.cc',
+ 'crdtp/error_support.h',
+ 'crdtp/error_support.cc',
+ 'crdtp/error_support_test.cc',
'crdtp/export.h',
+ 'crdtp/find_by_first.h',
+ 'crdtp/find_by_first_test.cc',
+ 'crdtp/frontend_channel.h',
'crdtp/glue.h',
'crdtp/glue_test.cc',
'crdtp/json.cc',
@@ -32,13 +42,19 @@ FILES_TO_SYNC = [
'crdtp/serializable.h',
'crdtp/serializable.cc',
'crdtp/serializable_test.cc',
+ 'crdtp/serializer_traits.h',
+ 'crdtp/serializer_traits_test.cc',
'crdtp/span.h',
+ 'crdtp/span.cc',
'crdtp/span_test.cc',
'crdtp/status.cc',
'crdtp/status.h',
+ 'crdtp/status_test_support.cc',
+ 'crdtp/status_test_support.h',
'crdtp/status_test.cc',
'crdtp/test_platform.cc',
'crdtp/test_platform.h',
+ 'crdtp/transcode.cc',
'inspector_protocol.gni',
'inspector_protocol.gypi',
'lib/*',
diff --git a/chromium/third_party/inspector_protocol/templates/Exported_h.template b/chromium/third_party/inspector_protocol/templates/Exported_h.template
index 765f6c2135b..6481eef50c6 100644
--- a/chromium/third_party/inspector_protocol/templates/Exported_h.template
+++ b/chromium/third_party/inspector_protocol/templates/Exported_h.template
@@ -20,8 +20,8 @@ namespace {{namespace}} {
#define {{"_".join(config.protocol.namespace)}}_exported_api_h
class {{config.exported.export_macro}} Exported {
public:
- virtual {{config.exported.string_out}} toJSONString() const = 0;
- virtual void writeBinary(std::vector<uint8_t>* out) const = 0;
+ virtual void AppendSerialized(std::vector<uint8_t>* out) const = 0;
+
virtual ~Exported() { }
};
#endif // !defined({{"_".join(config.protocol.namespace)}}_exported_api_h)
@@ -61,7 +61,6 @@ namespace {{param.name | to_title_case}}Enum {
class {{config.exported.export_macro}} {{type.id}} : public Exported {
public:
- static std::unique_ptr<protocol::{{domain.domain}}::API::{{type.id}}> fromJSONString(const {{config.exported.string_in}}& json);
static std::unique_ptr<protocol::{{domain.domain}}::API::{{type.id}}> fromBinary(const uint8_t* data, size_t length);
};
{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/templates/Imported_h.template b/chromium/third_party/inspector_protocol/templates/Imported_h.template
index d82466a9849..bb1dcf4a205 100644
--- a/chromium/third_party/inspector_protocol/templates/Imported_h.template
+++ b/chromium/third_party/inspector_protocol/templates/Imported_h.template
@@ -29,14 +29,10 @@ public:
return std::unique_ptr<ImportedValue>(new ImportedValue(value));
}
- void writeJSON(StringBuilder* output) const override {
- auto json = m_exported->toJSONString();
- String local_json = ({{config.imported.from_imported_string % "std::move(json)"}});
- StringUtil::builderAppend(*output, local_json);
- }
void AppendSerialized(std::vector<uint8_t>* output) const override {
- m_exported->writeBinary(output);
+ m_exported->AppendSerialized(output);
}
+
std::unique_ptr<Value> clone() const override {
return std::unique_ptr<Value>(new ImportedValue(m_exported));
}
@@ -56,7 +52,7 @@ struct ValueConversions<{{"::".join(config.imported.namespace)}}::{{domain.domai
static std::unique_ptr<{{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}> fromValue(protocol::Value* value, ErrorSupport* errors)
{
if (!value) {
- errors->addError("value expected");
+ errors->AddError("value expected");
return nullptr;
}
@@ -64,7 +60,7 @@ struct ValueConversions<{{"::".join(config.imported.namespace)}}::{{domain.domai
value->AppendSerialized(&binary);
auto result = {{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}::fromBinary(binary.data(), binary.size());
if (!result)
- errors->addError("cannot parse");
+ errors->AddError("cannot parse");
return result;
}
@@ -72,11 +68,6 @@ struct ValueConversions<{{"::".join(config.imported.namespace)}}::{{domain.domai
{
return ImportedValue::fromExported(exported);
}
-
- static std::unique_ptr<protocol::Value> toValue(const std::unique_ptr<{{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}>& value)
- {
- return toValue(value.get());
- }
};
{% endfor %}
diff --git a/chromium/third_party/inspector_protocol/templates/TypeBuilder_cpp.template b/chromium/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
index 6236ae5b150..f0a3d3ccaf4 100644
--- a/chromium/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
+++ b/chromium/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
@@ -8,6 +8,11 @@
#include {{format_include(config.protocol.package, "Protocol")}}
+#include "{{config.crdtp.dir}}/cbor.h"
+#include "{{config.crdtp.dir}}/find_by_first.h"
+#include "{{config.crdtp.dir}}/serializer_traits.h"
+#include "{{config.crdtp.dir}}/span.h"
+
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
@@ -51,27 +56,27 @@ const char* {{type.id}}::{{property.name | to_title_case}}Enum::{{literal | dash
std::unique_ptr<{{type.id}}> {{type.id}}::fromValue(protocol::Value* value, ErrorSupport* errors)
{
if (!value || value->type() != protocol::Value::TypeObject) {
- errors->addError("object expected");
+ errors->AddError("object expected");
return nullptr;
}
std::unique_ptr<{{type.id}}> result(new {{type.id}}());
protocol::DictionaryValue* object = DictionaryValue::cast(value);
- errors->push();
+ errors->Push();
{% for property in type.properties %}
protocol::Value* {{property.name}}Value = object->get("{{property.name}}");
{% if property.optional %}
if ({{property.name}}Value) {
- errors->setName("{{property.name}}");
+ errors->SetName("{{property.name}}");
result->m_{{property.name}} = ValueConversions<{{protocol.resolve_type(property).raw_type}}>::fromValue({{property.name}}Value, errors);
}
{% else %}
- errors->setName("{{property.name}}");
+ errors->SetName("{{property.name}}");
result->m_{{property.name}} = ValueConversions<{{protocol.resolve_type(property).raw_type}}>::fromValue({{property.name}}Value, errors);
{% endif %}
{% endfor %}
- errors->pop();
- if (errors->hasErrors())
+ errors->Pop();
+ if (!errors->Errors().empty())
return nullptr;
return result;
}
@@ -92,6 +97,18 @@ std::unique_ptr<protocol::DictionaryValue> {{type.id}}::toValue() const
return result;
}
+void {{type.id}}::AppendSerialized(std::vector<uint8_t>* out) const {
+ {{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
+ envelope_encoder.EncodeStart(out);
+ out->push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
+ {% for property in type.properties %}
+ {% set property_field = "m_" + property.name %}
+ {{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{property.name}}"), {{property_field}}, out);
+ {% endfor %}
+ out->push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
+ envelope_encoder.EncodeStop(out);
+}
+
std::unique_ptr<{{type.id}}> {{type.id}}::clone() const
{
ErrorSupport errors;
@@ -99,27 +116,6 @@ std::unique_ptr<{{type.id}}> {{type.id}}::clone() const
}
{% if protocol.is_exported(domain.domain, type.id) %}
-{{config.exported.string_out}} {{type.id}}::toJSONString() const
-{
- String json = toValue()->toJSONString();
- return {{config.exported.to_string_out % "json"}};
-}
-
-void {{type.id}}::writeBinary(std::vector<uint8_t>* out) const
-{
- toValue()->AppendSerialized(out);
-}
-
-// static
-std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromJSONString(const {{config.exported.string_in}}& json)
-{
- ErrorSupport errors;
- std::unique_ptr<Value> value = StringUtil::parseJSON(json);
- if (!value)
- return nullptr;
- return protocol::{{domain.domain}}::{{type.id}}::fromValue(value.get(), &errors);
-}
-
// static
std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromBinary(const uint8_t* data, size_t length)
{
@@ -175,7 +171,7 @@ void Frontend::{{event.name | to_method_case}}(
{%- endif %} {{parameter.name}}{%- if not loop.last -%}, {% endif -%}
{% endfor -%})
{
- if (!m_frontendChannel)
+ if (!frontend_channel_)
return;
{% if event.parameters %}
std::unique_ptr<{{event.name | to_title_case}}Notification> messageData = {{event.name | to_title_case}}Notification::{{"create" | to_method_case}}()
@@ -191,69 +187,79 @@ void Frontend::{{event.name | to_method_case}}(
messageData->{{"set" | to_method_case}}{{parameter.name | to_title_case}}(std::move({{parameter.name}}).takeJust());
{% endif %}
{% endfor %}
- m_frontendChannel->sendProtocolNotification(InternalResponse::createNotification("{{domain.domain}}.{{event.name}}", std::move(messageData)));
+ frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}", std::move(messageData)));
{% else %}
- m_frontendChannel->sendProtocolNotification(InternalResponse::createNotification("{{domain.domain}}.{{event.name}}"));
+ frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}"));
{% endif %}
}
{% endfor %}
void Frontend::flush()
{
- m_frontendChannel->flushProtocolNotifications();
+ frontend_channel_->FlushProtocolNotifications();
}
-void Frontend::sendRawCBORNotification(std::vector<uint8_t> notification)
+void Frontend::sendRawNotification(std::unique_ptr<Serializable> notification)
{
- m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromBinary(std::move(notification)));
+ frontend_channel_->SendProtocolNotification(std::move(notification));
}
// --------------------- Dispatcher.
-class DispatcherImpl : public protocol::DispatcherBase {
+class DomainDispatcherImpl : public protocol::DomainDispatcher {
public:
- DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend)
- : DispatcherBase(frontendChannel)
- , m_backend(backend) {
- {% for command in domain.commands %}
- {% if "redirect" in command %}
- m_redirects["{{domain.domain}}.{{command.name}}"] = "{{command.redirect}}.{{command.name}}";
- {% continue %}
- {% endif %}
- {% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
- m_dispatchMap["{{domain.domain}}.{{command.name}}"] = &DispatcherImpl::{{command.name}};
- {% endfor %}
- }
- ~DispatcherImpl() override { }
- bool canDispatch(const String& method) override;
- void dispatch(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<protocol::DictionaryValue> messageObject) override;
- std::unordered_map<String, String>& redirects() { return m_redirects; }
+ DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend)
+ : DomainDispatcher(frontendChannel)
+ , m_backend(backend) {}
+ ~DomainDispatcherImpl() override { }
+
+ using CallHandler = void (DomainDispatcherImpl::*)(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors);
-protected:
- using CallHandler = void (DispatcherImpl::*)(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors);
- using DispatchMap = std::unordered_map<String, CallHandler>;
- DispatchMap m_dispatchMap;
- std::unordered_map<String, String> m_redirects;
+ std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) override;
{% for command in domain.commands %}
{% if "redirect" in command %}{% continue %}{% endif %}
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
- void {{command.name}}(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*);
+ void {{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors);
{% endfor %}
-
+ protected:
Backend* m_backend;
};
-bool DispatcherImpl::canDispatch(const String& method) {
- return m_dispatchMap.find(method) != m_dispatchMap.end();
+namespace {
+// This helper method with a static map of command methods (instance methods
+// of DomainDispatcherImpl declared just above) by their name is used immediately below,
+// in the DomainDispatcherImpl::Dispatch method.
+DomainDispatcherImpl::CallHandler CommandByName({{config.crdtp.namespace}}::span<uint8_t> command_name) {
+ static auto* commands = [](){
+ auto* commands = new std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>,
+ DomainDispatcherImpl::CallHandler>>{
+ {% for command in domain.commands|sort(attribute="name",case_sensitive=True) %}
+ {% if "redirect" in command %}{% continue %}{% endif %}
+ {% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
+ {
+ {{config.crdtp.namespace}}::SpanFrom("{{command.name}}"),
+ &DomainDispatcherImpl::{{command.name}}
+ },
+ {% endfor %}
+ };
+ return commands;
+ }();
+ return {{config.crdtp.namespace}}::FindByFirst<DomainDispatcherImpl::CallHandler>(*commands, command_name, nullptr);
}
-
-void DispatcherImpl::dispatch(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<protocol::DictionaryValue> messageObject)
-{
- std::unordered_map<String, CallHandler>::iterator it = m_dispatchMap.find(method);
- DCHECK(it != m_dispatchMap.end());
- protocol::ErrorSupport errors;
- (this->*(it->second))(callId, method, message, std::move(messageObject), &errors);
+} // namespace
+
+std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> DomainDispatcherImpl::Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) {
+ CallHandler handler = CommandByName(command_name);
+ if (!handler) return nullptr;
+ return [this, handler](const {{config.crdtp.namespace}}::Dispatchable& dispatchable){
+ std::unique_ptr<DictionaryValue> params =
+ DictionaryValue::cast(protocol::Value::parseBinary(dispatchable.Params().data(),
+ dispatchable.Params().size()));
+ ErrorSupport errors;
+ errors.Push();
+ (this->*handler)(dispatchable, params.get(), &errors);
+ };
}
{% for command in domain.commands %}
@@ -262,10 +268,11 @@ void DispatcherImpl::dispatch(int callId, const String& method, const ProtocolMe
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
{% if protocol.is_async_command(domain.domain, command.name) %}
-class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DispatcherBase::Callback {
+class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DomainDispatcher::Callback {
public:
- {{command_name_title}}CallbackImpl(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, const ProtocolMessage& message)
- : DispatcherBase::Callback(std::move(backendImpl), callId, method, message) { }
+ {{command_name_title}}CallbackImpl(std::unique_ptr<DomainDispatcher::WeakPtr> backendImpl, int callId, {{config.crdtp.namespace}}::span<uint8_t> message)
+ : DomainDispatcher::Callback(std::move(backendImpl), callId,
+{{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), message) { }
void sendSuccess(
{%- for parameter in command.returns -%}
@@ -277,16 +284,16 @@ public:
{%- if not loop.last -%}, {% endif -%}
{%- endfor -%}) override
{
- std::unique_ptr<protocol::DictionaryValue> resultObject = DictionaryValue::create();
- {% for parameter in command.returns %}
- {% if "optional" in parameter %}
- if ({{parameter.name}}.isJust())
- resultObject->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue({{parameter.name}}.fromJust()));
- {% else %}
- resultObject->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue({{protocol.resolve_type(parameter).to_raw_type % parameter.name}}));
- {% endif %}
- {% endfor %}
- sendIfActive(std::move(resultObject), DispatchResponse::OK());
+ std::vector<uint8_t> result_buffer;
+ {{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
+ envelope_encoder.EncodeStart(&result_buffer);
+ result_buffer.push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
+ {% for parameter in command.returns %}
+ {{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{parameter.name}}"), {{parameter.name}}, &result_buffer);
+ {% endfor %}
+ result_buffer.push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
+ envelope_encoder.EncodeStop(&result_buffer);
+ sendIfActive({{config.crdtp.namespace}}::Serializable::From(std::move(result_buffer)), DispatchResponse::Success());
}
void fallThrough() override
@@ -296,37 +303,31 @@ public:
void sendFailure(const DispatchResponse& response) override
{
- DCHECK(response.status() == DispatchResponse::kError);
+ DCHECK(response.IsError());
sendIfActive(nullptr, response);
}
};
{% endif %}
-void DispatcherImpl::{{command.name}}(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors)
+void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors)
{
{% if "parameters" in command %}
// Prepare input parameters.
- protocol::DictionaryValue* object = DictionaryValue::cast(requestMessageObject->get("params"));
- errors->push();
{% for parameter in command.parameters %}
{% set parameter_type = protocol.resolve_type(parameter) %}
- protocol::Value* {{parameter.name}}Value = object ? object->get("{{parameter.name}}") : nullptr;
+ protocol::Value* {{parameter.name}}Value = params ? params->get("{{parameter.name}}") : nullptr;
{% if parameter.optional %}
Maybe<{{parameter_type.raw_type}}> in_{{parameter.name}};
if ({{parameter.name}}Value) {
- errors->setName("{{parameter.name}}");
+ errors->SetName("{{parameter.name}}");
in_{{parameter.name}} = ValueConversions<{{parameter_type.raw_type}}>::fromValue({{parameter.name}}Value, errors);
}
{% else %}
- errors->setName("{{parameter.name}}");
+ errors->SetName("{{parameter.name}}");
{{parameter_type.type}} in_{{parameter.name}} = ValueConversions<{{parameter_type.raw_type}}>::fromValue({{parameter.name}}Value, errors);
{% endif %}
{% endfor %}
- errors->pop();
- if (errors->hasErrors()) {
- reportProtocolError(callId, DispatchResponse::kInvalidParams, kInvalidParamsString, errors);
- return;
- }
+ if (MaybeReportInvalidParams(dispatchable, *errors)) return;
{% endif %}
{% if "returns" in command and not protocol.is_async_command(domain.domain, command.name) %}
// Declare output parameters.
@@ -340,7 +341,7 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, const Pr
{% endif %}
{% if not protocol.is_async_command(domain.domain, command.name) %}
- std::unique_ptr<DispatcherBase::WeakPtr> weak = weakPtr();
+ std::unique_ptr<DomainDispatcher::WeakPtr> weak = weakPtr();
DispatchResponse response = m_backend->{{command.name | to_method_case}}(
{%- for parameter in command.parameters -%}
{%- if not loop.first -%}, {% endif -%}
@@ -356,31 +357,31 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, const Pr
&out_{{parameter.name}}
{%- endfor %}
{% endif %});
- if (response.status() == DispatchResponse::kFallThrough) {
- channel()->fallThrough(callId, method, message);
+ if (response.IsFallThrough()) {
+ channel()->FallThrough(dispatchable.CallId(), {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), dispatchable.Serialized());
return;
}
{% if "returns" in command %}
- std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create();
- if (response.status() == DispatchResponse::kSuccess) {
- {% for parameter in command.returns %}
- {% if "optional" in parameter %}
- if (out_{{parameter.name}}.isJust())
- result->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue(out_{{parameter.name}}.fromJust()));
- {% else %}
- result->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue({{protocol.resolve_type(parameter).to_raw_type % ("out_" + parameter.name)}}));
- {% endif %}
- {% endfor %}
- }
- if (weak->get())
- weak->get()->sendResponse(callId, response, std::move(result));
+ if (weak->get()) {
+ std::vector<uint8_t> result;
+ if (response.IsSuccess()) {
+ {{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
+ envelope_encoder.EncodeStart(&result);
+ result.push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
+ {% for parameter in command.returns %}
+ {{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{parameter.name}}"), out_{{parameter.name}}, &result);
+ {% endfor %}
+ result.push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
+ envelope_encoder.EncodeStop(&result);
+ }
+ weak->get()->sendResponse(dispatchable.CallId(), response, {{config.crdtp.namespace}}::Serializable::From(std::move(result)));
+ }
{% else %}
if (weak->get())
- weak->get()->sendResponse(callId, response);
+ weak->get()->sendResponse(dispatchable.CallId(), response);
{% endif %}
return;
{% else %}
- std::unique_ptr<{{command_name_title}}CallbackImpl> callback(new {{command.name | to_title_case}}CallbackImpl(weakPtr(), callId, method, message));
m_backend->{{command.name | to_method_case}}(
{%- for property in command.parameters -%}
{%- if not loop.first -%}, {% endif -%}
@@ -391,18 +392,34 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, const Pr
{%- endif -%}
{%- endfor -%}
{%- if command.parameters -%}, {% endif -%}
- std::move(callback));
- return;
+ std::make_unique<{{command_name_title}}CallbackImpl>(weakPtr(), dispatchable.CallId(), dispatchable.Serialized()));
{% endif %}
}
{% endfor %}
+namespace {
+// This helper method (with a static map of redirects) is used from Dispatcher::wire
+// immediately below.
+const std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>, {{config.crdtp.namespace}}::span<uint8_t>>>& SortedRedirects() {
+ static auto* redirects = [](){
+ auto* redirects = new std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>, {{config.crdtp.namespace}}::span<uint8_t>>>{
+ {% for command in domain.commands|sort(attribute="name",case_sensitive=True) %}
+ {% if "redirect" in command %}
+ { {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), {{config.crdtp.namespace}}::SpanFrom("{{command.redirect}}.{{command.name}}") },
+ {% endif %}
+ {% endfor %}
+ };
+ return redirects;
+ }();
+ return *redirects;
+}
+} // namespace
+
// static
void Dispatcher::wire(UberDispatcher* uber, Backend* backend)
{
- std::unique_ptr<DispatcherImpl> dispatcher(new DispatcherImpl(uber->channel(), backend));
- uber->setupRedirects(dispatcher->redirects());
- uber->registerBackend("{{domain.domain}}", std::move(dispatcher));
+ auto dispatcher = std::make_unique<DomainDispatcherImpl>(uber->channel(), backend);
+ uber->WireBackend({{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}"), SortedRedirects(), std::move(dispatcher));
}
} // {{domain.domain}}
diff --git a/chromium/third_party/inspector_protocol/templates/TypeBuilder_h.template b/chromium/third_party/inspector_protocol/templates/TypeBuilder_h.template
index 19fd0f1260a..bc3998e4a27 100644
--- a/chromium/third_party/inspector_protocol/templates/TypeBuilder_h.template
+++ b/chromium/third_party/inspector_protocol/templates/TypeBuilder_h.template
@@ -100,15 +100,8 @@ public:
{% endfor %}
std::unique_ptr<protocol::DictionaryValue> toValue() const;
- void AppendSerialized(std::vector<uint8_t>* out) const override {
- toValue()->AppendSerialized(out);
- }
- String toJSON() const { return toValue()->toJSONString(); }
+ void AppendSerialized(std::vector<uint8_t>* out) const override;
std::unique_ptr<{{type.id}}> clone() const;
- {% if protocol.is_exported(domain.domain, type.id) %}
- {{config.exported.string_out}} toJSONString() const override;
- void writeBinary(std::vector<uint8_t>* out) const override;
- {% endif %}
template<int STATE>
class {{type.id}}Builder {
@@ -246,7 +239,7 @@ public:
{% if protocol.generate_disable(domain) %}
virtual DispatchResponse {{"disable" | to_method_case}}()
{
- return DispatchResponse::OK();
+ return DispatchResponse::Success();
}
{% endif %}
};
@@ -255,7 +248,7 @@ public:
class {{config.protocol.export_macro}} Frontend {
public:
- explicit Frontend(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) { }
+ explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {}
{% for event in domain.events %}
{% if not protocol.generate_event(domain.domain, event.name) %}{% continue %}{% endif %}
void {{event.name | to_method_case}}(
@@ -269,10 +262,10 @@ public:
);
{% endfor %}
- void flush();
- void sendRawCBORNotification(std::vector<uint8_t>);
-private:
- FrontendChannel* m_frontendChannel;
+ void flush();
+ void sendRawNotification(std::unique_ptr<Serializable>);
+ private:
+ FrontendChannel* frontend_channel_;
};
// ------------- Dispatcher.