diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-03-12 09:13:00 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-03-16 09:58:26 +0000 |
commit | 03561cae90f1d99b5c54b1ef3be69f10e882b25e (patch) | |
tree | cc5f0958e823c044e7ae51cc0117fe51432abe5e /chromium/mojo | |
parent | fa98118a45f7e169f8846086dc2c22c49a8ba310 (diff) | |
download | qtwebengine-chromium-03561cae90f1d99b5c54b1ef3be69f10e882b25e.tar.gz |
BASELINE: Update Chromium to 88.0.4324.208
Change-Id: I3ae87d23e4eff4b4a469685658740a213600c667
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/mojo')
104 files changed, 2291 insertions, 1103 deletions
diff --git a/chromium/mojo/BUILD.gn b/chromium/mojo/BUILD.gn index ea1cc00ddc4..c1fe06e9f60 100644 --- a/chromium/mojo/BUILD.gn +++ b/chromium/mojo/BUILD.gn @@ -59,7 +59,6 @@ test("mojo_perftests") { "//base", "//base/test:test_support", "//mojo/core:embedder_internal", - "//mojo/core:test_utils", "//mojo/core/embedder", "//testing/gtest", ] diff --git a/chromium/mojo/DIR_METADATA b/chromium/mojo/DIR_METADATA new file mode 100644 index 00000000000..06992d0dc06 --- /dev/null +++ b/chromium/mojo/DIR_METADATA @@ -0,0 +1,11 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +monorail { + component: "Internals>Mojo" +}
\ No newline at end of file diff --git a/chromium/mojo/OWNERS b/chromium/mojo/OWNERS index 5d06c5f1b75..15bd89fcb36 100644 --- a/chromium/mojo/OWNERS +++ b/chromium/mojo/OWNERS @@ -5,5 +5,3 @@ sky@chromium.org # For Fuchsia-specific changes: per-file *_fuchsia*=file://build/fuchsia/OWNERS - -# COMPONENT: Internals>Mojo diff --git a/chromium/mojo/core/BUILD.gn b/chromium/mojo/core/BUILD.gn index db6af19ea9f..89add521c60 100644 --- a/chromium/mojo/core/BUILD.gn +++ b/chromium/mojo/core/BUILD.gn @@ -12,13 +12,6 @@ if (is_mac) { import("//third_party/protobuf/proto_library.gni") } -# This file depends on the legacy global sources assignment filter. It should -# be converted to check target platform before assigning source files to the -# sources variable. Remove this import and set_sources_assignment_filter call -# when the file has been converted. See https://crbug.com/1018739 for details. -import("//build/config/deprecated_default_sources_assignment_filter.gni") -set_sources_assignment_filter(deprecated_default_sources_assignment_filter) - component("embedder_internal") { output_name = "mojo_core_embedder_internal" public_deps = [ ":impl_for_embedder" ] @@ -81,9 +74,7 @@ template("core_impl_source_set") { sources = [ "atomic_flag.h", "broker.h", - "broker_win.cc", "channel.cc", - "channel_win.cc", "configuration.cc", "connection_params.cc", "core.cc", @@ -163,6 +154,10 @@ template("core_impl_source_set") { } if (is_win) { + sources += [ + "broker_win.cc", + "channel_win.cc", + ] cflags = [ "/wd4324" ] # Structure was padded due to __declspec(align()), # which is uninteresting. } @@ -269,27 +264,6 @@ if (is_chromeos || is_linux || is_android || is_win) { } } -source_set("test_utils") { - testonly = true - - sources = [ - "test_utils.cc", - "test_utils.h", - ] - - public_deps = [ - "//mojo/public/c/system", - "//mojo/public/cpp/system", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//mojo/core/test:test_support", - "//testing/gtest:gtest", - ] -} - source_set("test_sources") { testonly = true sources = [ @@ -316,12 +290,12 @@ source_set("test_sources") { "data_pipe_unittest.cc", "invitation_unittest.cc", "multiprocess_message_pipe_unittest.cc", + "node_controller_unittest.cc", "platform_wrapper_unittest.cc", ] } deps = [ - ":test_utils", "//base", "//base/test:test_support", "//mojo/core:embedder_internal", diff --git a/chromium/mojo/core/channel.h b/chromium/mojo/core/channel.h index 8d705c570bf..1a8ca65f613 100644 --- a/chromium/mojo/core/channel.h +++ b/chromium/mojo/core/channel.h @@ -278,6 +278,11 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel HandlePolicy handle_policy, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner); +#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC) + // At this point only ChannelPosix needs InitFeatures. + static void set_posix_use_writev(bool use_writev); +#endif + // Allows the caller to change the Channel's HandlePolicy after construction. void set_handle_policy(HandlePolicy policy) { handle_policy_ = policy; } diff --git a/chromium/mojo/core/channel_fuzzer.cc b/chromium/mojo/core/channel_fuzzer.cc index cb41000d645..2fb651700c8 100644 --- a/chromium/mojo/core/channel_fuzzer.cc +++ b/chromium/mojo/core/channel_fuzzer.cc @@ -4,7 +4,7 @@ #include <stdint.h> -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/message_loop/message_pump_type.h" #include "base/no_destructor.h" #include "base/run_loop.h" diff --git a/chromium/mojo/core/channel_posix.cc b/chromium/mojo/core/channel_posix.cc index d7d9d6cfee1..141316d1c13 100644 --- a/chromium/mojo/core/channel_posix.cc +++ b/chromium/mojo/core/channel_posix.cc @@ -8,6 +8,7 @@ #include <sys/socket.h> #include <algorithm> +#include <atomic> #include <limits> #include <memory> @@ -28,6 +29,7 @@ #include "mojo/public/cpp/platform/socket_utils_posix.h" #if !defined(OS_NACL) +#include <limits.h> #include <sys/uio.h> #endif @@ -36,6 +38,10 @@ namespace core { namespace { +#if !defined(OS_NACL) +std::atomic<bool> g_use_writev{false}; +#endif + const size_t kMaxBatchReadCapacity = 256 * 1024; // A view over a Channel::Message object. The write queue uses these since @@ -89,6 +95,10 @@ class MessageView { num_handles_sent_ = num_handles_sent; } + size_t num_handles_remaining() const { + return handles_.size() - num_handles_sent_; + } + private: Channel::MessagePtr message_; size_t offset_; @@ -447,6 +457,11 @@ class ChannelPosix : public Channel, } bool FlushOutgoingMessagesNoLock() { +#if !defined(OS_NACL) + if (g_use_writev) + return FlushOutgoingMessagesWritevNoLock(); +#endif + base::circular_deque<MessageView> messages; std::swap(outgoing_messages_, messages); @@ -478,6 +493,112 @@ class ChannelPosix : public Channel, return true; } +#if !defined(OS_NACL) + bool WriteOutgoingMessagesWithWritev() { + if (outgoing_messages_.empty()) + return true; + + // If all goes well we can submit a writev(2) with a iovec of size + // outgoing_messages_.size() but never more than the kernel allows. + size_t num_messages_to_send = + std::min<size_t>(IOV_MAX, outgoing_messages_.size()); + iovec iov[num_messages_to_send]; + memset(&iov[0], 0, sizeof(iov)); + + // Populate the iov. + size_t num_iovs_set = 0; + for (auto it = outgoing_messages_.begin(); + num_iovs_set < num_messages_to_send; ++it) { + if (it->num_handles_remaining() > 0) { + // We can't send handles with writev(2) so stop at this message. + break; + } + + iov[num_iovs_set].iov_base = const_cast<void*>(it->data()); + iov[num_iovs_set].iov_len = it->data_num_bytes(); + num_iovs_set++; + } + + UMA_HISTOGRAM_COUNTS_1000("Mojo.Channel.WritevBatchedMessages", + num_iovs_set); + + size_t iov_offset = 0; + while (iov_offset < num_iovs_set) { + ssize_t bytes_written = SocketWritev(socket_.get(), &iov[iov_offset], + num_iovs_set - iov_offset); + if (bytes_written < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + WaitForWriteOnIOThreadNoLock(); + return true; + } + return false; + } + + // Let's walk our outgoing_messages_ popping off outgoing_messages_ + // that were fully written. + size_t bytes_remaining = bytes_written; + while (bytes_remaining > 0) { + if (bytes_remaining >= outgoing_messages_.front().data_num_bytes()) { + // This message was fully written. + bytes_remaining -= outgoing_messages_.front().data_num_bytes(); + outgoing_messages_.pop_front(); + iov_offset++; + } else { + // This message was partially written, account for what was + // already written. + outgoing_messages_.front().advance_data_offset(bytes_remaining); + bytes_remaining = 0; + + // Update the iov too as we will call writev again. + iov[iov_offset].iov_base = + const_cast<void*>(outgoing_messages_.front().data()); + iov[iov_offset].iov_len = outgoing_messages_.front().data_num_bytes(); + } + } + } + + return true; + } + + // FlushOutgoingMessagesWritevNoLock is equivalent to + // FlushOutgoingMessagesNoLock except it looks for opportunities to make only + // a single write syscall by using writev(2) instead of write(2). In most + // situations this is very straight forward; however, when a handle needs to + // be transferred we cannot use writev(2) and instead will fall back to the + // standard write. + bool FlushOutgoingMessagesWritevNoLock() { + do { + // If the first message contains a handle we will flush it first using a + // standard write, we will also use the standard write if we only have a + // single message. + while (!outgoing_messages_.empty() && + (outgoing_messages_.front().num_handles_remaining() > 0 || + outgoing_messages_.size() == 1)) { + MessageView message = std::move(outgoing_messages_.front()); + + outgoing_messages_.pop_front(); + size_t messages_before_write = outgoing_messages_.size(); + if (!WriteNoLock(std::move(message))) + return false; + + if (outgoing_messages_.size() > messages_before_write) { + // It was re-queued by WriteNoLock. + return true; + } + } + + if (!WriteOutgoingMessagesWithWritev()) + return false; + + // At this point if we have more messages then it's either because we + // exceeded IOV_MAX OR it's because we ran into a FileHandle. Either way + // we just start the process all over again and it will flush any + // FileHandles before attempting writev(2) again. + } while (!outgoing_messages_.empty()); + return true; + } +#endif // !defined(OS_NACL) + #if defined(OS_IOS) bool OnControlMessage(Message::MessageType message_type, const void* payload, @@ -601,6 +722,13 @@ class ChannelPosix : public Channel, } // namespace // static +#if !defined(OS_NACL) +void Channel::set_posix_use_writev(bool use_writev) { + g_use_writev = use_writev; +} +#endif + +// static scoped_refptr<Channel> Channel::Create( Delegate* delegate, ConnectionParams connection_params, diff --git a/chromium/mojo/core/channel_unittest.cc b/chromium/mojo/core/channel_unittest.cc index 4fcb57b9651..58d6ee44ad6 100644 --- a/chromium/mojo/core/channel_unittest.cc +++ b/chromium/mojo/core/channel_unittest.cc @@ -14,7 +14,7 @@ #include "base/process/process_metrics.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/mojo/core/core.cc b/chromium/mojo/core/core.cc index 9f344cfab31..61444a3dec1 100644 --- a/chromium/mojo/core/core.cc +++ b/chromium/mojo/core/core.cc @@ -137,7 +137,7 @@ void Core::SetIOTaskRunner( NodeController* Core::GetNodeController() { base::AutoLock lock(node_controller_lock_); if (!node_controller_) - node_controller_.reset(new NodeController(this)); + node_controller_ = std::make_unique<NodeController>(); return node_controller_.get(); } diff --git a/chromium/mojo/core/core_test_base.h b/chromium/mojo/core/core_test_base.h index 5d37d252c2e..ebc5fe154e3 100644 --- a/chromium/mojo/core/core_test_base.h +++ b/chromium/mojo/core/core_test_base.h @@ -9,7 +9,6 @@ #include "base/macros.h" #include "base/synchronization/lock.h" -#include "mojo/core/test_utils.h" #include "mojo/public/c/system/types.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/mojo/core/core_unittest.cc b/chromium/mojo/core/core_unittest.cc index 8846051ad03..0d2024ccd9a 100644 --- a/chromium/mojo/core/core_unittest.cc +++ b/chromium/mojo/core/core_unittest.cc @@ -11,7 +11,6 @@ #include "base/bind.h" #include "build/build_config.h" #include "mojo/core/core_test_base.h" -#include "mojo/core/test_utils.h" #include "mojo/public/cpp/system/wait.h" #if defined(OS_WIN) @@ -35,7 +34,7 @@ TEST_F(CoreTest, GetTimeTicksNow) { const MojoTimeTicks start = core()->GetTimeTicksNow(); ASSERT_NE(static_cast<MojoTimeTicks>(0), start) << "GetTimeTicksNow should return nonzero value"; - test::Sleep(test::DeadlineFromMilliseconds(15)); + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(15)); const MojoTimeTicks finish = core()->GetTimeTicksNow(); // Allow for some fuzz in sleep. ASSERT_GE((finish - start), static_cast<MojoTimeTicks>(8000)) diff --git a/chromium/mojo/core/data_pipe_unittest.cc b/chromium/mojo/core/data_pipe_unittest.cc index 9ff3e1668bf..395e8d8fdac 100644 --- a/chromium/mojo/core/data_pipe_unittest.cc +++ b/chromium/mojo/core/data_pipe_unittest.cc @@ -16,7 +16,6 @@ #include "build/build_config.h" #include "mojo/core/embedder/embedder.h" #include "mojo/core/test/mojo_test_base.h" -#include "mojo/core/test_utils.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/functions.h" #include "mojo/public/c/system/message_pipe.h" @@ -45,6 +44,28 @@ const int kMultiprocessMaxIter = 5; // Capacity that will cause data pipe creation to fail. constexpr size_t kOversizedCapacity = std::numeric_limits<uint32_t>::max(); +// A timeout smaller than |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. +// Warning: This may lead to flakiness, but this is unavoidable if, e.g., you're +// trying to ensure that functions with timeouts are reasonably accurate. We +// want this to be as small as possible without causing too much flakiness. +base::TimeDelta EpsilonDeadline() { + const int64_t tiny_timeout = TestTimeouts::tiny_timeout().InMicroseconds(); +// Originally, our epsilon timeout was 10 ms, which was mostly fine but flaky on +// some Windows bots. I don't recall ever seeing flakes on other bots. At 30 ms +// tests seem reliable on Windows bots, but not at 25 ms. We'd like this timeout +// to be as small as possible (see the description in the .h file). +// +// Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN, +// etc.). Based on this, set it to (usually be) 30 ms on Windows and 20 ms +// elsewhere. +#if defined(OS_WIN) || defined(OS_ANDROID) + const int64_t deadline = (tiny_timeout * 3) / 10; +#else + const int64_t deadline = (tiny_timeout * 2) / 10; +#endif + return base::TimeDelta::FromMicroseconds(deadline); +} + // TODO(rockot): There are many uses of ASSERT where EXPECT would be more // appropriate. Fix this. @@ -948,7 +969,7 @@ TEST_F(DataPipeTest, AllOrNone) { if (num_bytes >= 10u * sizeof(int32_t)) break; - test::Sleep(test::EpsilonDeadline()); + base::PlatformThread::Sleep(EpsilonDeadline()); } ASSERT_EQ(10u * sizeof(int32_t), num_bytes); @@ -1154,7 +1175,7 @@ TEST_F(DataPipeTest, WriteCloseProducerRead) { if (num_bytes >= 2u * kTestDataSize) break; - test::Sleep(test::EpsilonDeadline()); + base::PlatformThread::Sleep(EpsilonDeadline()); } ASSERT_EQ(2u * kTestDataSize, num_bytes); @@ -1423,7 +1444,7 @@ TEST_F(DataPipeTest, TwoPhaseMoreInvalidArguments) { // Wait a bit, to make sure that if a signal were (incorrectly) sent, it'd // have time to propagate. - test::Sleep(test::EpsilonDeadline()); + base::PlatformThread::Sleep(EpsilonDeadline()); // Still no data. num_bytes = 1000u; @@ -1441,7 +1462,7 @@ TEST_F(DataPipeTest, TwoPhaseMoreInvalidArguments) { ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndWriteData(0u)); // Wait a bit (as above). - test::Sleep(test::EpsilonDeadline()); + base::PlatformThread::Sleep(EpsilonDeadline()); // Still no data. num_bytes = 1000u; @@ -1460,7 +1481,7 @@ TEST_F(DataPipeTest, TwoPhaseMoreInvalidArguments) { ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndWriteData(0u)); // Wait a bit (as above). - test::Sleep(test::EpsilonDeadline()); + base::PlatformThread::Sleep(EpsilonDeadline()); // Still no data. num_bytes = 1000u; @@ -1705,7 +1726,7 @@ bool ReadAllData(MojoHandle consumer, if (num_bytes == 0) { if (expect_empty) { // Expect no more data. - test::Sleep(test::TinyDeadline()); + base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); MojoReadDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_READ_DATA_FLAG_QUERY; diff --git a/chromium/mojo/core/embedder/embedder.cc b/chromium/mojo/core/embedder/embedder.cc index 3a4e3172938..29b6d058e91 100644 --- a/chromium/mojo/core/embedder/embedder.cc +++ b/chromium/mojo/core/embedder/embedder.cc @@ -5,10 +5,14 @@ #include "mojo/core/embedder/embedder.h" #include <stdint.h> +#include <atomic> #include <utility> +#include "base/feature_list.h" #include "base/memory/ref_counted.h" #include "base/task_runner.h" +#include "build/build_config.h" +#include "mojo/core/channel.h" #include "mojo/core/configuration.h" #include "mojo/core/core.h" #include "mojo/core/entrypoints.h" @@ -18,6 +22,21 @@ namespace mojo { namespace core { +namespace { +#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC) +const base::Feature kMojoPosixUseWritev{"MojoPosixUseWritev", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif +} // namespace + +// InitFeatures will be called as soon as the base::FeatureList is initialized. +void InitFeatures() { +#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MAC) + Channel::set_posix_use_writev( + base::FeatureList::IsEnabled(kMojoPosixUseWritev)); +#endif +} + void Init(const Configuration& configuration) { internal::g_configuration = configuration; InitializeCore(); diff --git a/chromium/mojo/core/embedder/embedder.h b/chromium/mojo/core/embedder/embedder.h index 575b301468d..56b8bb6c9d5 100644 --- a/chromium/mojo/core/embedder/embedder.h +++ b/chromium/mojo/core/embedder/embedder.h @@ -38,6 +38,14 @@ COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) void Init(); COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner(); +// InitFeatures will be called as soon as the base::FeatureList is initialized. +// NOTE: This is temporarily necessary because of how Mojo is started with +// respect to base::FeatureList. +// +// TODO(rockot): Remove once a long term solution is in place for using +// base::Features inside of Mojo. +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) void InitFeatures(); + } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/embedder/scoped_ipc_support.cc b/chromium/mojo/core/embedder/scoped_ipc_support.cc index 4bfbc9e0775..0fbf446eca9 100644 --- a/chromium/mojo/core/embedder/scoped_ipc_support.cc +++ b/chromium/mojo/core/embedder/scoped_ipc_support.cc @@ -5,7 +5,7 @@ #include "mojo/core/embedder/scoped_ipc_support.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread_restrictions.h" #include "mojo/core/core.h" diff --git a/chromium/mojo/core/embedder_unittest.cc b/chromium/mojo/core/embedder_unittest.cc index 5d85b367e2c..7f24ea6b159 100644 --- a/chromium/mojo/core/embedder_unittest.cc +++ b/chromium/mojo/core/embedder_unittest.cc @@ -30,7 +30,6 @@ #include "mojo/core/core.h" #include "mojo/core/shared_buffer_dispatcher.h" #include "mojo/core/test/mojo_test_base.h" -#include "mojo/core/test_utils.h" #include "mojo/public/c/system/core.h" #include "mojo/public/cpp/system/handle.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -47,7 +46,7 @@ MojoResult CreateSharedBufferFromRegion(T&& region, MojoHandle* handle) { scoped_refptr<SharedBufferDispatcher> buffer; MojoResult result = SharedBufferDispatcher::CreateFromPlatformSharedMemoryRegion( - T::TakeHandleForSerialization(std::move(region)), &buffer); + T::TakeHandleForSerialization(std::forward<T>(region)), &buffer); if (result != MOJO_RESULT_OK) return result; diff --git a/chromium/mojo/core/invitation_unittest.cc b/chromium/mojo/core/invitation_unittest.cc index 684bbfe2a41..c88c20bf83a 100644 --- a/chromium/mojo/core/invitation_unittest.cc +++ b/chromium/mojo/core/invitation_unittest.cc @@ -18,7 +18,7 @@ #include "base/process/process.h" #include "base/run_loop.h" #include "base/synchronization/lock.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/multiprocess_test.h" #include "base/test/task_environment.h" #include "base/threading/sequenced_task_runner_handle.h" diff --git a/chromium/mojo/core/message_pipe_dispatcher.cc b/chromium/mojo/core/message_pipe_dispatcher.cc index e17fdabc2d3..980d01ceccc 100644 --- a/chromium/mojo/core/message_pipe_dispatcher.cc +++ b/chromium/mojo/core/message_pipe_dispatcher.cc @@ -199,42 +199,51 @@ MojoResult MessagePipeDispatcher::ReadMessage( } MojoResult MessagePipeDispatcher::SetQuota(MojoQuotaType type, uint64_t limit) { - base::AutoLock lock(signal_lock_); - - switch (type) { - case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH: - if (limit == MOJO_QUOTA_LIMIT_NONE) - receive_queue_length_limit_.reset(); - else - receive_queue_length_limit_ = limit; - break; - - case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE: - if (limit == MOJO_QUOTA_LIMIT_NONE) - receive_queue_memory_size_limit_.reset(); - else - receive_queue_memory_size_limit_ = limit; - break; - - case MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT: - if (limit == MOJO_QUOTA_LIMIT_NONE) { - unread_message_count_limit_.reset(); - node_controller_->node()->SetAcknowledgeRequestInterval(port_, 0); - } else { - unread_message_count_limit_ = limit; - // Setting the acknowledge request interval for the port to half the - // unread quota limit, means the ack roundtrip has half the window to - // catch up with sent messages. In other words, if the producer is - // producing messages at a steady rate of limit/2 packets per message - // round trip or lower, the quota limit won't be exceeded. This is - // assuming the consumer is consuming messages at the same rate. - node_controller_->node()->SetAcknowledgeRequestInterval( - port_, (limit + 1) / 2); - } - break; + base::Optional<uint64_t> new_ack_request_interval; + { + base::AutoLock lock(signal_lock_); + switch (type) { + case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH: + if (limit == MOJO_QUOTA_LIMIT_NONE) + receive_queue_length_limit_.reset(); + else + receive_queue_length_limit_ = limit; + break; + + case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE: + if (limit == MOJO_QUOTA_LIMIT_NONE) + receive_queue_memory_size_limit_.reset(); + else + receive_queue_memory_size_limit_ = limit; + break; + + case MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT: + if (limit == MOJO_QUOTA_LIMIT_NONE) { + unread_message_count_limit_.reset(); + new_ack_request_interval = 0; + } else { + unread_message_count_limit_ = limit; + // Setting the acknowledge request interval for the port to half the + // unread quota limit, means the ack roundtrip has half the window to + // catch up with sent messages. In other words, if the producer is + // producing messages at a steady rate of limit/2 packets per message + // round trip or lower, the quota limit won't be exceeded. This is + // assuming the consumer is consuming messages at the same rate. + new_ack_request_interval = (limit + 1) / 2; + } + break; + + default: + return MOJO_RESULT_INVALID_ARGUMENT; + } + } - default: - return MOJO_RESULT_INVALID_ARGUMENT; + if (new_ack_request_interval.has_value()) { + // NOTE: It is not safe to call into SetAcknowledgeRequestInterval while + // holding a `signal_lock_`, as it may re-enter this object when the peer is + // in the same process. + node_controller_->node()->SetAcknowledgeRequestInterval( + port_, *new_ack_request_interval); } return MOJO_RESULT_OK; diff --git a/chromium/mojo/core/message_pipe_perftest.cc b/chromium/mojo/core/message_pipe_perftest.cc index af832586393..f7ae32e345c 100644 --- a/chromium/mojo/core/message_pipe_perftest.cc +++ b/chromium/mojo/core/message_pipe_perftest.cc @@ -9,7 +9,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/check_op.h" #include "base/macros.h" #include "base/strings/stringprintf.h" @@ -19,7 +19,6 @@ #include "mojo/core/handle_signals_state.h" #include "mojo/core/test/mojo_test_base.h" #include "mojo/core/test/test_utils.h" -#include "mojo/core/test_utils.h" #include "mojo/public/c/system/functions.h" #include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/mojo/core/message_pipe_unittest.cc b/chromium/mojo/core/message_pipe_unittest.cc index 20c71ff4549..9aa13f69f2f 100644 --- a/chromium/mojo/core/message_pipe_unittest.cc +++ b/chromium/mojo/core/message_pipe_unittest.cc @@ -14,7 +14,6 @@ #include "base/memory/ref_counted.h" #include "build/build_config.h" #include "mojo/core/test/mojo_test_base.h" -#include "mojo/core/test_utils.h" #include "mojo/public/c/system/core.h" #include "mojo/public/c/system/types.h" #include "mojo/public/cpp/system/message_pipe.h" diff --git a/chromium/mojo/core/multiprocess_message_pipe_unittest.cc b/chromium/mojo/core/multiprocess_message_pipe_unittest.cc index bdce6c7fd8a..26e2c80b0d2 100644 --- a/chromium/mojo/core/multiprocess_message_pipe_unittest.cc +++ b/chromium/mojo/core/multiprocess_message_pipe_unittest.cc @@ -26,7 +26,6 @@ #include "mojo/core/handle_signals_state.h" #include "mojo/core/test/mojo_test_base.h" #include "mojo/core/test/test_utils.h" -#include "mojo/core/test_utils.h" #include "mojo/public/c/system/buffer.h" #include "mojo/public/c/system/functions.h" #include "mojo/public/c/system/types.h" diff --git a/chromium/mojo/core/node_channel.cc b/chromium/mojo/core/node_channel.cc index 5db339ec802..e9cc9f23c2f 100644 --- a/chromium/mojo/core/node_channel.cc +++ b/chromium/mojo/core/node_channel.cc @@ -45,6 +45,8 @@ enum class MessageType : uint32_t { BIND_BROKER_HOST, }; +#pragma pack(push, 1) + struct Header { MessageType type; uint32_t padding; @@ -130,6 +132,8 @@ struct EventMessageFromRelayData { }; #endif +#pragma pack(pop) + template <typename DataType> Channel::MessagePtr CreateMessage(MessageType type, size_t payload_size, diff --git a/chromium/mojo/core/node_channel_fuzzer.cc b/chromium/mojo/core/node_channel_fuzzer.cc index 54fe757e0de..33188323f05 100644 --- a/chromium/mojo/core/node_channel_fuzzer.cc +++ b/chromium/mojo/core/node_channel_fuzzer.cc @@ -4,7 +4,7 @@ #include <stdint.h> -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/message_loop/message_pump_type.h" #include "base/no_destructor.h" #include "base/run_loop.h" diff --git a/chromium/mojo/core/node_channel_unittest.cc b/chromium/mojo/core/node_channel_unittest.cc index 13c46f13fea..e89f5a13bb0 100644 --- a/chromium/mojo/core/node_channel_unittest.cc +++ b/chromium/mojo/core/node_channel_unittest.cc @@ -4,7 +4,7 @@ #include "mojo/core/node_channel.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/memory/scoped_refptr.h" #include "base/message_loop/message_pump_type.h" #include "base/test/task_environment.h" diff --git a/chromium/mojo/core/node_controller.cc b/chromium/mojo/core/node_controller.cc index 029bd350b08..823a4619efa 100644 --- a/chromium/mojo/core/node_controller.cc +++ b/chromium/mojo/core/node_controller.cc @@ -21,7 +21,6 @@ #include "mojo/core/broker.h" #include "mojo/core/broker_host.h" #include "mojo/core/configuration.h" -#include "mojo/core/core.h" #include "mojo/core/request_context.h" #include "mojo/core/user_message_impl.h" #include "mojo/public/cpp/platform/named_platform_channel.h" @@ -146,10 +145,8 @@ class ThreadDestructionObserver NodeController::~NodeController() = default; -NodeController::NodeController(Core* core) - : core_(core), - name_(GetRandomNodeName()), - node_(new ports::Node(name_, this)) { +NodeController::NodeController() + : name_(GetRandomNodeName()), node_(new ports::Node(name_, this)) { DVLOG(1) << "Initializing node " << name_; } @@ -588,10 +585,17 @@ void NodeController::AddPeer(const ports::NodeName& name, } } -void NodeController::DropPeer(const ports::NodeName& name, +void NodeController::DropPeer(const ports::NodeName& node_name, NodeChannel* channel) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); + // NOTE: Either the `peers_` erasure or the `pending_invitations_` erasure + // below, if executed, may drop the last reference to the named NodeChannel + // and thus result in its deletion. The passed `node_name` argument may be + // owned by that same NodeChannel, so we make a copy of it here to avoid + // potentially unsafe references further below. + ports::NodeName name = node_name; + { base::AutoLock lock(peers_lock_); auto it = peers_.find(name); diff --git a/chromium/mojo/core/node_controller.h b/chromium/mojo/core/node_controller.h index f35237f4eb5..b0ddd290973 100644 --- a/chromium/mojo/core/node_controller.h +++ b/chromium/mojo/core/node_controller.h @@ -52,11 +52,10 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, }; // |core| owns and out-lives us. - explicit NodeController(Core* core); + NodeController(); ~NodeController() override; const ports::NodeName& name() const { return name_; } - Core* core() const { return core_; } ports::Node* node() const { return node_.get(); } scoped_refptr<base::SingleThreadTaskRunner> io_task_runner() const { return io_task_runner_; @@ -135,6 +134,8 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, base::span<const unsigned char> data); static void DeserializeMessageAsEventForFuzzer(Channel::MessagePtr message); + scoped_refptr<NodeChannel> GetBrokerChannel(); + private: friend Core; @@ -176,7 +177,6 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name); scoped_refptr<NodeChannel> GetInviterChannel(); - scoped_refptr<NodeChannel> GetBrokerChannel(); void AddPeer(const ports::NodeName& name, scoped_refptr<NodeChannel> channel, @@ -254,7 +254,6 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, void ForceDisconnectProcessForTestingOnIOThread(base::ProcessId process_id); // These are safe to access from any thread as long as the Node is alive. - Core* const core_; const ports::NodeName name_; const std::unique_ptr<ports::Node> node_; scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; @@ -319,7 +318,7 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, AtomicFlag shutdown_callback_flag_; // All other fields below must only be accessed on the I/O thread, i.e., the - // thread on which core_->io_task_runner() runs tasks. + // thread on which `io_task_runner_` runs tasks. // Channels to invitees during handshake. NodeMap pending_invitations_; diff --git a/chromium/mojo/core/node_controller_unittest.cc b/chromium/mojo/core/node_controller_unittest.cc new file mode 100644 index 00000000000..316e1623763 --- /dev/null +++ b/chromium/mojo/core/node_controller_unittest.cc @@ -0,0 +1,42 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "mojo/core/core.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/public/c/system/types.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace core { +namespace { + +using NodeControllerTest = test::MojoTestBase; + +TEST_F(NodeControllerTest, AcceptInvitationFailure) { + // Spawn a child process that will send an invalid AcceptInvitation + // NodeChannel message. This is a regression test for + // https://crbug.com/1162198. + RunTestClient("SendInvalidAcceptInvitation", + [&](MojoHandle h) { WriteMessage(h, "hi"); }); +} + +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(SendInvalidAcceptInvitation, + NodeControllerTest, + h) { + // A little communication to synchronize against Mojo bringup. By the time + // this read completes, we must have an internal NodeController with the + // parent test process connected as its broker. + EXPECT_EQ("hi", ReadMessage(h)); + + // Send an unexpected AcceptInvitation message to the parent process. This + // exercises the regression code path in the parent process. + NodeController* controller = Core::Get()->GetNodeController(); + scoped_refptr<NodeChannel> channel = controller->GetBrokerChannel(); + channel->AcceptInvitation(ports::NodeName{0, 0}, ports::NodeName{0, 0}); +} + +} // namespace +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/core/ports/node.cc b/chromium/mojo/core/ports/node.cc index 5b2558a0465..7907b38d4e6 100644 --- a/chromium/mojo/core/ports/node.cc +++ b/chromium/mojo/core/ports/node.cc @@ -920,7 +920,8 @@ int Node::OnUserMessageReadAckRequest( } } - delegate_->ForwardEvent(peer_node_name, std::move(event_to_send)); + if (event_to_send) + delegate_->ForwardEvent(peer_node_name, std::move(event_to_send)); return OK; } diff --git a/chromium/mojo/core/test/BUILD.gn b/chromium/mojo/core/test/BUILD.gn index 9d0d150771d..d3025e4c1c2 100644 --- a/chromium/mojo/core/test/BUILD.gn +++ b/chromium/mojo/core/test/BUILD.gn @@ -4,13 +4,6 @@ import("//third_party/protobuf/proto_library.gni") -# This file depends on the legacy global sources assignment filter. It should -# be converted to check target platform before assigning source files to the -# sources variable. Remove this import and set_sources_assignment_filter call -# when the file has been converted. See https://crbug.com/1018739 for details. -import("//build/config/deprecated_default_sources_assignment_filter.gni") -set_sources_assignment_filter(deprecated_default_sources_assignment_filter) - static_library("test_support") { testonly = true sources = [ @@ -19,7 +12,6 @@ static_library("test_support") { "mojo_test_base.cc", "mojo_test_base.h", "test_utils.h", - "test_utils_win.cc", ] if (!is_ios) { @@ -29,6 +21,10 @@ static_library("test_support") { ] } + if (is_win) { + sources += [ "test_utils_win.cc" ] + } + if (is_fuchsia || is_posix) { sources += [ "test_utils.cc" ] } diff --git a/chromium/mojo/core/test_utils.cc b/chromium/mojo/core/test_utils.cc deleted file mode 100644 index acbfca018af..00000000000 --- a/chromium/mojo/core/test_utils.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/core/test_utils.h" - -#include <stdint.h> - -#include <limits> - -#include "base/check_op.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "build/build_config.h" - -namespace mojo { -namespace core { -namespace test { - -MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds) { - return static_cast<MojoDeadline>(milliseconds) * 1000; -} - -MojoDeadline EpsilonDeadline() { -// Originally, our epsilon timeout was 10 ms, which was mostly fine but flaky on -// some Windows bots. I don't recall ever seeing flakes on other bots. At 30 ms -// tests seem reliable on Windows bots, but not at 25 ms. We'd like this timeout -// to be as small as possible (see the description in the .h file). -// -// Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN, -// etc.). Based on this, set it to (usually be) 30 ms on Windows and 20 ms -// elsewhere. -#if defined(OS_WIN) || defined(OS_ANDROID) - return (TinyDeadline() * 3) / 10; -#else - return (TinyDeadline() * 2) / 10; -#endif -} - -MojoDeadline TinyDeadline() { - return static_cast<MojoDeadline>( - TestTimeouts::tiny_timeout().InMicroseconds()); -} - -MojoDeadline ActionDeadline() { - return static_cast<MojoDeadline>( - TestTimeouts::action_timeout().InMicroseconds()); -} - -void Sleep(MojoDeadline deadline) { - CHECK_LE(deadline, - static_cast<MojoDeadline>(std::numeric_limits<int64_t>::max())); - base::PlatformThread::Sleep( - base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline))); -} - -Stopwatch::Stopwatch() = default; - -Stopwatch::~Stopwatch() = default; - -void Stopwatch::Start() { - start_time_ = base::TimeTicks::Now(); -} - -MojoDeadline Stopwatch::Elapsed() { - int64_t result = (base::TimeTicks::Now() - start_time_).InMicroseconds(); - // |DCHECK_GE|, not |CHECK_GE|, since this may be performance-important. - DCHECK_GE(result, 0); - return static_cast<MojoDeadline>(result); -} - -} // namespace test -} // namespace core -} // namespace mojo diff --git a/chromium/mojo/core/test_utils.h b/chromium/mojo/core/test_utils.h deleted file mode 100644 index 98ac51e182d..00000000000 --- a/chromium/mojo/core/test_utils.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_CORE_TEST_UTILS_H_ -#define MOJO_CORE_TEST_UTILS_H_ - -#include "base/macros.h" -#include "base/time/time.h" -#include "mojo/public/c/system/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace core { -namespace test { - -MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds); - -// A timeout smaller than |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. -// Warning: This may lead to flakiness, but this is unavoidable if, e.g., you're -// trying to ensure that functions with timeouts are reasonably accurate. We -// want this to be as small as possible without causing too much flakiness. -MojoDeadline EpsilonDeadline(); - -// |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. (Expect this to be on -// the order of 100 ms.) -MojoDeadline TinyDeadline(); - -// |TestTimeouts::action_timeout()|, as a |MojoDeadline|. (Expect this to be on -// the order of 10 s.) -MojoDeadline ActionDeadline(); - -// Sleeps for at least the specified duration. -void Sleep(MojoDeadline deadline); - -// Stopwatch ------------------------------------------------------------------- - -// A simple "stopwatch" for measuring time elapsed from a given starting point. -class Stopwatch { - public: - Stopwatch(); - ~Stopwatch(); - - void Start(); - // Returns the amount of time elapsed since the last call to |Start()| (in - // microseconds). - MojoDeadline Elapsed(); - - private: - base::TimeTicks start_time_; - - DISALLOW_COPY_AND_ASSIGN(Stopwatch); -}; - -} // namespace test -} // namespace core -} // namespace mojo - -#endif // MOJO_CORE_TEST_UTILS_H_ diff --git a/chromium/mojo/core/trap_unittest.cc b/chromium/mojo/core/trap_unittest.cc index 8ff1f74a6ad..65595510930 100644 --- a/chromium/mojo/core/trap_unittest.cc +++ b/chromium/mojo/core/trap_unittest.cc @@ -14,7 +14,7 @@ #include "base/memory/ptr_util.h" #include "base/rand_util.h" #include "base/synchronization/waitable_event.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" #include "base/time/time.h" diff --git a/chromium/mojo/public/DEPS b/chromium/mojo/public/DEPS index 185539038b8..8bed1ad0e7c 100644 --- a/chromium/mojo/public/DEPS +++ b/chromium/mojo/public/DEPS @@ -9,4 +9,5 @@ include_rules = [ # Temporary until mojom [Native] is gone. "+ipc/ipc_param_traits.h", + "+third_party/perfetto/protos/perfetto/trace/track_event", ] diff --git a/chromium/mojo/public/c/system/types.h b/chromium/mojo/public/c/system/types.h index bb6504343a7..56ffd789916 100644 --- a/chromium/mojo/public/c/system/types.h +++ b/chromium/mojo/public/c/system/types.h @@ -124,18 +124,6 @@ const MojoResult MOJO_RESULT_SHOULD_WAIT = 17; #define MOJO_RESULT_SHOULD_WAIT ((MojoResult)17) #endif -// |MojoDeadline|: Used to specify deadlines (timeouts), in microseconds (except -// for |MOJO_DEADLINE_INDEFINITE|). -// |MOJO_DEADLINE_INDEFINITE| - Used to indicate "forever". - -typedef uint64_t MojoDeadline; - -#ifdef __cplusplus -const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast<MojoDeadline>(-1); -#else -#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline)-1) -#endif - // Flags passed to |MojoInitialize()| via |MojoInitializeOptions|. typedef uint32_t MojoInitializeFlags; diff --git a/chromium/mojo/public/cpp/base/BUILD.gn b/chromium/mojo/public/cpp/base/BUILD.gn index 9aaf1e0d796..ee0eb158939 100644 --- a/chromium/mojo/public/cpp/base/BUILD.gn +++ b/chromium/mojo/public/cpp/base/BUILD.gn @@ -90,6 +90,8 @@ component("shared_typemap_traits") { "generic_pending_receiver_mojom_traits.h", "read_only_buffer_mojom_traits.cc", "read_only_buffer_mojom_traits.h", + "read_only_file_mojom_traits.cc", + "read_only_file_mojom_traits.h", "shared_memory_mojom_traits.cc", "shared_memory_mojom_traits.h", "time_mojom_traits.cc", diff --git a/chromium/mojo/public/cpp/base/big_buffer.h b/chromium/mojo/public/cpp/base/big_buffer.h index ef13e1e39b0..81d0b994d41 100644 --- a/chromium/mojo/public/cpp/base/big_buffer.h +++ b/chromium/mojo/public/cpp/base/big_buffer.h @@ -176,7 +176,7 @@ class COMPONENT_EXPORT(MOJO_BASE) BigBufferView { static BigBufferView CreateInvalidForTest(); private: - BigBuffer::StorageType storage_type_; + BigBuffer::StorageType storage_type_ = BigBuffer::StorageType::kBytes; base::span<const uint8_t> bytes_; base::Optional<internal::BigBufferSharedMemoryRegion> shared_memory_; diff --git a/chromium/mojo/public/cpp/base/file_unittest.cc b/chromium/mojo/public/cpp/base/file_unittest.cc index e1db729c752..90d08eb97b2 100644 --- a/chromium/mojo/public/cpp/base/file_unittest.cc +++ b/chromium/mojo/public/cpp/base/file_unittest.cc @@ -3,9 +3,13 @@ // found in the LICENSE file. #include "base/files/scoped_temp_dir.h" +#include "base/sync_socket.h" +#include "build/build_config.h" #include "mojo/public/cpp/base/file_mojom_traits.h" +#include "mojo/public/cpp/base/read_only_file_mojom_traits.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "mojo/public/mojom/base/file.mojom.h" +#include "mojo/public/mojom/base/read_only_file.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo_base { @@ -69,5 +73,97 @@ TEST(FileTest, InvalidFile) { EXPECT_FALSE(file_out.IsValid()); } +TEST(FileTest, ReadOnlyFile) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + base::File file( + temp_dir.GetPath().AppendASCII("test_file.txt"), + base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ); + const base::StringPiece test_content = + "A test string to be stored in a test file"; + file.WriteAtCurrentPos(test_content.data(), + base::checked_cast<int>(test_content.size())); + file.Close(); + + base::File readonly(temp_dir.GetPath().AppendASCII("test_file.txt"), + base::File::FLAG_OPEN | base::File::FLAG_READ); + + base::File file_out; + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::ReadOnlyFile>( + &readonly, &file_out)); + std::vector<char> content(test_content.size()); + ASSERT_TRUE(file_out.IsValid()); + ASSERT_FALSE(file_out.async()); + ASSERT_EQ(static_cast<int>(test_content.size()), + file_out.Read(0, content.data(), + base::checked_cast<int>(test_content.size()))); + EXPECT_EQ(test_content, + base::StringPiece(content.data(), test_content.size())); +} + +// This dies only if we can interrogate the underlying platform handle. +#if defined(OS_WIN) || defined(OS_POSIX) || defined(OS_FUCHSIA) +#if !defined(OS_NACL) && !defined(OS_AIX) +TEST(FileTest, ReadOnlyFileDeath) { +#if defined(OFFICIAL_BUILD) + const char kReadOnlyFileCheckFailedRegex[] = ""; +#else + const char kReadOnlyFileCheckFailedRegex[] = "Check failed: IsReadOnlyFile"; +#endif + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + base::File file( + temp_dir.GetPath().AppendASCII("test_file.txt"), + base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ); + const base::StringPiece test_content = + "A test string to be stored in a test file"; + file.WriteAtCurrentPos(test_content.data(), + base::checked_cast<int>(test_content.size())); + file.Close(); + + base::File writable( + temp_dir.GetPath().AppendASCII("test_file.txt"), + base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); + + base::File file_out; + EXPECT_DEATH_IF_SUPPORTED( + mojo::test::SerializeAndDeserialize<mojom::ReadOnlyFile>(&writable, + &file_out), + kReadOnlyFileCheckFailedRegex); +} +#endif // !defined(OS_NACL) && !defined(OS_AIX) +#endif // defined(OS_WIN) || defined(OS_POSIX) || defined(OS_FUCHSIA) + +// This should work on all platforms. This check might be relaxed in which case +// this test can be removed. +#if DCHECK_IS_ON() +TEST(FileTest, NonPhysicalFileDeath) { +#if defined(OFFICIAL_BUILD) + const char kPhysicalFileCheckFailedRegex[] = ""; +#else + const char kPhysicalFileCheckFailedRegex[] = "Check failed: IsPhysicalFile"; +#endif + + base::SyncSocket sync_a; + base::SyncSocket sync_b; + ASSERT_TRUE(base::SyncSocket::CreatePair(&sync_a, &sync_b)); + base::File file_pipe_a(sync_a.Take()); + base::File file_pipe_b(sync_b.Take()); + + base::File file_out; + EXPECT_DEATH_IF_SUPPORTED( + mojo::test::SerializeAndDeserialize<mojom::ReadOnlyFile>(&file_pipe_a, + &file_out), + kPhysicalFileCheckFailedRegex); + EXPECT_DEATH_IF_SUPPORTED( + mojo::test::SerializeAndDeserialize<mojom::ReadOnlyFile>(&file_pipe_b, + &file_out), + kPhysicalFileCheckFailedRegex); +} +#endif // DCHECK_IS_ON() + } // namespace file_unittest } // namespace mojo_base diff --git a/chromium/mojo/public/cpp/base/read_only_file_mojom_traits.cc b/chromium/mojo/public/cpp/base/read_only_file_mojom_traits.cc new file mode 100644 index 00000000000..190e659d16e --- /dev/null +++ b/chromium/mojo/public/cpp/base/read_only_file_mojom_traits.cc @@ -0,0 +1,101 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/base/read_only_file_mojom_traits.h" + +#include "base/files/file.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> +#endif +#if defined(OS_WIN) +#include <windows.h> +#include <winternl.h> +#endif + +namespace mojo { +namespace { +#if defined(OS_WIN) +bool GetGrantedAccess(HANDLE handle, DWORD* flags) { + static const auto nt_query_object = + reinterpret_cast<decltype(&NtQueryObject)>( + GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryObject")); + PUBLIC_OBJECT_BASIC_INFORMATION info; + ULONG len = sizeof(info); + ULONG consumed = 0; + auto ret = + nt_query_object(handle, ObjectBasicInformation, &info, len, &consumed); + if (ret) + return false; + *flags = info.GrantedAccess; + return true; +} +#endif // defined(OS_WIN) + +// True if the underlying handle is only readable. Where possible this excludes +// deletion, writing, truncation, append and other operations that might modify +// the underlying file. False if we can tell that the file could be modified. +// On platforms where we cannot test the handle, always returns true. +bool IsReadOnlyFile(base::File& file) { + bool is_readonly = true; +#if defined(OS_WIN) + DWORD flags = 0; + if (!GetGrantedAccess(file.GetPlatformFile(), &flags)) + return false; + + // Cannot use GENERIC_WRITE as that includes SYNCHRONIZE. + // This is ~(all the writable permissions). + is_readonly = + !(flags & (FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | + FILE_WRITE_EA | WRITE_DAC | WRITE_OWNER | DELETE)); +#elif defined(OS_FUCHSIA) || \ + (defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_AIX)) + is_readonly = + (fcntl(file.GetPlatformFile(), F_GETFL) & O_ACCMODE) == O_RDONLY; +#endif + return is_readonly; +} + +bool IsPhysicalFile(base::File& file) { +#if defined(OS_WIN) + // Verify if this is a real file (not a socket/pipe etc.). + DWORD type = GetFileType(file.GetPlatformFile()); + return type == FILE_TYPE_DISK; +#else + // This may block but in practice this is unlikely for already opened + // physical files. + struct stat st; + if (fstat(file.GetPlatformFile(), &st) != 0) + return false; + return S_ISREG(st.st_mode); +#endif +} + +} // namespace + +mojo::PlatformHandle StructTraits<mojo_base::mojom::ReadOnlyFileDataView, + base::File>::fd(base::File& file) { + DCHECK(file.IsValid()); + // For now we require real files as on some platforms it is too difficult to + // be sure that more general handles cannot be written or made writable. This + // could be relaxed if an interface needs readonly pipes. This check may block + // so cannot be enabled in release builds. + DCHECK(IsPhysicalFile(file)); + CHECK(IsReadOnlyFile(file)); + + return mojo::PlatformHandle( + base::ScopedPlatformFile(file.TakePlatformFile())); +} + +bool StructTraits<mojo_base::mojom::ReadOnlyFileDataView, base::File>::Read( + mojo_base::mojom::ReadOnlyFileDataView data, + base::File* file) { + *file = base::File(data.TakeFd().TakePlatformFile(), data.async()); + return true; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/base/read_only_file_mojom_traits.h b/chromium/mojo/public/cpp/base/read_only_file_mojom_traits.h new file mode 100644 index 00000000000..025ee7f2374 --- /dev/null +++ b/chromium/mojo/public/cpp/base/read_only_file_mojom_traits.h @@ -0,0 +1,29 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BASE_READ_ONLY_FILE_MOJOM_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BASE_READ_ONLY_FILE_MOJOM_TRAITS_H_ + +#include "base/component_export.h" +#include "base/files/file.h" +#include "mojo/public/mojom/base/read_only_file.mojom-shared.h" + +namespace mojo { + +template <> +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) + StructTraits<mojo_base::mojom::ReadOnlyFileDataView, base::File> { + static bool IsNull(const base::File& file) { return !file.IsValid(); } + + static void SetToNull(base::File* file) { *file = base::File(); } + + static mojo::PlatformHandle fd(base::File& file); + static bool async(base::File& file) { return file.async(); } + static bool Read(mojo_base::mojom::ReadOnlyFileDataView data, + base::File* file); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BASE_READ_ONLY_FILE_MOJOM_TRAITS_H_ diff --git a/chromium/mojo/public/cpp/base/values_mojom_traits.cc b/chromium/mojo/public/cpp/base/values_mojom_traits.cc index 9a1d84c604a..69bd8167377 100644 --- a/chromium/mojo/public/cpp/base/values_mojom_traits.cc +++ b/chromium/mojo/public/cpp/base/values_mojom_traits.cc @@ -20,8 +20,8 @@ bool StructTraits<mojo_base::mojom::DictionaryValueDataView, base::Value>::Read( dict_storage.reserve(view.size()); for (size_t i = 0; i < view.size(); ++i) { base::StringPiece key; - auto value = std::make_unique<base::Value>(); - if (!view.keys().Read(i, &key) || !view.values().Read(i, value.get())) + base::Value value; + if (!view.keys().Read(i, &key) || !view.values().Read(i, &value)) return false; dict_storage.emplace_back(key.as_string(), std::move(value)); } diff --git a/chromium/mojo/public/cpp/base/values_unittest.cc b/chromium/mojo/public/cpp/base/values_unittest.cc index 75d975470bc..5624238916a 100644 --- a/chromium/mojo/public/cpp/base/values_unittest.cc +++ b/chromium/mojo/public/cpp/base/values_unittest.cc @@ -85,17 +85,14 @@ TEST(ValuesStructTraitsTest, DictionaryValue) { // move-only types and initializer lists don't mix. Initializer lists can't be // modified: thus it's not possible to move. std::vector<base::Value::DictStorage::value_type> storage; - storage.emplace_back("null", std::make_unique<base::Value>()); - storage.emplace_back("bool", std::make_unique<base::Value>(false)); - storage.emplace_back("int", std::make_unique<base::Value>(0)); - storage.emplace_back("double", std::make_unique<base::Value>(0.0)); - storage.emplace_back("string", std::make_unique<base::Value>("0")); - storage.emplace_back( - "binary", std::make_unique<base::Value>(base::Value::BlobStorage({0}))); - storage.emplace_back( - "dictionary", std::make_unique<base::Value>(base::Value::DictStorage())); - storage.emplace_back( - "list", std::make_unique<base::Value>(base::Value::ListStorage())); + storage.emplace_back("null", base::Value()); + storage.emplace_back("bool", false); + storage.emplace_back("int", 0); + storage.emplace_back("double", 0.0); + storage.emplace_back("string", "0"); + storage.emplace_back("binary", base::Value::BlobStorage({0})); + storage.emplace_back("dictionary", base::Value::DictStorage()); + storage.emplace_back("list", base::Value::ListStorage()); base::Value in(base::Value::DictStorage(std::move(storage))); base::Value out; diff --git a/chromium/mojo/public/cpp/bindings/BUILD.gn b/chromium/mojo/public/cpp/bindings/BUILD.gn index fb725f77058..77efb306eaf 100644 --- a/chromium/mojo/public/cpp/bindings/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/BUILD.gn @@ -2,13 +2,14 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//base/trace_event/tracing.gni") import("//build/buildflag_header.gni") import("//build/config/compiler/compiler.gni") import("//build/config/nacl/config.gni") import("//tools/ipc_fuzzer/ipc_fuzzer.gni") declare_args() { - enable_mojo_tracing = false + enable_mojo_tracing = extended_tracing_enabled # enable_random_mojo_delays starts a task runner that periodically pauses # random Mojo bindings and later resumes them, in order to test whether parts @@ -79,7 +80,6 @@ component("bindings_base") { "lib/serialization_util.h", "lib/string_serialization.h", "lib/template_util.h", - "lib/tracing_helper.h", "lib/unserialized_message_context.cc", "lib/unserialized_message_context.h", "lib/validate_params.h", diff --git a/chromium/mojo/public/cpp/bindings/README.md b/chromium/mojo/public/cpp/bindings/README.md index a15cf13632f..9eff612fb6e 100644 --- a/chromium/mojo/public/cpp/bindings/README.md +++ b/chromium/mojo/public/cpp/bindings/README.md @@ -168,7 +168,7 @@ defines a `BindNewPipeAndPassReceiver` method: ``` cpp mojo::Remote<sample::mojom::Logger> logger; -auto receiver = logger.BindNewPipeAndPassReceiver()); +auto receiver = logger.BindNewPipeAndPassReceiver(); ``` This second snippet is equivalent to the first one. diff --git a/chromium/mojo/public/cpp/bindings/associated_binding.h b/chromium/mojo/public/cpp/bindings/associated_binding.h index a0b6eeadc21..62f12b16564 100644 --- a/chromium/mojo/public/cpp/bindings/associated_binding.h +++ b/chromium/mojo/public/cpp/bindings/associated_binding.h @@ -149,9 +149,29 @@ class AssociatedBinding : public AssociatedBindingBase { return old_impl; } + // Acquires a callback which may be run to report the currently dispatching + // message as bad and close this binding. Note that this is only legal to + // call from directly within stack frame of a message dispatch, but the + // returned callback may be called exactly once any time thereafter to report + // the message as bad. |GetBadMessageCallback()| may only be called once per + // message, and the returned callback must be run on the same sequence to + // which this binding is bound. + ReportBadMessageCallback GetBadMessageCallback() { + return base::BindOnce( + [](ReportBadMessageCallback inner_callback, + base::WeakPtr<AssociatedBinding> binding, const std::string& error) { + std::move(inner_callback).Run(error); + if (binding) + binding->Close(); + }, + mojo::GetBadMessageCallback(), weak_ptr_factory_.GetWeakPtr()); + } + private: typename Interface::template Stub_<ImplRefTraits> stub_; + base::WeakPtrFactory<AssociatedBinding> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(AssociatedBinding); }; diff --git a/chromium/mojo/public/cpp/bindings/associated_receiver.h b/chromium/mojo/public/cpp/bindings/associated_receiver.h index f03b37dd27a..2d9ed6a09d5 100644 --- a/chromium/mojo/public/cpp/bindings/associated_receiver.h +++ b/chromium/mojo/public/cpp/bindings/associated_receiver.h @@ -241,6 +241,26 @@ class AssociatedReceiver { return binding_.SwapImplForTesting(new_impl); } + // Reports the currently dispatching message as bad and resets this receiver. + // Note that this is only legal to call from within the stack frame of a + // message dispatch. If you need to do asynchronous work before determining + // the legitimacy of a message, use GetBadMessageCallback() and retain its + // result until ready to invoke or discard it. + void ReportBadMessage(const std::string& error) { + GetBadMessageCallback().Run(error); + } + + // Acquires a callback which may be run to report the currently dispatching + // message as bad and reset this receiver. Note that this is only legal to + // call from directly within stack frame of a message dispatch, but the + // returned callback may be called exactly once any time thereafter to report + // the message as bad. |GetBadMessageCallback()| may only be called once per + // message, and the returned callback must be run on the same sequence to + // which this Receiver is bound. + ReportBadMessageCallback GetBadMessageCallback() { + return binding_.GetBadMessageCallback(); + } + private: // TODO(https://crbug.com/875030): Move AssociatedBinding details into this // class. diff --git a/chromium/mojo/public/cpp/bindings/connector.h b/chromium/mojo/public/cpp/bindings/connector.h index 1616f1bf023..905829cdfb1 100644 --- a/chromium/mojo/public/cpp/bindings/connector.h +++ b/chromium/mojo/public/cpp/bindings/connector.h @@ -85,7 +85,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) Connector : public MessageReceiver { Connector(ScopedMessagePipeHandle message_pipe, ConnectorConfig config, scoped_refptr<base::SequencedTaskRunner> runner, - const char* heap_profiler_tag = "unknown interface"); + const char* interface_name = "unknown interface"); ~Connector() override; // Sets outgoing serialization mode. @@ -306,7 +306,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) Connector : public MessageReceiver { // The tag used to track heap allocations that originated from a Watcher // notification. - const char* heap_profiler_tag_ = "unknown interface"; + const char* interface_name_ = "unknown interface"; // A cached pointer to the RunLoopNestingObserver for the thread on which this // Connector was created. diff --git a/chromium/mojo/public/cpp/bindings/lib/connector.cc b/chromium/mojo/public/cpp/bindings/lib/connector.cc index 93ea25007ec..2766f8d65c3 100644 --- a/chromium/mojo/public/cpp/bindings/lib/connector.cc +++ b/chromium/mojo/public/cpp/bindings/lib/connector.cc @@ -21,14 +21,15 @@ #include "base/task/current_thread.h" #include "base/threading/sequence_local_storage_slot.h" #include "base/trace_event/trace_event.h" +#include "base/trace_event/typed_macros.h" #include "mojo/public/c/system/quota.h" #include "mojo/public/cpp/bindings/features.h" #include "mojo/public/cpp/bindings/lib/may_auto_lock.h" #include "mojo/public/cpp/bindings/lib/message_quota_checker.h" -#include "mojo/public/cpp/bindings/lib/tracing_helper.h" #include "mojo/public/cpp/bindings/mojo_buildflags.h" #include "mojo/public/cpp/bindings/sync_handle_watcher.h" #include "mojo/public/cpp/system/wait.h" +#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h" #if defined(ENABLE_IPC_FUZZER) #include "mojo/public/cpp/bindings/message_dumper.h" @@ -145,14 +146,14 @@ void Connector::ActiveDispatchTracker::NotifyBeginNesting() { Connector::Connector(ScopedMessagePipeHandle message_pipe, ConnectorConfig config, scoped_refptr<base::SequencedTaskRunner> runner, - const char* heap_profiler_tag) + const char* interface_name) : message_pipe_(std::move(message_pipe)), task_runner_(std::move(runner)), error_(false), force_immediate_dispatch_(!EnableTaskPerMessage()), outgoing_serialization_mode_(g_default_outgoing_serialization_mode), incoming_serialization_mode_(g_default_incoming_serialization_mode), - heap_profiler_tag_(heap_profiler_tag), + interface_name_(interface_name), nesting_observer_(RunLoopNestingObserver::GetForThread()) { if (config == MULTI_THREADED_SEND) lock_.emplace(); @@ -410,7 +411,7 @@ void Connector::WaitToReadMore() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); handle_watcher_ = std::make_unique<SimpleWatcher>( FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, task_runner_, - heap_profiler_tag_); + interface_name_); MojoResult rv = handle_watcher_->Watch( message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, base::BindRepeating(&Connector::OnWatcherHandleReady, @@ -459,11 +460,11 @@ MojoResult Connector::ReadMessage(Message* message) { // was a problem extracting handles from it. We treat this essentially as // a bad IPC because we don't really have a better option. // - // We include |heap_profiler_tag_| in the error message since it usually + // We include |interface_name_| in the error message since it usually // (via this Connector's owner) provides useful information about which // binding interface is using this Connector. NotifyBadMessage(handle.get(), - std::string(heap_profiler_tag_) + + std::string(interface_name_) + "One or more handle attachments were invalid."); return MOJO_RESULT_ABORTED; } @@ -490,11 +491,15 @@ bool Connector::DispatchMessage(Message message) { } TRACE_EVENT_WITH_FLOW0("toplevel.flow", "mojo::Message Receive", - MANGLE_MESSAGE_ID(message.header()->trace_id), - TRACE_EVENT_FLAG_FLOW_IN); + message.header()->trace_id, TRACE_EVENT_FLAG_FLOW_IN); #if !BUILDFLAG(MOJO_TRACE_ENABLED) // This emits just full class name, and is inferior to mojo tracing. - TRACE_EVENT0("mojom", heap_profiler_tag_); + TRACE_EVENT("toplevel", "Connector::DispatchMessage", + [this](perfetto::EventContext ctx) { + ctx.event() + ->set_chrome_mojo_event_info() + ->set_watcher_notify_interface_tag(interface_name_); + }); #endif if (connection_group_) diff --git a/chromium/mojo/public/cpp/bindings/lib/message.cc b/chromium/mojo/public/cpp/bindings/lib/message.cc index 8c489947606..d5403f933ce 100644 --- a/chromium/mojo/public/cpp/bindings/lib/message.cc +++ b/chromium/mojo/public/cpp/bindings/lib/message.cc @@ -20,9 +20,9 @@ #include "base/strings/stringprintf.h" #include "base/threading/sequence_local_storage_slot.h" #include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_id_helper.h" #include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" -#include "mojo/public/cpp/bindings/lib/tracing_helper.h" #include "mojo/public/cpp/bindings/lib/unserialized_message_context.h" namespace mojo { @@ -47,19 +47,6 @@ void AllocateHeaderFromBuffer(internal::Buffer* buffer, HeaderType** header) { (*header)->num_bytes = sizeof(HeaderType); } -uint32_t GetTraceId(void* object) { - // |object| is a pointer to some object, which we are going to use as - // a hopefully unique id for this message. - // Additionally xor it with a counter to protect against the situations when - // a new object is allocated with the same address. - // The counter alone is not sufficient because we also have to deal with - // different processes, and the counter is only process-unique. - static std::atomic<int> counter{0}; - uint64_t value = reinterpret_cast<intptr_t>(object); - return static_cast<uint32_t>(counter.fetch_add(1, std::memory_order_relaxed) ^ - (value >> 32) ^ ((value << 32) >> 32)); -} - void WriteMessageHeader(uint32_t name, uint32_t flags, uint32_t trace_id, @@ -96,15 +83,15 @@ void WriteMessageHeader(uint32_t name, void CreateSerializedMessageObject(uint32_t name, uint32_t flags, - uint32_t trace_id, size_t payload_size, size_t payload_interface_id_count, MojoCreateMessageFlags create_message_flags, std::vector<ScopedHandle>* handles, ScopedMessageHandle* out_handle, internal::Buffer* out_buffer) { - TRACE_EVENT_WITH_FLOW0("toplevel.flow", "mojo::Message Send", - MANGLE_MESSAGE_ID(trace_id), + uint32_t trace_id = + static_cast<uint32_t>(base::trace_event::GetNextGlobalTraceId()); + TRACE_EVENT_WITH_FLOW0("toplevel.flow", "mojo::Message Send", trace_id, TRACE_EVENT_FLAG_FLOW_OUT); ScopedMessageHandle handle; @@ -147,10 +134,10 @@ void SerializeUnserializedContext(MojoMessageHandle message, uintptr_t context_value) { auto* context = reinterpret_cast<internal::UnserializedMessageContext*>(context_value); - uint32_t trace_id = GetTraceId(context); + uint32_t trace_id = + static_cast<uint32_t>(base::trace_event::GetNextGlobalTraceId()); - TRACE_EVENT_WITH_FLOW0("toplevel.flow", "mojo::Message Send", - MANGLE_MESSAGE_ID(trace_id), + TRACE_EVENT_WITH_FLOW0("toplevel.flow", "mojo::Message Send", trace_id, TRACE_EVENT_FLAG_FLOW_OUT); void* buffer; @@ -244,7 +231,7 @@ Message::Message(uint32_t name, MojoCreateMessageFlags create_message_flags, std::vector<ScopedHandle>* handles) { CreateSerializedMessageObject( - name, flags, GetTraceId(this), payload_size, payload_interface_id_count, + name, flags, payload_size, payload_interface_id_count, create_message_flags, handles, &handle_, &payload_buffer_); transferable_ = true; serialized_ = true; diff --git a/chromium/mojo/public/cpp/bindings/lib/serialization.h b/chromium/mojo/public/cpp/bindings/lib/serialization.h index 1f5d7de89b5..7927d1d4092 100644 --- a/chromium/mojo/public/cpp/bindings/lib/serialization.h +++ b/chromium/mojo/public/cpp/bindings/lib/serialization.h @@ -63,7 +63,11 @@ struct MojomSerializationImplTraits< template <typename MojomType, typename UserType> mojo::Message SerializeAsMessageImpl(UserType* input) { SerializationContext context; - mojo::Message message(0, 0, 0, 0, nullptr); + // Note that this is only called by application code serializing a structure + // manually (e.g. for storage). As such we don't want Mojo's soft message size + // limits to be applied. + mojo::Message message(0, 0, 0, 0, MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE, + nullptr); typename MojomTypeTraits<MojomType>::Data::BufferWriter writer; MojomSerializationImplTraits<MojomType>::Serialize( *input, message.payload_buffer(), &writer, &context); diff --git a/chromium/mojo/public/cpp/bindings/lib/tracing_helper.h b/chromium/mojo/public/cpp/bindings/lib/tracing_helper.h deleted file mode 100644 index ebc5cc27e85..00000000000 --- a/chromium/mojo/public/cpp/bindings/lib/tracing_helper.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_TRACING_HELPER_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TRACING_HELPER_H_ - -#define MANGLE_MESSAGE_ID(id) (id ^ ::mojo::internal::kMojoMessageMangleMask) - -namespace mojo { -namespace internal { - -// Mojo message id is 32-bit, but for tracing we ensure that mojo messages -// don't collide with other trace events. -constexpr uint64_t kMojoMessageMangleMask = 0x655b2a8e8efdf27f; - -} // namespace internal -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_TRACING_HELPER_H_ diff --git a/chromium/mojo/public/cpp/bindings/pending_receiver.h b/chromium/mojo/public/cpp/bindings/pending_receiver.h index 07d300f424e..1df56ce9278 100644 --- a/chromium/mojo/public/cpp/bindings/pending_receiver.h +++ b/chromium/mojo/public/cpp/bindings/pending_receiver.h @@ -74,7 +74,7 @@ class PendingReceiver { Interface>)(T&&)>>::value>* = nullptr> PendingReceiver(T&& other) : PendingReceiver(PendingReceiverConverter<T>::template To<Interface>( - std::move(other))) {} + std::forward<T>(other))) {} #endif // !defined(OS_NACL) ~PendingReceiver() = default; diff --git a/chromium/mojo/public/cpp/bindings/receiver.h b/chromium/mojo/public/cpp/bindings/receiver.h index 6f8b9976581..e1357f64fa2 100644 --- a/chromium/mojo/public/cpp/bindings/receiver.h +++ b/chromium/mojo/public/cpp/bindings/receiver.h @@ -185,9 +185,9 @@ class Receiver { } // Sets the message filter to be notified of each incoming message before - // dispatch. If a filter returns |false| from Accept(), the message is not - // dispatched and the pipe is closed. Filters cannot be removed once added - // and only one can be set. + // dispatch. If a filter returns |false| from WillDispatch(), the message is + // not dispatched and the pipe is closed. Filters cannot be removed once + // added and only one can be set. void SetFilter(std::unique_ptr<MessageFilter> filter) { DCHECK(is_bound()); internal_state_.SetFilter(std::move(filter)); diff --git a/chromium/mojo/public/cpp/bindings/service_factory.cc b/chromium/mojo/public/cpp/bindings/service_factory.cc index 18f12eb3490..b083ac15839 100644 --- a/chromium/mojo/public/cpp/bindings/service_factory.cc +++ b/chromium/mojo/public/cpp/bindings/service_factory.cc @@ -5,33 +5,44 @@ #include "mojo/public/cpp/bindings/service_factory.h" #include "base/bind.h" +#include "base/stl_util.h" namespace mojo { +ServiceFactory::ServiceFactory() = default; + ServiceFactory::~ServiceFactory() = default; -bool ServiceFactory::MaybeRunService(mojo::GenericPendingReceiver* receiver) { - DCHECK(receiver->is_valid()); +bool ServiceFactory::CanRunService( + const GenericPendingReceiver& receiver) const { + DCHECK(receiver.is_valid()); + return base::Contains(constructors_, *receiver.interface_name()); +} + +bool ServiceFactory::RunService(GenericPendingReceiver receiver, + base::OnceClosure termination_callback) { + DCHECK(receiver.is_valid()); // We grab a weak handle to the receiver's message pipe first. If any function // accepts the receiver, we will tie its returned object's lifetime to the // connection state of that pipe. - MessagePipeHandle pipe = receiver->pipe(); - - for (const auto& callback : callbacks_) { - if (auto instance = callback.Run(receiver)) { - DCHECK(!receiver->is_valid()); - instance->WatchPipe( - pipe, base::BindOnce(&ServiceFactory::OnInstanceDisconnected, - weak_ptr_factory_.GetWeakPtr(), instance.get())); - instances_.insert(std::move(instance)); - return true; - } - - DCHECK(receiver->is_valid()); - } + MessagePipeHandle pipe = receiver.pipe(); - return false; + auto it = constructors_.find(*receiver.interface_name()); + if (it == constructors_.end()) + return false; + + auto instance = it->second.Run(std::move(receiver)); + auto disconnect_callback = + base::BindOnce(&ServiceFactory::OnInstanceDisconnected, + weak_ptr_factory_.GetWeakPtr(), instance.get()); + if (termination_callback) { + disconnect_callback = + std::move(disconnect_callback).Then(std::move(termination_callback)); + } + instance->WatchPipe(pipe, std::move(disconnect_callback)); + instances_.insert(std::move(instance)); + return true; } void ServiceFactory::OnInstanceDisconnected(InstanceHolderBase* instance) { @@ -48,21 +59,26 @@ void ServiceFactory::InstanceHolderBase::WatchPipe( base::OnceClosure disconnect_callback) { DCHECK(!disconnect_callback_); disconnect_callback_ = std::move(disconnect_callback); - watcher_.Watch(pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + watcher_.Watch(pipe, MOJO_HANDLE_SIGNAL_READABLE, MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, - base::BindRepeating(&InstanceHolderBase::OnDisconnect, + base::BindRepeating(&InstanceHolderBase::OnPipeSignaled, base::Unretained(this))); } -void ServiceFactory::InstanceHolderBase::OnDisconnect( +void ServiceFactory::InstanceHolderBase::OnPipeSignaled( MojoResult result, const HandleSignalsState& state) { - // It doesn't matter what the parameters are, since the only way the watcher - // can signal is if the peer was closed or the local pipe handle was closed. - // The callback always destroys |this| when run, so there's also no chance of - // this method running more than once. - DCHECK(disconnect_callback_); - std::move(disconnect_callback_).Run(); + // We only care about the two conditions below. FAILED_PRECONDITION implies + // that the peer was closed and all its sent messages have been read (and + // dispatched) locally, while CANCELLED implies that the service pipe was + // closed locally. In both cases, we run the callback which will delete + // `this` and, ultimately, the service instance itself. + if (result == MOJO_RESULT_FAILED_PRECONDITION || + result == MOJO_RESULT_CANCELLED) { + watcher_.Cancel(); + DCHECK(disconnect_callback_); + std::move(disconnect_callback_).Run(); + } } } // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/service_factory.h b/chromium/mojo/public/cpp/bindings/service_factory.h index e31441389cd..baaf683056b 100644 --- a/chromium/mojo/public/cpp/bindings/service_factory.h +++ b/chromium/mojo/public/cpp/bindings/service_factory.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_ #define MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_ +#include <map> #include <memory> #include "base/bind.h" @@ -35,11 +36,11 @@ struct ServiceFactoryTraits; // where |T| is any type (generally an implementation of |Interface|), and // |Interface| is a mojom interface. // -// Any time |MaybeRunService()| is called on the ServiceFactory, it will match -// the GenericPendingReceiver argument's interface type against the list of -// factories it has available, and if it finds a match it will run that function -// and retain ownership of the returned object until the corresponding receiver -// is disconnected. +// Any time |RunService()| is called on the ServiceFactory, it will match the +// GenericPendingReceiver argument's interface type against the list of +// factories it has available and run the corresponding function, retaining +// ownership of the returned object until the corresponding receiver is +// disconnected. // // Typical usage might look something like: // @@ -51,31 +52,54 @@ struct ServiceFactoryTraits; // return std::make_unique<bar::BarImpl>(std::move(receiver)); // } // -// void HandleServiceRequest(mojo::GenericPendingReceiver receiver) { -// static base::NoDestructor<mojo::ServiceFactory> factory{ -// RunFooService, -// RunBarService, -// }; +// void RegisterServices(mojo::ServiceFactory& services) { +// services.Add(RunFooService); +// services.Add(RunBarService); +// } // -// if (!factory->MaybeRunService(&receiver)) { -// // The receiver was for neither the Foo nor Bar service. Sad! -// LOG(ERROR) << "Unknown service: " << *receiver.interface_name(); +// void HandleServiceRequest(const mojo::ServiceFactory& factory, +// mojo::GenericPendingReceiver receiver) { +// if (factory.CanRunService(receiver)) { +// factory.RunService(std::move(receiver), base::NullCallback()); +// return; // } +// +// // The receiver was for neither the Foo nor Bar service. Sad! +// LOG(ERROR) << "Unknown service: " << *receiver.interface_name(); // } // class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory { public: - template <typename... Funcs> - explicit ServiceFactory(Funcs... fns) - : callbacks_({base::BindRepeating(&RunFunction<Funcs>, fns)...}) {} + ServiceFactory(); ~ServiceFactory(); - // Attempts to run a service supported by this factory. + // Adds a new service to the factory. The argument may be any function that + // accepts a single PendingReceiver<T> and returns a unique_ptr<T>, where T is + // a service interface (that is, a generated mojom interface class + // corresponding to some service's main interface.) + template <typename Func> + void Add(Func func) { + using Interface = typename internal::ServiceFactoryTraits<Func>::Interface; + constructors_[Interface::Name_] = + base::BindRepeating(&RunConstructor<Func>, func); + } + + // If `receiver` is references an interface matching a service known to this + // factory, this returns true. Otherwise it returns false. `receiver` MUST be + // valid. + bool CanRunService(const GenericPendingReceiver& receiver) const; + + // Consumes `receiver` and binds it to a new instance of the corresponding + // service, constructed using the service's registered function within this + // factory. + // + // `termination_callback`, if not null, will be invoked on the calling + // TaskRunner whenever the new service instance is eventually destroyed. // - // Returns |true| and consumes |*receiver| if it is a suitable match for some - // function known by the factory; otherwise returns |false| and leaves - // |*receiver| intact. - bool MaybeRunService(GenericPendingReceiver* receiver); + // If the service represented by `receiver` is not known to this factory, it + // is discarded and `termination_callback` is never run. + bool RunService(GenericPendingReceiver receiver, + base::OnceClosure termination_callback); private: class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InstanceHolderBase { @@ -87,7 +111,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory { base::OnceClosure disconnect_callback); private: - void OnDisconnect(MojoResult result, const HandleSignalsState& state); + void OnPipeSignaled(MojoResult result, const HandleSignalsState& state); SimpleWatcher watcher_; base::OnceClosure disconnect_callback_; @@ -109,23 +133,20 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory { }; template <typename Func> - static std::unique_ptr<InstanceHolderBase> RunFunction( + static std::unique_ptr<InstanceHolderBase> RunConstructor( Func fn, - GenericPendingReceiver* receiver) { + GenericPendingReceiver receiver) { using Interface = typename internal::ServiceFactoryTraits<Func>::Interface; - if (auto typed_receiver = receiver->As<Interface>()) { - return std::make_unique<InstanceHolder<Interface>>( - fn(std::move(typed_receiver))); - } - return nullptr; + return std::make_unique<InstanceHolder<Interface>>( + fn(receiver.As<Interface>())); } void OnInstanceDisconnected(InstanceHolderBase* instance); - using GenericCallback = + using Constructor = base::RepeatingCallback<std::unique_ptr<InstanceHolderBase>( - GenericPendingReceiver*)>; - const std::vector<GenericCallback> callbacks_; + GenericPendingReceiver)>; + std::map<std::string, Constructor> constructors_; base::flat_set<std::unique_ptr<InstanceHolderBase>, base::UniquePtrComparator> instances_; diff --git a/chromium/mojo/public/cpp/bindings/struct_ptr.h b/chromium/mojo/public/cpp/bindings/struct_ptr.h index e76c34188dd..af192d33831 100644 --- a/chromium/mojo/public/cpp/bindings/struct_ptr.h +++ b/chromium/mojo/public/cpp/bindings/struct_ptr.h @@ -171,7 +171,11 @@ class InlinedStructPtr { DCHECK(state_ == VALID); return &value_; } - Struct* get() const { return &value_; } + Struct* get() const { + if (state_ == NIL) + return nullptr; + return &value_; + } void Swap(InlinedStructPtr* other) { std::swap(value_, other->value_); diff --git a/chromium/mojo/public/cpp/bindings/tests/BUILD.gn b/chromium/mojo/public/cpp/bindings/tests/BUILD.gn index fec6e6b0441..459a4dc4f79 100644 --- a/chromium/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/tests/BUILD.gn @@ -28,6 +28,7 @@ source_set("tests") { "handle_passing_unittest.cc", "hash_unittest.cc", "idle_tracking_unittest.cc", + "interface_unittest.cc", "lazy_serialization_unittest.cc", "map_unittest.cc", "message_queue.cc", @@ -154,6 +155,7 @@ mojom("test_mojom") { "enum_headers_unittest.test-mojom", "flush_async_unittest.test-mojom", "idle_tracking_unittest.test-mojom", + "interface_unittest.test-mojom", "receiver_unittest.test-mojom", "remote_unittest.test-mojom", "service_factory_unittest.test-mojom", diff --git a/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h b/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h index 592e83c0e2b..2fe3211b64a 100644 --- a/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h +++ b/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h @@ -9,7 +9,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/task_runner.h" diff --git a/chromium/mojo/public/cpp/platform/BUILD.gn b/chromium/mojo/public/cpp/platform/BUILD.gn index 9f17405f4bf..6cd7355222f 100644 --- a/chromium/mojo/public/cpp/platform/BUILD.gn +++ b/chromium/mojo/public/cpp/platform/BUILD.gn @@ -4,13 +4,6 @@ import("//build/config/nacl/config.gni") -# This file depends on the legacy global sources assignment filter. It should -# be converted to check target platform before assigning source files to the -# sources variable. Remove this import and set_sources_assignment_filter call -# when the file has been converted. See https://crbug.com/1018739 for details. -import("//build/config/deprecated_default_sources_assignment_filter.gni") -set_sources_assignment_filter(deprecated_default_sources_assignment_filter) - component("platform") { output_name = "mojo_cpp_platform" @@ -22,7 +15,6 @@ component("platform") { ] sources = [ - "named_platform_channel_win.cc", "platform_channel.cc", "platform_channel_endpoint.cc", "platform_channel_server_endpoint.cc", @@ -47,14 +39,16 @@ component("platform") { sources += [ "named_platform_channel_mac.cc" ] } + if (is_win) { + sources += [ "named_platform_channel_win.cc" ] + } + if (is_fuchsia) { public_deps += [ "//third_party/fuchsia-sdk/sdk/pkg/fdio", "//third_party/fuchsia-sdk/sdk/pkg/zx", ] - } - - if (!is_fuchsia) { + } else { sources += [ "named_platform_channel.cc" ] public += [ "named_platform_channel.h" ] } diff --git a/chromium/mojo/public/cpp/system/platform_handle.cc b/chromium/mojo/public/cpp/system/platform_handle.cc index 64c50bb2cb8..3b04fd7a28c 100644 --- a/chromium/mojo/public/cpp/system/platform_handle.cc +++ b/chromium/mojo/public/cpp/system/platform_handle.cc @@ -14,19 +14,20 @@ namespace mojo { namespace { -uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) { +uint64_t ReleasePlatformHandleValueFromPlatformFile( + base::ScopedPlatformFile file) { #if defined(OS_WIN) - return reinterpret_cast<uint64_t>(file); + return reinterpret_cast<uint64_t>(file.Take()); #else - return static_cast<uint64_t>(file); + return static_cast<uint64_t>(file.release()); #endif } -base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) { +base::ScopedPlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) { #if defined(OS_WIN) - return reinterpret_cast<base::PlatformFile>(value); + return base::ScopedPlatformFile(reinterpret_cast<base::PlatformFile>(value)); #else - return static_cast<base::PlatformFile>(value); + return base::ScopedPlatformFile(static_cast<base::PlatformFile>(value)); #endif } @@ -198,12 +199,12 @@ PlatformHandle UnwrapPlatformHandle(ScopedHandle handle) { return PlatformHandle::FromMojoPlatformHandle(&platform_handle); } -// Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object. -ScopedHandle WrapPlatformFile(base::PlatformFile platform_file) { +ScopedHandle WrapPlatformFile(base::ScopedPlatformFile platform_file) { MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); platform_handle.type = kPlatformFileHandleType; - platform_handle.value = PlatformHandleValueFromPlatformFile(platform_file); + platform_handle.value = + ReleasePlatformHandleValueFromPlatformFile(std::move(platform_file)); MojoHandle mojo_handle; MojoResult result = @@ -213,7 +214,8 @@ ScopedHandle WrapPlatformFile(base::PlatformFile platform_file) { return ScopedHandle(Handle(mojo_handle)); } -MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file) { +MojoResult UnwrapPlatformFile(ScopedHandle handle, + base::ScopedPlatformFile* file) { MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(), @@ -222,7 +224,7 @@ MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file) { return result; if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { - *file = base::kInvalidPlatformFile; + *file = base::ScopedPlatformFile(); } else { CHECK_EQ(platform_handle.type, kPlatformFileHandleType); *file = PlatformFileFromPlatformHandleValue(platform_handle.value); diff --git a/chromium/mojo/public/cpp/system/platform_handle.h b/chromium/mojo/public/cpp/system/platform_handle.h index 106a8527254..676906ce5fa 100644 --- a/chromium/mojo/public/cpp/system/platform_handle.h +++ b/chromium/mojo/public/cpp/system/platform_handle.h @@ -13,7 +13,6 @@ #include <stdint.h> -#include "base/compiler_specific.h" #include "base/files/platform_file.h" #include "base/macros.h" #include "base/memory/read_only_shared_memory_region.h" @@ -27,53 +26,16 @@ #include "mojo/public/cpp/system/handle.h" #include "mojo/public/cpp/system/system_export.h" -#if defined(OS_WIN) -#include <windows.h> -#endif - namespace mojo { #if defined(OS_WIN) const MojoPlatformHandleType kPlatformFileHandleType = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; - -const MojoPlatformHandleType kPlatformSharedBufferHandleType = - MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; - -#elif defined(OS_FUCHSIA) -const MojoPlatformHandleType kPlatformFileHandleType = - MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; -const MojoPlatformHandleType kPlatformSharedBufferHandleType = - MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE; - -#elif defined(OS_POSIX) -const MojoPlatformHandleType kPlatformFileHandleType = - MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; - -#if defined(OS_MAC) -const MojoPlatformHandleType kPlatformSharedBufferHandleType = - MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; #else -const MojoPlatformHandleType kPlatformSharedBufferHandleType = +const MojoPlatformHandleType kPlatformFileHandleType = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; -#endif // defined(OS_MAC) - #endif // defined(OS_WIN) -// Used to specify the protection status of a base::SharedMemoryHandle memory -// handle wrapped or unwrapped by mojo::WrapSharedMemoryHandle or -// mojo::UnwrapSharedMemoryHandle below. See those functions for additional -// details. -enum class UnwrappedSharedMemoryHandleProtection { - // Indicates that the base::SharedMemoryHandle supports being mapped to - // writable memory regions. - kReadWrite, - - // Indicates that the base::SharedMemoryHandle supports being mapped only to - // read-only memory regions. - kReadOnly, -}; - // Wraps and unwraps base::subtle::PlatformSharedMemoryRegions. This should be // used only while transitioning from the legacy shared memory API. In new code // only base::*SharedMemoryRegion should be used instead. @@ -90,14 +52,15 @@ MOJO_CPP_SYSTEM_EXPORT ScopedHandle WrapPlatformHandle(PlatformHandle handle); // support library. MOJO_CPP_SYSTEM_EXPORT PlatformHandle UnwrapPlatformHandle(ScopedHandle handle); -// Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object. -// If |platform_file| is valid, this will return a valid handle. +// Wraps a ScopedPlatformFile as a Mojo handle. Takes ownership of the file +// object. If |platform_file| is valid, this will return a valid handle. MOJO_CPP_SYSTEM_EXPORT -ScopedHandle WrapPlatformFile(base::PlatformFile platform_file); +ScopedHandle WrapPlatformFile(base::ScopedPlatformFile platform_file); // Unwraps a PlatformFile from a Mojo handle. MOJO_CPP_SYSTEM_EXPORT -MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file); +MojoResult UnwrapPlatformFile(ScopedHandle handle, + base::ScopedPlatformFile* file); // Helpers for wrapping and unwrapping new base shared memory API primitives. // If the input |region| is valid for the Wrap* functions, they will always diff --git a/chromium/mojo/public/cpp/system/simple_watcher.cc b/chromium/mojo/public/cpp/system/simple_watcher.cc index f34b90e05fb..700fda474bf 100644 --- a/chromium/mojo/public/cpp/system/simple_watcher.cc +++ b/chromium/mojo/public/cpp/system/simple_watcher.cc @@ -13,7 +13,9 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/heap_profiler.h" #include "base/trace_event/trace_event.h" +#include "base/trace_event/typed_macros.h" #include "mojo/public/c/system/trap.h" +#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h" namespace mojo { @@ -32,9 +34,9 @@ class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> { MojoTriggerCondition condition, int watch_id, MojoResult* result, - const char* heap_profiler_tag) { + const char* handler_tag) { scoped_refptr<Context> context = - new Context(watcher, task_runner, watch_id, heap_profiler_tag); + new Context(watcher, task_runner, watch_id, handler_tag); // If MojoAddTrigger succeeds, it effectively assumes ownership of a // reference to |context|. In that case, this reference is balanced in @@ -70,11 +72,11 @@ class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> { Context(base::WeakPtr<SimpleWatcher> weak_watcher, scoped_refptr<base::SequencedTaskRunner> task_runner, int watch_id, - const char* heap_profiler_tag) + const char* handler_tag) : weak_watcher_(weak_watcher), task_runner_(task_runner), watch_id_(watch_id), - heap_profiler_tag_(heap_profiler_tag) {} + handler_tag_(handler_tag) {} ~Context() = default; @@ -92,10 +94,8 @@ class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> { weak_watcher_->OnHandleReady(watch_id_, result, state); } else { { - // Annotate the posted task with |heap_profiler_tag_| as the IPC - // interface. - base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash( - heap_profiler_tag_); + // Annotate the posted task with |handler_tag_| as the IPC interface. + base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(handler_tag_); task_runner_->PostTask( FROM_HERE, base::BindOnce(&SimpleWatcher::OnHandleReady, weak_watcher_, watch_id_, result, state)); @@ -106,7 +106,7 @@ class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> { const base::WeakPtr<SimpleWatcher> weak_watcher_; const scoped_refptr<base::SequencedTaskRunner> task_runner_; const int watch_id_; - const char* heap_profiler_tag_ = nullptr; + const char* handler_tag_ = nullptr; DISALLOW_COPY_AND_ASSIGN(Context); }; @@ -114,14 +114,13 @@ class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> { SimpleWatcher::SimpleWatcher(const base::Location& from_here, ArmingPolicy arming_policy, scoped_refptr<base::SequencedTaskRunner> runner, - const char* heap_profiler_tag) + const char* handler_tag) : arming_policy_(arming_policy), task_runner_(std::move(runner)), is_default_task_runner_(base::ThreadTaskRunnerHandle::IsSet() && task_runner_ == base::ThreadTaskRunnerHandle::Get()), - heap_profiler_tag_(heap_profiler_tag ? heap_profiler_tag - : from_here.file_name()) { + handler_tag_(handler_tag ? handler_tag : from_here.file_name()) { MojoResult rv = CreateTrap(&Context::CallNotify, &trap_handle_); DCHECK_EQ(MOJO_RESULT_OK, rv); DCHECK(task_runner_->RunsTasksInCurrentSequence()); @@ -152,7 +151,7 @@ MojoResult SimpleWatcher::Watch(Handle handle, MojoResult result = MOJO_RESULT_UNKNOWN; context_ = Context::Create(weak_factory_.GetWeakPtr(), task_runner_, trap_handle_.get(), handle_, signals, condition, - watch_id_, &result, heap_profiler_tag_); + watch_id_, &result, handler_tag_); if (!context_) { handle_.set_value(kInvalidHandleValue); callback_.Reset(); @@ -225,14 +224,17 @@ void SimpleWatcher::ArmOrNotify() { MojoResult ready_result; HandleSignalsState ready_state; MojoResult rv = Arm(&ready_result, &ready_state); - if (rv == MOJO_RESULT_OK) + + // NOTE: If the watched handle has been closed, the above call will result in + // MOJO_RESULT_NOT_FOUND. A MOJO_RESULT_CANCELLED notification will already + // have been posted to this object as a result, so there's nothing else to do. + if (rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND) return; DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv); { - // Annotate the posted task with |heap_profiler_tag_| as the IPC interface. - base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash( - heap_profiler_tag_); + // Annotate the posted task with |handler_tag_| as the IPC interface. + base::TaskAnnotator::ScopedSetIpcHash scoped_set_ipc_hash(handler_tag_); task_runner_->PostTask(FROM_HERE, base::BindOnce(&SimpleWatcher::OnHandleReady, weak_factory_.GetWeakPtr(), watch_id_, @@ -261,12 +263,16 @@ void SimpleWatcher::OnHandleReady(int watch_id, // NOTE: It's legal for |callback| to delete |this|. if (!callback.is_null()) { - TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_); + TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(handler_tag_); // Lot of janks caused are grouped to OnHandleReady tasks. This trace event - // helps identify the cause of janks. It is ok to pass |heap_profiler_tag_| + // helps identify the cause of janks. It is ok to pass |handler_tag_| // here since it is a string literal. - // TODO(927206): Consider renaming |heap_profiler_tag_|. - TRACE_EVENT0("toplevel", heap_profiler_tag_); + TRACE_EVENT("toplevel", "SimpleWatcher::OnHandleReady", + [this](perfetto::EventContext ctx) { + ctx.event() + ->set_chrome_mojo_event_info() + ->set_watcher_notify_interface_tag(handler_tag_); + }); base::WeakPtr<SimpleWatcher> weak_self = weak_factory_.GetWeakPtr(); callback.Run(result, state); diff --git a/chromium/mojo/public/cpp/system/simple_watcher.h b/chromium/mojo/public/cpp/system/simple_watcher.h index 24123bcb905..3ccec253e88 100644 --- a/chromium/mojo/public/cpp/system/simple_watcher.h +++ b/chromium/mojo/public/cpp/system/simple_watcher.h @@ -90,7 +90,7 @@ class MOJO_CPP_SYSTEM_EXPORT SimpleWatcher { ArmingPolicy arming_policy, scoped_refptr<base::SequencedTaskRunner> runner = base::SequencedTaskRunnerHandle::Get(), - const char* heap_profiler_tag = nullptr); + const char* handler_tag = nullptr); ~SimpleWatcher(); // Indicates if the SimpleWatcher is currently watching a handle. @@ -226,7 +226,7 @@ class MOJO_CPP_SYSTEM_EXPORT SimpleWatcher { // Tag used to ID memory allocations that originated from notifications in // this watcher. - const char* heap_profiler_tag_ = nullptr; + const char* handler_tag_ = nullptr; base::WeakPtrFactory<SimpleWatcher> weak_factory_{this}; diff --git a/chromium/mojo/public/cpp/test_support/lib/test_utils.cc b/chromium/mojo/public/cpp/test_support/lib/test_utils.cc index 062106db864..6fa00a537ba 100644 --- a/chromium/mojo/public/cpp/test_support/lib/test_utils.cc +++ b/chromium/mojo/public/cpp/test_support/lib/test_utils.cc @@ -10,7 +10,7 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "mojo/public/cpp/system/core.h" #include "mojo/public/cpp/system/wait.h" #include "mojo/public/cpp/test_support/test_support.h" diff --git a/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn b/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn index 404d9cf94f4..6eda7da6d8a 100644 --- a/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn +++ b/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/python.gni") import("../../../tools/bindings/mojom.gni") group("test_data_deps") { @@ -254,7 +255,8 @@ copy("validation_test_data") { outputs = [ "$root_gen_dir/layout_test_data/{{source_root_relative_dir}}/{{source_file_part}}" ] } -action_foreach("validation_test_data_list") { +# TODO(crbug.com/1112471): Get this to run cleanly under Python 3. +python2_action_foreach("validation_test_data_list") { testonly = true script = "//mojo/public/tools/bindings/gen_data_files_list.py" inputs = mojom_generator_sources diff --git a/chromium/mojo/public/js/BUILD.gn b/chromium/mojo/public/js/BUILD.gn index 47ee8536397..e3febd86e77 100644 --- a/chromium/mojo/public/js/BUILD.gn +++ b/chromium/mojo/public/js/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/python.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//third_party/closure_compiler/closure_args.gni") import("//third_party/closure_compiler/compile_js.gni") @@ -9,7 +10,8 @@ import("//tools/grit/grit_rule.gni") interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings" -action("bindings") { +# TODO(crbug.com/1112471): Get this to run cleanly under Python 3. +python2_action("bindings") { bindings_js_files = [ # This must be the first file in the list, because it initializes global # variable |mojo| that the others need to refer to. @@ -49,26 +51,71 @@ action("bindings") { deps = [ "//mojo/public/interfaces/bindings:bindings_js__generator" ] } -bindings_lite_sources = [ - "bindings_lite.js", - "interface_support.js", -] +template("concatenate_files") { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(target_name) { + script = "//mojo/public/tools/bindings/concatenate-files.py" + inputs = invoker.inputs + output = "$target_gen_dir/${invoker.output}" + outputs = [ output ] + args = rebase_path(inputs, root_build_dir) + + [ rebase_path(output, root_build_dir) ] + if (defined(invoker.deps)) { + deps = invoker.deps + } + } +} -bindings_lite_compiled_file = "$target_gen_dir/mojo_bindings_lite.js" +concatenate_files("generate_mojo_internal_js") { + inputs = [ + "mojo_internal_preamble.js.part", + "bindings_lite.js", + ] + output = "mojo_internal.js" +} + +concatenate_files("generate_interface_support_js") { + inputs = [ + "interface_support_preamble.js.part", + "interface_support.js", + ] + output = "interface_support.js" +} js_library("bindings_lite_sources") { - sources = [ "compile_preamble.js" ] + bindings_lite_sources + sources = [ + "$target_gen_dir/interface_support.js", + "$target_gen_dir/mojo_internal.js", + "compile_preamble.js", + ] deps = [ "//mojo/public/interfaces/bindings:bindings_js_library_for_compile" ] + extra_deps = [ + ":generate_interface_support_js", + ":generate_mojo_internal_js", + ] +} + +# All generated mojom JS modules consume the API defined by this module. The +# uncompiled source is simply a concatenation of the various pieces listed +# below. +concatenate_files("bindings_uncompiled_module") { + inputs = [ + "bindings_module_preamble.js.part", + "bindings_lite.js", + "$root_gen_dir/mojo/public/interfaces/bindings/interface_control_messages.mojom-lite.js", + "interface_support.js", + "bindings_uncompiled_module_export.js.part", + ] + output = "bindings_uncompiled.js" + deps = [ "//mojo/public/interfaces/bindings:bindings_js_library" ] } if (enable_mojom_closure_compile || enable_js_type_check) { js_binary("bindings_lite") { - outputs = [ bindings_lite_compiled_file ] + outputs = [ "$target_gen_dir/mojo_bindings_lite.js" ] + sources = [] deps = [ ":bindings_lite_sources" ] - externs_list = [ - "$externs_path/mojo_core.js", - "$externs_path/pending.js", - ] + externs_list = [ "$externs_path/mojo_core.js" ] closure_flags = strict_error_checking_closure_args + [ "compilation_level=ADVANCED_OPTIMIZATIONS", "language_in=ECMASCRIPT_2017", @@ -78,19 +125,61 @@ if (enable_mojom_closure_compile || enable_js_type_check) { "isolation_mode=IIFE", ] } + + concatenate_files("bindings_compiled_module") { + inputs = [ + "$target_gen_dir/mojo_bindings_lite.js", + "bindings_module_export.js.part", + ] + output = "bindings_compiled.js" + deps = [ ":bindings_lite" ] + } + + copy("bindings_module") { + sources = [ "$target_gen_dir/bindings_compiled.js" ] + outputs = [ "$target_gen_dir/bindings.js" ] + deps = [ ":bindings_compiled_module" ] + } } else { action("bindings_lite") { - all_sources = bindings_lite_sources + [ "$root_gen_dir/mojo/public/interfaces/bindings/interface_control_messages.mojom-lite.js" ] + inputs = [ + "$target_gen_dir/mojo_internal.js", + "$root_gen_dir/mojo/public/interfaces/bindings/interface_control_messages.mojom-lite-for-compile.js", + "$target_gen_dir/interface_support.js", + ] script = "//mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py" - sources = all_sources - outputs = [ bindings_lite_compiled_file ] - args = rebase_path(all_sources, root_build_dir) + - [ rebase_path(bindings_lite_compiled_file, root_build_dir) ] - deps = [ "//mojo/public/interfaces/bindings:bindings_js__generator" ] + sources = inputs + outputs = [ "$target_gen_dir/mojo_bindings_lite.js" ] + args = + rebase_path(inputs, root_build_dir) + + [ rebase_path("$target_gen_dir/mojo_bindings_lite.js", root_build_dir) ] + deps = [ + ":generate_interface_support_js", + ":generate_mojo_internal_js", + "//mojo/public/interfaces/bindings:bindings_js__generator", + ] } + + copy("bindings_module") { + 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.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") { + sources = [ "$target_gen_dir/bindings_uncompiled.js" ] + extra_deps = [ ":bindings_uncompiled_module" ] } grit("resources") { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + run_under_python2 = true source = "mojo_bindings_resources.grd" outputs = [ @@ -100,14 +189,10 @@ grit("resources") { "mojo_bindings_resources.pak", ] - grit_flags = [ - "-E", - "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir), - ] - deps = [ ":bindings", ":bindings_lite", + ":bindings_module", "//mojo/public/mojom/base:base_js", ] } diff --git a/chromium/mojo/public/js/bindings_lite.js b/chromium/mojo/public/js/bindings_lite.js index d2521fc30b2..1821540fda8 100644 --- a/chromium/mojo/public/js/bindings_lite.js +++ b/chromium/mojo/public/js/bindings_lite.js @@ -1,33 +1,6 @@ // 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'; - -goog.provide('mojo.internal'); - -// "self" is always defined as opposed to "this", which isn't defined in -// modules, or "window", which isn't defined in workers. -/** @const {!Object} */ -mojo.internal.globalScope = self; - -/** - * This is effectively the same as goog.provide, but it's made available under - * the mojo.internal namespace to avoid potential collisions in certain - * compilation environments. - * - * @param {string} namespace - * @export - */ -mojo.internal.exportModule = function(namespace) { - let current = mojo.internal.globalScope; - const parts = namespace.split('.'); - - for (let part; parts.length && (part = parts.shift());) { - if (!current[part]) - current[part] = {}; - current = current[part]; - } -}; /** @const {number} */ mojo.internal.kArrayHeaderSize = 8; @@ -85,7 +58,7 @@ mojo.internal.align = function(size, alignment) { /** * @param {!DataView} dataView * @param {number} byteOffset - * @param {number} value + * @param {number|bigint} value */ mojo.internal.setInt64 = function(dataView, byteOffset, value) { if (mojo.internal.kHostLittleEndian) { @@ -93,11 +66,12 @@ mojo.internal.setInt64 = function(dataView, byteOffset, value) { byteOffset, Number(BigInt(value) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); dataView.setInt32( - byteOffset + 4, Number(BigInt(value) >> BigInt(32)), + byteOffset + 4, + Number((BigInt(value) >> BigInt(32)) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); } else { dataView.setInt32( - byteOffset, Number(BigInt(value) >> BigInt(32)), + byteOffset, Number((BigInt(value) >> BigInt(32)) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); dataView.setUint32( byteOffset + 4, Number(BigInt(value) & BigInt(0xffffffff)), @@ -108,7 +82,7 @@ mojo.internal.setInt64 = function(dataView, byteOffset, value) { /** * @param {!DataView} dataView * @param {number} byteOffset - * @param {number} value + * @param {number|bigint} value */ mojo.internal.setUint64 = function(dataView, byteOffset, value) { if (mojo.internal.kHostLittleEndian) { @@ -116,11 +90,12 @@ mojo.internal.setUint64 = function(dataView, byteOffset, value) { byteOffset, Number(BigInt(value) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); dataView.setUint32( - byteOffset + 4, Number(BigInt(value) >> BigInt(32)), + byteOffset + 4, + Number((BigInt(value) >> BigInt(32)) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); } else { dataView.setUint32( - byteOffset, Number(BigInt(value) >> BigInt(32)), + byteOffset, Number((BigInt(value) >> BigInt(32)) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); dataView.setUint32( byteOffset + 4, Number(BigInt(value) & BigInt(0xffffffff)), @@ -131,7 +106,7 @@ mojo.internal.setUint64 = function(dataView, byteOffset, value) { /** * @param {!DataView} dataView * @param {number} byteOffset - * @return {number} + * @return {bigint} */ mojo.internal.getInt64 = function(dataView, byteOffset) { let low, high; @@ -142,12 +117,24 @@ mojo.internal.getInt64 = function(dataView, byteOffset) { low = dataView.getUint32(byteOffset + 4, mojo.internal.kHostLittleEndian); high = dataView.getInt32(byteOffset, mojo.internal.kHostLittleEndian); } - const value = (BigInt(high) << BigInt(32)) | BigInt(low); - if (value <= BigInt(Number.MAX_SAFE_INTEGER) && - value >= BigInt(Number.MIN_SAFE_INTEGER)) { - return Number(value); + return (BigInt(high) << BigInt(32)) | BigInt(low); +}; + +/** + * @param {!DataView} dataView + * @param {number} byteOffset + * @return {bigint} + */ +mojo.internal.getUint64 = function(dataView, byteOffset) { + let low, high; + if (mojo.internal.kHostLittleEndian) { + low = dataView.getUint32(byteOffset, mojo.internal.kHostLittleEndian); + high = dataView.getUint32(byteOffset + 4, mojo.internal.kHostLittleEndian); + } else { + low = dataView.getUint32(byteOffset + 4, mojo.internal.kHostLittleEndian); + high = dataView.getUint32(byteOffset, mojo.internal.kHostLittleEndian); } - return value; + return (BigInt(high) << BigInt(32)) | BigInt(low); }; /** @@ -249,26 +236,6 @@ mojo.internal.computeTotalArraySize = function(arraySpec, value) { return totalSize; }; -/** - * @param {!DataView} dataView - * @param {number} byteOffset - * @return {number} - */ -mojo.internal.getUint64 = function(dataView, byteOffset) { - let low, high; - if (mojo.internal.kHostLittleEndian) { - low = dataView.getUint32(byteOffset, mojo.internal.kHostLittleEndian); - high = dataView.getUint32(byteOffset + 4, mojo.internal.kHostLittleEndian); - } else { - low = dataView.getUint32(byteOffset + 4, mojo.internal.kHostLittleEndian); - high = dataView.getUint32(byteOffset, mojo.internal.kHostLittleEndian); - } - const value = (BigInt(high) << BigInt(32)) | BigInt(low); - if (value <= BigInt(Number.MAX_SAFE_INTEGER)) - return Number(value); - return value; -}; - /** Owns an outgoing message buffer and facilitates serialization. */ mojo.internal.Message = class { /** @@ -683,7 +650,9 @@ mojo.internal.Decoder = class { const relativeOffset = this.decodeUint64(offset); if (relativeOffset == 0) return 0; - return this.data_.byteOffset + offset + relativeOffset; + if (relativeOffset > BigInt(Number.MAX_SAFE_INTEGER)) + throw new Error('Mesage offset too large'); + return this.data_.byteOffset + offset + Number(relativeOffset); } /** diff --git a/chromium/mojo/public/js/bindings_module_export.js.part b/chromium/mojo/public/js/bindings_module_export.js.part new file mode 100644 index 00000000000..455896d4ab1 --- /dev/null +++ b/chromium/mojo/public/js/bindings_module_export.js.part @@ -0,0 +1,3 @@ +const mojoTmp = self['mojo']; +delete self['mojo']; +export const mojo = mojoTmp; diff --git a/chromium/mojo/public/js/bindings_module_preamble.js.part b/chromium/mojo/public/js/bindings_module_preamble.js.part new file mode 100644 index 00000000000..1d00fd82e02 --- /dev/null +++ b/chromium/mojo/public/js/bindings_module_preamble.js.part @@ -0,0 +1,4 @@ +const mojo = { + internal: { interfaceSupport: {} }, + interfaceControl: {} +}; diff --git a/chromium/mojo/public/js/bindings_uncompiled_module_export.js.part b/chromium/mojo/public/js/bindings_uncompiled_module_export.js.part new file mode 100644 index 00000000000..712a9e34ea4 --- /dev/null +++ b/chromium/mojo/public/js/bindings_uncompiled_module_export.js.part @@ -0,0 +1 @@ +export {mojo}; diff --git a/chromium/mojo/public/js/interface_support.js b/chromium/mojo/public/js/interface_support.js index 6d95bb8ce7a..1810c790e0c 100644 --- a/chromium/mojo/public/js/interface_support.js +++ b/chromium/mojo/public/js/interface_support.js @@ -1,14 +1,6 @@ // 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'; - -goog.require('mojo.interfaceControl.RUN_MESSAGE_ID'); -goog.require('mojo.interfaceControl.RunResponseMessageParamsSpec'); -goog.require('mojo.internal'); - -goog.provide('mojo.internal.interfaceSupport'); - /** * Handles incoming interface control messages on a remote or router endpoint. diff --git a/chromium/mojo/public/js/interface_support_preamble.js.part b/chromium/mojo/public/js/interface_support_preamble.js.part new file mode 100644 index 00000000000..bb98866e4c5 --- /dev/null +++ b/chromium/mojo/public/js/interface_support_preamble.js.part @@ -0,0 +1,8 @@ +'use strict'; + +goog.require('mojo.interfaceControl.RUN_MESSAGE_ID'); +goog.require('mojo.interfaceControl.RunResponseMessageParamsSpec'); +goog.require('mojo.internal'); + +goog.provide('mojo.internal.interfaceSupport'); + diff --git a/chromium/mojo/public/js/lib/interface_endpoint_client.js b/chromium/mojo/public/js/lib/interface_endpoint_client.js index 9b45b69b05e..392cb59d297 100644 --- a/chromium/mojo/public/js/lib/interface_endpoint_client.js +++ b/chromium/mojo/public/js/lib/interface_endpoint_client.js @@ -33,12 +33,17 @@ } InterfaceEndpointClient.prototype.initControllerIfNecessary_ = function() { + if (!this.handle_) { + return false; + } + if (this.controller_ || this.handle_.pendingAssociation()) { - return; + return true; } this.controller_ = this.handle_.groupController().attachEndpointClient( this.handle_, this); + return true; }; InterfaceEndpointClient.prototype.onAssociationEvent = function( @@ -84,7 +89,9 @@ return false; } - this.initControllerIfNecessary_(); + if (!this.initControllerIfNecessary_()) { + return false; + } return this.controller_.sendMessage(message); }; @@ -99,7 +106,9 @@ return Promise.reject(); } - this.initControllerIfNecessary_(); + if (!this.initControllerIfNecessary_()) { + return Promise.reject(Error('Endpoint has been closed')); + } // Reserve 0 in case we want it to convey special meaning in the future. var requestID = this.nextRequestID_++; diff --git a/chromium/mojo/public/js/mojo_bindings_resources.grd b/chromium/mojo/public/js/mojo_bindings_resources.grd index e86d260df3c..999a411ea9b 100644 --- a/chromium/mojo/public/js/mojo_bindings_resources.grd +++ b/chromium/mojo/public/js/mojo_bindings_resources.grd @@ -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,58 +18,89 @@ <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" + <include name="IDR_MOJO_BIG_BUFFER_MOJOM_WEBUI_JS" + file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/big_buffer.mojom-webui.js" use_base_dir="false" + resource_path="mojo/mojo/public/mojom/base/big_buffer.mojom-webui.js" type="BINDATA" /> - <include name="IDR_MOJO_FILE_MOJOM_LITE_JS" - file="${root_gen_dir}/mojo/public/mojom/base/file.mojom-lite.js" + <include name="IDR_MOJO_FILE_MOJOM_WEBUI_JS" + file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/file.mojom-webui.js" use_base_dir="false" + resource_path="mojo/mojo/public/mojom/base/file.mojom-webui.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_STRING16_MOJOM_WEBUI_JS" + file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/string16.mojom-webui.js" + use_base_dir="false" + resource_path="mojo/mojo/public/mojom/base/string16.mojom-webui.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_PROCESS_ID_MOJOM_WEBUI_JS" + file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/process_id.mojom-webui.js" + use_base_dir="false" + resource_path="mojo/mojo/public/mojom/base/process_id.mojom-webui.js" + type="BINDATA" /> <include name="IDR_MOJO_TIME_MOJOM_HTML" file="${root_gen_dir}/mojo/public/mojom/base/time.mojom.html" use_base_dir="false" - type="BINDATA" - compress="gzip" /> + resource_path="mojo/mojo/public/mojom/base/time.mojom.html" + type="BINDATA" /> <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" /> + type="BINDATA" /> + <include name="IDR_MOJO_TIME_MOJOM_WEBUI_JS" + file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/time.mojom-webui.js" + use_base_dir="false" + resource_path="mojo/mojo/public/mojom/base/time.mojom-webui.js" + type="BINDATA" /> </if> </includes> </release> diff --git a/chromium/mojo/public/js/mojo_internal_preamble.js.part b/chromium/mojo/public/js/mojo_internal_preamble.js.part new file mode 100644 index 00000000000..28a18b25150 --- /dev/null +++ b/chromium/mojo/public/js/mojo_internal_preamble.js.part @@ -0,0 +1,27 @@ +'use strict'; + +goog.provide('mojo.internal'); + +// "self" is always defined as opposed to "this", which isn't defined in +// modules, or "window", which isn't defined in workers. +/** @const {!Object} */ +mojo.internal.globalScope = self; + +/** + * This is effectively the same as goog.provide, but it's made available under + * the mojo.internal namespace to avoid potential collisions in certain + * compilation environments. + * + * @param {string} namespace + * @export + */ +mojo.internal.exportModule = function(namespace) { + let current = mojo.internal.globalScope; + const parts = namespace.split('.'); + + for (let part; parts.length && (part = parts.shift());) { + if (!current[part]) + current[part] = {}; + current = current[part]; + } +}; diff --git a/chromium/mojo/public/mojom/base/BUILD.gn b/chromium/mojo/public/mojom/base/BUILD.gn index 61bb72a6183..53acc61d85d 100644 --- a/chromium/mojo/public/mojom/base/BUILD.gn +++ b/chromium/mojo/public/mojom/base/BUILD.gn @@ -21,6 +21,7 @@ mojom_component("base") { "message_pump_type.mojom", "process_id.mojom", "read_only_buffer.mojom", + "read_only_file.mojom", "ref_counted_memory.mojom", "shared_memory.mojom", "string16.mojom", @@ -42,6 +43,8 @@ mojom_component("base") { enabled_features += [ "file_path_is_string" ] } + webui_module_path = "chrome://resources/mojo/mojo/public/mojom/base" + output_prefix = "mojo_base_mojom" macro_prefix = "MOJO_BASE_MOJOM" @@ -121,6 +124,24 @@ mojom_component("base") { { types = [ { + mojom = "mojo_base.mojom.ReadOnlyFile" + cpp = "::base::File" + move_only = true + nullable_is_same_type = true + force_serialize = true + }, + ] + traits_headers = + [ "//mojo/public/cpp/base/read_only_file_mojom_traits.h" ] + traits_public_deps = [ + "//base", + "//mojo/public/cpp/base", + "//mojo/public/cpp/base:shared_typemap_traits", + ] + }, + { + types = [ + { mojom = "mojo_base.mojom.GenericPendingReceiver" cpp = "::mojo::GenericPendingReceiver" move_only = true diff --git a/chromium/mojo/public/mojom/base/big_string.mojom b/chromium/mojo/public/mojom/base/big_string.mojom index c971caf5674..fd0a5c00690 100644 --- a/chromium/mojo/public/mojom/base/big_string.mojom +++ b/chromium/mojo/public/mojom/base/big_string.mojom @@ -14,6 +14,7 @@ import "mojo/public/mojom/base/big_buffer.mojom"; // an arbitrarily large amount of data (available memory permitting) without // negatively impacting IPC performance or hitting hard message size // boundaries. +[Stable] struct BigString { mojo_base.mojom.BigBuffer data; }; diff --git a/chromium/mojo/public/mojom/base/file.mojom b/chromium/mojo/public/mojom/base/file.mojom index 199af80a262..6c7f5043be0 100644 --- a/chromium/mojo/public/mojom/base/file.mojom +++ b/chromium/mojo/public/mojom/base/file.mojom @@ -5,6 +5,7 @@ module mojo_base.mojom; // Corresponds to |base::File| in base/files/file.h +[Stable] struct File { handle<platform> fd; bool async; diff --git a/chromium/mojo/public/mojom/base/process_id.mojom b/chromium/mojo/public/mojom/base/process_id.mojom index f3b17780a51..3a6e09f4242 100644 --- a/chromium/mojo/public/mojom/base/process_id.mojom +++ b/chromium/mojo/public/mojom/base/process_id.mojom @@ -4,6 +4,7 @@ module mojo_base.mojom; +[Stable] struct ProcessId { // This is the storage for the pid, on windows the pid is a DWORD and the // value can be DWORD_MAX which maps gracefully to uint32 while on linux diff --git a/chromium/mojo/public/mojom/base/read_only_file.mojom b/chromium/mojo/public/mojom/base/read_only_file.mojom new file mode 100644 index 00000000000..6f361ffe7f2 --- /dev/null +++ b/chromium/mojo/public/mojom/base/read_only_file.mojom @@ -0,0 +1,20 @@ +// 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 mojo_base.mojom; + +// Corresponds to |base::File| in base/files/file.h but, on most +// platforms, will not serialise handles which are writable. At +// present this only supports physically backed files, but this may be +// relaxed in future. +// +// SECURITY_NOTE: This type is an indication that a readonly handle can +// be provided. A sandboxed process should ensure that the handle cannot +// be made writable. This may not be possible on all platforms. +// +// See |file_mojom_traits.cc| for details. +struct ReadOnlyFile { + handle<platform> fd; + bool async; +}; diff --git a/chromium/mojo/public/mojom/base/time.mojom b/chromium/mojo/public/mojom/base/time.mojom index 64777e56754..5eb3fa5cee2 100644 --- a/chromium/mojo/public/mojom/base/time.mojom +++ b/chromium/mojo/public/mojom/base/time.mojom @@ -11,10 +11,12 @@ struct Time { int64 internal_value; }; +[Stable] struct TimeDelta { int64 microseconds; }; +[Stable] struct TimeTicks { // The internal value is expressed in terms of microseconds since a fixed but // intentionally unspecified epoch. diff --git a/chromium/mojo/public/mojom/base/token.mojom b/chromium/mojo/public/mojom/base/token.mojom index ac5f32c37b9..62f85b3f0c9 100644 --- a/chromium/mojo/public/mojom/base/token.mojom +++ b/chromium/mojo/public/mojom/base/token.mojom @@ -5,6 +5,7 @@ module mojo_base.mojom; // Corresponds to |base::Token| defined in base/token.h +[Stable] struct Token { uint64 high; uint64 low; diff --git a/chromium/mojo/public/mojom/base/values.mojom b/chromium/mojo/public/mojom/base/values.mojom index ea7c1fa590f..5ffe845af53 100644 --- a/chromium/mojo/public/mojom/base/values.mojom +++ b/chromium/mojo/public/mojom/base/values.mojom @@ -8,6 +8,7 @@ module mojo_base.mojom; // // One notable caveat is that Value supports arbitrary binary data, which JSON // does not support natively. +[Stable] union Value { // Null type placeholder. This field is never used. uint8 null_value; @@ -29,10 +30,12 @@ union Value { // declaration. Though both of these types are mapped to base::Value in C++, // the generated deserialization will guarantee that the method is only invoked // with a base::Value of the correct subtype. +[Stable] struct DictionaryValue { map<string, Value> storage; }; +[Stable] struct ListValue { array<Value> storage; }; diff --git a/chromium/mojo/public/tools/bindings/BUILD.gn b/chromium/mojo/public/tools/bindings/BUILD.gn index 8ba6e922a4b..f25c4afa8b0 100644 --- a/chromium/mojo/public/tools/bindings/BUILD.gn +++ b/chromium/mojo/public/tools/bindings/BUILD.gn @@ -2,10 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/python.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//third_party/jinja2/jinja2.gni") -action("precompile_templates") { +# TODO(crbug.com/1112471): Get this to run cleanly under Python 3. +python2_action("precompile_templates") { sources = mojom_generator_sources sources += [ "$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl", @@ -69,11 +71,16 @@ action("precompile_templates") { "$mojom_generator_root/generators/js_templates/fuzzing.tmpl", "$mojom_generator_root/generators/js_templates/interface_definition.tmpl", "$mojom_generator_root/generators/js_templates/lite/enum_definition.tmpl", + "$mojom_generator_root/generators/js_templates/lite/enum_definition_for_module.tmpl", "$mojom_generator_root/generators/js_templates/lite/interface_definition.tmpl", + "$mojom_generator_root/generators/js_templates/lite/interface_definition_for_module.tmpl", "$mojom_generator_root/generators/js_templates/lite/module_definition.tmpl", "$mojom_generator_root/generators/js_templates/lite/mojom-lite.js.tmpl", + "$mojom_generator_root/generators/js_templates/lite/mojom.m.js.tmpl", "$mojom_generator_root/generators/js_templates/lite/struct_definition.tmpl", + "$mojom_generator_root/generators/js_templates/lite/struct_definition_for_module.tmpl", "$mojom_generator_root/generators/js_templates/lite/union_definition.tmpl", + "$mojom_generator_root/generators/js_templates/lite/union_definition_for_module.tmpl", "$mojom_generator_root/generators/js_templates/module.amd.tmpl", "$mojom_generator_root/generators/js_templates/module_definition.tmpl", "$mojom_generator_root/generators/js_templates/struct_definition.tmpl", diff --git a/chromium/mojo/public/tools/bindings/README.md b/chromium/mojo/public/tools/bindings/README.md index 228a2d3355b..7ddbb79a6ff 100644 --- a/chromium/mojo/public/tools/bindings/README.md +++ b/chromium/mojo/public/tools/bindings/README.md @@ -418,6 +418,13 @@ interesting attributes supported today. strict presubmit check. See [Versioning](#Versioning) for more details on backward-compatibility constraints. +**`[Uuid=<UUID>]`** +: Specifies a UUID to be associated with a given interface. The UUID is + intended to remain stable across all changes to the interface definition, + including name changes. The value given for this attribute should be a + standard UUID string representation as specified by RFC 4122. New UUIDs can + be generated with common tools such as `uuidgen`. + **`[EnableIf=value]`** : The `EnableIf` attribute is used to conditionally enable definitions when the mojom is parsed. If the `mojom` target in the GN file does not include @@ -712,6 +719,24 @@ values and will need to deal with them gracefully. See [C++ Versioning Considerations](/mojo/public/cpp/bindings/README.md#Versioning-Considerations) for details. +### Renaming versioned structs +It's possible to rename versioned structs by using the `[RenamedFrom]` attribute. +RenamedFrom + +``` cpp +module asdf.mojom; + +// Old version: +[Stable] +struct OldStruct { +}; + +// New version: +[Stable, RenamedFrom="asdf.mojom.OldStruct"] +struct NewStruct { +}; +``` + ## Grammar Reference Below is the (BNF-ish) context-free grammar of the Mojom language: diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl index f8b28e79c3b..a1a87fb125d 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -16,6 +16,10 @@ class {{export_attribute}} {{interface.name}} : public {{interface.name}}InterfaceBase { public: static const char Name_[]; +{%- if interface.uuid %} + static constexpr base::Token Uuid_{ {{interface.uuid[0]}}ULL, + {{interface.uuid[1]}}ULL }; +{%- endif %} static constexpr uint32_t Version_ = {{interface.version}}; static constexpr bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %}; static constexpr bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %}; diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index d8d10b3c672..c3ca8506c11 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -32,6 +32,9 @@ std::move(p_{{param.name}}) {#--- Begin #} const char {{class_name}}::Name_[] = "{{module_namespace}}.{{class_name}}"; +{%- if interface.uuid %} +constexpr base::Token {{class_name}}::Uuid_; +{%- endif %} {#--- Constants #} {%- for constant in interface.constants %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl index eb7fd89a4e7..e934b41c728 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl @@ -59,6 +59,9 @@ class {{export_attribute}} {{struct.name}} { template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> bool Equals(const T& other) const; + template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> + bool operator==(const T& rhs) const { return Equals(rhs); } + {%- if struct|is_hashable %} size_t Hash(size_t seed) const; {%- endif %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl index 40603a7fe53..05686c9ddf7 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl @@ -49,6 +49,11 @@ class {{export_attribute}} {{union.name}} { T, {{union.name}}>::value>::type* = nullptr> bool Equals(const T& other) const; + template <typename T, + typename std::enable_if<std::is_same< + T, {{union.name}}>::value>::type* = nullptr> + bool operator==(const T& rhs) const { return Equals(rhs); } + {%- if union|is_hashable %} size_t Hash(size_t seed) const; {%- endif %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/enum_definition_for_module.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/enum_definition_for_module.tmpl new file mode 100644 index 00000000000..27c4817c9d9 --- /dev/null +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/enum_definition_for_module.tmpl @@ -0,0 +1,23 @@ +{%- macro enum_def(enum) -%} +/** + * @const { {$: !mojo.internal.MojomType} } + */ +export const {{enum|type_in_js_module}}Spec = { $: mojo.internal.Enum() }; + +/** + * @enum {number} + */ +export const {{enum|type_in_js_module}} = { + {# Set up the enum here, but fill out the values later. #} +{%- for field in enum.fields %} + {{field.name}}: {{field.numeric_value}}, +{%- endfor %} +{%- if enum.min_value is not none %} + MIN_VALUE: {{enum.min_value}}, +{%- endif %} +{%- if enum.max_value is not none %} + MAX_VALUE: {{enum.max_value}}, +{%- endif %} +}; + +{%- endmacro %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition_for_module.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition_for_module.tmpl new file mode 100644 index 00000000000..bc000ba3d82 --- /dev/null +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition_for_module.tmpl @@ -0,0 +1,211 @@ +{% macro generateMethodAnnotation(method) %} + /** +{%- for param in method.parameters %} + * @param { {{param.kind|param_type_in_js_module}} } {{param.name|sanitize_identifier}} +{%- endfor -%} +{%- if method.response_parameters != None %} +{%- if method.response_parameters|length == 0 %} + * @return {!Promise} +{%- else %} + * @return {!Promise<{ +{%- for response_parameter in method.response_parameters %} + {{response_parameter.name}}: {{response_parameter.kind|param_type_in_js_module}}, +{%- endfor %} + * }>} +{%- endif %} +{%- endif %} + */ +{% endmacro %} + +/** + * @implements {mojo.internal.interfaceSupport.PendingReceiver} + */ +export const {{interface.name}}PendingReceiver = class { + /** @param {!MojoHandle} handle */ + constructor(handle) { + /** @public {!MojoHandle} */ + this.handle = handle; + } +}; + +/** @interface */ +export const {{interface.name}}Interface = class { +{%- for method in interface.methods %} + {{generateMethodAnnotation(method)}} + {{method.name}}( +{%- for param in method.parameters -%} +{{param.name|sanitize_identifier}}{% if not loop.last %}, {% endif %} +{%- endfor -%} +) {} +{%- endfor %} +}; + +/** + * @implements { {{interface.name}}Interface } + */ +export const {{interface.name}}Remote = class { + /** @param {MojoHandle=} opt_handle */ + constructor(opt_handle) { + /** + * @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<!{{interface.name}}PendingReceiver>} + */ + this.proxy = + new mojo.internal.interfaceSupport.InterfaceRemoteBase( + {{interface.name}}PendingReceiver, + opt_handle); + + /** + * @public {!mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper<!{{interface.name}}PendingReceiver>} + */ + this.$ = new mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper(this.proxy); + + /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */ + this.onConnectionError = this.proxy.getConnectionErrorEventRouter(); + } + +{%- for method in interface.methods -%} +{%- set interface_message_id = + interface.mojom_name ~ "_" ~ method.mojom_name %} + + {{generateMethodAnnotation(method)}} + {{method.name}}( +{%- for param in method.parameters %} + {{param.name}}{%- if not loop.last %},{% endif %} +{%- endfor -%}) { +{%- if method.response_parameters != None %} + return this.proxy.sendMessage( +{%- else %} + this.proxy.sendMessage( +{%- endif %} + {{method.ordinal}}, + {{interface_message_id}}_ParamsSpec.$, +{%- if method.response_parameters != None %} + {{interface_message_id}}_ResponseParamsSpec.$, +{%- else %} + null, +{%- endif %} + [ +{%- for param in method.parameters %} + {{param.name}}{%- if not loop.last %},{% endif %} +{%- endfor %} + ]); + } +{%- endfor %} +}; + +/** + * An object which receives request messages for the {{interface.name}} + * mojom interface. Must be constructed over an object which implements that + * interface. + */ +export const {{interface.name}}Receiver = class { + /** + * @param {!{{interface.name}}Interface } impl + */ + constructor(impl) { + /** @private {!mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal<!{{interface.name}}Remote>} */ + this.helper_internal_ = new mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal( + {{interface.name}}Remote); + + /** + * @public {!mojo.internal.interfaceSupport.InterfaceReceiverHelper<!{{interface.name}}Remote>} + */ + this.$ = new mojo.internal.interfaceSupport.InterfaceReceiverHelper(this.helper_internal_); + +{% for method in interface.methods %} +{%- set interface_message_id = + interface.mojom_name ~ "_" ~ method.mojom_name %} + this.helper_internal_.registerHandler( + {{method.ordinal}}, + {{interface_message_id}}_ParamsSpec.$, +{%- if method.response_parameters != None %} + {{interface_message_id}}_ResponseParamsSpec.$, +{%- else %} + null, +{%- endif %} + impl.{{method.name}}.bind(impl)); +{%- endfor %} + /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */ + this.onConnectionError = this.helper_internal_.getConnectionErrorEventRouter(); + } +}; + +export const {{interface.name}} = class { + /** + * @return {!string} + */ + static get $interfaceName() { + return "{{mojom_namespace}}.{{interface.name}}"; + } + + /** + * Returns a remote for this interface which sends messages to the browser. + * The browser must have an interface request binder registered for this + * interface and accessible to the calling document's frame. + * + * @return {!{{interface.name}}Remote} + */ + static getRemote() { + let remote = new {{interface.name}}Remote; + Mojo.bindInterface("{{mojom_namespace}}.{{interface.name}}", + remote.$.bindNewPipeAndPassReceiver().handle); + return remote; + } +}; + +{#--- Enums #} +{% from "lite/enum_definition_for_module.tmpl" import enum_def with context %} +{%- for enum in interface.enums %} +{{enum_def(enum)}} +{%- endfor %} + +/** + * An object which receives request messages for the {{interface.name}} + * mojom interface and dispatches them as callbacks. One callback receiver exists + * on this object for each message defined in the mojom interface, and each + * receiver can have any number of listeners added to it. + */ +export const {{interface.name}}CallbackRouter = class { + constructor() { + this.helper_internal_ = new mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal( + {{interface.name}}Remote); + + /** + * @public {!mojo.internal.interfaceSupport.InterfaceReceiverHelper<!{{interface.name}}Remote>} + */ + this.$ = new mojo.internal.interfaceSupport.InterfaceReceiverHelper(this.helper_internal_); + + this.router_ = new mojo.internal.interfaceSupport.CallbackRouter; +{% for method in interface.methods %} +{%- set interface_message_id = + interface.mojom_name ~ "_" ~ method.mojom_name %} + /** + * @public {!mojo.internal.interfaceSupport.InterfaceCallbackReceiver} + */ + this.{{method.name}} = + new mojo.internal.interfaceSupport.InterfaceCallbackReceiver( + this.router_); + + this.helper_internal_.registerHandler( + {{method.ordinal}}, + {{interface_message_id}}_ParamsSpec.$, +{%- if method.response_parameters != None %} + {{interface_message_id}}_ResponseParamsSpec.$, + this.{{method.name}}.createReceiverHandler(true /* expectsResponse */)); +{%- else %} + null, + this.{{method.name}}.createReceiverHandler(false /* expectsResponse */)); +{%- endif %} +{%- endfor %} + /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */ + this.onConnectionError = this.helper_internal_.getConnectionErrorEventRouter(); + } + + /** + * @param {number} id An ID returned by a prior call to addListener. + * @return {boolean} True iff the identified listener was found and removed. + */ + removeListener(id) { + return this.router_.removeListener(id); + } +}; diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/module_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/module_definition.tmpl index e8124d4a865..99422f42f30 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/module_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/module_definition.tmpl @@ -8,8 +8,7 @@ goog.provide('{{module.namespace}}.{{constant.name}}'); * @const { {{constant.kind|lite_closure_type_with_nullability}} } * @export */ -{{module.namespace}}.{{constant.name}} = - {{constant.value|expression_to_text_lite}}; +{{module.namespace}}.{{constant.name}} = {{constant|constant_value}}; {%- endfor %} {#--- Enums #} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom-lite.js.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom-lite.js.tmpl index a02c7a32077..e057457d615 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom-lite.js.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom-lite.js.tmpl @@ -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'); @@ -11,7 +15,7 @@ goog.require('mojo.internal.interfaceSupport'); {% for kind in module.imported_kinds.values() %} goog.require('{{kind|lite_js_import_name}}'); {%- endfor %} -{% else %} +{% elif not for_bindings_internals %} mojo.internal.exportModule('{{module.namespace}}'); {% endif %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom.m.js.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom.m.js.tmpl new file mode 100644 index 00000000000..659d6480c9e --- /dev/null +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/mojom.m.js.tmpl @@ -0,0 +1,66 @@ +// 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. + +{% if not for_bindings_internals -%} +import {mojo} from '{{bindings_library_path}}'; +{%- endif %} + +{% for path, kinds in js_module_imports.items() -%} +import { +{%- for kind in kinds %} +{%- for item in kind|imports_for_kind %} + {{item.name}} as {{item.alias}} +{%- if not loop.last -%},{% endif -%} +{%- endfor %} +{%- if not loop.last -%},{% endif -%} +{%- endfor %} +} from '{{path}}'; + +{% endfor -%} + +{#--- Constants #} +{%- for constant in module.constants %} +/** + * @const { {{constant.kind|type_in_js_module_with_nullability}} } + */ +export const {{constant.name}} = {{constant|constant_value_in_js_module}}; + +{% endfor -%} + +{#--- Enums #} +{%- from "lite/enum_definition_for_module.tmpl" import enum_def with context %} +{%- for enum in enums %} +{{enum_def(enum)}} +{% endfor -%} + +{#--- Interfaces #} +{%- for interface in interfaces %} +{% include "lite/interface_definition_for_module.tmpl" %} +{%- endfor %} + +{#--- Struct and Union forward declarations #} +{% for struct in structs %} +/** + * @const { {$:!mojo.internal.MojomType}} + */ +export const {{struct.name}}Spec = + { $: /** @type {!mojo.internal.MojomType} */ ({}) }; +{% endfor %} +{%- for union in unions %} +/** + * @const { {$:!mojo.internal.MojomType} } + */ +export const {{union.name}}Spec = + { $: /** @type {!mojo.internal.MojomType} */ ({}) }; +{% endfor %} + +{#--- Struct definitions #} +{% for struct in structs %} +{%- include "lite/struct_definition_for_module.tmpl" %} +{% endfor -%} + +{#--- Union definitions #} +{% for union in unions %} +{%- include "lite/union_definition_for_module.tmpl" %} +{% endfor %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl index 1d6acbcb5d7..2e4eb953412 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl @@ -4,7 +4,7 @@ * @export */ {{module.namespace}}.{{struct.name}}_{{constant.name}} = - {{constant.value|expression_to_text_lite}}; + {{constant|constant_value}}; {% endfor %} {%- from "lite/enum_definition.tmpl" import enum_def with context %} @@ -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 %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition_for_module.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition_for_module.tmpl new file mode 100644 index 00000000000..b6ef3829b6f --- /dev/null +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition_for_module.tmpl @@ -0,0 +1,49 @@ +{% for constant in struct.constants %} +/** + * @const { {{constant.kind|type_in_js_module_with_nullability}} } + */ +export const {{struct.name}}_{{constant.name}} = + {{constant|constant_value_in_js_module}}; +{% endfor %} + +{%- from "lite/enum_definition_for_module.tmpl" import enum_def with context %} +{% for enum in struct.enums %} +{{enum_def(enum)}} +{% endfor %} + +mojo.internal.Struct( + {{struct.name}}Spec.$, + '{{struct.name}}', + {{struct.packed|payload_size}}, + [ +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} + mojo.internal.StructField( + '{{packed_field.field.name}}', {{packed_field.offset}}, + {% if packed_field.field.kind|is_bool_kind %}{{packed_field.bit}} + {%- else %}0{% endif %}, + {{packed_field.field.kind|spec_type_in_js_module}}, + {{packed_field.field|default_value_in_js_module}}, +{%- if packed_field.field.kind.is_nullable %} + true /* nullable */), +{%- else %} + false /* nullable */), +{%- endif %} +{%- endfor %} + ]); + +{% if generate_struct_deserializers %} +export const {{struct.name}}_Deserialize = + mojo.internal.createStructDeserializer({{struct.name}}Spec.$); +{% endif %} + +/** + * @record + */ +export const {{struct.name}} = class { + constructor() { +{%- for packed_field in struct.packed.packed_fields %} + /** @type { {{packed_field.field.kind|field_type_in_js_module}} } */ + this.{{packed_field.field.name}}; +{%- endfor %} + } +}; diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/union_definition_for_module.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/union_definition_for_module.tmpl new file mode 100644 index 00000000000..68be8b22b56 --- /dev/null +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/union_definition_for_module.tmpl @@ -0,0 +1,22 @@ +mojo.internal.Union( + {{union.name}}Spec.$, '{{union.name}}', + { +{%- for field in union.fields %} + '{{field.name}}': { + 'ordinal': {{field.ordinal}}, + 'type': {{field.kind|spec_type_in_js_module}}, +{%- if field.kind.is_nullable %} + 'nullable': true, +{%- endif %} + }, +{%- endfor %} + }); + +/** + * @typedef { { +{%- for field in union.fields %} + * {{field.name}}: ({{field.kind|type_in_js_module_with_nullability}}|undefined), +{%- endfor %} + * } } + */ +export const {{union.name}} = {}; diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index d73b3f48354..fc7728a1f59 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -35,8 +35,6 @@ _kind_to_cpp_literal_suffix = { } - - class _NameFormatter(object): """A formatter for the names of kinds or values.""" @@ -268,16 +266,19 @@ class Generator(generator.Generator): return used_typemaps def _GetExtraPublicHeaders(self): + headers = set() + all_enums = list(self.module.enums) for struct in self.module.structs: all_enums.extend(struct.enums) for interface in self.module.interfaces: all_enums.extend(interface.enums) + if interface.uuid: + headers.add('base/token.h') types = set(self._GetFullMojomNameForKind(typename) for typename in self.module.structs + all_enums + self.module.unions) - headers = set() for typename, typemap in self.typemap.items(): if typename in types: headers.update(typemap.get("public_headers", [])) diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py index 96b2fdfae0c..c51824a444b 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py @@ -20,9 +20,12 @@ import mojom.generate.generator as generator import mojom.generate.module as mojom from mojom.generate.template_expander import UseJinja -sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, - os.pardir, os.pardir, os.pardir, os.pardir, - 'build', 'android', 'gyp')) +# Item 0 of sys.path is the directory of the main file; item 1 is PYTHONPATH +# (if set); item 2 is system libraries. +sys.path.insert( + 1, + os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, + os.pardir, os.pardir, 'build', 'android', 'gyp')) from util import build_utils diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py index d8e275d83d2..c7068f5d008 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -1,7 +1,6 @@ # Copyright 2013 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. - """Generates JavaScript source files from a mojom.Module.""" import mojom.generate.generator as generator @@ -17,115 +16,115 @@ else: import urllib.request as urllib_request _kind_to_javascript_default_value = { - mojom.BOOL: "false", - mojom.INT8: "0", - mojom.UINT8: "0", - mojom.INT16: "0", - mojom.UINT16: "0", - mojom.INT32: "0", - mojom.UINT32: "0", - mojom.FLOAT: "0", - mojom.HANDLE: "null", - mojom.DCPIPE: "null", - mojom.DPPIPE: "null", - mojom.MSGPIPE: "null", - mojom.SHAREDBUFFER: "null", - mojom.PLATFORMHANDLE: "null", - mojom.NULLABLE_HANDLE: "null", - mojom.NULLABLE_DCPIPE: "null", - mojom.NULLABLE_DPPIPE: "null", - mojom.NULLABLE_MSGPIPE: "null", - mojom.NULLABLE_SHAREDBUFFER: "null", - mojom.NULLABLE_PLATFORMHANDLE: "null", - mojom.INT64: "0", - mojom.UINT64: "0", - mojom.DOUBLE: "0", - mojom.STRING: "null", - mojom.NULLABLE_STRING: "null" + mojom.BOOL: "false", + mojom.INT8: "0", + mojom.UINT8: "0", + mojom.INT16: "0", + mojom.UINT16: "0", + mojom.INT32: "0", + mojom.UINT32: "0", + mojom.FLOAT: "0", + mojom.HANDLE: "null", + mojom.DCPIPE: "null", + mojom.DPPIPE: "null", + mojom.MSGPIPE: "null", + mojom.SHAREDBUFFER: "null", + mojom.PLATFORMHANDLE: "null", + mojom.NULLABLE_HANDLE: "null", + mojom.NULLABLE_DCPIPE: "null", + mojom.NULLABLE_DPPIPE: "null", + mojom.NULLABLE_MSGPIPE: "null", + mojom.NULLABLE_SHAREDBUFFER: "null", + mojom.NULLABLE_PLATFORMHANDLE: "null", + mojom.INT64: "0", + mojom.UINT64: "0", + mojom.DOUBLE: "0", + mojom.STRING: "null", + mojom.NULLABLE_STRING: "null" } _kind_to_codec_type = { - mojom.BOOL: "codec.Uint8", - mojom.INT8: "codec.Int8", - mojom.UINT8: "codec.Uint8", - mojom.INT16: "codec.Int16", - mojom.UINT16: "codec.Uint16", - mojom.INT32: "codec.Int32", - mojom.UINT32: "codec.Uint32", - mojom.FLOAT: "codec.Float", - mojom.HANDLE: "codec.Handle", - mojom.DCPIPE: "codec.Handle", - mojom.DPPIPE: "codec.Handle", - mojom.MSGPIPE: "codec.Handle", - mojom.SHAREDBUFFER: "codec.Handle", - mojom.PLATFORMHANDLE: "codec.Handle", - mojom.NULLABLE_HANDLE: "codec.NullableHandle", - mojom.NULLABLE_DCPIPE: "codec.NullableHandle", - mojom.NULLABLE_DPPIPE: "codec.NullableHandle", - mojom.NULLABLE_MSGPIPE: "codec.NullableHandle", - mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle", - mojom.NULLABLE_PLATFORMHANDLE: "codec.NullableHandle", - mojom.INT64: "codec.Int64", - mojom.UINT64: "codec.Uint64", - mojom.DOUBLE: "codec.Double", - mojom.STRING: "codec.String", - mojom.NULLABLE_STRING: "codec.NullableString", + mojom.BOOL: "codec.Uint8", + mojom.INT8: "codec.Int8", + mojom.UINT8: "codec.Uint8", + mojom.INT16: "codec.Int16", + mojom.UINT16: "codec.Uint16", + mojom.INT32: "codec.Int32", + mojom.UINT32: "codec.Uint32", + mojom.FLOAT: "codec.Float", + mojom.HANDLE: "codec.Handle", + mojom.DCPIPE: "codec.Handle", + mojom.DPPIPE: "codec.Handle", + mojom.MSGPIPE: "codec.Handle", + mojom.SHAREDBUFFER: "codec.Handle", + mojom.PLATFORMHANDLE: "codec.Handle", + mojom.NULLABLE_HANDLE: "codec.NullableHandle", + mojom.NULLABLE_DCPIPE: "codec.NullableHandle", + mojom.NULLABLE_DPPIPE: "codec.NullableHandle", + mojom.NULLABLE_MSGPIPE: "codec.NullableHandle", + mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle", + mojom.NULLABLE_PLATFORMHANDLE: "codec.NullableHandle", + mojom.INT64: "codec.Int64", + mojom.UINT64: "codec.Uint64", + mojom.DOUBLE: "codec.Double", + mojom.STRING: "codec.String", + mojom.NULLABLE_STRING: "codec.NullableString", } _kind_to_closure_type = { - mojom.BOOL: "boolean", - mojom.INT8: "number", - mojom.UINT8: "number", - mojom.INT16: "number", - mojom.UINT16: "number", - mojom.INT32: "number", - mojom.UINT32: "number", - mojom.FLOAT: "number", - mojom.INT64: "number", - mojom.UINT64: "number", - mojom.DOUBLE: "number", - mojom.STRING: "string", - mojom.NULLABLE_STRING: "string", - mojom.HANDLE: "MojoHandle", - mojom.DCPIPE: "MojoHandle", - mojom.DPPIPE: "MojoHandle", - mojom.MSGPIPE: "MojoHandle", - mojom.SHAREDBUFFER: "MojoHandle", - mojom.PLATFORMHANDLE: "MojoHandle", - mojom.NULLABLE_HANDLE: "MojoHandle", - mojom.NULLABLE_DCPIPE: "MojoHandle", - mojom.NULLABLE_DPPIPE: "MojoHandle", - mojom.NULLABLE_MSGPIPE: "MojoHandle", - mojom.NULLABLE_SHAREDBUFFER: "MojoHandle", - mojom.NULLABLE_PLATFORMHANDLE: "MojoHandle", + mojom.BOOL: "boolean", + mojom.INT8: "number", + mojom.UINT8: "number", + mojom.INT16: "number", + mojom.UINT16: "number", + mojom.INT32: "number", + mojom.UINT32: "number", + mojom.FLOAT: "number", + mojom.INT64: "number", + mojom.UINT64: "number", + mojom.DOUBLE: "number", + mojom.STRING: "string", + mojom.NULLABLE_STRING: "string", + mojom.HANDLE: "MojoHandle", + mojom.DCPIPE: "MojoHandle", + mojom.DPPIPE: "MojoHandle", + mojom.MSGPIPE: "MojoHandle", + mojom.SHAREDBUFFER: "MojoHandle", + mojom.PLATFORMHANDLE: "MojoHandle", + mojom.NULLABLE_HANDLE: "MojoHandle", + mojom.NULLABLE_DCPIPE: "MojoHandle", + mojom.NULLABLE_DPPIPE: "MojoHandle", + mojom.NULLABLE_MSGPIPE: "MojoHandle", + mojom.NULLABLE_SHAREDBUFFER: "MojoHandle", + mojom.NULLABLE_PLATFORMHANDLE: "MojoHandle", } _kind_to_lite_js_type = { - mojom.BOOL: "mojo.internal.Bool", - mojom.INT8: "mojo.internal.Int8", - mojom.UINT8: "mojo.internal.Uint8", - mojom.INT16: "mojo.internal.Int16", - mojom.UINT16: "mojo.internal.Uint16", - mojom.INT32: "mojo.internal.Int32", - mojom.UINT32: "mojo.internal.Uint32", - mojom.FLOAT: "mojo.internal.Float", - mojom.HANDLE: "mojo.internal.Handle", - mojom.DCPIPE: "mojo.internal.Handle", - mojom.DPPIPE: "mojo.internal.Handle", - mojom.MSGPIPE: "mojo.internal.Handle", - mojom.SHAREDBUFFER: "mojo.internal.Handle", - mojom.PLATFORMHANDLE: "mojo.internal.Handle", - mojom.NULLABLE_HANDLE: "mojo.internal.Handle", - mojom.NULLABLE_DCPIPE: "mojo.internal.Handle", - mojom.NULLABLE_DPPIPE: "mojo.internal.Handle", - mojom.NULLABLE_MSGPIPE: "mojo.internal.Handle", - mojom.NULLABLE_SHAREDBUFFER: "mojo.internal.Handle", - mojom.NULLABLE_PLATFORMHANDLE: "mojo.internal.Handle", - mojom.INT64: "mojo.internal.Int64", - mojom.UINT64: "mojo.internal.Uint64", - mojom.DOUBLE: "mojo.internal.Double", - mojom.STRING: "mojo.internal.String", - mojom.NULLABLE_STRING: "mojo.internal.String", + mojom.BOOL: "mojo.internal.Bool", + mojom.INT8: "mojo.internal.Int8", + mojom.UINT8: "mojo.internal.Uint8", + mojom.INT16: "mojo.internal.Int16", + mojom.UINT16: "mojo.internal.Uint16", + mojom.INT32: "mojo.internal.Int32", + mojom.UINT32: "mojo.internal.Uint32", + mojom.FLOAT: "mojo.internal.Float", + mojom.HANDLE: "mojo.internal.Handle", + mojom.DCPIPE: "mojo.internal.Handle", + mojom.DPPIPE: "mojo.internal.Handle", + mojom.MSGPIPE: "mojo.internal.Handle", + mojom.SHAREDBUFFER: "mojo.internal.Handle", + mojom.PLATFORMHANDLE: "mojo.internal.Handle", + mojom.NULLABLE_HANDLE: "mojo.internal.Handle", + mojom.NULLABLE_DCPIPE: "mojo.internal.Handle", + mojom.NULLABLE_DPPIPE: "mojo.internal.Handle", + mojom.NULLABLE_MSGPIPE: "mojo.internal.Handle", + mojom.NULLABLE_SHAREDBUFFER: "mojo.internal.Handle", + mojom.NULLABLE_PLATFORMHANDLE: "mojo.internal.Handle", + mojom.INT64: "mojo.internal.Int64", + mojom.UINT64: "mojo.internal.Uint64", + mojom.DOUBLE: "mojo.internal.Double", + mojom.STRING: "mojo.internal.String", + mojom.NULLABLE_STRING: "mojo.internal.String", } _js_reserved_keywords = [ @@ -176,34 +175,51 @@ _js_reserved_keywords = [ ] _primitive_kind_to_fuzz_type = { - mojom.BOOL: "Bool", - mojom.INT8: "Int8", - mojom.UINT8: "Uint8", - mojom.INT16: "Int16", - mojom.UINT16: "Uint16", - mojom.INT32: "Int32", - mojom.UINT32: "Uint32", - mojom.FLOAT: "Float", - mojom.INT64: "Int64", - mojom.UINT64: "Uint64", - mojom.DOUBLE: "Double", - mojom.STRING: "String", - mojom.NULLABLE_STRING: "String", - mojom.HANDLE: "Handle", - mojom.DCPIPE: "DataPipeConsumer", - mojom.DPPIPE: "DataPipeProducer", - mojom.MSGPIPE: "MessagePipe", - mojom.SHAREDBUFFER: "SharedBuffer", - mojom.PLATFORMHANDLE: "PlatformHandle", - mojom.NULLABLE_HANDLE: "Handle", - mojom.NULLABLE_DCPIPE: "DataPipeConsumer", - mojom.NULLABLE_DPPIPE: "DataPipeProducer", - mojom.NULLABLE_MSGPIPE: "MessagePipe", - mojom.NULLABLE_SHAREDBUFFER: "SharedBuffer", - mojom.NULLABLE_PLATFORMHANDLE: "PlatformHandle", + mojom.BOOL: "Bool", + mojom.INT8: "Int8", + mojom.UINT8: "Uint8", + mojom.INT16: "Int16", + mojom.UINT16: "Uint16", + mojom.INT32: "Int32", + mojom.UINT32: "Uint32", + mojom.FLOAT: "Float", + mojom.INT64: "Int64", + mojom.UINT64: "Uint64", + mojom.DOUBLE: "Double", + mojom.STRING: "String", + mojom.NULLABLE_STRING: "String", + mojom.HANDLE: "Handle", + mojom.DCPIPE: "DataPipeConsumer", + mojom.DPPIPE: "DataPipeProducer", + mojom.MSGPIPE: "MessagePipe", + mojom.SHAREDBUFFER: "SharedBuffer", + mojom.PLATFORMHANDLE: "PlatformHandle", + mojom.NULLABLE_HANDLE: "Handle", + mojom.NULLABLE_DCPIPE: "DataPipeConsumer", + mojom.NULLABLE_DPPIPE: "DataPipeProducer", + mojom.NULLABLE_MSGPIPE: "MessagePipe", + mojom.NULLABLE_SHAREDBUFFER: "SharedBuffer", + mojom.NULLABLE_PLATFORMHANDLE: "PlatformHandle", } +_SHARED_MODULE_PREFIX = 'chrome://resources/mojo' + + +def _GetWebUiModulePath(module): + """Returns the path to a WebUI module, from the perspective of a WebUI page + that makes it available. This is based on the corresponding mojom target's + webui_module_path value. Returns None if the target specifies no module + path. Otherwise, returned paths always end in a '/' and begin with either + `_SHARED_MODULE_PREFIX` or a '/'.""" + path = module.metadata.get('webui_module_path') + if path is None or path == '/': + return path + if path.startswith(_SHARED_MODULE_PREFIX): + return path.rstrip('/') + '/' + return '/{}/'.format(path.strip('/')) + + def JavaScriptPayloadSize(packed): packed_fields = packed.packed_fields if not packed_fields: @@ -260,26 +276,46 @@ class JavaScriptStylizer(generator.Stylizer): return mojom_name def StylizeModule(self, mojom_namespace): - return '.'.join(generator.ToCamel(word, lower_initial=True) - for word in mojom_namespace.split('.')) + return '.'.join( + generator.ToCamel(word, lower_initial=True) + for word in mojom_namespace.split('.')) class Generator(generator.Generator): - def _GetParameters(self, for_compile=False): + def _GetParameters(self, for_compile=False, for_webui_module=False): return { - "enums": self.module.enums, - "html_imports": self._GenerateHtmlImports(), - "imports": self.module.imports, - "interfaces": self.module.interfaces, - "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 @@ -288,65 +324,75 @@ class Generator(generator.Generator): def GetFilters(self): js_filters = { - "closure_type": self._ClosureType, - "decode_snippet": self._JavaScriptDecodeSnippet, - "default_value": self._JavaScriptDefaultValue, - "encode_snippet": self._JavaScriptEncodeSnippet, - "expression_to_text": self._ExpressionToText, - "expression_to_text_lite": self._ExpressionToTextLite, - "field_offset": JavaScriptFieldOffset, - "get_relative_url": GetRelativeUrl, - "has_callbacks": mojom.HasCallbacks, - "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind, - "is_array_kind": mojom.IsArrayKind, - "is_associated_interface_kind": mojom.IsAssociatedInterfaceKind, - "is_pending_associated_remote_kind": mojom.IsPendingAssociatedRemoteKind, - "is_associated_interface_request_kind": - mojom.IsAssociatedInterfaceRequestKind, - "is_pending_associated_receiver_kind": - mojom.IsPendingAssociatedReceiverKind, - "is_bool_kind": mojom.IsBoolKind, - "is_enum_kind": mojom.IsEnumKind, - "is_any_handle_kind": mojom.IsAnyHandleKind, - "is_any_interface_kind": mojom.IsAnyInterfaceKind, - "is_interface_kind": mojom.IsInterfaceKind, - "is_pending_remote_kind": mojom.IsPendingRemoteKind, - "is_interface_request_kind": mojom.IsInterfaceRequestKind, - "is_pending_receiver_kind": mojom.IsPendingReceiverKind, - "is_map_kind": mojom.IsMapKind, - "is_object_kind": mojom.IsObjectKind, - "is_reference_kind": mojom.IsReferenceKind, - "is_string_kind": mojom.IsStringKind, - "is_struct_kind": mojom.IsStructKind, - "is_union_kind": mojom.IsUnionKind, - "js_type": self._JavaScriptType, - "lite_default_value": self._LiteJavaScriptDefaultValue, - "lite_js_type": self._LiteJavaScriptType, - "lite_js_import_name": self._LiteJavaScriptImportName, - "method_passes_associated_kinds": mojom.MethodPassesAssociatedKinds, - "namespace_declarations": self._NamespaceDeclarations, - "closure_type_with_nullability": self._ClosureTypeWithNullability, - "lite_closure_param_type": self._LiteClosureParamType, - "lite_closure_type": self._LiteClosureType, - "lite_closure_type_with_nullability": - self._LiteClosureTypeWithNullability, - "lite_closure_field_type": self._LiteClosureFieldType, - "payload_size": JavaScriptPayloadSize, - "to_camel": generator.ToCamel, - "union_decode_snippet": self._JavaScriptUnionDecodeSnippet, - "union_encode_snippet": self._JavaScriptUnionEncodeSnippet, - "validate_array_params": self._JavaScriptValidateArrayParams, - "validate_enum_params": self._JavaScriptValidateEnumParams, - "validate_map_params": self._JavaScriptValidateMapParams, - "validate_nullable_params": self._JavaScriptNullableParam, - "validate_struct_params": self._JavaScriptValidateStructParams, - "validate_union_params": self._JavaScriptValidateUnionParams, - "sanitize_identifier": self._JavaScriptSanitizeIdentifier, - "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces, - "fuzz_handle_name": self._FuzzHandleName, - "is_primitive_kind": self._IsPrimitiveKind, - "primitive_to_fuzz_type": self._PrimitiveToFuzzType, - "to_js_boolean": self._ToJsBoolean, + "closure_type": self._ClosureType, + "constant_value": self._GetConstantValue, + "constant_value_in_js_module": self._GetConstantValueInJsModule, + "decode_snippet": self._JavaScriptDecodeSnippet, + "default_value": self._JavaScriptDefaultValue, + "default_value_in_js_module": self._DefaultValueInJsModule, + "encode_snippet": self._JavaScriptEncodeSnippet, + "expression_to_text": self._ExpressionToText, + "field_offset": JavaScriptFieldOffset, + "field_type_in_js_module": self._GetFieldTypeInJsModule, + "get_relative_url": GetRelativeUrl, + "has_callbacks": mojom.HasCallbacks, + "imports_for_kind": self._GetImportsForKind, + "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind, + "is_array_kind": mojom.IsArrayKind, + "is_associated_interface_kind": mojom.IsAssociatedInterfaceKind, + "is_pending_associated_remote_kind": + mojom.IsPendingAssociatedRemoteKind, + "is_associated_interface_request_kind": + mojom.IsAssociatedInterfaceRequestKind, + "is_pending_associated_receiver_kind": + mojom.IsPendingAssociatedReceiverKind, + "is_bool_kind": mojom.IsBoolKind, + "is_enum_kind": mojom.IsEnumKind, + "is_any_handle_kind": mojom.IsAnyHandleKind, + "is_any_interface_kind": mojom.IsAnyInterfaceKind, + "is_interface_kind": mojom.IsInterfaceKind, + "is_pending_remote_kind": mojom.IsPendingRemoteKind, + "is_interface_request_kind": mojom.IsInterfaceRequestKind, + "is_pending_receiver_kind": mojom.IsPendingReceiverKind, + "is_map_kind": mojom.IsMapKind, + "is_object_kind": mojom.IsObjectKind, + "is_reference_kind": mojom.IsReferenceKind, + "is_string_kind": mojom.IsStringKind, + "is_struct_kind": mojom.IsStructKind, + "is_union_kind": mojom.IsUnionKind, + "js_type": self._JavaScriptType, + "lite_default_value": self._LiteJavaScriptDefaultValue, + "lite_js_type": self._LiteJavaScriptType, + "lite_js_import_name": self._LiteJavaScriptImportName, + "method_passes_associated_kinds": mojom.MethodPassesAssociatedKinds, + "namespace_declarations": self._NamespaceDeclarations, + "closure_type_with_nullability": self._ClosureTypeWithNullability, + "lite_closure_param_type": self._LiteClosureParamType, + "lite_closure_type": self._LiteClosureType, + "lite_closure_type_with_nullability": + self._LiteClosureTypeWithNullability, + "lite_closure_field_type": self._LiteClosureFieldType, + "param_type_in_js_module": self._GetParamTypeInJsModule, + "payload_size": JavaScriptPayloadSize, + "spec_type_in_js_module": self._GetSpecTypeInJsModule, + "to_camel": generator.ToCamel, + "type_in_js_module": self._GetTypeInJsModule, + "type_in_js_module_with_nullability": + self._GetTypeInJsModuleWithNullability, + "union_decode_snippet": self._JavaScriptUnionDecodeSnippet, + "union_encode_snippet": self._JavaScriptUnionEncodeSnippet, + "validate_array_params": self._JavaScriptValidateArrayParams, + "validate_enum_params": self._JavaScriptValidateEnumParams, + "validate_map_params": self._JavaScriptValidateMapParams, + "validate_nullable_params": self._JavaScriptNullableParam, + "validate_struct_params": self._JavaScriptValidateStructParams, + "validate_union_params": self._JavaScriptValidateUnionParams, + "sanitize_identifier": self._JavaScriptSanitizeIdentifier, + "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces, + "fuzz_handle_name": self._FuzzHandleName, + "is_primitive_kind": self._IsPrimitiveKind, + "primitive_to_fuzz_type": self._PrimitiveToFuzzType, + "to_js_boolean": self._ToJsBoolean, } return js_filters @@ -370,6 +416,14 @@ class Generator(generator.Generator): def _GenerateLiteBindingsForCompile(self): return self._GetParameters(for_compile=True) + @UseJinja("lite/mojom.m.js.tmpl") + 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.") @@ -391,6 +445,23 @@ class Generator(generator.Generator): "%s-lite.js" % self.module.path) self.WriteWithComment(self._GenerateLiteBindingsForCompile(), "%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( + os.path.relpath(path, os.path.dirname(self.module.path))) + if relpath.startswith('.') or relpath.startswith('/'): + return relpath + return './' + relpath + + 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() @@ -416,8 +487,7 @@ class Generator(generator.Generator): return kind.module.namespace + "." + kind.name + "Ptr" if mojom.IsPendingRemoteKind(kind): return kind.kind.module.namespace + "." + kind.kind.name + "Ptr" - if (mojom.IsStructKind(kind) or - mojom.IsEnumKind(kind)): + if (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)): return kind.module.namespace + "." + kind.name # TODO(calamity): Support unions properly. if mojom.IsUnionKind(kind): @@ -425,17 +495,17 @@ class Generator(generator.Generator): if mojom.IsArrayKind(kind): return "Array<%s>" % self._ClosureType(kind.kind) if mojom.IsMapKind(kind): - return "Map<%s, %s>" % ( - self._ClosureType(kind.key_kind), self._ClosureType(kind.value_kind)) + return "Map<%s, %s>" % (self._ClosureType( + kind.key_kind), self._ClosureType(kind.value_kind)) if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): return "mojo.InterfaceRequest" # TODO(calamity): Support associated interfaces properly. - if (mojom.IsAssociatedInterfaceKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind)): + if (mojom.IsAssociatedInterfaceKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind)): return "mojo.AssociatedInterfacePtrInfo" # TODO(calamity): Support associated interface requests properly. - if (mojom.IsAssociatedInterfaceRequestKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): + if (mojom.IsAssociatedInterfaceRequestKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): return "mojo.AssociatedInterfaceRequest" # TODO(calamity): Support enums properly. @@ -445,93 +515,147 @@ class Generator(generator.Generator): # Indicates whether a kind of suitable to stringify and use as an Object # property name. This is checked for map key types to allow most kinds of # mojom maps to be represented as either a Map or an Object. - return (mojom.IsIntegralKind(kind) or mojom.IsFloatKind(kind) or - mojom.IsDoubleKind(kind) or mojom.IsStringKind(kind) or - mojom.IsEnumKind(kind)) - - def _LiteClosureType(self, kind): - if kind in mojom.PRIMITIVES: - return _kind_to_closure_type[kind] - if mojom.IsArrayKind(kind): - return "Array<%s>" % self._LiteClosureTypeWithNullability(kind.kind) - if mojom.IsMapKind(kind) and self._IsStringableKind(kind.key_kind): - return "Object<%s, %s>" % ( - self._LiteClosureTypeWithNullability(kind.key_kind), - self._LiteClosureTypeWithNullability(kind.value_kind)) - if mojom.IsMapKind(kind): - return "Map<%s, %s>" % ( - self._LiteClosureTypeWithNullability(kind.key_kind), - self._LiteClosureTypeWithNullability(kind.value_kind)) - - if (mojom.IsAssociatedKind(kind) or mojom.IsInterfaceRequestKind(kind) or - mojom.IsPendingRemoteKind(kind) or mojom.IsPendingReceiverKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): - named_kind = kind.kind - else: - named_kind = kind - - name = [] - if named_kind.module: - name.append(named_kind.module.namespace) - if named_kind.parent_kind: - name.append(named_kind.parent_kind.name) + if kind == mojom.INT64 or kind == mojom.UINT64: + # JS BigInts are not stringable and cannot be used as Object property + # names. + return False + return (mojom.IsIntegralKind(kind) or mojom.IsFloatKind(kind) + or mojom.IsDoubleKind(kind) or mojom.IsStringKind(kind) + or mojom.IsEnumKind(kind)) + + def _GetTypeNameForNewBindings(self, + kind, + with_nullability=False, + for_module=False): + def recurse_with_nullability(kind): + return self._GetTypeNameForNewBindings(kind, + with_nullability=True, + for_module=for_module) + + def get_type_name(kind): + if kind == mojom.INT64 or kind == mojom.UINT64: + return "bigint" + if kind in mojom.PRIMITIVES: + return _kind_to_closure_type[kind] + if mojom.IsArrayKind(kind): + return "Array<%s>" % recurse_with_nullability(kind.kind) + if mojom.IsMapKind(kind) and self._IsStringableKind(kind.key_kind): + return "Object<%s, %s>" % (recurse_with_nullability( + kind.key_kind), recurse_with_nullability(kind.value_kind)) + if mojom.IsMapKind(kind): + return "Map<%s, %s>" % (recurse_with_nullability( + kind.key_kind), recurse_with_nullability(kind.value_kind)) + + if (mojom.IsAssociatedKind(kind) or mojom.IsInterfaceRequestKind(kind) + or mojom.IsPendingRemoteKind(kind) + or mojom.IsPendingReceiverKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): + named_kind = kind.kind + else: + named_kind = kind - if mojom.IsEnumKind(kind) and named_kind.parent_kind: - name = ".".join(name) - name += "_" + named_kind.name - else: - name.append("" + named_kind.name) - name = ".".join(name) + name = [] + qualified = (not for_module) or (self.module is not named_kind.module) + if qualified and named_kind.module: + name.append(named_kind.module.namespace) + if named_kind.parent_kind: + name.append(named_kind.parent_kind.name) + + if mojom.IsEnumKind(kind) and named_kind.parent_kind: + name = ".".join(name) + name += "_" + named_kind.name + else: + name.append("" + named_kind.name) + name = ".".join(name) + + if for_module: + name = name.replace(".", "_") + + if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) + or mojom.IsEnumKind(kind)): + return name + if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind): + return name + "Remote" + if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind( + kind): + return name + "PendingReceiver" + # TODO(calamity): Support associated interfaces properly. + if (mojom.IsAssociatedInterfaceKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind)): + return "Object" + # TODO(calamity): Support associated interface requests properly. + if (mojom.IsAssociatedInterfaceRequestKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): + return "Object" + raise Exception("No valid closure type: %s" % kind) + + if with_nullability: + return ('?' if mojom.IsNullableKind(kind) else '!') + get_type_name(kind) + + return get_type_name(kind) - if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) or - mojom.IsEnumKind(kind)): - return name - if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind): - return name + "Remote" - if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): - return name + "PendingReceiver" - # TODO(calamity): Support associated interfaces properly. - if (mojom.IsAssociatedInterfaceKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind)): - return "Object" - # TODO(calamity): Support associated interface requests properly. - if (mojom.IsAssociatedInterfaceRequestKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): - return "Object" + def _LiteClosureType(self, kind): + return self._GetTypeNameForNewBindings(kind, + with_nullability=False, + for_module=False) - raise Exception("No valid closure type: %s" % kind) + def _GetTypeInJsModule(self, kind): + return self._GetTypeNameForNewBindings(kind, + with_nullability=False, + for_module=True) def _ClosureTypeWithNullability(self, kind): return ("" if mojom.IsNullableKind(kind) else "!") + self._ClosureType(kind) + def _GetParamTypeNameForNewBindings(self, kind, for_module=False): + def get_type_name(kind): + if mojom.IsEnumKind(kind): + return "number" + prefix = "" if mojom.IsNullableKind(kind) else "!" + if mojom.IsArrayKind(kind): + return prefix + ("Array<%s>" % get_type_name(kind.kind)) + if mojom.IsMapKind(kind) and self._IsStringableKind(kind.key_kind): + return "(%sMap<%s, %s>|%sObject<%s, %s>)" % ( + prefix, get_type_name(kind.key_kind), get_type_name( + kind.value_kind), prefix, get_type_name( + kind.key_kind), get_type_name(kind.value_kind)) + if mojom.IsMapKind(kind): + return "{}Map<{}, {}>".format(prefix, get_type_name(kind.key_kind), + get_type_name(kind.value_kind)) + return prefix + self._GetTypeNameForNewBindings(kind, + for_module=for_module) + + return get_type_name(kind) + def _LiteClosureParamType(self, kind): - if mojom.IsEnumKind(kind): - return "number" - prefix = "" if mojom.IsNullableKind(kind) else "!" - if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): - return prefix + "Object" - if mojom.IsArrayKind(kind): - return prefix + ("Array<%s>" % - self._LiteClosureParamType(kind.kind)) - if mojom.IsMapKind(kind): - return "%sMap<%s, %s>|%sObject<%s, %s>" % ( - prefix, self._LiteClosureParamType(kind.key_kind), - self._LiteClosureParamType(kind.value_kind), - prefix, self._LiteClosureParamType(kind.key_kind), - self._LiteClosureParamType(kind.value_kind)) + return self._GetParamTypeNameForNewBindings(kind, for_module=False) - return prefix + self._LiteClosureType(kind) + def _GetParamTypeInJsModule(self, kind): + return self._GetParamTypeNameForNewBindings(kind, for_module=True) def _LiteClosureTypeWithNullability(self, kind): - return (("?" if mojom.IsNullableKind(kind) else "!") + - self._LiteClosureType(kind)) + return self._GetTypeNameForNewBindings(kind, + with_nullability=True, + for_module=False) - def _LiteClosureFieldType(self, kind): + def _GetTypeInJsModuleWithNullability(self, kind): + return self._GetTypeNameForNewBindings(kind, + with_nullability=True, + for_module=True) + + def _GetFieldTypeForNewBindings(self, kind, for_module=False): if mojom.IsNullableKind(kind): - return "(" + self._LiteClosureType(kind) + "|undefined)" + return "({}|undefined)".format( + self._GetTypeNameForNewBindings(kind, for_module=for_module)) else: - return "!" + self._LiteClosureType(kind) + return "!" + self._GetTypeNameForNewBindings(kind, for_module=for_module) + + def _LiteClosureFieldType(self, kind): + return self._GetFieldTypeForNewBindings(kind, for_module=False) + + def _GetFieldTypeInJsModule(self, kind): + return self._GetFieldTypeForNewBindings(kind, for_module=True) def _NamespaceDeclarations(self, namespace): pieces = namespace.split('.') @@ -542,6 +666,35 @@ class Generator(generator.Generator): declarations.append('.'.join(declaration)) return declarations + def _GetNameInJsModule(self, kind): + qualifier = "" + if kind.module is not self.module and kind.module.namespace: + qualifier = kind.module.namespace + '.' + if kind.parent_kind: + qualifier += kind.parent_kind.name + '.' + return (qualifier + kind.name).replace('.', '_') + + def _GetImportsForKind(self, kind): + qualified_name = self._GetNameInJsModule(kind) + + def make_import(name, suffix=''): + class ImportInfo(object): + def __init__(self, name, alias): + self.name = name + self.alias = alias + + return ImportInfo(name + suffix, qualified_name + suffix) + + if (mojom.IsEnumKind(kind) or mojom.IsStructKind(kind) + or mojom.IsUnionKind(kind)): + return [make_import(kind.name), make_import(kind.name, 'Spec')] + if mojom.IsInterfaceKind(kind): + return [ + make_import(kind.name, 'Remote'), + make_import(kind.name, 'PendingReceiver') + ] + assert False, kind.name + def _JavaScriptType(self, kind): name = [] if kind.module and kind.module.path != self.module.path: @@ -551,55 +704,67 @@ class Generator(generator.Generator): name.append(kind.name) return ".".join(name) + def _GetSpecType(self, kind, for_module=False): + def get_spec(kind): + if self._IsPrimitiveKind(kind): + return _kind_to_lite_js_type[kind] + if mojom.IsArrayKind(kind): + return "mojo.internal.Array(%s, %s)" % (get_spec( + kind.kind), "true" if mojom.IsNullableKind(kind.kind) else "false") + if mojom.IsMapKind(kind): + return "mojo.internal.Map(%s, %s, %s)" % ( + get_spec(kind.key_kind), get_spec(kind.value_kind), + "true" if mojom.IsNullableKind(kind.value_kind) else "false") + + if (mojom.IsAssociatedKind(kind) or mojom.IsInterfaceRequestKind(kind) + or mojom.IsPendingRemoteKind(kind) + or mojom.IsPendingReceiverKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): + named_kind = kind.kind + else: + named_kind = kind + + name = [] + qualified = (not for_module) or (self.module is not named_kind.module) + if qualified and named_kind.module: + name.append(named_kind.module.namespace) + if named_kind.parent_kind: + parent_name = named_kind.parent_kind.name + if mojom.IsStructKind(named_kind.parent_kind) and not for_module: + parent_name += "Spec" + name.append(parent_name) + name.append(named_kind.name) + name = ".".join(name) + if for_module: + name = name.replace(".", "_") + + if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) + or mojom.IsEnumKind(kind)): + return "%sSpec.$" % name + if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind): + return "mojo.internal.InterfaceProxy(%sRemote)" % name + if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind( + kind): + return "mojo.internal.InterfaceRequest(%sPendingReceiver)" % name + if (mojom.IsAssociatedInterfaceKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind)): + # TODO(rockot): Implement associated interfaces. + return "mojo.internal.AssociatedInterfaceProxy(%sRemote)" % (name) + if (mojom.IsAssociatedInterfaceRequestKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): + return "mojo.internal.AssociatedInterfaceRequest(%sPendingReceiver)" % ( + name) + + return name + + return get_spec(kind) + def _LiteJavaScriptType(self, kind): - if self._IsPrimitiveKind(kind): - return _kind_to_lite_js_type[kind] - if mojom.IsArrayKind(kind): - return "mojo.internal.Array(%s, %s)" % ( - self._LiteJavaScriptType(kind.kind), - "true" if mojom.IsNullableKind(kind.kind) else "false") - if mojom.IsMapKind(kind): - return "mojo.internal.Map(%s, %s, %s)" % ( - self._LiteJavaScriptType(kind.key_kind), - self._LiteJavaScriptType(kind.value_kind), - "true" if mojom.IsNullableKind(kind.value_kind) else "false") - - if (mojom.IsAssociatedKind(kind) or mojom.IsInterfaceRequestKind(kind) or - mojom.IsPendingRemoteKind(kind) or mojom.IsPendingReceiverKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): - named_kind = kind.kind - else: - named_kind = kind + return self._GetSpecType(kind, for_module=False) - name = [] - if named_kind.module: - name.append(named_kind.module.namespace) - if named_kind.parent_kind: - parent_name = named_kind.parent_kind.name - if mojom.IsStructKind(named_kind.parent_kind): - parent_name += "Spec" - name.append(parent_name) - name.append(named_kind.name) - name = ".".join(name) - - if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) or - mojom.IsEnumKind(kind)): - return "%sSpec.$" % name - if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind): - return "mojo.internal.InterfaceProxy(%sRemote)" % name - if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): - return "mojo.internal.InterfaceRequest(%sPendingReceiver)" % name - if (mojom.IsAssociatedInterfaceKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind)): - # TODO(rockot): Implement associated interfaces. - return "mojo.internal.AssociatedInterfaceProxy(%sRemote)" % (name) - if (mojom.IsAssociatedInterfaceRequestKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): - return "mojo.internal.AssociatedInterfaceRequest(%sPendingReceiver)" % ( - name) - - return name + def _GetSpecTypeInJsModule(self, kind): + return self._GetSpecType(kind, for_module=True) def _LiteJavaScriptImportName(self, kind): name = [] @@ -630,31 +795,39 @@ class Generator(generator.Generator): return "new %sPtr()" % self._JavaScriptType(field.kind) if mojom.IsPendingRemoteKind(field.kind): return "new %sPtr()" % self._JavaScriptType(field.kind.kind) - if (mojom.IsInterfaceRequestKind(field.kind) or - mojom.IsPendingReceiverKind(field.kind)): + if (mojom.IsInterfaceRequestKind(field.kind) + or mojom.IsPendingReceiverKind(field.kind)): return "new bindings.InterfaceRequest()" - if (mojom.IsAssociatedInterfaceKind(field.kind) or - mojom.IsPendingAssociatedRemoteKind(field.kind)): + if (mojom.IsAssociatedInterfaceKind(field.kind) + or mojom.IsPendingAssociatedRemoteKind(field.kind)): return "new associatedBindings.AssociatedInterfacePtrInfo()" - if (mojom.IsAssociatedInterfaceRequestKind(field.kind) or - mojom.IsPendingAssociatedReceiverKind(field.kind)): + if (mojom.IsAssociatedInterfaceRequestKind(field.kind) + or mojom.IsPendingAssociatedReceiverKind(field.kind)): return "new associatedBindings.AssociatedInterfaceRequest()" if mojom.IsEnumKind(field.kind): return "0" raise Exception("No valid default: %s" % field) - def _LiteJavaScriptDefaultValue(self, field): + def _GetDefaultValue(self, field, for_module=False): if field.default: if mojom.IsStructKind(field.kind): assert field.default == "default" - return "null"; - return self._ExpressionToTextLite(field.default) + return "null" + return self._ExpressionToTextLite(field.default, for_module=for_module) + if field.kind == mojom.INT64 or field.kind == mojom.UINT64: + return "BigInt(0)" if field.kind in mojom.PRIMITIVES: return _kind_to_javascript_default_value[field.kind] if mojom.IsEnumKind(field.kind): return "0" return "null" + def _LiteJavaScriptDefaultValue(self, field): + return self._GetDefaultValue(field, for_module=False) + + def _DefaultValueInJsModule(self, field): + return self._GetDefaultValue(field, for_module=True) + def _CodecType(self, kind): if kind in mojom.PRIMITIVES: return _kind_to_codec_type[kind] @@ -665,8 +838,8 @@ class Generator(generator.Generator): if mojom.IsUnionKind(kind): return self._JavaScriptType(kind) if mojom.IsArrayKind(kind): - array_type = ("NullableArrayOf" if mojom.IsNullableKind(kind) - else "ArrayOf") + array_type = ("NullableArrayOf" + if mojom.IsNullableKind(kind) else "ArrayOf") array_length = "" if kind.length is None else ", %d" % kind.length element_type = self._ElementCodecType(kind.kind) return "new codec.%s(%s%s)" % (array_type, element_type, array_length) @@ -679,17 +852,18 @@ class Generator(generator.Generator): "NullableInterface" if mojom.IsNullableKind(kind) else "Interface", self._JavaScriptType(kind.kind)) if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): - return "codec.%s" % ( - "NullableInterfaceRequest" if mojom.IsNullableKind(kind) - else "InterfaceRequest") - if (mojom.IsAssociatedInterfaceKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind)): + return "codec.%s" % ("NullableInterfaceRequest" if + mojom.IsNullableKind(kind) else "InterfaceRequest") + if (mojom.IsAssociatedInterfaceKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind)): return "codec.%s" % ("NullableAssociatedInterfacePtrInfo" - if mojom.IsNullableKind(kind) else "AssociatedInterfacePtrInfo") - if (mojom.IsAssociatedInterfaceRequestKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): + if mojom.IsNullableKind(kind) else + "AssociatedInterfacePtrInfo") + if (mojom.IsAssociatedInterfaceRequestKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): return "codec.%s" % ("NullableAssociatedInterfaceRequest" - if mojom.IsNullableKind(kind) else "AssociatedInterfaceRequest") + if mojom.IsNullableKind(kind) else + "AssociatedInterfaceRequest") if mojom.IsEnumKind(kind): return "new codec.Enum(%s)" % self._JavaScriptType(kind) if mojom.IsMapKind(kind): @@ -700,19 +874,18 @@ class Generator(generator.Generator): raise Exception("No codec type for %s" % kind) def _ElementCodecType(self, kind): - return ("codec.PackedBool" if mojom.IsBoolKind(kind) - else self._CodecType(kind)) + return ("codec.PackedBool" + if mojom.IsBoolKind(kind) else self._CodecType(kind)) def _JavaScriptDecodeSnippet(self, kind): - if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or - mojom.IsAnyInterfaceKind(kind)): + if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) + or mojom.IsAnyInterfaceKind(kind)): return "decodeStruct(%s)" % self._CodecType(kind) if mojom.IsStructKind(kind): return "decodeStructPointer(%s)" % self._JavaScriptType(kind) if mojom.IsMapKind(kind): - return "decodeMapPointer(%s, %s)" % ( - self._ElementCodecType(kind.key_kind), - self._ElementCodecType(kind.value_kind)) + return "decodeMapPointer(%s, %s)" % (self._ElementCodecType( + kind.key_kind), self._ElementCodecType(kind.value_kind)) if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): return "decodeArrayPointer(codec.PackedBool)" if mojom.IsArrayKind(kind): @@ -724,19 +897,18 @@ class Generator(generator.Generator): raise Exception("No decode snippet for %s" % kind) def _JavaScriptEncodeSnippet(self, kind): - if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or - mojom.IsAnyInterfaceKind(kind)): + if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) + or mojom.IsAnyInterfaceKind(kind)): return "encodeStruct(%s, " % self._CodecType(kind) if mojom.IsUnionKind(kind): return "encodeStruct(%s, " % self._JavaScriptType(kind) if mojom.IsStructKind(kind): return "encodeStructPointer(%s, " % self._JavaScriptType(kind) if mojom.IsMapKind(kind): - return "encodeMapPointer(%s, %s, " % ( - self._ElementCodecType(kind.key_kind), - self._ElementCodecType(kind.value_kind)) + return "encodeMapPointer(%s, %s, " % (self._ElementCodecType( + kind.key_kind), self._ElementCodecType(kind.value_kind)) if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): - return "encodeArrayPointer(codec.PackedBool, "; + return "encodeArrayPointer(codec.PackedBool, " if mojom.IsArrayKind(kind): return "encodeArrayPointer(%s, " % self._CodecType(kind.kind) if mojom.IsEnumKind(kind): @@ -760,8 +932,7 @@ class Generator(generator.Generator): nullable = self._JavaScriptNullableParam(field) element_kind = field.kind.kind element_size = pack.PackedField.GetSizeForKind(element_kind) - expected_dimension_sizes = GetArrayExpectedDimensionSizes( - field.kind) + expected_dimension_sizes = GetArrayExpectedDimensionSizes(field.kind) element_type = self._ElementCodecType(element_kind) return "%s, %s, %s, %s, 0" % \ (element_size, element_type, nullable, @@ -783,7 +954,7 @@ class Generator(generator.Generator): def _JavaScriptValidateMapParams(self, field): nullable = self._JavaScriptNullableParam(field) keys_type = self._ElementCodecType(field.kind.key_kind) - values_kind = field.kind.value_kind; + values_kind = field.kind.value_kind values_type = self._ElementCodecType(values_kind) values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false" return "%s, %s, %s, %s" % \ @@ -811,23 +982,24 @@ class Generator(generator.Generator): if isinstance(token, mojom.BuiltinValue): if token.value == "double.INFINITY" or token.value == "float.INFINITY": - return "Infinity"; + return "Infinity" if token.value == "double.NEGATIVE_INFINITY" or \ token.value == "float.NEGATIVE_INFINITY": - return "-Infinity"; + return "-Infinity" if token.value == "double.NAN" or token.value == "float.NAN": - return "NaN"; + return "NaN" return token - def _ExpressionToTextLite(self, token): + def _ExpressionToTextLite(self, token, for_module=False): if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): # Generate the following for: # - Enums: NamespaceUid.Enum.CONSTANT_NAME # - Struct: NamespaceUid.Struct_CONSTANT_NAME name_prefix = [] - if token.module: + qualified = (not for_module) or (token.module is not self.module) + if token.module and qualified: name_prefix.append(token.module.namespace) if token.parent_kind: name_prefix.append(token.parent_kind.name) @@ -838,20 +1010,72 @@ class Generator(generator.Generator): name.append(token.name) separator = "." - if mojom.IsStructKind(token.parent_kind): + if mojom.IsStructKind(token.parent_kind) or for_module: separator = "_" return ".".join(name_prefix) + separator + ".".join(name) return self._ExpressionToText(token) + def _GetConstantValue(self, constant, for_module=False): + assert isinstance(constant, mojom.Constant) + text = self._ExpressionToTextLite(constant.value, for_module=for_module) + if constant.kind == mojom.INT64 or constant.kind == mojom.UINT64: + return "BigInt('{}')".format(text) + return text + + def _GetConstantValueInJsModule(self, constant): + return self._GetConstantValue(constant, for_module=True) + def _GenerateHtmlImports(self): result = [] for full_import in self.module.imports: - result.append(os.path.relpath(full_import.path, - os.path.dirname(self.module.path))) + result.append( + os.path.relpath(full_import.path, os.path.dirname(self.module.path))) return result + def _GetJsModuleImports(self, for_webui_module=False): + this_module_path = _GetWebUiModulePath(self.module) + this_module_is_shared = bool( + this_module_path and this_module_path.startswith(_SHARED_MODULE_PREFIX)) + imports = dict() + for spec, kind in self.module.imported_kinds.items(): + if for_webui_module: + assert this_module_path is not None + 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. + def strip_prefix(s, prefix): + if s.startswith(prefix): + return s[len(prefix):] + return s + + import_path = urllib_request.pathname2url( + os.path.relpath( + strip_prefix(import_path, _SHARED_MODULE_PREFIX), + strip_prefix(this_module_path, _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): result = [] for interface in self.module.interfaces: @@ -863,20 +1087,17 @@ class Generator(generator.Generator): def _FuzzHandleName(self, kind): if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): - return '{0}.{1}Request'.format(kind.kind.module.namespace, - kind.kind.name) + return '{0}.{1}Request'.format(kind.kind.module.namespace, kind.kind.name) elif mojom.IsInterfaceKind(kind): - return '{0}.{1}Ptr'.format(kind.module.namespace, - kind.name) + return '{0}.{1}Ptr'.format(kind.module.namespace, kind.name) elif mojom.IsPendingRemoteKind(kind): - return '{0}.{1}Ptr'.format(kind.kind.module.namespace, - kind.kind.name) - elif (mojom.IsAssociatedInterfaceRequestKind(kind) or - mojom.IsPendingAssociatedReceiverKind(kind)): + return '{0}.{1}Ptr'.format(kind.kind.module.namespace, kind.kind.name) + elif (mojom.IsAssociatedInterfaceRequestKind(kind) + or mojom.IsPendingAssociatedReceiverKind(kind)): return '{0}.{1}AssociatedRequest'.format(kind.kind.module.namespace, kind.kind.name) - elif (mojom.IsAssociatedInterfaceKind(kind) or - mojom.IsPendingAssociatedRemoteKind(kind)): + elif (mojom.IsAssociatedInterfaceKind(kind) + or mojom.IsPendingAssociatedRemoteKind(kind)): return '{0}.{1}AssociatedPtr'.format(kind.kind.module.namespace, kind.kind.name) elif mojom.IsSharedBufferKind(kind): diff --git a/chromium/mojo/public/tools/bindings/mojom.gni b/chromium/mojo/public/tools/bindings/mojom.gni index a676fae5d22..8106c57ea0c 100644 --- a/chromium/mojo/public/tools/bindings/mojom.gni +++ b/chromium/mojo/public/tools/bindings/mojom.gni @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/python.gni") import("//third_party/closure_compiler/closure_args.gni") import("//third_party/closure_compiler/compile_js.gni") import("//third_party/protobuf/proto_library.gni") @@ -77,7 +78,6 @@ mojom_parser_sources = [ "$_mojom_library_root/__init__.py", "$_mojom_library_root/error.py", "$_mojom_library_root/generate/__init__.py", - "$_mojom_library_root/generate/constant_resolver.py", "$_mojom_library_root/generate/generator.py", "$_mojom_library_root/generate/module.py", "$_mojom_library_root/generate/pack.py", @@ -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 @@ -500,12 +515,6 @@ template("mojom") { sources_list = invoker.sources } - # Reset sources_assignment_filter for the BUILD.gn file to prevent - # regression during the migration of Chromium away from the feature. - # See docs/no_sources_assignment_filter.md for more information. - # TODO(crbug.com/1018739): remove this when migration is done. - set_sources_assignment_filter([]) - # Listed sources may be relative to the current target dir, or they may be # absolute paths, including paths to generated mojom files. While those are # fine as-is for input references, deriving output paths can be more subtle. @@ -621,7 +630,8 @@ template("mojom") { enabled_features += [ "is_win" ] } - action(parser_target_name) { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(parser_target_name) { script = mojom_parser_script inputs = mojom_parser_sources + [ build_metadata_filename ] sources = sources_list @@ -643,7 +653,7 @@ template("mojom") { # Resolve relative input mojom paths against both the root src dir and # the root gen dir. "--input-root", - rebase_path("//"), + rebase_path("//."), "--input-root", rebase_path(root_gen_dir), @@ -662,6 +672,13 @@ template("mojom") { enabled_feature, ] } + + if (defined(invoker.webui_module_path)) { + args += [ + "--add-module-metadata", + "webui_module_path=${invoker.webui_module_path}", + ] + } } } @@ -731,7 +748,8 @@ template("mojom") { } } - action(generator_cpp_message_ids_target_name) { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_cpp_message_ids_target_name) { script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources sources = sources_list @@ -770,7 +788,9 @@ template("mojom") { } generator_shared_target_name = "${target_name}_shared__generator" - action(generator_shared_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_shared_target_name) { visibility = [ ":*" ] script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources @@ -889,7 +909,9 @@ template("mojom") { generator_mojolpm_proto_target_name = "${target_name}_mojolpm_proto_generator" - action(generator_mojolpm_proto_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_mojolpm_proto_target_name) { script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources sources = invoker.sources @@ -1078,7 +1100,9 @@ template("mojom") { ] generator_target_name = "${target_name}${variant_suffix}__generator" - action(generator_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_target_name) { visibility = [ ":*" ] script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources @@ -1258,7 +1282,9 @@ template("mojom") { } write_file(_typemap_config_filename, _rebased_typemap_configs, "json") _mojom_target_name = target_name - action(_typemap_validator_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(_typemap_validator_target_name) { script = "$mojom_generator_root/validate_typemap_config.py" inputs = [ _typemap_config_filename ] outputs = [ _typemap_stamp_filename ] @@ -1269,7 +1295,8 @@ template("mojom") { ] } - action(type_mappings_target_name) { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(type_mappings_target_name) { inputs = mojom_generator_sources + jinja2_sources + [ _typemap_stamp_filename ] outputs = [ type_mappings_path ] @@ -1319,6 +1346,10 @@ template("mojom") { full_name = get_label_info("$d", "label_no_toolchain") public_deps += [ "${full_name}${variant_suffix}_headers" ] } + if (defined(bindings_configuration.for_blink) && + bindings_configuration.for_blink) { + public_deps += [ "//mojo/public/cpp/bindings:wtf_support" ] + } } if (!force_source_set && defined(invoker.component_macro_prefix)) { @@ -1438,7 +1469,8 @@ template("mojom") { java_generator_target_name = target_name + "_java__generator" if (sources_list != []) { - action(java_generator_target_name) { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(java_generator_target_name) { script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources sources = sources_list @@ -1477,7 +1509,9 @@ template("mojom") { } java_srcjar_target_name = target_name + "_java_sources" - action(java_srcjar_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(java_srcjar_target_name) { script = "//build/android/gyp/zip.py" inputs = [] if (output_file_base_paths != []) { @@ -1536,7 +1570,9 @@ template("mojom") { !use_typescript_for_target) { if (sources_list != []) { generator_js_target_name = "${target_name}_js__generator" - action(generator_js_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_js_target_name) { script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources sources = sources_list @@ -1557,10 +1593,15 @@ template("mojom") { outputs += [ "$root_gen_dir/$base_path.js", "$root_gen_dir/$base_path.externs.js", + "$root_gen_dir/$base_path.m.js", "$root_gen_dir/$base_path-lite.js", "$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 @@ -1609,13 +1650,19 @@ template("mojom") { foreach(base_path, output_file_base_paths) { data += [ "$root_gen_dir/${base_path}.js", + "$root_gen_dir/${base_path}.m.js", "$root_gen_dir/${base_path}-lite.js", ] } deps += [ ":$generator_js_target_name" ] } - data_deps = [] + if (defined(invoker.disallow_native_types) && + invoker.disallow_native_types) { + data_deps = [] + } else { + data_deps = [ "//mojo/public/js:bindings_module" ] + } foreach(d, all_deps) { full_name = get_label_info(d, "label_no_toolchain") data_deps += [ "${full_name}_js_data_deps" ] @@ -1671,6 +1718,64 @@ template("mojom") { group(js_library_for_compile_target_name) { } } + + js_modules_target_name = "${target_name}_js_modules" + if (sources_list != []) { + js_library(js_modules_target_name) { + extra_public_deps = [ ":$generator_js_target_name" ] + sources = [] + foreach(base_path, output_file_base_paths) { + sources += [ "$root_gen_dir/${base_path}.m.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}_js_modules" ] + } + } + } else { + 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) { @@ -1707,7 +1812,9 @@ template("mojom") { # Generate Typescript bindings. generator_ts_target_name = "${target_name}_${dependency_type.name}__ts__generator" - action(generator_ts_target_name) { + + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_ts_target_name) { script = mojom_generator_script inputs = mojom_generator_sources + jinja2_sources sources = sources_list @@ -1774,7 +1881,8 @@ template("mojom") { "${target_name}_${dependency_type.name}__js__generator" generator_js_target_names += [ generator_js_target_name ] - action(generator_js_target_name) { + # TODO(crbug.com/1112471): Get this to run cleanly under Python 3. + python2_action(generator_js_target_name) { script = "$mojom_generator_root/compile_typescript.py" sources = ts_outputs outputs = js_outputs diff --git a/chromium/mojo/public/tools/mojom/mojom/BUILD.gn b/chromium/mojo/public/tools/mojom/mojom/BUILD.gn index 7416ef19e3e..51facc0ccb8 100644 --- a/chromium/mojo/public/tools/mojom/mojom/BUILD.gn +++ b/chromium/mojo/public/tools/mojom/mojom/BUILD.gn @@ -8,7 +8,6 @@ group("mojom") { "error.py", "fileutil.py", "generate/__init__.py", - "generate/constant_resolver.py", "generate/generator.py", "generate/module.py", "generate/pack.py", diff --git a/chromium/mojo/public/tools/mojom/mojom/generate/constant_resolver.py b/chromium/mojo/public/tools/mojom/mojom/generate/constant_resolver.py deleted file mode 100644 index 0dfd996e355..00000000000 --- a/chromium/mojo/public/tools/mojom/mojom/generate/constant_resolver.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2015 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. -"""Resolves the values used for constants and enums.""" - -from itertools import ifilter - -from mojom.generate import module as mojom - - -def ResolveConstants(module, expression_to_text): - in_progress = set() - computed = set() - - def GetResolvedValue(named_value): - assert isinstance(named_value, (mojom.EnumValue, mojom.ConstantValue)) - if isinstance(named_value, mojom.EnumValue): - field = next( - ifilter(lambda field: field.name == named_value.name, - named_value.enum.fields), None) - if not field: - raise RuntimeError( - 'Unable to get computed value for field %s of enum %s' % - (named_value.name, named_value.enum.name)) - if field not in computed: - ResolveEnum(named_value.enum) - return field.resolved_value - else: - ResolveConstant(named_value.constant) - named_value.resolved_value = named_value.constant.resolved_value - return named_value.resolved_value - - def ResolveConstant(constant): - if constant in computed: - return - if constant in in_progress: - raise RuntimeError('Circular dependency for constant: %s' % constant.name) - in_progress.add(constant) - if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)): - resolved_value = GetResolvedValue(constant.value) - else: - resolved_value = expression_to_text(constant.value) - constant.resolved_value = resolved_value - in_progress.remove(constant) - computed.add(constant) - - def ResolveEnum(enum): - def ResolveEnumField(enum, field, default_value): - if field in computed: - return - if field in in_progress: - raise RuntimeError('Circular dependency for enum: %s' % enum.name) - in_progress.add(field) - if field.value: - if isinstance(field.value, mojom.EnumValue): - resolved_value = GetResolvedValue(field.value) - elif isinstance(field.value, str): - resolved_value = int(field.value, 0) - else: - raise RuntimeError('Unexpected value: %s' % field.value) - else: - resolved_value = default_value - field.resolved_value = resolved_value - in_progress.remove(field) - computed.add(field) - - current_value = 0 - for field in enum.fields: - ResolveEnumField(enum, field, current_value) - current_value = field.resolved_value + 1 - - for constant in module.constants: - ResolveConstant(constant) - - for enum in module.enums: - ResolveEnum(enum) - - for struct in module.structs: - for constant in struct.constants: - ResolveConstant(constant) - for enum in struct.enums: - ResolveEnum(enum) - for field in struct.fields: - if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)): - field.default.resolved_value = GetResolvedValue(field.default) - - for interface in module.interfaces: - for constant in interface.constants: - ResolveConstant(constant) - for enum in interface.enums: - ResolveEnum(enum) - - return module diff --git a/chromium/mojo/public/tools/mojom/mojom/generate/module.py b/chromium/mojo/public/tools/mojom/mojom/generate/module.py index ebbc9b322ea..e76f66a8d24 100644 --- a/chromium/mojo/public/tools/mojom/mojom/generate/module.py +++ b/chromium/mojo/public/tools/mojom/mojom/generate/module.py @@ -13,6 +13,7 @@ # method.AddParameter('baz', 0, mojom.INT32) import pickle +from uuid import UUID class BackwardCompatibilityChecker(object): @@ -286,6 +287,7 @@ ATTRIBUTE_EXTENSIBLE = 'Extensible' ATTRIBUTE_STABLE = 'Stable' ATTRIBUTE_SYNC = 'Sync' ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize' +ATTRIBUTE_UUID = 'Uuid' class NamedValue(object): @@ -1188,6 +1190,20 @@ class Interface(ReferenceKind): self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums, rhs.constants, rhs.attributes)) + @property + def uuid(self): + uuid_str = self.attributes.get(ATTRIBUTE_UUID) if self.attributes else None + if uuid_str is None: + return None + + try: + u = UUID(uuid_str) + except: + raise ValueError('Invalid format for Uuid attribute on interface {}. ' + 'Expected standard RFC 4122 string representation of ' + 'a UUID.'.format(self.mojom_name)) + return (int(u.hex[:16], 16), int(u.hex[16:], 16)) + def __hash__(self): return id(self) @@ -1346,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. diff --git a/chromium/mojo/public/tools/mojom/mojom_parser.py b/chromium/mojo/public/tools/mojom/mojom_parser.py index 12adbfb9d3f..998304c3c99 100755 --- a/chromium/mojo/public/tools/mojom/mojom_parser.py +++ b/chromium/mojo/public/tools/mojom/mojom_parser.py @@ -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__': |