Implement mojom dependency import and add tests
The converter will now import mojom-webui dependencies.
This CL introduces tests that checks that:
1. Depending on a typemapped mojom would not require additional wiring
from the dependant (conversion should be opaque).
2. Typemapper and data view behave correctly when several transformation
operations are needed.
Sample output:
converter => gpaste/6222141612883968
Bug: 40615900
Change-Id: I630ee93c023f9edb623c531ae10d0cb8ba01643a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5832814
Commit-Queue: Fred Shih <ffred@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: Charlie Reis <creis@chromium.org>
Reviewed-by: Ken Rockot <rockot@google.com>
Cr-Commit-Position: refs/heads/main@{#1352413}
diff --git a/content/browser/webui/web_ui_mojo_browsertest.cc b/content/browser/webui/web_ui_mojo_browsertest.cc
index a521469..f4d9081 100644
--- a/content/browser/webui/web_ui_mojo_browsertest.cc
+++ b/content/browser/webui/web_ui_mojo_browsertest.cc
@@ -129,6 +129,7 @@
const base::flat_map<int32_t, std::optional<mojom::TestEnum>>& enum_map,
mojom::SimpleMappedTypePtr simple_mapped,
mojom::NestedMappedTypePtr nested_mapped,
+ mojom::StringDictPtr dict_ptr,
EchoCallback callback) override {
std::move(callback).Run(
optional_bool.has_value() ? std::make_optional(!optional_bool.value())
@@ -148,7 +149,8 @@
? std::make_optional(mojom::TestEnum::kTwo)
: std::nullopt),
optional_bools, optional_ints, optional_enums, bool_map, int_map,
- enum_map, simple_mapped->Clone(), nested_mapped->Clone());
+ enum_map, simple_mapped->Clone(), nested_mapped->Clone(),
+ dict_ptr ? dict_ptr->Clone() : nullptr);
}
private:
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 2b0d2a3..634cdd8 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1163,6 +1163,27 @@
}
}
+# Test cross target mojo typemapping.
+mojom("web_ui_ts_test_other_mojo_bindings") {
+ testonly = true
+ sources = [ "data/web_ui_ts_test_other_types.test-mojom" ]
+ webui_module_path = "/content/test/data"
+
+ ts_typemaps = [
+ {
+ types = [
+ {
+ mojom = "content.mojom.MappedDict"
+ ts = "MappedDictType"
+ ts_import = "./web_ui_mojo_ts_test_other_mapped_types.js"
+ converter = "MappedDictConverter"
+ import = "web_ui_mojo_ts_test_converters.js"
+ },
+ ]
+ },
+ ]
+}
+
mojom("web_ui_ts_test_mojo_bindings") {
testonly = true
sources = [
@@ -1172,6 +1193,8 @@
public_deps = [ "//url/mojom:url_mojom_gurl" ]
webui_module_path = "/content/test/data"
+ deps = [ ":web_ui_ts_test_other_mojo_bindings" ]
+
ts_typemaps = [
{
types = [
@@ -1184,10 +1207,17 @@
{
mojom = "content.mojom.NestedMappedType"
ts = "TestNode"
- ts_import = "./web_ui_mojo_ts_test_node.js"
+ ts_import = "./web_ui_mojo_ts_test_mapped_types.js"
converter = "NestedTypeConverter"
import = "web_ui_mojo_ts_test_converters.js"
},
+ {
+ mojom = "content.mojom.StringDict"
+ ts = "StringDictType"
+ ts_import = "./web_ui_mojo_ts_test_mapped_types.js"
+ converter = "StringDictConverter"
+ import = "web_ui_mojo_ts_test_converters.js"
+ },
]
},
]
@@ -1198,8 +1228,9 @@
out_folder = "$target_gen_dir/data"
in_files = [
"web_ui_mojo_ts_test.ts",
- "web_ui_mojo_ts_test_node.ts",
"web_ui_mojo_ts_test_converters.ts",
+ "web_ui_mojo_ts_test_mapped_types.ts",
+ "web_ui_mojo_ts_test_other_mapped_types.ts",
"web_ui_managed_interface_test.ts",
]
}
@@ -1209,10 +1240,13 @@
out_dir = "$target_gen_dir/data/tsc"
in_files = [
"web_ui_mojo_ts_test.ts",
- "web_ui_mojo_ts_test_node.ts",
"web_ui_mojo_ts_test_converters.ts",
+ "web_ui_mojo_ts_test_mapped_types.ts",
+ "web_ui_mojo_ts_test_other_mapped_types.ts",
"web_ui_ts_test.test-mojom-converters.ts",
"web_ui_ts_test.test-mojom-webui.ts",
+ "web_ui_ts_test_other_types.test-mojom-converters.ts",
+ "web_ui_ts_test_other_types.test-mojom-webui.ts",
"web_ui_ts_test_types.test-mojom-webui.ts",
"web_ui_managed_interface_test.ts",
"web_ui_managed_interface_test.test-mojom-webui.ts",
@@ -1222,6 +1256,7 @@
":preprocess_mojo_webui_test",
":web_ui_managed_interface_tests_bindings_ts__generator",
":web_ui_ts_test_mojo_bindings_ts__generator",
+ ":web_ui_ts_test_other_mojo_bindings_ts__generator",
]
}
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 08ff279..890106d 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -7956,11 +7956,13 @@
data/web_ui_mojo_ts_test.html
data/web_ui_mojo_ts_test.ts
data/web_ui_mojo_ts_test_converters.ts
-data/web_ui_mojo_ts_test_node.ts
+data/web_ui_mojo_ts_test_mapped_types.ts
+data/web_ui_mojo_ts_test_other_mapped_types.ts
data/web_ui_shared_worker.js
data/web_ui_test.test-mojom
data/web_ui_test_types.test-mojom
data/web_ui_ts_test.test-mojom
+data/web_ui_ts_test_other_types.test-mojom
data/web_ui_ts_test_types.test-mojom
data/webkit/async_script_abort_on_end.html
data/webkit/resources/xslt-bad-import-uri.xml
diff --git a/content/test/data/web_ui_mojo_ts_test.ts b/content/test/data/web_ui_mojo_ts_test.ts
index 1e78647..1203b95 100644
--- a/content/test/data/web_ui_mojo_ts_test.ts
+++ b/content/test/data/web_ui_mojo_ts_test.ts
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {TestNode} from './web_ui_mojo_ts_test_node.js';
+import {StringDictType, TestNode} from './web_ui_mojo_ts_test_mapped_types.js';
import {OptionalNumericsStruct, TestEnum, WebUITsMojoTestCache} from './web_ui_ts_test.test-mojom-webui.js';
const TEST_DATA: Array<{url: string, contents: string}> = [
@@ -77,7 +77,7 @@
} =
await cache.echo(
true, null, TestEnum.kOne, testStruct, [], [], [], {}, {}, {}, '',
- new TestNode());
+ new TestNode(), null);
if (optionalBool !== false) {
return false;
}
@@ -131,8 +131,8 @@
} =
await cache.echo(
null, 1, null, testStruct, inOptionalBools, inOptionalInts,
- inOptionalEnums, inBoolMap, inIntMap, inEnumMap, '',
- new TestNode());
+ inOptionalEnums, inBoolMap, inIntMap, inEnumMap, '', new TestNode(),
+ null);
if (optionalBool !== null) {
return false;
}
@@ -170,7 +170,8 @@
{
const str = 'foobear';
const result = await cache.echo(
- null, 1, null, testStruct, [], [], [], {}, {}, {}, str, new TestNode());
+ null, 1, null, testStruct, [], [], [], {}, {}, {}, str, new TestNode(),
+ null);
if (result.simpleMappedType !== str) {
return false;
@@ -180,7 +181,8 @@
// Tests an empty nested struct to test basic encoding/decoding.
{
const result = await cache.echo(
- null, 1, null, testStruct, [], [], [], {}, {}, {}, '', new TestNode());
+ null, 1, null, testStruct, [], [], [], {}, {}, {}, '', new TestNode(),
+ null);
assertObjectEquals(
new TestNode(), result.nestedMappedType,
@@ -197,7 +199,7 @@
cursor = cursor!.next = new TestNode();
}
const result = await cache.echo(
- null, 1, null, testStruct, [], [], [], {}, {}, {}, '', chain);
+ null, 1, null, testStruct, [], [], [], {}, {}, {}, '', chain, null);
if (JSON.stringify(chain) !== JSON.stringify(result.nestedMappedType)) {
throw new Error(
@@ -207,6 +209,22 @@
}
}
+ {
+ let map: StringDictType = new Map<string, string>();
+ map.set('foo', 'bear');
+ map.set('some', 'where');
+ const result = await cache.echo(
+ null, 1, null, testStruct, [], [], [], {}, {}, {}, '', new TestNode(),
+ map);
+
+ for (const key of map.keys()) {
+ assert(
+ result.otherMappedType!.get(key) === map.get(key),
+ `Expected value: ${map.get(key)} for key: ${key}, got: ${
+ result.otherMappedType!.get(key)}`);
+ }
+ }
+
return true;
}
diff --git a/content/test/data/web_ui_mojo_ts_test_converters.ts b/content/test/data/web_ui_mojo_ts_test_converters.ts
index 87391f0..18a04a8 100644
--- a/content/test/data/web_ui_mojo_ts_test_converters.ts
+++ b/content/test/data/web_ui_mojo_ts_test_converters.ts
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {TestNode} from './web_ui_mojo_ts_test_node.js';
-import {NestedMappedTypeDataView, NestedMappedTypeTypeMapper, SimpleMappedTypeDataView, SimpleMappedTypeTypeMapper,} from './web_ui_ts_test.test-mojom-converters.js';
+import {StringDictType, TestNode} from './web_ui_mojo_ts_test_mapped_types.js';
+import {MappedDictType} from './web_ui_mojo_ts_test_other_mapped_types.js';
+import {NestedMappedTypeDataView, NestedMappedTypeTypeMapper, SimpleMappedTypeDataView, SimpleMappedTypeTypeMapper, StringDictDataView, StringDictTypeMapper} from './web_ui_ts_test.test-mojom-converters.js';
+import {MappedDictDataView, MappedDictTypeMapper} from './web_ui_ts_test_other_types.test-mojom-converters.js';
export class SimpleTypeConverter implements SimpleMappedTypeTypeMapper<string> {
value(mappedType: string): string {
@@ -25,3 +27,37 @@
return new TestNode(dataView.nested());
}
}
+
+export class MappedDictConverter implements
+ MappedDictTypeMapper<MappedDictType> {
+ data(dict: MappedDictType): {[key: string]: string} {
+ const converted: {[key: string]: string} = {};
+ for (let k of dict.keys()) {
+ converted[k] = dict.get(k) || '';
+ }
+ return converted;
+ }
+
+ convert(view: MappedDictDataView): MappedDictType {
+ const converted = new Map<string, string>();
+ for (let k in view.data()) {
+ converted.set(k, view.data()[k]!);
+ }
+ return converted;
+ }
+}
+
+// This should be trivial to implement because StringDict is synonymous
+// with mapped type. If typemapping is working correctly, we should be
+// able to switch back and forth between MappedDictType and StringDictType
+// freely.
+export class StringDictConverter implements
+ StringDictTypeMapper<StringDictType> {
+ data(dict: StringDictType): MappedDictType {
+ return dict;
+ }
+
+ convert(view: StringDictDataView): StringDictType {
+ return view.data();
+ }
+}
diff --git a/content/test/data/web_ui_mojo_ts_test_node.ts b/content/test/data/web_ui_mojo_ts_test_mapped_types.ts
similarity index 66%
rename from content/test/data/web_ui_mojo_ts_test_node.ts
rename to content/test/data/web_ui_mojo_ts_test_mapped_types.ts
index 0d672ad..2206c7d 100644
--- a/content/test/data/web_ui_mojo_ts_test_node.ts
+++ b/content/test/data/web_ui_mojo_ts_test_mapped_types.ts
@@ -1,6 +1,7 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import {MappedDictType} from './web_ui_mojo_ts_test_other_mapped_types.js';
// This is effectively the same structure as NestedMappedType mojom,
// except it is implemented purely in ts and has a different field name
@@ -15,3 +16,8 @@
this.next = next || null;
}
}
+
+// Test using an interface as the type declaration then returning a
+// concrete type in the converter. This allows impls to be hidden
+// away from users.
+export interface StringDictType extends MappedDictType {}
diff --git a/content/test/data/web_ui_mojo_ts_test_other_mapped_types.ts b/content/test/data/web_ui_mojo_ts_test_other_mapped_types.ts
new file mode 100644
index 0000000..0a40fd8
--- /dev/null
+++ b/content/test/data/web_ui_mojo_ts_test_other_mapped_types.ts
@@ -0,0 +1,9 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export interface MappedDictType {
+ get(key: string): string|undefined;
+ set(key: string, value: string): void;
+ keys(): Iterable<string>;
+}
diff --git a/content/test/data/web_ui_ts_test.test-mojom b/content/test/data/web_ui_ts_test.test-mojom
index dc30a28..de5ef0a 100644
--- a/content/test/data/web_ui_ts_test.test-mojom
+++ b/content/test/data/web_ui_ts_test.test-mojom
@@ -5,6 +5,7 @@
module content.mojom;
import "content/test/data/web_ui_ts_test_types.test-mojom";
+import "content/test/data/web_ui_ts_test_other_types.test-mojom";
import "url/mojom/url.mojom";
enum TestEnum {
@@ -26,6 +27,10 @@
NestedMappedType? nested;
};
+struct StringDict {
+ MappedDict data;
+};
+
interface WebUITsMojoTestCache {
Put(url.mojom.Url url, string contents);
GetAll() => (array<TsCacheItem> items);
@@ -42,7 +47,8 @@
map<int32, int32?> int_map,
map<int32, TestEnum?> enum_map,
SimpleMappedType simple_mapped_type,
- NestedMappedType nested_mapped_type)
+ NestedMappedType nested_mapped_type,
+ StringDict? other_mapped_type)
=> (
bool? optional_bool,
uint8? optional_uint8,
@@ -55,5 +61,6 @@
map<int32, int32?> int_map,
map<int32, TestEnum?> enum_map,
SimpleMappedType simple_mapped_type,
- NestedMappedType nested_mapped_type);
+ NestedMappedType nested_mapped_type,
+ StringDict? other_mapped_type);
};
diff --git a/content/test/data/web_ui_ts_test_other_types.test-mojom b/content/test/data/web_ui_ts_test_other_types.test-mojom
new file mode 100644
index 0000000..d43870e
--- /dev/null
+++ b/content/test/data/web_ui_ts_test_other_types.test-mojom
@@ -0,0 +1,9 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+struct MappedDict {
+ map<string, string> data;
+};
diff --git a/content/test/web_ui_mojo_test_resources.grd b/content/test/web_ui_mojo_test_resources.grd
index 2940285..7962396 100644
--- a/content/test/web_ui_mojo_test_resources.grd
+++ b/content/test/web_ui_mojo_test_resources.grd
@@ -31,8 +31,11 @@
</if>
<include name="IDR_WEB_UI_TS_TEST_MOJOM_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test.test-mojom-webui.js" />
<include name="IDR_WEB_UI_TS_TEST_MOJOM_CONVERTER_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test.test-mojom-converters.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test.test-mojom-converters.js" />
+ <include name="IDR_WEB_UI_TS_TEST_MOJOM_OTHER_TYPES_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test_other_types.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test_other_types.test-mojom-webui.js" />
+ <include name="IDR_WEB_UI_TS_TEST_MOJOM_OTHER_TYPES_CONVERTER_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test_other_types.test-mojom-converters.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test_other_types.test-mojom-converters.js" />
+ <include name="IDR_WEB_UI_TS_TEST_MAPPED_TYPES_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_mojo_ts_test_mapped_types.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_mojo_ts_test_mapped_types.js" />
+ <include name="IDR_WEB_UI_TS_TEST_OTHER_MAPPED_TYPES_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_mojo_ts_test_other_mapped_types.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_mojo_ts_test_other_mapped_types.js" />
<include name="IDR_WEB_UI_TS_TEST_CONVERTERS_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_mojo_ts_test_converters.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_mojo_ts_test_converters.js" />
- <include name="IDR_WEB_UI_TS_TEST_NODE_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_mojo_ts_test_node.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_mojo_ts_test_node.js" />
<include name="IDR_WEB_UI_TS_TEST_TYPES_MOJOM_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test_types.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test_types.test-mojom-webui.js" />
</includes>
</release>
diff --git a/mojo/public/tools/bindings/generators/mojom_ts_generator.py b/mojo/public/tools/bindings/generators/mojom_ts_generator.py
index dacb05c..39c6629 100644
--- a/mojo/public/tools/bindings/generators/mojom_ts_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_ts_generator.py
@@ -708,20 +708,55 @@
return imports
+ # Returns a list of imports in the format:
+ # {
+ # <import path>: [list of types],
+ # ...
+ # }
def _ConverterImports(self):
- imports = {}
- # TODO(ffred): we also need to import types from other *-mojom-webui
- # dependencies.
- typemapped_structs = self._TypeMappedStructs()
- for struct in typemapped_structs:
+ def needs_import(kind):
+ return mojom.IsStructKind(kind) or mojom.IsUnionKind(kind)
+
+ class Import:
+
+ def __init__(self, typename, path, alias=None):
+ self.typename = typename
+ self.path = path
+ self.alias = alias
+
+ def import_name(self):
+ if self.alias:
+ return f"{self.typename} as {self.alias}"
+ return self.typename
+
+ # A dictionary keyed by the qualified type name to an import configuration.
+ qualified_type_to_import = {}
+
+ # Build up our import repository.
+ # Add the mojom module imports first.
+ for import_path, kinds in self._GetJsModuleImports().items():
+ for kind in kinds:
+ if needs_import(kind):
+ qualified_type_to_import[kind.qualified_name] = Import(
+ kind.name, import_path, self._GetNameInJsModule(kind))
+
+ # Then add the typemap imports.
+ for qualified_name, typemap in self.typemap.items():
+ qualified_type_to_import[qualified_name] = Import(typemap['typename'],
+ typemap['type_import'])
+
+ # Now we create the list of imports, based on the struct deps.
+ imports = {}
+ for struct in self._TypeMappedStructs():
for field in struct.fields:
- if mojom.IsStructKind(field.kind):
+ if needs_import(field.kind):
qualified = field.kind.qualified_name
- if qualified in self.typemap:
- typemap = self.typemap[qualified]
- typemap_import = typemap['type_import']
- if typemap_import:
- imports.setdefault(typemap_import, []).append(typemap['typename'])
+ type_import = qualified_type_to_import[qualified]
+ # We should have an entry for all non-primitive types
+ assert type_import != None
+
+ imports.setdefault(type_import.path,
+ []).append(type_import.import_name())
return imports