0

Mojo: Add WebUI JS module support

This adds a new _webui_js sub-target for any mojom target that
explicitly sets webui_module_path in GN.

This allows mojom JS modules to work well with WebUI resource URLs,
which are typically either a chrome://resources/ URL for shared
resources, or a relative path-only URL for resources exclusive to a
single WebUI page.

Bug: 1004256
Change-Id: I5b8be3f69733239f81b313f882d3185183b72a99
Tbr: dcheng@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2459393
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: dpapad <dpapad@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Bo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#817225}
This commit is contained in:
Ken Rockot
2020-10-14 22:19:44 +00:00
committed by Commit Bot
parent 38ed04dd43
commit cb1d5c5fc1
25 changed files with 488 additions and 282 deletions

@ -934,6 +934,7 @@ grit("generate_mojo_resources") {
]
deps = [
"//mojo/public/js:bindings_lite",
"//mojo/public/js:bindings_module",
"//mojo/public/mojom/base:base_js",
]
}

@ -93,44 +93,12 @@ const std::map<int, std::string> CreateContentResourceIdToAliasMap() {
"mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js"},
{IDR_URL_MOJO_HTML, "mojo/url/mojom/url.mojom.html"},
{IDR_URL_MOJO_JS, "mojo/url/mojom/url.mojom-lite.js"},
{IDR_URL_MOJOM_WEBUI_JS, "mojo/url/mojom/url.mojom-webui.js"},
{IDR_VULKAN_INFO_MOJO_JS, "gpu/ipc/common/vulkan_info.mojom-lite.js"},
{IDR_VULKAN_TYPES_MOJO_JS, "gpu/ipc/common/vulkan_types.mojom-lite.js"},
};
}
const std::map<int, std::string> CreateMojoResourceIdToAliasMap() {
return std::map<int, std::string> {
{IDR_MOJO_MOJO_BINDINGS_LITE_HTML,
"mojo/mojo/public/js/mojo_bindings_lite.html"},
{IDR_MOJO_MOJO_BINDINGS_LITE_JS,
"mojo/mojo/public/js/mojo_bindings_lite.js"},
{IDR_MOJO_BIG_BUFFER_MOJOM_HTML,
"mojo/mojo/public/mojom/base/big_buffer.mojom.html"},
{IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS,
"mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js"},
{IDR_MOJO_FILE_MOJOM_HTML,
"mojo/mojo/public/mojom/base/file.mojom.html"},
{IDR_MOJO_FILE_MOJOM_LITE_JS,
"mojo/mojo/public/mojom/base/file.mojom-lite.js"},
{IDR_MOJO_STRING16_MOJOM_HTML,
"mojo/mojo/public/mojom/base/string16.mojom.html"},
{IDR_MOJO_STRING16_MOJOM_LITE_JS,
"mojo/mojo/public/mojom/base/string16.mojom-lite.js"},
{IDR_MOJO_TEXT_DIRECTION_MOJOM_HTML,
"mojo/mojo/public/mojom/base/text_direction.mojom.html"},
{IDR_MOJO_TEXT_DIRECTION_MOJOM_LITE_JS,
"mojo/mojo/public/mojom/base/text_direction.mojom-lite.js"},
#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) || defined(OS_ANDROID)
{IDR_MOJO_TIME_MOJOM_HTML,
"mojo/mojo/public/mojom/base/time.mojom.html"},
{IDR_MOJO_TIME_MOJOM_LITE_JS,
"mojo/mojo/public/mojom/base/time.mojom-lite.js"},
#endif // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) ||
// defined(OS_CHROMEOS) || defined(OS_ANDROID)
};
}
const std::map<int, std::string> CreateSkiaResourceIdToAliasMap() {
return std::map<int, std::string>{
{IDR_SKIA_BITMAP_MOJOM_LITE_JS,
@ -248,6 +216,14 @@ void AddAliasedResourcesToMap(
}
}
// Adds |resources| to |resources_map| using the path given by resource_path in
// each GRD entry.
void AddGritResourcesToMap(base::span<const GritResourceMap> resources,
ResourcesMap* resources_map) {
for (const GritResourceMap& entry : resources)
AddResource(entry.name, entry.value, resources_map);
}
const ResourcesMap* CreateResourcesMap() {
ResourcesMap* result = new ResourcesMap();
AddResourcesToMap(result);
@ -256,9 +232,9 @@ const ResourcesMap* CreateResourcesMap() {
AddAliasedResourcesToMap(CreateContentResourceIdToAliasMap(),
kMediaInternalsResources,
kMediaInternalsResourcesSize, result);
AddAliasedResourcesToMap(CreateMojoResourceIdToAliasMap(),
kMojoBindingsResources, kMojoBindingsResourcesSize,
result);
AddGritResourcesToMap(
base::make_span(kMojoBindingsResources, kMojoBindingsResourcesSize),
result);
AddAliasedResourcesToMap(CreateSkiaResourceIdToAliasMap(), kSkiaResources,
kSkiaResourcesSize, result);
#if defined(OS_CHROMEOS)

@ -8,11 +8,9 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
@ -29,8 +27,6 @@
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/referrer.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
@ -41,7 +37,10 @@
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/data/web_ui_test_mojo_bindings.mojom.h"
#include "content/test/data/web_ui_test.test-mojom.h"
#include "content/test/data/web_ui_test_types.test-mojom.h"
#include "content/test/grit/web_ui_mojo_test_resources.h"
#include "content/test/grit/web_ui_mojo_test_resources_map.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
@ -49,87 +48,58 @@
namespace content {
namespace {
bool g_got_message = false;
const char kMojoWebUiHost[] = "mojo-web-ui";
const char kDummyWebUiHost[] = "dummy-web-ui";
base::FilePath GetFilePathForJSResource(const std::string& path) {
base::ScopedAllowBlockingForTesting allow_blocking;
std::string binding_path = "gen/" + path;
#if defined(OS_WIN)
base::ReplaceChars(binding_path, "//", "\\", &binding_path);
#endif
base::FilePath exe_dir;
base::PathService::Get(base::DIR_EXE, &exe_dir);
return exe_dir.AppendASCII(binding_path);
}
// The bindings for the page are generated from a .mojom file. This code looks
// up the generated file from disk and returns it.
void GetResource(const std::string& id,
WebUIDataSource::GotDataCallback callback) {
base::ScopedAllowBlockingForTesting allow_blocking;
std::string contents;
if (base::EndsWith(id, ".mojom-lite.js", base::CompareCase::SENSITIVE)) {
CHECK(base::ReadFileToString(GetFilePathForJSResource(id), &contents))
<< id;
} else {
base::FilePath path;
CHECK(base::PathService::Get(content::DIR_TEST_DATA, &path));
path = path.AppendASCII(id.substr(0, id.find("?")));
CHECK(base::ReadFileToString(path, &contents)) << path.value();
}
base::RefCountedString* ref_contents = new base::RefCountedString;
ref_contents->data() = contents;
std::move(callback).Run(ref_contents);
}
class BrowserTargetImpl : public mojom::BrowserTarget {
class WebUIMojoTestCacheImpl : public mojom::WebUIMojoTestCache {
public:
BrowserTargetImpl(base::RunLoop* run_loop,
mojo::PendingReceiver<mojom::BrowserTarget> receiver)
: run_loop_(run_loop), receiver_(this, std::move(receiver)) {}
explicit WebUIMojoTestCacheImpl(
mojo::PendingReceiver<mojom::WebUIMojoTestCache> receiver)
: receiver_(this, std::move(receiver)) {}
~BrowserTargetImpl() override {}
~WebUIMojoTestCacheImpl() override = default;
// mojom::BrowserTarget overrides:
void Start(StartCallback closure) override { std::move(closure).Run(); }
void Stop() override {
g_got_message = true;
run_loop_->Quit();
// mojom::WebUIMojoTestCache overrides:
void Put(const GURL& url, const std::string& contents) override {
cache_[url] = contents;
}
protected:
base::RunLoop* const run_loop_;
void GetAll(GetAllCallback callback) override {
std::vector<mojom::CacheItemPtr> items;
for (const auto& entry : cache_)
items.push_back(mojom::CacheItem::New(entry.first, entry.second));
std::move(callback).Run(std::move(items));
}
private:
mojo::Receiver<mojom::BrowserTarget> receiver_;
DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl);
mojo::Receiver<mojom::WebUIMojoTestCache> receiver_;
std::map<GURL, std::string> cache_;
};
// WebUIController that sets up mojo bindings.
class TestWebUIController : public WebUIController {
public:
TestWebUIController(WebUI* web_ui,
base::RunLoop* run_loop,
int bindings = BINDINGS_POLICY_MOJO_WEB_UI)
: WebUIController(web_ui), run_loop_(run_loop) {
explicit TestWebUIController(WebUI* web_ui,
int bindings = BINDINGS_POLICY_MOJO_WEB_UI)
: WebUIController(web_ui) {
const base::span<const GritResourceMap> kMojoWebUiResources =
base::make_span(kWebUiMojoTestResources, kWebUiMojoTestResourcesSize);
web_ui->SetBindings(bindings);
{
WebUIDataSource* data_source = WebUIDataSource::Create("mojo-web-ui");
WebUIDataSource* data_source = WebUIDataSource::Create(kMojoWebUiHost);
data_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ScriptSrc,
"script-src chrome://resources 'self' 'unsafe-eval';");
data_source->DisableTrustedTypesCSP();
data_source->SetRequestFilter(
base::BindRepeating([](const std::string& path) { return true; }),
base::BindRepeating(&GetResource));
for (const GritResourceMap& resource : kMojoWebUiResources)
data_source->AddResourcePath(resource.name, resource.value);
data_source->AddResourcePath("", IDR_WEB_UI_MOJO_HTML);
WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
data_source);
}
{
WebUIDataSource* data_source = WebUIDataSource::Create("dummy-web-ui");
WebUIDataSource* data_source = WebUIDataSource::Create(kDummyWebUiHost);
data_source->SetRequestFilter(
base::BindRepeating([](const std::string& path) { return true; }),
base::BindRepeating([](const std::string& id,
@ -142,40 +112,34 @@ class TestWebUIController : public WebUIController {
}
protected:
base::RunLoop* const run_loop_;
std::unique_ptr<BrowserTargetImpl> browser_target_;
std::unique_ptr<WebUIMojoTestCacheImpl> cache_;
private:
DISALLOW_COPY_AND_ASSIGN(TestWebUIController);
};
// TestWebUIController that additionally creates the ping test BrowserTarget
// implementation at the right time.
class PingTestWebUIController : public TestWebUIController {
// TestWebUIController that can bind a WebUIMojoTestCache interface when
// requested by the page.
class CacheTestWebUIController : public TestWebUIController {
public:
PingTestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
: TestWebUIController(web_ui, run_loop) {}
explicit CacheTestWebUIController(WebUI* web_ui)
: TestWebUIController(web_ui) {}
~CacheTestWebUIController() override = default;
~PingTestWebUIController() override {}
void CreateHandler(mojo::PendingReceiver<mojom::BrowserTarget> receiver) {
browser_target_ =
std::make_unique<BrowserTargetImpl>(run_loop_, std::move(receiver));
void CreateHandler(
mojo::PendingReceiver<mojom::WebUIMojoTestCache> receiver) {
cache_ = std::make_unique<WebUIMojoTestCacheImpl>(std::move(receiver));
}
private:
DISALLOW_COPY_AND_ASSIGN(PingTestWebUIController);
};
// WebUIControllerFactory that creates TestWebUIController.
class TestWebUIControllerFactory : public WebUIControllerFactory {
public:
TestWebUIControllerFactory()
: run_loop_(nullptr),
registered_controllers_(
{{"ping", base::BindRepeating(
&TestWebUIControllerFactory::CreatePingController,
base::Unretained(this))},
: registered_controllers_(
{{"cache", base::BindRepeating(
&TestWebUIControllerFactory::CreateCacheController,
base::Unretained(this))},
{"hybrid", base::BindRepeating(
&TestWebUIControllerFactory::CreateHybridController,
base::Unretained(this))},
@ -184,8 +148,6 @@ class TestWebUIControllerFactory : public WebUIControllerFactory {
&TestWebUIControllerFactory::CreateWebUIController,
base::Unretained(this))}}) {}
void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
std::unique_ptr<WebUIController> CreateWebUIControllerForURL(
WebUI* web_ui,
const GURL& url) override {
@ -196,7 +158,7 @@ class TestWebUIControllerFactory : public WebUIControllerFactory {
if (it != registered_controllers_.end())
return it->second.Run(web_ui);
return std::make_unique<TestWebUIController>(web_ui, run_loop_);
return std::make_unique<TestWebUIController>(web_ui);
}
WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
@ -219,22 +181,20 @@ class TestWebUIControllerFactory : public WebUIControllerFactory {
void set_web_ui_enabled(bool enabled) { web_ui_enabled_ = enabled; }
private:
std::unique_ptr<WebUIController> CreatePingController(WebUI* web_ui) {
return std::make_unique<PingTestWebUIController>(web_ui, run_loop_);
std::unique_ptr<WebUIController> CreateCacheController(WebUI* web_ui) {
return std::make_unique<CacheTestWebUIController>(web_ui);
}
std::unique_ptr<WebUIController> CreateHybridController(WebUI* web_ui) {
return std::make_unique<TestWebUIController>(
web_ui, run_loop_,
BINDINGS_POLICY_WEB_UI | BINDINGS_POLICY_MOJO_WEB_UI);
web_ui, BINDINGS_POLICY_WEB_UI | BINDINGS_POLICY_MOJO_WEB_UI);
}
std::unique_ptr<WebUIController> CreateWebUIController(WebUI* web_ui) {
return std::make_unique<TestWebUIController>(web_ui, run_loop_,
return std::make_unique<TestWebUIController>(web_ui,
BINDINGS_POLICY_WEB_UI);
}
base::RunLoop* run_loop_;
bool web_ui_enabled_ = true;
const base::flat_map<
std::string,
@ -256,14 +216,15 @@ class TestWebUIContentBrowserClient : public ContentBrowserClient {
void RegisterBrowserInterfaceBindersForFrame(
RenderFrameHost* render_frame_host,
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) override {
map->Add<mojom::BrowserTarget>(
base::BindRepeating(&TestWebUIContentBrowserClient::BindBrowserTarget,
base::Unretained(this)));
map->Add<mojom::WebUIMojoTestCache>(base::BindRepeating(
&TestWebUIContentBrowserClient::BindTestCache, base::Unretained(this)));
}
void BindBrowserTarget(content::RenderFrameHost* render_frame_host,
mojo::PendingReceiver<mojom::BrowserTarget> receiver) {
void BindTestCache(
content::RenderFrameHost* render_frame_host,
mojo::PendingReceiver<mojom::WebUIMojoTestCache> receiver) {
auto* contents = WebContents::FromRenderFrameHost(render_frame_host);
static_cast<PingTestWebUIController*>(contents->GetWebUI()->GetController())
static_cast<CacheTestWebUIController*>(
contents->GetWebUI()->GetController())
->CreateHandler(std::move(receiver));
}
};
@ -283,8 +244,9 @@ class WebUIMojoTest : public ContentBrowserTest {
void NavigateWithNewWebUI(const std::string& path) {
// Load a dummy WebUI URL first so that a new WebUI is set up when we load
// the URL we're actually interested in.
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("dummy-web-ui")));
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("mojo-web-ui/" + path)));
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL(kDummyWebUiHost)));
EXPECT_TRUE(NavigateToURL(
shell(), GetWebUIURL(kMojoWebUiHost + std::string("/") + path)));
}
// Run |script| and return a boolean result.
@ -314,78 +276,50 @@ class WebUIMojoTest : public ContentBrowserTest {
DISALLOW_COPY_AND_ASSIGN(WebUIMojoTest);
};
bool IsGeneratedResourceAvailable(const std::string& resource_path) {
// Currently there is no way to have a generated file included in the isolate
// files. If the bindings file doesn't exist assume we're on such a bot and
// pass.
// TODO(sky): remove this conditional when isolates support copying from gen.
base::ScopedAllowBlockingForTesting allow_blocking;
const base::FilePath test_file_path(GetFilePathForJSResource(resource_path));
if (base::PathExists(test_file_path))
return true;
LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate";
return false;
}
// Loads a WebUI page that contains Mojo JS bindings and verifies a message
// round-trip between the page and the browser.
IN_PROC_BROWSER_TEST_F(WebUIMojoTest, EndToEndCommunication) {
GURL kTestUrl(GetWebUIURL(std::string(kMojoWebUiHost) + "/?cache"));
const std::string kTestScript = "runTest();";
bool passed = false;
EXPECT_TRUE(NavigateToURL(shell(), kTestUrl));
EXPECT_TRUE(ExecuteScriptAndExtractBool(shell()->web_contents(), kTestScript,
&passed));
EXPECT_TRUE(passed);
// Loads a webui page that contains mojo bindings and verifies a message makes
// it from the browser to the page and back.
IN_PROC_BROWSER_TEST_F(WebUIMojoTest, EndToEndPing) {
if (!IsGeneratedResourceAvailable(
"content/test/data/web_ui_test_mojo_bindings.mojom-lite.js"))
return;
GURL test_url(GetWebUIURL("mojo-web-ui/web_ui_mojo.html?ping"));
// Check that a second shell works correctly.
passed = false;
Shell* other_shell = CreateBrowser();
EXPECT_TRUE(NavigateToURL(other_shell, kTestUrl));
EXPECT_TRUE(ExecuteScriptAndExtractBool(other_shell->web_contents(),
kTestScript, &passed));
EXPECT_TRUE(passed);
{
g_got_message = false;
base::RunLoop run_loop;
factory()->set_run_loop(&run_loop);
EXPECT_TRUE(NavigateToURL(shell(), test_url));
// RunLoop is quit when message received from page.
run_loop.Run();
EXPECT_TRUE(g_got_message);
}
// We expect two independent chrome://foo tabs/shells to use a separate
// process.
EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(),
other_shell->web_contents()->GetMainFrame()->GetProcess());
{
// Check that a second shell works correctly.
Shell* other_shell = CreateBrowser();
g_got_message = false;
base::RunLoop other_run_loop;
factory()->set_run_loop(&other_run_loop);
EXPECT_TRUE(NavigateToURL(other_shell, test_url));
// RunLoop is quit when message received from page.
other_run_loop.Run();
EXPECT_TRUE(g_got_message);
// Close the second shell and wait until its process exits.
RenderProcessHostWatcher process_watcher(
other_shell->web_contents()->GetMainFrame()->GetProcess(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
other_shell->Close();
process_watcher.Wait();
// We expect two independent chrome://foo tabs/shells to use a separate
// process.
EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(),
other_shell->web_contents()->GetMainFrame()->GetProcess());
// Check that a third shell works correctly, even if we force it to share a
// process with the first shell, by forcing an artificially low process
// limit.
RenderProcessHost::SetMaxRendererProcessCount(1);
// Close the second shell and wait until its process exits.
RenderProcessHostWatcher process_watcher(
other_shell->web_contents()->GetMainFrame()->GetProcess(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
other_shell->Close();
process_watcher.Wait();
}
{
// Check that a third shell works correctly, even if we force it to share a
// process with the first shell, by forcing an artificially low process
// limit.
RenderProcessHost::SetMaxRendererProcessCount(1);
Shell* other_shell = CreateBrowser();
g_got_message = false;
base::RunLoop other_run_loop;
factory()->set_run_loop(&other_run_loop);
EXPECT_TRUE(NavigateToURL(other_shell, test_url));
// RunLoop is quit when message received from page.
other_run_loop.Run();
EXPECT_TRUE(g_got_message);
EXPECT_EQ(shell()->web_contents()->GetMainFrame()->GetProcess(),
other_shell->web_contents()->GetMainFrame()->GetProcess());
}
other_shell = CreateBrowser();
passed = false;
EXPECT_TRUE(NavigateToURL(other_shell, kTestUrl));
EXPECT_EQ(shell()->web_contents()->GetMainFrame()->GetProcess(),
other_shell->web_contents()->GetMainFrame()->GetProcess());
EXPECT_TRUE(ExecuteScriptAndExtractBool(other_shell->web_contents(),
kTestScript, &passed));
EXPECT_TRUE(passed);
}
// Disabled due to flakiness: crbug.com/860385.
@ -445,8 +379,8 @@ IN_PROC_BROWSER_TEST_F(WebUIMojoTest, MAYBE_ChromeSendAvailable) {
}
IN_PROC_BROWSER_TEST_F(WebUIMojoTest, ChromeSendAvailable_AfterCrash) {
GURL test_url(
GetWebUIURL("mojo-web-ui/web_ui_mojo_native.html?webui_bindings"));
GURL test_url(GetWebUIURL(std::string(kMojoWebUiHost) +
"/web_ui_mojo_native.html?webui_bindings"));
// Navigate with normal WebUI bindings and ensure chrome.send is available.
EXPECT_TRUE(NavigateToURL(shell(), test_url));

@ -35,6 +35,7 @@ Other resources that belong in this file:
<include name="IDR_UNGUESSABLE_TOKEN_MOJO_JS" file="${root_gen_dir}/mojo/public/mojom/base/unguessable_token.mojom-lite.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_URL_MOJO_HTML" file="${root_gen_dir}/url/mojom/url.mojom.html" use_base_dir="false" type="BINDATA" />
<include name="IDR_URL_MOJO_JS" file="${root_gen_dir}/url/mojom/url.mojom-lite.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_URL_MOJOM_WEBUI_JS" file="${root_gen_dir}/mojom-webui/url/mojom/url.mojom-webui.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_VULKAN_INFO_MOJO_JS" file="${root_gen_dir}/gpu/ipc/common/vulkan_info.mojom-lite.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_VULKAN_TYPES_MOJO_JS" file="${root_gen_dir}/gpu/ipc/common/vulkan_types.mojom-lite.js" use_base_dir="false" type="BINDATA" />
</includes>

@ -408,6 +408,7 @@ repack("pak") {
"$root_gen_dir/content/content_resources.pak",
"$root_gen_dir/content/dev_ui_content_resources.pak",
"$root_gen_dir/content/shell/shell_resources.pak",
"$root_gen_dir/content/test/web_ui_mojo_test_resources.pak",
"$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
"$root_gen_dir/net/net_resources.pak",
"$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
@ -428,6 +429,7 @@ repack("pak") {
"//content/browser/resources/media:media_internals_resources",
"//content/browser/tracing:resources",
"//content/browser/webrtc/resources",
"//content/test:web_ui_mojo_test_resources",
"//mojo/public/js:resources",
"//net:net_resources",
"//third_party/blink/public:devtools_inspector_resources",

@ -18,6 +18,9 @@ import("//net/features.gni")
import("//ppapi/buildflags/buildflags.gni")
import("//testing/test.gni")
import("//third_party/blink/public/public_features.gni")
import("//third_party/closure_compiler/closure_args.gni")
import("//third_party/closure_compiler/compile_js.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
import("//v8/gni/v8.gni")
@ -778,7 +781,26 @@ mojom("content_test_mojo_bindings") {
}
mojom("web_ui_test_mojo_bindings") {
sources = [ "data/web_ui_test_mojo_bindings.mojom" ]
testonly = true
sources = [
"data/web_ui_test.test-mojom",
"data/web_ui_test_types.test-mojom",
]
public_deps = [ "//url/mojom:url_mojom_gurl" ]
webui_module_path = "/content/test/data"
}
js_library("web_ui_mojo_test_js") {
sources = [ "data/web_ui_mojo_test.js" ]
deps = [ ":web_ui_test_mojo_bindings_webui_js" ]
}
# NOTE: Building this target serves as a compile test for type-checking of
# WebUI JS that consumes generated Mojom JS bindings.
js_type_check("web_ui_mojo_test_js_type_check") {
uses_js_modules = true
deps = [ ":web_ui_mojo_test_js" ]
closure_flags = mojom_js_args
}
mojom("mojo_web_test_bindings") {
@ -1181,6 +1203,7 @@ test("content_browsertests") {
]
public_deps = [
":web_ui_mojo_test_resources",
"//content:content_resources",
"//content:dev_ui_content_resources",
]
@ -1190,6 +1213,7 @@ test("content_browsertests") {
":content_test_mojo_bindings",
":test_interfaces",
":test_support",
":web_ui_mojo_test_js_type_check",
":web_ui_test_mojo_bindings",
"//base/test:test_support",
"//build:chromecast_buildflags",
@ -1305,7 +1329,6 @@ test("content_browsertests") {
data = []
data_deps = [
":web_ui_test_mojo_bindings_js_data_deps",
"//content/shell:pak",
"//testing/buildbot/filters:content_browsertests_filters",
"//third_party/mesa_headers",
@ -1621,6 +1644,22 @@ test("content_browsertests") {
}
}
grit("web_ui_mojo_test_resources") {
source = "web_ui_mojo_test_resources.grd"
outputs = [
"grit/web_ui_mojo_test_resources.h",
"grit/web_ui_mojo_test_resources_map.cc",
"grit/web_ui_mojo_test_resources_map.h",
"web_ui_mojo_test_resources.pak",
]
grit_flags = [
"-E",
"root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
]
deps = [ ":web_ui_test_mojo_bindings_webui_js" ]
}
static_library("run_all_unittests") {
testonly = true
sources = [ "run_all_unittests.cc" ]

@ -1,10 +0,0 @@
<html>
<head>
<script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script>
<script src="content/test/data/web_ui_test_mojo_bindings.mojom-lite.js"></script>
<script src="web_ui_mojo.js"></script>
</head>
<body>
x
</body>
</html>

@ -1,10 +0,0 @@
// Copyright 2014 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.
(async () => {
const browserTarget = content.mojom.BrowserTarget.getRemote();
await browserTarget.start();
browserTarget.stop();
})();

@ -0,0 +1,8 @@
<html>
<head>
<script src="web_ui_mojo_test.js" type="module"></script>
</head>
<body>
x
</body>
</html>

@ -0,0 +1,46 @@
// 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.
import {WebUIMojoTestCache} from './content/test/data/web_ui_test.test-mojom-webui.js';
/** @type {{send: function(*)}} */
Window.prototype.domAutomationController;
const TEST_DATA = [
{ url: 'https://google.com/', contents: 'i am in fact feeling lucky' },
{ url: 'https://youtube.com/', contents: 'probably cat videos?' },
{ url: 'https://example.com/', contents: 'internets wow' },
];
async function doTest() {
const cache = WebUIMojoTestCache.getRemote();
for (const entry of TEST_DATA) {
cache.put({ url: entry.url }, entry.contents);
}
const {items} = await cache.getAll();
if (items.length != TEST_DATA.length) {
return false;
}
const entries = {};
for (const item of items) {
entries[item.url.url] = item.contents;
}
for (const entry of TEST_DATA) {
if (!(entry.url in entries)) {
return false;
}
if (entries[entry.url] != entry.contents) {
return false;
}
}
return true;
}
window.runTest = async function() {
window.domAutomationController.send(await doTest());
}

@ -0,0 +1,13 @@
// 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.
module content.mojom;
import "content/test/data/web_ui_test_types.test-mojom";
import "url/mojom/url.mojom";
interface WebUIMojoTestCache {
Put(url.mojom.Url url, string contents);
GetAll() => (array<CacheItem> items);
};

@ -1,10 +0,0 @@
// Copyright 2014 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.
module content.mojom;
interface BrowserTarget {
Start() => ();
Stop();
};

@ -0,0 +1,16 @@
// 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.
module content.mojom;
import "url/mojom/url.mojom";
// This struct is imported by web_ui_test.test-mojom and is used to provide
// test coverage of non-shared WebUI mojom JS modules importing other
// non-shared WebUI mojom JS modules.
struct CacheItem {
url.mojom.Url url;
string contents;
};

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file specifies resources for content_browsertests.
-->
<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
<outputs>
<output filename="grit/web_ui_mojo_test_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="grit/web_ui_mojo_test_resources_map.cc" type="resource_file_map_source" />
<output filename="grit/web_ui_mojo_test_resources_map.h" type="resource_map_header" />
<output filename="web_ui_mojo_test_resources.pak" type="data_package" />
</outputs>
<translations />
<release seq="1">
<includes>
<include name="IDR_WEB_UI_MOJO_HTML" file="data/web_ui_mojo_test.html" type="BINDATA" />
<include name="IDR_WEB_UI_MOJO_JS" file="data/web_ui_mojo_test.js" type="BINDATA" resource_path="web_ui_mojo_test.js" />
<include name="IDR_WEB_UI_MOJO_NATIVE_HTML" file="data/web_ui_mojo_native.html" type="BINDATA" resource_path="web_ui_mojo_native.html" />
<include name="IDR_WEB_UI_MOJO_NATIVE_JS" file="data/web_ui_mojo_native.js" type="BINDATA" resource_path="web_ui_mojo_native.js" />
<include name="IDR_WEB_UI_TEST_MOJOM_JS" file="${root_gen_dir}/mojom-webui/content/test/data/web_ui_test.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="content/test/data/web_ui_test.test-mojom-webui.js" />
<include name="IDR_WEB_UI_TEST_TYPES_MOJOM_JS" file="${root_gen_dir}/mojom-webui/content/test/data/web_ui_test_types.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="content/test/data/web_ui_test_types.test-mojom-webui.js" />
</includes>
</release>
</grit>

@ -103,7 +103,7 @@ concatenate_files("bindings_uncompiled_module") {
"interface_support.js",
"bindings_uncompiled_module_export.js.part",
]
output = "bindings_uncompiled.m.js"
output = "bindings_uncompiled.js"
deps = [ "//mojo/public/interfaces/bindings:bindings_js_library" ]
}
@ -128,13 +128,13 @@ if (enable_mojom_closure_compile || enable_js_type_check) {
"$target_gen_dir/mojo_bindings_lite.js",
"bindings_module_export.js.part",
]
output = "bindings_compiled.m.js"
output = "bindings_compiled.js"
deps = [ ":bindings_lite" ]
}
copy("bindings_module") {
sources = [ "$target_gen_dir/bindings_compiled.m.js" ]
outputs = [ "$target_gen_dir/bindings.m.js" ]
sources = [ "$target_gen_dir/bindings_compiled.js" ]
outputs = [ "$target_gen_dir/bindings.js" ]
deps = [ ":bindings_compiled_module" ]
}
} else {
@ -158,19 +158,19 @@ if (enable_mojom_closure_compile || enable_js_type_check) {
}
copy("bindings_module") {
sources = [ "$target_gen_dir/bindings_uncompiled.m.js" ]
outputs = [ "$target_gen_dir/bindings.m.js" ]
sources = [ "$target_gen_dir/bindings_uncompiled.js" ]
outputs = [ "$target_gen_dir/bindings.js" ]
deps = [ ":bindings_uncompiled_module" ]
}
}
# This is the library target used in the dependency tree of any JS libraries
# or binaries compiling against mojom JS bindings. This library is functionally
# equivalent to the bindings.m.js generated by the ":bindings_module" target and
# equivalent to the bindings.js generated by the ":bindings_module" target and
# used at runtime by all consumers, except that this module includes all type
# annotations and is suitable for Closure compilation and type checking.
js_library("bindings_uncompiled.m") {
sources = [ "$target_gen_dir/bindings_uncompiled.m.js" ]
js_library("bindings_uncompiled") {
sources = [ "$target_gen_dir/bindings_uncompiled.js" ]
extra_deps = [ ":bindings_uncompiled_module" ]
}
@ -192,6 +192,7 @@ grit("resources") {
deps = [
":bindings",
":bindings_lite",
":bindings_module",
"//mojo/public/mojom/base:base_js",
]
}

@ -7,7 +7,7 @@
<output filename="grit/mojo_bindings_resources_map.h"
type="resource_map_header" />
<output filename="grit/mojo_bindings_resources_map.cc"
type="resource_map_source" />
type="resource_file_map_source" />
<output filename="mojo_bindings_resources.pak" type="data_package" />
</outputs>
<translations />
@ -18,55 +18,73 @@
<include name="IDR_MOJO_MOJO_BINDINGS_JS"
file="${root_gen_dir}/mojo/public/js/mojo_bindings.js"
use_base_dir="false"
resource_path="mojo/mojo/public/js/mojo_bindings.js"
type="BINDATA" />
</if>
<include name="IDR_MOJO_BINDINGS_JS"
file="${root_gen_dir}/mojo/public/js/bindings.js"
use_base_dir="false"
resource_path="mojo/mojo/public/js/bindings.js"
type="BINDATA" />
<include name="IDR_MOJO_MOJO_BINDINGS_LITE_HTML"
file="mojo_bindings_lite.html"
resource_path="mojo/mojo/public/js/mojo_bindings_lite.html"
type="BINDATA" />
<include name="IDR_MOJO_MOJO_BINDINGS_LITE_JS"
file="${root_gen_dir}/mojo/public/js/mojo_bindings_lite.js"
use_base_dir="false"
resource_path="mojo/mojo/public/js/mojo_bindings_lite.js"
type="BINDATA" />
<include name="IDR_MOJO_BIG_BUFFER_MOJOM_HTML"
file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom.html"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/big_buffer.mojom.html"
type="BINDATA" />
<include name="IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS"
file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom-lite.js"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js"
type="BINDATA" />
<include name="IDR_MOJO_FILE_MOJOM_HTML"
file="${root_gen_dir}/mojo/public/mojom/base/file.mojom.html"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/file.mojom.html"
type="BINDATA" />
<include name="IDR_MOJO_FILE_MOJOM_LITE_JS"
file="${root_gen_dir}/mojo/public/mojom/base/file.mojom-lite.js"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/file.mojom-lite.js"
type="BINDATA" />
<include name="IDR_MOJO_STRING16_MOJOM_HTML"
file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom.html"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/string16.mojom.html"
type="BINDATA" />
<include name="IDR_MOJO_STRING16_MOJOM_LITE_JS"
file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom-lite.js"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/string16.mojom-lite.js"
type="BINDATA" />
<include name="IDR_MOJO_TEXT_DIRECTION_MOJOM_HTML"
file="${root_gen_dir}/mojo/public/mojom/base/text_direction.mojom.html"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/text_direction.mojom.html"
type="BINDATA" />
<include name="IDR_MOJO_TEXT_DIRECTION_MOJOM_LITE_JS"
file="${root_gen_dir}/mojo/public/mojom/base/text_direction.mojom-lite.js"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/text_direction.mojom-lite.js"
type="BINDATA" />
<if expr="is_win or is_macosx or is_linux or is_android">
<include name="IDR_MOJO_TIME_MOJOM_HTML"
file="${root_gen_dir}/mojo/public/mojom/base/time.mojom.html"
use_base_dir="false"
resource_path="mojo/mojo/public/mojom/base/time.mojom.html"
type="BINDATA"
compress="gzip" />
<include name="IDR_MOJO_TIME_MOJOM_LITE_JS"
file="${root_gen_dir}/mojo/public/mojom/base/time.mojom-lite.js"
resource_path="mojo/mojo/public/mojom/base/time.mojom-lite.js"
use_base_dir="false"
type="BINDATA"
compress="gzip" />

@ -1,7 +1,11 @@
{# For bindings internals, generated code is concatenated into a larger module
# at build time, so we avoid a superfluous file header here. #}
{%- if not for_bindings_internals -%}
// Copyright 2018 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.
'use strict';
{%- endif %}
{% if generate_closure_exports -%}
goog.require('mojo.internal');

@ -40,6 +40,7 @@ mojo.internal.Struct(
{% if generate_closure_exports -%}
goog.provide('{{module.namespace}}.{{struct.name}}');
{%- endif %}
/** @record */
{{module.namespace}}.{{struct.name}} = class {
@ -51,4 +52,3 @@ goog.provide('{{module.namespace}}.{{struct.name}}');
}
};
{%- endif %}

@ -203,6 +203,16 @@ _primitive_kind_to_fuzz_type = {
}
_SHARED_MODULE_PREFIX = 'chrome://resources/mojo'
def _GetWebUiModulePath(module):
path = module.metadata.get('webui_module_path')
if path is None:
return None
return path.strip('/')
def JavaScriptPayloadSize(packed):
packed_fields = packed.packed_fields
if not packed_fields:
@ -265,24 +275,40 @@ class JavaScriptStylizer(generator.Stylizer):
class Generator(generator.Generator):
def _GetParameters(self, for_compile=False):
def _GetParameters(self, for_compile=False, for_webui_module=False):
return {
"bindings_library_path": self._GetBindingsLibraryPath(),
"enums": self.module.enums,
"for_bindings_internals": self.disallow_native_types,
"html_imports": self._GenerateHtmlImports(),
"imports": self.module.imports,
"interfaces": self.module.interfaces,
"js_module_imports": self._GetJsModuleImports(),
"kinds": self.module.kinds,
"module": self.module,
"mojom_filename": os.path.basename(self.module.path),
"mojom_namespace": self.module.mojom_namespace,
"structs": self.module.structs + self._GetStructsFromMethods(),
"unions": self.module.unions,
"generate_fuzzing": self.generate_fuzzing,
"generate_closure_exports": for_compile,
"generate_struct_deserializers": self.js_generate_struct_deserializers,
"bindings_library_path":
self._GetBindingsLibraryPath(for_webui_module=for_webui_module),
"enums":
self.module.enums,
"for_bindings_internals":
self.disallow_native_types,
"html_imports":
self._GenerateHtmlImports(),
"imports":
self.module.imports,
"interfaces":
self.module.interfaces,
"js_module_imports":
self._GetJsModuleImports(for_webui_module=for_webui_module),
"kinds":
self.module.kinds,
"module":
self.module,
"mojom_filename":
os.path.basename(self.module.path),
"mojom_namespace":
self.module.mojom_namespace,
"structs":
self.module.structs + self._GetStructsFromMethods(),
"unions":
self.module.unions,
"generate_fuzzing":
self.generate_fuzzing,
"generate_closure_exports":
for_compile,
"generate_struct_deserializers":
self.js_generate_struct_deserializers,
}
@staticmethod
@ -386,6 +412,10 @@ class Generator(generator.Generator):
def _GenerateJsModule(self):
return self._GetParameters()
@UseJinja("lite/mojom.m.js.tmpl")
def _GenerateWebUiModule(self):
return self._GetParameters(for_webui_module=True)
def GenerateFiles(self, args):
if self.variant:
raise Exception("Variants not supported in JavaScript bindings.")
@ -409,6 +439,9 @@ class Generator(generator.Generator):
"%s-lite-for-compile.js" % self.module.path)
self.WriteWithComment(self._GenerateJsModule(),
"%s.m.js" % self.module.path)
if _GetWebUiModulePath(self.module) is not None:
self.WriteWithComment(self._GenerateWebUiModule(),
"mojom-webui/%s-webui.js" % self.module.path)
def _GetRelativePath(self, path):
relpath = urllib_request.pathname2url(
@ -417,8 +450,10 @@ class Generator(generator.Generator):
return relpath
return './' + relpath
def _GetBindingsLibraryPath(self):
return self._GetRelativePath('mojo/public/js/bindings.m.js')
def _GetBindingsLibraryPath(self, for_webui_module=False):
if for_webui_module:
return "chrome://resources/mojo/mojo/public/js/bindings.js"
return self._GetRelativePath('mojo/public/js/bindings.js')
def _SetUniqueNameForImports(self):
used_names = set()
@ -951,13 +986,39 @@ class Generator(generator.Generator):
os.path.relpath(full_import.path, os.path.dirname(self.module.path)))
return result
def _GetJsModuleImports(self):
def _GetJsModuleImports(self, for_webui_module=False):
this_module_path = _GetWebUiModulePath(self.module)
if this_module_path:
this_module_is_shared = this_module_path.startswith(_SHARED_MODULE_PREFIX)
imports = dict()
for spec, kind in self.module.imported_kinds.items():
path = self._GetRelativePath(kind.module.path) + '.m.js'
if path not in imports:
imports[path] = []
imports[path].append(kind)
if for_webui_module:
base_path = _GetWebUiModulePath(kind.module)
assert base_path is not None
import_path = '{}/{}-webui.js'.format(
base_path, os.path.basename(kind.module.path))
import_module_is_shared = import_path.startswith(_SHARED_MODULE_PREFIX)
if import_module_is_shared == this_module_is_shared:
# Either we're a non-shared resource importing another non-shared
# resource, or we're a shared resource importing another shared
# resource. In both cases, we assume a relative import path will
# suffice.
import_path = os.path.relpath(
import_path.lstrip(_SHARED_MODULE_PREFIX),
this_module_path.lstrip(_SHARED_MODULE_PREFIX))
if (not import_path.startswith('.')
and not import_path.startswith('/')):
import_path = './' + import_path
else:
assert import_module_is_shared, \
'Shared WebUI module "{}" cannot depend on non-shared WebUI ' \
'module "{}"'.format(self.module.path, kind.module.path)
else:
import_path = self._GetRelativePath(kind.module.path) + '.m.js'
if import_path not in imports:
imports[import_path] = []
imports[import_path].append(kind)
return imports
def _GetStructsFromMethods(self):

@ -291,6 +291,21 @@ if (enable_scrambled_message_ids) {
# List of extra C++ templates that are used to generate additional source
# and/or header files. The templates should end with extension ".tmpl".
#
# webui_module_path (optional)
# The path or URL at which modules generated by this target will be
# accessible to WebUI pages. This may either be an absolute path or
# a full URL path starting with "chrome://resources/mojo".
#
# If an absolute path, a WebUI page may only import these modules if
# they are manually packaged and mapped independently by that page's
# WebUIDataSource. The mapped path must match the path given here.
#
# If this is is instead a URL string starting with
# "chrome://resources/mojo", the generated resources must be added to
# content_resources.grd and registered with
# content::SharedResourcesDataSource with a corresponding path, at which
# point they will be made available to all WebUI pages at the given URL.
#
# The following parameters are used to support the component build. They are
# needed so that bindings which are linked with a component can use the same
# export settings for classes. The first three are for the chromium variant, and
@ -656,6 +671,13 @@ template("mojom") {
enabled_feature,
]
}
if (defined(invoker.webui_module_path)) {
args += [
"--add-module-metadata",
"webui_module_path=${invoker.webui_module_path}",
]
}
}
}
@ -1556,6 +1578,10 @@ template("mojom") {
"$root_gen_dir/$base_path.html",
"$root_gen_dir/$base_path-lite-for-compile.js",
]
if (defined(invoker.webui_module_path)) {
outputs += [ "$root_gen_dir/mojom-webui/$base_path-webui.js" ]
}
}
response_file_contents = filelist
@ -1689,7 +1715,7 @@ template("mojom") {
invoker.disallow_native_types) {
deps = []
} else {
deps = [ "//mojo/public/js:bindings_uncompiled.m" ]
deps = [ "//mojo/public/js:bindings_uncompiled" ]
}
foreach(d, all_deps) {
full_name = get_label_info(d, "label_no_toolchain")
@ -1700,6 +1726,36 @@ template("mojom") {
group(js_modules_target_name) {
}
}
if (defined(invoker.webui_module_path)) {
webui_js_target_name = "${target_name}_webui_js"
if (sources_list != []) {
js_library(webui_js_target_name) {
extra_public_deps = [ ":$generator_js_target_name" ]
sources = []
foreach(base_path, output_file_base_paths) {
sources += [ "$root_gen_dir/mojom-webui/${base_path}-webui.js" ]
}
externs_list = [
"${externs_path}/mojo_core.js",
"${externs_path}/pending.js",
]
if (defined(invoker.disallow_native_types) &&
invoker.disallow_native_types) {
deps = []
} else {
deps = [ "//mojo/public/js:bindings_uncompiled" ]
}
foreach(d, all_deps) {
full_name = get_label_info(d, "label_no_toolchain")
deps += [ "${full_name}_webui_js" ]
}
}
} else {
group(webui_js_target_name) {
}
}
}
}
if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&
use_typescript_for_target) {

@ -1362,6 +1362,7 @@ class Module(object):
self.attributes = attributes
self.imports = []
self.imported_kinds = {}
self.metadata = {}
def __repr__(self):
# Gives us a decent __repr__ for modules.

@ -98,7 +98,7 @@ def _GetModuleFilename(mojom_filename):
def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,
dependencies, loaded_modules):
dependencies, loaded_modules, module_metadata):
"""Recursively ensures that a module and its dependencies are loaded.
Args:
@ -111,10 +111,8 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,
by absolute file path.
loaded_modules: A mapping of all modules loaded so far, including non-input
modules that were pulled in as transitive dependencies of the inputs.
import_set: The working set of mojom imports processed so far in this
call stack. Used to detect circular dependencies.
import_stack: An ordered list of imports processed so far in this call
stack. Used to report circular dependencies.
module_metadata: Metadata to be attached to every module loaded by this
helper.
Returns:
None
@ -129,7 +127,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,
for dep_abspath, dep_path in dependencies[mojom_abspath]:
if dep_abspath not in loaded_modules:
_EnsureInputLoaded(dep_abspath, dep_path, abs_paths, asts, dependencies,
loaded_modules)
loaded_modules, module_metadata)
imports = {}
for imp in asts[mojom_abspath].import_list:
@ -137,6 +135,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,
imports[path] = loaded_modules[abs_paths[path]]
loaded_modules[mojom_abspath] = translate.OrderedModule(
asts[mojom_abspath], module_path, imports)
loaded_modules[mojom_abspath].metadata = dict(module_metadata)
def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):
@ -161,6 +160,7 @@ def _ParseMojoms(mojom_files,
input_root_paths,
output_root_path,
enabled_features,
module_metadata,
allowed_imports=None):
"""Parses a set of mojom files and produces serialized module outputs.
@ -176,6 +176,8 @@ def _ParseMojoms(mojom_files,
modules for any transitive dependencies not listed in mojom_files.
enabled_features: A list of enabled feature names, controlling which AST
nodes are filtered by [EnableIf] attributes.
module_metadata: A list of 2-tuples representing metadata key-value pairs to
attach to each compiled module output.
Returns:
None.
@ -236,7 +238,7 @@ def _ParseMojoms(mojom_files,
num_existing_modules_loaded = len(loaded_modules)
for mojom_abspath, mojom_path in mojom_files_to_parse.items():
_EnsureInputLoaded(mojom_abspath, mojom_path, abs_paths, loaded_mojom_asts,
input_dependencies, loaded_modules)
input_dependencies, loaded_modules, module_metadata)
assert (num_existing_modules_loaded +
len(mojom_files_to_parse) == len(loaded_modules))
@ -333,6 +335,16 @@ already present in the provided output root.""")
'build-time dependency checking for mojom imports, where each build '
'metadata file corresponds to a build target in the dependency graph of '
'a typical build system.')
arg_parser.add_argument(
'--add-module-metadata',
dest='module_metadata',
default=[],
action='append',
metavar='KEY=VALUE',
help='Adds a metadata key-value pair to the output module. This can be '
'used by build toolchains to augment parsed mojom modules with product-'
'specific metadata for later extraction and use by custom bindings '
'generators.')
args, _ = arg_parser.parse_known_args(command_line)
if args.mojom_file_list:
@ -353,8 +365,9 @@ already present in the provided output root.""")
else:
allowed_imports = None
module_metadata = map(lambda kvp: tuple(kvp.split('=')), args.module_metadata)
_ParseMojoms(mojom_files, input_roots, output_root, args.enabled_features,
allowed_imports)
module_metadata, allowed_imports)
if __name__ == '__main__':

@ -57,6 +57,22 @@ js_modules_args = [
"chrome_pass=false",
]
mojom_js_args = [
"js_module_root=gen/mojo/public/js/",
"js_module_root=gen/mojom-webui/",
# Mojom WebUI JS modules load bindings.js which may be compiled and stripped
# of type annotations. Rewrite these references to the uncompiled equivalent
# for compilation and type-checking.
"browser_resolver_prefix_replacements=\"chrome://resources/mojo/mojo/public/js/bindings.js=/bindings_uncompiled.js\"",
# Shared Mojom WebUI JS modules are by convention exposed through URLs like
# chrome://resources/mojo/<path-to-module>. At build time these will be given
# as gen/<path-to-module> for compilation, so we can rewrite them as absolute
# paths.
"browser_resolver_prefix_replacements=\"chrome://resources/mojo/=/\"",
]
polymer3_args = js_modules_args + [
"browser_resolver_prefix_replacements=\"../polymer/polymer_bundled.min.js=../polymer/polymer_bundled.js\"",
"browser_resolver_prefix_replacements=\"chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js=../../third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js\"",

@ -484,6 +484,9 @@
"content/shell/shell_resources.grd": {
"includes": [2940],
},
"content/test/web_ui_mojo_test_resources.grd": {
"includes": [2950],
},
# This file is generated during the build.
"<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.grd": {

@ -40,6 +40,8 @@ mojom("url_mojom_gurl") {
traits_public_deps = [ "//url" ]
},
]
webui_module_path = "chrome://resources/mojo/url/mojom"
}
mojom("url_mojom_origin") {