Commit 12b4c387fe4 for nodejs
commit 12b4c387fe4c73bb6379cea58c2eb12d1fef5eea
Author: Node.js GitHub Bot <github-bot@iojs.org>
Date: Sun Nov 8 00:36:00 2026 +0000
deps: update inspector_protocol to c0045057ce801744c5e88596a2f1bab1802e
PR-URL: https://github.com/nodejs/node/pull/66653
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
diff --git a/deps/inspector_protocol/README.node b/deps/inspector_protocol/README.node
index 6216d7a8ea0..ca6864b0508 100644
--- a/deps/inspector_protocol/README.node
+++ b/deps/inspector_protocol/README.node
@@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0
-Revision: 1b1bcbbe060e8c8cd8704f00f78978c50991b307
+Revision: c0045057ce801744c5e88596a2f1bab1802e39f0
License: BSD
License File: LICENSE
Security Critical: no
diff --git a/deps/inspector_protocol/code_generator.py b/deps/inspector_protocol/code_generator.py
index b25ffc29b0a..2ad9465a6b2 100755
--- a/deps/inspector_protocol/code_generator.py
+++ b/deps/inspector_protocol/code_generator.py
@@ -101,35 +101,35 @@ def read_config():
config_base)
config_json_file.close()
defaults = {
- ".use_snake_file_names": False,
- ".use_title_case_methods": False,
- ".use_embedder_types": False,
- ".imported": False,
- ".imported.export_macro": "",
- ".imported.export_header": False,
- ".imported.header": False,
- ".imported.package": False,
- ".imported.options": False,
- ".protocol.export_macro": "",
- ".protocol.export_header": False,
- ".protocol.options": False,
- ".protocol.file_name_prefix": "",
- ".exported": False,
- ".exported.export_macro": "",
- ".exported.export_header": False,
- ".lib": False,
- ".lib.export_macro": "",
- ".lib.export_header": False,
- ".crdtp": False,
- ".crdtp.dir": os.path.join(inspector_protocol_dir, "crdtp"),
- ".crdtp.namespace": "crdtp",
+ ".use_snake_file_names": False,
+ ".use_title_case_methods": False,
+ ".use_embedder_types": False,
+ ".imported": False,
+ ".imported.export_macro": "",
+ ".imported.export_header": False,
+ ".imported.header": False,
+ ".imported.package": False,
+ ".imported.options": False,
+ ".protocol.export_macro": "",
+ ".protocol.export_header": False,
+ ".protocol.options": False,
+ ".protocol.file_name_prefix": "",
+ ".exported": False,
+ ".exported.export_macro": "",
+ ".exported.export_header": False,
+ ".lib": False,
+ ".lib.export_macro": "",
+ ".lib.export_header": False,
+ ".crdtp": False,
+ ".crdtp.dir": os.path.join(inspector_protocol_dir, "crdtp"),
+ ".crdtp.namespace": "crdtp",
}
for key_value in config_values:
parts = key_value.split("=")
if len(parts) == 2:
defaults["." + parts[0]] = parts[1]
- return (jinja_dir, config_file, init_defaults(config_partial, "", defaults),
- depfile, stamp)
+ return (jinja_dir, config_file, init_defaults(config_partial, "",
+ defaults), depfile, stamp)
except Exception:
# Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
exc = sys.exc_info()[1]
@@ -286,32 +286,32 @@ def create_any_type_definition():
def create_string_type_definition():
# pylint: disable=W0622
return {
- "return_type": "String",
- "pass_type": "const String&",
- "to_pass_type": "%s",
- "to_raw_type": "%s",
- "to_rvalue": "%s",
- "type": "String",
- "raw_type": "String",
- "raw_pass_type": "const String&",
- "raw_return_type": "String",
- "is_primitive": True
+ "return_type": "String",
+ "pass_type": "const String&",
+ "to_pass_type": "%s",
+ "to_raw_type": "%s",
+ "to_rvalue": "%s",
+ "type": "String",
+ "raw_type": "String",
+ "raw_pass_type": "const String&",
+ "raw_return_type": "String",
+ "is_primitive": True
}
def create_binary_type_definition():
# pylint: disable=W0622
return {
- "return_type": "Binary",
- "pass_type": "const Binary&",
- "to_pass_type": "%s",
- "to_raw_type": "%s",
- "to_rvalue": "%s",
- "type": "Binary",
- "raw_type": "Binary",
- "raw_pass_type": "const Binary&",
- "raw_return_type": "Binary",
- "is_primitive": True
+ "return_type": "Binary",
+ "pass_type": "const Binary&",
+ "to_pass_type": "%s",
+ "to_raw_type": "%s",
+ "to_rvalue": "%s",
+ "type": "Binary",
+ "raw_type": "Binary",
+ "raw_pass_type": "const Binary&",
+ "raw_return_type": "Binary",
+ "is_primitive": True
}
@@ -333,17 +333,17 @@ def create_primitive_type_definition(type):
"boolean": "TypeBoolean",
}
return {
- "return_type": typedefs[type],
- "pass_type": typedefs[type],
- "to_pass_type": "%s",
- "to_raw_type": "%s",
- "to_rvalue": "%s",
- "type": typedefs[type],
- "raw_type": typedefs[type],
- "raw_pass_type": typedefs[type],
- "raw_return_type": typedefs[type],
- "default_value": defaults[type],
- "is_primitive": True
+ "return_type": typedefs[type],
+ "pass_type": typedefs[type],
+ "to_pass_type": "%s",
+ "to_raw_type": "%s",
+ "to_rvalue": "%s",
+ "type": typedefs[type],
+ "raw_type": typedefs[type],
+ "raw_pass_type": typedefs[type],
+ "raw_return_type": typedefs[type],
+ "default_value": defaults[type],
+ "is_primitive": True
}
def wrap_array_definition(type):
@@ -444,10 +444,12 @@ class Protocol(object):
domain_match = re.match(r'^(.*)[.]', ref)
if domain_match:
referenced_domain_name = domain_match.group(1)
- if referenced_domain_name != domain['domain'] and not referenced_domain_name in dependencies:
- sys.stderr.write(("Domains [%s] uses type [%s] from domain [%s], but did not declare the dependency\n\n"
- ) % (domain["domain"], ref, referenced_domain_name))
- exit(1)
+ if referenced_domain_name != domain[
+ 'domain'] and not referenced_domain_name in dependencies:
+ sys.stderr.write((
+ "Domains [%s] uses type [%s] from domain [%s], but did not declare the dependency\n\n"
+ ) % (domain["domain"], ref, referenced_domain_name))
+ exit(1)
def generate_used_types(self):
all_refs = set()
@@ -552,10 +554,10 @@ class Protocol(object):
return self.type_definitions[prop["type"]]
def optional_type(self, prop):
- type = self.resolve_type(prop)
- template = ("std::optional<{}>" if type.get('is_primitive', False)
- else "std::unique_ptr<{}>")
- return template.format(type.get("raw_type"))
+ type = self.resolve_type(prop)
+ template = ("std::optional<{}>"
+ if type.get('is_primitive', False) else "std::unique_ptr<{}>")
+ return template.format(type.get("raw_type"))
def generate_command(self, domain, command):
if not self.config.protocol.options:
@@ -572,6 +574,12 @@ class Protocol(object):
def generate_type(self, domain, typename):
return domain + "." + typename in self.used_types
+ def should_pass_associated_data(self, domain, command):
+ if not self.config.protocol.options:
+ return False
+ return self.check_options(self.config.protocol.options, domain, command,
+ "pass_associated_data", None, False)
+
def is_async_command(self, domain, command):
if not self.config.protocol.options:
return False
@@ -658,25 +666,23 @@ def main():
}
if domain["domain"] in protocol.generate_domains:
- output_h = os.path.join(config.protocol.output, to_file_name(
- config, file_name + ".h"))
+ output_h = os.path.join(config.protocol.output,
+ to_file_name(config, file_name + ".h"))
outputs[output_h] = h_template.render(template_context)
source_set.add(h_template.filename)
- output_cpp = os.path.join(config.protocol.output, to_file_name(
- config, file_name + ".cpp"))
+ output_cpp = os.path.join(config.protocol.output,
+ to_file_name(config, file_name + ".cpp"))
outputs[output_cpp] = cpp_template.render(template_context)
source_set.add(cpp_template.filename)
if domain["domain"] in protocol.exported_domains:
- output_export_h = os.path.join(config.exported.output, to_file_name(
- config, file_name + ".h"))
- outputs[output_export_h] = exported_template.render(
- template_context)
+ output_export_h = os.path.join(config.exported.output,
+ to_file_name(config, file_name + ".h"))
+ outputs[output_export_h] = exported_template.render(template_context)
source_set.add(exported_template.filename)
if domain["domain"] in protocol.imported_domains:
- output_import_h = os.path.join(config.protocol.output, to_file_name(
- config, file_name + ".h"))
- outputs[output_import_h] = imported_template.render(
- template_context)
+ output_import_h = os.path.join(config.protocol.output,
+ to_file_name(config, file_name + ".h"))
+ outputs[output_import_h] = imported_template.render(template_context)
source_set.add(imported_template.filename)
if config.lib:
@@ -699,19 +705,19 @@ def main():
if not config.use_embedder_types:
protocol_h_templates += [
- "Values_h.template",
- "Object_h.template",
- "ValueConversions_h.template",
+ "Values_h.template",
+ "Object_h.template",
+ "ValueConversions_h.template",
]
protocol_cpp_templates += [
- "Protocol_cpp.template",
- "Values_cpp.template",
- "Object_cpp.template",
- "ValueConversions_cpp.template",
+ "Protocol_cpp.template",
+ "Values_cpp.template",
+ "Object_cpp.template",
+ "ValueConversions_cpp.template",
]
else:
protocol_h_templates += [
- "Forward_h.template",
+ "Forward_h.template",
]
def generate_lib_file(file_name, template_files):
@@ -729,8 +735,9 @@ def main():
config, "Protocol.h")), protocol_h_templates)
if not config.use_embedder_types:
- generate_lib_file(os.path.join(config.lib.output, to_file_name(
- config, "Protocol.cpp")), protocol_cpp_templates)
+ generate_lib_file(
+ os.path.join(config.lib.output, to_file_name(config, "Protocol.cpp")),
+ protocol_cpp_templates)
if stamp_filename:
with open(stamp_filename, "w"):
@@ -748,8 +755,8 @@ def main():
if deps_filename:
assert stamp_filename
with open(deps_filename, "w") as deps_file:
- deps_file.write("%s: %s\n" % (
- stamp_filename, " ".join(sorted(source_set))))
+ deps_file.write("%s: %s\n" %
+ (stamp_filename, " ".join(sorted(source_set))))
if __name__ == "__main__":
diff --git a/deps/inspector_protocol/concatenate_protocols.py b/deps/inspector_protocol/concatenate_protocols.py
index 8ad4f6de712..14bb366497c 100755
--- a/deps/inspector_protocol/concatenate_protocols.py
+++ b/deps/inspector_protocol/concatenate_protocols.py
@@ -58,8 +58,8 @@ def main(argv):
if deps_filename:
assert stamp_filename
with open(deps_filename, "w") as deps_file:
- deps_file.write("%s: %s\n" % (
- stamp_filename, " ".join(sorted(source_set))))
+ deps_file.write("%s: %s\n" %
+ (stamp_filename, " ".join(sorted(source_set))))
if __name__ == '__main__':
diff --git a/deps/inspector_protocol/crdtp/cbor.cc b/deps/inspector_protocol/crdtp/cbor.cc
index dc52549c55a..7e62d765141 100644
--- a/deps/inspector_protocol/crdtp/cbor.cc
+++ b/deps/inspector_protocol/crdtp/cbor.cc
@@ -1074,5 +1074,97 @@ Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
*(out) = new_envelope_size & 0xff;
return Status();
}
+
+span<uint8_t> GetString8ValueFromMap(span<uint8_t> message,
+ span<uint8_t> string8_key) {
+ CBORTokenizer tokenizer(message);
+ if (tokenizer.TokenTag() != CBORTokenTag::ENVELOPE) {
+ return {};
+ }
+ tokenizer.EnterEnvelope();
+ if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) {
+ return {};
+ }
+ tokenizer.Next();
+ span<uint8_t> result;
+ bool found_key = false;
+ bool found_value = false;
+ bool is_key = true;
+ while (tokenizer.TokenTag() != CBORTokenTag::STOP &&
+ tokenizer.TokenTag() != CBORTokenTag::DONE &&
+ tokenizer.TokenTag() != CBORTokenTag::ERROR_VALUE) {
+ if (tokenizer.TokenTag() == CBORTokenTag::MAP_START ||
+ tokenizer.TokenTag() == CBORTokenTag::ARRAY_START) {
+ return {};
+ }
+ if (is_key) {
+ if (tokenizer.TokenTag() == CBORTokenTag::STRING8 &&
+ SpanEquals(tokenizer.GetString8(), string8_key)) {
+ if (found_key) {
+ return {}; // Duplicate key
+ }
+ found_key = true;
+ }
+ } else if (found_key && !found_value) {
+ if (tokenizer.TokenTag() == CBORTokenTag::STRING8) {
+ result = tokenizer.GetString8();
+ }
+ found_value = true;
+ }
+ tokenizer.Next();
+ is_key = !is_key;
+ }
+ if (tokenizer.TokenTag() != CBORTokenTag::STOP) {
+ return {};
+ }
+ return result;
+}
+
+bool HasKeyInMap(span<uint8_t> message, span<uint8_t> key) {
+ CBORTokenizer tokenizer(message);
+ if (tokenizer.TokenTag() != CBORTokenTag::ENVELOPE) {
+ return false;
+ }
+ tokenizer.EnterEnvelope();
+ if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) {
+ return false;
+ }
+ tokenizer.Next();
+ bool is_key = true;
+ size_t nested_depth = 0;
+ while (tokenizer.TokenTag() != CBORTokenTag::DONE &&
+ tokenizer.TokenTag() != CBORTokenTag::ERROR_VALUE) {
+ if (tokenizer.TokenTag() == CBORTokenTag::STOP) {
+ if (nested_depth == 0) {
+ break;
+ }
+ nested_depth--;
+ if (nested_depth == 0) {
+ is_key = !is_key;
+ }
+ tokenizer.Next();
+ continue;
+ }
+ if (tokenizer.TokenTag() == CBORTokenTag::MAP_START ||
+ tokenizer.TokenTag() == CBORTokenTag::ARRAY_START) {
+ nested_depth++;
+ tokenizer.Next();
+ continue;
+ }
+ if (nested_depth > 0) {
+ tokenizer.Next();
+ continue;
+ }
+ if (is_key) {
+ if (tokenizer.TokenTag() == CBORTokenTag::STRING8 &&
+ SpanEquals(tokenizer.GetString8(), key)) {
+ return true;
+ }
+ }
+ tokenizer.Next();
+ is_key = !is_key;
+ }
+ return false;
+}
} // namespace cbor
} // namespace crdtp
diff --git a/deps/inspector_protocol/crdtp/cbor.h b/deps/inspector_protocol/crdtp/cbor.h
index 2d925fad5cc..d7bcfa2652f 100644
--- a/deps/inspector_protocol/crdtp/cbor.h
+++ b/deps/inspector_protocol/crdtp/cbor.h
@@ -315,6 +315,18 @@ CRDTP_EXPORT Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
span<uint8_t> string8_value,
std::vector<uint8_t>* cbor);
+// Safely extracts the value for |string8_key| from a CBOR encoded map wrapped
+// in an envelope. This is a shallow parser that skips unknown keys and
+// complex values. If the key is not found, or if it is found more than once,
+// or if it contains nested maps/arrays, returns an empty span.
+// Explicitly rejects duplicate keys and non-STRING8 values.
+CRDTP_EXPORT span<uint8_t> GetString8ValueFromMap(span<uint8_t> message,
+ span<uint8_t> string8_key);
+// Safely checks if |key| exists in the top-level of a CBOR encoded map wrapped
+// in an envelope. Shallow parser that skips nested structures.
+// Returns true as soon as the key is found at the top level.
+CRDTP_EXPORT bool HasKeyInMap(span<uint8_t> message, span<uint8_t> key);
+
namespace internals { // Exposed only for writing tests.
CRDTP_EXPORT size_t ReadTokenStart(span<uint8_t> bytes,
cbor::MajorType* type,
diff --git a/deps/inspector_protocol/crdtp/cbor_test.cc b/deps/inspector_protocol/crdtp/cbor_test.cc
index 2d3689e7300..4b23d3629d8 100644
--- a/deps/inspector_protocol/crdtp/cbor_test.cc
+++ b/deps/inspector_protocol/crdtp/cbor_test.cc
@@ -1445,5 +1445,362 @@ TEST(AppendString8EntryToMapTest, InvalidEnvelope_Error) {
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0));
}
}
+
+// =============================================================================
+// cbor::GetString8ValueFromMap
+// =============================================================================
+
+TEST(GetString8ValueFromMapTest, HandlesEmptyStringKey) {
+ std::vector<uint8_t> encoded = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom(""), SpanFrom("value1"),
+ &encoded),
+ StatusIsOk());
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key2"), SpanFrom("value2"),
+ &encoded),
+ StatusIsOk());
+
+ span<uint8_t> result1 =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom(""));
+ EXPECT_EQ("value1", std::string(result1.begin(), result1.end()));
+
+ span<uint8_t> result2 =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key2"));
+ EXPECT_EQ("value2", std::string(result2.begin(), result2.end()));
+}
+
+TEST(GetString8ValueFromMapTest, ExtractsValueSuccessfully) {
+ std::vector<uint8_t> encoded = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key1"), SpanFrom("value1"),
+ &encoded),
+ StatusIsOk());
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key2"), SpanFrom("value2"),
+ &encoded),
+ StatusIsOk());
+
+ span<uint8_t> result1 =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key1"));
+ EXPECT_EQ("value1", std::string(result1.begin(), result1.end()));
+
+ span<uint8_t> result2 =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key2"));
+ EXPECT_EQ("value2", std::string(result2.begin(), result2.end()));
+}
+
+TEST(GetString8ValueFromMapTest, HandlesMissingKey) {
+ std::vector<uint8_t> encoded = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
+ EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key1"), SpanFrom("value1"),
+ &encoded),
+ StatusIsOk());
+
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("missing_key"));
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(GetString8ValueFromMapTest, HandlesNonStringValuesGracefully) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("int_key"), &encoded);
+ EncodeInt32(42, &encoded);
+
+ EncodeString8(SpanFrom("string_key"), &encoded);
+ EncodeString8(SpanFrom("string_value"), &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ span<uint8_t> result1 =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("int_key"));
+ EXPECT_TRUE(result1.empty());
+
+ span<uint8_t> result2 =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("string_key"));
+ EXPECT_EQ("string_value", std::string(result2.begin(), result2.end()));
+}
+
+TEST(GetString8ValueFromMapTest, HandlesErrorValue) {
+ // Envelope with 2 bytes of content:
+ // [Map Start (0xbf), String Start length 1 (0x61)].
+ // The string key is truncated (missing its data byte), which triggers
+ // CBORTokenTag::ERROR_VALUE.
+ std::vector<uint8_t> encoded = {
+ 0xd8, 0x5a, 0, 0, 0, 2,
+ EncodeIndefiniteLengthMapStart(),
+ 0x61 /* major type 3 (string), additional info 1 (length 1) */
+ };
+
+ // Without the fix for the infinite loop, this call would never return.
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key"));
+
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(GetString8ValueFromMapTest, RejectsDuplicateKeys) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeString8(SpanFrom("value1"), &encoded);
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeString8(SpanFrom("value2"), &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key"));
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(GetString8ValueFromMapTest, RejectsNestedStructures) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("fake"), &encoded);
+ // Nested map: {"key": "spoofed"}
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeString8(SpanFrom("spoofed"), &encoded);
+ encoded.push_back(EncodeStop());
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key"));
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(GetString8ValueFromMapTest, RejectsString16Values) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ // "value16" in UTF-16LE
+ std::vector<uint16_t> value16 = {'v', 'a', 'l', 'u', 'e', '1', '6'};
+ EncodeString16(SpanFrom(value16), &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key"));
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(GetString8ValueFromMapTest, HandlesEmptyValuesWithDuplicates) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeString8(SpanFrom(""), &encoded);
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeString8(SpanFrom("value2"), &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key"));
+ EXPECT_TRUE(result.empty());
+}
+
+
+TEST(GetString8ValueFromMapTest, RejectsTruncatedMap) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ // Missing value here.
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(encoded), SpanFrom("key"));
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(GetString8ValueFromMapTest, InvalidMessage) {
+ std::vector<uint8_t> msg = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), 42};
+ span<uint8_t> result =
+ GetString8ValueFromMap(SpanFrom(msg), SpanFrom("key"));
+ EXPECT_TRUE(result.empty());
+}
+
+TEST(HasKeyInMapTest, FindsKeySuccessfully) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key1"), &encoded);
+ EncodeString8(SpanFrom("value1"), &encoded);
+
+ EncodeString8(SpanFrom("key2"), &encoded);
+ EncodeInt32(42, &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key1")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key2")));
+}
+
+TEST(HasKeyInMapTest, HandlesMissingKey) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key1"), &encoded);
+ EncodeString8(SpanFrom("value1"), &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ EXPECT_FALSE(HasKeyInMap(SpanFrom(encoded), SpanFrom("missing_key")));
+}
+
+TEST(HasKeyInMapTest, WorksWithVariousValueTypes) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("string_key"), &encoded);
+ EncodeString8(SpanFrom("value"), &encoded);
+
+ EncodeString8(SpanFrom("int_key"), &encoded);
+ EncodeInt32(42, &encoded);
+
+ EncodeString8(SpanFrom("double_key"), &encoded);
+ EncodeDouble(3.14, &encoded);
+
+ EncodeString8(SpanFrom("bool_true_key"), &encoded);
+ encoded.push_back(EncodeTrue());
+
+ EncodeString8(SpanFrom("bool_false_key"), &encoded);
+ encoded.push_back(EncodeFalse());
+
+ EncodeString8(SpanFrom("null_key"), &encoded);
+ encoded.push_back(EncodeNull());
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("string_key")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("int_key")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("double_key")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("bool_true_key")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("bool_false_key")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("null_key")));
+}
+
+TEST(HasKeyInMapTest, HandlesErrorValue) {
+ // Envelope with 2 bytes of content:
+ // [Map Start (0xbf), String Start length 1 (0x61)].
+ // The string key is truncated (missing its data byte), which triggers
+ // CBORTokenTag::ERROR_VALUE.
+ std::vector<uint8_t> encoded = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(),
+ 0x61 /* major type 3 (string), additional info 1 (length 1) */
+ };
+
+ EXPECT_FALSE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key")));
+}
+
+TEST(HasKeyInMapTest, AllowsDuplicateKeys) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeInt32(42, &encoded);
+
+ EncodeString8(SpanFrom("key"), &encoded);
+ EncodeInt32(24, &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key")));
+}
+
+TEST(HasKeyInMapTest, AllowsNestedStructures) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key1"), &encoded);
+ // Start a nested map as value.
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+ EncodeString8(SpanFrom("inner_key"), &encoded);
+ EncodeInt32(42, &encoded);
+ encoded.push_back(EncodeStop());
+
+ EncodeString8(SpanFrom("key2"), &encoded);
+ EncodeInt32(24, &encoded);
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ // We should find top-level keys even with nested structures.
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key1")));
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key2")));
+
+ // We should NOT find keys that are inside nested structures (they are not
+ // top-level).
+ EXPECT_FALSE(HasKeyInMap(SpanFrom(encoded), SpanFrom("inner_key")));
+}
+
+TEST(HasKeyInMapTest, HandlesTruncation) {
+ std::vector<uint8_t> encoded;
+ EnvelopeEncoder envelope;
+ envelope.EncodeStart(&encoded);
+ encoded.push_back(EncodeIndefiniteLengthMapStart());
+
+ EncodeString8(SpanFrom("key1"), &encoded);
+ // Missing value here.
+ // Truncated before we can write key2.
+
+ encoded.push_back(EncodeStop());
+ envelope.EncodeStop(&encoded);
+
+ // "key1" is found because we return early on seeing the key.
+ EXPECT_TRUE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key1")));
+
+ // "key2" is not found because it is not in the map (and map is truncated).
+ EXPECT_FALSE(HasKeyInMap(SpanFrom(encoded), SpanFrom("key2")));
+}
+
+TEST(HasKeyInMapTest, InvalidMessage) {
+ std::vector<uint8_t> msg = {
+ 0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), 42};
+ EXPECT_FALSE(HasKeyInMap(SpanFrom(msg), SpanFrom("key")));
+}
+
} // namespace cbor
} // namespace crdtp
diff --git a/deps/inspector_protocol/crdtp/dispatch.cc b/deps/inspector_protocol/crdtp/dispatch.cc
index b36b91ad0e3..9f94cf2793c 100644
--- a/deps/inspector_protocol/crdtp/dispatch.cc
+++ b/deps/inspector_protocol/crdtp/dispatch.cc
@@ -30,6 +30,14 @@ DispatchResponse DispatchResponse::FallThrough() {
return result;
}
+// static
+DispatchResponse DispatchResponse::FallThrough(std::string associated_data) {
+ DispatchResponse result;
+ result.code_ = DispatchCode::FALL_THROUGH;
+ result.message_ = std::move(associated_data);
+ return result;
+}
+
// static
DispatchResponse DispatchResponse::ParseError(std::string message) {
DispatchResponse result;
@@ -89,7 +97,12 @@ DispatchResponse DispatchResponse::SessionNotFound(std::string message) {
// =============================================================================
// Dispatchable - a shallow parser for CBOR encoded DevTools messages
// =============================================================================
-Dispatchable::Dispatchable(span<uint8_t> serialized) : serialized_(serialized) {
+Dispatchable::Dispatchable(span<uint8_t> serialized,
+ std::string_view associated_data,
+ FallthroughCallback fallthrough_callback)
+ : serialized_(serialized),
+ associated_data_(associated_data),
+ fallthrough_callback_(std::move(fallthrough_callback)) {
Status s = cbor::CheckCBORMessage(serialized);
if (!s.ok()) {
status_ = {Error::MESSAGE_MUST_BE_AN_OBJECT, s.pos};
@@ -264,6 +277,17 @@ bool Dispatchable::MaybeParseSessionId(cbor::CBORTokenizer* tokenizer) {
return true;
}
+FallthroughCallback Dispatchable::TakeFallthroughCallback() {
+ FallthroughCallback result;
+ std::swap(result, fallthrough_callback_);
+ return result;
+}
+
+void Dispatchable::DispatchFallThrough(const std::string& associated_data) {
+ assert(fallthrough_callback_);
+ TakeFallthroughCallback()(call_id_, method_, serialized_, associated_data);
+}
+
namespace {
class ProtocolError : public Serializable {
public:
@@ -424,13 +448,13 @@ void DomainDispatcher::Callback::dispose() {
DomainDispatcher::Callback::Callback(
std::unique_ptr<DomainDispatcher::WeakPtr> backend_impl,
- int call_id,
- span<uint8_t> method,
- span<uint8_t> message)
+ Dispatchable& dispatchable)
: backend_impl_(std::move(backend_impl)),
- call_id_(call_id),
- method_(method),
- message_(message.begin(), message.end()) {}
+ call_id_(dispatchable.CallId()),
+ method_(dispatchable.Method()),
+ message_(dispatchable.Serialized().begin(),
+ dispatchable.Serialized().end()),
+ fallthrough_callback_(dispatchable.TakeFallthroughCallback()) {}
void DomainDispatcher::Callback::sendIfActive(
std::unique_ptr<Serializable> partialMessage,
@@ -445,8 +469,11 @@ void DomainDispatcher::Callback::sendIfActive(
void DomainDispatcher::Callback::fallThroughIfActive() {
if (!backend_impl_ || !backend_impl_->get())
return;
- backend_impl_->get()->channel()->FallThrough(call_id_, method_,
- SpanFrom(message_));
+ // Fallthrough callback may be retaining session which outlives backend.
+ // TODO(caseq): handle fall-through associated data for async callbacks.
+ fallthrough_callback_(call_id_, method_, SpanFrom(message_),
+ std::string_view());
+ fallthrough_callback_ = nullptr;
backend_impl_ = nullptr;
}
@@ -499,17 +526,6 @@ std::unique_ptr<DomainDispatcher::WeakPtr> DomainDispatcher::weakPtr() {
// =============================================================================
// 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);
@@ -526,36 +542,34 @@ size_t DotIdx(span<uint8_t> method) {
}
} // namespace
-UberDispatcher::DispatchResult UberDispatcher::Dispatch(
- const Dispatchable& dispatchable) const {
+void UberDispatcher::Dispatch(Dispatchable& dispatchable) {
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);
- });
+ if (DomainDispatcher* dispatcher = FindByFirst(dispatchers_, domain)) {
+ if (dispatcher->Dispatch(command, dispatchable)) {
+ return;
}
}
}
- 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")));
- });
+ if (auto fallthrough = dispatchable.TakeFallthroughCallback()) {
+ fallthrough(dispatchable.CallId(), dispatchable.Method(),
+ dispatchable.Serialized(), std::string_view());
+ } else {
+ SendMethodNotFound(dispatchable.CallId(), method);
+ }
+}
+
+void UberDispatcher::SendMethodNotFound(int call_id, span<uint8_t> method) {
+ frontend_channel_->SendProtocolResponse(
+ call_id,
+ CreateErrorResponse(call_id,
+ DispatchResponse::MethodNotFound(
+ "'" + std::string(method.begin(), method.end()) +
+ "' wasn't found")));
}
template <typename T>
diff --git a/deps/inspector_protocol/crdtp/dispatch.h b/deps/inspector_protocol/crdtp/dispatch.h
index 8da8255c6fe..4f5d33990cf 100644
--- a/deps/inspector_protocol/crdtp/dispatch.h
+++ b/deps/inspector_protocol/crdtp/dispatch.h
@@ -9,7 +9,9 @@
#include <cstdint>
#include <functional>
#include <string>
+#include <string_view>
#include <unordered_set>
+
#include "export.h"
#include "serializable.h"
#include "span.h"
@@ -55,6 +57,7 @@ class CRDTP_EXPORT DispatchResponse {
static DispatchResponse Success();
static DispatchResponse FallThrough();
+ static DispatchResponse FallThrough(std::string associated_data);
// Indicates that a message could not be parsed. E.g., malformed JSON.
static DispatchResponse ParseError(std::string message);
@@ -84,9 +87,20 @@ class CRDTP_EXPORT DispatchResponse {
private:
DispatchResponse() = default;
DispatchCode code_;
+ // For error responses, a message describing the error.
+ // For fall-through responses, fall-through associated message.
std::string message_;
};
+// This callback is invoked if the command was not dispatched due to handler
+// not being found. The caller has an opportunity to dispatch the command
+// to a different layer then.
+using FallthroughCallback =
+ std::function<void(int call_id,
+ span<uint8_t> method,
+ span<uint8_t> serialized_message,
+ std::string_view associated_data)>;
+
// =============================================================================
// Dispatchable - a shallow parser for CBOR encoded DevTools messages
// =============================================================================
@@ -100,7 +114,16 @@ class CRDTP_EXPORT Dispatchable {
// |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);
+ // |associated_data| would be passed as is to handler methods that were
+ // configured as those willing to receive it.
+ // |fallthrough_callback|, if non-empty, will be invoked by the dispatcher
+ // if no handler was found or if the handler chose to fall through,
+ // and can be used to pass command to be handled elsewhere.
+ // Note that in case of an async command, fallthrough_callback may be invoked
+ // asynchronously.
+ Dispatchable(span<uint8_t> serialized,
+ std::string_view associated_data,
+ FallthroughCallback fallthrough_callback);
// The serialized message that we just parsed.
span<uint8_t> Serialized() const { return serialized_; }
@@ -127,6 +150,17 @@ class CRDTP_EXPORT Dispatchable {
// not parse into this; it only provides access to its raw contents here.
span<uint8_t> Params() const { return params_; }
+ std::string_view AssociatedData() const { return associated_data_; }
+
+ // Takes the fallthrough callback, if any.
+ FallthroughCallback TakeFallthroughCallback();
+ // Takes the fallthrough callback and dispatches it with stored
+ // call/method/message. The associated_data is passed through to
+ // the other handling layer (not to be confused with the member
+ // associated_data which is the one to be passed to handlers
+ // during current dispatch).
+ void DispatchFallThrough(const std::string& associated_data);
+
private:
bool MaybeParseProperty(cbor::CBORTokenizer* tokenizer);
bool MaybeParseCallId(cbor::CBORTokenizer* tokenizer);
@@ -144,6 +178,8 @@ class CRDTP_EXPORT Dispatchable {
bool params_seen_ = false;
span<uint8_t> params_;
span<uint8_t> session_id_;
+ std::string_view associated_data_;
+ FallthroughCallback fallthrough_callback_;
};
// =============================================================================
@@ -197,10 +233,7 @@ class CRDTP_EXPORT DomainDispatcher {
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);
+ Callback(std::unique_ptr<WeakPtr> backend_impl, Dispatchable& dispatchable);
void sendIfActive(std::unique_ptr<Serializable> partialMessage,
const DispatchResponse& response);
@@ -214,20 +247,19 @@ class CRDTP_EXPORT DomainDispatcher {
// storage for |method| is the binary of the running process.
span<uint8_t> method_;
std::vector<uint8_t> message_;
+ FallthroughCallback fallthrough_callback_;
};
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;
+ // corresponding method and attempeds to parse and execute it.
+ // If the command is not found, returns false. Otherwise and possible
+ // result, whether a response or error, is send using underlying
+ // frontend_channel_.
+ virtual bool Dispatch(span<uint8_t> command_name,
+ Dispatchable& dispatchable) = 0;
// Sends a response to the client via the channel.
void sendResponse(int call_id,
@@ -253,25 +285,6 @@ class CRDTP_EXPORT DomainDispatcher {
// =============================================================================
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();
@@ -280,7 +293,9 @@ class CRDTP_EXPORT UberDispatcher {
// 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;
+ void Dispatch(Dispatchable& dispatchable);
+
+ void SendMethodNotFound(int call_id, span<uint8_t> method);
// Invoked from generated code for wiring domain backends; that is,
// connecting domain handlers to an uber dispatcher.
@@ -308,6 +323,7 @@ class CRDTP_EXPORT UberDispatcher {
std::vector<std::pair<span<uint8_t>, std::unique_ptr<DomainDispatcher>>>
dispatchers_;
};
+
} // namespace crdtp
#endif // CRDTP_DISPATCH_H_
diff --git a/deps/inspector_protocol/crdtp/dispatch_test.cc b/deps/inspector_protocol/crdtp/dispatch_test.cc
index c7290f92961..e21b9bb309d 100644
--- a/deps/inspector_protocol/crdtp/dispatch_test.cc
+++ b/deps/inspector_protocol/crdtp/dispatch_test.cc
@@ -55,13 +55,21 @@ TEST(DispatchResponseTest, FallThrough) {
EXPECT_EQ(DispatchCode::FALL_THROUGH, error.Code());
}
+TEST(DispatchResponseTest, FallThroughWithAssociatedData) {
+ DispatchResponse fall_through = DispatchResponse::FallThrough("some details");
+ EXPECT_FALSE(fall_through.IsSuccess());
+ EXPECT_TRUE(fall_through.IsFallThrough());
+ EXPECT_EQ(DispatchCode::FALL_THROUGH, fall_through.Code());
+ EXPECT_EQ("some details", fall_through.Message());
+}
+
// =============================================================================
// 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);
+ Dispatchable empty(empty_span, std::string_view(), nullptr);
EXPECT_FALSE(empty.ok());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, empty.DispatchError().Code());
EXPECT_EQ("Message must be an object", empty.DispatchError().Message());
@@ -71,7 +79,7 @@ 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));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_FALSE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -84,7 +92,7 @@ TEST(DispatchableTest, MessageMustHaveIntegerIdProperty_IncorrectType) {
std::vector<uint8_t> cbor;
ASSERT_TRUE(
json::ConvertJSONToCBOR(SpanFrom("{\"id\":\"foo\"}"), &cbor).ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_FALSE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -96,7 +104,7 @@ 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));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -110,7 +118,7 @@ TEST(DispatchableTest, MessageMustHaveStringMethodProperty_IncorrectType) {
ASSERT_TRUE(
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":42}"), &cbor)
.ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -128,7 +136,7 @@ TEST(DispatchableTest, MessageMayHaveStringSessionIdProperty) {
"}"),
&cbor)
.ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -145,7 +153,7 @@ TEST(DispatchableTest, MessageMayHaveObjectParamsProperty) {
"}"),
&cbor)
.ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -159,7 +167,7 @@ TEST(DispatchableTest, MessageWithUnknownProperty) {
ASSERT_TRUE(
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"unknown\":42}"), &cbor)
.ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
@@ -177,7 +185,7 @@ TEST(DispatchableTest, DuplicateMapKey) {
SCOPED_TRACE("json = " + json);
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom(json), &cbor).ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_EQ(DispatchCode::PARSE_ERROR, dispatchable.DispatchError().Code());
EXPECT_THAT(dispatchable.DispatchError().Message(),
@@ -195,7 +203,7 @@ TEST(DispatchableTest, ValidMessageParsesOK_NoParams) {
SCOPED_TRACE("json = " + json);
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom(json), &cbor).ok());
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_TRUE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(42, dispatchable.CallId());
@@ -228,7 +236,7 @@ TEST(DispatchableTest, ValidMessageParsesOK_WithParams) {
cbor::EncodeString8(SpanFrom("f421ssvaz4"), &cbor);
cbor.push_back(cbor::EncodeStop());
envelope.EncodeStop(&cbor);
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), "associated data", nullptr);
EXPECT_TRUE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(42, dispatchable.CallId());
@@ -242,6 +250,7 @@ TEST(DispatchableTest, ValidMessageParsesOK_WithParams) {
ASSERT_EQ(cbor::CBORTokenTag::STRING8, params_tokenizer.TokenTag());
EXPECT_EQ("params payload", std::string(params_tokenizer.GetString8().begin(),
params_tokenizer.GetString8().end()));
+ EXPECT_EQ(dispatchable.AssociatedData(), "associated data");
}
TEST(DispatchableTest, FaultyCBORTrailingJunk) {
@@ -266,7 +275,7 @@ TEST(DispatchableTest, FaultyCBORTrailingJunk) {
cbor.push_back('a');
cbor.push_back('i');
cbor.push_back('l');
- Dispatchable dispatchable(SpanFrom(cbor));
+ Dispatchable dispatchable(SpanFrom(cbor), std::string_view(), nullptr);
EXPECT_FALSE(dispatchable.ok());
EXPECT_EQ(DispatchCode::PARSE_ERROR, dispatchable.DispatchError().Code());
EXPECT_EQ(57u, trailing_junk_pos);
@@ -341,9 +350,7 @@ class TestChannel : public FrontendChannel {
cbor_ = message->Serialize();
}
- void FallThrough(int call_id,
- span<uint8_t> method,
- span<uint8_t> message) override {}
+
void FlushProtocolNotifications() override {}
@@ -359,11 +366,9 @@ TEST(UberDispatcherTest, MethodNotFound) {
std::vector<uint8_t> message;
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":\"Foo.bar\"}"),
&message);
- Dispatchable dispatchable(SpanFrom(message));
+ Dispatchable dispatchable(SpanFrom(message), std::string_view(), nullptr);
ASSERT_TRUE(dispatchable.ok());
- UberDispatcher::DispatchResult dispatched = dispatcher.Dispatch(dispatchable);
- EXPECT_FALSE(dispatched.MethodFound());
- dispatched.Run();
+ dispatcher.Dispatch(dispatchable);
EXPECT_EQ(
"{\"id\":42,\"error\":"
"{\"code\":-32601,\"message\":\"'Foo.bar' wasn't found\"}}",
@@ -376,13 +381,12 @@ class TestDomain : public DomainDispatcher {
public:
explicit TestDomain(FrontendChannel* channel) : DomainDispatcher(channel) {}
- std::function<void(const Dispatchable&)> Dispatch(
- span<uint8_t> command_name) override {
+ bool Dispatch(span<uint8_t> command_name,
+ Dispatchable& dispatchable) override {
dispatched_commands_.push_back(
std::string(command_name.begin(), command_name.end()));
- return [this](const Dispatchable& dispatchable) {
- executed_commands_.push_back(dispatchable.CallId());
- };
+ executed_commands_.push_back(dispatchable.CallId());
+ return true;
}
// Command names of the dispatched commands.
@@ -418,23 +422,17 @@ TEST(UberDispatcherTest, DispatchingToDomainWithRedirects) {
std::vector<uint8_t> message;
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":\"Foo.execute\"}"),
&message);
- Dispatchable dispatchable(SpanFrom(message));
+ Dispatchable dispatchable(SpanFrom(message), std::string_view(), nullptr);
ASSERT_TRUE(dispatchable.ok());
- UberDispatcher::DispatchResult dispatched =
- dispatcher.Dispatch(dispatchable);
- EXPECT_TRUE(dispatched.MethodFound());
- dispatched.Run();
+ dispatcher.Dispatch(dispatchable);
}
{
std::vector<uint8_t> message;
json::ConvertJSONToCBOR(SpanFrom("{\"id\":43,\"method\":\"Foo.redirect\"}"),
&message);
- Dispatchable dispatchable(SpanFrom(message));
+ Dispatchable dispatchable(SpanFrom(message), std::string_view(), nullptr);
ASSERT_TRUE(dispatchable.ok());
- UberDispatcher::DispatchResult dispatched =
- dispatcher.Dispatch(dispatchable);
- EXPECT_TRUE(dispatched.MethodFound());
- dispatched.Run();
+ dispatcher.Dispatch(dispatchable);
}
EXPECT_THAT(foo->DispatchedCommands(), testing::ElementsAre("execute"));
EXPECT_THAT(foo->ExecutedCommands(), testing::ElementsAre(42));
diff --git a/deps/inspector_protocol/crdtp/frontend_channel.h b/deps/inspector_protocol/crdtp/frontend_channel.h
index 652c65219d4..fddb52e51e3 100644
--- a/deps/inspector_protocol/crdtp/frontend_channel.h
+++ b/deps/inspector_protocol/crdtp/frontend_channel.h
@@ -30,14 +30,6 @@ class CRDTP_EXPORT FrontendChannel {
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;
diff --git a/deps/inspector_protocol/inspector_protocol.gni b/deps/inspector_protocol/inspector_protocol.gni
index 6b278c9fa8e..b9948c08a04 100644
--- a/deps/inspector_protocol/inspector_protocol.gni
+++ b/deps/inspector_protocol/inspector_protocol.gni
@@ -144,8 +144,8 @@ template("inspector_protocol_concatenate") {
inputs += invoker.inputs
args += rebase_path(invoker.inputs, root_build_dir)
- outputs += [invoker.output_file]
- args += [rebase_path(invoker.output_file, root_build_dir)]
+ outputs += [ invoker.output_file ]
+ args += [ rebase_path(invoker.output_file, root_build_dir) ]
forward_variables_from(invoker,
[
diff --git a/deps/inspector_protocol/lib/Values_cpp.template b/deps/inspector_protocol/lib/Values_cpp.template
index abeee79e5c3..2d34fb3de4f 100644
--- a/deps/inspector_protocol/lib/Values_cpp.template
+++ b/deps/inspector_protocol/lib/Values_cpp.template
@@ -461,7 +461,7 @@ double DictionaryValue::doubleProperty(const String& name, double defaultValue)
void DictionaryValue::remove(const String& name)
{
m_data.erase(name);
- m_order.erase(std::remove(m_order.begin(), m_order.end(), name), m_order.end());
+ std::erase(m_order, name);
}
void DictionaryValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
diff --git a/deps/inspector_protocol/pdl.py b/deps/inspector_protocol/pdl.py
index 5b53291fd5b..eb8f2474531 100644
--- a/deps/inspector_protocol/pdl.py
+++ b/deps/inspector_protocol/pdl.py
@@ -89,15 +89,16 @@ def parse(data, file_name, map_binary_to_string, source_set):
protocol['domains'].append(domain)
continue
- match = re.compile(
- r'^include (.*)').match(line)
+ match = re.compile(r'^include (.*)').match(line)
if match:
included_filename = match.group(1)
if os.path.isabs(included_filename):
raise Exception("Only relative paths are supported in includes")
- resolved_path = os.path.normpath(os.path.join(os.path.dirname(file_name), included_filename))
+ resolved_path = os.path.normpath(
+ os.path.join(os.path.dirname(file_name), included_filename))
with open(resolved_path, 'r') as file:
- included_data = parse(file.read(), resolved_path, map_binary_to_string, source_set)
+ included_data = parse(file.read(), resolved_path, map_binary_to_string,
+ source_set)
protocol['domains'].extend(included_data['domains'])
continue
diff --git a/deps/inspector_protocol/templates/TypeBuilder_cpp.template b/deps/inspector_protocol/templates/TypeBuilder_cpp.template
index a38182c311a..2f70a9d1299 100644
--- a/deps/inspector_protocol/templates/TypeBuilder_cpp.template
+++ b/deps/inspector_protocol/templates/TypeBuilder_cpp.template
@@ -18,6 +18,7 @@ namespace {{namespace}} {
namespace {{domain.domain}} {
using {{config.crdtp.namespace}}::DeserializerState;
+using {{config.crdtp.namespace}}::FallthroughCallback;
using {{config.crdtp.namespace}}::ProtocolTypeTraits;
// ------------- Enum values from types.
@@ -155,14 +156,14 @@ public:
, m_backend(backend) {}
~DomainDispatcherImpl() override { }
- using CallHandler = void (DomainDispatcherImpl::*)(const {{config.crdtp.namespace}}::Dispatchable& dispatchable);
+ using CallHandler = void (DomainDispatcherImpl::*)({{config.crdtp.namespace}}::Dispatchable& dispatchable);
- std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) override;
+ bool Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name, {{config.crdtp.namespace}}::Dispatchable& dispatchable) 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}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable);
+ void {{command.name}}({{config.crdtp.namespace}}::Dispatchable& dispatchable);
{% endfor %}
protected:
Backend* m_backend;
@@ -191,13 +192,12 @@ DomainDispatcherImpl::CallHandler CommandByName({{config.crdtp.namespace}}::span
}
} // namespace
-std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> DomainDispatcherImpl::Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) {
+bool DomainDispatcherImpl::Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name, {{config.crdtp.namespace}}::Dispatchable& dispatchable) {
CallHandler handler = CommandByName(command_name);
- if (!handler) return nullptr;
+ if (!handler) return false;
- return [this, handler](const {{config.crdtp.namespace}}::Dispatchable& dispatchable) {
- (this->*handler)(dispatchable);
- };
+ (this->*handler)(dispatchable);
+ return true;
}
{% for command in domain.commands %}
@@ -208,9 +208,8 @@ std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> DomainDispa
class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DomainDispatcher::Callback {
public:
- {{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) { }
+ {{command_name_title}}CallbackImpl(std::unique_ptr<DomainDispatcher::WeakPtr> backendImpl, {{config.crdtp.namespace}}::Dispatchable& dispatchable)
+ : DomainDispatcher::Callback(std::move(backendImpl), dispatchable) { }
void sendSuccess(
{%- for parameter in command.returns -%}
@@ -270,7 +269,7 @@ CRDTP_END_DESERIALIZER()
} // namespace
-void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable)
+void DomainDispatcherImpl::{{command.name}}({{config.crdtp.namespace}}::Dispatchable& dispatchable)
{
// Prepare input parameters.
{% if "parameters" in command %}
@@ -304,6 +303,13 @@ void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Di
{{protocol.resolve_type(parameter).to_pass_type % ("params." + parameter.name)}}
{%- endif -%}
{%- endfor %}
+
+ {%- if protocol.should_pass_associated_data(domain.domain, command.name) -%}
+ {%- if command.parameters -%}, {% endif -%}
+ {{config.crdtp.namespace}}::ConvertAssociatedData<std::remove_cvref_t<{{protocol.type_definition(domain.domain + ".string").pass_type}}>>(
+ dispatchable.AssociatedData())
+ {%- endif -%}
+
{%- if "returns" in command %}
{%- for parameter in command.returns -%}
{%- if not loop.first or command.parameters -%}, {% endif -%}
@@ -311,7 +317,7 @@ void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Di
{%- endfor %}
{% endif %});
if (response.IsFallThrough()) {
- channel()->FallThrough(dispatchable.CallId(), {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), dispatchable.Serialized());
+ dispatchable.DispatchFallThrough(response.Message());
return;
}
{% if "returns" in command %}
@@ -344,7 +350,7 @@ void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Di
{%- endif -%}
{%- endfor -%}
{%- if command.parameters -%}, {% endif -%}
- std::make_unique<{{command_name_title}}CallbackImpl>(weakPtr(), dispatchable.CallId(), dispatchable.Serialized()));
+ std::make_unique<{{command_name_title}}CallbackImpl>(weakPtr(), dispatchable));
{% endif %}
}
{% endfor %}
diff --git a/deps/inspector_protocol/templates/TypeBuilder_h.template b/deps/inspector_protocol/templates/TypeBuilder_h.template
index 33c5d1af535..38b1fcf6da2 100644
--- a/deps/inspector_protocol/templates/TypeBuilder_h.template
+++ b/deps/inspector_protocol/templates/TypeBuilder_h.template
@@ -256,8 +256,12 @@ public:
{{protocol.resolve_type(parameter).pass_type}} in_{{parameter.name}}
{%- endif -%}
{%- endfor -%}
- {%- if protocol.is_async_command(domain.domain, command.name) -%}
+ {%- if protocol.should_pass_associated_data(domain.domain, command.name) -%}
{%- if command.parameters -%}, {% endif -%}
+ {{protocol.type_definition(domain.domain + ".string").pass_type}} associated_data
+ {%- endif -%}
+ {%- if protocol.is_async_command(domain.domain, command.name) -%}
+ {%- if command.parameters or protocol.should_pass_associated_data(domain.domain, command.name) -%}, {% endif -%}
std::unique_ptr<{{command.name | to_title_case}}Callback> callback
{%- else -%}
{%- for parameter in command.returns -%}