diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/third_party/inspector_protocol | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-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')
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. |