diff options
226 files changed, 4529 insertions, 3190 deletions
diff --git a/content/browser/mojo/mojo_application_host.cc b/content/browser/mojo/mojo_application_host.cc index 883945f..a9d0002 100644 --- a/content/browser/mojo/mojo_application_host.cc +++ b/content/browser/mojo/mojo_application_host.cc @@ -63,9 +63,18 @@ bool MojoApplicationHost::Init() { mojo::embedder::PlatformChannelPair channel_pair; + scoped_refptr<base::TaskRunner> io_task_runner; + if (io_task_runner_override_) { + io_task_runner = io_task_runner_override_; + } else { + io_task_runner = + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO) + ->task_runner(); + } + mojo::ScopedMessagePipeHandle message_pipe = channel_init_.Init( PlatformFileFromScopedPlatformHandle(channel_pair.PassServerHandle()), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)); + io_task_runner); if (!message_pipe.is_valid()) return false; @@ -93,4 +102,9 @@ void MojoApplicationHost::WillDestroySoon() { channel_init_.WillDestroySoon(); } +void MojoApplicationHost::OverrideIOTaskRunnerForTest( + scoped_refptr<base::TaskRunner> io_task_runner) { + io_task_runner_override_ = io_task_runner; +} + } // namespace content diff --git a/content/browser/mojo/mojo_application_host.h b/content/browser/mojo/mojo_application_host.h index fc0693a..216b65a 100644 --- a/content/browser/mojo/mojo_application_host.h +++ b/content/browser/mojo/mojo_application_host.h @@ -8,8 +8,8 @@ #include "base/memory/scoped_ptr.h" #include "base/process/process_handle.h" #include "content/common/application_setup.mojom.h" +#include "content/common/mojo/channel_init.h" #include "content/common/mojo/service_registry_impl.h" -#include "third_party/mojo/src/mojo/edk/embedder/channel_init.h" #include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h" #if defined(OS_ANDROID) @@ -48,8 +48,11 @@ class CONTENT_EXPORT MojoApplicationHost { } #endif + void OverrideIOTaskRunnerForTest( + scoped_refptr<base::TaskRunner> io_task_runner); + private: - mojo::embedder::ChannelInit channel_init_; + ChannelInit channel_init_; mojo::embedder::ScopedPlatformHandle client_handle_; bool did_activate_; @@ -57,6 +60,8 @@ class CONTENT_EXPORT MojoApplicationHost { scoped_ptr<ApplicationSetup> application_setup_; ServiceRegistryImpl service_registry_; + scoped_refptr<base::TaskRunner> io_task_runner_override_; + #if defined(OS_ANDROID) scoped_ptr<ServiceRegistryAndroid> service_registry_android_; #endif diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 6564c438..16aa989 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -110,6 +110,7 @@ #include "content/common/frame_messages.h" #include "content/common/gpu/gpu_memory_buffer_factory.h" #include "content/common/gpu/gpu_messages.h" +#include "content/common/mojo/channel_init.h" #include "content/common/mojo/mojo_messages.h" #include "content/common/resource_messages.h" #include "content/common/view_messages.h" @@ -664,9 +665,15 @@ scoped_ptr<IPC::ChannelProxy> RenderProcessHostImpl::CreateChannelProxy( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); if (ShouldUseMojoChannel()) { VLOG(1) << "Mojo Channel is enabled on host"; + scoped_refptr<base::TaskRunner> io_task_runner = + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO) + ->task_runner(); if (!channel_mojo_host_) { - channel_mojo_host_.reset(new IPC::ChannelMojoHost( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); + channel_mojo_host_.reset(new IPC::ChannelMojoHost(io_task_runner)); + } + + if (run_renderer_in_process()) { + ChannelInit::SetSingleProcessIOTaskRunner(io_task_runner); } return IPC::ChannelProxy::Create( diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc index 5df6631..8acc37d 100644 --- a/content/child/child_thread_impl.cc +++ b/content/child/child_thread_impl.cc @@ -49,12 +49,14 @@ #include "content/child/thread_safe_sender.h" #include "content/child/websocket_dispatcher.h" #include "content/common/child_process_messages.h" +#include "content/common/mojo/channel_init.h" #include "content/public/common/content_switches.h" #include "ipc/ipc_logging.h" #include "ipc/ipc_switches.h" #include "ipc/ipc_sync_channel.h" #include "ipc/ipc_sync_message_filter.h" #include "ipc/mojo/ipc_channel_mojo.h" +#include "ipc/mojo/scoped_ipc_support.h" #if defined(OS_WIN) #include "content/common/handle_enumerator_win.h" @@ -203,6 +205,42 @@ ChildThread* ChildThread::Get() { return ChildThreadImpl::current(); } +// Mojo client channel delegate to be used in single process mode. +class ChildThreadImpl::SingleProcessChannelDelegate + : public IPC::ChannelMojo::Delegate { + public: + explicit SingleProcessChannelDelegate() : weak_factory_(this) {} + + ~SingleProcessChannelDelegate() override {} + + base::WeakPtr<IPC::ChannelMojo::Delegate> ToWeakPtr() override { + return weak_factory_.GetWeakPtr(); + } + + scoped_refptr<base::TaskRunner> GetIOTaskRunner() override { + return ChannelInit::GetSingleProcessIOTaskRunner(); + } + + void OnChannelCreated(base::WeakPtr<IPC::ChannelMojo> channel) override {} + + void DeleteSoon() { + ChannelInit::GetSingleProcessIOTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&base::DeletePointer<SingleProcessChannelDelegate>, + base::Unretained(this))); + } + + private: + base::WeakPtrFactory<IPC::ChannelMojo::Delegate> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(SingleProcessChannelDelegate); +}; + +void ChildThreadImpl::SingleProcessChannelDelegateDeleter::operator()( + SingleProcessChannelDelegate* delegate) const { + delegate->DeleteSoon(); +} + ChildThreadImpl::Options::Options() : channel_name(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProcessChannelID)), @@ -250,8 +288,20 @@ void ChildThreadImpl::ConnectChannel(bool use_mojo_channel) { bool create_pipe_now = true; if (use_mojo_channel) { VLOG(1) << "Mojo is enabled on child"; - channel_->Init(IPC::ChannelMojo::CreateClientFactory(channel_name_), - create_pipe_now); + scoped_refptr<base::TaskRunner> io_task_runner = + ChannelInit::GetSingleProcessIOTaskRunner(); + if (io_task_runner) { + single_process_channel_delegate_.reset(new SingleProcessChannelDelegate); + } else { + io_task_runner = ChildProcess::current()->io_message_loop_proxy(); + } + DCHECK(io_task_runner); + ipc_support_.reset(new IPC::ScopedIPCSupport(io_task_runner)); + channel_->Init( + IPC::ChannelMojo::CreateClientFactory( + single_process_channel_delegate_.get(), + channel_name_), + create_pipe_now); return; } diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h index 4016861..b6094b5 100644 --- a/content/child/child_thread_impl.h +++ b/content/child/child_thread_impl.h @@ -29,6 +29,7 @@ class TraceMemoryController; namespace IPC { class MessageFilter; +class ScopedIPCSupport; class SyncChannel; class SyncMessageFilter; } // namespace IPC @@ -239,6 +240,15 @@ class CONTENT_EXPORT ChildThreadImpl void EnsureConnected(); + class SingleProcessChannelDelegate; + class SingleProcessChannelDelegateDeleter { + public: + void operator()(SingleProcessChannelDelegate* delegate) const; + }; + + scoped_ptr<IPC::ScopedIPCSupport> ipc_support_; + scoped_ptr<SingleProcessChannelDelegate, SingleProcessChannelDelegateDeleter> + single_process_channel_delegate_; scoped_ptr<MojoApplication> mojo_application_; std::string channel_name_; diff --git a/content/child/mojo/mojo_application.cc b/content/child/mojo/mojo_application.cc index 34fc314..f896dc6 100644 --- a/content/child/mojo/mojo_application.cc +++ b/content/child/mojo/mojo_application.cc @@ -6,6 +6,7 @@ #include "content/child/child_process.h" #include "content/common/application_setup.mojom.h" +#include "content/common/mojo/channel_init.h" #include "content/common/mojo/mojo_messages.h" #include "ipc/ipc_message.h" #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h" @@ -34,9 +35,15 @@ void MojoApplication::OnActivate( #elif defined(OS_WIN) base::PlatformFile handle = file; #endif + scoped_refptr<base::TaskRunner> io_task_runner = + ChannelInit::GetSingleProcessIOTaskRunner(); + if (!io_task_runner) { + io_task_runner = ChildProcess::current()->io_message_loop_proxy(); + } + DCHECK(io_task_runner); + mojo::ScopedMessagePipeHandle message_pipe = - channel_init_.Init(handle, - ChildProcess::current()->io_message_loop_proxy()); + channel_init_.Init(handle, io_task_runner); DCHECK(message_pipe.is_valid()); ApplicationSetupPtr application_setup; diff --git a/content/child/mojo/mojo_application.h b/content/child/mojo/mojo_application.h index 481c072..5b89ddb 100644 --- a/content/child/mojo/mojo_application.h +++ b/content/child/mojo/mojo_application.h @@ -5,9 +5,9 @@ #ifndef CONTENT_CHILD_MOJO_MOJO_APPLICATION_H_ #define CONTENT_CHILD_MOJO_MOJO_APPLICATION_H_ +#include "content/common/mojo/channel_init.h" #include "content/common/mojo/service_registry_impl.h" #include "ipc/ipc_platform_file.h" -#include "third_party/mojo/src/mojo/edk/embedder/channel_init.h" namespace IPC { class Message; @@ -31,7 +31,7 @@ class MojoApplication { private: void OnActivate(const IPC::PlatformFileForTransit& file); - mojo::embedder::ChannelInit channel_init_; + ChannelInit channel_init_; ServiceRegistryImpl service_registry_; diff --git a/content/common/mojo/channel_init.cc b/content/common/mojo/channel_init.cc new file mode 100644 index 0000000..1599eb11 --- /dev/null +++ b/content/common/mojo/channel_init.cc @@ -0,0 +1,80 @@ +// 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. + +#include "content/common/mojo/channel_init.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/lazy_instance.h" +#include "base/message_loop/message_loop.h" +#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" + +namespace content { + +namespace { + +base::LazyInstance<scoped_refptr<base::TaskRunner>> + g_single_process_task_runner = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +ChannelInit::ChannelInit() : channel_info_(nullptr), weak_factory_(this) {} + +ChannelInit::~ChannelInit() { + if (channel_info_) + mojo::embedder::DestroyChannel(channel_info_, + base::Bind(&base::DoNothing), nullptr); +} + +mojo::ScopedMessagePipeHandle ChannelInit::Init( + base::PlatformFile file, + scoped_refptr<base::TaskRunner> io_thread_task_runner) { + scoped_ptr<IPC::ScopedIPCSupport> ipc_support( + new IPC::ScopedIPCSupport(io_thread_task_runner)); + mojo::ScopedMessagePipeHandle message_pipe = + mojo::embedder::CreateChannel( + mojo::embedder::ScopedPlatformHandle( + mojo::embedder::PlatformHandle(file)), + io_thread_task_runner, + base::Bind(&ChannelInit::OnCreatedChannel, + weak_factory_.GetWeakPtr(), + base::Passed(&ipc_support)), + base::MessageLoop::current()->task_runner()).Pass(); + return message_pipe.Pass(); +} + +void ChannelInit::WillDestroySoon() { + if (channel_info_) + mojo::embedder::WillDestroyChannelSoon(channel_info_); +} + +// static +scoped_refptr<base::TaskRunner> ChannelInit::GetSingleProcessIOTaskRunner() { + return g_single_process_task_runner.Get(); +} + +// static +void ChannelInit::SetSingleProcessIOTaskRunner( + scoped_refptr<base::TaskRunner> io_task_runner) { + g_single_process_task_runner.Get() = io_task_runner; +} + +// static +void ChannelInit::OnCreatedChannel( + base::WeakPtr<ChannelInit> self, + scoped_ptr<IPC::ScopedIPCSupport> ipc_support, + mojo::embedder::ChannelInfo* channel) { + // If |self| was already destroyed, shut the channel down. + if (!self) { + mojo::embedder::DestroyChannel(channel, + base::Bind(&base::DoNothing), nullptr); + return; + } + + DCHECK(!self->channel_info_); + self->channel_info_ = channel; + self->ipc_support_ = ipc_support.Pass(); +} + +} // namespace content diff --git a/content/common/mojo/channel_init.h b/content/common/mojo/channel_init.h new file mode 100644 index 0000000..659d4bb --- /dev/null +++ b/content/common/mojo/channel_init.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef CONTENT_COMMON_MOJO_CHANNEL_INIT_H_ +#define CONTENT_COMMON_MOJO_CHANNEL_INIT_H_ + +#include "base/files/file.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "content/common/content_export.h" +#include "ipc/mojo/scoped_ipc_support.h" +#include "third_party/mojo/src/mojo/edk/embedder/channel_info_forward.h" +#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" + +namespace base { +class MessageLoopProxy; +class TaskRunner; +} + +namespace content { + +// ChannelInit handles creation and destruction of the Mojo channel. It is not +// thread-safe, but may be used on any single thread with a MessageLoop. +class CONTENT_EXPORT ChannelInit { + public: + ChannelInit(); + ~ChannelInit(); + + // Initializes the channel. This takes ownership of |file|. Returns the + // primordial MessagePipe for the channel. + mojo::ScopedMessagePipeHandle Init( + base::PlatformFile file, + scoped_refptr<base::TaskRunner> io_thread_task_runner); + + // Notifies the channel that we (hence it) will soon be destroyed. + void WillDestroySoon(); + + // Get/Set a shared I/O TaskRunner for children to use in single process mode. + static scoped_refptr<base::TaskRunner> GetSingleProcessIOTaskRunner(); + static void SetSingleProcessIOTaskRunner( + scoped_refptr<base::TaskRunner> io_task_runner); + + private: + // Invoked on the thread on which this object lives once the channel has been + // established. This is a static method that takes a weak pointer to self, + // since we want to destroy the channel if we were destroyed first. + static void OnCreatedChannel( + base::WeakPtr<ChannelInit> self, + scoped_ptr<IPC::ScopedIPCSupport> ipc_support, + mojo::embedder::ChannelInfo* channel); + + // If non-null the channel has been established. + mojo::embedder::ChannelInfo* channel_info_; + + scoped_ptr<IPC::ScopedIPCSupport> ipc_support_; + base::WeakPtrFactory<ChannelInit> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ChannelInit); +}; + +} // namespace content + +#endif // CONTENT_COMMON_MOJO_CHANNEL_INIT_H_ diff --git a/content/content_common.gypi b/content/content_common.gypi index c62e10f..3a63205 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -416,6 +416,8 @@ 'common/message_router.cc', 'common/message_router.h', 'common/mime_registry_messages.h', + 'common/mojo/channel_init.cc', + 'common/mojo/channel_init.h', 'common/mojo/mojo_messages.h', 'common/mojo/service_registry_impl.cc', 'common/mojo/service_registry_impl.h', diff --git a/content/test/data/web_ui_mojo.js b/content/test/data/web_ui_mojo.js index 38b0194..978d068 100644 --- a/content/test/data/web_ui_mojo.js +++ b/content/test/data/web_ui_mojo.js @@ -3,17 +3,16 @@ // found in the LICENSE file. define('main', [ - 'mojo/public/js/connection', + 'mojo/public/js/router', 'content/test/data/web_ui_test_mojo_bindings.mojom', 'content/public/renderer/service_provider', -], function (connection, bindings, serviceProvider) { +], function (router, bindings, serviceProvider) { var browserTarget; return function() { - browserTarget = connection.bindProxyHandle( - serviceProvider.connectToService(bindings.BrowserTarget.name), - undefined, - bindings.BrowserTarget); + browserTarget = new bindings.BrowserTarget.proxyClass( + new router.Router( + serviceProvider.connectToService(bindings.BrowserTarget.name))); browserTarget.start().then(function() { browserTarget.stop(); diff --git a/content/test/render_thread_impl_browser_test_ipc_helper.cc b/content/test/render_thread_impl_browser_test_ipc_helper.cc index 59d09c2..3075f75 100644 --- a/content/test/render_thread_impl_browser_test_ipc_helper.cc +++ b/content/test/render_thread_impl_browser_test_ipc_helper.cc @@ -4,6 +4,7 @@ #include "content/test/render_thread_impl_browser_test_ipc_helper.h" +#include "content/common/mojo/channel_init.h" #include "ipc/mojo/ipc_channel_mojo_host.h" #include "testing/gtest/include/gtest/gtest.h" @@ -40,13 +41,17 @@ void RenderThreadImplBrowserIPCTestHelper::SetupIpcThread() { base::Thread::Options options; options.message_loop_type = base::MessageLoop::TYPE_IO; ASSERT_TRUE(ipc_thread_->StartWithOptions(options)); + ChannelInit::SetSingleProcessIOTaskRunner(ipc_thread_->task_runner()); } void RenderThreadImplBrowserIPCTestHelper::SetupMojo() { InitializeMojo(); + ipc_support_.reset(new IPC::ScopedIPCSupport(ipc_thread_->task_runner())); mojo_host_.reset(new IPC::ChannelMojoHost(ipc_thread_->task_runner())); mojo_application_host_.reset(new MojoApplicationHost()); + mojo_application_host_->OverrideIOTaskRunnerForTest( + ipc_thread_->task_runner()); channel_ = IPC::ChannelProxy::Create( IPC::ChannelMojo::CreateServerFactory(mojo_host_->channel_delegate(), diff --git a/content/test/render_thread_impl_browser_test_ipc_helper.h b/content/test/render_thread_impl_browser_test_ipc_helper.h index ce3b257..b2eebd4 100644 --- a/content/test/render_thread_impl_browser_test_ipc_helper.h +++ b/content/test/render_thread_impl_browser_test_ipc_helper.h @@ -10,6 +10,7 @@ #include "content/common/mojo/mojo_messages.h" #include "ipc/ipc_channel_proxy.h" #include "ipc/mojo/ipc_channel_mojo.h" +#include "ipc/mojo/scoped_ipc_support.h" namespace IPC { class ChannelMojoHost; @@ -42,6 +43,7 @@ class RenderThreadImplBrowserIPCTestHelper { scoped_ptr<base::Thread> ipc_thread_; scoped_ptr<base::MessageLoopForIO> message_loop_; scoped_ptr<DummyListener> dummy_listener_; + scoped_ptr<IPC::ScopedIPCSupport> ipc_support_; scoped_ptr<MojoApplicationHost> mojo_application_host_; scoped_ptr<IPC::ChannelMojoHost> mojo_host_; std::string channel_id_; diff --git a/crypto/crypto.gyp b/crypto/crypto.gyp index 632c498..c8fdbc5 100644 --- a/crypto/crypto.gyp +++ b/crypto/crypto.gyp @@ -238,7 +238,7 @@ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', ], 'sources': [ - '<@(hmac_win64_related_sources)', + '<@(nacl_win64_sources)', ], 'defines': [ 'CRYPTO_IMPLEMENTATION', diff --git a/crypto/crypto.gypi b/crypto/crypto.gypi index 82b5da5..6fdbdf4 100644 --- a/crypto/crypto.gypi +++ b/crypto/crypto.gypi @@ -23,7 +23,6 @@ 'wincrypt_shim.h', ], }, - 'hmac_win64_related_sources': [ '<@(hmac_win64_related_sources)' ], 'crypto_sources': [ # NOTE: all transitive dependencies of HMAC on windows need # to be placed in the source list above. @@ -100,6 +99,11 @@ 'third_party/nss/pk11akey.cc', 'third_party/nss/rsawrapr.c', 'third_party/nss/secsign.cc', - ] + ], + 'nacl_win64_sources': [ + '<@(hmac_win64_related_sources)', + 'random.cc', + 'random.h', + ], } } diff --git a/ipc/mojo/BUILD.gn b/ipc/mojo/BUILD.gn index 39855c4..6d2a9f8 100644 --- a/ipc/mojo/BUILD.gn +++ b/ipc/mojo/BUILD.gn @@ -28,6 +28,8 @@ component("mojo") { "ipc_mojo_handle_attachment.h", "ipc_mojo_message_helper.cc", "ipc_mojo_message_helper.h", + "scoped_ipc_support.cc", + "scoped_ipc_support.h", ] defines = [ "IPC_MOJO_IMPLEMENTATION" ] diff --git a/ipc/mojo/ipc_channel_mojo.cc b/ipc/mojo/ipc_channel_mojo.cc index 0c6c994..6cf8130 100644 --- a/ipc/mojo/ipc_channel_mojo.cc +++ b/ipc/mojo/ipc_channel_mojo.cc @@ -48,9 +48,9 @@ class MojoChannelFactory : public ChannelFactory { //------------------------------------------------------------------------------ -class ClientChannelMojo - : public ChannelMojo, - public NON_EXPORTED_BASE(mojo::InterfaceImpl<ClientChannel>) { +class ClientChannelMojo : public ChannelMojo, + public ClientChannel, + public mojo::ErrorHandler { public: ClientChannelMojo(ChannelMojo::Delegate* delegate, const ChannelHandle& handle, @@ -58,7 +58,7 @@ class ClientChannelMojo ~ClientChannelMojo() override; // MojoBootstrap::Delegate implementation void OnPipeAvailable(mojo::embedder::ScopedPlatformHandle handle) override; - // InterfaceImpl implementation + // mojo::ErrorHandler implementation void OnConnectionError() override; // ClientChannel implementation void Init( @@ -66,13 +66,17 @@ class ClientChannelMojo int32_t peer_pid, const mojo::Callback<void(int32_t)>& callback) override; + private: + mojo::Binding<ClientChannel> binding_; + DISALLOW_COPY_AND_ASSIGN(ClientChannelMojo); }; ClientChannelMojo::ClientChannelMojo(ChannelMojo::Delegate* delegate, const ChannelHandle& handle, Listener* listener) - : ChannelMojo(delegate, handle, Channel::MODE_CLIENT, listener) { + : ChannelMojo(delegate, handle, Channel::MODE_CLIENT, listener), + binding_(this) { } ClientChannelMojo::~ClientChannelMojo() { @@ -80,7 +84,7 @@ ClientChannelMojo::~ClientChannelMojo() { void ClientChannelMojo::OnPipeAvailable( mojo::embedder::ScopedPlatformHandle handle) { - mojo::WeakBindToPipe(this, CreateMessagingPipe(handle.Pass())); + binding_.Bind(CreateMessagingPipe(handle.Pass())); } void ClientChannelMojo::OnConnectionError() { @@ -106,7 +110,7 @@ class ServerChannelMojo : public ChannelMojo, public mojo::ErrorHandler { // MojoBootstrap::Delegate implementation void OnPipeAvailable(mojo::embedder::ScopedPlatformHandle handle) override; - // ErrorHandler implementation + // mojo::ErrorHandler implementation void OnConnectionError() override; // Channel override void Close() override; @@ -180,7 +184,7 @@ base::ScopedFD TakeOrDupFile(internal::PlatformFileAttachment* attachment) { void ChannelMojo::ChannelInfoDeleter::operator()( mojo::embedder::ChannelInfo* ptr) const { - mojo::embedder::DestroyChannel(ptr); + mojo::embedder::DestroyChannel(ptr, base::Bind(&base::DoNothing), nullptr); } //------------------------------------------------------------------------------ @@ -219,9 +223,10 @@ scoped_ptr<ChannelFactory> ChannelMojo::CreateServerFactory( // static scoped_ptr<ChannelFactory> ChannelMojo::CreateClientFactory( + ChannelMojo::Delegate* delegate, const ChannelHandle& channel_handle) { return make_scoped_ptr( - new MojoChannelFactory(NULL, channel_handle, Channel::MODE_CLIENT)); + new MojoChannelFactory(delegate, channel_handle, Channel::MODE_CLIENT)); } ChannelMojo::ChannelMojo(ChannelMojo::Delegate* delegate, @@ -253,6 +258,8 @@ ChannelMojo::~ChannelMojo() { } void ChannelMojo::InitDelegate(ChannelMojo::Delegate* delegate) { + ipc_support_.reset( + new ScopedIPCSupport(base::MessageLoop::current()->task_runner())); delegate_ = delegate->ToWeakPtr(); delegate_->OnChannelCreated(weak_factory_.GetWeakPtr()); } @@ -275,6 +282,7 @@ bool ChannelMojo::Connect() { void ChannelMojo::Close() { message_reader_.reset(); channel_info_.reset(); + ipc_support_.reset(); } void ChannelMojo::OnBootstrapError() { @@ -288,7 +296,7 @@ void ChannelMojo::InitMessageReader(mojo::ScopedMessagePipeHandle pipe, for (size_t i = 0; i < pending_messages_.size(); ++i) { bool sent = message_reader_->Send(make_scoped_ptr(pending_messages_[i])); - pending_messages_[i] = NULL; + pending_messages_[i] = nullptr; if (!sent) { pending_messages_.clear(); listener_->OnChannelError(); diff --git a/ipc/mojo/ipc_channel_mojo.h b/ipc/mojo/ipc_channel_mojo.h index 71d1d10..1959e0f 100644 --- a/ipc/mojo/ipc_channel_mojo.h +++ b/ipc/mojo/ipc_channel_mojo.h @@ -15,6 +15,7 @@ #include "ipc/ipc_export.h" #include "ipc/mojo/ipc_message_pipe_reader.h" #include "ipc/mojo/ipc_mojo_bootstrap.h" +#include "ipc/mojo/scoped_ipc_support.h" #include "third_party/mojo/src/mojo/edk/embedder/channel_info_forward.h" #include "third_party/mojo/src/mojo/public/cpp/system/core.h" @@ -75,6 +76,7 @@ class IPC_MOJO_EXPORT ChannelMojo const ChannelHandle& channel_handle); static scoped_ptr<ChannelFactory> CreateClientFactory( + Delegate* delegate, const ChannelHandle& channel_handle); ~ChannelMojo() override; @@ -147,6 +149,8 @@ class IPC_MOJO_EXPORT ChannelMojo scoped_ptr<internal::MessagePipeReader, ReaderDeleter> message_reader_; ScopedVector<Message> pending_messages_; + scoped_ptr<ScopedIPCSupport> ipc_support_; + base::WeakPtrFactory<ChannelMojo> weak_factory_; DISALLOW_COPY_AND_ASSIGN(ChannelMojo); diff --git a/ipc/mojo/ipc_channel_mojo_unittest.cc b/ipc/mojo/ipc_channel_mojo_unittest.cc index d0ab5d6..c6d1894 100644 --- a/ipc/mojo/ipc_channel_mojo_unittest.cc +++ b/ipc/mojo/ipc_channel_mojo_unittest.cc @@ -9,6 +9,7 @@ #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/pickle.h" +#include "base/run_loop.h" #include "base/test/test_timeouts.h" #include "base/threading/thread.h" #include "ipc/ipc_message.h" @@ -17,6 +18,7 @@ #include "ipc/mojo/ipc_channel_mojo_host.h" #include "ipc/mojo/ipc_mojo_handle_attachment.h" #include "ipc/mojo/ipc_mojo_message_helper.h" +#include "ipc/mojo/scoped_ipc_support.h" #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" @@ -63,6 +65,8 @@ class ListenerThatExpectsOK : public IPC::Listener { class ChannelClient { public: explicit ChannelClient(IPC::Listener* listener, const char* name) { + ipc_support_.reset( + new IPC::ScopedIPCSupport(main_message_loop_.task_runner())); channel_ = IPC::ChannelMojo::Create(NULL, IPCTestBase::GetChannelName(name), IPC::Channel::MODE_CLIENT, @@ -73,14 +77,45 @@ class ChannelClient { CHECK(channel_->Connect()); } + void Close() { + channel_->Close(); + + base::RunLoop run_loop; + base::MessageLoop::current()->PostTask(FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + } + IPC::ChannelMojo* channel() const { return channel_.get(); } private: base::MessageLoopForIO main_message_loop_; + scoped_ptr<IPC::ScopedIPCSupport> ipc_support_; scoped_ptr<IPC::ChannelMojo> channel_; }; -class IPCChannelMojoTest : public IPCTestBase { +class IPCChannelMojoTestBase : public IPCTestBase { + public: + void InitWithMojo(const std::string& test_client_name) { + Init(test_client_name); + ipc_support_.reset(new IPC::ScopedIPCSupport(task_runner())); + } + + void TearDown() override { + // Make sure Mojo IPC support is properly shutdown on the I/O loop before + // TearDown continues. + ipc_support_.reset(); + base::RunLoop run_loop; + task_runner()->PostTask(FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + + IPCTestBase::TearDown(); + } + + private: + scoped_ptr<IPC::ScopedIPCSupport> ipc_support_; +}; + +class IPCChannelMojoTest : public IPCChannelMojoTestBase { protected: scoped_ptr<IPC::ChannelFactory> CreateChannelFactory( const IPC::ChannelHandle& handle, @@ -122,7 +157,7 @@ class TestChannelListenerWithExtraExpectations }; TEST_F(IPCChannelMojoTest, ConnectedFromClient) { - Init("IPCChannelMojoTestClient"); + InitWithMojo("IPCChannelMojoTestClient"); // Set up IPC channel and start client. TestChannelListenerWithExtraExpectations listener; @@ -159,6 +194,8 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestClient) { EXPECT_TRUE(listener.is_connected_called()); EXPECT_TRUE(listener.HasSentAll()); + client.Close(); + return 0; } @@ -186,7 +223,7 @@ class ListenerExpectingErrors : public IPC::Listener { }; -class IPCChannelMojoErrorTest : public IPCTestBase { +class IPCChannelMojoErrorTest : public IPCChannelMojoTestBase { protected: scoped_ptr<IPC::ChannelFactory> CreateChannelFactory( const IPC::ChannelHandle& handle, @@ -229,11 +266,13 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoErraticTestClient) { base::MessageLoop::current()->Run(); + client.Close(); + return 0; } TEST_F(IPCChannelMojoErrorTest, SendFailWithPendingMessages) { - Init("IPCChannelMojoErraticTestClient"); + InitWithMojo("IPCChannelMojoErraticTestClient"); // Set up IPC channel and start client. ListenerExpectingErrors listener; @@ -373,7 +412,7 @@ class ListenerThatExpectsMessagePipe : public IPC::Listener { }; TEST_F(IPCChannelMojoTest, SendMessagePipe) { - Init("IPCChannelMojoTestSendMessagePipeClient"); + InitWithMojo("IPCChannelMojoTestSendMessagePipeClient"); ListenerThatExpectsOK listener; CreateChannel(&listener); @@ -398,11 +437,13 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestSendMessagePipeClient) { base::MessageLoop::current()->Run(); + client.Close(); + return 0; } #if defined(OS_WIN) -class IPCChannelMojoDeadHandleTest : public IPCTestBase { +class IPCChannelMojoDeadHandleTest : public IPCChannelMojoTestBase { protected: virtual scoped_ptr<IPC::ChannelFactory> CreateChannelFactory( const IPC::ChannelHandle& handle, @@ -429,7 +470,7 @@ class IPCChannelMojoDeadHandleTest : public IPCTestBase { TEST_F(IPCChannelMojoDeadHandleTest, InvalidClientHandle) { // Any client type is fine as it is going to be killed anyway. - Init("IPCChannelMojoTestDoNothingClient"); + InitWithMojo("IPCChannelMojoTestDoNothingClient"); // Set up IPC channel and start client. ListenerExpectingErrors listener; @@ -489,7 +530,7 @@ class ListenerThatExpectsFile : public IPC::Listener { TEST_F(IPCChannelMojoTest, SendPlatformHandle) { - Init("IPCChannelMojoTestSendPlatformHandleClient"); + InitWithMojo("IPCChannelMojoTestSendPlatformHandleClient"); ListenerThatExpectsOK listener; CreateChannel(&listener); @@ -517,6 +558,8 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestSendPlatformHandleClient) { base::MessageLoop::current()->Run(); + client.Close(); + return 0; } @@ -544,7 +587,7 @@ class ListenerThatExpectsFileAndPipe : public IPC::Listener { }; TEST_F(IPCChannelMojoTest, SendPlatformHandleAndPipe) { - Init("IPCChannelMojoTestSendPlatformHandleAndPipeClient"); + InitWithMojo("IPCChannelMojoTestSendPlatformHandleAndPipeClient"); ListenerThatExpectsOK listener; CreateChannel(&listener); @@ -574,6 +617,8 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN( base::MessageLoop::current()->Run(); + client.Close(); + return 0; } @@ -597,7 +642,7 @@ class ListenerThatVerifiesPeerPid : public IPC::Listener { }; TEST_F(IPCChannelMojoTest, VerifyGlobalPid) { - Init("IPCChannelMojoTestVerifyGlobalPidClient"); + InitWithMojo("IPCChannelMojoTestVerifyGlobalPidClient"); ListenerThatVerifiesPeerPid listener; CreateChannel(&listener); @@ -605,7 +650,7 @@ TEST_F(IPCChannelMojoTest, VerifyGlobalPid) { ASSERT_TRUE(StartClient()); base::MessageLoop::current()->Run(); - this->channel()->Close(); + channel()->Close(); EXPECT_TRUE(WaitForClientShutdown()); DestroyChannel(); @@ -620,6 +665,8 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestVerifyGlobalPidClient) { base::MessageLoop::current()->Run(); + client.Close(); + return 0; } diff --git a/ipc/mojo/ipc_mojo.gyp b/ipc/mojo/ipc_mojo.gyp index 0f746fc..44f1817 100644 --- a/ipc/mojo/ipc_mojo.gyp +++ b/ipc/mojo/ipc_mojo.gyp @@ -42,6 +42,8 @@ 'ipc_mojo_message_helper.h', 'ipc_message_pipe_reader.cc', 'ipc_message_pipe_reader.h', + 'scoped_ipc_support.cc', + 'scoped_ipc_support.h', ], # TODO(gregoryd): direct_dependent_settings should be shared with the # 64-bit target, but it doesn't work due to a bug in gyp diff --git a/ipc/mojo/scoped_ipc_support.cc b/ipc/mojo/scoped_ipc_support.cc new file mode 100644 index 0000000..fafc9c2 --- /dev/null +++ b/ipc/mojo/scoped_ipc_support.cc @@ -0,0 +1,112 @@ +// 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. + +#include "ipc/mojo/scoped_ipc_support.h" + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" +#include "third_party/mojo/src/mojo/edk/embedder/process_delegate.h" + +namespace IPC { + +namespace { + +class IPCSupportInitializer : public mojo::embedder::ProcessDelegate { + public: + IPCSupportInitializer() + : init_count_(0), + shutting_down_(false) { + } + + ~IPCSupportInitializer() override {} + + void Init(scoped_refptr<base::TaskRunner> io_thread_task_runner) { + base::AutoLock locker(lock_); + DCHECK((init_count_ == 0 && !io_thread_task_runner_) || + io_thread_task_runner_ == io_thread_task_runner); + + if (shutting_down_) { + // If reinitialized before a pending shutdown task is executed, we + // effectively cancel the shutdown task. + DCHECK(init_count_ == 1); + shutting_down_ = false; + return; + } + + init_count_++; + if (init_count_ == 1) { + io_thread_task_runner_ = io_thread_task_runner; + mojo::embedder::InitIPCSupport(mojo::embedder::ProcessType::NONE, + io_thread_task_runner_, + this, io_thread_task_runner_, + mojo::embedder::ScopedPlatformHandle()); + } + } + + void ShutDown() { + base::AutoLock locker(lock_); + DCHECK(init_count_ > 0); + DCHECK(!shutting_down_); + + if (init_count_ > 1) { + init_count_--; + return; + } + + shutting_down_ = true; + if (base::MessageLoop::current() && + base::MessageLoop::current()->task_runner() == io_thread_task_runner_) { + base::AutoUnlock unlocker_(lock_); + ShutDownOnIOThread(); + } else { + io_thread_task_runner_->PostTask( + FROM_HERE, + base::Bind(&IPCSupportInitializer::ShutDownOnIOThread, + base::Unretained(this))); + } + } + + private: + void ShutDownOnIOThread() { + base::AutoLock locker(lock_); + if (shutting_down_) { + DCHECK(init_count_ == 1); + mojo::embedder::ShutdownIPCSupportOnIOThread(); + init_count_ = 0; + shutting_down_ = false; + io_thread_task_runner_ = nullptr; + } + } + + void OnShutdownComplete() override {} + + base::Lock lock_; + size_t init_count_; + bool shutting_down_; + + scoped_refptr<base::TaskRunner> io_thread_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(IPCSupportInitializer); +}; + +base::LazyInstance<IPCSupportInitializer>::Leaky ipc_support_initializer; + +} // namespace + +ScopedIPCSupport::ScopedIPCSupport( + scoped_refptr<base::TaskRunner> io_thread_task_runner) { + ipc_support_initializer.Get().Init(io_thread_task_runner); +} + +ScopedIPCSupport::~ScopedIPCSupport() { + ipc_support_initializer.Get().ShutDown(); +} + +} // namespace IPC diff --git a/ipc/mojo/scoped_ipc_support.h b/ipc/mojo/scoped_ipc_support.h new file mode 100644 index 0000000..21013fa --- /dev/null +++ b/ipc/mojo/scoped_ipc_support.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef IPC_MOJO_SCOPED_IPC_SUPPORT_H_ +#define IPC_MOJO_SCOPED_IPC_SUPPORT_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/task_runner.h" +#include "ipc/ipc_export.h" + +namespace IPC { + +// Perform any necessary Mojo IPC initialization. A ScopedIPCSupport object +// should be instantiated and retained by any component which makes direct calls +// to the Mojo EDK. This is used to ensure that the EDK is initialized within +// the current process and that it is shutdown cleanly when no longer in use. +// +// NOTE: Unless you are making explicit calls to functions in the +// mojo::embedder namespace, you almost definitely DO NOT need this and should +// not be using it. +class IPC_MOJO_EXPORT ScopedIPCSupport { + public: + ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner); + ~ScopedIPCSupport(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport); +}; + +} // namespace IPC + +#endif // IPC_MOJO_SCOPED_IPC_SUPPORT_H_ diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn index c21dc3f..d99e716 100644 --- a/mojo/android/BUILD.gn +++ b/mojo/android/BUILD.gn @@ -85,7 +85,6 @@ android_library("mojo_javatests") { "javatests/src/org/chromium/mojo/bindings/ValidationTest.java", "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java", "javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java", - "javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java", "javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java", ] diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java index 4744508..3b332e1 100644 --- a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java +++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java @@ -10,7 +10,6 @@ import org.chromium.mojo.MojoTestCase; import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler; import org.chromium.mojo.bindings.test.mojom.imported.ImportedInterface; import org.chromium.mojo.bindings.test.mojom.sample.Factory; -import org.chromium.mojo.bindings.test.mojom.sample.FactoryClient; import org.chromium.mojo.bindings.test.mojom.sample.NamedObject; import org.chromium.mojo.bindings.test.mojom.sample.NamedObject.GetNameResponse; import org.chromium.mojo.bindings.test.mojom.sample.Request; @@ -101,18 +100,11 @@ public class InterfacesTest extends MojoTestCase { public class MockFactoryImpl extends CapturingErrorHandler implements Factory { private boolean mClosed = false; - private FactoryClient mFactoryClient; public boolean isClosed() { return mClosed; } - @Override - public void setClient(FactoryClient client) { - mFactoryClient = client; - mCloseablesToClose.add(client); - } - /** * @see org.chromium.mojo.bindings.Interface#close() */ @@ -122,17 +114,18 @@ public class InterfacesTest extends MojoTestCase { } @Override - public void doStuff(Request request, MessagePipeHandle pipe) { + public void doStuff(Request request, MessagePipeHandle pipe, DoStuffResponse callback) { if (pipe != null) { pipe.close(); } Response response = new Response(); response.x = 42; - mFactoryClient.didStuff(response, "Hello"); + + callback.call(response, "Hello"); } @Override - public void doStuff2(ConsumerHandle pipe) { + public void doStuff2(ConsumerHandle pipe, DoStuff2Response callback) { } @Override @@ -154,54 +147,6 @@ public class InterfacesTest extends MojoTestCase { } /** - * Basic implementation of {@link FactoryClient}. - */ - public static class MockFactoryClientImpl implements FactoryClient { - - private boolean mClosed = false; - private boolean mDidStuffCalled = false; - - public boolean isClosed() { - return mClosed; - } - - public boolean wasDidStuffCalled() { - return mDidStuffCalled; - } - - /** - * @see org.chromium.mojo.bindings.Interface#close() - */ - @Override - public void close() { - mClosed = true; - } - - /** - * @see ConnectionErrorHandler#onConnectionError(MojoException) - */ - @Override - public void onConnectionError(MojoException e) { - } - - /** - * @see FactoryClient#didStuff(Response, java.lang.String) - */ - @Override - public void didStuff(Response response, String text) { - mDidStuffCalled = true; - } - - /** - * @see FactoryClient#didStuff2(String) - */ - @Override - public void didStuff2(String text) { - } - - } - - /** * @see MojoTestCase#tearDown() */ @Override @@ -225,17 +170,6 @@ public class InterfacesTest extends MojoTestCase { return proxy; } - private <I extends InterfaceWithClient<C>, P extends InterfaceWithClient.Proxy<C>, - C extends Interface> P newProxyOverPipeWithClient( - InterfaceWithClient.Manager<I, P, C> manager, I impl, C client) { - Pair<MessagePipeHandle, MessagePipeHandle> handles = - CoreImpl.getInstance().createMessagePipe(null); - P proxy = manager.attachProxy(handles.first, client); - mCloseablesToClose.add(proxy); - manager.bind(impl, handles.second); - return proxy; - } - /** * Check that the given proxy receives the calls. If |impl| is not null, also check that the * calls are forwared to |impl|. @@ -311,34 +245,13 @@ public class InterfacesTest extends MojoTestCase { @SmallTest public void testInterfaceClosing() { MockFactoryImpl impl = new MockFactoryImpl(); - MockFactoryClientImpl client = new MockFactoryClientImpl(); - Factory.Proxy proxy = newProxyOverPipeWithClient( - Factory.MANAGER, impl, client); + Factory.Proxy proxy = newProxyOverPipe(Factory.MANAGER, impl); assertFalse(impl.isClosed()); - assertFalse(client.isClosed()); proxy.close(); runLoopUntilIdle(); assertTrue(impl.isClosed()); - assertTrue(client.isClosed()); - } - - @SmallTest - public void testClient() { - MockFactoryImpl impl = new MockFactoryImpl(); - MockFactoryClientImpl client = new MockFactoryClientImpl(); - Factory.Proxy proxy = newProxyOverPipeWithClient( - Factory.MANAGER, impl, client); - Request request = new Request(); - request.x = 42; - proxy.doStuff(request, null); - - assertFalse(client.wasDidStuffCalled()); - - runLoopUntilIdle(); - - assertTrue(client.wasDidStuffCalled()); } } diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java index 076c94b..7942be6 100644 --- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java +++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java @@ -11,8 +11,7 @@ import org.chromium.base.test.util.UrlUtils; import org.chromium.mojo.HandleMock; import org.chromium.mojo.MojoTestCase; import org.chromium.mojo.bindings.test.mojom.mojo.ConformanceTestInterface; -import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface1; -import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface2TestHelper; +import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface; import org.chromium.mojo.system.Handle; import java.io.File; @@ -119,41 +118,6 @@ public class ValidationTest extends MojoTestCase { } } - private static class RoutingMessageReceiver implements MessageReceiver { - private final MessageReceiver mRequest; - private final MessageReceiver mResponse; - - private RoutingMessageReceiver(MessageReceiver request, MessageReceiver response) { - this.mRequest = request; - this.mResponse = response; - } - - /** - * @see MessageReceiver#accept(Message) - */ - @Override - public boolean accept(Message message) { - try { - MessageHeader header = message.asServiceMessage().getHeader(); - if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { - return mResponse.accept(message); - } else { - return mRequest.accept(message); - } - } catch (DeserializationException e) { - return false; - } - } - - /** - * @see MessageReceiver#close() - */ - @Override - public void close() { - } - - } - /** * A trivial message receiver that refuses all messages it receives. */ @@ -188,11 +152,7 @@ public class ValidationTest extends MojoTestCase { */ @SmallTest public void testIntegration() throws FileNotFoundException { - runTest("integration_", - new RoutingMessageReceiver(IntegrationTestInterface1.MANAGER.buildStub(null, - IntegrationTestInterface1.MANAGER.buildProxy(null, - new SinkMessageReceiver())), - IntegrationTestInterface2TestHelper - .newIntegrationTestInterface2MethodCallback())); + runTest("integration_", IntegrationTestInterface.MANAGER.buildStub(null, + IntegrationTestInterface.MANAGER.buildProxy(null, new SinkMessageReceiver()))); } } diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java deleted file mode 100644 index 2a2b6b8..0000000 --- a/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.mojo.bindings.test.mojom.mojo; - -import org.chromium.mojo.bindings.MessageReceiver; -import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface2.Method0Response; -import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface2_Internal.IntegrationTestInterface2Method0ResponseParamsForwardToCallback; - -/** - * Helper class to access {@link IntegrationTestInterface2_Internal} package protected method for - * tests. - */ -public class IntegrationTestInterface2TestHelper { - - private static final class SinkMethod0Response implements Method0Response { - @Override - public void call(byte[] arg1) { - } - } - - /** - * Creates a new {@link MessageReceiver} to use for the callback of - * |IntegrationTestInterface2#method0(Method0Response)|. - */ - public static MessageReceiver newIntegrationTestInterface2MethodCallback() { - return new IntegrationTestInterface2Method0ResponseParamsForwardToCallback( - new SinkMethod0Response()); - } -} diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java index 19b2d29..aaa49f2 100644 --- a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java +++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java @@ -16,6 +16,7 @@ import org.chromium.mojo.system.MessagePipeHandle; import org.chromium.mojo.system.MojoException; import org.chromium.mojo.system.MojoResult; import org.chromium.mojo.system.Pair; +import org.chromium.mojo.system.RunLoop; import org.chromium.mojo.system.SharedBufferHandle; import org.chromium.mojo.system.SharedBufferHandle.DuplicateOptions; import org.chromium.mojo.system.SharedBufferHandle.MapFlags; @@ -206,6 +207,22 @@ public class CoreImpl implements Core, AsyncWaiter { } /** + * @see Core#createDefaultRunLoop() + */ + @Override + public RunLoop createDefaultRunLoop() { + return null; + } + + /** + * @see Core#getCurrentRunLoop() + */ + @Override + public RunLoop getCurrentRunLoop() { + return null; + } + + /** * @see AsyncWaiter#asyncWait(Handle, Core.HandleSignals, long, Callback) */ @Override diff --git a/mojo/services/html_viewer/html_viewer.cc b/mojo/services/html_viewer/html_viewer.cc index 1278a24..c89447b 100644 --- a/mojo/services/html_viewer/html_viewer.cc +++ b/mojo/services/html_viewer/html_viewer.cc @@ -75,7 +75,9 @@ class HTMLViewerApplication : public mojo::Application { web_media_player_factory_(web_media_player_factory), is_headless_(is_headless) {} - void Initialize(ShellPtr shell, Array<String> args) override { + void Initialize(ShellPtr shell, + Array<String> args, + const String& url) override { ServiceProviderPtr service_provider; shell_ = shell.Pass(); shell_->ConnectToApplication("mojo:network_service", diff --git a/third_party/mojo/mojo_edk.gyp b/third_party/mojo/mojo_edk.gyp index 01edb62..20aa38a 100644 --- a/third_party/mojo/mojo_edk.gyp +++ b/third_party/mojo/mojo_edk.gyp @@ -24,6 +24,7 @@ 'dependencies': [ '../../base/base.gyp:base', '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../../crypto/crypto.gyp:crypto', ], 'includes': [ 'mojo_edk_system_impl.gypi', @@ -86,6 +87,8 @@ 'sources': [ 'src/mojo/edk/test/multiprocess_test_helper.cc', 'src/mojo/edk/test/multiprocess_test_helper.h', + 'src/mojo/edk/test/scoped_ipc_support.cc', + 'src/mojo/edk/test/scoped_ipc_support.h', 'src/mojo/edk/test/test_utils.h', 'src/mojo/edk/test/test_utils_posix.cc', 'src/mojo/edk/test/test_utils_win.cc', @@ -138,6 +141,7 @@ 'dependencies': [ '../../base/base.gyp:base_win64', '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + '../../crypto/crypto.gyp:crypto_nacl_win64', ], 'includes': [ 'mojo_edk_system_impl.gypi', diff --git a/third_party/mojo/mojo_edk_system_impl.gypi b/third_party/mojo/mojo_edk_system_impl.gypi index 50991bf..62b8529 100644 --- a/third_party/mojo/mojo_edk_system_impl.gypi +++ b/third_party/mojo/mojo_edk_system_impl.gypi @@ -14,8 +14,6 @@ 'sources': [ 'src/mojo/edk/embedder/configuration.h', 'src/mojo/edk/embedder/channel_info_forward.h', - 'src/mojo/edk/embedder/channel_init.cc', - 'src/mojo/edk/embedder/channel_init.h', 'src/mojo/edk/embedder/embedder.cc', 'src/mojo/edk/embedder/embedder.h', 'src/mojo/edk/embedder/embedder_internal.h', @@ -64,6 +62,7 @@ 'src/mojo/edk/system/core.h', 'src/mojo/edk/system/data_pipe.cc', 'src/mojo/edk/system/data_pipe.h', + 'src/mojo/edk/system/data_pipe_impl.h', 'src/mojo/edk/system/data_pipe_consumer_dispatcher.cc', 'src/mojo/edk/system/data_pipe_consumer_dispatcher.h', 'src/mojo/edk/system/data_pipe_producer_dispatcher.cc', @@ -77,12 +76,14 @@ 'src/mojo/edk/system/handle_table.h', 'src/mojo/edk/system/incoming_endpoint.cc', 'src/mojo/edk/system/incoming_endpoint.h', - 'src/mojo/edk/system/local_data_pipe.cc', - 'src/mojo/edk/system/local_data_pipe.h', + 'src/mojo/edk/system/local_data_pipe_impl.cc', + 'src/mojo/edk/system/local_data_pipe_impl.h', 'src/mojo/edk/system/local_message_pipe_endpoint.cc', 'src/mojo/edk/system/local_message_pipe_endpoint.h', 'src/mojo/edk/system/mapping_table.cc', 'src/mojo/edk/system/mapping_table.h', + 'src/mojo/edk/system/master_connection_manager.cc', + 'src/mojo/edk/system/master_connection_manager.h', 'src/mojo/edk/system/memory.cc', 'src/mojo/edk/system/memory.h', 'src/mojo/edk/system/message_in_transit.cc', @@ -108,8 +109,12 @@ 'src/mojo/edk/system/shared_buffer_dispatcher.h', 'src/mojo/edk/system/simple_dispatcher.cc', 'src/mojo/edk/system/simple_dispatcher.h', + 'src/mojo/edk/system/slave_connection_manager.cc', + 'src/mojo/edk/system/slave_connection_manager.h', 'src/mojo/edk/system/transport_data.cc', 'src/mojo/edk/system/transport_data.h', + 'src/mojo/edk/system/unique_identifier.cc', + 'src/mojo/edk/system/unique_identifier.h', 'src/mojo/edk/system/waiter.cc', 'src/mojo/edk/system/waiter.h', # Test-only code: diff --git a/third_party/mojo/mojo_edk_tests.gyp b/third_party/mojo/mojo_edk_tests.gyp index 91a777f..13d24cb 100644 --- a/third_party/mojo/mojo_edk_tests.gyp +++ b/third_party/mojo/mojo_edk_tests.gyp @@ -177,7 +177,7 @@ 'src/mojo/edk/system/core_test_base.h', 'src/mojo/edk/system/data_pipe_unittest.cc', 'src/mojo/edk/system/dispatcher_unittest.cc', - 'src/mojo/edk/system/local_data_pipe_unittest.cc', + 'src/mojo/edk/system/local_data_pipe_impl_unittest.cc', 'src/mojo/edk/system/memory_unittest.cc', 'src/mojo/edk/system/message_pipe_dispatcher_unittest.cc', 'src/mojo/edk/system/message_pipe_test_utils.h', @@ -193,6 +193,7 @@ 'src/mojo/edk/system/simple_dispatcher_unittest.cc', 'src/mojo/edk/system/test_utils.cc', 'src/mojo/edk/system/test_utils.h', + 'src/mojo/edk/system/unique_identifier_unittest.cc', 'src/mojo/edk/system/waiter_test_utils.cc', 'src/mojo/edk/system/waiter_test_utils.h', 'src/mojo/edk/system/waiter_unittest.cc', diff --git a/third_party/mojo/mojo_public.gyp b/third_party/mojo/mojo_public.gyp index 0ab9e87..683791d 100644 --- a/third_party/mojo/mojo_public.gyp +++ b/third_party/mojo/mojo_public.gyp @@ -333,7 +333,6 @@ 'src/mojo/public/interfaces/bindings/tests/no_module.mojom', 'src/mojo/public/interfaces/bindings/tests/rect.mojom', 'src/mojo/public/interfaces/bindings/tests/regression_tests.mojom', - 'src/mojo/public/interfaces/bindings/tests/regression_tests_import.mojom', 'src/mojo/public/interfaces/bindings/tests/sample_factory.mojom', 'src/mojo/public/interfaces/bindings/tests/sample_import.mojom', 'src/mojo/public/interfaces/bindings/tests/sample_import2.mojom', diff --git a/third_party/mojo/mojom_bindings_generator_variables.gypi b/third_party/mojo/mojom_bindings_generator_variables.gypi index 727eef0..d09c13c 100644 --- a/third_party/mojo/mojom_bindings_generator_variables.gypi +++ b/third_party/mojo/mojom_bindings_generator_variables.gypi @@ -19,7 +19,6 @@ '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl', '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl', '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl', - '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl', '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl', '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl', '<(DEPTH)/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl', diff --git a/third_party/mojo/src/mojo/edk/embedder/BUILD.gn b/third_party/mojo/src/mojo/edk/embedder/BUILD.gn index 5eb77d7..9650209 100644 --- a/third_party/mojo/src/mojo/edk/embedder/BUILD.gn +++ b/third_party/mojo/src/mojo/edk/embedder/BUILD.gn @@ -11,8 +11,6 @@ mojo_edk_source_set("embedder") { sources = [ "channel_info_forward.h", - "channel_init.cc", - "channel_init.h", "configuration.h", "embedder.cc", "embedder.h", @@ -84,6 +82,7 @@ mojo_edk_source_set("platform") { deps = [ "//base", + "//crypto", ] if (is_android) { @@ -100,6 +99,8 @@ mojo_edk_source_set("delegates") { sources = [ "master_process_delegate.h", + "process_delegate.h", + "process_type.h", "slave_process_delegate.h", ] diff --git a/third_party/mojo/src/mojo/edk/embedder/channel_init.cc b/third_party/mojo/src/mojo/edk/embedder/channel_init.cc deleted file mode 100644 index 0b6d76c..0000000 --- a/third_party/mojo/src/mojo/edk/embedder/channel_init.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/channel_init.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "mojo/edk/embedder/embedder.h" - -namespace mojo { -namespace embedder { - -ChannelInit::ChannelInit() : channel_info_(nullptr), weak_factory_(this) { -} - -ChannelInit::~ChannelInit() { - // TODO(vtl): This is likely leaky in common scenarios (we're on the main - // thread, which outlives the I/O thread, and we're destroyed after the I/O - // thread is destroyed. - if (channel_info_) - DestroyChannel(channel_info_); -} - -ScopedMessagePipeHandle ChannelInit::Init( - base::PlatformFile file, - scoped_refptr<base::TaskRunner> io_thread_task_runner) { - ScopedMessagePipeHandle message_pipe = - CreateChannel(ScopedPlatformHandle(PlatformHandle(file)), - io_thread_task_runner, - base::Bind(&ChannelInit::OnCreatedChannel, - weak_factory_.GetWeakPtr()), - base::MessageLoop::current()->task_runner()).Pass(); - return message_pipe.Pass(); -} - -void ChannelInit::WillDestroySoon() { - if (channel_info_) - WillDestroyChannelSoon(channel_info_); -} - -// static -void ChannelInit::OnCreatedChannel(base::WeakPtr<ChannelInit> self, - ChannelInfo* channel) { - // If |self| was already destroyed, shut the channel down. - if (!self) { - DestroyChannel(channel); - return; - } - - DCHECK(!self->channel_info_); - self->channel_info_ = channel; -} - -} // namespace embedder -} // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/embedder/channel_init.h b/third_party/mojo/src/mojo/edk/embedder/channel_init.h deleted file mode 100644 index 478da41..0000000 --- a/third_party/mojo/src/mojo/edk/embedder/channel_init.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_ -#define MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_ - -#include "base/files/file.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "mojo/edk/embedder/channel_info_forward.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace base { -class MessageLoopProxy; -class TaskRunner; -} - -namespace mojo { -namespace embedder { - -// |ChannelInit| handles creation (and destruction) of the Mojo channel. It is -// not thread-safe, but may be used on any single thread (with a |MessageLoop|). -class MOJO_SYSTEM_IMPL_EXPORT ChannelInit { - public: - ChannelInit(); - ~ChannelInit(); - - // Initializes the channel. This takes ownership of |file|. Returns the - // primordial |MessagePipe| for the channel. - mojo::ScopedMessagePipeHandle Init( - base::PlatformFile file, - scoped_refptr<base::TaskRunner> io_thread_task_runner); - - // Notifies the channel that we (hence it) will soon be destroyed. - void WillDestroySoon(); - - private: - // Invoked on the thread on which this object lives once the channel has been - // established. (This is a static method that takes a weak pointer to self, - // since we want to destroy the channel even if we're destroyed.) - static void OnCreatedChannel(base::WeakPtr<ChannelInit> self, - ChannelInfo* channel); - - // If non-null the channel has been established. - ChannelInfo* channel_info_; - - base::WeakPtrFactory<ChannelInit> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(ChannelInit); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_ diff --git a/third_party/mojo/src/mojo/edk/embedder/embedder.cc b/third_party/mojo/src/mojo/edk/embedder/embedder.cc index d0a5135..a4997e7 100644 --- a/third_party/mojo/src/mojo/edk/embedder/embedder.cc +++ b/third_party/mojo/src/mojo/edk/embedder/embedder.cc @@ -6,25 +6,60 @@ #include "base/atomicops.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/task_runner.h" #include "mojo/edk/embedder/embedder_internal.h" +#include "mojo/edk/embedder/master_process_delegate.h" #include "mojo/edk/embedder/platform_support.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "mojo/edk/embedder/slave_process_delegate.h" #include "mojo/edk/system/channel.h" #include "mojo/edk/system/channel_manager.h" #include "mojo/edk/system/configuration.h" +#include "mojo/edk/system/connection_manager.h" #include "mojo/edk/system/core.h" +#include "mojo/edk/system/master_connection_manager.h" #include "mojo/edk/system/message_pipe_dispatcher.h" #include "mojo/edk/system/platform_handle_dispatcher.h" #include "mojo/edk/system/raw_channel.h" +#include "mojo/edk/system/slave_connection_manager.h" namespace mojo { namespace embedder { +namespace internal { + +// Declared in embedder_internal.h. +PlatformSupport* g_platform_support = nullptr; +system::Core* g_core = nullptr; +ProcessType g_process_type = ProcessType::UNINITIALIZED; + +} // namespace internal + namespace { +// The following global variables are set in |InitIPCSupport()| and reset by +// |ShutdownIPCSupport()|/|ShutdownIPCSupportOnIOThread()|. + +// Note: This needs to be |AddRef()|ed/|Release()|d. +base::TaskRunner* g_delegate_thread_task_runner = nullptr; + +ProcessDelegate* g_process_delegate = nullptr; + +// Note: This needs to be |AddRef()|ed/|Release()|d. +base::TaskRunner* g_io_thread_task_runner = nullptr; + +// Instance of |ConnectionManager| used by the channel manager (below). +system::ConnectionManager* g_connection_manager = nullptr; + +// Instance of |ChannelManager| used by the channel management functions +// (|CreateChannel()|, etc.). +system::ChannelManager* g_channel_manager = nullptr; + // TODO(vtl): For now, we need this to be thread-safe (since theoretically we // currently support multiple channel creation threads -- possibly one per // channel). Eventually, we won't need it to be thread-safe (we'll require a @@ -46,18 +81,22 @@ system::ChannelId MakeChannelId() { return static_cast<system::ChannelId>(-new_counter_value); } -} // namespace +// Note: Called on the I/O thread. +void ShutdownIPCSupportHelper() { + // Save these before nuking them using |ShutdownChannelOnIOThread()|. + scoped_refptr<base::TaskRunner> delegate_thread_task_runner( + g_delegate_thread_task_runner); + ProcessDelegate* process_delegate = g_process_delegate; -namespace internal { + ShutdownIPCSupportOnIOThread(); -// Declared in embedder_internal.h. -PlatformSupport* g_platform_support = nullptr; -system::Core* g_core = nullptr; -system::ChannelManager* g_channel_manager = nullptr; -MasterProcessDelegate* g_master_process_delegate = nullptr; -SlaveProcessDelegate* g_slave_process_delegate = nullptr; + bool ok = delegate_thread_task_runner->PostTask( + FROM_HERE, base::Bind(&ProcessDelegate::OnShutdownComplete, + base::Unretained(process_delegate))); + DCHECK(ok); +} -} // namespace internal +} // namespace Configuration* GetConfiguration() { return system::GetMutableConfiguration(); @@ -71,33 +110,150 @@ void Init(scoped_ptr<PlatformSupport> platform_support) { DCHECK(!internal::g_core); internal::g_core = new system::Core(internal::g_platform_support); +} - DCHECK(!internal::g_channel_manager); - internal::g_channel_manager = - new system::ChannelManager(internal::g_platform_support); +MojoResult AsyncWait(MojoHandle handle, + MojoHandleSignals signals, + const base::Callback<void(MojoResult)>& callback) { + return internal::g_core->AsyncWait(handle, signals, callback); } -void InitMaster(scoped_refptr<base::TaskRunner> delegate_thread_task_runner, - MasterProcessDelegate* master_process_delegate, - scoped_refptr<base::TaskRunner> io_thread_task_runner) { - // |Init()| must have already been called. +MojoResult CreatePlatformHandleWrapper( + ScopedPlatformHandle platform_handle, + MojoHandle* platform_handle_wrapper_handle) { + DCHECK(platform_handle_wrapper_handle); + + scoped_refptr<system::Dispatcher> dispatcher( + new system::PlatformHandleDispatcher(platform_handle.Pass())); + + DCHECK(internal::g_core); + MojoHandle h = internal::g_core->AddDispatcher(dispatcher); + if (h == MOJO_HANDLE_INVALID) { + LOG(ERROR) << "Handle table full"; + dispatcher->Close(); + return MOJO_RESULT_RESOURCE_EXHAUSTED; + } + + *platform_handle_wrapper_handle = h; + return MOJO_RESULT_OK; +} + +MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, + ScopedPlatformHandle* platform_handle) { + DCHECK(platform_handle); + DCHECK(internal::g_core); + scoped_refptr<system::Dispatcher> dispatcher( + internal::g_core->GetDispatcher(platform_handle_wrapper_handle)); + if (!dispatcher) + return MOJO_RESULT_INVALID_ARGUMENT; - // TODO(vtl): This is temporary. We really want to construct a - // |MasterConnectionManager| here, which will in turn hold on to the delegate. - internal::g_master_process_delegate = master_process_delegate; + if (dispatcher->GetType() != system::Dispatcher::kTypePlatformHandle) + return MOJO_RESULT_INVALID_ARGUMENT; + + *platform_handle = + static_cast<system::PlatformHandleDispatcher*>(dispatcher.get()) + ->PassPlatformHandle() + .Pass(); + return MOJO_RESULT_OK; } -void InitSlave(scoped_refptr<base::TaskRunner> delegate_thread_task_runner, - SlaveProcessDelegate* slave_process_delegate, - scoped_refptr<base::TaskRunner> io_thread_task_runner, - ScopedPlatformHandle platform_handle) { +void InitIPCSupport(ProcessType process_type, + scoped_refptr<base::TaskRunner> delegate_thread_task_runner, + ProcessDelegate* process_delegate, + scoped_refptr<base::TaskRunner> io_thread_task_runner, + ScopedPlatformHandle platform_handle) { // |Init()| must have already been called. DCHECK(internal::g_core); + // And not |InitIPCSupport()| (without |ShutdownIPCSupport()|). + DCHECK(internal::g_process_type == ProcessType::UNINITIALIZED); - // TODO(vtl): This is temporary. We really want to construct a - // |SlaveConnectionManager| here, which will in turn hold on to the delegate. - internal::g_slave_process_delegate = slave_process_delegate; + internal::g_process_type = process_type; + + DCHECK(delegate_thread_task_runner); + DCHECK(!g_delegate_thread_task_runner); + g_delegate_thread_task_runner = delegate_thread_task_runner.get(); + g_delegate_thread_task_runner->AddRef(); + + DCHECK(process_delegate->GetType() == process_type); + DCHECK(!g_process_delegate); + g_process_delegate = process_delegate; + + DCHECK(io_thread_task_runner); + DCHECK(!g_io_thread_task_runner); + g_io_thread_task_runner = io_thread_task_runner.get(); + g_io_thread_task_runner->AddRef(); + + DCHECK(!g_connection_manager); + switch (process_type) { + case ProcessType::UNINITIALIZED: + CHECK(false); + break; + case ProcessType::NONE: + DCHECK(!platform_handle.is_valid()); // We wouldn't do anything with it. + // Nothing to do. + break; + case ProcessType::MASTER: + DCHECK(!platform_handle.is_valid()); // We wouldn't do anything with it. + g_connection_manager = new system::MasterConnectionManager(); + static_cast<system::MasterConnectionManager*>(g_connection_manager) + ->Init(g_delegate_thread_task_runner, + static_cast<MasterProcessDelegate*>(g_process_delegate)); + break; + case ProcessType::SLAVE: + DCHECK(platform_handle.is_valid()); + g_connection_manager = new system::SlaveConnectionManager(); + static_cast<system::SlaveConnectionManager*>(g_connection_manager) + ->Init(g_delegate_thread_task_runner, + static_cast<SlaveProcessDelegate*>(g_process_delegate), + platform_handle.Pass()); + break; + } + + DCHECK(!g_channel_manager); + g_channel_manager = + new system::ChannelManager(internal::g_platform_support, + io_thread_task_runner, g_connection_manager); +} + +void ShutdownIPCSupportOnIOThread() { + DCHECK(internal::g_process_type != ProcessType::UNINITIALIZED); + + g_channel_manager->ShutdownOnIOThread(); + delete g_channel_manager; + g_channel_manager = nullptr; + + if (g_connection_manager) { + g_connection_manager->Shutdown(); + delete g_connection_manager; + g_connection_manager = nullptr; + } + + g_io_thread_task_runner->Release(); + g_io_thread_task_runner = nullptr; + + g_delegate_thread_task_runner->Release(); + g_delegate_thread_task_runner = nullptr; + + g_process_delegate = nullptr; + + internal::g_process_type = ProcessType::UNINITIALIZED; +} + +void ShutdownIPCSupport() { + DCHECK(internal::g_process_type != ProcessType::UNINITIALIZED); + + bool ok = g_io_thread_task_runner->PostTask( + FROM_HERE, base::Bind(&ShutdownIPCSupportHelper)); + DCHECK(ok); +} + +void ConnectToSlave(SlaveInfo slave_info, + ScopedPlatformHandle platform_handle) { + DCHECK(platform_handle.is_valid()); + DCHECK(internal::g_process_type == ProcessType::MASTER); + static_cast<system::MasterConnectionManager*>(g_connection_manager) + ->AddSlave(slave_info, platform_handle.Pass()); } // TODO(vtl): Write tests for this. @@ -109,8 +265,8 @@ ScopedMessagePipeHandle CreateChannelOnIOThread( *channel_info = new ChannelInfo(MakeChannelId()); scoped_refptr<system::MessagePipeDispatcher> dispatcher = - internal::g_channel_manager->CreateChannelOnIOThread( - (*channel_info)->channel_id, platform_handle.Pass()); + g_channel_manager->CreateChannelOnIOThread((*channel_info)->channel_id, + platform_handle.Pass()); ScopedMessagePipeHandle rv( MessagePipeHandle(internal::g_core->AddDispatcher(dispatcher))); @@ -123,7 +279,7 @@ ScopedMessagePipeHandle CreateChannelOnIOThread( ScopedMessagePipeHandle CreateChannel( ScopedPlatformHandle platform_handle, scoped_refptr<base::TaskRunner> io_thread_task_runner, - DidCreateChannelCallback callback, + const DidCreateChannelCallback& callback, scoped_refptr<base::TaskRunner> callback_thread_task_runner) { DCHECK(platform_handle.is_valid()); DCHECK(io_thread_task_runner); @@ -132,7 +288,7 @@ ScopedMessagePipeHandle CreateChannel( system::ChannelId channel_id = MakeChannelId(); scoped_ptr<ChannelInfo> channel_info(new ChannelInfo(channel_id)); scoped_refptr<system::MessagePipeDispatcher> dispatcher = - internal::g_channel_manager->CreateChannel( + g_channel_manager->CreateChannel( channel_id, platform_handle.Pass(), io_thread_task_runner, base::Bind(callback, base::Unretained(channel_info.release())), callback_thread_task_runner); @@ -146,66 +302,32 @@ ScopedMessagePipeHandle CreateChannel( } // TODO(vtl): Write tests for this. -void DestroyChannel(ChannelInfo* channel_info) { +void DestroyChannelOnIOThread(ChannelInfo* channel_info) { DCHECK(channel_info); DCHECK(channel_info->channel_id); - DCHECK(internal::g_channel_manager); - // This will destroy the channel synchronously if called from the channel - // thread. - internal::g_channel_manager->ShutdownChannel(channel_info->channel_id); + DCHECK(g_channel_manager); + g_channel_manager->ShutdownChannelOnIOThread(channel_info->channel_id); delete channel_info; } -void WillDestroyChannelSoon(ChannelInfo* channel_info) { +// TODO(vtl): Write tests for this. +void DestroyChannel( + ChannelInfo* channel_info, + const DidDestroyChannelCallback& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner) { DCHECK(channel_info); - DCHECK(internal::g_channel_manager); - internal::g_channel_manager->WillShutdownChannel(channel_info->channel_id); -} - -MojoResult CreatePlatformHandleWrapper( - ScopedPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle) { - DCHECK(platform_handle_wrapper_handle); - - scoped_refptr<system::Dispatcher> dispatcher( - new system::PlatformHandleDispatcher(platform_handle.Pass())); - - DCHECK(internal::g_core); - MojoHandle h = internal::g_core->AddDispatcher(dispatcher); - if (h == MOJO_HANDLE_INVALID) { - LOG(ERROR) << "Handle table full"; - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - *platform_handle_wrapper_handle = h; - return MOJO_RESULT_OK; -} - -MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, - ScopedPlatformHandle* platform_handle) { - DCHECK(platform_handle); - - DCHECK(internal::g_core); - scoped_refptr<system::Dispatcher> dispatcher( - internal::g_core->GetDispatcher(platform_handle_wrapper_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (dispatcher->GetType() != system::Dispatcher::kTypePlatformHandle) - return MOJO_RESULT_INVALID_ARGUMENT; - - *platform_handle = - static_cast<system::PlatformHandleDispatcher*>(dispatcher.get()) - ->PassPlatformHandle() - .Pass(); - return MOJO_RESULT_OK; + DCHECK(channel_info->channel_id); + DCHECK(!callback.is_null()); + DCHECK(g_channel_manager); + g_channel_manager->ShutdownChannel(channel_info->channel_id, callback, + callback_thread_task_runner); + delete channel_info; } -MojoResult AsyncWait(MojoHandle handle, - MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback) { - return internal::g_core->AsyncWait(handle, signals, callback); +void WillDestroyChannelSoon(ChannelInfo* channel_info) { + DCHECK(channel_info); + DCHECK(g_channel_manager); + g_channel_manager->WillShutdownChannel(channel_info->channel_id); } } // namespace embedder diff --git a/third_party/mojo/src/mojo/edk/embedder/embedder.h b/third_party/mojo/src/mojo/edk/embedder/embedder.h index 4898ae1..142c86f 100644 --- a/third_party/mojo/src/mojo/edk/embedder/embedder.h +++ b/third_party/mojo/src/mojo/edk/embedder/embedder.h @@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "base/task_runner.h" #include "mojo/edk/embedder/channel_info_forward.h" +#include "mojo/edk/embedder/process_type.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -18,9 +19,15 @@ namespace mojo { namespace embedder { struct Configuration; -class MasterProcessDelegate; class PlatformSupport; -class SlaveProcessDelegate; +class ProcessDelegate; +typedef void* SlaveInfo; + +// Basic configuration/initialization ------------------------------------------ + +// |Init()| sets up the basic Mojo system environment, making the |Mojo...()| +// functions available and functional. This is never shut down (except in tests +// -- see test_embedder.h). // Returns the global configuration. In general, you should not need to change // the configuration, but if you do you must do it before calling |Init()|. @@ -30,31 +37,88 @@ MOJO_SYSTEM_IMPL_EXPORT Configuration* GetConfiguration(); // initialize the (global, singleton) system. MOJO_SYSTEM_IMPL_EXPORT void Init(scoped_ptr<PlatformSupport> platform_support); -// Initializes a master process. To be called after |Init()|. -// |master_process_delegate| should live forever (or until after -// |mojo::embedder::test::Shutdown()|); its methods will be called using -// |delegate_thread_task_runner|, which must be the task runner for the thread -// calling |InitMaster()|. |io_thread_task_runner| should be the task runner for -// some I/O thread; this should be the same as that provided to -// |CreateChannel()| (or on which |CreateChannelOnIOThread()| is called). -// TODO(vtl): Remove the |io_thread_task_runner| argument from -// |CreateChannel()| (and eventually |CreateChannel()| altogether) and require -// that either this or |InitSlave()| be called. Currently, |CreateChannel()| can -// be used with different I/O threads, but this capability will be removed. -MOJO_SYSTEM_IMPL_EXPORT void InitMaster( - scoped_refptr<base::TaskRunner> delegate_thread_task_runner, - MasterProcessDelegate* master_process_delegate, - scoped_refptr<base::TaskRunner> io_thread_task_runner); +// Basic functions ------------------------------------------------------------- + +// The functions in this section are available once |Init()| has been called. + +// Start waiting on the handle asynchronously. On success, |callback| will be +// called exactly once, when |handle| satisfies a signal in |signals| or it +// becomes known that it will never do so. |callback| will be executed on an +// arbitrary thread, so it must not call any Mojo system or embedder functions. +MOJO_SYSTEM_IMPL_EXPORT MojoResult +AsyncWait(MojoHandle handle, + MojoHandleSignals signals, + const base::Callback<void(MojoResult)>& callback); + +// Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking +// ownership of it). This |MojoHandle| can then, e.g., be passed through message +// pipes. Note: This takes ownership (and thus closes) |platform_handle| even on +// failure, which is different from what you'd expect from a Mojo API, but it +// makes for a more convenient embedder API. +MOJO_SYSTEM_IMPL_EXPORT MojoResult +CreatePlatformHandleWrapper(ScopedPlatformHandle platform_handle, + MojoHandle* platform_handle_wrapper_handle); + +// Retrieves the |PlatformHandle| that was wrapped into a |MojoHandle| (using +// |CreatePlatformHandleWrapper()| above). Note that the |MojoHandle| must still +// be closed separately. +MOJO_SYSTEM_IMPL_EXPORT MojoResult +PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, + ScopedPlatformHandle* platform_handle); -// Initializes a slave process. Similar to |InitMaster()| (see above). -// |platform_handle| should be connected to the handle passed to |AddSlave()|. -// TODO(vtl): |AddSlave()| doesn't exist yet. -MOJO_SYSTEM_IMPL_EXPORT void InitSlave( +// Initialialization/shutdown for interprocess communication (IPC) ------------- + +// |InitIPCSupport()| sets up the subsystem for interprocess communication, +// making the IPC functions (in the following section) available and functional. +// (This may only be done after |Init()|.) +// +// This subsystem may be shut down, using |ShutdownIPCSupportOnIOThread()| or +// |ShutdownIPCSupport()|. None of the IPC functions may be called while or +// after either of these is called. + +// Initializes a process of the given type; to be called after |Init()|. +// - |process_delegate| must be a process delegate of the appropriate type +// corresponding to |process_type|; its methods will be called on +// |delegate_thread_task_runner|. +// - |delegate_thread_task_runner|, |process_delegate|, and +// |io_thread_task_runner| should live at least until +// |ShutdownIPCSupport()|'s callback has been run or +// |ShutdownIPCSupportOnIOThread()| has completed. +// - For slave processes (i.e., |process_type| is |ProcessType::SLAVE|), +// |platform_handle| should be connected to the handle passed to +// |ConnectToSlave()| (in the master process). For other processes, +// |platform_handle| is ignored (and should not be valid). +MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport( + ProcessType process_type, scoped_refptr<base::TaskRunner> delegate_thread_task_runner, - SlaveProcessDelegate* slave_process_delegate, + ProcessDelegate* process_delegate, scoped_refptr<base::TaskRunner> io_thread_task_runner, ScopedPlatformHandle platform_handle); +// Shuts down the subsystem initialized by |InitIPCSupport()|. This must be +// called on the I/O thread (given to |InitIPCSupport()|). This completes +// synchronously and does not result in a call to the process delegate's +// |OnShutdownComplete()|. +MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupportOnIOThread(); + +// Like |ShutdownIPCSupportOnIOThread()|, but may be called from any thread, +// signalling shutdown completion via the process delegate's +// |OnShutdownComplete()|. +MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(); + +// Interprocess communication (IPC) functions ---------------------------------- + +// Connects to a slave process to the IPC system. This should only be called in +// a process initialized (using |InitIPCSupport()|) with process type +// |ProcessType::MASTER|. |slave_info| is caller-dependent slave information, +// which should remain alive until the master process delegate's +// |OnSlaveDisconnect()| is called. |platform_handle| should be a handle to one +// end of an OS "pipe"; the slave process should |InitIPCSupport()| with +// |ProcessType::SLAVE| and the handle to the other end of this OS "pipe". +MOJO_SYSTEM_IMPL_EXPORT void ConnectToSlave( + SlaveInfo slave_info, + ScopedPlatformHandle platform_handle); + // A "channel" is a connection on top of an OS "pipe", on top of which Mojo // message pipes (etc.) can be multiplexed. It must "live" on some I/O thread. // @@ -111,48 +175,33 @@ typedef base::Callback<void(ChannelInfo*)> DidCreateChannelCallback; MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle CreateChannel(ScopedPlatformHandle platform_handle, scoped_refptr<base::TaskRunner> io_thread_task_runner, - DidCreateChannelCallback callback, + const DidCreateChannelCallback& callback, scoped_refptr<base::TaskRunner> callback_thread_task_runner); // Destroys a channel that was created using |CreateChannel()| (or -// |CreateChannelOnIOThread()|); may be called from any thread. |channel_info| -// should be the value provided to the callback to |CreateChannel()| (or -// returned by |CreateChannelOnIOThread()|). If called from the I/O thread, this -// will complete synchronously (in particular, it will post no tasks). -// TODO(vtl): If called from some other thread, it'll post tasks to the I/O -// thread. This is obviously potentially problematic if you want to shut the I/O -// thread down. -MOJO_SYSTEM_IMPL_EXPORT void DestroyChannel(ChannelInfo* channel_info); +// |CreateChannelOnIOThread()|); must be called from the channel's I'O thread. +// |channel_info| should be the value provided to the callback to +// |CreateChannel()| (or returned by |CreateChannelOnIOThread()|). Completes +// synchronously (and posts no tasks). +MOJO_SYSTEM_IMPL_EXPORT void DestroyChannelOnIOThread( + ChannelInfo* channel_info); + +typedef base::Closure DidDestroyChannelCallback; +// Like |DestroyChannelOnIOThread()|, but asynchronous and may be called from +// any thread. The callback will be called using |callback_thread_task_runner| +// if that is non-null, or otherwise it will be called on the "channel thread". +// The "channel thread" must remain alive and continue to process tasks until +// the callback has been executed. +MOJO_SYSTEM_IMPL_EXPORT void DestroyChannel( + ChannelInfo* channel_info, + const DidDestroyChannelCallback& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner); // Inform the channel that it will soon be destroyed (doing so is optional). // This may be called from any thread, but the caller must ensure that this is // called before |DestroyChannel()|. MOJO_SYSTEM_IMPL_EXPORT void WillDestroyChannelSoon(ChannelInfo* channel_info); -// Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking -// ownership of it). This |MojoHandle| can then, e.g., be passed through message -// pipes. Note: This takes ownership (and thus closes) |platform_handle| even on -// failure, which is different from what you'd expect from a Mojo API, but it -// makes for a more convenient embedder API. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -CreatePlatformHandleWrapper(ScopedPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle); -// Retrieves the |PlatformHandle| that was wrapped into a |MojoHandle| (using -// |CreatePlatformHandleWrapper()| above). Note that the |MojoHandle| must still -// be closed separately. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, - ScopedPlatformHandle* platform_handle); - -// Start waiting the handle asynchronously. On success, |callback| will be -// called exactly once, when |handle| satisfies a signal in |signals| or it -// becomes known that it will never do so. |callback| will be executed on an -// arbitrary thread. It must not call any Mojo system or embedder functions. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -AsyncWait(MojoHandle handle, - MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback); - } // namespace embedder } // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/embedder/embedder_internal.h b/third_party/mojo/src/mojo/edk/embedder/embedder_internal.h index 7d90148..833e66b 100644 --- a/third_party/mojo/src/mojo/edk/embedder/embedder_internal.h +++ b/third_party/mojo/src/mojo/edk/embedder/embedder_internal.h @@ -11,6 +11,12 @@ #include <stdint.h> +#include "mojo/edk/embedder/process_type.h" + +namespace base { +class TaskRunner; +} + namespace mojo { namespace system { @@ -26,9 +32,7 @@ typedef uint64_t ChannelId; namespace embedder { class PlatformSupport; -// TODO(vtl): Remove these (see below). -class MasterProcessDelegate; -class SlaveProcessDelegate; +class ProcessDelegate; // This is a type that's opaque to users of the embedder API (which only // gives/takes |ChannelInfo*|s). We make it a struct to make it @@ -48,15 +52,11 @@ extern PlatformSupport* g_platform_support; // Instance of |Core| used by the system functions (|Mojo...()|). extern system::Core* g_core; -// Instance of |ChannelManager| used by the channel management functions -// (|mojo::embedder::CreateChannel()|, etc.). -extern system::ChannelManager* g_channel_manager; - -// TODO(vtl): Remove these: We'll eventually really want to hold on to a -// |MasterConnectionManager*| or a |SlaveConnectionManager*|. For now, keep -// these around as globals to avoid triggering leak detectors. -extern MasterProcessDelegate* g_master_process_delegate; -extern SlaveProcessDelegate* g_slave_process_delegate; +// Type of process initialized in |InitIPCSupport()| (set to |UNINITIALIZED| if +// "outside" |InitIPCSupport()|/|ShutdownIPCSupport()|). This is declared here +// so that |mojo::embedder::test::Shutdown()| can check that it's only called +// after |ShutdownIPCSupport()|. +extern ProcessType g_process_type; } // namespace internal diff --git a/third_party/mojo/src/mojo/edk/embedder/embedder_unittest.cc b/third_party/mojo/src/mojo/edk/embedder/embedder_unittest.cc index aa0b08b..48a59c1 100644 --- a/third_party/mojo/src/mojo/edk/embedder/embedder_unittest.cc +++ b/third_party/mojo/src/mojo/edk/embedder/embedder_unittest.cc @@ -18,6 +18,7 @@ #include "mojo/edk/embedder/test_embedder.h" #include "mojo/edk/system/test_utils.h" #include "mojo/edk/test/multiprocess_test_helper.h" +#include "mojo/edk/test/scoped_ipc_support.h" #include "mojo/public/c/system/core.h" #include "testing/gtest/include/gtest/gtest.h" @@ -44,7 +45,7 @@ class ScopedTestChannel { ScopedPlatformHandle platform_handle) : io_thread_task_runner_(io_thread_task_runner), bootstrap_message_pipe_(MOJO_HANDLE_INVALID), - did_create_channel_event_(true, false), // Manual reset. + event_(true, false), // Manual reset. channel_info_(nullptr) { bootstrap_message_pipe_ = CreateChannel(platform_handle.Pass(), io_thread_task_runner_, @@ -60,12 +61,17 @@ class ScopedTestChannel { // the I/O thread must be alive and pumping messages.) ~ScopedTestChannel() { // |WaitForChannelCreationCompletion()| must be called before destruction. - CHECK(did_create_channel_event_.IsSignaled()); - DestroyChannel(channel_info_); + CHECK(event_.IsSignaled()); + event_.Reset(); + DestroyChannel(channel_info_, + base::Bind(&ScopedTestChannel::DidDestroyChannel, + base::Unretained(this)), + nullptr); + event_.Wait(); } // Waits for channel creation to be completed. - void WaitForChannelCreationCompletion() { did_create_channel_event_.Wait(); } + void WaitForChannelCreationCompletion() { event_.Wait(); } MojoHandle bootstrap_message_pipe() const { return bootstrap_message_pipe_; } @@ -78,9 +84,11 @@ class ScopedTestChannel { CHECK(channel_info); CHECK(!channel_info_); channel_info_ = channel_info; - did_create_channel_event_.Signal(); + event_.Signal(); } + void DidDestroyChannel() { event_.Signal(); } + scoped_refptr<base::TaskRunner> io_thread_task_runner_; // Valid from creation until whenever it gets closed (by the "owner" of this @@ -90,8 +98,9 @@ class ScopedTestChannel { MojoHandle bootstrap_message_pipe_; // Set after channel creation has been completed (i.e., the callback to - // |CreateChannel()| has been called). - base::WaitableEvent did_create_channel_event_; + // |CreateChannel()| has been called). Also used in the destructor to wait for + // |DestroyChannel()| completion. + base::WaitableEvent event_; // Valid after channel creation completion until destruction. ChannelInfo* channel_info_; @@ -120,6 +129,8 @@ class EmbedderTest : public testing::Test { }; TEST_F(EmbedderTest, ChannelsBasic) { + mojo::test::ScopedIPCSupport ipc_support(test_io_task_runner()); + PlatformChannelPair channel_pair; ScopedTestChannel server_channel(test_io_task_runner(), channel_pair.PassServerHandle()); @@ -247,6 +258,8 @@ TEST_F(EmbedderTest, AsyncWait) { } TEST_F(EmbedderTest, ChannelsHandlePassing) { + mojo::test::ScopedIPCSupport ipc_support(test_io_task_runner()); + PlatformChannelPair channel_pair; ScopedTestChannel server_channel(test_io_task_runner(), channel_pair.PassServerHandle()); @@ -389,6 +402,10 @@ TEST_F(EmbedderTest, ChannelsHandlePassing) { #define MAYBE_MultiprocessChannels MultiprocessChannels #endif // defined(OS_ANDROID) TEST_F(EmbedderTest, MAYBE_MultiprocessChannels) { + // TODO(vtl): This should eventually initialize a master process instead, + // probably. + mojo::test::ScopedIPCSupport ipc_support(test_io_task_runner()); + mojo::test::MultiprocessTestHelper multiprocess_test_helper; multiprocess_test_helper.StartChild("MultiprocessChannelsClient"); @@ -512,6 +529,10 @@ MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) { test::InitWithSimplePlatformSupport(); { + // TODO(vtl): This should eventually initialize a slave process instead, + // probably. + mojo::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner()); + ScopedTestChannel client_channel(test_io_thread.task_runner(), client_platform_handle.Pass()); MojoHandle client_mp = client_channel.bootstrap_message_pipe(); diff --git a/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h b/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h index 13791f4..bc3c909 100644 --- a/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h +++ b/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h @@ -7,40 +7,38 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "mojo/edk/embedder/process_delegate.h" #include "mojo/edk/system/system_impl_export.h" namespace mojo { namespace embedder { -// An interface for containers of slave process information, to be used by -// |MasterProcessDelegate| (below). -class MOJO_SYSTEM_IMPL_EXPORT SlaveInfo { - public: - SlaveInfo() {} - virtual ~SlaveInfo() {} - - private: - DISALLOW_COPY_AND_ASSIGN(SlaveInfo); -}; +typedef void* SlaveInfo; // An interface for the master process delegate (which lives in the master // process). -class MOJO_SYSTEM_IMPL_EXPORT MasterProcessDelegate { +class MOJO_SYSTEM_IMPL_EXPORT MasterProcessDelegate : public ProcessDelegate { public: + ProcessType GetType() const override; + // Called when contact with the slave process specified by |slave_info| has // been lost. // TODO(vtl): Obviously, there needs to be a suitable embedder API for // connecting to a process. What will it be? Mention that here once it exists. - virtual void OnSlaveDisconnect(scoped_ptr<SlaveInfo> slave_info) = 0; + virtual void OnSlaveDisconnect(SlaveInfo slave_info) = 0; protected: MasterProcessDelegate() {} - virtual ~MasterProcessDelegate() {} + ~MasterProcessDelegate() override {} private: DISALLOW_COPY_AND_ASSIGN(MasterProcessDelegate); }; +inline ProcessType MasterProcessDelegate::GetType() const { + return ProcessType::MASTER; +} + } // namespace embedder } // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/embedder/platform_support.h b/third_party/mojo/src/mojo/edk/embedder/platform_support.h index 4556ee3..80ee81b 100644 --- a/third_party/mojo/src/mojo/edk/embedder/platform_support.h +++ b/third_party/mojo/src/mojo/edk/embedder/platform_support.h @@ -18,10 +18,13 @@ class PlatformSharedBuffer; // This class is provided by the embedder to implement (typically // platform-dependent) things needed by the Mojo system implementation. +// Implementations must be thread-safe. class MOJO_SYSTEM_IMPL_EXPORT PlatformSupport { public: virtual ~PlatformSupport() {} + virtual void GetCryptoRandomBytes(void* bytes, size_t num_bytes) = 0; + virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) = 0; virtual PlatformSharedBuffer* CreateSharedBufferFromHandle( size_t num_bytes, diff --git a/third_party/mojo/src/mojo/edk/embedder/process_delegate.h b/third_party/mojo/src/mojo/edk/embedder/process_delegate.h new file mode 100644 index 0000000..325c532 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/embedder/process_delegate.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_ +#define MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_ + +#include "base/macros.h" +#include "mojo/edk/embedder/process_type.h" +#include "mojo/edk/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +// An interface for process delegates. +class MOJO_SYSTEM_IMPL_EXPORT ProcessDelegate { + public: + virtual ProcessType GetType() const; + + // Called when |ShutdownIPCSupport()| has "completed". Note that this is NOT + // called if |ShutdownIPCSupportOnIOThread()| is used instead. + virtual void OnShutdownComplete() = 0; + + protected: + ProcessDelegate() {} + virtual ~ProcessDelegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ProcessDelegate); +}; + +inline ProcessType ProcessDelegate::GetType() const { + return ProcessType::NONE; +} + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_ diff --git a/third_party/mojo/src/mojo/edk/embedder/process_type.h b/third_party/mojo/src/mojo/edk/embedder/process_type.h new file mode 100644 index 0000000..87292df9 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/embedder/process_type.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef MOJO_EDK_EMBEDDER_PROCESS_TYPE_H_ +#define MOJO_EDK_EMBEDDER_PROCESS_TYPE_H_ + +namespace mojo { +namespace embedder { + +enum class ProcessType { + // |InitIPCSupport()| has not been called (or |ShutdownIPCSupport()| has been + // called). + UNINITIALIZED, + // Process without connection management. + NONE, + // Master process. + MASTER, + // Slave process. + SLAVE, +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EDK_EMBEDDER_PROCESS_TYPE_H_ diff --git a/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.cc b/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.cc index c59cfb3..5024087 100644 --- a/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.cc +++ b/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.cc @@ -4,11 +4,17 @@ #include "mojo/edk/embedder/simple_platform_support.h" +#include "crypto/random.h" #include "mojo/edk/embedder/simple_platform_shared_buffer.h" namespace mojo { namespace embedder { +void SimplePlatformSupport::GetCryptoRandomBytes(void* bytes, + size_t num_bytes) { + crypto::RandBytes(bytes, num_bytes); +} + PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBuffer( size_t num_bytes) { return SimplePlatformSharedBuffer::Create(num_bytes); diff --git a/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.h b/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.h index 2013f8d..9be7dc0 100644 --- a/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.h +++ b/third_party/mojo/src/mojo/edk/embedder/simple_platform_support.h @@ -22,6 +22,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSupport : public PlatformSupport { SimplePlatformSupport() {} ~SimplePlatformSupport() override {} + void GetCryptoRandomBytes(void* bytes, size_t num_bytes) override; PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) override; PlatformSharedBuffer* CreateSharedBufferFromHandle( size_t num_bytes, diff --git a/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h b/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h index 1c55a7a..d959215 100644 --- a/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h +++ b/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "mojo/edk/embedder/process_delegate.h" #include "mojo/edk/system/system_impl_export.h" namespace mojo { @@ -14,8 +15,10 @@ namespace embedder { // An interface for the slave process delegate (which lives in each slave // process). -class MOJO_SYSTEM_IMPL_EXPORT SlaveProcessDelegate { +class MOJO_SYSTEM_IMPL_EXPORT SlaveProcessDelegate : public ProcessDelegate { public: + ProcessType GetType() const override; + // Called when contact with the master process has been lost. // TODO(vtl): Obviously, there needs to be a suitable embedder API for // connecting to the master process. What will it be? Mention that here once @@ -24,12 +27,16 @@ class MOJO_SYSTEM_IMPL_EXPORT SlaveProcessDelegate { protected: SlaveProcessDelegate() {} - virtual ~SlaveProcessDelegate() {} + ~SlaveProcessDelegate() override {} private: DISALLOW_COPY_AND_ASSIGN(SlaveProcessDelegate); }; +inline ProcessType SlaveProcessDelegate::GetType() const { + return ProcessType::SLAVE; +} + } // namespace embedder } // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/embedder/test_embedder.cc b/third_party/mojo/src/mojo/edk/embedder/test_embedder.cc index 3412626..791a888 100644 --- a/third_party/mojo/src/mojo/edk/embedder/test_embedder.cc +++ b/third_party/mojo/src/mojo/edk/embedder/test_embedder.cc @@ -46,9 +46,9 @@ void InitWithSimplePlatformSupport() { } bool Shutdown() { - CHECK(internal::g_channel_manager); - delete internal::g_channel_manager; - internal::g_channel_manager = nullptr; + // If |InitIPCSupport()| was called, then |ShutdownIPCSupport()| must have + // been called first. + CHECK(internal::g_process_type == ProcessType::UNINITIALIZED); CHECK(internal::g_core); bool rv = system::internal::ShutdownCheckNoLeaks(internal::g_core); diff --git a/third_party/mojo/src/mojo/edk/js/tests/connection_tests.js b/third_party/mojo/src/mojo/edk/js/tests/connection_tests.js index 17009d9..ff59aeb 100644 --- a/third_party/mojo/src/mojo/edk/js/tests/connection_tests.js +++ b/third_party/mojo/src/mojo/edk/js/tests/connection_tests.js @@ -83,14 +83,15 @@ define([ function createPeerConnection(handle, stubClass, proxyClass) { var c = new connection.Connection(handle, stubClass, proxyClass); - c.local.peer = c.remote; - c.remote.peer = c.local; + if (c.local) + c.local.peer = c.remote; + if (c.remote) + c.remote.peer = c.local; return c; } function testClientServer() { var receivedFrobinate = false; - var receivedDidFrobinate = false; // ServiceImpl ------------------------------------------------------------ @@ -107,21 +108,7 @@ define([ expect(baz).toBeTruthy(); expect(core.close(port)).toBe(core.RESULT_OK); - this.peer.didFrobinate(42); - }; - - // ServiceClientImpl ------------------------------------------------------ - - function ServiceClientImpl() { - } - - ServiceClientImpl.prototype = - Object.create(sample_service.ServiceClient.stubClass.prototype); - - ServiceClientImpl.prototype.didFrobinate = function(result) { - receivedDidFrobinate = true; - - expect(result).toBe(42); + return Promise.resolve(42); }; var pipe = core.createMessagePipe(); @@ -129,10 +116,10 @@ define([ var sourcePipe = core.createMessagePipe(); var connection0 = createPeerConnection( - pipe.handle0, ServiceImpl, sample_service.ServiceClient.proxyClass); + pipe.handle0, ServiceImpl); var connection1 = createPeerConnection( - pipe.handle1, ServiceClientImpl, sample_service.Service.proxyClass); + pipe.handle1, undefined, sample_service.Service.proxyClass); var foo = new sample_service.Foo(); foo.bar = new sample_service.Bar(); @@ -143,7 +130,6 @@ define([ mockSupport.pumpOnce(core.RESULT_OK); expect(receivedFrobinate).toBeTruthy(); - expect(receivedDidFrobinate).toBeTruthy(); connection0.close(); connection1.close(); @@ -179,8 +165,7 @@ define([ var foo = new sample_service.Foo(); foo.bar = new sample_service.Bar(); - // TODO(darin): crbug.com/357043: pass null in place of |foo| here. - connection1.remote.frobinate(foo, true, null); + connection1.remote.frobinate(null, true, null); // Write failures are not reported. expect(connection1.encounteredError()).toBeFalsy(); @@ -213,24 +198,15 @@ define([ return Promise.resolve({a: a, b: b}); }; - // ProviderClientImpl ------------------------------------------------------ - - function ProviderClientImpl() { - } - - ProviderClientImpl.prototype = - Object.create(sample_interfaces.ProviderClient.stubClass.prototype); - var pipe = core.createMessagePipe(); var connection0 = createPeerConnection( pipe.handle0, - ProviderImpl, - sample_interfaces.ProviderClient.proxyClass); + ProviderImpl); var connection1 = createPeerConnection( pipe.handle1, - ProviderClientImpl, + undefined, sample_interfaces.Provider.proxyClass); var origReadMessage = core.readMessage; diff --git a/third_party/mojo/src/mojo/edk/js/tests/sample_service_tests.js b/third_party/mojo/src/mojo/edk/js/tests/sample_service_tests.js index 2afdf3e..ac8ce2e 100644 --- a/third_party/mojo/src/mojo/edk/js/tests/sample_service_tests.js +++ b/third_party/mojo/src/mojo/edk/js/tests/sample_service_tests.js @@ -146,14 +146,14 @@ define([ function SimpleMessageReceiver() { } - SimpleMessageReceiver.prototype.accept = function(message) { + SimpleMessageReceiver.prototype.acceptAndExpectResponse = function(message) { if (dumpMessageAsHex) { var uint8Array = new Uint8Array(message.buffer.arrayBuffer); console.log(hexdump.dumpArray(uint8Array)); } // Imagine some IPC happened here. var serviceImpl = new ServiceImpl(); - serviceImpl.accept(message); + return serviceImpl.acceptWithResponder(message, { accept: function() {} }); }; var serviceProxy = new sample.Service.proxyClass; diff --git a/third_party/mojo/src/mojo/edk/system/BUILD.gn b/third_party/mojo/src/mojo/edk/system/BUILD.gn index a386ff9..40bf20d 100644 --- a/third_party/mojo/src/mojo/edk/system/BUILD.gn +++ b/third_party/mojo/src/mojo/edk/system/BUILD.gn @@ -44,6 +44,7 @@ component("system") { "core.h", "data_pipe.cc", "data_pipe.h", + "data_pipe_impl.h", "data_pipe_consumer_dispatcher.cc", "data_pipe_consumer_dispatcher.h", "data_pipe_producer_dispatcher.cc", @@ -57,8 +58,8 @@ component("system") { "handle_table.h", "incoming_endpoint.cc", "incoming_endpoint.h", - "local_data_pipe.cc", - "local_data_pipe.h", + "local_data_pipe_impl.cc", + "local_data_pipe_impl.h", "local_message_pipe_endpoint.cc", "local_message_pipe_endpoint.h", "mapping_table.cc", @@ -117,7 +118,6 @@ component("system") { deps = [ "//base", "//base/third_party/dynamic_annotations", - "//crypto", ] allow_circular_includes_from = [ "../embedder" ] @@ -158,7 +158,7 @@ test("mojo_system_unittests") { "core_unittest.cc", "data_pipe_unittest.cc", "dispatcher_unittest.cc", - "local_data_pipe_unittest.cc", + "local_data_pipe_impl_unittest.cc", "memory_unittest.cc", "message_pipe_dispatcher_unittest.cc", "message_pipe_test_utils.cc", diff --git a/third_party/mojo/src/mojo/edk/system/async_waiter.cc b/third_party/mojo/src/mojo/edk/system/async_waiter.cc index 071eb01..4f539b9 100644 --- a/third_party/mojo/src/mojo/edk/system/async_waiter.cc +++ b/third_party/mojo/src/mojo/edk/system/async_waiter.cc @@ -7,7 +7,7 @@ namespace mojo { namespace system { -AsyncWaiter::AsyncWaiter(AwakeCallback callback) : callback_(callback) { +AsyncWaiter::AsyncWaiter(const AwakeCallback& callback) : callback_(callback) { } AsyncWaiter::~AsyncWaiter() { diff --git a/third_party/mojo/src/mojo/edk/system/async_waiter.h b/third_party/mojo/src/mojo/edk/system/async_waiter.h index da412c0..df3b482 100644 --- a/third_party/mojo/src/mojo/edk/system/async_waiter.h +++ b/third_party/mojo/src/mojo/edk/system/async_waiter.h @@ -20,7 +20,7 @@ class MOJO_SYSTEM_IMPL_EXPORT AsyncWaiter final : public Awakable { typedef base::Callback<void(MojoResult)> AwakeCallback; // |callback| must satisfy the same contract as |Awakable::Awake()|. - explicit AsyncWaiter(AwakeCallback callback); + explicit AsyncWaiter(const AwakeCallback& callback); virtual ~AsyncWaiter(); private: diff --git a/third_party/mojo/src/mojo/edk/system/channel_manager.cc b/third_party/mojo/src/mojo/edk/system/channel_manager.cc index 634accc..a5301b3 100644 --- a/third_party/mojo/src/mojo/edk/system/channel_manager.cc +++ b/third_party/mojo/src/mojo/edk/system/channel_manager.cc @@ -18,27 +18,68 @@ namespace system { namespace { -void ShutdownChannelHelper(const ChannelInfo& channel_info) { - if (base::MessageLoopProxy::current() == - channel_info.channel_thread_task_runner) { - channel_info.channel->Shutdown(); +void ShutdownChannelHelper( + const ChannelInfo& channel_info, + const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner) { + DCHECK(base::MessageLoopProxy::current() == + channel_info.channel_thread_task_runner); + channel_info.channel->Shutdown(); + if (callback_thread_task_runner) { + bool ok = callback_thread_task_runner->PostTask(FROM_HERE, callback); + DCHECK(ok); } else { - channel_info.channel->WillShutdownSoon(); - channel_info.channel_thread_task_runner->PostTask( - FROM_HERE, base::Bind(&Channel::Shutdown, channel_info.channel)); + callback.Run(); } } } // namespace -ChannelManager::ChannelManager(embedder::PlatformSupport* platform_support) - : platform_support_(platform_support) { +ChannelManager::ChannelManager( + embedder::PlatformSupport* platform_support, + scoped_refptr<base::TaskRunner> io_thread_task_runner, + ConnectionManager* connection_manager) + : platform_support_(platform_support), + io_thread_task_runner_(io_thread_task_runner), + connection_manager_(connection_manager) { + DCHECK(platform_support_); + DCHECK(io_thread_task_runner_); + // (|connection_manager_| may be null.) } ChannelManager::~ChannelManager() { - // No need to take the lock. - for (const auto& map_elem : channel_infos_) - ShutdownChannelHelper(map_elem.second); + // |Shutdown()| must be called before destruction and have been completed. + // TODO(vtl): This doesn't verify the above condition very strictly at all + // (e.g., we may never have had any channels, or we may have manually shut all + // the channels down). + DCHECK(channel_infos_.empty()); +} + +void ChannelManager::ShutdownOnIOThread() { + // Taking this lock really shouldn't be necessary, but we do it for + // consistency. + base::hash_map<ChannelId, ChannelInfo> channel_infos; + { + base::AutoLock locker(lock_); + channel_infos.swap(channel_infos_); + } + + for (const auto& map_elem : channel_infos) { + const ChannelInfo& channel_info = map_elem.second; + DCHECK(base::MessageLoopProxy::current() == + channel_info.channel_thread_task_runner); + channel_info.channel->Shutdown(); + } +} + +void ChannelManager::Shutdown( + const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner) { + bool ok = io_thread_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ChannelManager::ShutdownHelper, base::Unretained(this), + callback, callback_thread_task_runner)); + DCHECK(ok); } scoped_refptr<MessagePipeDispatcher> ChannelManager::CreateChannelOnIOThread( @@ -57,9 +98,10 @@ scoped_refptr<MessagePipeDispatcher> ChannelManager::CreateChannel( ChannelId channel_id, embedder::ScopedPlatformHandle platform_handle, scoped_refptr<base::TaskRunner> io_thread_task_runner, - base::Closure callback, + const base::Closure& callback, scoped_refptr<base::TaskRunner> callback_thread_task_runner) { - DCHECK(io_thread_task_runner); + // TODO(vtl): Remove |io_thread_task_runner| argument. + DCHECK_EQ(io_thread_task_runner, io_thread_task_runner_); DCHECK(!callback.is_null()); // (|callback_thread_task_runner| may be null.) @@ -67,12 +109,13 @@ scoped_refptr<MessagePipeDispatcher> ChannelManager::CreateChannel( scoped_refptr<system::MessagePipeDispatcher> dispatcher = system::MessagePipeDispatcher::CreateRemoteMessagePipe( &bootstrap_channel_endpoint); - io_thread_task_runner->PostTask( + bool ok = io_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&ChannelManager::CreateChannelHelper, base::Unretained(this), channel_id, base::Passed(&platform_handle), bootstrap_channel_endpoint, callback, callback_thread_task_runner)); + DCHECK(ok); return dispatcher; } @@ -87,7 +130,24 @@ void ChannelManager::WillShutdownChannel(ChannelId channel_id) { GetChannel(channel_id)->WillShutdownSoon(); } -void ChannelManager::ShutdownChannel(ChannelId channel_id) { +void ChannelManager::ShutdownChannelOnIOThread(ChannelId channel_id) { + ChannelInfo channel_info; + { + base::AutoLock locker(lock_); + auto it = channel_infos_.find(channel_id); + DCHECK(it != channel_infos_.end()); + channel_info.Swap(&it->second); + channel_infos_.erase(it); + } + DCHECK(base::MessageLoopProxy::current() == + channel_info.channel_thread_task_runner); + channel_info.channel->Shutdown(); +} + +void ChannelManager::ShutdownChannel( + ChannelId channel_id, + const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner) { ChannelInfo channel_info; { base::AutoLock locker(lock_); @@ -96,7 +156,23 @@ void ChannelManager::ShutdownChannel(ChannelId channel_id) { channel_info.Swap(&it->second); channel_infos_.erase(it); } - ShutdownChannelHelper(channel_info); + channel_info.channel->WillShutdownSoon(); + bool ok = channel_info.channel_thread_task_runner->PostTask( + FROM_HERE, base::Bind(&ShutdownChannelHelper, channel_info, callback, + callback_thread_task_runner)); + DCHECK(ok); +} + +void ChannelManager::ShutdownHelper( + const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner) { + ShutdownOnIOThread(); + if (callback_thread_task_runner) { + bool ok = callback_thread_task_runner->PostTask(FROM_HERE, callback); + DCHECK(ok); + } else { + callback.Run(); + } } void ChannelManager::CreateChannelOnIOThreadHelper( @@ -126,14 +202,16 @@ void ChannelManager::CreateChannelHelper( ChannelId channel_id, embedder::ScopedPlatformHandle platform_handle, scoped_refptr<system::ChannelEndpoint> bootstrap_channel_endpoint, - base::Closure callback, + const base::Closure& callback, scoped_refptr<base::TaskRunner> callback_thread_task_runner) { CreateChannelOnIOThreadHelper(channel_id, platform_handle.Pass(), bootstrap_channel_endpoint); - if (callback_thread_task_runner) - callback_thread_task_runner->PostTask(FROM_HERE, callback); - else + if (callback_thread_task_runner) { + bool ok = callback_thread_task_runner->PostTask(FROM_HERE, callback); + DCHECK(ok); + } else { callback.Run(); + } } } // namespace system diff --git a/third_party/mojo/src/mojo/edk/system/channel_manager.h b/third_party/mojo/src/mojo/edk/system/channel_manager.h index da3e030..77ff953 100644 --- a/third_party/mojo/src/mojo/edk/system/channel_manager.h +++ b/third_party/mojo/src/mojo/edk/system/channel_manager.h @@ -29,6 +29,7 @@ namespace system { class Channel; class ChannelEndpoint; +class ConnectionManager; class MessagePipeDispatcher; // IDs for |Channel|s managed by a |ChannelManager|. (IDs should be thought of @@ -42,17 +43,37 @@ const ChannelId kInvalidChannelId = 0; // specifically noted. class MOJO_SYSTEM_IMPL_EXPORT ChannelManager { public: - // |*platform_support| must remain alive longer than this object. - explicit ChannelManager(embedder::PlatformSupport* platform_support); + // |io_thread_task_runner| should be the |TaskRunner| for the I/O thread, on + // which this channel manager will create all channels. Connection manager is + // optional and may be null. All arguments (if non-null) must remain alive at + // least until after shutdown completion. + ChannelManager(embedder::PlatformSupport* platform_support, + scoped_refptr<base::TaskRunner> io_thread_task_runner, + ConnectionManager* connection_manager); ~ChannelManager(); + // Shuts down the channel manager, including shutting down all channels (as if + // |ShutdownChannelOnIOThread()| were called for each channel). This must be + // called from the I/O thread (given to the constructor) and completes + // synchronously. This, or |Shutdown()|, must be called before destroying this + // object. + void ShutdownOnIOThread(); + + // Like |ShutdownOnIOThread()|, but may be called from any thread. On + // completion, will call |callback| ("on" |io_thread_task_runner| if + // |callback_thread_task_runner| is null else by posted using + // |callback_thread_task_runner|). Note: This will always post a task to the + // I/O thread, even it is the current thread. + // TODO(vtl): Consider if this is really necessary, since it only has one use + // (in tests). + void Shutdown(const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner); + // Creates a |Channel| and adds it to the set of channels managed by this - // |ChannelManager|. |channel_id| should be a valid |ChannelId| (i.e., - // nonzero) not "assigned" to any other |Channel| being managed by this + // |ChannelManager|. This must be called from the I/O thread (given to the + // constructor). |channel_id| should be a valid |ChannelId| (i.e., nonzero) + // not "assigned" to any other |Channel| being managed by this // |ChannelManager|. - // TODO(vtl): Currently, this should be called on any I/O thread (which will - // become the new channel's "channel thread"). Eventually, the channel manager - // will have an assigned I/O thread, on which this must be called. scoped_refptr<MessagePipeDispatcher> CreateChannelOnIOThread( ChannelId channel_id, embedder::ScopedPlatformHandle platform_handle); @@ -69,7 +90,7 @@ class MOJO_SYSTEM_IMPL_EXPORT ChannelManager { ChannelId channel_id, embedder::ScopedPlatformHandle platform_handle, scoped_refptr<base::TaskRunner> io_thread_task_runner, - base::Closure callback, + const base::Closure& callback, scoped_refptr<base::TaskRunner> callback_thread_task_runner); // Gets the |Channel| with the given ID (which must exist). @@ -82,14 +103,31 @@ class MOJO_SYSTEM_IMPL_EXPORT ChannelManager { // the channel). void WillShutdownChannel(ChannelId channel_id); - // Shuts down the channel specified by the given ID. It is up to the caller to - // guarantee that this is only called once per channel (that was added using - // |CreateChannelOnIOThread()|). If called from the channel's creation thread - // (i.e., |base::MessageLoopProxy::current()| is the channel thread's - // |TaskRunner|), this will complete synchronously. - void ShutdownChannel(ChannelId channel_id); + // Shuts down the channel specified by the given ID. This, or + // |ShutdownChannel()|, should be called once per channel (created using + // |CreateChannelOnIOThread()| or |CreateChannel()|). This must be called from + // the channel's "channel thread", and completes synchronously. + // TODO(vtl): "channel thread" will become "this object's I/O thread". + void ShutdownChannelOnIOThread(ChannelId channel_id); + + // Like |ShutdownChannelOnIOThread()|, but may be called from any thread. It + // will always post a task to the channel's "channel thread", and post + // |callback| to |callback_thread_task_runner| (or execute it directly on the + // "channel thread" if |callback_thread_task_runner| is null) on completion. + // TODO(vtl): "channel thread" will become "this object's I/O thread". + void ShutdownChannel( + ChannelId channel_id, + const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner); + + ConnectionManager* connection_manager() const { return connection_manager_; } private: + // Used by |Shutdown()|. Called on the I/O thread. + void ShutdownHelper( + const base::Closure& callback, + scoped_refptr<base::TaskRunner> callback_thread_task_runner); + // Used by |CreateChannelOnIOThread()| and |CreateChannelHelper()|. Called on // the I/O thread. void CreateChannelOnIOThreadHelper( @@ -102,10 +140,13 @@ class MOJO_SYSTEM_IMPL_EXPORT ChannelManager { ChannelId channel_id, embedder::ScopedPlatformHandle platform_handle, scoped_refptr<system::ChannelEndpoint> bootstrap_channel_endpoint, - base::Closure callback, + const base::Closure& callback, scoped_refptr<base::TaskRunner> callback_thread_task_runner); + // Note: These must not be used after shutdown. embedder::PlatformSupport* const platform_support_; + const scoped_refptr<base::TaskRunner> io_thread_task_runner_; + ConnectionManager* const connection_manager_; // Note: |Channel| methods should not be called under |lock_|. mutable base::Lock lock_; // Protects the members below. diff --git a/third_party/mojo/src/mojo/edk/system/channel_manager_unittest.cc b/third_party/mojo/src/mojo/edk/system/channel_manager_unittest.cc index 86ab4a0..1a3176e 100644 --- a/third_party/mojo/src/mojo/edk/system/channel_manager_unittest.cc +++ b/third_party/mojo/src/mojo/edk/system/channel_manager_unittest.cc @@ -10,10 +10,7 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/run_loop.h" #include "base/task_runner.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" -#include "base/time/time.h" #include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/simple_platform_support.h" #include "mojo/edk/system/channel.h" @@ -27,42 +24,44 @@ namespace { class ChannelManagerTest : public testing::Test { public: - ChannelManagerTest() : message_loop_(base::MessageLoop::TYPE_IO) {} + ChannelManagerTest() + : message_loop_(base::MessageLoop::TYPE_IO), + channel_manager_(&platform_support_, + message_loop_.task_runner(), + nullptr) {} ~ChannelManagerTest() override {} protected: - embedder::SimplePlatformSupport* platform_support() { - return &platform_support_; - } - base::MessageLoop* message_loop() { return &message_loop_; } + ChannelManager& channel_manager() { return channel_manager_; } private: embedder::SimplePlatformSupport platform_support_; base::MessageLoop message_loop_; + // Note: This should be *after* the above, since they must be initialized + // before it (and should outlive it). + ChannelManager channel_manager_; DISALLOW_COPY_AND_ASSIGN(ChannelManagerTest); }; TEST_F(ChannelManagerTest, Basic) { - ChannelManager cm(platform_support()); - embedder::PlatformChannelPair channel_pair; const ChannelId id = 1; scoped_refptr<MessagePipeDispatcher> d = - cm.CreateChannelOnIOThread(id, channel_pair.PassServerHandle()); + channel_manager().CreateChannelOnIOThread( + id, channel_pair.PassServerHandle()); - scoped_refptr<Channel> ch = cm.GetChannel(id); + scoped_refptr<Channel> ch = channel_manager().GetChannel(id); EXPECT_TRUE(ch); // |ChannelManager| should have a ref. EXPECT_FALSE(ch->HasOneRef()); - cm.WillShutdownChannel(id); + channel_manager().WillShutdownChannel(id); // |ChannelManager| should still have a ref. EXPECT_FALSE(ch->HasOneRef()); - cm.ShutdownChannel(id); - // On the "I/O" thread, so shutdown should happen synchronously. + channel_manager().ShutdownChannelOnIOThread(id); // |ChannelManager| should have given up its ref. EXPECT_TRUE(ch->HasOneRef()); @@ -70,33 +69,33 @@ TEST_F(ChannelManagerTest, Basic) { } TEST_F(ChannelManagerTest, TwoChannels) { - ChannelManager cm(platform_support()); - embedder::PlatformChannelPair channel_pair; const ChannelId id1 = 1; scoped_refptr<MessagePipeDispatcher> d1 = - cm.CreateChannelOnIOThread(id1, channel_pair.PassServerHandle()); + channel_manager().CreateChannelOnIOThread( + id1, channel_pair.PassServerHandle()); const ChannelId id2 = 2; scoped_refptr<MessagePipeDispatcher> d2 = - cm.CreateChannelOnIOThread(id2, channel_pair.PassClientHandle()); + channel_manager().CreateChannelOnIOThread( + id2, channel_pair.PassClientHandle()); - scoped_refptr<Channel> ch1 = cm.GetChannel(id1); + scoped_refptr<Channel> ch1 = channel_manager().GetChannel(id1); EXPECT_TRUE(ch1); - scoped_refptr<Channel> ch2 = cm.GetChannel(id2); + scoped_refptr<Channel> ch2 = channel_manager().GetChannel(id2); EXPECT_TRUE(ch2); // Calling |WillShutdownChannel()| multiple times (on |id1|) is okay. - cm.WillShutdownChannel(id1); - cm.WillShutdownChannel(id1); + channel_manager().WillShutdownChannel(id1); + channel_manager().WillShutdownChannel(id1); EXPECT_FALSE(ch1->HasOneRef()); // Not calling |WillShutdownChannel()| (on |id2|) is okay too. - cm.ShutdownChannel(id1); + channel_manager().ShutdownChannelOnIOThread(id1); EXPECT_TRUE(ch1->HasOneRef()); - cm.ShutdownChannel(id2); + channel_manager().ShutdownChannelOnIOThread(id2); EXPECT_TRUE(ch2->HasOneRef()); EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); @@ -110,7 +109,7 @@ class OtherThread : public base::SimpleThread { OtherThread(scoped_refptr<base::TaskRunner> task_runner, ChannelManager* channel_manager, ChannelId channel_id, - base::Closure quit_closure) + const base::Closure& quit_closure) : base::SimpleThread("other_thread"), task_runner_(task_runner), channel_manager_(channel_manager), @@ -132,20 +131,12 @@ class OtherThread : public base::SimpleThread { // |ChannelManager| should still have a ref. EXPECT_FALSE(ch->HasOneRef()); - channel_manager_->ShutdownChannel(channel_id_); - // This doesn't happen synchronously, so we "wait" until it does. - // TODO(vtl): Probably |Channel| should provide some notification of being - // shut down. - base::TimeTicks start_time(base::TimeTicks::Now()); - for (;;) { - if (ch->HasOneRef()) - break; - - // Check, instead of assert, since if things go wrong, dying is more - // reliable than tearing down. - CHECK_LT(base::TimeTicks::Now() - start_time, - TestTimeouts::action_timeout()); - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); + { + base::MessageLoop message_loop; + base::RunLoop run_loop; + channel_manager_->ShutdownChannel(channel_id_, run_loop.QuitClosure(), + message_loop.task_runner()); + run_loop.Run(); } CHECK(task_runner_->PostTask(FROM_HERE, quit_closure_)); @@ -160,16 +151,15 @@ class OtherThread : public base::SimpleThread { }; TEST_F(ChannelManagerTest, CallsFromOtherThread) { - ChannelManager cm(platform_support()); - embedder::PlatformChannelPair channel_pair; const ChannelId id = 1; scoped_refptr<MessagePipeDispatcher> d = - cm.CreateChannelOnIOThread(id, channel_pair.PassServerHandle()); + channel_manager().CreateChannelOnIOThread( + id, channel_pair.PassServerHandle()); base::RunLoop run_loop; - OtherThread thread(base::MessageLoopProxy::current(), &cm, id, + OtherThread thread(base::MessageLoopProxy::current(), &channel_manager(), id, run_loop.QuitClosure()); thread.Start(); run_loop.Run(); diff --git a/third_party/mojo/src/mojo/edk/system/connection_manager.h b/third_party/mojo/src/mojo/edk/system/connection_manager.h index fede7a5..921d19c 100644 --- a/third_party/mojo/src/mojo/edk/system/connection_manager.h +++ b/third_party/mojo/src/mojo/edk/system/connection_manager.h @@ -6,6 +6,7 @@ #define MOJO_EDK_SYSTEM_CONNECTION_MANAGER_H_ #include "base/macros.h" +#include "mojo/edk/system/system_impl_export.h" #include "mojo/edk/system/unique_identifier.h" namespace mojo { @@ -62,15 +63,21 @@ const ProcessIdentifier kInvalidProcessIdentifier = 0; // connected to the master by a special dedicated |RawChannel|, on which it does // synchronous IPC (note, however, that the master should never block on any // slave). -class ConnectionManager { +class MOJO_SYSTEM_IMPL_EXPORT ConnectionManager { public: - // All of these methods return true on success or false on failure. Failure is - // obviously fatal for the establishment of a particular connection, but - // should not be treated as fatal to the process. Failure may, e.g., be caused - // by a misbehaving (malicious) untrusted peer process. + virtual ~ConnectionManager() {} + + // Shuts down this connection manager. No other methods may be called after + // this is (or while it is being) called. + virtual void Shutdown() = 0; // TODO(vtl): Add a "get my own process identifier" method? + // All of the methods below return true on success or false on failure. + // Failure is obviously fatal for the establishment of a particular + // connection, but should not be treated as fatal to the process. Failure may, + // e.g., be caused by a misbehaving (malicious) untrusted peer process. + // Allows a process who makes the identical call (with equal |connection_id|) // to connect to the calling process. (On success, there will be a "pending // connection" for the given |connection_id| for the calling process.) @@ -93,7 +100,6 @@ class ConnectionManager { protected: ConnectionManager() {} - virtual ~ConnectionManager() {} private: DISALLOW_COPY_AND_ASSIGN(ConnectionManager); diff --git a/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc b/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc index f1939a0..fea0920 100644 --- a/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc +++ b/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc @@ -17,6 +17,7 @@ #include "base/threading/thread_checker.h" #include "mojo/edk/embedder/master_process_delegate.h" #include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/edk/embedder/simple_platform_support.h" #include "mojo/edk/embedder/slave_process_delegate.h" #include "mojo/edk/system/master_connection_manager.h" #include "mojo/edk/system/slave_connection_manager.h" @@ -63,10 +64,10 @@ bool IsValidSlaveProcessIdentifier(ProcessIdentifier process_identifier) { process_identifier != kMasterProcessIdentifier; } -class TestSlaveInfo : public embedder::SlaveInfo { +class TestSlaveInfo { public: explicit TestSlaveInfo(const std::string& name) : name_(name) {} - ~TestSlaveInfo() override { CHECK(thread_checker_.CalledOnValidThread()); } + ~TestSlaveInfo() { CHECK(thread_checker_.CalledOnValidThread()); } const std::string& name() const { return name_; } @@ -85,7 +86,7 @@ void ConnectSlave(MasterConnectionManager* master, SlaveConnectionManager* slave, const std::string& slave_name) { embedder::PlatformChannelPair platform_channel_pair; - master->AddSlave(make_scoped_ptr(new TestSlaveInfo(slave_name)), + master->AddSlave(new TestSlaveInfo(slave_name), platform_channel_pair.PassServerHandle()); slave->Init(base::MessageLoop::current()->task_runner(), slave_process_delegate, platform_channel_pair.PassClientHandle()); @@ -113,14 +114,16 @@ class MockMasterProcessDelegate : public embedder::MasterProcessDelegate { } // |embedder::MasterProcessDelegate| implementation: - void OnSlaveDisconnect(scoped_ptr<embedder::SlaveInfo> slave_info) override { + void OnShutdownComplete() override { NOTREACHED(); } + + void OnSlaveDisconnect(embedder::SlaveInfo slave_info) override { CHECK(thread_checker_.CalledOnValidThread()); on_slave_disconnect_calls_++; last_slave_disconnect_name_ = - static_cast<TestSlaveInfo*>(slave_info.get())->name(); + static_cast<TestSlaveInfo*>(slave_info)->name(); DVLOG(1) << "Disconnected from slave process " << last_slave_disconnect_name_; - slave_info.reset(); + delete static_cast<TestSlaveInfo*>(slave_info); if (current_run_loop_) current_run_loop_->Quit(); @@ -155,6 +158,8 @@ class MockSlaveProcessDelegate : public embedder::SlaveProcessDelegate { } // |embedder::SlaveProcessDelegate| implementation: + void OnShutdownComplete() override { NOTREACHED(); } + void OnMasterDisconnect() override { CHECK(thread_checker_.CalledOnValidThread()); on_master_disconnect_calls_++; @@ -178,12 +183,15 @@ class ConnectionManagerTest : public testing::Test { ConnectionManagerTest() {} ~ConnectionManagerTest() override {} + embedder::PlatformSupport* platform_support() { return &platform_support_; } + base::MessageLoop& message_loop() { return message_loop_; } MockMasterProcessDelegate& master_process_delegate() { return master_process_delegate_; } private: + embedder::SimplePlatformSupport platform_support_; base::MessageLoop message_loop_; MockMasterProcessDelegate master_process_delegate_; @@ -203,7 +211,8 @@ TEST_F(ConnectionManagerTest, BasicConnectSlaves) { SlaveConnectionManager slave2; ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(slave1.AllowConnect(connection_id)); EXPECT_TRUE(slave2.AllowConnect(connection_id)); @@ -291,7 +300,8 @@ TEST_F(ConnectionManagerTest, SlaveCancelConnect) { SlaveConnectionManager slave2; ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(slave1.AllowConnect(connection_id)); EXPECT_TRUE(slave2.AllowConnect(connection_id)); @@ -321,7 +331,8 @@ TEST_F(ConnectionManagerTest, ErrorRemovePending) { SlaveConnectionManager slave2; ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(slave1.AllowConnect(connection_id)); EXPECT_TRUE(slave2.AllowConnect(connection_id)); @@ -352,7 +363,8 @@ TEST_F(ConnectionManagerTest, ConnectSlaveToSelf) { SlaveConnectionManager slave; ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(slave.AllowConnect(connection_id)); EXPECT_TRUE(slave.AllowConnect(connection_id)); @@ -388,7 +400,8 @@ TEST_F(ConnectionManagerTest, ConnectSlavesTwice) { SlaveConnectionManager slave2; ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(slave1.AllowConnect(connection_id)); EXPECT_TRUE(slave2.AllowConnect(connection_id)); @@ -407,7 +420,7 @@ TEST_F(ConnectionManagerTest, ConnectSlavesTwice) { // tracking and is prone to races -- especially if we want slaves to be able // to tear down no-longer-needed connections.) But the slaves should be able // to do the tracking themselves (using the peer process identifiers). - connection_id = ConnectionIdentifier::Generate(); + connection_id = ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(slave1.AllowConnect(connection_id)); EXPECT_TRUE(slave2.AllowConnect(connection_id)); @@ -436,7 +449,8 @@ TEST_F(ConnectionManagerTest, ConnectMasterToSlave) { SlaveConnectionManager slave; ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(master.AllowConnect(connection_id)); EXPECT_TRUE(slave.AllowConnect(connection_id)); @@ -463,7 +477,8 @@ TEST_F(ConnectionManagerTest, ConnectMasterToSelf) { master.Init(base::MessageLoop::current()->task_runner(), &master_process_delegate()); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(master.AllowConnect(connection_id)); EXPECT_TRUE(master.AllowConnect(connection_id)); @@ -494,7 +509,8 @@ TEST_F(ConnectionManagerTest, MasterCancelConnect) { SlaveConnectionManager slave; ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); - ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + ConnectionIdentifier connection_id = + ConnectionIdentifier::Generate(platform_support()); EXPECT_TRUE(master.AllowConnect(connection_id)); EXPECT_TRUE(slave.AllowConnect(connection_id)); @@ -517,7 +533,7 @@ TEST_F(ConnectionManagerTest, AddSlaveThenImmediateShutdown) { MockSlaveProcessDelegate slave_process_delegate; SlaveConnectionManager slave; embedder::PlatformChannelPair platform_channel_pair; - master.AddSlave(make_scoped_ptr(new TestSlaveInfo("slave")), + master.AddSlave(new TestSlaveInfo("slave"), platform_channel_pair.PassServerHandle()); master.Shutdown(); // Since we never initialized |slave|, we don't have to shut it down. diff --git a/third_party/mojo/src/mojo/edk/system/core.cc b/third_party/mojo/src/mojo/edk/system/core.cc index 4460088..80c3d36 100644 --- a/third_party/mojo/src/mojo/edk/system/core.cc +++ b/third_party/mojo/src/mojo/edk/system/core.cc @@ -17,7 +17,6 @@ #include "mojo/edk/system/data_pipe_producer_dispatcher.h" #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/local_data_pipe.h" #include "mojo/edk/system/memory.h" #include "mojo/edk/system/message_pipe.h" #include "mojo/edk/system/message_pipe_dispatcher.h" @@ -100,7 +99,7 @@ scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { MojoResult Core::AsyncWait(MojoHandle handle, MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback) { + const base::Callback<void(MojoResult)>& callback) { scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); DCHECK(dispatcher); @@ -381,7 +380,7 @@ MojoResult Core::CreateDataPipe( } DCHECK_NE(handle_pair.second, MOJO_HANDLE_INVALID); - scoped_refptr<DataPipe> data_pipe(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> data_pipe(DataPipe::CreateLocal(validated_options)); producer_dispatcher->Init(data_pipe); consumer_dispatcher->Init(data_pipe); diff --git a/third_party/mojo/src/mojo/edk/system/core.h b/third_party/mojo/src/mojo/edk/system/core.h index 7833193c..aadfb66 100644 --- a/third_party/mojo/src/mojo/edk/system/core.h +++ b/third_party/mojo/src/mojo/edk/system/core.h @@ -58,7 +58,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // awakable.h. In particular, it must not call any Mojo system functions. MojoResult AsyncWait(MojoHandle handle, MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback); + const base::Callback<void(MojoResult)>& callback); embedder::PlatformSupport* platform_support() const { return platform_support_; diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe.cc b/third_party/mojo/src/mojo/edk/system/data_pipe.cc index 2f433bd..d5e9db9 100644 --- a/third_party/mojo/src/mojo/edk/system/data_pipe.cc +++ b/third_party/mojo/src/mojo/edk/system/data_pipe.cc @@ -12,6 +12,8 @@ #include "base/logging.h" #include "mojo/edk/system/awakable_list.h" #include "mojo/edk/system/configuration.h" +#include "mojo/edk/system/data_pipe_impl.h" +#include "mojo/edk/system/local_data_pipe_impl.h" #include "mojo/edk/system/memory.h" #include "mojo/edk/system/options_validation.h" @@ -83,6 +85,13 @@ MojoResult DataPipe::ValidateCreateOptions( return MOJO_RESULT_OK; } +// static +DataPipe* DataPipe::CreateLocal( + const MojoCreateDataPipeOptions& validated_options) { + return new DataPipe(true, true, validated_options, + make_scoped_ptr(new LocalDataPipeImpl())); +} + void DataPipe::ProducerCancelAllAwakables() { base::AutoLock locker(lock_); DCHECK(has_local_producer_no_lock()); @@ -91,17 +100,7 @@ void DataPipe::ProducerCancelAllAwakables() { void DataPipe::ProducerClose() { base::AutoLock locker(lock_); - DCHECK(producer_open_); - producer_open_ = false; - DCHECK(has_local_producer_no_lock()); - producer_awakable_list_.reset(); - // Not a bug, except possibly in "user" code. - DVLOG_IF(2, producer_in_two_phase_write_no_lock()) - << "Producer closed with active two-phase write"; - producer_two_phase_max_num_bytes_written_ = 0; - ProducerCloseImplNoLock(); - AwakeConsumerAwakablesForStateChangeNoLock( - ConsumerGetHandleSignalsStateImplNoLock()); + ProducerCloseNoLock(); } MojoResult DataPipe::ProducerWriteData(UserPointer<const void> elements, @@ -117,7 +116,7 @@ MojoResult DataPipe::ProducerWriteData(UserPointer<const void> elements, // Returning "busy" takes priority over "invalid argument". uint32_t max_num_bytes_to_write = num_bytes.Get(); - if (max_num_bytes_to_write % element_num_bytes_ != 0) + if (max_num_bytes_to_write % element_num_bytes() != 0) return MOJO_RESULT_INVALID_ARGUMENT; if (max_num_bytes_to_write == 0) @@ -126,11 +125,11 @@ MojoResult DataPipe::ProducerWriteData(UserPointer<const void> elements, uint32_t min_num_bytes_to_write = all_or_none ? max_num_bytes_to_write : 0; HandleSignalsState old_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); - MojoResult rv = ProducerWriteDataImplNoLock( + impl_->ConsumerGetHandleSignalsState(); + MojoResult rv = impl_->ProducerWriteData( elements, num_bytes, max_num_bytes_to_write, min_num_bytes_to_write); HandleSignalsState new_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); + impl_->ConsumerGetHandleSignalsState(); if (!new_consumer_state.equals(old_consumer_state)) AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); return rv; @@ -151,12 +150,12 @@ MojoResult DataPipe::ProducerBeginWriteData( uint32_t min_num_bytes_to_write = 0; if (all_or_none) { min_num_bytes_to_write = buffer_num_bytes.Get(); - if (min_num_bytes_to_write % element_num_bytes_ != 0) + if (min_num_bytes_to_write % element_num_bytes() != 0) return MOJO_RESULT_INVALID_ARGUMENT; } - MojoResult rv = ProducerBeginWriteDataImplNoLock(buffer, buffer_num_bytes, - min_num_bytes_to_write); + MojoResult rv = impl_->ProducerBeginWriteData(buffer, buffer_num_bytes, + min_num_bytes_to_write); if (rv != MOJO_RESULT_OK) return rv; // Note: No need to awake producer awakables, even though we're going from @@ -177,25 +176,25 @@ MojoResult DataPipe::ProducerEndWriteData(uint32_t num_bytes_written) { // consumer has been closed. HandleSignalsState old_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); + impl_->ConsumerGetHandleSignalsState(); MojoResult rv; if (num_bytes_written > producer_two_phase_max_num_bytes_written_ || - num_bytes_written % element_num_bytes_ != 0) { + num_bytes_written % element_num_bytes() != 0) { rv = MOJO_RESULT_INVALID_ARGUMENT; producer_two_phase_max_num_bytes_written_ = 0; } else { - rv = ProducerEndWriteDataImplNoLock(num_bytes_written); + rv = impl_->ProducerEndWriteData(num_bytes_written); } // Two-phase write ended even on failure. DCHECK(!producer_in_two_phase_write_no_lock()); // If we're now writable, we *became* writable (since we weren't writable // during the two-phase write), so awake producer awakables. HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); + impl_->ProducerGetHandleSignalsState(); if (new_producer_state.satisfies(MOJO_HANDLE_SIGNAL_WRITABLE)) AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); HandleSignalsState new_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); + impl_->ConsumerGetHandleSignalsState(); if (!new_consumer_state.equals(old_consumer_state)) AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); return rv; @@ -204,7 +203,7 @@ MojoResult DataPipe::ProducerEndWriteData(uint32_t num_bytes_written) { HandleSignalsState DataPipe::ProducerGetHandleSignalsState() { base::AutoLock locker(lock_); DCHECK(has_local_producer_no_lock()); - return ProducerGetHandleSignalsStateImplNoLock(); + return impl_->ProducerGetHandleSignalsState(); } MojoResult DataPipe::ProducerAddAwakable(Awakable* awakable, @@ -214,7 +213,7 @@ MojoResult DataPipe::ProducerAddAwakable(Awakable* awakable, base::AutoLock locker(lock_); DCHECK(has_local_producer_no_lock()); - HandleSignalsState producer_state = ProducerGetHandleSignalsStateImplNoLock(); + HandleSignalsState producer_state = impl_->ProducerGetHandleSignalsState(); if (producer_state.satisfies(signals)) { if (signals_state) *signals_state = producer_state; @@ -236,7 +235,26 @@ void DataPipe::ProducerRemoveAwakable(Awakable* awakable, DCHECK(has_local_producer_no_lock()); producer_awakable_list_->Remove(awakable); if (signals_state) - *signals_state = ProducerGetHandleSignalsStateImplNoLock(); + *signals_state = impl_->ProducerGetHandleSignalsState(); +} + +void DataPipe::ProducerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) { + base::AutoLock locker(lock_); + DCHECK(has_local_producer_no_lock()); + impl_->ProducerStartSerialize(channel, max_size, max_platform_handles); +} + +bool DataPipe::ProducerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) { + base::AutoLock locker(lock_); + DCHECK(has_local_producer_no_lock()); + return impl_->ProducerEndSerialize(channel, destination, actual_size, + platform_handles); } bool DataPipe::ProducerIsBusy() const { @@ -252,17 +270,7 @@ void DataPipe::ConsumerCancelAllAwakables() { void DataPipe::ConsumerClose() { base::AutoLock locker(lock_); - DCHECK(consumer_open_); - consumer_open_ = false; - DCHECK(has_local_consumer_no_lock()); - consumer_awakable_list_.reset(); - // Not a bug, except possibly in "user" code. - DVLOG_IF(2, consumer_in_two_phase_read_no_lock()) - << "Consumer closed with active two-phase read"; - consumer_two_phase_max_num_bytes_read_ = 0; - ConsumerCloseImplNoLock(); - AwakeProducerAwakablesForStateChangeNoLock( - ProducerGetHandleSignalsStateImplNoLock()); + ConsumerCloseNoLock(); } MojoResult DataPipe::ConsumerReadData(UserPointer<void> elements, @@ -276,7 +284,7 @@ MojoResult DataPipe::ConsumerReadData(UserPointer<void> elements, return MOJO_RESULT_BUSY; uint32_t max_num_bytes_to_read = num_bytes.Get(); - if (max_num_bytes_to_read % element_num_bytes_ != 0) + if (max_num_bytes_to_read % element_num_bytes() != 0) return MOJO_RESULT_INVALID_ARGUMENT; if (max_num_bytes_to_read == 0) @@ -285,11 +293,11 @@ MojoResult DataPipe::ConsumerReadData(UserPointer<void> elements, uint32_t min_num_bytes_to_read = all_or_none ? max_num_bytes_to_read : 0; HandleSignalsState old_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - MojoResult rv = ConsumerReadDataImplNoLock( + impl_->ProducerGetHandleSignalsState(); + MojoResult rv = impl_->ConsumerReadData( elements, num_bytes, max_num_bytes_to_read, min_num_bytes_to_read, peek); HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); + impl_->ProducerGetHandleSignalsState(); if (!new_producer_state.equals(old_producer_state)) AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); return rv; @@ -304,7 +312,7 @@ MojoResult DataPipe::ConsumerDiscardData(UserPointer<uint32_t> num_bytes, return MOJO_RESULT_BUSY; uint32_t max_num_bytes_to_discard = num_bytes.Get(); - if (max_num_bytes_to_discard % element_num_bytes_ != 0) + if (max_num_bytes_to_discard % element_num_bytes() != 0) return MOJO_RESULT_INVALID_ARGUMENT; if (max_num_bytes_to_discard == 0) @@ -314,11 +322,11 @@ MojoResult DataPipe::ConsumerDiscardData(UserPointer<uint32_t> num_bytes, all_or_none ? max_num_bytes_to_discard : 0; HandleSignalsState old_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - MojoResult rv = ConsumerDiscardDataImplNoLock( + impl_->ProducerGetHandleSignalsState(); + MojoResult rv = impl_->ConsumerDiscardData( num_bytes, max_num_bytes_to_discard, min_num_bytes_to_discard); HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); + impl_->ProducerGetHandleSignalsState(); if (!new_producer_state.equals(old_producer_state)) AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); return rv; @@ -332,7 +340,7 @@ MojoResult DataPipe::ConsumerQueryData(UserPointer<uint32_t> num_bytes) { return MOJO_RESULT_BUSY; // Note: Don't need to validate |*num_bytes| for query. - return ConsumerQueryDataImplNoLock(num_bytes); + return impl_->ConsumerQueryData(num_bytes); } MojoResult DataPipe::ConsumerBeginReadData( @@ -348,12 +356,12 @@ MojoResult DataPipe::ConsumerBeginReadData( uint32_t min_num_bytes_to_read = 0; if (all_or_none) { min_num_bytes_to_read = buffer_num_bytes.Get(); - if (min_num_bytes_to_read % element_num_bytes_ != 0) + if (min_num_bytes_to_read % element_num_bytes() != 0) return MOJO_RESULT_INVALID_ARGUMENT; } - MojoResult rv = ConsumerBeginReadDataImplNoLock(buffer, buffer_num_bytes, - min_num_bytes_to_read); + MojoResult rv = impl_->ConsumerBeginReadData(buffer, buffer_num_bytes, + min_num_bytes_to_read); if (rv != MOJO_RESULT_OK) return rv; DCHECK(consumer_in_two_phase_read_no_lock()); @@ -368,25 +376,25 @@ MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) { return MOJO_RESULT_FAILED_PRECONDITION; HandleSignalsState old_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); + impl_->ProducerGetHandleSignalsState(); MojoResult rv; if (num_bytes_read > consumer_two_phase_max_num_bytes_read_ || - num_bytes_read % element_num_bytes_ != 0) { + num_bytes_read % element_num_bytes() != 0) { rv = MOJO_RESULT_INVALID_ARGUMENT; consumer_two_phase_max_num_bytes_read_ = 0; } else { - rv = ConsumerEndReadDataImplNoLock(num_bytes_read); + rv = impl_->ConsumerEndReadData(num_bytes_read); } // Two-phase read ended even on failure. DCHECK(!consumer_in_two_phase_read_no_lock()); // If we're now readable, we *became* readable (since we weren't readable // during the two-phase read), so awake consumer awakables. HandleSignalsState new_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); + impl_->ConsumerGetHandleSignalsState(); if (new_consumer_state.satisfies(MOJO_HANDLE_SIGNAL_READABLE)) AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); + impl_->ProducerGetHandleSignalsState(); if (!new_producer_state.equals(old_producer_state)) AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); return rv; @@ -395,7 +403,7 @@ MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) { HandleSignalsState DataPipe::ConsumerGetHandleSignalsState() { base::AutoLock locker(lock_); DCHECK(has_local_consumer_no_lock()); - return ConsumerGetHandleSignalsStateImplNoLock(); + return impl_->ConsumerGetHandleSignalsState(); } MojoResult DataPipe::ConsumerAddAwakable(Awakable* awakable, @@ -405,7 +413,7 @@ MojoResult DataPipe::ConsumerAddAwakable(Awakable* awakable, base::AutoLock locker(lock_); DCHECK(has_local_consumer_no_lock()); - HandleSignalsState consumer_state = ConsumerGetHandleSignalsStateImplNoLock(); + HandleSignalsState consumer_state = impl_->ConsumerGetHandleSignalsState(); if (consumer_state.satisfies(signals)) { if (signals_state) *signals_state = consumer_state; @@ -427,7 +435,26 @@ void DataPipe::ConsumerRemoveAwakable(Awakable* awakable, DCHECK(has_local_consumer_no_lock()); consumer_awakable_list_->Remove(awakable); if (signals_state) - *signals_state = ConsumerGetHandleSignalsStateImplNoLock(); + *signals_state = impl_->ConsumerGetHandleSignalsState(); +} + +void DataPipe::ConsumerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) { + base::AutoLock locker(lock_); + DCHECK(has_local_consumer_no_lock()); + impl_->ConsumerStartSerialize(channel, max_size, max_platform_handles); +} + +bool DataPipe::ConsumerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) { + base::AutoLock locker(lock_); + DCHECK(has_local_consumer_no_lock()); + return impl_->ConsumerEndSerialize(channel, destination, actual_size, + platform_handles); } bool DataPipe::ConsumerIsBusy() const { @@ -437,11 +464,9 @@ bool DataPipe::ConsumerIsBusy() const { DataPipe::DataPipe(bool has_local_producer, bool has_local_consumer, - const MojoCreateDataPipeOptions& validated_options) - : may_discard_((validated_options.flags & - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)), - element_num_bytes_(validated_options.element_num_bytes), - capacity_num_bytes_(validated_options.capacity_num_bytes), + const MojoCreateDataPipeOptions& validated_options, + scoped_ptr<DataPipeImpl> impl) + : validated_options_(validated_options), producer_open_(true), consumer_open_(true), producer_awakable_list_(has_local_producer ? new AwakableList() @@ -449,11 +474,16 @@ DataPipe::DataPipe(bool has_local_producer, consumer_awakable_list_(has_local_consumer ? new AwakableList() : nullptr), producer_two_phase_max_num_bytes_written_(0), - consumer_two_phase_max_num_bytes_read_(0) { + consumer_two_phase_max_num_bytes_read_(0), + impl_(impl.Pass()) { + impl_->set_owner(this); + +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) // Check that the passed in options actually are validated. MojoCreateDataPipeOptions unused = {0}; DCHECK_EQ(ValidateCreateOptions(MakeUserPointer(&validated_options), &unused), MOJO_RESULT_OK); +#endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) } DataPipe::~DataPipe() { @@ -463,6 +493,36 @@ DataPipe::~DataPipe() { DCHECK(!consumer_awakable_list_); } +void DataPipe::ProducerCloseNoLock() { + lock_.AssertAcquired(); + DCHECK(producer_open_); + producer_open_ = false; + DCHECK(has_local_producer_no_lock()); + producer_awakable_list_.reset(); + // Not a bug, except possibly in "user" code. + DVLOG_IF(2, producer_in_two_phase_write_no_lock()) + << "Producer closed with active two-phase write"; + producer_two_phase_max_num_bytes_written_ = 0; + impl_->ProducerClose(); + AwakeConsumerAwakablesForStateChangeNoLock( + impl_->ConsumerGetHandleSignalsState()); +} + +void DataPipe::ConsumerCloseNoLock() { + lock_.AssertAcquired(); + DCHECK(consumer_open_); + consumer_open_ = false; + DCHECK(has_local_consumer_no_lock()); + consumer_awakable_list_.reset(); + // Not a bug, except possibly in "user" code. + DVLOG_IF(2, consumer_in_two_phase_read_no_lock()) + << "Consumer closed with active two-phase read"; + consumer_two_phase_max_num_bytes_read_ = 0; + impl_->ConsumerClose(); + AwakeProducerAwakablesForStateChangeNoLock( + impl_->ProducerGetHandleSignalsState()); +} + void DataPipe::AwakeProducerAwakablesForStateChangeNoLock( const HandleSignalsState& new_producer_state) { lock_.AssertAcquired(); diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe.h b/third_party/mojo/src/mojo/edk/system/data_pipe.h index d893465..dfac644 100644 --- a/third_party/mojo/src/mojo/edk/system/data_pipe.h +++ b/third_party/mojo/src/mojo/edk/system/data_pipe.h @@ -7,10 +7,12 @@ #include <stdint.h> +#include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" +#include "mojo/edk/embedder/platform_handle_vector.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/edk/system/memory.h" #include "mojo/edk/system/system_impl_export.h" @@ -22,6 +24,8 @@ namespace system { class Awakable; class AwakableList; +class Channel; +class DataPipeImpl; // |DataPipe| is a base class for secondary objects implementing data pipes, // similar to |MessagePipe| (see the explanatory comment in core.cc). It is @@ -46,6 +50,14 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipe UserPointer<const MojoCreateDataPipeOptions> in_options, MojoCreateDataPipeOptions* out_options); + // Creates a local (both producer and consumer) data pipe (using + // |LocalDataPipeImpl|. |validated_options| should be the output of + // |ValidateOptions()|. In particular: |struct_size| is ignored (so + // |validated_options| must be the current version of the struct) and + // |capacity_num_bytes| must be nonzero. + static DataPipe* CreateLocal( + const MojoCreateDataPipeOptions& validated_options); + // These are called by the producer dispatcher to implement its methods of // corresponding names. void ProducerCancelAllAwakables(); @@ -64,6 +76,13 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipe HandleSignalsState* signals_state); void ProducerRemoveAwakable(Awakable* awakable, HandleSignalsState* signals_state); + void ProducerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles); + bool ProducerEndSerialize(Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles); bool ProducerIsBusy() const; // These are called by the consumer dispatcher to implement its methods of @@ -90,60 +109,34 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipe HandleSignalsState* signals_state); void ConsumerRemoveAwakable(Awakable* awakable, HandleSignalsState* signals_state); + void ConsumerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles); + bool ConsumerEndSerialize(Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles); bool ConsumerIsBusy() const; - protected: - DataPipe(bool has_local_producer, - bool has_local_consumer, - const MojoCreateDataPipeOptions& validated_options); - - friend class base::RefCountedThreadSafe<DataPipe>; - virtual ~DataPipe(); + // The following are only to be used by |DataPipeImpl| (and its subclasses): - virtual void ProducerCloseImplNoLock() = 0; - // |num_bytes.Get()| will be a nonzero multiple of |element_num_bytes_|. - virtual MojoResult ProducerWriteDataImplNoLock( - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_write, - uint32_t min_num_bytes_to_write) = 0; - virtual MojoResult ProducerBeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_write) = 0; - virtual MojoResult ProducerEndWriteDataImplNoLock( - uint32_t num_bytes_written) = 0; - // Note: A producer should not be writable during a two-phase write. - virtual HandleSignalsState ProducerGetHandleSignalsStateImplNoLock() - const = 0; - - virtual void ConsumerCloseImplNoLock() = 0; - // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|. - virtual MojoResult ConsumerReadDataImplNoLock(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_read, - uint32_t min_num_bytes_to_read, - bool peek) = 0; - virtual MojoResult ConsumerDiscardDataImplNoLock( - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_discard, - uint32_t min_num_bytes_to_discard) = 0; - // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|. - virtual MojoResult ConsumerQueryDataImplNoLock( - UserPointer<uint32_t> num_bytes) = 0; - virtual MojoResult ConsumerBeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_read) = 0; - virtual MojoResult ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read) = 0; - // Note: A consumer should not be writable during a two-phase read. - virtual HandleSignalsState ConsumerGetHandleSignalsStateImplNoLock() - const = 0; + void ProducerCloseNoLock(); + void ConsumerCloseNoLock(); // Thread-safe and fast (they don't take the lock): - bool may_discard() const { return may_discard_; } - size_t element_num_bytes() const { return element_num_bytes_; } - size_t capacity_num_bytes() const { return capacity_num_bytes_; } + const MojoCreateDataPipeOptions& validated_options() const { + return validated_options_; + } + bool may_discard() const { + return (validated_options_.flags & + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD); + } + size_t element_num_bytes() const { + return validated_options_.element_num_bytes; + } + size_t capacity_num_bytes() const { + return validated_options_.capacity_num_bytes; + } // Must be called under lock. bool producer_open_no_lock() const { @@ -181,6 +174,22 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipe } private: + friend class base::RefCountedThreadSafe<DataPipe>; + + // |validated_options| should be the output of |ValidateOptions()|. In + // particular: |struct_size| is ignored (so |validated_options| must be the + // current version of the struct) and |capacity_num_bytes| must be nonzero. + // TODO(vtl): |has_local_producer|/|has_local_consumer| shouldn't really be + // arguments here. Instead, they should be determined from the |impl| ... but + // the |impl|'s typically figures these out by examining the owner, i.e., the + // |DataPipe| object. Probably, this indicates that more stuff should be moved + // to |DataPipeImpl|, but for now we'll live with this. + DataPipe(bool has_local_producer, + bool has_local_consumer, + const MojoCreateDataPipeOptions& validated_options, + scoped_ptr<DataPipeImpl> impl); + virtual ~DataPipe(); + void AwakeProducerAwakablesForStateChangeNoLock( const HandleSignalsState& new_producer_state); void AwakeConsumerAwakablesForStateChangeNoLock( @@ -195,9 +204,8 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipe return !!consumer_awakable_list_; } - const bool may_discard_; - const size_t element_num_bytes_; - const size_t capacity_num_bytes_; + MSVC_SUPPRESS_WARNING(4324) // Suppress an alignment warning on 64-bit MSVC. + const MojoCreateDataPipeOptions validated_options_; mutable base::Lock lock_; // Protects the following members. // *Known* state of producer or consumer. @@ -209,6 +217,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipe // These are nonzero if and only if a two-phase write/read is in progress. uint32_t producer_two_phase_max_num_bytes_written_; uint32_t consumer_two_phase_max_num_bytes_read_; + scoped_ptr<DataPipeImpl> impl_; DISALLOW_COPY_AND_ASSIGN(DataPipe); }; diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.cc index 21127c6..65ec8ef 100644 --- a/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ b/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.cc @@ -126,6 +126,27 @@ void DataPipeConsumerDispatcher::RemoveAwakableImplNoLock( data_pipe_->ConsumerRemoveAwakable(awakable, signals_state); } +void DataPipeConsumerDispatcher::StartSerializeImplNoLock( + Channel* channel, + size_t* max_size, + size_t* max_platform_handles) { + DCHECK(HasOneRef()); // Only one ref => no need to take the lock. + data_pipe_->ConsumerStartSerialize(channel, max_size, max_platform_handles); +} + +bool DataPipeConsumerDispatcher::EndSerializeAndCloseImplNoLock( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) { + DCHECK(HasOneRef()); // Only one ref => no need to take the lock. + + bool rv = data_pipe_->ConsumerEndSerialize(channel, destination, actual_size, + platform_handles); + data_pipe_ = nullptr; + return rv; +} + bool DataPipeConsumerDispatcher::IsBusyNoLock() const { lock().AssertAcquired(); return data_pipe_->ConsumerIsBusy(); diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.h b/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.h index 10a3d94..34b3f4a 100644 --- a/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.h +++ b/third_party/mojo/src/mojo/edk/system/data_pipe_consumer_dispatcher.h @@ -50,6 +50,14 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher : public Dispatcher { HandleSignalsState* signals_state) override; void RemoveAwakableImplNoLock(Awakable* awakable, HandleSignalsState* signals_state) override; + void StartSerializeImplNoLock(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) override; + bool EndSerializeAndCloseImplNoLock( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) override; bool IsBusyNoLock() const override; // Protected by |lock()|: diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe_impl.h b/third_party/mojo/src/mojo/edk/system/data_pipe_impl.h new file mode 100644 index 0000000..e1339c0 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/data_pipe_impl.h @@ -0,0 +1,122 @@ +// 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. + +#ifndef MOJO_EDK_SYSTEM_DATA_PIPE_IMPL_H_ +#define MOJO_EDK_SYSTEM_DATA_PIPE_IMPL_H_ + +#include <stdint.h> + +#include "base/macros.h" +#include "mojo/edk/embedder/platform_handle_vector.h" +#include "mojo/edk/system/data_pipe.h" +#include "mojo/edk/system/handle_signals_state.h" +#include "mojo/edk/system/memory.h" +#include "mojo/edk/system/system_impl_export.h" +#include "mojo/public/c/system/types.h" + +namespace mojo { +namespace system { + +class Channel; + +// Base class/interface for classes that "implement" |DataPipe| for various +// situations (local versus remote). The methods, other than the constructor, +// |set_owner()|, and the destructor, are always protected by |DataPipe|'s +// |lock_|. +class MOJO_SYSTEM_IMPL_EXPORT DataPipeImpl { + public: + virtual ~DataPipeImpl() {} + + // This is only called by |DataPipe| during its construction. + void set_owner(DataPipe* owner) { owner_ = owner; } + + virtual void ProducerClose() = 0; + // |num_bytes.Get()| will be a nonzero multiple of |element_num_bytes()|. + virtual MojoResult ProducerWriteData(UserPointer<const void> elements, + UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_write, + uint32_t min_num_bytes_to_write) = 0; + virtual MojoResult ProducerBeginWriteData( + UserPointer<void*> buffer, + UserPointer<uint32_t> buffer_num_bytes, + uint32_t min_num_bytes_to_write) = 0; + virtual MojoResult ProducerEndWriteData(uint32_t num_bytes_written) = 0; + // Note: A producer should not be writable during a two-phase write. + virtual HandleSignalsState ProducerGetHandleSignalsState() const = 0; + virtual void ProducerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) = 0; + virtual bool ProducerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) = 0; + + virtual void ConsumerClose() = 0; + // |num_bytes.Get()| will be a nonzero multiple of |element_num_bytes()|. + virtual MojoResult ConsumerReadData(UserPointer<void> elements, + UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_read, + uint32_t min_num_bytes_to_read, + bool peek) = 0; + virtual MojoResult ConsumerDiscardData(UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_discard, + uint32_t min_num_bytes_to_discard) = 0; + // |num_bytes.Get()| will be a nonzero multiple of |element_num_bytes()|. + virtual MojoResult ConsumerQueryData(UserPointer<uint32_t> num_bytes) = 0; + virtual MojoResult ConsumerBeginReadData( + UserPointer<const void*> buffer, + UserPointer<uint32_t> buffer_num_bytes, + uint32_t min_num_bytes_to_read) = 0; + virtual MojoResult ConsumerEndReadData(uint32_t num_bytes_read) = 0; + // Note: A consumer should not be writable during a two-phase read. + virtual HandleSignalsState ConsumerGetHandleSignalsState() const = 0; + virtual void ConsumerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) = 0; + virtual bool ConsumerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) = 0; + + protected: + DataPipeImpl() : owner_() {} + + DataPipe* owner() const { return owner_; } + + bool may_discard() const { return owner_->may_discard(); } + size_t element_num_bytes() const { return owner_->element_num_bytes(); } + size_t capacity_num_bytes() const { return owner_->capacity_num_bytes(); } + bool producer_open() const { return owner_->producer_open_no_lock(); } + bool consumer_open() const { return owner_->consumer_open_no_lock(); } + uint32_t producer_two_phase_max_num_bytes_written() const { + return owner_->producer_two_phase_max_num_bytes_written_no_lock(); + } + uint32_t consumer_two_phase_max_num_bytes_read() const { + return owner_->consumer_two_phase_max_num_bytes_read_no_lock(); + } + void set_producer_two_phase_max_num_bytes_written(uint32_t num_bytes) { + owner_->set_producer_two_phase_max_num_bytes_written_no_lock(num_bytes); + } + void set_consumer_two_phase_max_num_bytes_read(uint32_t num_bytes) { + owner_->set_consumer_two_phase_max_num_bytes_read_no_lock(num_bytes); + } + bool producer_in_two_phase_write() const { + return owner_->producer_in_two_phase_write_no_lock(); + } + bool consumer_in_two_phase_read() const { + return owner_->consumer_in_two_phase_read_no_lock(); + } + + private: + DataPipe* owner_; + + DISALLOW_COPY_AND_ASSIGN(DataPipeImpl); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_DATA_PIPE_IMPL_H_ diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.cc b/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.cc index 0531126..f5c8b82 100644 --- a/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ b/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.cc @@ -99,6 +99,27 @@ void DataPipeProducerDispatcher::RemoveAwakableImplNoLock( data_pipe_->ProducerRemoveAwakable(awakable, signals_state); } +void DataPipeProducerDispatcher::StartSerializeImplNoLock( + Channel* channel, + size_t* max_size, + size_t* max_platform_handles) { + DCHECK(HasOneRef()); // Only one ref => no need to take the lock. + data_pipe_->ProducerStartSerialize(channel, max_size, max_platform_handles); +} + +bool DataPipeProducerDispatcher::EndSerializeAndCloseImplNoLock( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) { + DCHECK(HasOneRef()); // Only one ref => no need to take the lock. + + bool rv = data_pipe_->ProducerEndSerialize(channel, destination, actual_size, + platform_handles); + data_pipe_ = nullptr; + return rv; +} + bool DataPipeProducerDispatcher::IsBusyNoLock() const { lock().AssertAcquired(); return data_pipe_->ProducerIsBusy(); diff --git a/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.h b/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.h index 39c070c..e3a00d2 100644 --- a/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.h +++ b/third_party/mojo/src/mojo/edk/system/data_pipe_producer_dispatcher.h @@ -50,6 +50,14 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher : public Dispatcher { HandleSignalsState* signals_state) override; void RemoveAwakableImplNoLock(Awakable* awakable, HandleSignalsState* signals_state) override; + void StartSerializeImplNoLock(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) override; + bool EndSerializeAndCloseImplNoLock( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) override; bool IsBusyNoLock() const override; // Protected by |lock()|: diff --git a/third_party/mojo/src/mojo/edk/system/dispatcher.h b/third_party/mojo/src/mojo/edk/system/dispatcher.h index c069269..4bbc5ed 100644 --- a/third_party/mojo/src/mojo/edk/system/dispatcher.h +++ b/third_party/mojo/src/mojo/edk/system/dispatcher.h @@ -297,6 +297,11 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher size_t* actual_size, embedder::PlatformHandleVector* platform_handles); + // This should be overridden to return true if/when there's an ongoing + // operation (e.g., two-phase read/writes on data pipes) that should prevent a + // handle from being sent over a message pipe (with status "busy"). + virtual bool IsBusyNoLock() const; + // Available to subclasses. (Note: Returns a non-const reference, just like // |base::AutoLock|'s constructor takes a non-const reference.) base::Lock& lock() const { return lock_; } @@ -304,11 +309,6 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher private: friend class DispatcherTransport; - // This should be overridden to return true if/when there's an ongoing - // operation (e.g., two-phase read/writes on data pipes) that should prevent a - // handle from being sent over a message pipe (with status "busy"). - virtual bool IsBusyNoLock() const; - // Closes the dispatcher. This must be done under lock, and unlike |Close()|, // the dispatcher must not be closed already. (This is the "equivalent" of // |CreateEquivalentDispatcherAndCloseNoLock()|, for situations where the diff --git a/third_party/mojo/src/mojo/edk/system/local_data_pipe.h b/third_party/mojo/src/mojo/edk/system/local_data_pipe.h deleted file mode 100644 index abe3a46..0000000 --- a/third_party/mojo/src/mojo/edk/system/local_data_pipe.h +++ /dev/null @@ -1,89 +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_EDK_SYSTEM_LOCAL_DATA_PIPE_H_ -#define MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_H_ - -#include "base/macros.h" -#include "base/memory/aligned_memory.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/data_pipe.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -// |LocalDataPipe| is a subclass that "implements" |DataPipe| for data pipes -// whose producer and consumer are both local. This class is thread-safe (with -// protection provided by |DataPipe|'s |lock_|. -class MOJO_SYSTEM_IMPL_EXPORT LocalDataPipe : public DataPipe { - public: - // |validated_options| should be the output of |DataPipe::ValidateOptions()|. - // In particular: |struct_size| is ignored (so |validated_options| must be the - // current version of the struct) and |capacity_num_bytes| must be nonzero. - explicit LocalDataPipe(const MojoCreateDataPipeOptions& validated_options); - - private: - friend class base::RefCountedThreadSafe<LocalDataPipe>; - ~LocalDataPipe() override; - - // |DataPipe| implementation: - void ProducerCloseImplNoLock() override; - MojoResult ProducerWriteDataImplNoLock( - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_write, - uint32_t min_num_bytes_to_write) override; - MojoResult ProducerBeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_write) override; - MojoResult ProducerEndWriteDataImplNoLock( - uint32_t num_bytes_written) override; - HandleSignalsState ProducerGetHandleSignalsStateImplNoLock() const override; - void ConsumerCloseImplNoLock() override; - MojoResult ConsumerReadDataImplNoLock(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_read, - uint32_t min_num_bytes_to_read, - bool peek) override; - MojoResult ConsumerDiscardDataImplNoLock( - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_discard, - uint32_t min_num_bytes_to_discard) override; - MojoResult ConsumerQueryDataImplNoLock( - UserPointer<uint32_t> num_bytes) override; - MojoResult ConsumerBeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_read) override; - MojoResult ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read) override; - HandleSignalsState ConsumerGetHandleSignalsStateImplNoLock() const override; - - void EnsureBufferNoLock(); - void DestroyBufferNoLock(); - - // Get the maximum (single) write/read size right now (in number of elements); - // result fits in a |uint32_t|. - size_t GetMaxNumBytesToWriteNoLock(); - size_t GetMaxNumBytesToReadNoLock(); - - // Marks the given number of bytes as consumed/discarded. |num_bytes| must be - // greater than |current_num_bytes_|. - void MarkDataAsConsumedNoLock(size_t num_bytes); - - // The members below are protected by |DataPipe|'s |lock_|: - scoped_ptr<char, base::AlignedFreeDeleter> buffer_; - // Circular buffer. - size_t start_index_; - size_t current_num_bytes_; - - DISALLOW_COPY_AND_ASSIGN(LocalDataPipe); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_H_ diff --git a/third_party/mojo/src/mojo/edk/system/local_data_pipe.cc b/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl.cc index 8b9f673..3ca76d3 100644 --- a/third_party/mojo/src/mojo/edk/system/local_data_pipe.cc +++ b/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl.cc @@ -8,7 +8,7 @@ // saved by the limit on capacity -- the maximum size of the buffer, checked in // |DataPipe::ValidateOptions()|, is currently sufficiently small.) -#include "mojo/edk/system/local_data_pipe.h" +#include "mojo/edk/system/local_data_pipe_impl.h" #include <string.h> @@ -16,33 +16,34 @@ #include "base/logging.h" #include "mojo/edk/system/configuration.h" +#include "mojo/edk/system/data_pipe.h" namespace mojo { namespace system { -LocalDataPipe::LocalDataPipe(const MojoCreateDataPipeOptions& options) - : DataPipe(true, true, options), start_index_(0), current_num_bytes_(0) { +LocalDataPipeImpl::LocalDataPipeImpl() + : start_index_(0), current_num_bytes_(0) { // Note: |buffer_| is lazily allocated, since a common case will be that one // of the handles is immediately passed off to another process. } -LocalDataPipe::~LocalDataPipe() { +LocalDataPipeImpl::~LocalDataPipeImpl() { } -void LocalDataPipe::ProducerCloseImplNoLock() { +void LocalDataPipeImpl::ProducerClose() { // If the consumer is still open and we still have data, we have to keep the // buffer around. Currently, we won't free it even if it empties later. (We // could do this -- requiring a check on every read -- but that seems to be // optimizing for the uncommon case.) - if (!consumer_open_no_lock() || !current_num_bytes_) { + if (!consumer_open() || !current_num_bytes_) { // Note: There can only be a two-phase *read* (by the consumer) if we still // have data. - DCHECK(!consumer_in_two_phase_read_no_lock()); - DestroyBufferNoLock(); + DCHECK(!consumer_in_two_phase_read()); + DestroyBuffer(); } } -MojoResult LocalDataPipe::ProducerWriteDataImplNoLock( +MojoResult LocalDataPipeImpl::ProducerWriteData( UserPointer<const void> elements, UserPointer<uint32_t> num_bytes, uint32_t max_num_bytes_to_write, @@ -50,7 +51,7 @@ MojoResult LocalDataPipe::ProducerWriteDataImplNoLock( DCHECK_EQ(max_num_bytes_to_write % element_num_bytes(), 0u); DCHECK_EQ(min_num_bytes_to_write % element_num_bytes(), 0u); DCHECK_GT(max_num_bytes_to_write, 0u); - DCHECK(consumer_open_no_lock()); + DCHECK(consumer_open()); size_t num_bytes_to_write = 0; if (may_discard()) { @@ -61,8 +62,8 @@ MojoResult LocalDataPipe::ProducerWriteDataImplNoLock( capacity_num_bytes()); if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) { // Discard as much as needed (discard oldest first). - MarkDataAsConsumedNoLock(num_bytes_to_write - - (capacity_num_bytes() - current_num_bytes_)); + MarkDataAsConsumed(num_bytes_to_write - + (capacity_num_bytes() - current_num_bytes_)); // No need to wake up write waiters, since we're definitely going to leave // the buffer full. } @@ -81,11 +82,11 @@ MojoResult LocalDataPipe::ProducerWriteDataImplNoLock( // The amount we can write in our first |memcpy()|. size_t num_bytes_to_write_first = - std::min(num_bytes_to_write, GetMaxNumBytesToWriteNoLock()); + std::min(num_bytes_to_write, GetMaxNumBytesToWrite()); // Do the first (and possibly only) |memcpy()|. size_t first_write_index = (start_index_ + current_num_bytes_) % capacity_num_bytes(); - EnsureBufferNoLock(); + EnsureBuffer(); elements.GetArray(buffer_.get() + first_write_index, num_bytes_to_write_first); @@ -101,17 +102,17 @@ MojoResult LocalDataPipe::ProducerWriteDataImplNoLock( return MOJO_RESULT_OK; } -MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock( +MojoResult LocalDataPipeImpl::ProducerBeginWriteData( UserPointer<void*> buffer, UserPointer<uint32_t> buffer_num_bytes, uint32_t min_num_bytes_to_write) { - DCHECK(consumer_open_no_lock()); + DCHECK(consumer_open()); // The index we need to start writing at. size_t write_index = (start_index_ + current_num_bytes_) % capacity_num_bytes(); - size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock(); + size_t max_num_bytes_to_write = GetMaxNumBytesToWrite(); if (min_num_bytes_to_write > max_num_bytes_to_write) { // In "may discard" mode, we can always write from the write index to the // end of the buffer. @@ -121,7 +122,7 @@ MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock( // We should only reach here if the start index is after the write index! DCHECK_GE(start_index_, write_index); DCHECK_GT(min_num_bytes_to_write - max_num_bytes_to_write, 0u); - MarkDataAsConsumedNoLock(min_num_bytes_to_write - max_num_bytes_to_write); + MarkDataAsConsumed(min_num_bytes_to_write - max_num_bytes_to_write); max_num_bytes_to_write = min_num_bytes_to_write; } else { // Don't return "should wait" since you can't wait for a specified amount @@ -134,30 +135,27 @@ MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock( if (max_num_bytes_to_write == 0) return MOJO_RESULT_SHOULD_WAIT; - EnsureBufferNoLock(); + EnsureBuffer(); buffer.Put(buffer_.get() + write_index); buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_write)); - set_producer_two_phase_max_num_bytes_written_no_lock( + set_producer_two_phase_max_num_bytes_written( static_cast<uint32_t>(max_num_bytes_to_write)); return MOJO_RESULT_OK; } -MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock( - uint32_t num_bytes_written) { - DCHECK_LE(num_bytes_written, - producer_two_phase_max_num_bytes_written_no_lock()); +MojoResult LocalDataPipeImpl::ProducerEndWriteData(uint32_t num_bytes_written) { + DCHECK_LE(num_bytes_written, producer_two_phase_max_num_bytes_written()); current_num_bytes_ += num_bytes_written; DCHECK_LE(current_num_bytes_, capacity_num_bytes()); - set_producer_two_phase_max_num_bytes_written_no_lock(0); + set_producer_two_phase_max_num_bytes_written(0); return MOJO_RESULT_OK; } -HandleSignalsState LocalDataPipe::ProducerGetHandleSignalsStateImplNoLock() - const { +HandleSignalsState LocalDataPipeImpl::ProducerGetHandleSignalsState() const { HandleSignalsState rv; - if (consumer_open_no_lock()) { + if (consumer_open()) { if ((may_discard() || current_num_bytes_ < capacity_num_bytes()) && - !producer_in_two_phase_write_no_lock()) + !producer_in_two_phase_write()) rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; } else { @@ -167,21 +165,38 @@ HandleSignalsState LocalDataPipe::ProducerGetHandleSignalsStateImplNoLock() return rv; } -void LocalDataPipe::ConsumerCloseImplNoLock() { +void LocalDataPipeImpl::ProducerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) { + // TODO(vtl): Support serializing producer data pipe handles. + *max_size = 0; + *max_platform_handles = 0; +} + +bool LocalDataPipeImpl::ProducerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) { + // TODO(vtl): Support serializing producer data pipe handles. + owner()->ProducerCloseNoLock(); + return false; +} + +void LocalDataPipeImpl::ConsumerClose() { // If the producer is around and in a two-phase write, we have to keep the // buffer around. (We then don't free it until the producer is closed. This // could be rectified, but again seems like optimizing for the uncommon case.) - if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock()) - DestroyBufferNoLock(); + if (!producer_open() || !producer_in_two_phase_write()) + DestroyBuffer(); current_num_bytes_ = 0; } -MojoResult LocalDataPipe::ConsumerReadDataImplNoLock( - UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_read, - uint32_t min_num_bytes_to_read, - bool peek) { +MojoResult LocalDataPipeImpl::ConsumerReadData(UserPointer<void> elements, + UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_read, + uint32_t min_num_bytes_to_read, + bool peek) { DCHECK_EQ(max_num_bytes_to_read % element_num_bytes(), 0u); DCHECK_EQ(min_num_bytes_to_read % element_num_bytes(), 0u); DCHECK_GT(max_num_bytes_to_read, 0u); @@ -189,20 +204,20 @@ MojoResult LocalDataPipe::ConsumerReadDataImplNoLock( if (min_num_bytes_to_read > current_num_bytes_) { // Don't return "should wait" since you can't wait for a specified amount of // data. - return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; + return producer_open() ? MOJO_RESULT_OUT_OF_RANGE + : MOJO_RESULT_FAILED_PRECONDITION; } size_t num_bytes_to_read = std::min(static_cast<size_t>(max_num_bytes_to_read), current_num_bytes_); if (num_bytes_to_read == 0) { - return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; + return producer_open() ? MOJO_RESULT_SHOULD_WAIT + : MOJO_RESULT_FAILED_PRECONDITION; } // The amount we can read in our first |memcpy()|. size_t num_bytes_to_read_first = - std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock()); + std::min(num_bytes_to_read, GetMaxNumBytesToRead()); elements.PutArray(buffer_.get() + start_index_, num_bytes_to_read_first); if (num_bytes_to_read_first < num_bytes_to_read) { @@ -212,12 +227,12 @@ MojoResult LocalDataPipe::ConsumerReadDataImplNoLock( } if (!peek) - MarkDataAsConsumedNoLock(num_bytes_to_read); + MarkDataAsConsumed(num_bytes_to_read); num_bytes.Put(static_cast<uint32_t>(num_bytes_to_read)); return MOJO_RESULT_OK; } -MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock( +MojoResult LocalDataPipeImpl::ConsumerDiscardData( UserPointer<uint32_t> num_bytes, uint32_t max_num_bytes_to_discard, uint32_t min_num_bytes_to_discard) { @@ -228,82 +243,98 @@ MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock( if (min_num_bytes_to_discard > current_num_bytes_) { // Don't return "should wait" since you can't wait for a specified amount of // data. - return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; + return producer_open() ? MOJO_RESULT_OUT_OF_RANGE + : MOJO_RESULT_FAILED_PRECONDITION; } // Be consistent with other operations; error if no data available. if (current_num_bytes_ == 0) { - return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; + return producer_open() ? MOJO_RESULT_SHOULD_WAIT + : MOJO_RESULT_FAILED_PRECONDITION; } size_t num_bytes_to_discard = std::min( static_cast<size_t>(max_num_bytes_to_discard), current_num_bytes_); - MarkDataAsConsumedNoLock(num_bytes_to_discard); + MarkDataAsConsumed(num_bytes_to_discard); num_bytes.Put(static_cast<uint32_t>(num_bytes_to_discard)); return MOJO_RESULT_OK; } -MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock( +MojoResult LocalDataPipeImpl::ConsumerQueryData( UserPointer<uint32_t> num_bytes) { // Note: This cast is safe, since the capacity fits into a |uint32_t|. num_bytes.Put(static_cast<uint32_t>(current_num_bytes_)); return MOJO_RESULT_OK; } -MojoResult LocalDataPipe::ConsumerBeginReadDataImplNoLock( +MojoResult LocalDataPipeImpl::ConsumerBeginReadData( UserPointer<const void*> buffer, UserPointer<uint32_t> buffer_num_bytes, uint32_t min_num_bytes_to_read) { - size_t max_num_bytes_to_read = GetMaxNumBytesToReadNoLock(); + size_t max_num_bytes_to_read = GetMaxNumBytesToRead(); if (min_num_bytes_to_read > max_num_bytes_to_read) { // Don't return "should wait" since you can't wait for a specified amount of // data. - return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; + return producer_open() ? MOJO_RESULT_OUT_OF_RANGE + : MOJO_RESULT_FAILED_PRECONDITION; } // Don't go into a two-phase read if there's no data. if (max_num_bytes_to_read == 0) { - return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; + return producer_open() ? MOJO_RESULT_SHOULD_WAIT + : MOJO_RESULT_FAILED_PRECONDITION; } buffer.Put(buffer_.get() + start_index_); buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_read)); - set_consumer_two_phase_max_num_bytes_read_no_lock( + set_consumer_two_phase_max_num_bytes_read( static_cast<uint32_t>(max_num_bytes_to_read)); return MOJO_RESULT_OK; } -MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock( - uint32_t num_bytes_read) { - DCHECK_LE(num_bytes_read, consumer_two_phase_max_num_bytes_read_no_lock()); +MojoResult LocalDataPipeImpl::ConsumerEndReadData(uint32_t num_bytes_read) { + DCHECK_LE(num_bytes_read, consumer_two_phase_max_num_bytes_read()); DCHECK_LE(start_index_ + num_bytes_read, capacity_num_bytes()); - MarkDataAsConsumedNoLock(num_bytes_read); - set_consumer_two_phase_max_num_bytes_read_no_lock(0); + MarkDataAsConsumed(num_bytes_read); + set_consumer_two_phase_max_num_bytes_read(0); return MOJO_RESULT_OK; } -HandleSignalsState LocalDataPipe::ConsumerGetHandleSignalsStateImplNoLock() - const { +HandleSignalsState LocalDataPipeImpl::ConsumerGetHandleSignalsState() const { HandleSignalsState rv; if (current_num_bytes_ > 0) { - if (!consumer_in_two_phase_read_no_lock()) + if (!consumer_in_two_phase_read()) rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE; rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } else if (producer_open_no_lock()) { + } else if (producer_open()) { rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; } - if (!producer_open_no_lock()) + if (!producer_open()) rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; return rv; } -void LocalDataPipe::EnsureBufferNoLock() { - DCHECK(producer_open_no_lock()); +void LocalDataPipeImpl::ConsumerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) { + // TODO(vtl): Support serializing consumer data pipe handles. + *max_size = 0; + *max_platform_handles = 0; +} + +bool LocalDataPipeImpl::ConsumerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) { + // TODO(vtl): Support serializing consumer data pipe handles. + owner()->ConsumerCloseNoLock(); + return false; +} + +void LocalDataPipeImpl::EnsureBuffer() { + DCHECK(producer_open()); if (buffer_) return; buffer_.reset(static_cast<char*>( @@ -311,7 +342,7 @@ void LocalDataPipe::EnsureBufferNoLock() { GetConfiguration().data_pipe_buffer_alignment_bytes))); } -void LocalDataPipe::DestroyBufferNoLock() { +void LocalDataPipeImpl::DestroyBuffer() { #ifndef NDEBUG // Scribble on the buffer to help detect use-after-frees. (This also helps the // unit test detect certain bugs without needing ASAN or similar.) @@ -321,7 +352,7 @@ void LocalDataPipe::DestroyBufferNoLock() { buffer_.reset(); } -size_t LocalDataPipe::GetMaxNumBytesToWriteNoLock() { +size_t LocalDataPipeImpl::GetMaxNumBytesToWrite() { size_t next_index = start_index_ + current_num_bytes_; if (next_index >= capacity_num_bytes()) { next_index %= capacity_num_bytes(); @@ -333,13 +364,13 @@ size_t LocalDataPipe::GetMaxNumBytesToWriteNoLock() { return capacity_num_bytes() - next_index; } -size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() { +size_t LocalDataPipeImpl::GetMaxNumBytesToRead() { if (start_index_ + current_num_bytes_ > capacity_num_bytes()) return capacity_num_bytes() - start_index_; return current_num_bytes_; } -void LocalDataPipe::MarkDataAsConsumedNoLock(size_t num_bytes) { +void LocalDataPipeImpl::MarkDataAsConsumed(size_t num_bytes) { DCHECK_LE(num_bytes, current_num_bytes_); start_index_ += num_bytes; start_index_ %= capacity_num_bytes(); diff --git a/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl.h b/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl.h new file mode 100644 index 0000000..14defcb --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl.h @@ -0,0 +1,94 @@ +// 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_EDK_SYSTEM_LOCAL_DATA_PIPE_IMPL_H_ +#define MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_IMPL_H_ + +#include "base/macros.h" +#include "base/memory/aligned_memory.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/edk/system/data_pipe_impl.h" +#include "mojo/edk/system/system_impl_export.h" + +namespace mojo { +namespace system { + +class DataPipe; + +// |LocalDataPipeImpl| is a subclass that "implements" |DataPipe| for data pipes +// whose producer and consumer are both local. See |DataPipeImpl| for more +// details. +class MOJO_SYSTEM_IMPL_EXPORT LocalDataPipeImpl : public DataPipeImpl { + public: + LocalDataPipeImpl(); + ~LocalDataPipeImpl() override; + + private: + // |DataPipeImpl| implementation: + void ProducerClose() override; + MojoResult ProducerWriteData(UserPointer<const void> elements, + UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_write, + uint32_t min_num_bytes_to_write) override; + MojoResult ProducerBeginWriteData(UserPointer<void*> buffer, + UserPointer<uint32_t> buffer_num_bytes, + uint32_t min_num_bytes_to_write) override; + MojoResult ProducerEndWriteData(uint32_t num_bytes_written) override; + HandleSignalsState ProducerGetHandleSignalsState() const override; + void ProducerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) override; + bool ProducerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) override; + void ConsumerClose() override; + MojoResult ConsumerReadData(UserPointer<void> elements, + UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_read, + uint32_t min_num_bytes_to_read, + bool peek) override; + MojoResult ConsumerDiscardData(UserPointer<uint32_t> num_bytes, + uint32_t max_num_bytes_to_discard, + uint32_t min_num_bytes_to_discard) override; + MojoResult ConsumerQueryData(UserPointer<uint32_t> num_bytes) override; + MojoResult ConsumerBeginReadData(UserPointer<const void*> buffer, + UserPointer<uint32_t> buffer_num_bytes, + uint32_t min_num_bytes_to_read) override; + MojoResult ConsumerEndReadData(uint32_t num_bytes_read) override; + HandleSignalsState ConsumerGetHandleSignalsState() const override; + void ConsumerStartSerialize(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) override; + bool ConsumerEndSerialize( + Channel* channel, + void* destination, + size_t* actual_size, + embedder::PlatformHandleVector* platform_handles) override; + + void EnsureBuffer(); + void DestroyBuffer(); + + // Get the maximum (single) write/read size right now (in number of elements); + // result fits in a |uint32_t|. + size_t GetMaxNumBytesToWrite(); + size_t GetMaxNumBytesToRead(); + + // Marks the given number of bytes as consumed/discarded. |num_bytes| must be + // greater than |current_num_bytes_|. + void MarkDataAsConsumed(size_t num_bytes); + + scoped_ptr<char, base::AlignedFreeDeleter> buffer_; + // Circular buffer. + size_t start_index_; + size_t current_num_bytes_; + + DISALLOW_COPY_AND_ASSIGN(LocalDataPipeImpl); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_IMPL_H_ diff --git a/third_party/mojo/src/mojo/edk/system/local_data_pipe_unittest.cc b/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl_unittest.cc index 1223a2ba..3b42e57 100644 --- a/third_party/mojo/src/mojo/edk/system/local_data_pipe_unittest.cc +++ b/third_party/mojo/src/mojo/edk/system/local_data_pipe_impl_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/edk/system/local_data_pipe.h" +#include "mojo/edk/system/local_data_pipe_impl.h" #include <string.h> @@ -20,14 +20,14 @@ const uint32_t kSizeOfOptions = static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)); // Validate options. -TEST(LocalDataPipeTest, Creation) { +TEST(LocalDataPipeImplTest, Creation) { // Create using default options. { // Get default options. MojoCreateDataPipeOptions default_options = {0}; EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( NullUserPointer(), &default_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(default_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(default_options)); dp->ProducerClose(); dp->ConsumerClose(); } @@ -44,7 +44,7 @@ TEST(LocalDataPipeTest, Creation) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); dp->ProducerClose(); dp->ConsumerClose(); } @@ -59,7 +59,7 @@ TEST(LocalDataPipeTest, Creation) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); dp->ProducerClose(); dp->ConsumerClose(); } @@ -74,7 +74,7 @@ TEST(LocalDataPipeTest, Creation) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); dp->ProducerClose(); dp->ConsumerClose(); } @@ -90,13 +90,13 @@ TEST(LocalDataPipeTest, Creation) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); dp->ProducerClose(); dp->ConsumerClose(); } } -TEST(LocalDataPipeTest, SimpleReadWrite) { +TEST(LocalDataPipeImplTest, SimpleReadWrite) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -107,7 +107,7 @@ TEST(LocalDataPipeTest, SimpleReadWrite) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); int32_t elements[10] = {0}; uint32_t num_bytes = 0; @@ -213,9 +213,9 @@ TEST(LocalDataPipeTest, SimpleReadWrite) { // Note: The "basic" waiting tests test that the "wait states" are correct in // various situations; they don't test that waiters are properly awoken on state // changes. (For that, we need to use multiple threads.) -TEST(LocalDataPipeTest, BasicProducerWaiting) { - // Note: We take advantage of the fact that for |LocalDataPipe|, capacities - // are strict maximums. This is not guaranteed by the API. +TEST(LocalDataPipeImplTest, BasicProducerWaiting) { + // Note: We take advantage of the fact that for |LocalDataPipeImpl|, + // capacities are strict maximums. This is not guaranteed by the API. const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. @@ -227,7 +227,7 @@ TEST(LocalDataPipeTest, BasicProducerWaiting) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); Waiter waiter; uint32_t context = 0; HandleSignalsState hss; @@ -392,7 +392,7 @@ TEST(LocalDataPipeTest, BasicProducerWaiting) { dp->ProducerClose(); } -TEST(LocalDataPipeTest, PeerClosedWaiting) { +TEST(LocalDataPipeImplTest, PeerClosedWaiting) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -408,7 +408,7 @@ TEST(LocalDataPipeTest, PeerClosedWaiting) { // Check MOJO_HANDLE_SIGNAL_PEER_CLOSED on producer. { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Add a waiter. waiter.Init(); ASSERT_EQ(MOJO_RESULT_OK, @@ -432,7 +432,7 @@ TEST(LocalDataPipeTest, PeerClosedWaiting) { // Check MOJO_HANDLE_SIGNAL_PEER_CLOSED on consumer. { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Add a waiter. waiter.Init(); ASSERT_EQ(MOJO_RESULT_OK, @@ -455,7 +455,7 @@ TEST(LocalDataPipeTest, PeerClosedWaiting) { } } -TEST(LocalDataPipeTest, BasicConsumerWaiting) { +TEST(LocalDataPipeImplTest, BasicConsumerWaiting) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -467,7 +467,7 @@ TEST(LocalDataPipeTest, BasicConsumerWaiting) { MakeUserPointer(&options), &validated_options)); { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); Waiter waiter; uint32_t context = 0; HandleSignalsState hss; @@ -622,7 +622,7 @@ TEST(LocalDataPipeTest, BasicConsumerWaiting) { // Test with two-phase APIs and closing the producer with an active consumer // waiter. { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); Waiter waiter; uint32_t context = 0; HandleSignalsState hss; @@ -713,7 +713,7 @@ TEST(LocalDataPipeTest, BasicConsumerWaiting) { } // Tests that data pipes aren't writable/readable during two-phase writes/reads. -TEST(LocalDataPipeTest, BasicTwoPhaseWaiting) { +TEST(LocalDataPipeImplTest, BasicTwoPhaseWaiting) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -724,7 +724,7 @@ TEST(LocalDataPipeTest, BasicTwoPhaseWaiting) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); Waiter waiter; HandleSignalsState hss; @@ -866,7 +866,7 @@ TEST(LocalDataPipeTest, BasicTwoPhaseWaiting) { } // Test that a "may discard" data pipe is writable even when it's full. -TEST(LocalDataPipeTest, BasicMayDiscardWaiting) { +TEST(LocalDataPipeImplTest, BasicMayDiscardWaiting) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. @@ -877,7 +877,7 @@ TEST(LocalDataPipeTest, BasicMayDiscardWaiting) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); Waiter waiter; HandleSignalsState hss; @@ -998,7 +998,7 @@ void Seq(int32_t start, size_t count, int32_t* out) { out[i] = start + static_cast<int32_t>(i); } -TEST(LocalDataPipeTest, MayDiscard) { +TEST(LocalDataPipeImplTest, MayDiscard) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. @@ -1009,7 +1009,7 @@ TEST(LocalDataPipeTest, MayDiscard) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); int32_t buffer[100] = {0}; uint32_t num_bytes = 0; @@ -1191,7 +1191,7 @@ TEST(LocalDataPipeTest, MayDiscard) { dp->ConsumerClose(); } -TEST(LocalDataPipeTest, AllOrNone) { +TEST(LocalDataPipeImplTest, AllOrNone) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -1202,7 +1202,7 @@ TEST(LocalDataPipeTest, AllOrNone) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Try writing way too much. uint32_t num_bytes = 20u * sizeof(int32_t); @@ -1351,7 +1351,7 @@ TEST(LocalDataPipeTest, AllOrNone) { dp->ConsumerClose(); } -TEST(LocalDataPipeTest, AllOrNoneMayDiscard) { +TEST(LocalDataPipeImplTest, AllOrNoneMayDiscard) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. @@ -1362,7 +1362,7 @@ TEST(LocalDataPipeTest, AllOrNoneMayDiscard) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Try writing way too much. uint32_t num_bytes = 20u * sizeof(int32_t); @@ -1444,13 +1444,13 @@ TEST(LocalDataPipeTest, AllOrNoneMayDiscard) { EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); // Note: All-or-none two-phase writes on a "may discard" data pipe are tested - // in LocalDataPipeTest.MayDiscard. + // in LocalDataPipeImplTest.MayDiscard. dp->ProducerClose(); dp->ConsumerClose(); } -TEST(LocalDataPipeTest, TwoPhaseAllOrNone) { +TEST(LocalDataPipeImplTest, TwoPhaseAllOrNone) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -1461,7 +1461,7 @@ TEST(LocalDataPipeTest, TwoPhaseAllOrNone) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Try writing way too much (two-phase). uint32_t num_bytes = 20u * sizeof(int32_t); @@ -1585,7 +1585,7 @@ TEST(LocalDataPipeTest, TwoPhaseAllOrNone) { // respectively, as much as possible, even if it has to "wrap around" the // internal circular buffer. (Note that the two-phase write and read do not do // this.) -TEST(LocalDataPipeTest, WrapAround) { +TEST(LocalDataPipeImplTest, WrapAround) { unsigned char test_data[1000]; for (size_t i = 0; i < arraysize(test_data); i++) test_data[i] = static_cast<unsigned char>(i); @@ -1603,7 +1603,7 @@ TEST(LocalDataPipeTest, WrapAround) { // pipe more space. ASSERT_EQ(100u, validated_options.capacity_num_bytes); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Write 20 bytes. uint32_t num_bytes = 20u; @@ -1670,7 +1670,7 @@ TEST(LocalDataPipeTest, WrapAround) { // Tests the behavior of closing the producer or consumer with respect to // writes and reads (simple and two-phase). -TEST(LocalDataPipeTest, CloseWriteRead) { +TEST(LocalDataPipeImplTest, CloseWriteRead) { const char kTestData[] = "hello world"; const uint32_t kTestDataSize = static_cast<uint32_t>(sizeof(kTestData)); @@ -1686,7 +1686,7 @@ TEST(LocalDataPipeTest, CloseWriteRead) { // Close producer first, then consumer. { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Write some data, so we'll have something to read. uint32_t num_bytes = kTestDataSize; @@ -1742,7 +1742,7 @@ TEST(LocalDataPipeTest, CloseWriteRead) { // Close consumer first, then producer. { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Write some data, so we'll have something to read. uint32_t num_bytes = kTestDataSize; @@ -1798,7 +1798,7 @@ TEST(LocalDataPipeTest, CloseWriteRead) { // Test closing the consumer first, then the producer, with an active // two-phase write. { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Start two-phase write. void* write_buffer_ptr = nullptr; @@ -1815,7 +1815,7 @@ TEST(LocalDataPipeTest, CloseWriteRead) { // Test closing the producer and then trying to read (with no data). { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Write some data, so we'll have something to read. uint32_t num_bytes = kTestDataSize; @@ -1867,7 +1867,7 @@ TEST(LocalDataPipeTest, CloseWriteRead) { } } -TEST(LocalDataPipeTest, TwoPhaseMoreInvalidArguments) { +TEST(LocalDataPipeImplTest, TwoPhaseMoreInvalidArguments) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. @@ -1878,7 +1878,7 @@ TEST(LocalDataPipeTest, TwoPhaseMoreInvalidArguments) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // No data. uint32_t num_bytes = 1000u; @@ -1993,7 +1993,7 @@ TEST(LocalDataPipeTest, TwoPhaseMoreInvalidArguments) { // |ConsumerBeginReadData()| isn't discardable until |ConsumerEndReadData()|, // and thus we erroneously allow |ProducerWriteData()| to succeed. Second, the // |ProducerWriteData()| then changes the data underneath the two-phase read.) -TEST(LocalDataPipeTest, DISABLED_MayDiscardTwoPhaseConsistent) { +TEST(LocalDataPipeImplTest, DISABLED_MayDiscardTwoPhaseConsistent) { const MojoCreateDataPipeOptions options = { kSizeOfOptions, // |struct_size|. MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. @@ -2004,7 +2004,7 @@ TEST(LocalDataPipeTest, DISABLED_MayDiscardTwoPhaseConsistent) { EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( MakeUserPointer(&options), &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); + scoped_refptr<DataPipe> dp(DataPipe::CreateLocal(validated_options)); // Write some elements. char elements[2] = {'a', 'b'}; diff --git a/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc b/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc index b401bcd..e98908d 100644 --- a/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc +++ b/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc @@ -37,12 +37,12 @@ class MasterConnectionManager::Helper : public RawChannel::Delegate { public: Helper(MasterConnectionManager* owner, ProcessIdentifier process_identifier, - scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::SlaveInfo slave_info, embedder::ScopedPlatformHandle platform_handle); ~Helper() override; void Init(); - scoped_ptr<embedder::SlaveInfo> Shutdown(); + embedder::SlaveInfo Shutdown(); private: // |RawChannel::Delegate| methods: @@ -58,7 +58,7 @@ class MasterConnectionManager::Helper : public RawChannel::Delegate { MasterConnectionManager* const owner_; const ProcessIdentifier process_identifier_; - scoped_ptr<embedder::SlaveInfo> slave_info_; + embedder::SlaveInfo const slave_info_; scoped_ptr<RawChannel> raw_channel_; DISALLOW_COPY_AND_ASSIGN(Helper); @@ -67,26 +67,26 @@ class MasterConnectionManager::Helper : public RawChannel::Delegate { MasterConnectionManager::Helper::Helper( MasterConnectionManager* owner, ProcessIdentifier process_identifier, - scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::SlaveInfo slave_info, embedder::ScopedPlatformHandle platform_handle) : owner_(owner), process_identifier_(process_identifier), - slave_info_(slave_info.Pass()), + slave_info_(slave_info), raw_channel_(RawChannel::Create(platform_handle.Pass())) { } MasterConnectionManager::Helper::~Helper() { - DCHECK(!slave_info_); + DCHECK(!raw_channel_); } void MasterConnectionManager::Helper::Init() { raw_channel_->Init(this); } -scoped_ptr<embedder::SlaveInfo> MasterConnectionManager::Helper::Shutdown() { +embedder::SlaveInfo MasterConnectionManager::Helper::Shutdown() { raw_channel_->Shutdown(); raw_channel_.reset(); - return slave_info_.Pass(); + return slave_info_; } void MasterConnectionManager::Helper::OnReadMessage( @@ -243,30 +243,13 @@ void MasterConnectionManager::Init( DCHECK(!private_thread_.message_loop()); delegate_thread_task_runner_ = delegate_thread_task_runner; - AssertOnDelegateThread(); master_process_delegate_ = master_process_delegate; CHECK(private_thread_.StartWithOptions( base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); } -void MasterConnectionManager::Shutdown() { - AssertOnDelegateThread(); - DCHECK(master_process_delegate_); - DCHECK(private_thread_.message_loop()); - - // The |Stop()| will actually finish all posted tasks. - private_thread_.message_loop()->PostTask( - FROM_HERE, base::Bind(&MasterConnectionManager::ShutdownOnPrivateThread, - base::Unretained(this))); - private_thread_.Stop(); - DCHECK(helpers_.empty()); - DCHECK(pending_connections_.empty()); - master_process_delegate_ = nullptr; - delegate_thread_task_runner_ = nullptr; -} - void MasterConnectionManager::AddSlave( - scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::SlaveInfo slave_info, embedder::ScopedPlatformHandle platform_handle) { // We don't really care if |slave_info| is non-null or not. DCHECK(platform_handle.is_valid()); @@ -278,11 +261,27 @@ void MasterConnectionManager::AddSlave( private_thread_.message_loop()->PostTask( FROM_HERE, base::Bind(&MasterConnectionManager::AddSlaveOnPrivateThread, - base::Unretained(this), base::Passed(&slave_info), + base::Unretained(this), base::Unretained(slave_info), base::Passed(&platform_handle), base::Unretained(&event))); event.Wait(); } +void MasterConnectionManager::Shutdown() { + AssertNotOnPrivateThread(); + DCHECK(master_process_delegate_); + DCHECK(private_thread_.message_loop()); + + // The |Stop()| will actually finish all posted tasks. + private_thread_.message_loop()->PostTask( + FROM_HERE, base::Bind(&MasterConnectionManager::ShutdownOnPrivateThread, + base::Unretained(this))); + private_thread_.Stop(); + DCHECK(helpers_.empty()); + DCHECK(pending_connections_.empty()); + master_process_delegate_ = nullptr; + delegate_thread_task_runner_ = nullptr; +} + bool MasterConnectionManager::AllowConnect( const ConnectionIdentifier& connection_id) { AssertNotOnPrivateThread(); @@ -482,16 +481,16 @@ void MasterConnectionManager::ShutdownOnPrivateThread() { if (!helpers_.empty()) { DVLOG(1) << "Shutting down with slaves still connected"; for (auto& p : helpers_) { - scoped_ptr<embedder::SlaveInfo> slave_info = p.second->Shutdown(); + embedder::SlaveInfo slave_info = p.second->Shutdown(); delete p.second; - CallOnSlaveDisconnect(slave_info.Pass()); + CallOnSlaveDisconnect(slave_info); } helpers_.clear(); } } void MasterConnectionManager::AddSlaveOnPrivateThread( - scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::SlaveInfo slave_info, embedder::ScopedPlatformHandle platform_handle, base::WaitableEvent* event) { DCHECK(platform_handle.is_valid()); @@ -502,8 +501,8 @@ void MasterConnectionManager::AddSlaveOnPrivateThread( ProcessIdentifier process_identifier = next_process_identifier_; next_process_identifier_++; - scoped_ptr<Helper> helper(new Helper( - this, process_identifier, slave_info.Pass(), platform_handle.Pass())); + scoped_ptr<Helper> helper( + new Helper(this, process_identifier, slave_info, platform_handle.Pass())); helper->Init(); DCHECK(helpers_.find(process_identifier) == helpers_.end()); @@ -520,7 +519,7 @@ void MasterConnectionManager::OnError(ProcessIdentifier process_identifier) { auto it = helpers_.find(process_identifier); DCHECK(it != helpers_.end()); Helper* helper = it->second; - scoped_ptr<embedder::SlaveInfo> slave_info = helper->Shutdown(); + embedder::SlaveInfo slave_info = helper->Shutdown(); helpers_.erase(it); delete helper; @@ -542,23 +541,17 @@ void MasterConnectionManager::OnError(ProcessIdentifier process_identifier) { } } - CallOnSlaveDisconnect(slave_info.Pass()); + CallOnSlaveDisconnect(slave_info); } void MasterConnectionManager::CallOnSlaveDisconnect( - scoped_ptr<embedder::SlaveInfo> slave_info) { + embedder::SlaveInfo slave_info) { AssertOnPrivateThread(); DCHECK(master_process_delegate_); delegate_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&embedder::MasterProcessDelegate::OnSlaveDisconnect, base::Unretained(master_process_delegate_), - base::Passed(&slave_info))); -} - -void MasterConnectionManager::AssertOnDelegateThread() const { - DCHECK(base::MessageLoop::current()); - DCHECK_EQ(base::MessageLoop::current()->task_runner(), - delegate_thread_task_runner_); + base::Unretained(slave_info))); } void MasterConnectionManager::AssertNotOnPrivateThread() const { diff --git a/third_party/mojo/src/mojo/edk/system/master_connection_manager.h b/third_party/mojo/src/mojo/edk/system/master_connection_manager.h index 5bdb810..8367723 100644 --- a/third_party/mojo/src/mojo/edk/system/master_connection_manager.h +++ b/third_party/mojo/src/mojo/edk/system/master_connection_manager.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include "base/compiler_specific.h" #include "base/containers/hash_tables.h" #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -25,7 +26,7 @@ namespace mojo { namespace embedder { class MasterProcessDelegate; -class SlaveInfo; +typedef void* SlaveInfo; } namespace system { @@ -35,10 +36,10 @@ const ProcessIdentifier kMasterProcessIdentifier = 1; // The |ConnectionManager| implementation for the master process. // -// Objects of this class may be created and destroyed on any thread. However, -// |Init()| and |Shutdown()| must be called on the "delegate thread". Otherwise, -// its public methods are thread-safe (except that they may not be called from -// its internal, private thread). +// This class is thread-safe (except that no public methods may be called from +// its internal, private thread), with condition that |Init()| be called before +// anything else and |Shutdown()| be called before destruction (and no other +// public methods may be called during/after |Shutdown()|). class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager : public ConnectionManager { public: @@ -54,18 +55,17 @@ class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager void Init(scoped_refptr<base::TaskRunner> delegate_thread_task_runner, embedder::MasterProcessDelegate* master_process_delegate); - // No other methods may be called after this is (or while it is being) called. - void Shutdown(); - // Adds a slave process and sets up/tracks a connection to that slave (using - // |platform_handle|). (|slave_info| is used by the caller/implementation of - // |embedder::MasterProcessDelegate| to track this process; ownership of - // |slave_info| will be returned to the delegate via |OnSlaveDisconnect()|, - // which will always be called for each slave, assuming proper shutdown.) - void AddSlave(scoped_ptr<embedder::SlaveInfo> slave_info, + // |platform_handle|). |slave_info| is used by the caller/implementation of + // |embedder::MasterProcessDelegate| to track this process. It must remain + // alive until the delegate's |OnSlaveDisconnect()| is called with it as the + // argument. |OnSlaveDisconnect()| will always be called for each slave, + // assuming proper shutdown.) + void AddSlave(embedder::SlaveInfo slave_info, embedder::ScopedPlatformHandle platform_handle); // |ConnectionManager| methods: + void Shutdown() override; bool AllowConnect(const ConnectionIdentifier& connection_id) override; bool CancelConnect(const ConnectionIdentifier& connection_id) override; bool Connect(const ConnectionIdentifier& connection_id, @@ -89,18 +89,13 @@ class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager // These should only be called on |private_thread_|: void ShutdownOnPrivateThread(); // Signals |*event| on completion. - void AddSlaveOnPrivateThread(scoped_ptr<embedder::SlaveInfo> slave_info, + void AddSlaveOnPrivateThread(embedder::SlaveInfo slave_info, embedder::ScopedPlatformHandle platform_handle, base::WaitableEvent* event); // Called by |Helper::OnError()|. void OnError(ProcessIdentifier process_identifier); // Posts a call to |master_process_delegate_->OnSlaveDisconnect()|. - void CallOnSlaveDisconnect(scoped_ptr<embedder::SlaveInfo> slave_info); - - // Asserts that the current thread is the delegate thread. (This actually - // checks the current message loop.) - // TODO(vtl): Probably we should actually check the thread. - void AssertOnDelegateThread() const; + void CallOnSlaveDisconnect(embedder::SlaveInfo slave_info); // Asserts that the current thread is *not* |private_thread_| (no-op if // DCHECKs are not enabled). This should only be called while diff --git a/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc index de8d17f..5ff8e56 100644 --- a/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc +++ b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc @@ -48,7 +48,6 @@ void SlaveConnectionManager::Init( DCHECK(!private_thread_.message_loop()); delegate_thread_task_runner_ = delegate_thread_task_runner; - AssertOnDelegateThread(); slave_process_delegate_ = slave_process_delegate; CHECK(private_thread_.StartWithOptions( base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); @@ -60,7 +59,7 @@ void SlaveConnectionManager::Init( } void SlaveConnectionManager::Shutdown() { - AssertOnDelegateThread(); + AssertNotOnPrivateThread(); DCHECK(slave_process_delegate_); DCHECK(private_thread_.message_loop()); @@ -294,12 +293,6 @@ void SlaveConnectionManager::OnError(Error error) { base::Unretained(slave_process_delegate_))); } -void SlaveConnectionManager::AssertOnDelegateThread() const { - DCHECK(base::MessageLoop::current()); - DCHECK_EQ(base::MessageLoop::current()->task_runner(), - delegate_thread_task_runner_); -} - void SlaveConnectionManager::AssertNotOnPrivateThread() const { // This should only be called after |Init()| and before |Shutdown()|. (If not, // the subsequent |DCHECK_NE()| is invalid, since the current thread may not diff --git a/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h index 731d9db..c710854 100644 --- a/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h +++ b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h @@ -5,6 +5,7 @@ #ifndef MOJO_EDK_SYSTEM_SLAVE_CONNECTION_MANAGER_H_ #define MOJO_EDK_SYSTEM_SLAVE_CONNECTION_MANAGER_H_ +#include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -31,10 +32,10 @@ namespace system { // The |ConnectionManager| implementation for slave processes. // -// Objects of this class may be created and destroyed on any thread. However, -// |Init()| and |Shutdown()| must be called on the "delegate thread". Otherwise, -// its public methods are thread-safe (except that they may not be called from -// its internal, private thread). +// This class is thread-safe (except that no public methods may be called from +// its internal, private thread), with condition that |Init()| be called before +// anything else and |Shutdown()| be called before destruction (and no other +// public methods may be called during/after |Shutdown()|). class MOJO_SYSTEM_IMPL_EXPORT SlaveConnectionManager : public ConnectionManager, public RawChannel::Delegate { @@ -52,10 +53,8 @@ class MOJO_SYSTEM_IMPL_EXPORT SlaveConnectionManager embedder::SlaveProcessDelegate* slave_process_delegate, embedder::ScopedPlatformHandle platform_handle); - // No other methods may be called after this is (or while it is being) called. - void Shutdown(); - // |ConnectionManager| methods: + void Shutdown() override; bool AllowConnect(const ConnectionIdentifier& connection_id) override; bool CancelConnect(const ConnectionIdentifier& connection_id) override; bool Connect(const ConnectionIdentifier& connection_id, @@ -81,11 +80,6 @@ class MOJO_SYSTEM_IMPL_EXPORT SlaveConnectionManager embedder::ScopedPlatformHandleVectorPtr platform_handles) override; void OnError(Error error) override; - // Asserts that the current thread is the delegate thread. (This actually - // checks the current message loop.) - // TODO(vtl): Probably we should actually check the thread. - void AssertOnDelegateThread() const; - // Asserts that the current thread is *not* |private_thread_| (no-op if // DCHECKs are not enabled). This should only be called while // |private_thread_| is alive (i.e., after |Init()| but before |Shutdown()|). diff --git a/third_party/mojo/src/mojo/edk/system/unique_identifier.cc b/third_party/mojo/src/mojo/edk/system/unique_identifier.cc index e851e2b..11e482b 100644 --- a/third_party/mojo/src/mojo/edk/system/unique_identifier.cc +++ b/third_party/mojo/src/mojo/edk/system/unique_identifier.cc @@ -7,7 +7,7 @@ #include <ostream> #include "base/strings/string_number_conversions.h" -#include "crypto/random.h" +#include "mojo/edk/embedder/platform_support.h" namespace mojo { namespace system { @@ -19,9 +19,10 @@ std::ostream& operator<<(std::ostream& out, } // static -UniqueIdentifier UniqueIdentifier::Generate() { +UniqueIdentifier UniqueIdentifier::Generate( + embedder::PlatformSupport* platform_support) { UniqueIdentifier rv; - crypto::RandBytes(rv.data_, sizeof(rv.data_)); + platform_support->GetCryptoRandomBytes(rv.data_, sizeof(rv.data_)); return rv; } diff --git a/third_party/mojo/src/mojo/edk/system/unique_identifier.h b/third_party/mojo/src/mojo/edk/system/unique_identifier.h index a7f68ba..c9cba88 100644 --- a/third_party/mojo/src/mojo/edk/system/unique_identifier.h +++ b/third_party/mojo/src/mojo/edk/system/unique_identifier.h @@ -31,6 +31,13 @@ struct hash<mojo::system::UniqueIdentifier>; } // BASE_HASH_NAMESPACE namespace mojo { + +namespace embedder { + +class PlatformSupport; + +} // namespace embedder + namespace system { // Declare this before |UniqueIdentifier|, so that it can be friended. @@ -44,7 +51,7 @@ class MOJO_SYSTEM_IMPL_EXPORT UniqueIdentifier { public: // This generates a new identifier. Uniqueness is "guaranteed" (i.e., // probabilistically) for identifiers. - static UniqueIdentifier Generate(); + static UniqueIdentifier Generate(embedder::PlatformSupport* platform_support); bool operator==(const UniqueIdentifier& other) const { return memcmp(data_, other.data_, sizeof(data_)) == 0; @@ -58,7 +65,9 @@ class MOJO_SYSTEM_IMPL_EXPORT UniqueIdentifier { private: friend BASE_HASH_NAMESPACE::hash<mojo::system::UniqueIdentifier>; - friend std::ostream& operator<<(std::ostream&, const UniqueIdentifier&); + friend MOJO_SYSTEM_IMPL_EXPORT std::ostream& operator<<( + std::ostream&, + const UniqueIdentifier&); explicit UniqueIdentifier() {} diff --git a/third_party/mojo/src/mojo/edk/system/unique_identifier_unittest.cc b/third_party/mojo/src/mojo/edk/system/unique_identifier_unittest.cc index 2be4f6e..5241293 100644 --- a/third_party/mojo/src/mojo/edk/system/unique_identifier_unittest.cc +++ b/third_party/mojo/src/mojo/edk/system/unique_identifier_unittest.cc @@ -8,21 +8,36 @@ #include <sstream> #include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "mojo/edk/embedder/simple_platform_support.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { namespace system { namespace { -TEST(UniqueIdentifierTest, Basic) { +class UniqueIdentifierTest : public testing::Test { + public: + UniqueIdentifierTest() {} + ~UniqueIdentifierTest() override {} + + embedder::PlatformSupport* platform_support() { return &platform_support_; } + + private: + embedder::SimplePlatformSupport platform_support_; + + DISALLOW_COPY_AND_ASSIGN(UniqueIdentifierTest); +}; + +TEST_F(UniqueIdentifierTest, Basic) { // (This also checks copy constructibility.) - UniqueIdentifier id1 = UniqueIdentifier::Generate(); + UniqueIdentifier id1 = UniqueIdentifier::Generate(platform_support()); EXPECT_EQ(id1, id1); EXPECT_FALSE(id1 != id1); EXPECT_FALSE(id1 < id1); - UniqueIdentifier id2 = UniqueIdentifier::Generate(); + UniqueIdentifier id2 = UniqueIdentifier::Generate(platform_support()); EXPECT_FALSE(id2 == id1); EXPECT_NE(id2, id1); @@ -32,14 +47,14 @@ TEST(UniqueIdentifierTest, Basic) { id2 = id1; } -TEST(UniqueIdentifierTest, Logging) { +TEST_F(UniqueIdentifierTest, Logging) { std::ostringstream oss1; - UniqueIdentifier id1 = UniqueIdentifier::Generate(); + UniqueIdentifier id1 = UniqueIdentifier::Generate(platform_support()); oss1 << id1; EXPECT_FALSE(oss1.str().empty()); std::ostringstream oss2; - UniqueIdentifier id2 = UniqueIdentifier::Generate(); + UniqueIdentifier id2 = UniqueIdentifier::Generate(platform_support()); oss2 << id2; EXPECT_FALSE(oss2.str().empty()); @@ -47,17 +62,17 @@ TEST(UniqueIdentifierTest, Logging) { EXPECT_NE(oss1.str(), oss2.str()); } -TEST(UniqueIdentifierTest, StdSet) { +TEST_F(UniqueIdentifierTest, StdSet) { std::set<UniqueIdentifier> s; EXPECT_TRUE(s.empty()); - UniqueIdentifier id1 = UniqueIdentifier::Generate(); + UniqueIdentifier id1 = UniqueIdentifier::Generate(platform_support()); EXPECT_TRUE(s.find(id1) == s.end()); s.insert(id1); EXPECT_TRUE(s.find(id1) != s.end()); EXPECT_FALSE(s.empty()); - UniqueIdentifier id2 = UniqueIdentifier::Generate(); + UniqueIdentifier id2 = UniqueIdentifier::Generate(platform_support()); EXPECT_TRUE(s.find(id2) == s.end()); s.insert(id2); EXPECT_TRUE(s.find(id2) != s.end()); @@ -74,17 +89,17 @@ TEST(UniqueIdentifierTest, StdSet) { EXPECT_TRUE(s.empty()); } -TEST(UniqueIdentifierTest, HashSet) { +TEST_F(UniqueIdentifierTest, HashSet) { base::hash_set<UniqueIdentifier> s; EXPECT_TRUE(s.empty()); - UniqueIdentifier id1 = UniqueIdentifier::Generate(); + UniqueIdentifier id1 = UniqueIdentifier::Generate(platform_support()); EXPECT_TRUE(s.find(id1) == s.end()); s.insert(id1); EXPECT_TRUE(s.find(id1) != s.end()); EXPECT_FALSE(s.empty()); - UniqueIdentifier id2 = UniqueIdentifier::Generate(); + UniqueIdentifier id2 = UniqueIdentifier::Generate(platform_support()); EXPECT_TRUE(s.find(id2) == s.end()); s.insert(id2); EXPECT_TRUE(s.find(id2) != s.end()); diff --git a/third_party/mojo/src/mojo/edk/test/BUILD.gn b/third_party/mojo/src/mojo/edk/test/BUILD.gn index 3a15663..e8f55ad 100644 --- a/third_party/mojo/src/mojo/edk/test/BUILD.gn +++ b/third_party/mojo/src/mojo/edk/test/BUILD.gn @@ -10,6 +10,8 @@ mojo_edk_source_set("test_support") { sources = [ "multiprocess_test_helper.cc", "multiprocess_test_helper.h", + "scoped_ipc_support.cc", + "scoped_ipc_support.h", "test_utils.h", "test_utils_posix.cc", "test_utils_win.cc", diff --git a/third_party/mojo/src/mojo/edk/test/scoped_ipc_support.cc b/third_party/mojo/src/mojo/edk/test/scoped_ipc_support.cc new file mode 100644 index 0000000..9f3b97d --- /dev/null +++ b/third_party/mojo/src/mojo/edk/test/scoped_ipc_support.cc @@ -0,0 +1,38 @@ +// 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. + +#include "mojo/edk/test/scoped_ipc_support.h" + +#include "base/message_loop/message_loop.h" +#include "mojo/edk/embedder/embedder.h" + +namespace mojo { +namespace test { + +ScopedIPCSupport::ScopedIPCSupport( + scoped_refptr<base::TaskRunner> io_thread_task_runner) + : io_thread_task_runner_(io_thread_task_runner), + event_(true, false) { // Manual reset. + // Note: Run delegate methods on the I/O thread. + embedder::InitIPCSupport(embedder::ProcessType::NONE, io_thread_task_runner, + this, io_thread_task_runner, + embedder::ScopedPlatformHandle()); +} + +ScopedIPCSupport::~ScopedIPCSupport() { + if (base::MessageLoop::current() && + base::MessageLoop::current()->task_runner() == io_thread_task_runner_) { + embedder::ShutdownIPCSupportOnIOThread(); + } else { + embedder::ShutdownIPCSupport(); + event_.Wait(); + } +} + +void ScopedIPCSupport::OnShutdownComplete() { + event_.Signal(); +} + +} // namespace test +} // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/test/scoped_ipc_support.h b/third_party/mojo/src/mojo/edk/test/scoped_ipc_support.h new file mode 100644 index 0000000..2569c4e --- /dev/null +++ b/third_party/mojo/src/mojo/edk/test/scoped_ipc_support.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_ +#define MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/waitable_event.h" +#include "base/task_runner.h" +#include "mojo/edk/embedder/process_delegate.h" + +namespace mojo { +namespace test { + +// A simple class that calls |mojo::embedder::InitIPCSupport()| (with +// |ProcessType::NONE|) on construction and |ShutdownIPCSupport()| on +// destruction (or |ShutdownIPCSupportOnIOThread()| if destroyed on the I/O +// thread). +// TODO(vtl): Need master/slave versions. +class ScopedIPCSupport : public embedder::ProcessDelegate { + public: + ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner); + ~ScopedIPCSupport() override; + + private: + // ProcessDelegate| implementation: + // Note: Executed on the I/O thread. + void OnShutdownComplete() override; + + scoped_refptr<base::TaskRunner> io_thread_task_runner_; + + // Set after shut down. + base::WaitableEvent event_; + + DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport); +}; + +} // namespace test +} // namespace mojo + +#endif // MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_ diff --git a/third_party/mojo/src/mojo/public/.gitignore b/third_party/mojo/src/mojo/public/.gitignore new file mode 100644 index 0000000..8fb6a7d --- /dev/null +++ b/third_party/mojo/src/mojo/public/.gitignore @@ -0,0 +1,2 @@ +*.pyc +/tools/prebuilt/ diff --git a/third_party/mojo/src/mojo/public/LICENSE b/third_party/mojo/src/mojo/public/LICENSE new file mode 100644 index 0000000..972bb2e --- /dev/null +++ b/third_party/mojo/src/mojo/public/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/mojo/src/mojo/public/VERSION b/third_party/mojo/src/mojo/public/VERSION index c755f39..29c42bf 100644 --- a/third_party/mojo/src/mojo/public/VERSION +++ b/third_party/mojo/src/mojo/public/VERSION @@ -1 +1 @@ -1027d24a1f68c6d10b7539b32114f1272b2cc9f1
\ No newline at end of file +3d23dae011859a2aae49f1d1adde705c8e85d819
\ No newline at end of file diff --git a/third_party/mojo/src/mojo/public/c/system/tests/core_perftest.cc b/third_party/mojo/src/mojo/public/c/system/tests/core_perftest.cc index 23fd92b..3ee3015 100644 --- a/third_party/mojo/src/mojo/public/c/system/tests/core_perftest.cc +++ b/third_party/mojo/src/mojo/public/c/system/tests/core_perftest.cc @@ -7,7 +7,6 @@ #include "mojo/public/c/system/core.h" #include <assert.h> -#include <stddef.h> #include <stdint.h> #include <stdio.h> @@ -37,8 +36,8 @@ class MessagePipeWriterThread : public mojo::Thread { // TODO(vtl): Should I throttle somehow? for (;;) { - MojoResult result = MojoWriteMessage( - handle_, buffer, num_bytes_, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); + MojoResult result = MojoWriteMessage(handle_, buffer, num_bytes_, nullptr, + 0, MOJO_WRITE_MESSAGE_FLAG_NONE); if (result == MOJO_RESULT_OK) { num_writes_++; continue; @@ -74,8 +73,8 @@ class MessagePipeReaderThread : public mojo::Thread { for (;;) { uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); - MojoResult result = MojoReadMessage( - handle_, buffer, &num_bytes, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE); + MojoResult result = MojoReadMessage(handle_, buffer, &num_bytes, nullptr, + nullptr, MOJO_READ_MESSAGE_FLAG_NONE); if (result == MOJO_RESULT_OK) { num_reads_++; continue; @@ -111,14 +110,14 @@ class MessagePipeReaderThread : public mojo::Thread { class CorePerftest : public testing::Test { public: - CorePerftest() : buffer_(NULL), num_bytes_(0) {} + CorePerftest() : buffer_(nullptr), num_bytes_(0) {} ~CorePerftest() override {} static void NoOp(void* /*closure*/) {} static void MessagePipe_CreateAndClose(void* closure) { CorePerftest* self = static_cast<CorePerftest*>(closure); - MojoResult result = MojoCreateMessagePipe(NULL, &self->h0_, &self->h1_); + MojoResult result = MojoCreateMessagePipe(nullptr, &self->h0_, &self->h1_); MOJO_ALLOW_UNUSED_LOCAL(result); assert(result == MOJO_RESULT_OK); result = MojoClose(self->h0_); @@ -129,21 +128,22 @@ class CorePerftest : public testing::Test { static void MessagePipe_WriteAndRead(void* closure) { CorePerftest* self = static_cast<CorePerftest*>(closure); - MojoResult result = MojoWriteMessage(self->h0_, self->buffer_, - self->num_bytes_, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE); + MojoResult result = + MojoWriteMessage(self->h0_, self->buffer_, self->num_bytes_, nullptr, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE); MOJO_ALLOW_UNUSED_LOCAL(result); assert(result == MOJO_RESULT_OK); uint32_t read_bytes = self->num_bytes_; - result = MojoReadMessage(self->h1_, self->buffer_, &read_bytes, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE); + result = MojoReadMessage(self->h1_, self->buffer_, &read_bytes, nullptr, + nullptr, MOJO_READ_MESSAGE_FLAG_NONE); assert(result == MOJO_RESULT_OK); } static void MessagePipe_EmptyRead(void* closure) { CorePerftest* self = static_cast<CorePerftest*>(closure); - MojoResult result = MojoReadMessage(self->h0_, NULL, NULL, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); + MojoResult result = + MojoReadMessage(self->h0_, nullptr, nullptr, nullptr, nullptr, + MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); MOJO_ALLOW_UNUSED_LOCAL(result); assert(result == MOJO_RESULT_SHOULD_WAIT); } @@ -158,7 +158,7 @@ class CorePerftest : public testing::Test { assert(num_writers > 0); assert(num_readers > 0); - MojoResult result = MojoCreateMessagePipe(NULL, &h0_, &h1_); + MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); MOJO_ALLOW_UNUSED_LOCAL(result); assert(result == MOJO_RESULT_OK); @@ -239,7 +239,7 @@ class CorePerftest : public testing::Test { static_cast<time_t>(microseconds / 1000000), // Seconds. static_cast<long>(microseconds % 1000000) * 1000L // Nanoseconds. }; - int rv = nanosleep(&req, NULL); + int rv = nanosleep(&req, nullptr); MOJO_ALLOW_UNUSED_LOCAL(rv); assert(rv == 0); } @@ -261,7 +261,7 @@ TEST_F(CorePerftest, MessagePipe_CreateAndClose) { } TEST_F(CorePerftest, MessagePipe_WriteAndRead) { - MojoResult result = MojoCreateMessagePipe(NULL, &h0_, &h1_); + MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); MOJO_ALLOW_UNUSED_LOCAL(result); assert(result == MOJO_RESULT_OK); char buffer[10000] = {0}; @@ -289,7 +289,7 @@ TEST_F(CorePerftest, MessagePipe_WriteAndRead) { } TEST_F(CorePerftest, MessagePipe_EmptyRead) { - MojoResult result = MojoCreateMessagePipe(NULL, &h0_, &h1_); + MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); MOJO_ALLOW_UNUSED_LOCAL(result); assert(result == MOJO_RESULT_OK); mojo::test::IterateAndReportPerf("MessagePipe_EmptyRead", nullptr, diff --git a/third_party/mojo/src/mojo/public/c/system/tests/core_unittest.cc b/third_party/mojo/src/mojo/public/c/system/tests/core_unittest.cc index 71e61f4..de59a70 100644 --- a/third_party/mojo/src/mojo/public/c/system/tests/core_unittest.cc +++ b/third_party/mojo/src/mojo/public/c/system/tests/core_unittest.cc @@ -40,29 +40,30 @@ TEST(CoreTest, InvalidHandle) { EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID)); // Wait: - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000, NULL)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000, + nullptr)); h0 = MOJO_HANDLE_INVALID; sig = ~MOJO_HANDLE_SIGNAL_NONE; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE, NULL, NULL)); - - // Message pipe: EXPECT_EQ( MOJO_RESULT_INVALID_ARGUMENT, - MojoWriteMessage(h0, buffer, 3, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); + MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE, nullptr, nullptr)); + + // Message pipe: + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWriteMessage(h0, buffer, 3, nullptr, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); buffer_size = static_cast<uint32_t>(sizeof(buffer)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoReadMessage(h0, buffer, &buffer_size, NULL, NULL, + MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); // Data pipe: buffer_size = static_cast<uint32_t>(sizeof(buffer)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoWriteData(h0, buffer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE)); - write_pointer = NULL; + write_pointer = nullptr; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoBeginWriteData(h0, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE)); @@ -70,7 +71,7 @@ TEST(CoreTest, InvalidHandle) { buffer_size = static_cast<uint32_t>(sizeof(buffer)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoReadData(h0, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); - read_pointer = NULL; + read_pointer = nullptr; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoBeginReadData(h0, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); @@ -79,7 +80,7 @@ TEST(CoreTest, InvalidHandle) { // Shared buffer: h1 = MOJO_HANDLE_INVALID; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoDuplicateBufferHandle(h0, NULL, &h1)); + MojoDuplicateBufferHandle(h0, nullptr, &h1)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoMapBuffer(h0, 0, 1, &write_pointer, MOJO_MAP_BUFFER_FLAG_NONE)); } @@ -92,7 +93,7 @@ TEST(CoreTest, BasicMessagePipe) { h0 = MOJO_HANDLE_INVALID; h1 = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &h0, &h1)); + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &h0, &h1)); EXPECT_NE(h0, MOJO_HANDLE_INVALID); EXPECT_NE(h1, MOJO_HANDLE_INVALID); @@ -110,19 +111,20 @@ TEST(CoreTest, BasicMessagePipe) { EXPECT_EQ(kSignalAll, state.satisfiable_signals); // Last parameter is optional. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, NULL)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, nullptr)); // Try to read. buffer_size = static_cast<uint32_t>(sizeof(buffer)); EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - MojoReadMessage(h0, buffer, &buffer_size, NULL, NULL, + MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); // Write to |h1|. static const char kHello[] = "hello"; buffer_size = static_cast<uint32_t>(sizeof(kHello)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h1, kHello, buffer_size, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h1, kHello, buffer_size, nullptr, + 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); // |h0| should be readable. uint32_t result_index = 1; @@ -137,8 +139,9 @@ TEST(CoreTest, BasicMessagePipe) { // Read from |h0|. buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(h0, buffer, &buffer_size, NULL, - NULL, MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr, + MOJO_READ_MESSAGE_FLAG_NONE)); EXPECT_EQ(static_cast<uint32_t>(sizeof(kHello)), buffer_size); EXPECT_STREQ(kHello, buffer); @@ -183,7 +186,7 @@ TEST(CoreTest, MAYBE_BasicDataPipe) { hp = MOJO_HANDLE_INVALID; hc = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(NULL, &hp, &hc)); + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &hp, &hc)); EXPECT_NE(hp, MOJO_HANDLE_INVALID); EXPECT_NE(hc, MOJO_HANDLE_INVALID); @@ -210,7 +213,7 @@ TEST(CoreTest, MAYBE_BasicDataPipe) { MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); // Try to begin a two-phase read from |hc|. - read_pointer = NULL; + read_pointer = nullptr; EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, MojoBeginReadData(hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); @@ -263,7 +266,7 @@ TEST(CoreTest, MAYBE_BasicDataPipe) { state.satisfiable_signals); // Do a two-phase read from |hc|. - read_pointer = NULL; + read_pointer = nullptr; EXPECT_EQ(MOJO_RESULT_OK, MojoBeginReadData(hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); ASSERT_LE(buffer_size, sizeof(buffer) - 1); @@ -290,11 +293,11 @@ TEST(CoreTest, MAYBE_BasicSharedBuffer) { // Create a shared buffer (|h0|). h0 = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(NULL, 100, &h0)); + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 100, &h0)); EXPECT_NE(h0, MOJO_HANDLE_INVALID); // Map everything. - pointer = NULL; + pointer = nullptr; EXPECT_EQ(MOJO_RESULT_OK, MojoMapBuffer(h0, 0, 100, &pointer, MOJO_MAP_BUFFER_FLAG_NONE)); ASSERT_TRUE(pointer); @@ -302,7 +305,7 @@ TEST(CoreTest, MAYBE_BasicSharedBuffer) { // Duplicate |h0| to |h1|. h1 = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, NULL, &h1)); + EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, nullptr, &h1)); EXPECT_NE(h1, MOJO_HANDLE_INVALID); // Close |h0|. @@ -315,7 +318,7 @@ TEST(CoreTest, MAYBE_BasicSharedBuffer) { EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer)); // Map half of |h1|. - pointer = NULL; + pointer = nullptr; EXPECT_EQ(MOJO_RESULT_OK, MojoMapBuffer(h1, 50, 50, &pointer, MOJO_MAP_BUFFER_FLAG_NONE)); ASSERT_TRUE(pointer); @@ -336,7 +339,7 @@ extern "C" const char* MinimalCTest(void); // This checks that things actually work in C (not C++). TEST(CoreTest, MinimalCTest) { const char* failure = MinimalCTest(); - EXPECT_TRUE(failure == NULL) << failure; + EXPECT_FALSE(failure) << failure; } // TODO(vtl): Add multi-threaded tests. diff --git a/third_party/mojo/src/mojo/public/c/system/tests/core_unittest_pure_c.c b/third_party/mojo/src/mojo/public/c/system/tests/core_unittest_pure_c.c index fd4d9e9..d5978a9 100644 --- a/third_party/mojo/src/mojo/public/c/system/tests/core_unittest_pure_c.c +++ b/third_party/mojo/src/mojo/public/c/system/tests/core_unittest_pure_c.c @@ -53,10 +53,9 @@ const char* MinimalCTest(void) { handle0 = MOJO_HANDLE_INVALID; EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0)); - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE, - NULL)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, NULL)); handle1 = MOJO_HANDLE_INVALID; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1)); @@ -72,37 +71,26 @@ const char* MinimalCTest(void) { EXPECT_EQ(123u, result_index); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_HANDLE_SIGNAL_PEER_CLOSED, states[0].satisfiable_signals); EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(handle0, - kHello, - (uint32_t)sizeof(kHello), - NULL, - 0u, - MOJO_WRITE_DATA_FLAG_NONE)); + MojoWriteMessage(handle0, kHello, (uint32_t)sizeof(kHello), NULL, + 0u, MOJO_WRITE_DATA_FLAG_NONE)); struct MojoHandleSignalsState state; - EXPECT_EQ( - MOJO_RESULT_OK, - MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); num_bytes = (uint32_t)sizeof(buffer); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(handle1, - buffer, - &num_bytes, - NULL, - NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(handle1, buffer, &num_bytes, NULL, + NULL, MOJO_READ_MESSAGE_FLAG_NONE)); EXPECT_EQ((uint32_t)sizeof(kHello), num_bytes); EXPECT_EQ(0, memcmp(buffer, kHello, sizeof(kHello))); diff --git a/third_party/mojo/src/mojo/public/cpp/application/application_connection.h b/third_party/mojo/src/mojo/public/cpp/application/application_connection.h index e461e6c..5990be3 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/application_connection.h +++ b/third_party/mojo/src/mojo/public/cpp/application/application_connection.h @@ -12,39 +12,40 @@ namespace mojo { -// An instance of this class is passed to -// ApplicationDelegate's ConfigureIncomingConnection() method each time a -// connection is made to this app, and to ApplicationDelegate's -// ConfigureOutgoingConnection() method when the app connects to -// another. +// Represents a connection to another application. An instance of this class is +// passed to ApplicationDelegate's ConfigureIncomingConnection() method each +// time a connection is made to this app, and to ApplicationDelegate's +// ConfigureOutgoingConnection() method when the app connects to another. // -// To use define a class that implements your specific service api, e.g. FooImpl -// to implement a service named Foo. -// That class must subclass an InterfaceImpl specialization. +// To use, define a class that implements your specific service API (e.g., +// FooImpl to implement a service named Foo). Then implement an +// InterfaceFactory<Foo> that binds instances of FooImpl to +// InterfaceRequest<Foo>s and register that on the connection like this: // -// Then implement an InterfaceFactory<Foo> that binds instances of FooImpl to -// InterfaceRequest<Foo>s and register that on the connection. +// connection->AddService(&factory); // -// connection->AddService(&factory); -// -// Or if you have multiple factories implemented by the same type, explicitly +// Or, if you have multiple factories implemented by the same type, explicitly // specify the interface to register the factory for: // -// connection->AddService<Foo>(&my_foo_and_bar_factory_); -// connection->AddService<Bar>(&my_foo_and_bar_factory_); +// connection->AddService<Foo>(&my_foo_and_bar_factory_); +// connection->AddService<Bar>(&my_foo_and_bar_factory_); // // The InterfaceFactory must outlive the ApplicationConnection. class ApplicationConnection { public: virtual ~ApplicationConnection(); + // Makes Interface available as a service to the remote application. + // |factory| will create implementations of Interface on demand. template <typename Interface> void AddService(InterfaceFactory<Interface>* factory) { AddServiceConnector( new internal::InterfaceFactoryConnector<Interface>(factory)); } - // Connect to the service implementing |Interface|. + // Binds |ptr| to an implemention of Interface in the remote application. + // |ptr| can immediately be used to start sending requests to the remote + // service. template <typename Interface> void ConnectToService(InterfacePtr<Interface>* ptr) { if (ServiceProvider* sp = GetServiceProvider()) { @@ -54,10 +55,12 @@ class ApplicationConnection { } } - // The url identifying the application on the other end of this connection. + // Returns the URL identifying the remote application on this connection. virtual const std::string& GetRemoteApplicationURL() = 0; - // Raw ServiceProvider interface to remote application. + // Returns the raw proxy to the remote application's ServiceProvider + // interface. Most applications will just use ConnectToService() instead. + // Caller does not take ownership. virtual ServiceProvider* GetServiceProvider() = 0; private: diff --git a/third_party/mojo/src/mojo/public/cpp/application/application_delegate.h b/third_party/mojo/src/mojo/public/cpp/application/application_delegate.h index b0f2916..f95dca5 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/application_delegate.h +++ b/third_party/mojo/src/mojo/public/cpp/application/application_delegate.h @@ -14,21 +14,24 @@ namespace mojo { class ApplicationConnection; class ApplicationImpl; +// An abstract class that the application may subclass to control various +// behaviors of ApplicationImpl. class ApplicationDelegate { public: ApplicationDelegate(); virtual ~ApplicationDelegate(); + // Called exactly once before any other method. virtual void Initialize(ApplicationImpl* app); // Override this method to configure what services a connection supports when // being connected to from an app. - // return false to reject the connection entirely. + // Return false to reject the connection entirely. virtual bool ConfigureIncomingConnection(ApplicationConnection* connection); // Override this method to configure what services a connection supports when // connecting to another app. - // return false to reject the connection entirely. + // Return false to reject the connection entirely. virtual bool ConfigureOutgoingConnection(ApplicationConnection* connection); private: diff --git a/third_party/mojo/src/mojo/public/cpp/application/application_impl.h b/third_party/mojo/src/mojo/public/cpp/application/application_impl.h index 46d368d..21d44d3 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/application_impl.h +++ b/third_party/mojo/src/mojo/public/cpp/application/application_impl.h @@ -52,17 +52,28 @@ class ApplicationDelegate; // class ApplicationImpl : public Application { public: + // Does not take ownership of |delegate|, which must remain valid for the + // lifetime of ApplicationImpl. ApplicationImpl(ApplicationDelegate* delegate, InterfaceRequest<Application> request); ~ApplicationImpl() override; + // The Mojo shell. This will return a valid pointer after Initialize() has + // been invoked. It will remain valid until UnbindConnections() is invoked or + // the ApplicationImpl is destroyed. Shell* shell() const { return shell_.get(); } + const std::string& url() const { return url_; } + // Returns any initial configuration arguments, passed by the Shell. const std::vector<std::string>& args() const { return args_; } bool HasArg(const std::string& arg) const; - // Establishes a new connection to an application. Caller does not own. + // Requests a new connection to an application. Returns a pointer to the + // connection if the connection is permitted by this application's delegate, + // or nullptr otherwise. Caller does not take ownership. The pointer remains + // valid until an error occurs on the connection with the Shell, or until the + // ApplicationImpl is destroyed, whichever occurs first. ApplicationConnection* ConnectToApplication(const String& application_url); // Connect to application identified by |application_url| and connect to the @@ -74,7 +85,9 @@ class ApplicationImpl : public Application { } // Application implementation. - void Initialize(ShellPtr shell, Array<String> args) override; + void Initialize(ShellPtr shell, + Array<String> args, + const mojo::String& url) override; // Block until the Application is initialized, if it is not already. void WaitForInitialize(); @@ -113,6 +126,7 @@ class ApplicationImpl : public Application { Binding<Application> binding_; ShellPtr shell_; ShellPtrWatcher* shell_watch_; + std::string url_; std::vector<std::string> args_; MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationImpl); diff --git a/third_party/mojo/src/mojo/public/cpp/application/connect.h b/third_party/mojo/src/mojo/public/cpp/application/connect.h index a41c028..79762e2 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/connect.h +++ b/third_party/mojo/src/mojo/public/cpp/application/connect.h @@ -9,6 +9,7 @@ namespace mojo { +// Binds |ptr| to a remote implementation of Interface from |service_provider|. template <typename Interface> inline void ConnectToService(ServiceProvider* service_provider, InterfacePtr<Interface>* ptr) { diff --git a/third_party/mojo/src/mojo/public/cpp/application/lazy_interface_ptr.h b/third_party/mojo/src/mojo/public/cpp/application/lazy_interface_ptr.h index a9b57db..2d58db1 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lazy_interface_ptr.h +++ b/third_party/mojo/src/mojo/public/cpp/application/lazy_interface_ptr.h @@ -10,6 +10,8 @@ namespace mojo { +// An InterfacePtr that will request an implementation from a specified +// ServiceProvider when it is first accessed with the get() method. template <typename Interface> class LazyInterfacePtr : public InterfacePtr<Interface> { public: diff --git a/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc b/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc index c66dbb5..1fbfcac 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc +++ b/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc @@ -71,10 +71,13 @@ ApplicationConnection* ApplicationImpl::ConnectToApplication( return registry; } -void ApplicationImpl::Initialize(ShellPtr shell, Array<String> args) { +void ApplicationImpl::Initialize(ShellPtr shell, + Array<String> args, + const mojo::String& url) { shell_ = shell.Pass(); shell_watch_ = new ShellPtrWatcher(this); shell_.set_error_handler(shell_watch_); + url_ = url; args_ = args.To<std::vector<std::string>>(); delegate_->Initialize(this); } diff --git a/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc b/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc index ba6dd3f..d4e002f 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc +++ b/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc @@ -47,7 +47,9 @@ class ShellAndArgumentGrabber : public Application { private: // Application implementation. - void Initialize(ShellPtr shell, Array<String> args) override { + void Initialize(ShellPtr shell, + Array<String> args, + const mojo::String& url) override { *args_ = args.Pass(); g_application_request = binding_.Unbind(); g_shell = shell.Pass(); @@ -137,7 +139,7 @@ void ApplicationTestBase::SetUp() { g_application_request.Pass()); // Fake application initialization with the given command line arguments. - application_impl_->Initialize(g_shell.Pass(), g_args.Clone()); + application_impl_->Initialize(g_shell.Pass(), g_args.Clone(), ""); } void ApplicationTestBase::TearDown() { diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/binding.h b/third_party/mojo/src/mojo/public/cpp/bindings/binding.h index 7b663c4..77d015a 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/binding.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/binding.h @@ -58,8 +58,6 @@ namespace mojo { template <typename Interface> class Binding : public ErrorHandler { public: - using Client = typename Interface::Client; - // Constructs an incomplete binding that will use the implementation |impl|. // The binding may be completed with a subsequent call to the |Bind| method. // Does not take ownership of |impl|, which must outlive the binding. @@ -102,7 +100,6 @@ class Binding : public ErrorHandler { // Tears down the binding, closing the message pipe and leaving the interface // implementation unbound. ~Binding() override { - delete proxy_; if (internal_router_) { internal_router_->set_error_handler(nullptr); delete internal_router_; @@ -118,14 +115,11 @@ class Binding : public ErrorHandler { internal::FilterChain filters; filters.Append<internal::MessageHeaderValidator>(); filters.Append<typename Interface::RequestValidator_>(); - filters.Append<typename Client::ResponseValidator_>(); internal_router_ = new internal::Router(handle.Pass(), filters.Pass(), waiter); internal_router_->set_incoming_receiver(&stub_); internal_router_->set_error_handler(this); - - proxy_ = new typename Client::Proxy_(internal_router_); } // Completes a binding that was constructed with only an interface @@ -188,9 +182,6 @@ class Binding : public ErrorHandler { // does not take ownership. Interface* impl() { return impl_; } - // Returns the client's interface. - Client* client() { return proxy_; } - // Indicates whether the binding has been completed (i.e., whether a message // pipe has been bound to the implementation). bool is_bound() const { return !!internal_router_; } @@ -200,7 +191,6 @@ class Binding : public ErrorHandler { private: internal::Router* internal_router_ = nullptr; - typename Client::Proxy_* proxy_ = nullptr; typename Interface::Stub_ stub_; Interface* impl_; ErrorHandler* error_handler_ = nullptr; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/callback.h b/third_party/mojo/src/mojo/public/cpp/bindings/callback.h index c2ed955..17d1caf 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/callback.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/callback.h @@ -38,7 +38,7 @@ class Callback<void(Args...)> { // As above, but can take an object that isn't derived from Runnable, so long // as it has a compatible operator() or Run() method. operator() will be - // prefered if the type has both. + // preferred if the type has both. template <typename Sink> Callback(const Sink& sink) { using sink_type = typename internal::Conditional< diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/interface_impl.h b/third_party/mojo/src/mojo/public/cpp/bindings/interface_impl.h index 5663cc3..b5191ef 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/interface_impl.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/interface_impl.h @@ -21,7 +21,6 @@ template <typename Interface> class InterfaceImpl : public Interface, public ErrorHandler { public: using ImplementedInterface = Interface; - using Client = typename Interface::Client; InterfaceImpl() : binding_(this), error_handler_impl_(this) { binding_.set_error_handler(&error_handler_impl_); @@ -38,15 +37,12 @@ class InterfaceImpl : public Interface, public ErrorHandler { return binding_.WaitForIncomingMethodCall(); } - Client* client() { return binding_.client(); } internal::Router* internal_router() { return binding_.internal_router(); } // Implements ErrorHandler. // - // Called when the client is no longer connected to this instance. NOTE: The - // client() method continues to return a non-null pointer after this method - // is called. After this method is called, any method calls made on client() - // will be silently ignored. + // Called when the underlying pipe is closed. After this point no more + // calls will be made on Interface and all responses will be silently ignored. void OnConnectionError() override {} void set_delete_on_error(bool delete_on_error) { @@ -159,9 +155,6 @@ Impl* WeakBindToProxy( // The instance is also bound to the current thread. Its methods will only be // called on the current thread, and if the current thread exits, then it will // also be deleted, and along with it, its end point of the pipe will be closed. -// -// Before returning, the instance will receive a SetClient call, providing it -// with a proxy to the client on the other end of the pipe. template <typename Impl, typename Interface> Impl* BindToRequest( Impl* instance, diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h b/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h index b7f1a4e..8b5e0b4 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h @@ -20,11 +20,6 @@ class ErrorHandler; // closes the pipe and deletes the proxy on destruction. The pointer must be // bound to a message pipe before the interface methods can be called. // -// Can also route incoming calls to a local implementation of the -// Interface::Client interface. To enable this, call the set_client() method. -// Calls to the client interface will originate from the same thread that owns -// this InterfacePtr. -// // This class is thread hostile, as is the local proxy it manages. All calls to // this class or the proxy should be from the same thread that created it. If // you need to move the proxy to a different thread, extract the message pipe @@ -87,23 +82,18 @@ class InterfacePtr { internal_state_.Swap(&doomed); } - // Blocks the current thread until the next incoming call to a client method - // or callback arrives, or until an error occurs. Returns |true| if a call - // arrived, or |false| in case of error. + // Blocks the current thread until the next incoming response callback arrives + // or an error occurs. Returns |true| if a response arrived, or |false| in + // case of error. // // This method may only be called after the InterfacePtr has been bound to a // message pipe. + // + // TODO(jamesr): Rename to WaitForIncomingResponse(). bool WaitForIncomingMethodCall() { return internal_state_.WaitForIncomingMethodCall(); } - // Enables routing of incoming method calls to a local implementation of the - // Interface::Client interface. Calls to |client| will come from the thread - // that owns this InterfacePtr. - void set_client(typename Interface::Client* client) { - internal_state_.set_client(client); - } - // Indicates whether the message pipe has encountered an error. If true, // method calls made on this interface will be dropped (and may already have // been dropped). diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h index ec70d37..a8f95b2 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h @@ -26,7 +26,7 @@ class Array_Data; struct StructHeader { uint32_t num_bytes; - uint32_t num_fields; + uint32_t version; }; static_assert(sizeof(StructHeader) == 8, "Bad sizeof(StructHeader)"); diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.cc b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.cc index 936d0ca..6332090 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.cc @@ -79,12 +79,8 @@ void DecodeHandle(Handle* handle, std::vector<Handle>* handles) { *handle = FetchAndReset(&handles->at(handle->value())); } -bool ValidateStructHeader(const void* data, - uint32_t min_num_bytes, - uint32_t min_num_fields, - BoundsChecker* bounds_checker) { - MOJO_DCHECK(min_num_bytes >= sizeof(StructHeader)); - +bool ValidateStructHeaderAndClaimMemory(const void* data, + BoundsChecker* bounds_checker) { if (!IsAligned(data)) { ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT); return false; @@ -96,11 +92,7 @@ bool ValidateStructHeader(const void* data, const StructHeader* header = static_cast<const StructHeader*>(data); - // TODO(yzshen): Currently our binding code cannot handle structs of smaller - // size or with fewer fields than the version that it sees. That needs to be - // changed in order to provide backward compatibility. - if (header->num_bytes < min_num_bytes || - header->num_fields < min_num_fields) { + if (header->num_bytes < sizeof(StructHeader)) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); return false; } diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.h index 6bebf90..ceb213d 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_serialization.h @@ -69,13 +69,15 @@ inline void Decode(T* obj, std::vector<Handle>* handles) { obj->ptr->DecodePointersAndHandles(handles); } -// If returns true, this function also claims the memory range of the size -// specified in the struct header, starting from |data|. -// Note: |min_num_bytes| must be no less than sizeof(StructHeader). -bool ValidateStructHeader(const void* data, - uint32_t min_num_bytes, - uint32_t min_num_fields, - BoundsChecker* bounds_checker); +// Validates that |data| contains a valid struct header, in terms of alignment +// and size (i.e., the |num_bytes| field of the header is sufficient for storing +// the header itself). Besides, it checks that the memory range +// [data, data + num_bytes) is not marked as occupied by other objects in +// |bounds_checker|. On success, the memory range is marked as occupied. +// Note: Does not verify |version| or that |num_bytes| is correct for the +// claimed version. +bool ValidateStructHeaderAndClaimMemory(const void* data, + BoundsChecker* bounds_checker); } // namespace internal } // namespace mojo diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/interface_ptr_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/interface_ptr_internal.h index a38baf7..3618a26 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/interface_ptr_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/interface_ptr_internal.h @@ -24,8 +24,8 @@ class InterfacePtrState { ~InterfacePtrState() { // Destruction order matters here. We delete |proxy_| first, even though - // |router_| may have a reference to it, so that |~Interface| may have a - // shot at generating new outbound messages (ie, invoking client methods). + // |router_| may have a reference to it, so that destructors for any request + // callbacks still pending can interact with the InterfacePtr. delete proxy_; delete router_; } @@ -71,13 +71,6 @@ class InterfacePtrState { bool is_bound() const { return handle_.is_valid() || router_; } - void set_client(typename Interface::Client* client) { - ConfigureProxyIfNecessary(); - - MOJO_DCHECK(proxy_); - proxy_->stub.set_sink(client); - } - bool encountered_error() const { return router_ ? router_->encountered_error() : false; } @@ -95,15 +88,7 @@ class InterfacePtrState { } private: - class ProxyWithStub : public Interface::Proxy_ { - public: - explicit ProxyWithStub(MessageReceiverWithResponder* receiver) - : Interface::Proxy_(receiver) {} - typename Interface::Client::Stub_ stub; - - private: - MOJO_DISALLOW_COPY_AND_ASSIGN(ProxyWithStub); - }; + using Proxy = typename Interface::Proxy_; void ConfigureProxyIfNecessary() { // The proxy has been configured. @@ -119,19 +104,15 @@ class InterfacePtrState { FilterChain filters; filters.Append<MessageHeaderValidator>(); - filters.Append<typename Interface::Client::RequestValidator_>(); filters.Append<typename Interface::ResponseValidator_>(); router_ = new Router(handle_.Pass(), filters.Pass(), waiter_); waiter_ = nullptr; - ProxyWithStub* proxy = new ProxyWithStub(router_); - router_->set_incoming_receiver(&proxy->stub); - - proxy_ = proxy; + proxy_ = new Proxy(router_); } - ProxyWithStub* proxy_; + Proxy* proxy_; Router* router_; // |proxy_| and |router_| are not initialized until read/write with the diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_data_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_data_internal.h index 7787714..16f6b08 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_data_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_data_internal.h @@ -44,10 +44,18 @@ class Map_Data { if (!data) return true; - if (!ValidateStructHeader(data, sizeof(Map_Data), 2, bounds_checker)) + if (!ValidateStructHeaderAndClaimMemory(data, bounds_checker)) return false; const Map_Data* object = static_cast<const Map_Data*>(data); + // TODO(yzshen): In order to work with other bindings which still interprets + // the |version| field as |num_fields|, |version| is set to 2. + if (object->header_.num_bytes != sizeof(Map_Data) || + object->header_.version != 2) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + if (!ValidateEncodedPointer(&object->keys.offset)) { ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER); return false; @@ -106,7 +114,9 @@ class Map_Data { private: Map_Data() { header_.num_bytes = sizeof(*this); - header_.num_fields = 2; + // TODO(yzshen): In order to work with other bindings which still interprets + // the |version| field as |num_fields|, set it to version 2 for now. + header_.version = 2; } ~Map_Data() = delete; }; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h index 8afc18c..84f927c 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h @@ -21,7 +21,7 @@ template <typename Key, typename Value> struct MapTraits<Key, Value, false> { // Map keys can't be move only types. static_assert(!internal::IsMoveOnlyType<Key>::value, - "Map keys can not be move only types."); + "Map keys cannot be move only types."); typedef Key KeyStorageType; typedef Key& KeyRefType; @@ -111,7 +111,7 @@ template <typename Key, typename Value> struct MapTraits<Key, Value, true> { // Map keys can't be move only types. static_assert(!internal::IsMoveOnlyType<Key>::value, - "Map keys can not be move only types."); + "Map keys cannot be move only types."); typedef Key KeyStorageType; typedef Key& KeyRefType; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_builder.cc b/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_builder.cc index cc914e8..837ac81 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_builder.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_builder.cc @@ -19,7 +19,9 @@ MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) : buf_(sizeof(MessageHeader) + payload_size) { MessageHeader* header; Allocate(&buf_, &header); - header->num_fields = 2; + // TODO(yzshen): In order to work with other bindings which still interprets + // the |version| field as |num_fields|, set it to version 2 for now. + header->version = 2; header->name = name; } @@ -41,7 +43,9 @@ MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name, : MessageBuilder(sizeof(MessageHeaderWithRequestID) + payload_size) { MessageHeaderWithRequestID* header; Allocate(&buf_, &header); - header->num_fields = 3; + // TODO(yzshen): In order to work with other bindings which still interprets + // the |version| field as |num_fields|, set it to version 3 for now. + header->version = 3; header->name = name; header->flags = flags; header->request_id = request_id; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_header_validator.cc b/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_header_validator.cc index 9d28ecc..fdcc365 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_header_validator.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/message_header_validator.cc @@ -17,17 +17,21 @@ bool IsValidMessageHeader(const MessageHeader* header) { // header. If we encounter fields we do not understand, we must ignore them. // Extra validation of the struct header: - if (header->num_fields == 2) { + if (header->version < 2) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + if (header->version == 2) { if (header->num_bytes != sizeof(MessageHeader)) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); return false; } - } else if (header->num_fields == 3) { + } else if (header->version == 3) { if (header->num_bytes != sizeof(MessageHeaderWithRequestID)) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); return false; } - } else if (header->num_fields > 3) { + } else if (header->version > 3) { if (header->num_bytes < sizeof(MessageHeaderWithRequestID)) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); return false; @@ -37,8 +41,8 @@ bool IsValidMessageHeader(const MessageHeader* header) { // Validate flags (allow unknown bits): // These flags require a RequestID. - if (header->num_fields < 3 && ((header->flags & kMessageExpectsResponse) || - (header->flags & kMessageIsResponse))) { + if (header->version < 3 && ((header->flags & kMessageExpectsResponse) || + (header->flags & kMessageIsResponse))) { ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID); return false; } @@ -65,10 +69,8 @@ bool MessageHeaderValidator::Accept(Message* message) { // if |message| contains handles. BoundsChecker bounds_checker(message->data(), message->data_num_bytes(), 0); - if (!ValidateStructHeader( - message->data(), sizeof(MessageHeader), 2, &bounds_checker)) { + if (!ValidateStructHeaderAndClaimMemory(message->data(), &bounds_checker)) return false; - } if (!IsValidMessageHeader(message->header())) return false; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h index 4facf6e..fbba6b3 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h @@ -19,11 +19,11 @@ enum ValidationError { // objects. VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE, // A struct header doesn't make sense, for example: - // - |num_bytes| is smaller than the size of the oldest version that we - // support. - // - |num_fields| is smaller than the field number of the oldest version that - // we support. - // - |num_bytes| and |num_fields| don't match. + // - |num_bytes| is smaller than the size of the struct header. + // - |num_bytes| and |version| don't match. + // TODO(yzshen): Consider splitting it into two different error codes. Because + // the former indicates someone is misbehaving badly whereas the latter could + // be due to an inappropriately-modified .mojom file. VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER, // An array header doesn't make sense, for example: // - |num_bytes| is smaller than the size of the header plus the size required diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/map.h b/third_party/mojo/src/mojo/public/cpp/bindings/map.h index 8ac183f..09ca4ad 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/map.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/map.h @@ -25,9 +25,9 @@ class Map { MOJO_MOVE_ONLY_TYPE(Map) public: - // Map keys can not be move only classes. + // Map keys cannot be move only classes. static_assert(!internal::IsMoveOnlyType<Key>::value, - "Map keys can not be move only types."); + "Map keys cannot be move only types."); typedef internal::MapTraits<Key, Value, diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/message.h b/third_party/mojo/src/mojo/public/cpp/bindings/message.h index 5cab4ea..80cd6d5 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/message.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/message.h @@ -43,7 +43,7 @@ class Message { bool has_flag(uint32_t flag) const { return !!(data_->header.flags & flag); } // Access the request_id field (if present). - bool has_request_id() const { return data_->header.num_fields >= 3; } + bool has_request_id() const { return data_->header.version >= 3; } uint64_t request_id() const { MOJO_DCHECK(has_request_id()); return static_cast<const internal::MessageHeaderWithRequestID*>( diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/no_interface.h b/third_party/mojo/src/mojo/public/cpp/bindings/no_interface.h index 4c35789..d8915cd 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/no_interface.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/no_interface.h @@ -12,7 +12,7 @@ namespace mojo { // NoInterface is for use in cases when a non-existent or empty interface is -// needed (e.g., when the Mojom "Peer" attribute is not present). +// needed. class NoInterfaceProxy; class NoInterfaceStub; @@ -24,7 +24,6 @@ class NoInterface { typedef NoInterfaceStub Stub_; typedef PassThroughFilter RequestValidator_; typedef PassThroughFilter ResponseValidator_; - typedef NoInterface Client; virtual ~NoInterface() {} }; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h b/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h index 7caf54a..c891a21 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h @@ -108,7 +108,6 @@ class StrongBinding : public ErrorHandler { } Interface* impl() { return binding_.impl(); } - typename Interface::Client* client() { return binding_.client(); } // Exposed for testing, should not generally be used. internal::Router* internal_router() { return binding_.internal_router(); } diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc index 5667f64..f936361 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/environment/environment.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "mojo/public/cpp/utility/run_loop.h" @@ -24,20 +26,26 @@ class StringRecorder { std::string* buf_; }; -class ImportedInterfaceImpl - : public InterfaceImpl<imported::ImportedInterface> { +class ImportedInterfaceImpl : public imported::ImportedInterface { public: + explicit ImportedInterfaceImpl( + InterfaceRequest<imported::ImportedInterface> request) + : binding_(this, request.Pass()) {} + void DoSomething() override { do_something_count_++; } static int do_something_count() { return do_something_count_; } private: static int do_something_count_; + Binding<ImportedInterface> binding_; }; int ImportedInterfaceImpl::do_something_count_ = 0; -class SampleNamedObjectImpl : public InterfaceImpl<sample::NamedObject> { +class SampleNamedObjectImpl : public sample::NamedObject { public: + explicit SampleNamedObjectImpl(InterfaceRequest<sample::NamedObject> request) + : binding_(this, request.Pass()) {} void SetName(const mojo::String& name) override { name_ = name; } void GetName(const mojo::Callback<void(mojo::String)>& callback) override { @@ -46,12 +54,17 @@ class SampleNamedObjectImpl : public InterfaceImpl<sample::NamedObject> { private: std::string name_; + StrongBinding<sample::NamedObject> binding_; }; -class SampleFactoryImpl : public InterfaceImpl<sample::Factory> { +class SampleFactoryImpl : public sample::Factory { public: + explicit SampleFactoryImpl(InterfaceRequest<sample::Factory> request) + : binding_(this, request.Pass()) {} + void DoStuff(sample::RequestPtr request, - ScopedMessagePipeHandle pipe) override { + ScopedMessagePipeHandle pipe, + const DoStuffCallback& callback) override { std::string text1; if (pipe.is_valid()) EXPECT_TRUE(ReadTextMessage(pipe.get(), &text1)); @@ -73,13 +86,14 @@ class SampleFactoryImpl : public InterfaceImpl<sample::Factory> { sample::ResponsePtr response(sample::Response::New()); response->x = 2; response->pipe = pipe0.Pass(); - client()->DidStuff(response.Pass(), text1); + callback.Run(response.Pass(), text1); if (request->obj) request->obj->DoSomething(); } - void DoStuff2(ScopedDataPipeConsumerHandle pipe) override { + void DoStuff2(ScopedDataPipeConsumerHandle pipe, + const DoStuff2Callback& callback) override { // Read the data from the pipe, writing the response (as a string) to // DidStuff2(). ASSERT_TRUE(pipe.is_valid()); @@ -95,13 +109,13 @@ class SampleFactoryImpl : public InterfaceImpl<sample::Factory> { ReadDataRaw( pipe.get(), data, &data_size, MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - client()->DidStuff2(data); + callback.Run(data); } void CreateNamedObject( InterfaceRequest<sample::NamedObject> object_request) override { EXPECT_TRUE(object_request.is_pending()); - BindToRequest(new SampleNamedObjectImpl(), &object_request); + new SampleNamedObjectImpl(object_request.Pass()); } // These aren't called or implemented, but exist here to test that the @@ -118,21 +132,26 @@ class SampleFactoryImpl : public InterfaceImpl<sample::Factory> { private: ScopedMessagePipeHandle pipe1_; + Binding<sample::Factory> binding_; }; -class SampleFactoryClientImpl : public sample::FactoryClient { +class HandlePassingTest : public testing::Test { public: - SampleFactoryClientImpl() : got_response_(false) {} + void TearDown() override { PumpMessages(); } - void set_expected_text_reply(const std::string& expected_text_reply) { - expected_text_reply_ = expected_text_reply; - } + void PumpMessages() { loop_.RunUntilIdle(); } + + private: + Environment env_; + RunLoop loop_; +}; - bool got_response() const { return got_response_; } +struct DoStuffCallback { + DoStuffCallback(bool* got_response, std::string* got_text_reply) + : got_response(got_response), got_text_reply(got_text_reply) {} - void DidStuff(sample::ResponsePtr response, - const String& text_reply) override { - EXPECT_EQ(expected_text_reply_, text_reply); + void Run(sample::ResponsePtr response, const String& text_reply) const { + *got_text_reply = text_reply; if (response->pipe.is_valid()) { std::string text2; @@ -149,40 +168,16 @@ class SampleFactoryClientImpl : public sample::FactoryClient { EXPECT_FALSE(response->pipe.is_valid()); } - got_response_ = true; + *got_response = true; } - void DidStuff2(const String& text_reply) override { - got_response_ = true; - EXPECT_EQ(expected_text_reply_, text_reply); - } - - private: - ScopedMessagePipeHandle pipe1_; - ScopedMessagePipeHandle pipe3_; - std::string expected_text_reply_; - bool got_response_; -}; - -class HandlePassingTest : public testing::Test { - public: - void TearDown() override { PumpMessages(); } - - void PumpMessages() { loop_.RunUntilIdle(); } - - private: - Environment env_; - RunLoop loop_; + bool* got_response; + std::string* got_text_reply; }; TEST_F(HandlePassingTest, Basic) { sample::FactoryPtr factory; - BindToProxy(new SampleFactoryImpl(), &factory); - - SampleFactoryClientImpl factory_client; - factory_client.set_expected_text_reply(kText1); - - factory.set_client(&factory_client); + SampleFactoryImpl factory_impl(GetProxy(&factory)); MessagePipe pipe0; EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1)); @@ -191,48 +186,62 @@ TEST_F(HandlePassingTest, Basic) { EXPECT_TRUE(WriteTextMessage(pipe1.handle1.get(), kText2)); imported::ImportedInterfacePtr imported; - BindToProxy(new ImportedInterfaceImpl(), &imported); + ImportedInterfaceImpl imported_impl(GetProxy(&imported)); sample::RequestPtr request(sample::Request::New()); request->x = 1; request->pipe = pipe1.handle0.Pass(); request->obj = imported.Pass(); - factory->DoStuff(request.Pass(), pipe0.handle0.Pass()); + bool got_response = false; + std::string got_text_reply; + DoStuffCallback cb(&got_response, &got_text_reply); + factory->DoStuff(request.Pass(), pipe0.handle0.Pass(), cb); - EXPECT_FALSE(factory_client.got_response()); + EXPECT_FALSE(*cb.got_response); int count_before = ImportedInterfaceImpl::do_something_count(); PumpMessages(); - EXPECT_TRUE(factory_client.got_response()); + EXPECT_TRUE(*cb.got_response); + EXPECT_EQ(kText1, *cb.got_text_reply); EXPECT_EQ(1, ImportedInterfaceImpl::do_something_count() - count_before); } TEST_F(HandlePassingTest, PassInvalid) { sample::FactoryPtr factory; - BindToProxy(new SampleFactoryImpl(), &factory); - - SampleFactoryClientImpl factory_client; - factory.set_client(&factory_client); + SampleFactoryImpl factory_impl(GetProxy(&factory)); sample::RequestPtr request(sample::Request::New()); request->x = 1; - factory->DoStuff(request.Pass(), ScopedMessagePipeHandle().Pass()); + bool got_response = false; + std::string got_text_reply; + DoStuffCallback cb(&got_response, &got_text_reply); + factory->DoStuff(request.Pass(), ScopedMessagePipeHandle().Pass(), cb); - EXPECT_FALSE(factory_client.got_response()); + EXPECT_FALSE(*cb.got_response); PumpMessages(); - EXPECT_TRUE(factory_client.got_response()); + EXPECT_TRUE(*cb.got_response); } +struct DoStuff2Callback { + DoStuff2Callback(bool* got_response, std::string* got_text_reply) + : got_response(got_response), got_text_reply(got_text_reply) {} + + void Run(const String& text_reply) const { + *got_response = true; + *got_text_reply = text_reply; + } + + bool* got_response; + std::string* got_text_reply; +}; + // Verifies DataPipeConsumer can be passed and read from. TEST_F(HandlePassingTest, DataPipe) { sample::FactoryPtr factory; - BindToProxy(new SampleFactoryImpl(), &factory); - - SampleFactoryClientImpl factory_client; - factory.set_client(&factory_client); + SampleFactoryImpl factory_impl(GetProxy(&factory)); // Writes a string to a data pipe and passes the data pipe (consumer) to the // factory. @@ -245,7 +254,6 @@ TEST_F(HandlePassingTest, DataPipe) { ASSERT_EQ(MOJO_RESULT_OK, CreateDataPipe(&options, &producer_handle, &consumer_handle)); std::string expected_text_reply = "got it"; - factory_client.set_expected_text_reply(expected_text_reply); // +1 for \0. uint32_t data_size = static_cast<uint32_t>(expected_text_reply.size() + 1); ASSERT_EQ(MOJO_RESULT_OK, @@ -254,21 +262,22 @@ TEST_F(HandlePassingTest, DataPipe) { &data_size, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - factory->DoStuff2(consumer_handle.Pass()); + bool got_response = false; + std::string got_text_reply; + DoStuff2Callback cb(&got_response, &got_text_reply); + factory->DoStuff2(consumer_handle.Pass(), cb); - EXPECT_FALSE(factory_client.got_response()); + EXPECT_FALSE(*cb.got_response); PumpMessages(); - EXPECT_TRUE(factory_client.got_response()); + EXPECT_TRUE(*cb.got_response); + EXPECT_EQ(expected_text_reply, *cb.got_text_reply); } TEST_F(HandlePassingTest, PipesAreClosed) { sample::FactoryPtr factory; - BindToProxy(new SampleFactoryImpl(), &factory); - - SampleFactoryClientImpl factory_client; - factory.set_client(&factory_client); + SampleFactoryImpl factory_impl(GetProxy(&factory)); MessagePipe extra_pipe; @@ -283,7 +292,8 @@ TEST_F(HandlePassingTest, PipesAreClosed) { sample::RequestPtr request(sample::Request::New()); request->more_pipes = pipes.Pass(); - factory->DoStuff(request.Pass(), ScopedMessagePipeHandle()); + factory->DoStuff(request.Pass(), ScopedMessagePipeHandle(), + sample::Factory::DoStuffCallback()); } // We expect the pipes to have been closed. @@ -308,7 +318,7 @@ TEST_F(HandlePassingTest, IsHandle) { TEST_F(HandlePassingTest, CreateNamedObject) { sample::FactoryPtr factory; - BindToProxy(new SampleFactoryImpl(), &factory); + SampleFactoryImpl factory_impl(GetProxy(&factory)); sample::NamedObjectPtr object1; EXPECT_FALSE(object1); diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc index ded5888..3fcade6 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc @@ -49,11 +49,15 @@ RunnableImpl<Method, Class> MakeRunnable(Method method, Class object) { typedef mojo::Callback<void(double)> CalcCallback; -class MathCalculatorImpl : public InterfaceImpl<math::Calculator> { +class MathCalculatorImpl : public math::Calculator { public: + explicit MathCalculatorImpl(InterfaceRequest<math::Calculator> request) + : total_(0.0), binding_(this, request.Pass()) {} ~MathCalculatorImpl() override {} - MathCalculatorImpl() : total_(0.0) {} + void CloseMessagePipe() { binding_.Close(); } + + void WaitForIncomingMethodCall() { binding_.WaitForIncomingMethodCall(); } void Clear(const CalcCallback& callback) override { total_ = 0.0; @@ -72,6 +76,7 @@ class MathCalculatorImpl : public InterfaceImpl<math::Calculator> { private: double total_; + Binding<math::Calculator> binding_; }; class MathCalculatorUI { @@ -142,22 +147,25 @@ class SelfDestructingMathCalculatorUI { // static int SelfDestructingMathCalculatorUI::num_instances_ = 0; -class ReentrantServiceImpl : public InterfaceImpl<sample::Service> { +class ReentrantServiceImpl : public sample::Service { public: ~ReentrantServiceImpl() override {} - ReentrantServiceImpl() : call_depth_(0), max_call_depth_(0) {} + explicit ReentrantServiceImpl(InterfaceRequest<sample::Service> request) + : call_depth_(0), max_call_depth_(0), binding_(this, request.Pass()) {} int max_call_depth() { return max_call_depth_; } void Frobinate(sample::FooPtr foo, sample::Service::BazOptions baz, - sample::PortPtr port) override { + sample::PortPtr port, + const sample::Service::FrobinateCallback& callback) override { max_call_depth_ = std::max(++call_depth_, max_call_depth_); if (call_depth_ == 1) { - EXPECT_TRUE(WaitForIncomingMethodCall()); + EXPECT_TRUE(binding_.WaitForIncomingMethodCall()); } call_depth_--; + callback.Run(5); } void GetPort(mojo::InterfaceRequest<sample::Port> port) override {} @@ -165,6 +173,7 @@ class ReentrantServiceImpl : public InterfaceImpl<sample::Service> { private: int call_depth_; int max_call_depth_; + Binding<sample::Service> binding_; }; class InterfacePtrTest : public testing::Test { @@ -180,7 +189,7 @@ class InterfacePtrTest : public testing::Test { TEST_F(InterfacePtrTest, EndToEnd) { math::CalculatorPtr calc; - BindToProxy(new MathCalculatorImpl(), &calc); + MathCalculatorImpl calc_impl(GetProxy(&calc)); // Suppose this is instantiated in a process that has pipe1_. MathCalculatorUI calculator_ui(calc.Pass()); @@ -195,7 +204,7 @@ TEST_F(InterfacePtrTest, EndToEnd) { TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { math::CalculatorPtr calc; - MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc); + MathCalculatorImpl calc_impl(GetProxy(&calc)); // Suppose this is instantiated in a process that has pipe1_. MathCalculatorUI calculator_ui(calc.Pass()); @@ -204,13 +213,13 @@ TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { calculator_ui.Add(2.0); EXPECT_EQ(0.0, calculator_ui.GetOutput()); - impl->WaitForIncomingMethodCall(); + calc_impl.WaitForIncomingMethodCall(); calculator_ui.WaitForIncomingMethodCall(); EXPECT_EQ(2.0, calculator_ui.GetOutput()); calculator_ui.Multiply(5.0); EXPECT_EQ(2.0, calculator_ui.GetOutput()); - impl->WaitForIncomingMethodCall(); + calc_impl.WaitForIncomingMethodCall(); calculator_ui.WaitForIncomingMethodCall(); EXPECT_EQ(10.0, calculator_ui.GetOutput()); } @@ -218,7 +227,7 @@ TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { TEST_F(InterfacePtrTest, Movable) { math::CalculatorPtr a; math::CalculatorPtr b; - BindToProxy(new MathCalculatorImpl(), &b); + MathCalculatorImpl calc_impl(GetProxy(&b)); EXPECT_TRUE(!a); EXPECT_FALSE(!b); @@ -254,7 +263,7 @@ TEST_F(InterfacePtrTest, Resettable) { TEST_F(InterfacePtrTest, EncounteredError) { math::CalculatorPtr proxy; - MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); + MathCalculatorImpl calc_impl(GetProxy(&proxy)); MathCalculatorUI calculator_ui(proxy.Pass()); @@ -267,7 +276,7 @@ TEST_F(InterfacePtrTest, EncounteredError) { EXPECT_FALSE(calculator_ui.encountered_error()); // Close the server. - server->internal_router()->CloseMessagePipe(); + calc_impl.CloseMessagePipe(); // The state change isn't picked up locally yet. EXPECT_FALSE(calculator_ui.encountered_error()); @@ -280,7 +289,7 @@ TEST_F(InterfacePtrTest, EncounteredError) { TEST_F(InterfacePtrTest, EncounteredErrorCallback) { math::CalculatorPtr proxy; - MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); + MathCalculatorImpl calc_impl(GetProxy(&proxy)); ErrorObserver error_observer; proxy.set_error_handler(&error_observer); @@ -296,7 +305,7 @@ TEST_F(InterfacePtrTest, EncounteredErrorCallback) { EXPECT_FALSE(calculator_ui.encountered_error()); // Close the server. - server->internal_router()->CloseMessagePipe(); + calc_impl.CloseMessagePipe(); // The state change isn't picked up locally yet. EXPECT_FALSE(calculator_ui.encountered_error()); @@ -311,17 +320,9 @@ TEST_F(InterfacePtrTest, EncounteredErrorCallback) { EXPECT_TRUE(error_observer.encountered_error()); } -TEST_F(InterfacePtrTest, NoClientAttribute) { - // This is a test to ensure the following compiles. The sample::Port interface - // does not have an explicit Client attribute. - sample::PortPtr port; - MessagePipe pipe; - port.Bind(pipe.handle0.Pass()); -} - -TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) { +TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) { math::CalculatorPtr proxy; - BindToProxy(new MathCalculatorImpl(), &proxy); + MathCalculatorImpl calc_impl(GetProxy(&proxy)); EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); @@ -334,9 +335,9 @@ TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) { EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); } -TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) { +TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) { math::CalculatorPtr proxy; - BindToProxy(new MathCalculatorImpl(), &proxy); + MathCalculatorImpl calc_impl(GetProxy(&proxy)); EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); @@ -351,14 +352,16 @@ TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) { TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) { sample::ServicePtr proxy; - ReentrantServiceImpl* impl = BindToProxy(new ReentrantServiceImpl(), &proxy); + ReentrantServiceImpl impl(GetProxy(&proxy)); - proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr); - proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr); + proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + sample::Service::FrobinateCallback()); + proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, + sample::Service::FrobinateCallback()); PumpMessages(); - EXPECT_EQ(2, impl->max_call_depth()); + EXPECT_EQ(2, impl.max_call_depth()); } class StrongMathCalculatorImpl : public math::Calculator, public ErrorHandler { diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc index 4864e35..a1fe8e6 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc @@ -13,8 +13,11 @@ namespace mojo { namespace test { namespace { -class ProviderImpl : public InterfaceImpl<sample::Provider> { +class ProviderImpl : public sample::Provider { public: + explicit ProviderImpl(InterfaceRequest<sample::Provider> request) + : binding_(this, request.Pass()) {} + void EchoString(const String& a, const Callback<void(String)>& callback) override { Callback<void(String)> callback_copy; @@ -39,6 +42,8 @@ class ProviderImpl : public InterfaceImpl<sample::Provider> { const Callback<void(sample::Enum)>& callback) override { callback.Run(a); } + + Binding<sample::Provider> binding_; }; class StringRecorder { @@ -86,7 +91,7 @@ class RequestResponseTest : public testing::Test { TEST_F(RequestResponseTest, EchoString) { sample::ProviderPtr provider; - BindToProxy(new ProviderImpl(), &provider); + ProviderImpl provider_impl(GetProxy(&provider)); std::string buf; provider->EchoString(String::From("hello"), StringRecorder(&buf)); @@ -98,7 +103,7 @@ TEST_F(RequestResponseTest, EchoString) { TEST_F(RequestResponseTest, EchoStrings) { sample::ProviderPtr provider; - BindToProxy(new ProviderImpl(), &provider); + ProviderImpl provider_impl(GetProxy(&provider)); std::string buf; provider->EchoStrings( @@ -111,7 +116,7 @@ TEST_F(RequestResponseTest, EchoStrings) { TEST_F(RequestResponseTest, EchoMessagePipeHandle) { sample::ProviderPtr provider; - BindToProxy(new ProviderImpl(), &provider); + ProviderImpl provider_impl(GetProxy(&provider)); MessagePipe pipe2; provider->EchoMessagePipeHandle(pipe2.handle1.Pass(), @@ -127,7 +132,7 @@ TEST_F(RequestResponseTest, EchoMessagePipeHandle) { TEST_F(RequestResponseTest, EchoEnum) { sample::ProviderPtr provider; - BindToProxy(new ProviderImpl(), &provider); + ProviderImpl provider_impl(GetProxy(&provider)); sample::Enum value; provider->EchoEnum(sample::ENUM_VALUE, EnumRecorder(&value)); diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/sample_service_unittest.cc index ff047cb..5d1617f 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/sample_service_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/sample_service_unittest.cc @@ -256,7 +256,10 @@ void DumpHex(const uint8_t* bytes, uint32_t num_bytes) { class ServiceImpl : public Service { public: - void Frobinate(FooPtr foo, BazOptions baz, PortPtr port) override { + void Frobinate(FooPtr foo, + BazOptions baz, + PortPtr port, + const Service::FrobinateCallback& callback) override { // Users code goes here to handle the incoming Frobinate message. // We mainly check that we're given the expected arguments. @@ -273,6 +276,7 @@ class ServiceImpl : public Service { Print(depth, "baz", baz); Print(depth, "port", port.get()); } + callback.Run(5); } void GetPort(mojo::InterfaceRequest<Port> port_request) override {} @@ -336,7 +340,8 @@ TEST_F(BindingsSampleTest, Basic) { CheckFoo(*foo); PortPtr port; - service->Frobinate(foo.Pass(), Service::BAZ_OPTIONS_EXTRA, port.Pass()); + service->Frobinate(foo.Pass(), Service::BAZ_OPTIONS_EXTRA, port.Pass(), + Service::FrobinateCallback()); delete service; } diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc index 52d1313..7f5a376 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <string.h> + #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" #include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,6 +30,48 @@ void CheckRect(const Rect& rect, int32_t factor = 1) { EXPECT_EQ(20 * factor, rect.height); } +MultiVersionStructPtr MakeMultiVersionStruct() { + MultiVersionStructPtr output(MultiVersionStruct::New()); + output->f_int32 = 123; + output->f_rect = MakeRect(5); + output->f_string = "hello"; + output->f_array = Array<int8_t>(3); + output->f_array[0] = 10; + output->f_array[1] = 9; + output->f_array[2] = 8; + MessagePipe pipe; + output->f_message_pipe = pipe.handle0.Pass(); + output->f_int16 = 42; + + return output.Pass(); +} + +template <typename U, typename T> +U SerializeAndDeserialize(T input) { + typedef typename mojo::internal::WrapperTraits<T>::DataType InputDataType; + typedef typename mojo::internal::WrapperTraits<U>::DataType OutputDataType; + + size_t size = GetSerializedSize_(input); + mojo::internal::FixedBuffer buf(size + 32); + InputDataType data; + Serialize_(input.Pass(), &buf, &data); + + std::vector<Handle> handles; + data->EncodePointersAndHandles(&handles); + + // Set the subsequent area to a special value, so that we can find out if we + // mistakenly access the area. + void* subsequent_area = buf.Allocate(32); + memset(subsequent_area, 0xAA, 32); + + OutputDataType output_data = reinterpret_cast<OutputDataType>(data); + output_data->DecodePointersAndHandles(&handles); + + U output; + Deserialize_(output_data, &output); + return output.Pass(); +} + class StructTest : public testing::Test { public: ~StructTest() override {} @@ -197,5 +242,182 @@ TEST_F(StructTest, Serialization_NullArrayPointers) { EXPECT_TRUE(region2->rects.is_null()); } +// Tests deserializing structs as a newer version. +TEST_F(StructTest, Versioning_OldToNew) { + { + MultiVersionStructV0Ptr input(MultiVersionStructV0::New()); + input->f_int32 = 123; + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV1Ptr input(MultiVersionStructV1::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV3Ptr input(MultiVersionStructV3::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + input->f_string = "hello"; + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV5Ptr input(MultiVersionStructV5::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + input->f_string = "hello"; + input->f_array = Array<int8_t>(3); + input->f_array[0] = 10; + input->f_array[1] = 9; + input->f_array[2] = 8; + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructV7Ptr input(MultiVersionStructV7::New()); + input->f_int32 = 123; + input->f_rect = MakeRect(5); + input->f_string = "hello"; + input->f_array = Array<int8_t>(3); + input->f_array[0] = 10; + input->f_array[1] = 9; + input->f_array[2] = 8; + MessagePipe pipe; + input->f_message_pipe = pipe.handle0.Pass(); + + MultiVersionStructPtr expected_output(MultiVersionStruct::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + // Save the raw handle value separately so that we can compare later. + MojoHandle expected_handle = input->f_message_pipe.get().value(); + + MultiVersionStructPtr output = + SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_EQ(expected_handle, output->f_message_pipe.get().value()); + output->f_message_pipe.reset(); + EXPECT_TRUE(output->Equals(*expected_output)); + } +} + +// Tests deserializing structs as an older version. +TEST_F(StructTest, Versioning_NewToOld) { + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + // Save the raw handle value separately so that we can compare later. + MojoHandle expected_handle = input->f_message_pipe.get().value(); + + MultiVersionStructV7Ptr output = + SerializeAndDeserialize<MultiVersionStructV7Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_EQ(expected_handle, output->f_message_pipe.get().value()); + output->f_message_pipe.reset(); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + expected_output->f_array = Array<int8_t>(3); + expected_output->f_array[0] = 10; + expected_output->f_array[1] = 9; + expected_output->f_array[2] = 8; + + MultiVersionStructV5Ptr output = + SerializeAndDeserialize<MultiVersionStructV5Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV3Ptr expected_output(MultiVersionStructV3::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + expected_output->f_string = "hello"; + + MultiVersionStructV3Ptr output = + SerializeAndDeserialize<MultiVersionStructV3Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV1Ptr expected_output(MultiVersionStructV1::New()); + expected_output->f_int32 = 123; + expected_output->f_rect = MakeRect(5); + + MultiVersionStructV1Ptr output = + SerializeAndDeserialize<MultiVersionStructV1Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } + + { + MultiVersionStructPtr input = MakeMultiVersionStruct(); + MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New()); + expected_output->f_int32 = 123; + + MultiVersionStructV0Ptr output = + SerializeAndDeserialize<MultiVersionStructV0Ptr>(input.Pass()); + EXPECT_TRUE(output); + EXPECT_TRUE(output->Equals(*expected_output)); + } +} + } // namespace test } // namespace mojo diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc index f811a22..aba7faa 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc @@ -130,7 +130,8 @@ TEST(UnionTest, ValidationJustWorksPod) { internal::PodUnion_Data* data; Serialize_(pod.Pass(), &buf, &data); void* raw_buf = buf.Leak(); - mojo::internal::BoundsChecker bounds_checker(data, size, 0); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::PodUnion_Data::Validate(raw_buf, &bounds_checker)); free(raw_buf); } @@ -151,7 +152,8 @@ TEST(UnionTest, OutOfAlignmentValidation) { internal::PodUnion_Data* data = reinterpret_cast<internal::PodUnion_Data*>(buf); - mojo::internal::BoundsChecker bounds_checker(data, size, 0); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); EXPECT_FALSE(internal::PodUnion_Data::Validate(buf, &bounds_checker)); free(raw_buf); } @@ -161,7 +163,8 @@ TEST(UnionTest, OOBValidation) { size_t size = sizeof(internal::PodUnion_Data) - 1; mojo::internal::FixedBuffer buf(size); internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); - mojo::internal::BoundsChecker bounds_checker(data, size, 0); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::PodUnion_Data::Validate(raw_buf, &bounds_checker)); free(raw_buf); @@ -173,7 +176,8 @@ TEST(UnionTest, UnknownTagValidation) { mojo::internal::FixedBuffer buf(size); internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF); - mojo::internal::BoundsChecker bounds_checker(data, size, 0); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::PodUnion_Data::Validate(raw_buf, &bounds_checker)); free(raw_buf); @@ -237,7 +241,8 @@ TEST(UnionTest, StringValidationNull) { internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.unknown = 0x0; - mojo::internal::BoundsChecker bounds_checker(data, size, 0); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker)); free(raw_buf); @@ -250,7 +255,8 @@ TEST(UnionTest, StringValidationPointerOverflow) { internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.unknown = 0xFFFFFFFFFFFFFFFF; - mojo::internal::BoundsChecker bounds_checker(data, size, 0); + mojo::internal::BoundsChecker bounds_checker(data, + static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker)); free(raw_buf); diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_test_input_parser.h b/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_test_input_parser.h index c8821cd..d5f8c81 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_test_input_parser.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_test_input_parser.h @@ -82,7 +82,7 @@ namespace test { // The following describes a valid message whose payload is a Foo struct: // // message header // [dist4]message_header // num_bytes -// [u4]3 // num_fields +// [u4]3 // version // [u4]0 // type // [u4]1 // flags // [u8]1234 // request_id @@ -90,7 +90,7 @@ namespace test { // // // payload // [dist4]foo // num_bytes -// [u4]2 // num_fields +// [u4]2 // version // [dist8]bar_ptr // x // [u4]0xABCD // y // [u4]0 // padding @@ -98,7 +98,7 @@ namespace test { // // [anchr]bar_ptr // [dist4]bar // num_bytes -// [u4]3 // num_fields +// [u4]3 // version // [s4]-1 // a // [b]00000010 // b and c // 0 0 0 // padding diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc index 6507f21..f8eeae3 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc @@ -225,7 +225,9 @@ class ValidationIntegrationTest : public ValidationTest { public: TestMessageReceiver(ValidationIntegrationTest* owner, ScopedMessagePipeHandle handle) - : owner_(owner), connector_(handle.Pass()) {} + : owner_(owner), connector_(handle.Pass()) { + connector_.set_enforce_errors_from_incoming_receiver(false); + } ~TestMessageReceiver() override {} bool Accept(Message* message) override { @@ -246,19 +248,14 @@ class ValidationIntegrationTest : public ValidationTest { ScopedMessagePipeHandle testee_endpoint_; }; -class IntegrationTestInterface1Client : public IntegrationTestInterface1 { - public: - ~IntegrationTestInterface1Client() override {} - - void Method0(BasicStructPtr param0) override {} -}; - -class IntegrationTestInterface1Impl - : public InterfaceImpl<IntegrationTestInterface1> { +class IntegrationTestInterfaceImpl : public IntegrationTestInterface { public: - ~IntegrationTestInterface1Impl() override {} + ~IntegrationTestInterfaceImpl() override {} - void Method0(BasicStructPtr param0) override {} + void Method0(BasicStructPtr param0, + const Method0Callback& callback) override { + callback.Run(Array<uint8_t>::New(0u)); + } }; TEST_F(ValidationTest, InputParser) { @@ -383,35 +380,32 @@ TEST_F(ValidationTest, NotImplemented) { RunValidationTests("not_implemented_", validators.GetHead()); } +// Test that InterfacePtr<X> applies the correct validators and they don't +// conflict with each other: +// - MessageHeaderValidator +// - X::ResponseValidator_ TEST_F(ValidationIntegrationTest, InterfacePtr) { - // Test that InterfacePtr<X> applies the correct validators and they don't - // conflict with each other: - // - MessageHeaderValidator - // - X::Client::RequestValidator_ - // - X::ResponseValidator_ - - IntegrationTestInterface1Client interface1_client; - IntegrationTestInterface2Ptr interface2_ptr = - MakeProxy<IntegrationTestInterface2>(testee_endpoint().Pass()); - interface2_ptr.set_client(&interface1_client); - interface2_ptr.internal_state()->router_for_testing()->EnableTestingMode(); - - RunValidationTests("integration_", test_message_receiver()); -} + IntegrationTestInterfacePtr interface_ptr = + MakeProxy<IntegrationTestInterface>(testee_endpoint().Pass()); + interface_ptr.internal_state()->router_for_testing()->EnableTestingMode(); -TEST_F(ValidationIntegrationTest, InterfaceImpl) { - // Test that InterfaceImpl<X> applies the correct validators and they don't - // conflict with each other: - // - MessageHeaderValidator - // - X::RequestValidator_ - // - X::Client::ResponseValidator_ - - // |interface1_impl| will delete itself when the pipe is closed. - IntegrationTestInterface1Impl* interface1_impl = - BindToPipe(new IntegrationTestInterface1Impl(), testee_endpoint().Pass()); - interface1_impl->internal_router()->EnableTestingMode(); + RunValidationTests("integration_intf_resp", test_message_receiver()); + RunValidationTests("integration_msghdr", test_message_receiver()); +} - RunValidationTests("integration_", test_message_receiver()); +// Test that Binding<X> applies the correct validators and they don't +// conflict with each other: +// - MessageHeaderValidator +// - X::RequestValidator_ +TEST_F(ValidationIntegrationTest, Binding) { + IntegrationTestInterfaceImpl interface_impl; + Binding<IntegrationTestInterface> binding( + &interface_impl, + MakeRequest<IntegrationTestInterface>(testee_endpoint().Pass())); + binding.internal_router()->EnableTestingMode(); + + RunValidationTests("integration_intf_rqst", test_message_receiver()); + RunValidationTests("integration_msghdr", test_message_receiver()); } } // namespace diff --git a/third_party/mojo/src/mojo/public/dart/application.dart b/third_party/mojo/src/mojo/public/dart/application.dart index 80cc144..77afce9 100644 --- a/third_party/mojo/src/mojo/public/dart/application.dart +++ b/third_party/mojo/src/mojo/public/dart/application.dart @@ -11,8 +11,8 @@ import 'dart:mojo_core' as core; import 'dart:typed_data'; import 'package:mojo/public/interfaces/application/application.mojom.dart' as application_mojom; -import 'package:mojo/public/interfaces/application/service_provider.mojom.dart' as service_provider; +import 'package:mojo/public/interfaces/application/service_provider.mojom.dart'; import 'package:mojo/public/interfaces/application/shell.mojom.dart' as shell_mojom; part 'src/application.dart'; -part 'src/service_provider.dart'; +part 'src/application_connection.dart'; diff --git a/third_party/mojo/src/mojo/public/dart/rules.gni b/third_party/mojo/src/mojo/public/dart/rules.gni index aea59e3..5845f2e 100644 --- a/third_party/mojo/src/mojo/public/dart/rules.gni +++ b/third_party/mojo/src/mojo/public/dart/rules.gni @@ -101,12 +101,17 @@ template("dart_packaged_application") { datadeps = invoker.datadeps } + line = "#!mojo mojo:dart_content_handler" + if (defined(invoker.strict) && invoker.strict == true) { + line = "#!mojo mojo:dart_content_handler?strict=true" + } + rebase_input = rebase_path(input, root_build_dir) rebase_output = rebase_path(output, root_build_dir) args = [ "--input=$rebase_input", "--output=$rebase_output", - "--line=#!mojo mojo:dart_content_handler", + "--line=$line", ] } } diff --git a/third_party/mojo/src/mojo/public/dart/src/application.dart b/third_party/mojo/src/mojo/public/dart/src/application.dart index e5b9d4b..4be554d 100644 --- a/third_party/mojo/src/mojo/public/dart/src/application.dart +++ b/third_party/mojo/src/mojo/public/dart/src/application.dart @@ -19,16 +19,17 @@ class _ApplicationImpl extends application_mojom.Application { super.delegate = this; } - void initialize(shell_mojom.ShellProxy shellProxy, List<String> args) { + void initialize( + shell_mojom.ShellProxy shellProxy, List<String> args, String url) { assert(shell == null); shell = shellProxy; - _application.initialize(args); + _application.initialize(args, url); } void acceptConnection( String requestorUrl, - service_provider.ServiceProviderStub services, - service_provider.ServiceProviderProxy exposedServices) => + ServiceProviderStub services, + ServiceProviderProxy exposedServices) => _application._acceptConnection(requestorUrl, services, exposedServices); void requestQuit() => _application._requestQuitAndClose(); @@ -36,72 +37,46 @@ class _ApplicationImpl extends application_mojom.Application { void close({bool nodefer: false}) => shell.close(); } -// ApplicationConnection represents a single outgoing connection to another app. -class ApplicationConnection { - // ServiceProvider used to obtain services from the remote application. - service_provider.ServiceProviderProxy serviceProvider; - - ApplicationConnection(this.serviceProvider); - - // Obtains a service from the remote application. - void connectToService(bindings.Proxy proxy) { - assert(!proxy.isBound); - var applicationPipe = new core.MojoMessagePipe(); - var proxyEndpoint = applicationPipe.endpoints[0]; - var applicationEndpoint = applicationPipe.endpoints[1]; - proxy.bind(proxyEndpoint); - serviceProvider.connectToService(proxy.name, applicationEndpoint); - } -} - // TODO(zra): Better documentation and examples. // To implement, do the following: +// - Optionally override initialize() to process command-line args. // - Optionally override acceptConnection() if services are to be provided. -// The override should assign a factory function to the passed in -// ServiceProvider's |factory| field, and then call listen on the -// ServiceProvider. The factory function should take a MojoMessagePipeEndpoint -// and return an object that implements the requested interface. -// - Optionally override initialize() where needed. -// - Optionally override requestClose() to clean up state specific to your -// application. -// To use an Application: -// - Call listen() on a newly created Application to begin providing services. -// - Call connectToService() to request services from the Shell. -// - Call close() to close connections to any requested ServiceProviders and the -// Shell. +// - Optionally override close() to clean up application resources. abstract class Application { _ApplicationImpl _applicationImpl; List<ApplicationConnection> _applicationConnections; - List<ServiceProvider> _serviceProviders; Application(core.MojoMessagePipeEndpoint endpoint) { _applicationConnections = []; - _serviceProviders = []; _applicationImpl = new _ApplicationImpl(this, endpoint); } Application.fromHandle(core.MojoHandle appHandle) { _applicationConnections = []; - _serviceProviders = []; _applicationImpl = new _ApplicationImpl.fromHandle(this, appHandle); } - void initialize(List<String> args) {} + void initialize(List<String> args, String url) {} - // Establishes a connection to the app at |url|. + // TODO(skydart): This is a temporary fix to allow sky application to consume + // mojo services. Do not use for any other purpose. + void initializeFromShellProxy(shell_mojom.ShellProxy shellProxy, + List<String> args, String url) { + _applicationImpl.initialize(shellProxy, args, url); + } + + // Returns a connection to the app at |url|. ApplicationConnection connectToApplication(String url) { - var serviceProviderProxy = - new service_provider.ServiceProviderProxy.unbound(); - // TODO: Need to expose ServiceProvider for local services. - _applicationImpl.shell.connectToApplication( - url, serviceProviderProxy, null); - var applicationConnection = new ApplicationConnection(serviceProviderProxy); - _applicationConnections.add(applicationConnection); - return applicationConnection; + var proxy = new ServiceProviderProxy.unbound(); + var stub = new ServiceProviderStub.unbound(); + _applicationImpl.shell.connectToApplication(url, proxy, stub); + var connection = new ApplicationConnection(stub, proxy); + _applicationConnections.add(connection); + return connection; } void connectToService(String url, bindings.Proxy proxy) { - connectToApplication(url).connectToService(proxy); + connectToApplication(url).requestService(proxy); } void requestQuit() {} @@ -115,21 +90,23 @@ abstract class Application { void close() { assert(_applicationImpl != null); - _applicationConnections.forEach((c) => c.serviceProvider.close()); + _applicationConnections.forEach((c) => c.close()); _applicationConnections.clear(); - _serviceProviders.forEach((sp) => sp.close()); - _serviceProviders.clear(); _applicationImpl.close(); } void _acceptConnection( String requestorUrl, - service_provider.ServiceProviderStub services, - service_provider.ServiceProviderProxy exposedServices) { - var serviceProvider = new ServiceProvider(services, exposedServices); - _serviceProviders.add(serviceProvider); - acceptConnection(requestorUrl, serviceProvider); + ServiceProviderStub services, + ServiceProviderProxy exposedServices) { + var connection = new ApplicationConnection(services, exposedServices); + _applicationConnections.add(connection); + acceptConnection(requestorUrl, connection); } - void acceptConnection(String requestorUrl, ServiceProvider serviceProvider) {} + // Override this method to provide services on |connection|. + // If you provide at least one service or set fallbackServiceProvider, + // then you must invoke connection.listen(). + void acceptConnection(String requestorUrl, ApplicationConnection connection) { + } } diff --git a/third_party/mojo/src/mojo/public/dart/src/application_connection.dart b/third_party/mojo/src/mojo/public/dart/src/application_connection.dart new file mode 100644 index 0000000..4f31468 --- /dev/null +++ b/third_party/mojo/src/mojo/public/dart/src/application_connection.dart @@ -0,0 +1,105 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of application; + +typedef core.Listener ServiceFactory(core.MojoMessagePipeEndpoint endpoint); +typedef void FallbackServiceFactory(String interfaceName, + core.MojoMessagePipeEndpoint endpoint); + + +class LocalServiceProvider extends ServiceProvider { + final ApplicationConnection connection; + + LocalServiceProvider(ApplicationConnection this.connection, + ServiceProviderStub stub) + : super.fromStub(stub) { + delegate = this; + } + + void connectToService(String interfaceName, + core.MojoMessagePipeEndpoint pipe) { + if (connection._nameToServiceFactory.containsKey(interfaceName)) { + var listener = connection._nameToServiceFactory[interfaceName](pipe); + if (listener != null) { + listener.listen(); + return; + } + } + if (connection.fallbackServiceFactory != null) { + connection.fallbackServiceFactory(interfaceName, pipe); + return; + } + // The specified interface isn't provided. Close the pipe so the + // remote endpoint sees that we don't support this interface. + pipe.handle.close(); + } +} + +// Encapsulates a pair of ServiceProviders that enable services (interface +// implementations) to be provided to a remote application as well as exposing +// services provided by a remote application. ApplicationConnection +// objects are returned by the Application ConnectToApplication() method +// and they're passed to the Application AcceptConnection() method. +// +// To request a service from the remote application: +// var proxy = applicationConnection.requestService(ViewManagerClient.name); +// +// To provide a service to the remote application, specify a function that +// returns a service. For example: +// applicationConnection.provideService(ViewManagerClient.name, (pipe) => +// new ViewManagerClientImpl(pipe)); +// +// To handle requests for any interface, set fallbackServiceFactory to a +// function that takes ownership of the incoming message pipe endpoint. If the +// fallbackServiceFactory function doesn't bind the pipe, it should close it. +// +// The fallbackServiceFactory is only used if a service wasn't specified +// with provideService(). + +class ApplicationConnection { + ServiceProviderProxy remoteServiceProvider; + LocalServiceProvider _localServiceProvider; + final _nameToServiceFactory = new Map<String, ServiceFactory>(); + FallbackServiceFactory fallbackServiceFactory; + + ApplicationConnection(ServiceProviderStub stub, ServiceProviderProxy proxy) + : remoteServiceProvider = proxy { + if (stub != null) _localServiceProvider = + new LocalServiceProvider(this, stub); + } + + bindings.Proxy requestService(bindings.Proxy proxy) { + assert(!proxy.isBound && + (remoteServiceProvider != null) && + remoteServiceProvider.isBound); + var applicationPipe = new core.MojoMessagePipe(); + var proxyEndpoint = applicationPipe.endpoints[0]; + var applicationEndpoint = applicationPipe.endpoints[1]; + proxy.bind(proxyEndpoint); + remoteServiceProvider.connectToService(proxy.name, applicationEndpoint); + return proxy; + } + + void provideService(String interfaceName, ServiceFactory factory) { + assert(_localServiceProvider != null); + _nameToServiceFactory[interfaceName] = factory; + } + + void listen() { + if (_localServiceProvider != null) _localServiceProvider.listen(); + } + + void close({bool nodefer: false}) { + if (remoteServiceProvider != null) { + remoteServiceProvider.close(); + remoteServiceProvider = null; + } + if (_localServiceProvider != null) { + _localServiceProvider.close(nodefer: nodefer); + _localServiceProvider = null; + } + + } +} diff --git a/third_party/mojo/src/mojo/public/dart/src/codec.dart b/third_party/mojo/src/mojo/public/dart/src/codec.dart index 268daee..677dc10 100644 --- a/third_party/mojo/src/mojo/public/dart/src/codec.dart +++ b/third_party/mojo/src/mojo/public/dart/src/codec.dart @@ -9,7 +9,9 @@ int align(int size) => size + (kAlignment - (size % kAlignment)) % kAlignment; const int kAlignment = 8; const int kSerializedHandleSize = 4; const int kPointerSize = 8; -const DataHeader kMapStructHeader = const DataHeader(24, 2); +// TODO(yzshen): In order to work with other bindings which still interprets +// the |version| field as |num_fields|, set it to version 2 for now. +const StructDataHeader kMapStructHeader = const StructDataHeader(24, 2); const int kUnspecifiedArrayLength = -1; const int kNothingNullable = 0; const int kArrayNullable = (1 << 0); @@ -18,6 +20,30 @@ const int kElementNullable = (1 << 1); bool isArrayNullable(int nullability) => (nullability & kArrayNullable) > 0; bool isElementNullable(int nullability) => (nullability & kElementNullable) > 0; +class StructDataHeader { + static const int kHeaderSize = 8; + static const int kSizeOffset = 0; + static const int kVersionOffset = 4; + final int size; + final int version; + + const StructDataHeader(this.size, this.version); + + String toString() => "StructDataHeader($size, $version)"; +} + +class ArrayDataHeader { + static const int kHeaderSize = 8; + static const int kSizeOffset = 0; + static const int kNumElementsOffset = 4; + final int size; + final int numElements; + + const ArrayDataHeader(this.size, this.numElements); + + String toString() => "ArrayDataHeader($size, $numElements)"; +} + class MojoCodecError { final String message; MojoCodecError(this.message); @@ -64,18 +90,30 @@ class Encoder { _buffer = buffer, _base = buffer.extent; - Encoder getEncoderAtOffset(DataHeader dataHeader) { + Encoder getStructEncoderAtOffset(StructDataHeader dataHeader) { var result = new Encoder._fromBuffer(_buffer); - result.encodeDataHeader(dataHeader); + result.encodeStructDataHeader(dataHeader); + return result; + } + + Encoder getArrayEncoderAtOffset(ArrayDataHeader dataHeader) { + var result = new Encoder._fromBuffer(_buffer); + result.encodeArrayDataHeader(dataHeader); return result; } Message get message => new Message(_buffer.trimmed, _buffer.handles); - void encodeDataHeader(DataHeader dataHeader) { + void encodeStructDataHeader(StructDataHeader dataHeader) { _buffer.claimMemory(align(dataHeader.size)); - encodeUint32(dataHeader.size, DataHeader.kSizeOffset); - encodeUint32(dataHeader.numFields, DataHeader.kNumFieldsOffset); + encodeUint32(dataHeader.size, StructDataHeader.kSizeOffset); + encodeUint32(dataHeader.version, StructDataHeader.kVersionOffset); + } + + void encodeArrayDataHeader(ArrayDataHeader dataHeader) { + _buffer.claimMemory(align(dataHeader.size)); + encodeUint32(dataHeader.size, ArrayDataHeader.kSizeOffset); + encodeUint32(dataHeader.numElements, ArrayDataHeader.kNumElementsOffset); } static const String kErrorUnsigned = @@ -223,8 +261,8 @@ class Encoder { Encoder encoderForArrayByTotalSize(int size, int length, int offset) { encodePointerToNextUnclaimed(offset); - return getEncoderAtOffset( - new DataHeader(DataHeader.kHeaderSize + size, length)); + return getArrayEncoderAtOffset( + new ArrayDataHeader(ArrayDataHeader.kHeaderSize + size, length)); } void encodeBoolArray( @@ -329,7 +367,8 @@ class Encoder { var encoder = encoderForArray( kSerializedHandleSize, value.length, offset, expectedLength); for (int i = 0; i < value.length; ++i) { - int handleOffset = DataHeader.kHeaderSize + kSerializedHandleSize * i; + int handleOffset = + ArrayDataHeader.kHeaderSize + kSerializedHandleSize * i; elementEncoder( encoder, value[i], handleOffset, isElementNullable(nullability)); } @@ -415,8 +454,8 @@ class Encoder { void appendBytes(Uint8List value) { _buffer.buffer.buffer.asUint8List().setRange( - _base + DataHeader.kHeaderSize, - _base + DataHeader.kHeaderSize + value.lengthInBytes, + _base + ArrayDataHeader.kHeaderSize, + _base + ArrayDataHeader.kHeaderSize + value.lengthInBytes, value); } @@ -452,7 +491,7 @@ class Encoder { Encoder encoderForMap(int offset) { encodePointerToNextUnclaimed(offset); - return getEncoderAtOffset(kMapStructHeader); + return getStructEncoderAtOffset(kMapStructHeader); } } @@ -588,31 +627,47 @@ class Decoder { return new Decoder.atOffset(this, newPosition, _validator); } - DataHeader decodeDataHeader() { - _validator.claimMemory(_base, _base + DataHeader.kHeaderSize); - int size = decodeUint32(DataHeader.kSizeOffset); - int numFields = decodeUint32(DataHeader.kNumFieldsOffset); + StructDataHeader decodeStructDataHeader() { + _validator.claimMemory(_base, _base + StructDataHeader.kHeaderSize); + int size = decodeUint32(StructDataHeader.kSizeOffset); + int version = decodeUint32(StructDataHeader.kVersionOffset); + if (size < 0) { + throw new MojoCodecError('Negative size.'); + } + if (version < 0) { + throw new MojoCodecError('Negative version number.'); + } + _validator.claimMemory(_base + StructDataHeader.kHeaderSize, _base + size); + return new StructDataHeader(size, version); + } + + ArrayDataHeader decodeArrayDataHeader() { + _validator.claimMemory(_base, _base + ArrayDataHeader.kHeaderSize); + int size = decodeUint32(ArrayDataHeader.kSizeOffset); + int numElements = decodeUint32(ArrayDataHeader.kNumElementsOffset); if (size < 0) { throw new MojoCodecError('Negative size.'); } - if (numFields < 0) { - throw new MojoCodecError('Negative number of fields.'); + if (numElements < 0) { + throw new MojoCodecError('Negative number of elements.'); } - _validator.claimMemory(_base + DataHeader.kHeaderSize, _base + size); - return new DataHeader(size, numFields); + _validator.claimMemory(_base + ArrayDataHeader.kHeaderSize, _base + size); + return new ArrayDataHeader(size, numElements); } // Decode arrays. - DataHeader decodeDataHeaderForBoolArray(int expectedLength) { - var header = decodeDataHeader(); - if (header.size < DataHeader.kHeaderSize + (header.numFields + 7) ~/ 8) { + ArrayDataHeader decodeDataHeaderForBoolArray(int expectedLength) { + var header = decodeArrayDataHeader(); + var arrayByteCount = + ArrayDataHeader.kHeaderSize + (header.numElements + 7) ~/ 8; + if (header.size < arrayByteCount) { throw new MojoCodecError('Array header is incorrect'); } if ((expectedLength != kUnspecifiedArrayLength) && - (header.numFields != expectedLength)) { + (header.numElements != expectedLength)) { throw new MojoCodecError( 'Incorrect array length. Expected $expectedLength, but got ' - '${header.numFields}.'); + '${header.numElements}.'); } return header; } @@ -625,9 +680,9 @@ class Decoder { var header = d.decodeDataHeaderForBoolArray(expectedLength); var bytes = new Uint8List.view( d._buffer.buffer, - d._buffer.offsetInBytes + d._base + DataHeader.kHeaderSize, - (header.numFields + 7) ~/ kAlignment); - var result = new List<bool>(header.numFields); + d._buffer.offsetInBytes + d._base + ArrayDataHeader.kHeaderSize, + (header.numElements + 7) ~/ kAlignment); + var result = new List<bool>(header.numElements); for (int i = 0; i < bytes.lengthInBytes; ++i) { for (int j = 0; j < kAlignment; ++j) { int boolIndex = i * kAlignment + j; @@ -639,22 +694,25 @@ class Decoder { return result; } - DataHeader decodeDataHeaderForArray(int elementSize, int expectedLength) { - var header = decodeDataHeader(); - if (header.size < DataHeader.kHeaderSize + header.numFields * elementSize) { + ArrayDataHeader decodeDataHeaderForArray(int elementSize, + int expectedLength) { + var header = decodeArrayDataHeader(); + var arrayByteCount = + ArrayDataHeader.kHeaderSize + header.numElements * elementSize; + if (header.size < arrayByteCount) { throw new MojoCodecError( 'Array header is incorrect: $header, elementSize = $elementSize'); } if ((expectedLength != kUnspecifiedArrayLength) && - (header.numFields != expectedLength)) { + (header.numElements != expectedLength)) { throw new MojoCodecError( 'Incorrect array length. Expected $expectedLength, but got ' - '${header.numFields}'); + '${header.numElements}'); } return header; } - DataHeader decodeDataHeaderForPointerArray(int expectedLength) => + ArrayDataHeader decodeDataHeaderForPointerArray(int expectedLength) => decodeDataHeaderForArray(kPointerSize, expectedLength); List decodeArray(Function arrayViewer, @@ -669,8 +727,8 @@ class Decoder { var header = d.decodeDataHeaderForArray(elementSize, expectedLength); return arrayViewer( d._buffer.buffer, - d._buffer.offsetInBytes + d._base + DataHeader.kHeaderSize, - header.numFields); + d._buffer.offsetInBytes + d._base + ArrayDataHeader.kHeaderSize, + header.numElements); } List<int> decodeInt8Array( @@ -732,11 +790,11 @@ class Decoder { return null; } var header = d.decodeDataHeaderForArray(4, expectedLength); - var result = new List(header.numFields); + var result = new List(header.numElements); for (int i = 0; i < result.length; ++i) { result[i] = elementDecoder( d, - DataHeader.kHeaderSize + kSerializedHandleSize * i, + ArrayDataHeader.kHeaderSize + kSerializedHandleSize * i, isElementNullable(nullability)); } return result; @@ -798,15 +856,15 @@ class Decoder { return _stringOfUtf8(bytes); } - DataHeader decodeDataHeaderForMap() { - var header = decodeDataHeader(); + StructDataHeader decodeDataHeaderForMap() { + var header = decodeStructDataHeader(); if (header.size != kMapStructHeader.size) { throw new MojoCodecError( 'Incorrect header for map. The size is incorrect.'); } - if (header.numFields != kMapStructHeader.numFields) { + if (header.version != kMapStructHeader.version) { throw new MojoCodecError( - 'Incorrect header for map. The number of fields is incorrect.'); + 'Incorrect header for map. The version is incorrect.'); } return header; } diff --git a/third_party/mojo/src/mojo/public/dart/src/drain_data.dart b/third_party/mojo/src/mojo/public/dart/src/drain_data.dart index ffc56e5..b5929e5 100644 --- a/third_party/mojo/src/mojo/public/dart/src/drain_data.dart +++ b/third_party/mojo/src/mojo/public/dart/src/drain_data.dart @@ -16,12 +16,16 @@ class DataPipeDrainer { _dataSize = 0; } + ByteData _copy(ByteData byteData) => + new ByteData.view( + new Uint8List.fromList(byteData.buffer.asUint8List()).buffer); + MojoResult _doRead() { ByteData thisRead = _consumer.beginRead(); if (thisRead == null) { throw 'Data pipe beginRead failed: ${_consumer.status}'; } - _dataList.add(thisRead); + _dataList.add(_copy(thisRead)); _dataSize += thisRead.lengthInBytes; return _consumer.endRead(thisRead.lengthInBytes); } diff --git a/third_party/mojo/src/mojo/public/dart/src/message.dart b/third_party/mojo/src/mojo/public/dart/src/message.dart index 918d219..9c7cf46 100644 --- a/third_party/mojo/src/mojo/public/dart/src/message.dart +++ b/third_party/mojo/src/mojo/public/dart/src/message.dart @@ -6,16 +6,20 @@ part of bindings; class MessageHeader { static const int kSimpleMessageSize = 16; - static const int kSimpleMessageNumFields = 2; + // TODO(yzshen): In order to work with other bindings which still interprets + // the |version| field as |num_fields|, set it to version 2 for now. + static const int kSimpleMessageVersion = 2; static const int kMessageWithRequestIdSize = 24; - static const int kMessageWithRequestIdNumFields = 3; - static const int kMessageTypeOffset = DataHeader.kHeaderSize; + // TODO(yzshen): In order to work with other bindings which still interprets + // the |version| field as |num_fields|, set it to version 3 for now. + static const int kMessageWithRequestIdVersion = 3; + static const int kMessageTypeOffset = StructDataHeader.kHeaderSize; static const int kMessageFlagsOffset = kMessageTypeOffset + 4; static const int kMessageRequestIdOffset = kMessageFlagsOffset + 4; static const int kMessageExpectsResponse = 1 << 0; static const int kMessageIsResponse = 1 << 1; - DataHeader _header; + StructDataHeader _header; int type; int flags; int requestId; @@ -24,17 +28,17 @@ class MessageHeader { (flags & (kMessageExpectsResponse | kMessageIsResponse)) != 0; MessageHeader(this.type) : - _header = new DataHeader(kSimpleMessageSize, kSimpleMessageNumFields), + _header = new StructDataHeader(kSimpleMessageSize, kSimpleMessageVersion), flags = 0, requestId = 0; MessageHeader.withRequestId(this.type, this.flags, this.requestId) : - _header = new DataHeader( - kMessageWithRequestIdSize, kMessageWithRequestIdNumFields); + _header = new StructDataHeader( + kMessageWithRequestIdSize, kMessageWithRequestIdVersion); MessageHeader.fromMessage(Message message) { var decoder = new Decoder(message); - _header = decoder.decodeDataHeader(); + _header = decoder.decodeStructDataHeader(); if (_header.size < kSimpleMessageSize) { throw new MojoCodecError( 'Incorrect message size. Got: ${_header.size} ' @@ -58,7 +62,7 @@ class MessageHeader { bool get hasRequestId => mustHaveRequestId(flags); void encode(Encoder encoder) { - encoder.encodeDataHeader(_header); + encoder.encodeStructDataHeader(_header); encoder.encodeUint32(type, kMessageTypeOffset); encoder.encodeUint32(flags, kMessageFlagsOffset); if (hasRequestId) { @@ -76,25 +80,25 @@ class MessageHeader { bool validateHeader(int expectedType, int expectedFlags) => (type == expectedType) && validateHeaderFlags(expectedFlags); - static void _validateDataHeader(DataHeader dataHeader) { - if (dataHeader.numFields < kSimpleMessageNumFields) { - throw 'Incorrect number of fields, expecting at least ' - '$kSimpleMessageNumFields, but got: ${dataHeader.numFields}.'; + static void _validateDataHeader(StructDataHeader dataHeader) { + if (dataHeader.version < kSimpleMessageVersion) { + throw 'Incorrect version, expecting at least ' + '$kSimpleMessageVersion, but got: ${dataHeader.version}.'; } if (dataHeader.size < kSimpleMessageSize) { throw 'Incorrect message size, expecting at least $kSimpleMessageSize, ' 'but got: ${dataHeader.size}'; } - if ((dataHeader.numFields == kSimpleMessageSize) && + if ((dataHeader.version == kSimpleMessageVersion) && (dataHeader.size != kSimpleMessageSize)) { - throw 'Incorrect message size for a message with $kSimpleMessageNumFields' - ' fields, expecting $kSimpleMessageSize, ' + throw 'Incorrect message size for a message of version ' + '$kSimpleMessageVersion, expecting $kSimpleMessageSize, ' 'but got ${dataHeader.size}'; } - if ((dataHeader.numFields == kMessageWithRequestIdNumFields) && + if ((dataHeader.version == kMessageWithRequestIdVersion) && (dataHeader.size != kMessageWithRequestIdSize)) { - throw 'Incorrect message size for a message with ' - '$kMessageWithRequestIdNumFields fields, expecting ' + throw 'Incorrect message size for a message of version ' + '$kMessageWithRequestIdVersion, expecting ' '$kMessageWithRequestIdSize, but got ${dataHeader.size}'; } } diff --git a/third_party/mojo/src/mojo/public/dart/src/proxy.dart b/third_party/mojo/src/mojo/public/dart/src/proxy.dart index 1186232..f1df3c7 100644 --- a/third_party/mojo/src/mojo/public/dart/src/proxy.dart +++ b/third_party/mojo/src/mojo/public/dart/src/proxy.dart @@ -50,7 +50,7 @@ abstract class Proxy extends core.MojoEventStreamListener { serviceMessage.buffer.lengthInBytes, serviceMessage.handles); if (!endpoint.status.isOk) { - throw "message pipe write failed"; + throw "message pipe write failed - ${endpoint.status}"; } } @@ -69,7 +69,7 @@ abstract class Proxy extends core.MojoEventStreamListener { serviceMessage.buffer.lengthInBytes, serviceMessage.handles); if (!endpoint.status.isOk) { - throw "message pipe write failed"; + throw "message pipe write failed - ${endpoint.status}"; } var completer = new Completer(); diff --git a/third_party/mojo/src/mojo/public/dart/src/service_provider.dart b/third_party/mojo/src/mojo/public/dart/src/service_provider.dart deleted file mode 100644 index 6b07f2e..0000000 --- a/third_party/mojo/src/mojo/public/dart/src/service_provider.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -part of application; - -typedef core.Listener ListenerFactory(core.MojoMessagePipeEndpoint endpoint); - -class ServiceProvider extends service_provider.ServiceProvider { - ListenerFactory factory; - - service_provider.ServiceProviderProxy _proxy; - - ServiceProvider( - service_provider.ServiceProviderStub services, - [service_provider.ServiceProviderProxy exposedServices = null]) - : _proxy = exposedServices, - super.fromStub(services) { - delegate = this; - } - - connectToService(String interfaceName, core.MojoMessagePipeEndpoint pipe) => - factory(pipe).listen(); - - requestService(String name, bindings.Proxy clientImpl) { - assert(_proxy != null); - assert(!clientImpl.isBound); - var pipe = new core.MojoMessagePipe(); - clientImpl.bind(pipe.endpoints[0]); - _proxy.connectToService(name, pipe.endpoints[1]); - } - - close({bool nodefer : false}) { - if (_proxy != null) { - _proxy.close(); - _proxy = null; - } - super.close(nodefer: nodefer); - } -} diff --git a/third_party/mojo/src/mojo/public/dart/src/struct.dart b/third_party/mojo/src/mojo/public/dart/src/struct.dart index 0111732..d4ac365 100644 --- a/third_party/mojo/src/mojo/public/dart/src/struct.dart +++ b/third_party/mojo/src/mojo/public/dart/src/struct.dart @@ -4,18 +4,6 @@ part of bindings; -class DataHeader { - static const int kHeaderSize = 8; - static const int kSizeOffset = 0; - static const int kNumFieldsOffset = 4; - final int size; - final int numFields; - - const DataHeader(this.size, this.numFields); - - String toString() => "DataHeader($size, $numFields)"; -} - abstract class Struct { final int encodedSize; diff --git a/third_party/mojo/src/mojo/public/go/bindings/async_waiter.go b/third_party/mojo/src/mojo/public/go/bindings/async_waiter.go new file mode 100644 index 0000000..15fd822 --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/bindings/async_waiter.go @@ -0,0 +1,266 @@ +// 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. + +package bindings + +import ( + "fmt" + "sync" + "sync/atomic" + + "mojo/public/go/system" +) + +var waiter *asyncWaiterImpl +var once sync.Once + +// GetAsyncWaiter returns a default implementation of |AsyncWaiter| interface. +func GetAsyncWaiter() AsyncWaiter { + once.Do(func() { + waiter = newAsyncWaiter() + }) + return waiter +} + +// AsyncWaitId is an id returned by |AsyncWait()| used to cancel it. +type AsyncWaitId uint64 + +// WaitResponse is a struct sent to a channel waiting for |AsyncWait()| to +// finish. It contains the same information as if |Wait()| was called on a +// handle. +type WaitResponse struct { + Result system.MojoResult + State system.MojoHandleSignalsState +} + +// AsyncWaiter defines an interface for asynchronously waiting (and cancelling +// asynchronous waits) on a handle. +type AsyncWaiter interface { + // AsyncWait asynchronously waits on a given handle until a signal + // indicated by |signals| is satisfied or it becomes known that no + // signal indicated by |signals| will ever be satisified. The wait + // response will be sent to |responseChan|. + // + // |handle| must not be closed or transferred until the wait response + // is received from |responseChan|. + AsyncWait(handle system.Handle, signals system.MojoHandleSignals, responseChan chan<- WaitResponse) AsyncWaitId + + // CancelWait cancels an outstanding async wait (specified by |id|) + // initiated by |AsyncWait()|. A response with Mojo result + // |MOJO_RESULT_ABORTED| is sent to the corresponding |responseChan|. + CancelWait(id AsyncWaitId) +} + +// waitRequest is a struct sent to asyncWaiterWorker to add another handle to +// the list of waiting handles. +type waitRequest struct { + handle system.Handle + signals system.MojoHandleSignals + + // Used for |CancelWait()| calls. The worker should issue IDs so that + // you can't cancel the wait until the worker received the wait request. + idChan chan<- AsyncWaitId + + // A channel end to send wait results. + responseChan chan<- WaitResponse +} + +// asyncWaiterWorker does the actual work, in its own goroutine. It calls +// |WaitMany()| on all provided handles. New handles a added via |waitChan| +// and removed via |cancelChan| messages. To wake the worker asyncWaiterImpl +// sends mojo messages to a dedicated message pipe, the other end of which has +// index 0 in all slices of the worker. +type asyncWaiterWorker struct { + // |handles| and |signals| are used to make |WaitMany()| calls directly. + // All these arrays should be operated simultaneously; i-th element + // of each refers to i-th handle. + handles []system.Handle + signals []system.MojoHandleSignals + asyncWaitIds []AsyncWaitId + responses []chan<- WaitResponse + + // Flag shared between waiterImpl and worker that is 1 iff the worker is + // already notified by waiterImpl. The worker sets it to 0 as soon as + // |WaitMany()| succeeds. + isNotified *int32 + waitChan <-chan waitRequest // should have a non-empty buffer + cancelChan <-chan AsyncWaitId // should have a non-empty buffer + lastUsedId AsyncWaitId // is incremented each |AsyncWait()| call +} + +// removeHandle removes handle at provided index without sending response by +// swapping all information associated with index-th handle with the last one +// and removing the last one. +func (w *asyncWaiterWorker) removeHandle(index int) { + l := len(w.handles) - 1 + // Swap with the last and remove last. + w.handles[index] = w.handles[l] + w.handles = w.handles[0:l] + w.signals[index] = w.signals[l] + w.signals = w.signals[0:l] + + w.asyncWaitIds[index] = w.asyncWaitIds[l] + w.asyncWaitIds = w.asyncWaitIds[0:l] + w.responses[index] = w.responses[l] + w.responses = w.responses[0:l] +} + +// sendWaitResponseAndRemove send response to corresponding channel and removes +// index-th waiting handle. +func (w *asyncWaiterWorker) sendWaitResponseAndRemove(index int, result system.MojoResult, state system.MojoHandleSignalsState) { + w.responses[index] <- WaitResponse{ + result, + state, + } + w.removeHandle(index) +} + +// respondToSatisfiedWaits responds to all wait requests that have at least +// one satisfied signal and removes them. +func (w *asyncWaiterWorker) respondToSatisfiedWaits(states []system.MojoHandleSignalsState) { + // Don't touch handle at index 0 as it is the waking handle. + for i := 1; i < len(states); { + if (states[i].SatisfiedSignals & w.signals[i]) != 0 { + // Respond and swap i-th with last and remove last. + w.sendWaitResponseAndRemove(i, system.MOJO_RESULT_OK, states[i]) + // Swap i-th with last and remove last. + states[i] = states[len(states)-1] + states = states[:len(states)-1] + } else { + i++ + } + } +} + +// processIncomingRequests processes all queued async wait or cancel requests +// sent by asyncWaiterImpl. +func (w *asyncWaiterWorker) processIncomingRequests() { + for { + select { + case request := <-w.waitChan: + w.handles = append(w.handles, request.handle) + w.signals = append(w.signals, request.signals) + w.responses = append(w.responses, request.responseChan) + + w.lastUsedId++ + id := w.lastUsedId + w.asyncWaitIds = append(w.asyncWaitIds, id) + request.idChan <- id + case AsyncWaitId := <-w.cancelChan: + // Zero index is reserved for the waking message pipe handle. + index := 0 + for i := 1; i < len(w.asyncWaitIds); i++ { + if w.asyncWaitIds[i] == AsyncWaitId { + index = i + break + } + } + // Do nothing if the id was not found as wait response may be + // already sent if the async wait was successful. + if index > 0 { + w.sendWaitResponseAndRemove(index, system.MOJO_RESULT_ABORTED, system.MojoHandleSignalsState{}) + } + default: + return + } + } +} + +// runLoop run loop of the asyncWaiterWorker. Blocks on |WaitMany()|. If the +// wait is interrupted by waking handle (index 0) then it means that the worker +// was woken by waiterImpl, so the worker processes incoming requests from +// waiterImpl; otherwise responses to corresponding wait request. +func (w *asyncWaiterWorker) runLoop() { + for { + result, index, states := system.GetCore().WaitMany(w.handles, w.signals, system.MOJO_DEADLINE_INDEFINITE) + // Set flag to 0, so that the next incoming request to + // waiterImpl would explicitly wake worker by sending a message + // to waking message pipe. + atomic.StoreInt32(w.isNotified, 0) + if index == -1 { + panic(fmt.Sprintf("error waiting on handles: %v", result)) + break + } + // Zero index means that the worker was signaled by asyncWaiterImpl. + if index == 0 { + if result != system.MOJO_RESULT_OK { + panic(fmt.Sprintf("error waiting on waking handle: %v", result)) + } + w.handles[0].(system.MessagePipeHandle).ReadMessage(system.MOJO_READ_MESSAGE_FLAG_NONE) + w.processIncomingRequests() + } else if result != system.MOJO_RESULT_OK { + w.sendWaitResponseAndRemove(index, result, system.MojoHandleSignalsState{}) + } else { + w.respondToSatisfiedWaits(states) + } + } +} + +// asyncWaiterImpl is an implementation of |AsyncWaiter| interface. +// Runs a worker in a separate goroutine and comunicates with it by sending a +// message to |wakingHandle| to wake worker from |WaitMany()| call and +// sending request via |waitChan| and |cancelChan|. +type asyncWaiterImpl struct { + wakingHandle system.MessagePipeHandle + + // Flag shared between waiterImpl and worker that is 1 iff the worker is + // already notified by waiterImpl. The worker sets it to 0 as soon as + // |WaitMany()| succeeds. + isWorkerNotified *int32 + waitChan chan<- waitRequest // should have a non-empty buffer + cancelChan chan<- AsyncWaitId // should have a non-empty buffer +} + +// newAsyncWaiter creates an asyncWaiterImpl and starts its worker goroutine. +func newAsyncWaiter() *asyncWaiterImpl { + result, h0, h1 := system.GetCore().CreateMessagePipe(nil) + if result != system.MOJO_RESULT_OK { + panic(fmt.Sprintf("can't create message pipe %v", result)) + } + waitChan := make(chan waitRequest, 10) + cancelChan := make(chan AsyncWaitId, 10) + isNotified := new(int32) + worker := asyncWaiterWorker{ + []system.Handle{h1}, + []system.MojoHandleSignals{system.MOJO_HANDLE_SIGNAL_READABLE}, + []AsyncWaitId{0}, + []chan<- WaitResponse{make(chan WaitResponse)}, + isNotified, + waitChan, + cancelChan, + 0, + } + go worker.runLoop() + return &asyncWaiterImpl{ + wakingHandle: h0, + isWorkerNotified: isNotified, + waitChan: waitChan, + cancelChan: cancelChan, + } +} + +// wakeWorker wakes the worker from |WaitMany()| call. This should be called +// after sending a message to |waitChan| or |cancelChan| to avoid deadlock. +func (w *asyncWaiterImpl) wakeWorker() { + if atomic.CompareAndSwapInt32(w.isWorkerNotified, 0, 1) { + w.wakingHandle.WriteMessage([]byte{0}, nil, system.MOJO_WRITE_MESSAGE_FLAG_NONE) + } +} + +func (w *asyncWaiterImpl) AsyncWait(handle system.Handle, signals system.MojoHandleSignals, responseChan chan<- WaitResponse) AsyncWaitId { + idChan := make(chan AsyncWaitId, 1) + w.waitChan <- waitRequest{ + handle, + signals, + idChan, + responseChan, + } + w.wakeWorker() + return <-idChan +} + +func (w *asyncWaiterImpl) CancelWait(id AsyncWaitId) { + w.cancelChan <- id + w.wakeWorker() +} diff --git a/third_party/mojo/src/mojo/public/go/bindings/decoder.go b/third_party/mojo/src/mojo/public/go/bindings/decoder.go index 3f3cce9..6f6b433 100644 --- a/third_party/mojo/src/mojo/public/go/bindings/decoder.go +++ b/third_party/mojo/src/mojo/public/go/bindings/decoder.go @@ -68,11 +68,15 @@ func (d *Decoder) pushState(header DataHeader, elementBitSize uint32) error { if err := d.claimData(int(header.Size - dataHeaderSize)); err != nil { return err } + elements := uint32(0) + if elementBitSize != 0 { + elements = header.ElementsOrVersion + } d.stateStack = append(d.stateStack, encodingState{ offset: oldEnd, limit: d.end, elementBitSize: elementBitSize, - elements: header.Elements, + elements: elements, }) return nil } @@ -94,13 +98,14 @@ func (d *Decoder) StartArray(elementBitSize uint32) (uint32, error) { if err != nil { return 0, err } - if got, want := int(header.Size), dataHeaderSize+bytesForBits(uint64(header.Elements)*uint64(elementBitSize)); got < want { + minSize := bytesForBits(uint64(header.ElementsOrVersion) * uint64(elementBitSize)) + if got, want := int(header.Size), dataHeaderSize+minSize; got < want { return 0, fmt.Errorf("data header size is too small: is %d, but should be at least %d", got, want) } if err := d.pushState(header, elementBitSize); err != nil { return 0, err } - return header.Elements, nil + return header.ElementsOrVersion, nil } // StartMap starts decoding a map and reads its data header. @@ -120,8 +125,8 @@ func (d *Decoder) StartMap() error { return nil } -// StartArray starts decoding a struct and reads its data header, -// returning number of fields declared in data header. +// StartStruct starts decoding a struct and reads its data header, +// returning struct version declared in data header. // Note: it doesn't read a pointer to the encoded struct. // Call |Finish()| after reading all fields. func (d *Decoder) StartStruct() (uint32, error) { @@ -135,7 +140,7 @@ func (d *Decoder) StartStruct() (uint32, error) { if err := d.pushState(header, 0); err != nil { return 0, err } - return header.Elements, nil + return header.ElementsOrVersion, nil } func (d *Decoder) readDataHeader() (DataHeader, error) { @@ -144,8 +149,8 @@ func (d *Decoder) readDataHeader() (DataHeader, error) { } oldEnd := d.end - dataHeaderSize header := DataHeader{ - Size: binary.LittleEndian.Uint32(d.buf[oldEnd:]), - Elements: binary.LittleEndian.Uint32(d.buf[oldEnd+4:]), + Size: binary.LittleEndian.Uint32(d.buf[oldEnd:]), + ElementsOrVersion: binary.LittleEndian.Uint32(d.buf[oldEnd+4:]), } return header, nil } diff --git a/third_party/mojo/src/mojo/public/go/bindings/encoder.go b/third_party/mojo/src/mojo/public/go/bindings/encoder.go index 5ea1a50..542cc21 100644 --- a/third_party/mojo/src/mojo/public/go/bindings/encoder.go +++ b/third_party/mojo/src/mojo/public/go/bindings/encoder.go @@ -74,10 +74,10 @@ func ensureElementBitSizeAndCapacity(state *encodingState, bitSize uint32) error if state == nil { return fmt.Errorf("empty state stack") } - if state.elementBitSize > 0 && state.elementBitSize != bitSize { + if state.elementBitSize != 0 && state.elementBitSize != bitSize { return fmt.Errorf("unexpected element bit size: expected %d, but got %d", state.elementBitSize, bitSize) } - if state.elementsProcessed >= state.elements { + if state.elementBitSize != 0 && state.elementsProcessed >= state.elements { return fmt.Errorf("can't process more than elements defined in header(%d)", state.elements) } byteSize := bytesForBits(uint64(state.bitOffset + bitSize)) @@ -112,11 +112,15 @@ func (e *Encoder) popState() { func (e *Encoder) pushState(header DataHeader, elementBitSize uint32) { oldEnd := e.end e.claimData(align(int(header.Size), defaultAlignment)) + elements := uint32(0) + if elementBitSize != 0 { + elements = header.ElementsOrVersion + } e.stateStack = append(e.stateStack, encodingState{ offset: oldEnd, limit: e.end, elementBitSize: elementBitSize, - elements: header.Elements, + elements: elements, }) e.writeDataHeader(header) } @@ -153,15 +157,15 @@ func (e *Encoder) StartMap() { // StartStruct starts encoding a struct and writes its data header. // Note: it doesn't write a pointer to the encoded struct. // Call |Finish()| after writing all fields. -func (e *Encoder) StartStruct(size, numFields uint32) { +func (e *Encoder) StartStruct(size, version uint32) { dataSize := dataHeaderSize + int(size) - header := DataHeader{uint32(dataSize), numFields} + header := DataHeader{uint32(dataSize), version} e.pushState(header, 0) } func (e *Encoder) writeDataHeader(header DataHeader) { binary.LittleEndian.PutUint32(e.buf[e.state().offset:], header.Size) - binary.LittleEndian.PutUint32(e.buf[e.state().offset+4:], header.Elements) + binary.LittleEndian.PutUint32(e.buf[e.state().offset+4:], header.ElementsOrVersion) e.state().offset += 8 } @@ -171,7 +175,7 @@ func (e *Encoder) Finish() error { if e.state() == nil { return fmt.Errorf("state stack is empty") } - if e.state().elementsProcessed != e.state().elements { + if e.state().elementBitSize != 0 && e.state().elementsProcessed != e.state().elements { return fmt.Errorf("unexpected number of elements written: defined in header %d, but written %d", e.state().elements, e.state().elementsProcessed) } e.popState() diff --git a/third_party/mojo/src/mojo/public/go/bindings/interface.go b/third_party/mojo/src/mojo/public/go/bindings/interface.go new file mode 100644 index 0000000..03ab791 --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/bindings/interface.go @@ -0,0 +1,60 @@ +// 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. + +package bindings + +import ( + "mojo/public/go/system" +) + +// messagePipeHandleOwner owns a message pipe handle, it can only pass it +// invalidating itself or close it. +type messagePipeHandleOwner struct { + handle system.MessagePipeHandle +} + +// PassMessagePipe passes ownership of the underlying message pipe handle to +// the newly created handle object, invalidating the underlying handle object +// in the process. +func (o *messagePipeHandleOwner) PassMessagePipe() system.MessagePipeHandle { + if o.handle == nil { + return &InvalidHandle{} + } + return o.handle.ToUntypedHandle().ToMessagePipeHandle() +} + +// Close closes the underlying handle. +func (o *messagePipeHandleOwner) Close() { + if o.handle != nil { + o.handle.Close() + } +} + +// InterfaceRequest represents a request from a remote client for an +// implementation of mojo interface over a specified message pipe. The +// implementor of the interface should remove the message pipe by calling +// PassMessagePipe() and attach it to the implementation. +type InterfaceRequest struct { + messagePipeHandleOwner +} + +// InterfacePointer owns a message pipe handle with an implementation of mojo +// interface attached to the other end of the message pipe. The client of the +// interface should remove the message pipe by calling PassMessagePipe() and +// attach it to the proxy. +type InterfacePointer struct { + messagePipeHandleOwner +} + +// CreateMessagePipeForInterface creates a message pipe with interface request +// on one end and interface pointer on the other end. The interface request +// should be attached to appropriate mojo interface implementation and +// the interface pointer should be attached to mojo interface proxy. +func CreateMessagePipeForMojoInterface() (InterfaceRequest, InterfacePointer) { + r, h0, h1 := system.GetCore().CreateMessagePipe(nil) + if r != system.MOJO_RESULT_OK { + panic("can't create a message pipe") + } + return InterfaceRequest{messagePipeHandleOwner{h0}}, InterfacePointer{messagePipeHandleOwner{h1}} +} diff --git a/third_party/mojo/src/mojo/public/go/bindings/message.go b/third_party/mojo/src/mojo/public/go/bindings/message.go index 786ec47..e5c8329 100644 --- a/third_party/mojo/src/mojo/public/go/bindings/message.go +++ b/third_party/mojo/src/mojo/public/go/bindings/message.go @@ -40,8 +40,8 @@ type Payload interface { // DataHeader is a header for a mojo complex element. type DataHeader struct { - Size uint32 - Elements uint32 + Size uint32 + ElementsOrVersion uint32 } // MessageHeader is a header information for a message. diff --git a/third_party/mojo/src/mojo/public/interfaces/application/application.mojom b/third_party/mojo/src/mojo/public/interfaces/application/application.mojom index cffcb1f..03fe943 100644 --- a/third_party/mojo/src/mojo/public/interfaces/application/application.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/application/application.mojom @@ -13,8 +13,9 @@ import "mojo/public/interfaces/application/shell.mojom"; interface Application { // Initializes the application with the specified arguments. This method is // guaranteed to be called before any other method is called, and will only be - // called once. - Initialize(Shell shell, array<string>? args); + // called once. The |url| parameter is the final url the application was found + // at, after all redirects and resolutions. + Initialize(Shell shell, array<string>? args, string url); // Called when another application (identified by |requestor_url|) attempts to // open a connection to this application. diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn index f68b113..dabc3f6 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -11,7 +11,6 @@ mojom("test_interfaces") { "no_module.mojom", "rect.mojom", "regression_tests.mojom", - "regression_tests_import.mojom", "sample_factory.mojom", "sample_import.mojom", "sample_import2.mojom", diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data index 9a983a7..c0be013 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data @@ -8,7 +8,6 @@ [u4]2 // num_fields [dist8]param0_ptr // param0 [dist8]param1_ptr // param1 -[u8]0 // unknown [anchr]method7_params [anchr]param0_ptr diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data index f4b6c40..0ee26af 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data @@ -1,7 +1,7 @@ [dist4]message_header // num_bytes [u4]3 // num_fields [u4]0 // name -[u4]2 // flags: Is response. +[u4]2 // flags kMessageIsResponse [u8]1 // request_id [anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected index 7ef22e9..7ef22e9 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data index a357013..25540b1 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data @@ -1,7 +1,7 @@ [dist4]message_header // num_bytes [u4]3 // num_fields [u4]0 // name -[u4]2 // flags: Is response. +[u4]2 // flags kMessageIsResponse [u8]1 // request_id [anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected index 5a1ec4e..5a1ec4e 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data index df7b7e8..c57d4bd 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data @@ -1,7 +1,8 @@ [dist4]message_header // num_bytes -[u4]2 // num_fields +[u4]3 // num_fields [u4]0 // name -[u4]0 // flags +[u4]1 // flags kMessageExpectsResponse +[u8]7 // request_id [anchr]message_header [dist4]method0_params // num_bytes diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected index 7ef22e9..7ef22e9 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data index 3dcca9d..8b6bd2d 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data @@ -1,7 +1,8 @@ [dist4]message_header // num_bytes -[u4]2 // num_fields +[u4]3 // num_fields [u4]0 // name -[u4]0 // flags +[u4]1 // flags kMessageExpectsResponse +[u8]7 // request_id [anchr]message_header [dist4]method0_params // num_bytes diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected index 25aceee..25aceee 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests.mojom index 4a85b0a..313f1f4 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests.mojom @@ -7,8 +7,6 @@ [JavaPackage="org.chromium.mojo.bindings.test.mojom.regression_tests"] module regression_tests; -import "regression_tests_import.mojom"; - interface CheckMethodWithEmptyResponse { WithouParameterAndEmptyResponse() => (); WithParameterAndEmptyResponse(bool b) => (); @@ -54,7 +52,3 @@ struct A { struct B { A? a; }; - -[Client=InterfaceWithClientImportedClient] -interface InterfaceWithClientImported { -}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests_import.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests_import.mojom deleted file mode 100644 index fbed983..0000000 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/regression_tests_import.mojom +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Module containing entities for regression tests of the generator. Entities -// must never be modified, instead new entity must be added to add new tests. -[JavaPackage="org.chromium.mojo.bindings.test.mojom.regression_tests_import"] -module regression_tests_import; - -interface InterfaceWithClientImportedClient { -}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_factory.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_factory.mojom index eb5d930..ade3bf3 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_factory.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_factory.mojom @@ -29,18 +29,13 @@ interface NamedObject { GetName() => (string name); }; -[Client=FactoryClient] interface Factory { - DoStuff(Request request, handle<message_pipe>? pipe); - DoStuff2(handle<data_pipe_consumer> pipe); + DoStuff(Request request, handle<message_pipe>? pipe) => + (Response response, string text); + DoStuff2(handle<data_pipe_consumer> pipe) => (string text); CreateNamedObject(NamedObject& obj); RequestImportedInterface( imported.ImportedInterface& obj) => (imported.ImportedInterface& obj); TakeImportedInterface( imported.ImportedInterface obj) => (imported.ImportedInterface obj); }; - -interface FactoryClient { - DidStuff(Response response, string text); - DidStuff2(string text); -}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom index 37405ca..52589a9 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom @@ -13,14 +13,9 @@ enum Enum { VALUE }; -[Client=ProviderClient] interface Provider { EchoString(string a) => (string a); EchoStrings(string a, string b) => (string a, string b); EchoMessagePipeHandle(handle<message_pipe> a) => (handle<message_pipe> a); EchoEnum(Enum a) => (Enum a); }; - -// TODO(darin): We shouldn't need this, but JS bindings don't work without it. -interface ProviderClient { -}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_service.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_service.mojom index 5c80781..700481c 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_service.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_service.mojom @@ -104,21 +104,16 @@ struct StructWithHoleV2 { int32 v3 = 3; }; -[Client=ServiceClient] interface Service { enum BazOptions { REGULAR = 0, EXTRA }; const uint8 kFavoriteBaz = 1; - Frobinate@0(Foo? foo@0, BazOptions baz@1, Port? port@2); + Frobinate@0(Foo? foo@0, BazOptions baz@1, Port? port@2) => (int32 result@0); GetPort@1(Port& port @0); }; -interface ServiceClient { - DidFrobinate@0(int32 result@0); -}; - // This interface is referenced above where it is defined. It also refers to // itself from a method. interface Port { diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_structs.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_structs.mojom index dc4f05e..97ce3a3 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_structs.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_structs.mojom @@ -283,3 +283,69 @@ struct BitArrayValues { array<array<bool>?> f5; array<array<bool, 2>?> f6; }; + +// Used to verify that different versions can be decoded correctly. + +struct MultiVersionStruct { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; + [MinVersion=5] + array<int8> f_array; + [MinVersion=7] + handle<message_pipe>? f_message_pipe; + [MinVersion=7] + bool f_bool; + [MinVersion=9] + int16 f_int16; +}; + +struct MultiVersionStructV0 { + [MinVersion=0] + int32 f_int32; +}; + +struct MultiVersionStructV1 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; +}; + +struct MultiVersionStructV3 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; +}; + +struct MultiVersionStructV5 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; + [MinVersion=5] + array<int8> f_array; +}; + +struct MultiVersionStructV7 { + [MinVersion=0] + int32 f_int32; + [MinVersion=1] + Rect? f_rect; + [MinVersion=3] + string? f_string; + [MinVersion=5] + array<int8> f_array; + [MinVersion=7] + handle<message_pipe>? f_message_pipe; + [MinVersion=7] + bool f_bool; +}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom index 290b792..fb1f4b7 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom @@ -49,12 +49,6 @@ struct BasicStruct { int32 a; }; -[Client=IntegrationTestInterface2] -interface IntegrationTestInterface1 { - Method0(BasicStruct param0); -}; - -[Client=IntegrationTestInterface1] -interface IntegrationTestInterface2 { - Method0() => (array<uint8> param0); +interface IntegrationTestInterface { + Method0(BasicStruct param0) => (array<uint8> param0); }; diff --git a/third_party/mojo/src/mojo/public/java/BUILD.gn b/third_party/mojo/src/mojo/public/java/BUILD.gn index 04d2ef6..edfd9a0 100644 --- a/third_party/mojo/src/mojo/public/java/BUILD.gn +++ b/third_party/mojo/src/mojo/public/java/BUILD.gn @@ -18,6 +18,7 @@ android_library("system") { "system/src/org/chromium/mojo/system/Pair.java", "system/src/org/chromium/mojo/system/SharedBufferHandle.java", "system/src/org/chromium/mojo/system/UntypedHandle.java", + "system/src/org/chromium/mojo/system/RunLoop.java", ] } @@ -36,7 +37,6 @@ android_library("bindings") { "bindings/src/org/chromium/mojo/bindings/HandleOwner.java", "bindings/src/org/chromium/mojo/bindings/Interface.java", "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java", - "bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java", "bindings/src/org/chromium/mojo/bindings/MessageHeader.java", "bindings/src/org/chromium/mojo/bindings/Message.java", "bindings/src/org/chromium/mojo/bindings/MessageReceiver.java", diff --git a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java index b95b6dd..fa1eb23 100644 --- a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java +++ b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java @@ -120,22 +120,47 @@ public class Decoder { // Claim the memory for the header. mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE); int size = readInt(DataHeader.SIZE_OFFSET); - int numFields = readInt(DataHeader.NUM_FIELDS_OFFSET); + int elementsOrVersion = readInt(DataHeader.ELEMENTS_OR_VERSION_OFFSET); if (size < 0) { throw new DeserializationException( "Negative size. Unsigned integers are not valid for java."); } - if (numFields < 0) { + if (elementsOrVersion < 0) { throw new DeserializationException( - "Negative number of fields. Unsigned integers are not valid for java."); + "Negative elements or version. Unsigned integers are not valid for java."); } // Claim the remaining memory. mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + size); - DataHeader res = new DataHeader(size, numFields); + DataHeader res = new DataHeader(size, elementsOrVersion); return res; } + public DataHeader readAndValidateDataHeader(DataHeader[] versionArray) { + DataHeader header = readDataHeader(); + int maxVersionIndex = versionArray.length - 1; + if (header.elementsOrVersion <= versionArray[maxVersionIndex].elementsOrVersion) { + DataHeader referenceHeader = null; + for (int index = maxVersionIndex; index >= 0; index--) { + DataHeader dataHeader = versionArray[index]; + if (header.elementsOrVersion >= dataHeader.elementsOrVersion) { + referenceHeader = dataHeader; + break; + } + } + if (referenceHeader == null || referenceHeader.size != header.size) { + throw new DeserializationException( + "Header doesn't correspond to any known version."); + } + } else { + if (header.size < versionArray[maxVersionIndex].size) { + throw new DeserializationException("Message newer than the last known version" + + " cannot be shorter than required by the last known version."); + } + } + return header; + } + /** * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an * array where element have the given size. @@ -153,9 +178,9 @@ public class Decoder { throw new DeserializationException( "Incorrect header for map. The size is incorrect."); } - if (si.numFields != BindingsHelper.MAP_STRUCT_HEADER.numFields) { + if (si.elementsOrVersion != BindingsHelper.MAP_STRUCT_HEADER.elementsOrVersion) { throw new DeserializationException( - "Incorrect header for map. The number of fields is incorrect."); + "Incorrect header for map. The version is incorrect."); } } @@ -244,10 +269,10 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForBooleanArray(expectedLength); - byte[] bytes = new byte[(si.numFields + 7) / BindingsHelper.ALIGNMENT]; + byte[] bytes = new byte[(si.elementsOrVersion + 7) / BindingsHelper.ALIGNMENT]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().get(bytes); - boolean[] result = new boolean[si.numFields]; + boolean[] result = new boolean[si.elementsOrVersion]; for (int i = 0; i < bytes.length; ++i) { for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { int booleanIndex = i * BindingsHelper.ALIGNMENT + j; @@ -268,7 +293,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(1, expectedLength); - byte[] result = new byte[si.numFields]; + byte[] result = new byte[si.elementsOrVersion]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().get(result); return result; @@ -283,7 +308,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(2, expectedLength); - short[] result = new short[si.numFields]; + short[] result = new short[si.elementsOrVersion]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().asShortBuffer().get(result); return result; @@ -298,7 +323,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - int[] result = new int[si.numFields]; + int[] result = new int[si.elementsOrVersion]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().asIntBuffer().get(result); return result; @@ -313,7 +338,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - float[] result = new float[si.numFields]; + float[] result = new float[si.elementsOrVersion]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().asFloatBuffer().get(result); return result; @@ -328,7 +353,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(8, expectedLength); - long[] result = new long[si.numFields]; + long[] result = new long[si.elementsOrVersion]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().asLongBuffer().get(result); return result; @@ -343,7 +368,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(8, expectedLength); - double[] result = new double[si.numFields]; + double[] result = new double[si.elementsOrVersion]; d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE); d.mMessage.getData().asDoubleBuffer().get(result); return result; @@ -447,7 +472,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - Handle[] result = new Handle[si.numFields]; + Handle[] result = new Handle[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readHandle( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -466,7 +491,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - UntypedHandle[] result = new UntypedHandle[si.numFields]; + UntypedHandle[] result = new UntypedHandle[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readUntypedHandle( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -485,7 +510,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.numFields]; + DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readConsumerHandle( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -504,7 +529,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.numFields]; + DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readProducerHandle( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -524,7 +549,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - MessagePipeHandle[] result = new MessagePipeHandle[si.numFields]; + MessagePipeHandle[] result = new MessagePipeHandle[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readMessagePipeHandle( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -544,7 +569,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - SharedBufferHandle[] result = new SharedBufferHandle[si.numFields]; + SharedBufferHandle[] result = new SharedBufferHandle[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readSharedBufferHandle( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -564,7 +589,7 @@ public class Decoder { return null; } DataHeader si = d.readDataHeaderForArray(4, expectedLength); - S[] result = manager.buildArray(si.numFields); + S[] result = manager.buildArray(si.elementsOrVersion); for (int i = 0; i < result.length; ++i) { // This cast is necessary because java 6 doesn't handle wildcard correctly when using // Manager<S, ? extends S> @@ -588,7 +613,7 @@ public class Decoder { } DataHeader si = d.readDataHeaderForArray(4, expectedLength); @SuppressWarnings("unchecked") - InterfaceRequest<I>[] result = new InterfaceRequest[si.numFields]; + InterfaceRequest<I>[] result = new InterfaceRequest[si.elementsOrVersion]; for (int i = 0; i < result.length; ++i) { result[i] = d.readInterfaceRequest( DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, @@ -610,13 +635,13 @@ public class Decoder { */ private DataHeader readDataHeaderForBooleanArray(int expectedLength) { DataHeader dataHeader = readDataHeader(); - if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.numFields + 7) / 8) { + if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.elementsOrVersion + 7) / 8) { throw new DeserializationException("Array header is incorrect."); } if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH - && dataHeader.numFields != expectedLength) { - throw new DeserializationException("Incorrect array length. Expected: " - + expectedLength + ", but got: " + dataHeader.numFields + "."); + && dataHeader.elementsOrVersion != expectedLength) { + throw new DeserializationException("Incorrect array length. Expected: " + expectedLength + + ", but got: " + dataHeader.elementsOrVersion + "."); } return dataHeader; } @@ -626,13 +651,14 @@ public class Decoder { */ private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) { DataHeader dataHeader = readDataHeader(); - if (dataHeader.size < (DataHeader.HEADER_SIZE + elementSize * dataHeader.numFields)) { + if (dataHeader.size + < (DataHeader.HEADER_SIZE + elementSize * dataHeader.elementsOrVersion)) { throw new DeserializationException("Array header is incorrect."); } if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH - && dataHeader.numFields != expectedLength) { - throw new DeserializationException("Incorrect array length. Expected: " - + expectedLength + ", but got: " + dataHeader.numFields + "."); + && dataHeader.elementsOrVersion != expectedLength) { + throw new DeserializationException("Incorrect array length. Expected: " + expectedLength + + ", but got: " + dataHeader.elementsOrVersion + "."); } return dataHeader; } diff --git a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java index 75cddc9..0f85232 100644 --- a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java +++ b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java @@ -150,7 +150,7 @@ public class Encoder { public void encode(DataHeader s) { mEncoderState.claimMemory(BindingsHelper.align(s.size)); encode(s.size, DataHeader.SIZE_OFFSET); - encode(s.numFields, DataHeader.NUM_FIELDS_OFFSET); + encode(s.elementsOrVersion, DataHeader.ELEMENTS_OR_VERSION_OFFSET); } /** diff --git a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java deleted file mode 100644 index f7d8afe..0000000 --- a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.mojo.bindings; - -import org.chromium.mojo.system.Core; -import org.chromium.mojo.system.MessagePipeHandle; -import org.chromium.mojo.system.Pair; - -/** - * Base class for mojo generated interfaces that have a client. - * - * @param <CI> the type of the client interface. - */ -public interface InterfaceWithClient<CI extends Interface> extends Interface { - - /** - * Proxy class for interfaces with a client. - */ - interface Proxy<CI extends Interface> extends Interface.Proxy, InterfaceWithClient<CI> { - } - - /** - * Base implementation of Proxy. - * - * @param <CI> the type of the client interface. - */ - abstract class AbstractProxy<CI extends Interface> extends Interface.AbstractProxy - implements Proxy<CI> { - - /** - * Constructor. - * - * @param core the Core implementation used to create pipes and access the async waiter. - * @param messageReceiver the message receiver to send message to. - */ - public AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) { - super(core, messageReceiver); - } - - /** - * @see InterfaceWithClient#setClient(Interface) - */ - @Override - public void setClient(CI client) { - throw new UnsupportedOperationException( - "Setting the client on a proxy is not supported"); - } - } - - /** - * Base manager implementation for interfaces that have a client. - * - * @param <I> the type of the interface the manager can handle. - * @param <P> the type of the proxy the manager can handle. To be noted, P always extends I. - * @param <CI> the type of the client interface. - */ - abstract class Manager<I extends InterfaceWithClient<CI>, P extends Proxy<CI>, - CI extends Interface> extends Interface.Manager<I, P> { - - /** - * @see Interface.Manager#bind(Interface, MessagePipeHandle) - */ - @Override - public final void bind(I impl, MessagePipeHandle handle) { - Router router = new RouterImpl(handle); - super.bind(handle.getCore(), impl, router); - @SuppressWarnings("unchecked") - CI client = (CI) getClientManager().attachProxy(handle.getCore(), router); - impl.setClient(client); - router.start(); - } - - /** - * Returns a Proxy that will send messages to the given |handle|. This implies that the - * other end of the handle must be connected to an implementation of the interface. |client| - * is the implementation of the client interface. - */ - public P attachProxy(MessagePipeHandle handle, CI client) { - Router router = new RouterImpl(handle); - DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler(); - handlers.addConnectionErrorHandler(client); - router.setErrorHandler(handlers); - getClientManager().bind(handle.getCore(), client, router); - P proxy = super.attachProxy(handle.getCore(), router); - handlers.addConnectionErrorHandler(proxy); - router.start(); - return proxy; - } - - /** - * Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where - * the first element is a proxy, and the second element is the request. The proxy can be - * used immediately. - * - * @param client the implementation of the client interface. - */ - public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core, CI client) { - Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); - P proxy = attachProxy(handles.first, client); - return Pair.create(proxy, new InterfaceRequest<I>(handles.second)); - } - - /** - * Returns a manager for the client inetrafce. - */ - protected abstract Interface.Manager<CI, ?> getClientManager(); - } - - /** - * Set the client implementation for this interface. - */ - public void setClient(CI client); -} diff --git a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java index dcaf86c..34f3d14 100644 --- a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java +++ b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java @@ -14,14 +14,14 @@ import java.nio.ByteBuffer; public class MessageHeader { private static final int SIMPLE_MESSAGE_SIZE = 16; - private static final int SIMPLE_MESSAGE_NUM_FIELDS = 2; + private static final int SIMPLE_MESSAGE_VERSION = 2; private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO = - new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_NUM_FIELDS); + new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_VERSION); private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 24; - private static final int MESSAGE_WITH_REQUEST_ID_NUM_FIELDS = 3; + private static final int MESSAGE_WITH_REQUEST_ID_VERSION = 3; private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO = - new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_NUM_FIELDS); + new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_VERSION); private static final int TYPE_OFFSET = 8; private static final int FLAGS_OFFSET = 12; @@ -216,28 +216,25 @@ public class MessageHeader { * Validate that the given {@link DataHeader} can be the data header of a message header. */ private static void validateDataHeader(DataHeader dataHeader) { - if (dataHeader.numFields < SIMPLE_MESSAGE_NUM_FIELDS) { - throw new DeserializationException( - "Incorrect number of fields, expecting at least " + SIMPLE_MESSAGE_NUM_FIELDS - + ", but got: " + dataHeader.numFields); + if (dataHeader.elementsOrVersion < SIMPLE_MESSAGE_VERSION) { + throw new DeserializationException("Incorrect number of fields, expecting at least " + + SIMPLE_MESSAGE_VERSION + ", but got: " + dataHeader.elementsOrVersion); } if (dataHeader.size < SIMPLE_MESSAGE_SIZE) { throw new DeserializationException( "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE + ", but got: " + dataHeader.size); } - if (dataHeader.numFields == SIMPLE_MESSAGE_NUM_FIELDS + if (dataHeader.elementsOrVersion == SIMPLE_MESSAGE_VERSION && dataHeader.size != SIMPLE_MESSAGE_SIZE) { - throw new DeserializationException( - "Incorrect message size for a message with " + SIMPLE_MESSAGE_NUM_FIELDS - + " fields, expecting " + SIMPLE_MESSAGE_SIZE + ", but got: " - + dataHeader.size); + throw new DeserializationException("Incorrect message size for a message with " + + SIMPLE_MESSAGE_VERSION + " fields, expecting " + SIMPLE_MESSAGE_SIZE + + ", but got: " + dataHeader.size); } - if (dataHeader.numFields == MESSAGE_WITH_REQUEST_ID_NUM_FIELDS + if (dataHeader.elementsOrVersion == MESSAGE_WITH_REQUEST_ID_VERSION && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) { - throw new DeserializationException( - "Incorrect message size for a message with " - + MESSAGE_WITH_REQUEST_ID_NUM_FIELDS + " fields, expecting " + throw new DeserializationException("Incorrect message size for a message with " + + MESSAGE_WITH_REQUEST_ID_VERSION + " fields, expecting " + MESSAGE_WITH_REQUEST_ID_SIZE + ", but got: " + dataHeader.size); } } diff --git a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java index 017d0ef..d7369fa 100644 --- a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java +++ b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java @@ -15,7 +15,6 @@ public abstract class Struct { * The header for a mojo complex element. */ public static final class DataHeader { - /** * The size of a serialized header, in bytes. */ @@ -29,18 +28,26 @@ public abstract class Struct { /** * The offset of the number of fields field. */ - public static final int NUM_FIELDS_OFFSET = 4; + public static final int ELEMENTS_OR_VERSION_OFFSET = 4; + /** + * The size of the object owning this header. + */ public final int size; - public final int numFields; + + /** + * Number of element (for an array) or version (for a struct) of the object owning this + * header. + */ + public final int elementsOrVersion; /** * Constructor. */ - public DataHeader(int size, int numFields) { + public DataHeader(int size, int elementsOrVersion) { super(); this.size = size; - this.numFields = numFields; + this.elementsOrVersion = elementsOrVersion; } /** @@ -50,7 +57,7 @@ public abstract class Struct { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + numFields; + result = prime * result + elementsOrVersion; result = prime * result + size; return result; } @@ -68,8 +75,7 @@ public abstract class Struct { return false; DataHeader other = (DataHeader) object; - return (numFields == other.numFields && - size == other.size); + return (elementsOrVersion == other.elementsOrVersion && size == other.size); } } diff --git a/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/Core.java index 660a13f..ba0e5c6 100644 --- a/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/Core.java +++ b/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/Core.java @@ -309,4 +309,13 @@ public interface Core { */ public AsyncWaiter getDefaultAsyncWaiter(); + /** + * Returns a new run loop. + */ + public RunLoop createDefaultRunLoop(); + + /** + * Returns the current run loop if it exists. + */ + public RunLoop getCurrentRunLoop(); } diff --git a/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java b/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java new file mode 100644 index 0000000..4038b295 --- /dev/null +++ b/third_party/mojo/src/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.mojo.system; + +import java.io.Closeable; + +/** + * Definition of a run loop. + */ +public interface RunLoop extends Closeable { + /** + * Start the run loop. It will continue until quit() is called. + */ + public void run(); + + /** + * Start the run loop and stop it as soon as no task is present in the work queue. + */ + public void runUntilIdle(); + + /* + * Quit the currently running run loop. + */ + public void quit(); + + /** + * Add a runnable to the queue of tasks. + * @param runnable Callback to be executed by the run loop. + * @param delay Delay, in MojoTimeTicks (microseconds) before the callback should + * be executed. + */ + public void postDelayedTask(Runnable runnable, long delay); + + /** + * Destroy the run loop and deregister it from Core. + */ + @Override + public abstract void close(); +} diff --git a/third_party/mojo/src/mojo/public/js/connection.js b/third_party/mojo/src/mojo/public/js/connection.js index 4a7a8b8..62e8798 100644 --- a/third_party/mojo/src/mojo/public/js/connection.js +++ b/third_party/mojo/src/mojo/public/js/connection.js @@ -73,52 +73,6 @@ define("mojo/public/js/connection", [ TestConnection.prototype = Object.create(Connection.prototype); - // TODO(hansmuller): remove when Shell.mojom loses its client. - function createOpenConnection( - messagePipeHandle, client, localInterface, remoteInterface) { - var stubClass = (localInterface && localInterface.stubClass) || EmptyStub; - var proxyClass = - (remoteInterface && remoteInterface.proxyClass) || EmptyProxy; - var proxy = new proxyClass; - var stub = new stubClass; - var router = new Router(messagePipeHandle); - var connection = new BaseConnection(stub, proxy, router); - - ProxyBindings(proxy).connection = connection; - ProxyBindings(proxy).local = connection.local; - StubBindings(stub).connection = connection; - StubBindings(proxy).remote = connection.remote; - - var clientImpl = client instanceof Function ? client(proxy) : client; - if (clientImpl) - StubBindings(stub).delegate = clientImpl; - - return connection; - } - - // TODO(hansmuller): remove when Shell.mojom loses its client. - // Return a message pipe handle. - function bindProxyClient(clientImpl, localInterface, remoteInterface) { - var messagePipe = core.createMessagePipe(); - if (messagePipe.result != core.RESULT_OK) - throw new Error("createMessagePipe failed " + messagePipe.result); - - createOpenConnection( - messagePipe.handle0, clientImpl, localInterface, remoteInterface); - return messagePipe.handle1; - } - - // TODO(hansmuller): remove when Shell.mojom loses its client. - // Return a proxy. - function bindProxyHandle(proxyHandle, localInterface, remoteInterface) { - if (!core.isHandle(proxyHandle)) - throw new Error("Not a handle " + proxyHandle); - - var connection = createOpenConnection( - proxyHandle, undefined, localInterface, remoteInterface); - return connection.remote; - } - // Return a handle for a message pipe that's connected to a proxy // for remoteInterface. Used by generated code for outgoing interface& // (request) parameters: the caller is given the generated proxy via @@ -193,10 +147,6 @@ define("mojo/public/js/connection", [ exports.Connection = Connection; exports.TestConnection = TestConnection; - // TODO(hansmuller): remove these when Shell.mojom loses its client. - exports.bindProxyHandle = bindProxyHandle; - exports.bindProxyClient = bindProxyClient; - exports.bindProxy = bindProxy; exports.bindImpl = bindImpl; exports.bindHandleToProxy = bindHandleToProxy; diff --git a/third_party/mojo/src/mojo/public/js/validation_unittests.js b/third_party/mojo/src/mojo/public/js/validation_unittests.js index 05988f3..8a1f385 100644 --- a/third_party/mojo/src/mojo/public/js/validation_unittests.js +++ b/third_party/mojo/src/mojo/public/js/validation_unittests.js @@ -254,8 +254,8 @@ define([ testInterface.ConformanceTestInterface.validateRequest]); } - function testIntegratedMessageValidation() { - var testFiles = getMessageTestFiles("integration_"); + function testIntegratedMessageValidation(testFilesPattern) { + var testFiles = getMessageTestFiles(testFilesPattern); expect(testFiles.length).toBeGreaterThan(0); for (var i = 0; i < testFiles.length; i++) { @@ -279,8 +279,8 @@ define([ var testConnection = new connection.TestConnection( testMessagePipe.handle1, - testInterface.IntegrationTestInterface1.stubClass, - testInterface.IntegrationTestInterface2.proxyClass); + testInterface.IntegrationTestInterface.stubClass, + testInterface.IntegrationTestInterface.proxyClass); var validationError = noError; testConnection.router_.validationErrorHandler = function(err) { @@ -295,8 +295,22 @@ define([ } } + function testIntegratedMessageHeaderValidation() { + testIntegratedMessageValidation("integration_msghdr"); + } + + function testIntegratedRequestMessageValidation() { + testIntegratedMessageValidation("integration_intf_rqst"); + } + + function testIntegratedResponseMessageValidation() { + testIntegratedMessageValidation("integration_intf_resp"); + } + expect(checkTestMessageParser()).toBeNull(); testConformanceMessageValidation(); - testIntegratedMessageValidation(); + testIntegratedMessageHeaderValidation(); + testIntegratedResponseMessageValidation(); + testIntegratedRequestMessageValidation(); this.result = "PASS"; }); diff --git a/third_party/mojo/src/mojo/public/mojo.gni b/third_party/mojo/src/mojo/public/mojo.gni index 9f1b86b..bc90a23 100644 --- a/third_party/mojo/src/mojo/public/mojo.gni +++ b/third_party/mojo/src/mojo/public/mojo.gni @@ -6,10 +6,19 @@ import("//build/module_args/mojo.gni") # If using the prebuilt shell, gate its usage by the platforms for which it is # published. -if (!defined(use_prebuilt_mojo_shell) || use_prebuilt_mojo_shell) { +use_prebuilt_mojo_shell = false +if (!defined(build_mojo_shell_from_source) || !build_mojo_shell_from_source) { use_prebuilt_mojo_shell = is_linux || is_android } +# If using the prebuilt network service, gate its usage by the platforms for +# which it is published. +use_prebuilt_network_service = false +if (!defined(build_network_service_from_source) || + !build_network_service_from_source) { + use_prebuilt_network_service = is_linux || is_android +} + # The absolute path to the directory containing the mojo public SDK (i.e., the # directory containing mojo/public). The build files within the Mojo public # SDK use this variable to allow themselves to be parameterized by the location diff --git a/third_party/mojo/src/mojo/public/mojo_application.gni b/third_party/mojo/src/mojo/public/mojo_application.gni index 7661c88..77ed4bb 100644 --- a/third_party/mojo/src/mojo/public/mojo_application.gni +++ b/third_party/mojo/src/mojo/public/mojo_application.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/module_args/mojo.gni") import("mojo.gni") import("mojo_sdk.gni") @@ -68,22 +69,22 @@ template("mojo_native_application") { libs = invoker.libs } - if (use_prebuilt_mojo_shell) { - copy_mojo_shell = - rebase_path("mojo/public/tools:copy_mojo_shell", ".", mojo_root) - } - - # Copy the prebuilt mojo_shell if using it. + data_deps = [] if (defined(invoker.data_deps)) { data_deps = invoker.data_deps - if (use_prebuilt_mojo_shell) { - data_deps += [ copy_mojo_shell ] - } - } else { - if (use_prebuilt_mojo_shell) { - data_deps = [ copy_mojo_shell ] - } } + + # Copy any necessary prebuilt artifacts. + if (use_prebuilt_mojo_shell) { + data_deps += + [ rebase_path("mojo/public/tools:copy_mojo_shell", ".", mojo_root) ] + } + if (use_prebuilt_network_service) { + data_deps += [ rebase_path("mojo/public/tools:copy_network_service", + ".", + mojo_root) ] + } + deps = rebase_path([ "mojo/public/c/system", "mojo/public/platform/native:system", diff --git a/third_party/mojo/src/mojo/public/python/BUILD.gn b/third_party/mojo/src/mojo/public/python/BUILD.gn index f4b0b03..d2e13c2 100644 --- a/third_party/mojo/src/mojo/public/python/BUILD.gn +++ b/third_party/mojo/src/mojo/public/python/BUILD.gn @@ -78,6 +78,16 @@ python_binary_source_set("python_common") { ] } +python_package("packaged_application") { + sources = [ + "mojo_application/__init__.py", + "mojo_application/application_delegate.py", + "mojo_application/application_impl.py", + "mojo_application/application_runner.py", + "mojo_application/service_provider_impl.py", + ] +} + # GYP version: mojo.gyp:mojo_python_bindings copy("bindings") { sources = [ diff --git a/third_party/mojo/src/mojo/public/python/mojo_application/__init__.py b/third_party/mojo/src/mojo/public/python/mojo_application/__init__.py new file mode 100644 index 0000000..50b23df --- /dev/null +++ b/third_party/mojo/src/mojo/public/python/mojo_application/__init__.py @@ -0,0 +1,3 @@ +# 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. diff --git a/third_party/mojo/src/mojo/public/python/mojo_application/application_delegate.py b/third_party/mojo/src/mojo/public/python/mojo_application/application_delegate.py new file mode 100644 index 0000000..c4a817d --- /dev/null +++ b/third_party/mojo/src/mojo/public/python/mojo_application/application_delegate.py @@ -0,0 +1,27 @@ +# 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. + +"""Interface for the delegate of ApplicationImpl.""" + +import mojo_application.application_impl +import mojo_application.service_provider_impl +import shell_mojom + +import mojo_system + +# pylint: disable=unused-argument +class ApplicationDelegate: + def Initialize(self, shell, application): + """ + Called from ApplicationImpl's Initialize() method. + """ + pass + + def OnAcceptConnection(self, service_provider, requestor_url, + exposed_services): + """ + Called from ApplicationImpl's OnAcceptConnection() method. Returns a bool + indicating whether this connection should be accepted. + """ + return True diff --git a/third_party/mojo/src/mojo/public/python/mojo_application/application_impl.py b/third_party/mojo/src/mojo/public/python/mojo_application/application_impl.py new file mode 100644 index 0000000..248329a --- /dev/null +++ b/third_party/mojo/src/mojo/public/python/mojo_application/application_impl.py @@ -0,0 +1,56 @@ +# 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. + +"""Python implementation of the Application interface.""" + +import application_mojom +import service_provider_mojom +import shell_mojom +from mojo_application.service_provider_impl import ServiceProviderImpl + +import mojo_system + +class ApplicationImpl(application_mojom.Application): + def __init__(self, delegate, app_request_handle): + self.shell = None + self.url = None + self.args = None + self._delegate = delegate + self._providers = [] + application_mojom.Application.manager.Bind(self, app_request_handle) + + def Initialize(self, shell, url, args): + self.shell = shell + self.url = url + self.args = args + self._delegate.Initialize(shell, self) + + def AcceptConnection(self, requestor_url, services, exposed_services): + service_provider = ServiceProviderImpl(services) + if self._delegate.OnAcceptConnection(service_provider, requestor_url, + exposed_services): + # We keep a reference to ServiceProviderImpl to ensure neither it nor + # |services| gets garbage collected. + services.Bind(service_provider) + self._providers.append(service_provider) + + def removeServiceProvider(): + self._providers.remove(service_provider) + service_provider.manager.AddOnErrorCallback(removeServiceProvider) + + def ConnectToService(self, application_url, service_class): + """ + Helper method to connect to a service. |application_url| is the URL of the + application to be connected to, and |service_class| is the class of the + service to be connected to. Returns a proxy to the service. + """ + application_proxy, request = ( + service_provider_mojom.ServiceProvider.manager.NewRequest()) + self.shell.ConnectToApplication(application_url, request, None) + + service_proxy, request = service_class.manager.NewRequest() + application_proxy.ConnectToService(service_class.manager.name, + request.PassMessagePipe()) + + return service_proxy diff --git a/third_party/mojo/src/mojo/public/python/mojo_application/application_runner.py b/third_party/mojo/src/mojo/public/python/mojo_application/application_runner.py new file mode 100644 index 0000000..94f472c --- /dev/null +++ b/third_party/mojo/src/mojo/public/python/mojo_application/application_runner.py @@ -0,0 +1,18 @@ +# 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. + +"""Helper for running Mojo applications in Python.""" + +from mojo_application.application_impl import ApplicationImpl + +import mojo_system + +def RunMojoApplication(application_delegate, app_request_handle): + loop = mojo_system.RunLoop() + + application = ApplicationImpl(application_delegate, + mojo_system.Handle(app_request_handle)) + application.manager.AddOnErrorCallback(loop.Quit) + + loop.Run() diff --git a/third_party/mojo/src/mojo/public/python/mojo_application/service_provider_impl.py b/third_party/mojo/src/mojo/public/python/mojo_application/service_provider_impl.py new file mode 100644 index 0000000..0166c76 --- /dev/null +++ b/third_party/mojo/src/mojo/public/python/mojo_application/service_provider_impl.py @@ -0,0 +1,24 @@ +# 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. + +"""Python implementation of the ServiceProvider interface.""" + +import logging + +import service_provider_mojom + +class ServiceProviderImpl(service_provider_mojom.ServiceProvider): + def __init__(self, provider): + self._provider = provider + self._name_to_service_connector = {} + + def AddService(self, service_class): + self._name_to_service_connector[service_class.manager.name] = service_class + + def ConnectToService(self, interface_name, pipe): + if interface_name in self._name_to_service_connector: + service = self._name_to_service_connector[interface_name] + service.manager.Bind(service(), pipe) + else: + logging.error("Unable to find service " + interface_name) diff --git a/third_party/mojo/src/mojo/public/python/mojo_bindings/descriptor.py b/third_party/mojo/src/mojo/public/python/mojo_bindings/descriptor.py index 45e073f..109e5ce 100644 --- a/third_party/mojo/src/mojo/public/python/mojo_bindings/descriptor.py +++ b/third_party/mojo/src/mojo/public/python/mojo_bindings/descriptor.py @@ -508,8 +508,8 @@ class MapType(SerializableType): '__module__': __name__, 'DESCRIPTOR': { 'fields': [ - SingleFieldGroup('keys', MapType._GetArrayType(key_type), 0, 0), - SingleFieldGroup('values', MapType._GetArrayType(value_type), 1, 1), + SingleFieldGroup('keys', MapType._GetArrayType(key_type), 0, 1), + SingleFieldGroup('values', MapType._GetArrayType(value_type), 1, 2), ], } } @@ -608,7 +608,10 @@ class FieldGroup(object): def GetByteSize(self): raise NotImplementedError() - def GetVersion(self): + def GetMinVersion(self): + raise NotImplementedError() + + def GetMaxVersion(self): raise NotImplementedError() def Serialize(self, obj, data_offset, data, handle_offset): @@ -617,6 +620,9 @@ class FieldGroup(object): def Deserialize(self, value, context): raise NotImplementedError() + def Filter(self, version): + raise NotImplementedError() + class SingleFieldGroup(FieldGroup, FieldDescriptor): """A FieldGroup that contains a single FieldDescriptor.""" @@ -632,7 +638,10 @@ class SingleFieldGroup(FieldGroup, FieldDescriptor): def GetByteSize(self): return self.field_type.GetByteSize() - def GetVersion(self): + def GetMinVersion(self): + return self.version + + def GetMaxVersion(self): return self.version def Serialize(self, obj, data_offset, data, handle_offset): @@ -643,12 +652,16 @@ class SingleFieldGroup(FieldGroup, FieldDescriptor): entity = self.field_type.Deserialize(value, context) return { self.name: entity } + def Filter(self, version): + return self + class BooleanGroup(FieldGroup): """A FieldGroup to pack booleans.""" def __init__(self, descriptors): FieldGroup.__init__(self, descriptors) - self.version = min([descriptor.version for descriptor in descriptors]) + self.min_version = min([descriptor.version for descriptor in descriptors]) + self.max_version = max([descriptor.version for descriptor in descriptors]) def GetTypeCode(self): return 'B' @@ -656,8 +669,11 @@ class BooleanGroup(FieldGroup): def GetByteSize(self): return 1 - def GetVersion(self): - return self.version + def GetMinVersion(self): + return self.min_version + + def GetMaxVersion(self): + return self.max_version def Serialize(self, obj, data_offset, data, handle_offset): value = _ConvertBooleansToByte( @@ -670,6 +686,10 @@ class BooleanGroup(FieldGroup): fillvalue=False) return dict(values) + def Filter(self, version): + return BooleanGroup( + filter(lambda d: d.version <= version, self.descriptors)) + def _SerializeNativeArray(value, data_offset, data, length): data_size = len(data) diff --git a/third_party/mojo/src/mojo/public/python/mojo_bindings/messaging.py b/third_party/mojo/src/mojo/public/python/mojo_bindings/messaging.py index 385a080..ebdf1ee 100644 --- a/third_party/mojo/src/mojo/public/python/mojo_bindings/messaging.py +++ b/third_party/mojo/src/mojo/public/python/mojo_bindings/messaging.py @@ -30,13 +30,13 @@ class MessagingException(Exception): class MessageHeader(object): """The header of a mojo message.""" - _SIMPLE_MESSAGE_NUM_FIELDS = 2 + _SIMPLE_MESSAGE_VERSION = 2 _SIMPLE_MESSAGE_STRUCT = struct.Struct("<IIII") _REQUEST_ID_STRUCT = struct.Struct("<Q") _REQUEST_ID_OFFSET = _SIMPLE_MESSAGE_STRUCT.size - _MESSAGE_WITH_REQUEST_ID_NUM_FIELDS = 3 + _MESSAGE_WITH_REQUEST_ID_VERSION = 3 _MESSAGE_WITH_REQUEST_ID_SIZE = ( _SIMPLE_MESSAGE_STRUCT.size + _REQUEST_ID_STRUCT.size) @@ -53,11 +53,11 @@ class MessageHeader(object): raise serialization.DeserializationException('Header is too short.') (size, version, message_type, flags) = ( cls._SIMPLE_MESSAGE_STRUCT.unpack_from(buf)) - if (version < cls._SIMPLE_MESSAGE_NUM_FIELDS): + if (version < cls._SIMPLE_MESSAGE_VERSION): raise serialization.DeserializationException('Incorrect version.') request_id = 0 if _HasRequestId(flags): - if version < cls._MESSAGE_WITH_REQUEST_ID_NUM_FIELDS: + if version < cls._MESSAGE_WITH_REQUEST_ID_VERSION: raise serialization.DeserializationException('Incorrect version.') if (size < cls._MESSAGE_WITH_REQUEST_ID_SIZE or len(data) < cls._MESSAGE_WITH_REQUEST_ID_SIZE): @@ -105,10 +105,10 @@ class MessageHeader(object): def Serialize(self): if not self._data: self._data = bytearray(self.size) - version = self._SIMPLE_MESSAGE_NUM_FIELDS + version = self._SIMPLE_MESSAGE_VERSION size = self._SIMPLE_MESSAGE_STRUCT.size if self.has_request_id: - version = self._MESSAGE_WITH_REQUEST_ID_NUM_FIELDS + version = self._MESSAGE_WITH_REQUEST_ID_VERSION size = self._MESSAGE_WITH_REQUEST_ID_SIZE self._SIMPLE_MESSAGE_STRUCT.pack_into(self._data, 0, size, version, self._message_type, self._flags) diff --git a/third_party/mojo/src/mojo/public/python/mojo_bindings/reflection.py b/third_party/mojo/src/mojo/public/python/mojo_bindings/reflection.py index 35b8ff2..cec05fa 100644 --- a/third_party/mojo/src/mojo/public/python/mojo_bindings/reflection.py +++ b/third_party/mojo/src/mojo/public/python/mojo_bindings/reflection.py @@ -12,6 +12,7 @@ import sys import mojo_bindings.messaging as messaging import mojo_bindings.promise as promise import mojo_bindings.serialization as serialization +import mojo_system class MojoEnumType(type): @@ -273,6 +274,10 @@ class InterfaceManager(object): router.Start() + def NewRequest(self): + pipe = mojo_system.MessagePipe() + return (self.Proxy(pipe.handle0), InterfaceRequest(pipe.handle1)) + def _InternalProxy(self, router, error_handler): if error_handler is None: error_handler = _ProxyErrorHandler() @@ -550,6 +555,7 @@ def _StubAccept(methods): # Close the connection in case of error. logging.warning( 'Error occured in accept method. Connection will be closed.') + logging.debug("Exception", exc_info=True) if self.impl.manager: self.impl.manager.Close() return False diff --git a/third_party/mojo/src/mojo/public/python/mojo_bindings/serialization.py b/third_party/mojo/src/mojo/public/python/mojo_bindings/serialization.py index b5ea1bd..8f5cc67 100644 --- a/third_party/mojo/src/mojo/public/python/mojo_bindings/serialization.py +++ b/third_party/mojo/src/mojo/public/python/mojo_bindings/serialization.py @@ -168,11 +168,14 @@ def NeededPaddingForAlignment(value, alignment=8): def _GetVersion(groups): - return sum([len(x.descriptors) for x in groups]) + if not len(groups): + return 0 + return max([x.GetMaxVersion() for x in groups]) def _FilterGroups(groups, version): - return [group for group in groups if group.GetVersion() < version] + return [group.Filter(version) for + group in groups if group.GetMinVersion() <= version] def _GetStruct(groups): diff --git a/third_party/mojo/src/mojo/public/python/mojo_system.pyx b/third_party/mojo/src/mojo/public/python/mojo_system.pyx index 4e684af..d5f164d 100644 --- a/third_party/mojo/src/mojo/public/python/mojo_system.pyx +++ b/third_party/mojo/src/mojo/public/python/mojo_system.pyx @@ -19,7 +19,7 @@ from cpython.mem cimport PyMem_Malloc, PyMem_Free from cpython.object cimport Py_EQ, Py_NE from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t -import ctypes +import weakref import threading import mojo_system_impl @@ -770,7 +770,7 @@ class RunLoop(object): def __init__(self): self.__run_loop = mojo_system_impl.RunLoop() - _RUN_LOOPS.loop = id(self) + _RUN_LOOPS.loop = weakref.ref(self) def __del__(self): del _RUN_LOOPS.loop @@ -797,7 +797,7 @@ class RunLoop(object): @staticmethod def Current(): if hasattr(_RUN_LOOPS, 'loop'): - return ctypes.cast(_RUN_LOOPS.loop, ctypes.py_object).value + return _RUN_LOOPS.loop() return None diff --git a/third_party/mojo/src/mojo/public/python/rules.gni b/third_party/mojo/src/mojo/public/python/rules.gni index 934a94e5..2c1fab1 100644 --- a/third_party/mojo/src/mojo/public/python/rules.gni +++ b/third_party/mojo/src/mojo/public/python/rules.gni @@ -62,6 +62,12 @@ template("python_packaged_application") { mojo_output = "$root_out_dir/" + target_name + ".mojo" } + if (defined(invoker.debug) && invoker.debug) { + content_handler_param = "?debug=true" + } else { + content_handler_param = "" + } + python_package(package_name) { sources = invoker.sources if (defined(invoker.deps)) { @@ -106,7 +112,7 @@ template("python_packaged_application") { args = [ "--input=$rebase_input", "--output=$rebase_output", - "--line=#!mojo mojo:py_content_handler", + "--line=#!mojo mojo:py_content_handler${content_handler_param}", ] } } diff --git a/third_party/mojo/src/mojo/public/tools/BUILD.gn b/third_party/mojo/src/mojo/public/tools/BUILD.gn index 103c259..e03d1d3 100644 --- a/third_party/mojo/src/mojo/public/tools/BUILD.gn +++ b/third_party/mojo/src/mojo/public/tools/BUILD.gn @@ -2,28 +2,64 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/module_args/mojo.gni") import("../mojo.gni") if (use_prebuilt_mojo_shell) { copy("copy_mojo_shell") { filename = "mojo_shell" - if (is_win) { - filename += ".exe" - } if (is_android) { filename = "MojoShell.apk" - } - sources = [ - "prebuilt/$filename", - ] - if (is_android) { + sources = [ + "prebuilt/shell/android-arm/$filename", + ] outputs = [ "$root_out_dir/apks/$filename", ] } else { + assert(is_linux) + sources = [ + "prebuilt/shell/linux-x64/$filename", + ] outputs = [ "$root_out_dir/$filename", ] } } } + +if (use_prebuilt_network_service) { + copy("copy_network_service") { + filename = "network_service.mojo" + if (is_android) { + sources = [ + "prebuilt/network_service/android-arm/$filename", + ] + } else { + assert(is_linux) + sources = [ + "prebuilt/network_service/linux-x64/$filename", + ] + } + outputs = [ + "$root_out_dir/$filename", + ] + } + + copy("copy_network_service_apptests") { + filename = "network_service_apptests.mojo" + if (is_android) { + sources = [ + "prebuilt/network_service_apptests/android-arm/$filename", + ] + } else { + assert(is_linux) + sources = [ + "prebuilt/network_service_apptests/linux-x64/$filename", + ] + } + outputs = [ + "$root_out_dir/$filename", + ] + } +} diff --git a/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION b/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION new file mode 100644 index 0000000..597e852 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION @@ -0,0 +1 @@ +bf9a4b53049b10cfdd85934075ed3e147be889f5
\ No newline at end of file diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl index 25c01bb..2c21d0d 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -6,9 +6,6 @@ class {{interface.name}}RequestValidator; {%- if interface|has_callbacks %} class {{interface.name}}ResponseValidator; {%- endif %} -{% if interface.client %} -class {{interface.client}}; -{% endif %} class {{interface.name}} { public: @@ -23,11 +20,6 @@ class {{interface.name}} { {%- else %} using ResponseValidator_ = mojo::PassThroughFilter; {%- endif %} -{% if interface.client %} - using Client = {{interface.client}}; -{% else %} - using Client = mojo::NoInterface; -{% endif %} {#--- Constants #} {%- for constant in interface.constants %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 2b45808..308c8b6 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -1,90 +1,29 @@ {%- import "interface_macros.tmpl" as interface_macros %} +{%- import "struct_macros.tmpl" as struct_macros %} {%- set class_name = interface.name %} {%- set proxy_name = interface.name ~ "Proxy" %} {%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %} -{%- macro alloc_params(parameters) %} -{%- for param in parameters %} -{%- if param.kind|is_object_kind %} -{{param.kind|cpp_result_type}} p{{loop.index}}; -Deserialize_(params->{{param.name}}.ptr, &p{{loop.index}}); -{% endif -%} +{%- macro alloc_params(struct) %} +{%- for param in struct.packed.packed_fields_in_ordinal_order %} + {{param.field.kind|cpp_result_type}} p_{{param.field.name}}{}; {%- endfor %} + {{struct_macros.deserialize(struct, "params", "p_%s")}} {%- endmacro %} {%- macro pass_params(parameters) %} {%- for param in parameters %} -{%- if param.kind|is_string_kind -%} -p{{loop.index}} -{%- elif param.kind|is_object_kind -%} -p{{loop.index}}.Pass() -{%- elif param.kind|is_interface_kind -%} -mojo::MakeProxy<{{param.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}}))) -{%- elif param.kind|is_interface_request_kind -%} -mojo::MakeRequest<{{param.kind.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}}))) -{%- elif param.kind|is_any_handle_kind -%} -mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}})) -{%- elif param.kind|is_enum_kind -%} -static_cast<{{param.kind|cpp_wrapper_type}}>(params->{{param.name}}) +{%- if param.kind|is_move_only_kind -%} +p_{{param.name}}.Pass() {%- else -%} -params->{{param.name}} +p_{{param.name}} {%- endif -%} {%- if not loop.last %}, {% endif %} {%- endfor %} {%- endmacro %} -{%- macro compute_payload_size(params_name, parameters) -%} - size_t payload_size = - mojo::internal::Align(sizeof({{params_name}})); -{#--- Computes #} -{%- for param in parameters %} -{%- if param.kind|is_object_kind %} - payload_size += GetSerializedSize_(in_{{param.name}}); -{%- endif %} -{%- endfor %} -{%- endmacro %} - -{%- macro build_message(params_name, parameters, params_description) -%} - {# TODO(yzshen): Consider refactoring to share code with - struct_serialization_definition.tmpl #} - {{params_name}}* params = - {{params_name}}::New(builder.buffer()); -{#--- Sets #} -{% for param in parameters %} -{%- if param.kind|is_object_kind %} -{%- if param.kind|is_array_kind %} - mojo::SerializeArray_<{{param.kind|get_array_validate_params|indent(24)}}>( - mojo::internal::Forward(in_{{param.name}}), builder.buffer(), ¶ms->{{param.name}}.ptr); -{%- elif param.kind|is_map_kind %} - mojo::SerializeMap_<{{param.kind.value_kind|get_map_validate_params|indent(24)}}>( - mojo::internal::Forward(in_{{param.name}}), builder.buffer(), ¶ms->{{param.name}}.ptr); -{%- else %} - Serialize_(mojo::internal::Forward(in_{{param.name}}), builder.buffer(), ¶ms->{{param.name}}.ptr); -{%- endif %} -{%- if not param.kind|is_nullable_kind %} - MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( - !params->{{param.name}}.ptr, - mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, - "null {{param.name}} argument in {{params_description}}"); -{%- endif %} -{%- elif param.kind|is_any_handle_kind %} -{%- if param.kind|is_interface_kind or - param.kind|is_interface_request_kind %} - // Delegate handle. - params->{{param.name}} = in_{{param.name}}.PassMessagePipe().release(); -{%- else %} - params->{{param.name}} = in_{{param.name}}.release(); -{%- endif %} -{%- if not param.kind|is_nullable_kind %} - MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( - !params->{{param.name}}.is_valid(), - mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, - "invalid {{param.name}} argument in {{params_description}}"); -{%- endif %} -{%- else %} - params->{{param.name}} = in_{{param.name}}; -{%- endif %} -{%- endfor %} +{%- macro build_message(struct, struct_display_name) -%} + {{struct_macros.serialize(struct, struct_display_name, "in_%s", "params", "builder.buffer()")}} mojo::Message message; params->EncodePointersAndHandles(message.mutable_handles()); builder.Finish(&message); @@ -119,7 +58,7 @@ bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( message->mutable_payload()); params->DecodePointersAndHandles(message->mutable_handles()); - {{alloc_params(method.response_parameters)|indent(2)}} + {{alloc_params(method|response_struct_from_method)}} callback_.Run({{pass_params(method.response_parameters)}}); return true; } @@ -135,21 +74,20 @@ bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( {%- for method in interface.methods %} {%- set message_name = "internal::k%s_%s_Name"|format(interface.name, method.name) %} -{%- set params_name = - "internal::%s_%s_Params_Data"|format(interface.name, method.name) %} +{%- set params_struct = method|struct_from_method %} {%- set params_description = "%s.%s request"|format(interface.name, method.name) %} void {{proxy_name}}::{{method.name}}( {{interface_macros.declare_request_params("in_", method)}}) { - {{compute_payload_size(params_name, method.parameters)}} + {{struct_macros.get_serialized_size(params_struct, "in_%s")}} {%- if method.response_parameters != None %} - mojo::internal::RequestMessageBuilder builder({{message_name}}, payload_size); + mojo::internal::RequestMessageBuilder builder({{message_name}}, size); {%- else %} - mojo::internal::MessageBuilder builder({{message_name}}, payload_size); + mojo::internal::MessageBuilder builder({{message_name}}, size); {%- endif %} - {{build_message(params_name, method.parameters, params_description)}} + {{build_message(params_struct, params_description)}} {%- if method.response_parameters != None %} mojo::MessageReceiver* responder = @@ -170,8 +108,7 @@ void {{proxy_name}}::{{method.name}}( {%- if method.response_parameters != None %} {%- set message_name = "internal::k%s_%s_Name"|format(interface.name, method.name) %} -{%- set params_name = - "internal::%s_%s_ResponseParams_Data"|format(interface.name, method.name) %} +{%- set response_params_struct = method|response_struct_from_method %} {%- set params_description = "%s.%s response"|format(interface.name, method.name) %} class {{class_name}}_{{method.name}}_ProxyToResponder @@ -197,10 +134,10 @@ class {{class_name}}_{{method.name}}_ProxyToResponder }; void {{class_name}}_{{method.name}}_ProxyToResponder::Run( {{interface_macros.declare_params("in_", method.response_parameters)}}) const { - {{compute_payload_size(params_name, method.response_parameters)}} + {{struct_macros.get_serialized_size(response_params_struct, "in_%s")}} mojo::internal::ResponseMessageBuilder builder( - {{message_name}}, payload_size, request_id_); - {{build_message(params_name, method.response_parameters, params_description)}} + {{message_name}}, size, request_id_); + {{build_message(response_params_struct, params_description)}} bool ok = responder_->Accept(&message); MOJO_ALLOW_UNUSED_LOCAL(ok); // TODO(darin): !ok returned here indicates a malformed message, and that may @@ -229,9 +166,8 @@ bool {{class_name}}Stub::Accept(mojo::Message* message) { message->mutable_payload()); params->DecodePointersAndHandles(message->mutable_handles()); - {{alloc_params(method.parameters)|indent(6)}} - // A null |sink_| typically means there is a missing call to - // InterfacePtr::set_client(). + {{alloc_params(method|struct_from_method)|indent(4)}} + // A null |sink_| means no implementation was bound. assert(sink_); sink_->{{method.name}}({{pass_params(method.parameters)}}); return true; @@ -261,9 +197,8 @@ bool {{class_name}}Stub::AcceptWithResponder( new {{class_name}}_{{method.name}}_ProxyToResponder( message->request_id(), responder); {{class_name}}::{{method.name}}Callback callback(runnable); - {{alloc_params(method.parameters)|indent(6)}} - // A null |sink_| typically means there is a missing call to - // InterfacePtr::set_client(). + {{alloc_params(method|struct_from_method)|indent(4)}} + // A null |sink_| means no implementation was bound. assert(sink_); sink_->{{method.name}}( {%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback); @@ -311,8 +246,6 @@ bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { } {%- endif %} - // A null |sink_| typically means there is a missing call to - // InterfacePtr::set_client(). assert(sink_); return sink_->Accept(message); } @@ -343,8 +276,6 @@ bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { } {%- endif %} - // A null |sink_| typically means there is a missing call to - // InterfacePtr::set_client(). assert(sink_); return sink_->Accept(message); } diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl index 863d18f..969bea2 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl @@ -47,10 +47,12 @@ namespace { {%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %} const uint32_t {{method_name}} = {{method.ordinal}}; {% set struct = method|struct_from_method %} -{%- include "params_definition.tmpl" %} +{% include "struct_declaration.tmpl" %} +{%- include "struct_definition.tmpl" %} {%- if method.response_parameters != None %} {%- set struct = method|response_struct_from_method %} -{%- include "params_definition.tmpl" %} +{% include "struct_declaration.tmpl" %} +{%- include "struct_definition.tmpl" %} {%- endif %} {%- endfor %} {%- endfor %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl deleted file mode 100644 index 0892b64..0000000 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl +++ /dev/null @@ -1,33 +0,0 @@ -{%- import "struct_macros.tmpl" as struct_macros %} -{%- set class_name = struct.name ~ "_Data" %} -class {{class_name}} { - public: - static {{class_name}}* New(mojo::internal::Buffer* buf) { - return new (buf->Allocate(sizeof({{class_name}}))) - {{class_name}}(); - } - - static bool Validate(const void* data, - mojo::internal::BoundsChecker* bounds_checker) { - {{ struct_macros.validate(struct, class_name)|indent(4) }} - } - - mojo::internal::StructHeader header_; -{{struct_macros.fields(struct)}} - - void EncodePointersAndHandles(std::vector<mojo::Handle>* handles) { - {{ struct_macros.encodes(struct)|indent(4) }} - } - - void DecodePointersAndHandles(std::vector<mojo::Handle>* handles) { - {{ struct_macros.decodes(struct)|indent(4) }} - } - - private: - {{class_name}}() { - header_.num_bytes = sizeof(*this); - header_.num_fields = {{struct.packed.packed_fields|length}}; - } -}; -static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}}, - "Bad sizeof({{class_name}})"); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl index ee8934e..255005a 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl @@ -1,4 +1,3 @@ -{%- import "struct_macros.tmpl" as struct_macros %} {%- set class_name = struct.name ~ "_Data" -%} class {{class_name}} { @@ -8,12 +7,39 @@ class {{class_name}} { static bool Validate(const void* data, mojo::internal::BoundsChecker* bounds_checker); - mojo::internal::StructHeader header_; -{{struct_macros.fields(struct)}} - void EncodePointersAndHandles(std::vector<mojo::Handle>* handles); void DecodePointersAndHandles(std::vector<mojo::Handle>* handles); + mojo::internal::StructHeader header_; +{%- for packed_field in struct.packed.packed_fields %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- if kind.spec == 'b' %} + uint8_t {{name}} : 1; +{%- elif kind|is_enum_kind %} + int32_t {{name}}; +{%- else %} + {{kind|cpp_field_type}} {{name}}; +{%- endif %} +{%- if not loop.last %} +{%- set next_pf = struct.packed.packed_fields[loop.index0 + 1] %} +{%- set pad = next_pf.offset - (packed_field.offset + packed_field.size) %} +{%- if pad > 0 %} + uint8_t pad{{loop.index0}}_[{{pad}}]; +{%- endif %} +{%- endif %} +{%- endfor %} + +{%- set num_fields = struct.versions[-1].num_fields %} +{%- if num_fields > 0 %} +{%- set last_field = struct.packed.packed_fields[num_fields - 1] %} +{%- set offset = last_field.offset + last_field.size %} +{%- set pad = offset|get_pad(8) %} +{%- if pad > 0 %} + uint8_t padfinal_[{{pad}}]; +{%- endif %} +{%- endif %} + private: {{class_name}}(); ~{{class_name}}() = delete; diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl index 461f158..79a0bc4 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl @@ -1,6 +1,69 @@ -{%- import "struct_macros.tmpl" as struct_macros %} {%- set class_name = struct.name ~ "_Data" %} +{#- TODO(yzshen): Consider eliminating _validate_object() and + _validate_handle(). #} + +{#- Validates the specified struct field, which is supposed to be an object + (struct/array/string/map/union). + This macro is expanded by the Validate() method. #} +{%- macro _validate_object(struct, packed_field) %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- set wrapper_type = kind|cpp_wrapper_type %} +{%- if not kind|is_nullable_kind %} + if (!object->{{name}}.offset) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{name}} field in {{struct.name}} struct"); + return false; + } +{%- endif %} + if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER); + return false; + } +{%- if kind|is_array_kind or kind|is_string_kind %} + if (!{{wrapper_type}}::Data_::Validate< + {{kind|get_array_validate_params|indent(10)}}>( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- elif kind|is_map_kind %} + if (!{{wrapper_type}}::Data_::Validate< + {{kind.value_kind|get_map_validate_params|indent(10)}}>( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- elif kind|is_struct_kind %} + if (!{{kind|get_name_for_kind}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- else %} + if (!{{wrapper_type}}::Data_::Validate( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- endif %} + return false; + } +{%- endmacro %} + +{#- Validates the specified struct field, which is supposed to be a handle. + This macro is expanded by the Validate() method. #} +{%- macro _validate_handle(struct, packed_field) %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- if not kind|is_nullable_kind %} + if (object->{{name}}.value() == mojo::internal::kEncodedInvalidHandleValue) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + "invalid {{name}} field in {{struct.name}} struct"); + return false; + } +{%- endif %} + if (!bounds_checker->ClaimHandle(object->{{name}})) { + ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE); + return false; + } +{%- endmacro %} + // static {{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) { return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}(); @@ -9,20 +72,109 @@ // static bool {{class_name}}::Validate(const void* data, mojo::internal::BoundsChecker* bounds_checker) { - {{ struct_macros.validate(struct, class_name)|indent(2) }} -} + if (!data) + return true; -{{class_name}}::{{class_name}}() { - header_.num_bytes = sizeof(*this); - header_.num_fields = {{struct.packed.packed_fields|length}}; + if (!ValidateStructHeaderAndClaimMemory(data, bounds_checker)) + return false; + + // NOTE: The memory backing |object| may be smaller than |sizeof(*object)| if + // the message comes from an older version. + const {{class_name}}* object = static_cast<const {{class_name}}*>(data); + + static const struct { + uint32_t version; + uint32_t num_bytes; + } kVersionSizes[] = { +{%- for version in struct.versions -%} + { {{version.version}}, {{version.num_bytes}} }{% if not loop.last %}, {% endif -%} +{%- endfor -%} + }; + + if (object->header_.version <= + kVersionSizes[MOJO_ARRAYSIZE(kVersionSizes) - 1].version) { + // Scan in reverse order to optimize for more recent versions. + for (int i = MOJO_ARRAYSIZE(kVersionSizes) - 1; i >= 0; --i) { + if (object->header_.version >= kVersionSizes[i].version) { + if (object->header_.num_bytes == kVersionSizes[i].num_bytes) + break; + + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } + } else if (object->header_.num_bytes < + kVersionSizes[MOJO_ARRAYSIZE(kVersionSizes) - 1].num_bytes) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + +{#- Before validating fields introduced at a certain version, we need to add + a version check, which makes sure we skip further validation if |object| + is from an earlier version. |last_checked_version| records the last + version that we have added such version check. #} +{%- set last_checked_version = 0 %} +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} +{%- set kind = packed_field.field.kind %} +{%- if kind|is_object_kind or kind|is_any_handle_kind %} +{%- if packed_field.min_version > last_checked_version %} +{%- set last_checked_version = packed_field.min_version %} + if (object->header_.version < {{packed_field.min_version}}) + return true; +{%- endif %} +{%- if kind|is_object_kind %} +{{_validate_object(struct, packed_field)}} +{%- elif kind|is_any_handle_kind %} +{{_validate_handle(struct, packed_field)}} +{%- endif %} +{%- endif %} +{%- endfor %} + + return true; } void {{class_name}}::EncodePointersAndHandles( std::vector<mojo::Handle>* handles) { - {{ struct_macros.encodes(struct)|indent(2) }} + MOJO_CHECK(header_.version == {{struct.versions[-1].version}}); +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- if pf.field.kind|is_object_kind %} + mojo::internal::Encode(&{{pf.field.name}}, handles); +{%- elif pf.field.kind|is_any_handle_kind %} + mojo::internal::EncodeHandle(&{{pf.field.name}}, handles); +{%- endif %} +{%- endfor %} } void {{class_name}}::DecodePointersAndHandles( std::vector<mojo::Handle>* handles) { - {{ struct_macros.decodes(struct)|indent(2) }} + // NOTE: The memory backing |this| may has be smaller than |sizeof(*this)|, if + // the message comes from an older version. +{#- Before decoding fields introduced at a certain version, we need to add + a version check, which makes sure we skip further decoding if |this| + is from an earlier version. |last_checked_version| records the last + version that we have added such version check. #} +{%- set last_checked_version = 0 %} +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if kind|is_object_kind or kind|is_any_handle_kind %} +{%- if pf.min_version > last_checked_version %} +{%- set last_checked_version = pf.min_version %} + if (header_.version < {{pf.min_version}}) + return; +{%- endif %} +{%- if kind|is_object_kind %} + mojo::internal::Decode(&{{name}}, handles); +{%- else %} + mojo::internal::DecodeHandle(&{{name}}, handles); +{%- endif %} +{%- endif %} +{%- endfor %} +} + +{{class_name}}::{{class_name}}() { + header_.num_bytes = sizeof(*this); + header_.version = {{struct.versions[-1].version}}; } diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl index 623ebb4..03911b9 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl @@ -1,124 +1,118 @@ -{%- macro validate(struct, class_name) %} - if (!data) - return true; +{# TODO(yzshen): Make these templates more readable. #} - if (!ValidateStructHeader( - data, sizeof({{class_name}}), - {{struct.packed.packed_fields|length}}, bounds_checker)) { - return false; - } - - const {{class_name}}* object = static_cast<const {{class_name}}*>(data); - MOJO_ALLOW_UNUSED_LOCAL(object); - -{%- for packed_field in struct.packed.packed_fields %} -{%- set name = packed_field.field.name %} -{%- set kind = packed_field.field.kind %} -{%- if kind|is_object_kind %} -{%- set wrapper_type = kind|cpp_wrapper_type %} -{%- if not kind|is_nullable_kind %} - if (!object->{{name}}.offset) { - ReportValidationError( - mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, - "null {{name}} field in {{struct.name}} struct"); - return false; - } -{%- endif %} - if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) { - ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER); - return false; - } -{%- if kind|is_array_kind or kind|is_string_kind %} - if (!{{wrapper_type}}::Data_::Validate< - {{kind|get_array_validate_params|indent(10)}}>( - mojo::internal::DecodePointerRaw(&object->{{name}}.offset), - bounds_checker)) { -{%- elif kind|is_map_kind %} - if (!{{wrapper_type}}::Data_::Validate< - {{kind.value_kind|get_map_validate_params|indent(10)}}>( - mojo::internal::DecodePointerRaw(&object->{{name}}.offset), - bounds_checker)) { -{%- elif kind|is_struct_kind %} - if (!{{kind|get_name_for_kind}}::Data_::Validate( - mojo::internal::DecodePointerRaw(&object->{{name}}.offset), - bounds_checker)) { -{%- else %} - if (!{{wrapper_type}}::Data_::Validate( - mojo::internal::DecodePointerRaw(&object->{{name}}.offset), - bounds_checker)) { -{%- endif %} - return false; - } -{%- elif kind|is_any_handle_kind %} -{%- if not kind|is_nullable_kind %} - if (object->{{name}}.value() == mojo::internal::kEncodedInvalidHandleValue) { - ReportValidationError( - mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, - "invalid {{name}} field in {{struct.name}} struct"); - return false; - } -{%- endif %} - if (!bounds_checker->ClaimHandle(object->{{name}})) { - ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE); - return false; - } -{%- endif %} +{# Computes the serialized size for the specified struct. + |struct| is the struct definition. + |input_field_pattern| should be a pattern that contains one string + placeholder, for example, "input->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the input fields. + This macro is expanded to compute seriailized size for both: + - user-defined structs: the input is an instance of the corresponding struct + wrapper class. + - method parameters/response parameters: the input is a list of + arguments. + It declares |size| of type size_t to store the resulting size. #} +{%- macro get_serialized_size(struct, input_field_pattern) -%} + size_t size = sizeof(internal::{{struct.name}}_Data); +{%- for pf in struct.packed.packed_fields_in_ordinal_order if pf.field.kind|is_object_kind %} + size += GetSerializedSize_({{input_field_pattern|format(pf.field.name)}}); {%- endfor %} +{%- endmacro -%} - return true; -{%- endmacro %} - -{%- macro field_line(field) %} -{%- set type = field.kind|cpp_field_type %} -{%- set name = field.name -%} -{%- if field.kind.spec == 'b' -%} - uint8_t {{name}} : 1; -{%- elif field.kind|is_enum_kind -%} - int32_t {{name}}; -{%- else -%} - {{type}} {{name}}; -{%- endif %} -{%- endmacro %} - -{%- macro fields(struct) %} -{%- for packed_field in struct.packed.packed_fields %} - {{field_line(packed_field.field)}} -{%- if not loop.last %} -{%- set next_pf = struct.packed.packed_fields[loop.index0 + 1] %} -{%- set pad = next_pf.offset - (packed_field.offset + packed_field.size) %} -{%- if pad > 0 %} - uint8_t pad{{loop.index0}}_[{{pad}}]; -{%- endif %} +{# Serializes the specified struct. + |struct| is the struct definition. + |struct_display_name| is the display name for the struct that can be showed + in error/log messages, for example, "FooStruct", "FooMethod request". + |input_field_pattern| should be a pattern that contains one string + placeholder, for example, "input->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the input fields. + |output| is the name of the output struct instance. + |buffer| is the name of the Buffer instance used. + This macro is expanded to do serialization for both: + - user-defined structs: the input is an instance of the corresponding struct + wrapper class. + - method parameters/response parameters: the input is a list of + arguments. #} +{%- macro serialize(struct, struct_display_name, input_field_pattern, output, buffer) -%} + internal::{{struct.name}}_Data* {{output}} = + internal::{{struct.name}}_Data::New({{buffer}}); +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set input_field = input_field_pattern|format(pf.field.name) %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if kind|is_object_kind %} +{%- if kind|is_array_kind %} + mojo::SerializeArray_<{{kind|get_array_validate_params|indent(24)}}>( + mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr); +{%- elif kind|is_map_kind %} + mojo::SerializeMap_<{{kind.value_kind|get_map_validate_params|indent(24)}}>( + mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr); +{%- else %} + Serialize_(mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr); {%- endif %} -{%- endfor -%} - -{%- set num_fields = struct.packed.packed_fields|length %} -{%- if num_fields > 0 %} -{%- set last_field = struct.packed.packed_fields[num_fields - 1] %} -{%- set offset = last_field.offset + last_field.size %} -{%- set pad = offset|get_pad(8) -%} -{%- if pad > 0 %} - uint8_t padfinal_[{{pad}}]; +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !{{output}}->{{name}}.ptr, + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{name}} in {{struct_display_name}}"); {%- endif %} -{%- endif %} -{%- endmacro %} - -{%- macro encodes(struct) -%} -{%- for pf in struct.packed.packed_fields %} -{%- if pf.field.kind|is_object_kind %} -mojo::internal::Encode(&{{pf.field.name}}, handles); -{%- elif pf.field.kind|is_any_handle_kind %} -mojo::internal::EncodeHandle(&{{pf.field.name}}, handles); +{%- elif kind|is_any_handle_kind %} +{%- if kind|is_interface_kind or kind|is_interface_request_kind %} + {{output}}->{{name}} = {{input_field}}.PassMessagePipe().release(); +{%- else %} + {{output}}->{{name}} = {{input_field}}.release(); {%- endif %} -{%- endfor %} +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !{{output}}->{{name}}.is_valid(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, + "invalid {{name}} in {{struct_display_name}}"); +{%- endif %} +{%- else %} + {{output}}->{{name}} = {{input_field}}; +{%- endif %} +{%- endfor %} {%- endmacro -%} -{%- macro decodes(struct) -%} -{%- for pf in struct.packed.packed_fields %} -{%- if pf.field.kind|is_object_kind %} -mojo::internal::Decode(&{{pf.field.name}}, handles); -{%- elif pf.field.kind|is_any_handle_kind %} -mojo::internal::DecodeHandle(&{{pf.field.name}}, handles); +{# Deserializes the specified struct. + |struct| is the struct definition. + |input| is the name of the input struct instance. + |output_field_pattern| should be a pattern that contains one string + placeholder, for example, "result->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the output fields. + This macro is expanded to do deserialization for both: + - user-defined structs: the output is an instance of the corresponding + struct wrapper class. + - method parameters/response parameters: the output is a list of + arguments. #} +{%- macro deserialize(struct, input, output_field_pattern) -%} + do { + // NOTE: The memory backing |{{input}}| may has be smaller than + // |sizeof(*{{input}})| if the message comes from an older version. +{#- Before deserialize fields introduced at a certain version, we need to add + a version check, which makes sure we skip further deserialization if + |input| is from an earlier version. |last_checked_version| records the + last version that we have added such version check. #} +{%- set last_checked_version = 0 %} +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set output_field = output_field_pattern|format(pf.field.name) %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if pf.min_version > last_checked_version %} +{%- set last_checked_version = pf.min_version %} + if ({{input}}->header_.version < {{pf.min_version}}) + break; +{%- endif %} +{%- if kind|is_object_kind %} + Deserialize_({{input}}->{{name}}.ptr, &{{output_field}}); +{%- elif kind|is_interface_kind or kind|is_interface_request_kind %} + {{output_field}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&{{input}}->{{name}}))); +{%- elif kind|is_any_handle_kind %} + {{output_field}}.reset(mojo::internal::FetchAndReset(&{{input}}->{{name}})); +{%- elif kind|is_enum_kind %} + {{output_field}} = static_cast<{{kind|cpp_wrapper_type}}>({{input}}->{{name}}); +{%- else %} + {{output_field}} = {{input}}->{{name}}; {%- endif %} {%- endfor %} -{%- endmacro -%} + } while (false); +{%- endmacro %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl index 1612bbe..fe25553 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl @@ -1,51 +1,15 @@ +{%- import "struct_macros.tmpl" as struct_macros %} size_t GetSerializedSize_(const {{struct.name}}Ptr& input) { if (!input) return 0; - size_t size = sizeof(internal::{{struct.name}}_Data); -{%- for pf in struct.packed.packed_fields if pf.field.kind|is_object_kind %} - size += GetSerializedSize_(input->{{pf.field.name}}); -{%- endfor %} + {{struct_macros.get_serialized_size(struct, "input->%s")}} return size; } void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buf, internal::{{struct.name}}_Data** output) { if (input) { - internal::{{struct.name}}_Data* result = - internal::{{struct.name}}_Data::New(buf); -{%- for pf in struct.packed.packed_fields %} -{%- if pf.field.kind|is_object_kind %} -{%- if pf.field.kind|is_array_kind %} - mojo::SerializeArray_<{{pf.field.kind|get_array_validate_params|indent(26)}}>( - mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr); -{%- elif pf.field.kind|is_map_kind %} - mojo::SerializeMap_<{{pf.field.kind.value_kind|get_map_validate_params|indent(26)}}>( - mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr); -{%- else %} - Serialize_(mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr); -{%- endif %} -{%- if not pf.field.kind|is_nullable_kind %} - MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( - !result->{{pf.field.name}}.ptr, - mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, - "null {{pf.field.name}} field in {{struct.name}} struct"); -{%- endif %} -{%- elif pf.field.kind|is_any_handle_kind %} -{%- if pf.field.kind|is_interface_kind %} - result->{{pf.field.name}} = input->{{pf.field.name}}.PassMessagePipe().release(); -{%- else %} - result->{{pf.field.name}} = input->{{pf.field.name}}.release(); -{%- endif %} -{%- if not pf.field.kind|is_nullable_kind %} - MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( - !result->{{pf.field.name}}.is_valid(), - mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, - "invalid {{pf.field.name}} field in {{struct.name}} struct"); -{%- endif %} -{%- else %} - result->{{pf.field.name}} = input->{{pf.field.name}}; -{%- endif %} -{%- endfor %} + {{struct_macros.serialize(struct, struct.name ~ " struct", "input->%s", "result", "buf")|indent(2)}} *output = result; } else { *output = nullptr; @@ -56,21 +20,7 @@ void Deserialize_(internal::{{struct.name}}_Data* input, {{struct.name}}Ptr* output) { if (input) { {{struct.name}}Ptr result({{struct.name}}::New()); -{%- for pf in struct.packed.packed_fields %} -{%- if pf.field.kind|is_object_kind %} - Deserialize_(input->{{pf.field.name}}.ptr, &result->{{pf.field.name}}); -{%- elif pf.field.kind|is_interface_kind %} - if (input->{{pf.field.name}}.is_valid()) - result->{{pf.field.name}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&input->{{pf.field.name}}))); -{%- elif pf.field.kind|is_any_handle_kind %} - result->{{pf.field.name}}.reset(mojo::internal::FetchAndReset(&input->{{pf.field.name}})); -{%- elif pf.field.kind|is_enum_kind %} - result->{{pf.field.name}} = static_cast<{{pf.field.kind|cpp_wrapper_type}}>( - input->{{pf.field.name}}); -{%- else %} - result->{{pf.field.name}} = input->{{pf.field.name}}; -{%- endif %} -{%- endfor %} + {{struct_macros.deserialize(struct, "input", "result->%s")|indent(2)}} *output = result.Pass(); } else { output->reset(); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl index ebcba7b..6242be4 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl @@ -32,7 +32,7 @@ class {{struct.name}} { {%- endif %} bool Equals(const {{struct.name}}& other) const; -{#--- Getters #} +{#--- Struct members #} {% for field in struct.fields %} {%- set type = field.kind|cpp_wrapper_type %} {%- set name = field.name %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl index a73ad8e..f8f92dc9 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl @@ -42,6 +42,19 @@ abstract class {{interface|name}} implements core.Listener { [Function responseFactory = null]); {%- endif %} {%- endfor %} + + +{#--- Interface Constants #} +{% for constant in interface.constants %} + static final {{constant|name}} = {{constant.value|expression_to_text}}; +{%- endfor %} + + +{#--- Interface Enums #} +{%- from "enum_definition.tmpl" import enum_def -%} +{%- for enum in interface.enums %} + {{ enum_def(" static ", enum) }} +{%- endfor %} } class {{interface|name}}Proxy extends bindings.Proxy implements {{interface|name}} { @@ -200,14 +213,3 @@ class {{interface|name}}Stub extends bindings.Stub { {#--- TODO(zra): Validation #} -{#--- Interface Constants #} -{% for constant in interface.constants %} -final {{constant|name}} = {{constant.value|expression_to_text}}; -{%- endfor %} - - -{#--- Interface Enums #} -{%- from "enum_definition.tmpl" import enum_def -%} -{%- for enum in interface.enums %} - {{ enum_def("", enum) }} -{%- endfor %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl index 93a3e42..7538e8b 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl @@ -12,7 +12,7 @@ if ({{variable}} == null) { {%- endif %} var encoder{{level + 1}} = encoder{{level}}.encodePointerArray({{variable}}.length, {{offset}}, {{kind|array_expected_length}}); for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) { - {{encode(variable~'[i'~level~']', sub_kind, 'bindings.DataHeader.kHeaderSize + bindings.kPointerSize * i'~level, 0, level+1)|indent(4)}} + {{encode(variable~'[i'~level~']', sub_kind, 'bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i'~level, 0, level+1)|indent(4)}} } } {%- elif kind|is_map_kind %} @@ -23,8 +23,8 @@ if ({{variable}} == null) { int size{{level}} = {{variable}}.length; var keys{{level}} = {{variable}}.keys.toList(); var values{{level}} = {{variable}}.values.toList(); - {{encode('keys'~level, kind.key_kind|array, 'bindings.DataHeader.kHeaderSize', 0, level+1, False)|indent(2)}} - {{encode('values'~level, kind.value_kind|array, 'bindings.DataHeader.kHeaderSize + bindings.kPointerSize', 0, level+1, False)|indent(2)}} + {{encode('keys'~level, kind.key_kind|array, 'bindings.ArrayDataHeader.kHeaderSize', 0, level+1, False)|indent(2)}} + {{encode('values'~level, kind.value_kind|array, 'bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize', 0, level+1, False)|indent(2)}} } {%- else %} encoder{{level}}.{{kind|encode_method(variable, offset, bit)}}; @@ -50,18 +50,18 @@ if (decoder{{level+1}} == null) { List<{{kind.key_kind|dart_type}}> keys{{level}}; List<{{kind.value_kind|dart_type}}> values{{level}}; { - {{decode('keys'~level, kind.key_kind|array, 'bindings.DataHeader.kHeaderSize', 0, level+1)|indent(4)}} + {{decode('keys'~level, kind.key_kind|array, 'bindings.ArrayDataHeader.kHeaderSize', 0, level+1)|indent(4)}} } { - {{decode('values'~level, kind.value_kind|array('keys'~level~'.length'), 'bindings.DataHeader.kHeaderSize + bindings.kPointerSize', 0, level+1)|indent(4)}} + {{decode('values'~level, kind.value_kind|array('keys'~level~'.length'), 'bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize', 0, level+1)|indent(4)}} } {{variable}} = new Map<{{kind.key_kind|dart_type}}, {{kind.value_kind|dart_type}}>.fromIterables( keys{{level}}, values{{level}}); {%- else %} var si{{level+1}} = decoder{{level+1}}.decodeDataHeaderForPointerArray({{kind|array_expected_length}}); - {{variable}} = new {{kind|dart_type}}(si{{level+1}}.numFields); - for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.numFields; ++i{{level+1}}) { - {{decode(variable~'[i'~(level+1)~']', kind.kind, 'bindings.DataHeader.kHeaderSize + bindings.kPointerSize * i'~(level+1), 0, level+1)|indent(4)}} + {{variable}} = new {{kind|dart_type}}(si{{level+1}}.numElements); + for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.numElements; ++i{{level+1}}) { + {{decode(variable~'[i'~(level+1)~']', kind.kind, 'bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i'~(level+1), 0, level+1)|indent(4)}} } {%- endif %} } @@ -75,8 +75,8 @@ if (decoder{{level+1}} == null) { {%- macro struct_def(struct) %} class {{struct|name}} extends bindings.Struct { static const int kStructSize = {{struct.versions[-1].num_bytes}}; - static const bindings.DataHeader kDefaultStructInfo = - const bindings.DataHeader(kStructSize, {{struct.packed.packed_fields|length}}); + static const bindings.StructDataHeader kDefaultStructInfo = + const bindings.StructDataHeader(kStructSize, {{struct.versions[-1].version}}); {#--- Enums #} {%- from "enum_definition.tmpl" import enum_def %} @@ -107,15 +107,15 @@ class {{struct|name}} extends bindings.Struct { } {{struct|name}} result = new {{struct|name}}(); - var mainDataHeader = decoder0.decodeDataHeader(); + var mainDataHeader = decoder0.decodeStructDataHeader(); if ((mainDataHeader.size < kStructSize) || - (mainDataHeader.numFields < {{struct.packed.packed_fields|length}})) { + (mainDataHeader.version < {{struct.versions[-1].version}})) { throw new bindings.MojoCodecError('Malformed header'); } {%- for byte in struct.bytes %} {%- for packed_field in byte.packed_fields %} - if (mainDataHeader.numFields > {{packed_field.ordinal}}) { + { {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(6)}} } {%- endfor %} @@ -125,9 +125,9 @@ class {{struct|name}} extends bindings.Struct { void encode(bindings.Encoder encoder) { {%- if not struct.bytes %} - encoder.getEncoderAtOffset(kDefaultStructInfo); + encoder.getStructEncoderAtOffset(kDefaultStructInfo); {%- else %} - var encoder0 = encoder.getEncoderAtOffset(kDefaultStructInfo); + var encoder0 = encoder.getStructEncoderAtOffset(kDefaultStructInfo); {%- endif %} {%- for byte in struct.bytes %} {%- for packed_field in byte.packed_fields %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/source.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/source.tmpl index 3769585..d691cde 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/source.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/source.tmpl @@ -12,13 +12,13 @@ package {{package}} import ( "fmt" - - "mojo/public/go/bindings" "mojo/public/go/system" +{% for i in imports %} + {{i}} +{% endfor %} ) var _ = fmt.Errorf -var _ = bindings.NewEncoder var _ = system.GetCore {% import "enum.tmpl" as enum_macros %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/struct.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/struct.tmpl index eb6b121..c9f044a 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/struct.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/struct.tmpl @@ -10,7 +10,7 @@ type {{struct|name(exported)}} struct { } func (s *{{struct|name(exported)}}) Encode(encoder *bindings.Encoder) error { - encoder.StartStruct({{struct.versions[-1].num_bytes}}, {{struct.packed.packed_fields|length}}) + encoder.StartStruct({{struct.versions[-1].num_bytes}}, {{struct.versions[-1].version}}) {% for byte in struct.bytes %} {% for packed_field in byte.packed_fields %} {{encode('s.'~packed_field.field|name(exported), packed_field.field.kind)|tab_indent()}} @@ -24,7 +24,7 @@ func (s *{{struct|name(exported)}}) Encode(encoder *bindings.Encoder) error { func (s *{{struct|name(exported)}}) Decode(decoder *bindings.Decoder) error { {% if struct.bytes %} - numFields, err := decoder.StartStruct() + version, err := decoder.StartStruct() {% else %} _, err := decoder.StartStruct() {% endif %} @@ -33,7 +33,7 @@ func (s *{{struct|name(exported)}}) Decode(decoder *bindings.Decoder) error { } {% for byte in struct.bytes %} {% for packed_field in byte.packed_fields %} - if numFields > {{packed_field.ordinal}} { + if version >= {{packed_field.min_version}} { {{decode('s.'~packed_field.field|name(exported), packed_field.field.kind)|tab_indent(2)}} } {% endfor %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl index 10e5d7a..a13be3e 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl @@ -1,4 +1,4 @@ {% from "interface_definition.tmpl" import interface_def %} {% include "header.java.tmpl" %} -{{ interface_def(interface, client) }} +{{ interface_def(interface) }} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl index 941fec7..e38c895 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl @@ -36,14 +36,6 @@ interface {{method|interface_response_name}} extends org.chromium.mojo.bindings. {%- endif -%} {%- endmacro -%} -{%- macro super_class(client, with_generic=True) -%} -{%- if client -%} -org.chromium.mojo.bindings.InterfaceWithClient{% if with_generic %}<{{client|java_type}}>{% endif %} -{%- else -%} -org.chromium.mojo.bindings.Interface -{%- endif -%} -{%- endmacro -%} - {%- macro flags_for_method(method, is_parameter) -%} {{flags(method.response_parameters, is_parameter)}} {%- endmacro -%} @@ -58,15 +50,13 @@ org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG {%- endif -%} {%- endmacro -%} -{%- macro manager_class(interface, client, fully_qualified=False) -%} -{% if fully_qualified %}{{super_class(client, False)}}.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy -{%- if client -%}, {{client|java_type}}{%- endif -%} -> +{%- macro manager_class(interface, fully_qualified=False) -%} +{% if fully_qualified %}org.chromium.mojo.bindings.Interface.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy> {%- endmacro -%} -{%- macro manager_def(interface, client) -%} -public static final {{manager_class(interface, client, True)}} MANAGER = - new {{manager_class(interface, client, True)}}() { +{%- macro manager_def(interface) -%} +public static final {{manager_class(interface, True)}} MANAGER = + new {{manager_class(interface, True)}}() { public String getName() { return "{{namespace|replace(".","::")}}::{{interface.name}}"; @@ -84,12 +74,6 @@ public static final {{manager_class(interface, client, True)}} MANAGER = public {{interface|name}}[] buildArray(int size) { return new {{interface|name}}[size]; } -{% if client %} - - protected org.chromium.mojo.bindings.Interface.Manager<{{client|java_type}}, ?> getClientManager() { - return {{client|java_type}}.MANAGER; - } -{% endif %} }; {%- endmacro -%} @@ -134,8 +118,8 @@ return false; {% endif %} {%- endmacro -%} -{% macro interface_def(interface, client) %} -public interface {{interface|name}} extends {{super_class(client)}} { +{% macro interface_def(interface) %} +public interface {{interface|name}} extends org.chromium.mojo.bindings.Interface { {% for constant in interface.constants %} {{constant_def(constant)|indent(4)}} @@ -145,10 +129,10 @@ public interface {{interface|name}} extends {{super_class(client)}} { {{enum_def(enum, false)|indent(4)}} {% endfor %} - public interface Proxy extends {{interface|name}}, {{super_class(client, False)}}.Proxy{% if client %}<{{client|java_type}}>{% endif %} { + public interface Proxy extends {{interface|name}}, org.chromium.mojo.bindings.Interface.Proxy { } - {{manager_class(interface, client)}} MANAGER = {{interface|name}}_Internal.MANAGER; + {{manager_class(interface)}} MANAGER = {{interface|name}}_Internal.MANAGER; {% for method in interface.methods %} void {{method|name}}({{declare_request_params(method)}}); @@ -159,16 +143,16 @@ public interface {{interface|name}} extends {{super_class(client)}} { } {% endmacro %} -{% macro interface_internal_def(interface, client) %} +{% macro interface_internal_def(interface) %} class {{interface|name}}_Internal { - {{manager_def(interface, client)|indent(4)}} + {{manager_def(interface)|indent(4)}} {% for method in interface.methods %} private static final int {{method|method_ordinal_name}} = {{method.ordinal}}; {% endfor %} - static final class Proxy extends {% if client %}org.chromium.mojo.bindings.InterfaceWithClient.AbstractProxy<{{client|java_type}}>{% else %}org.chromium.mojo.bindings.Interface.AbstractProxy{% endif %} implements {{interface|name}}.Proxy { + static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy implements {{interface|name}}.Proxy { Proxy(org.chromium.mojo.system.Core core, org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl index efb17f3..50c7a7b 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl @@ -1,4 +1,4 @@ {% from "interface_definition.tmpl" import interface_internal_def %} {% include "header.java.tmpl" %} -{{ interface_internal_def(interface, client) }} +{{ interface_internal_def(interface) }} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl index 333e0da..f0cb982 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl @@ -95,8 +95,8 @@ if (decoder{{level+1}} == null) { } {% else %} DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{kind|array_expected_length}}); - {{variable}} = {{kind|new_array('si'~(level+1)~'.numFields')}}; - for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.numFields; ++i{{level+1}}) { + {{variable}} = {{kind|new_array('si'~(level+1)~'.elementsOrVersion')}}; + for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.elementsOrVersion; ++i{{level+1}}) { {{decode(variable~'[i'~(level+1)~']', kind.kind, 'DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE * i'~(level+1), 0, level+1)|indent(8)}} } {% endif %} @@ -111,7 +111,12 @@ if (decoder{{level+1}} == null) { {{'static' if inner_class else 'public'}} final class {{struct|name}} extends org.chromium.mojo.bindings.Struct { private static final int STRUCT_SIZE = {{struct.versions[-1].num_bytes}}; - private static final DataHeader DEFAULT_STRUCT_INFO = new DataHeader(STRUCT_SIZE, {{struct.packed.packed_fields|length}}); + private static final DataHeader[] VERSION_ARRAY = new DataHeader[] { +{%- for version in struct.versions -%} + new DataHeader({{version.num_bytes}}, {{version.version}}){% if not loop.last %}, {% endif -%} +{%- endfor -%} + }; + private static final DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[{{struct.versions|length - 1}}]; {% for constant in struct.constants %} {{constant_def(constant)|indent(4)}} @@ -149,13 +154,13 @@ if (decoder{{level+1}} == null) { } {{struct|name}} result = new {{struct|name}}(); {% if not struct.bytes %} - decoder0.readDataHeader(); + decoder0.readAndValidateDataHeader(VERSION_ARRAY); {% else %} - DataHeader mainDataHeader = decoder0.readDataHeader(); + DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); {% endif %} {% for byte in struct.bytes %} {% for packed_field in byte.packed_fields %} - if (mainDataHeader.numFields > {{packed_field.ordinal}}) { + if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) { {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(12)}} } {% endfor %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py index 1196276..bdcb84f 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py @@ -57,6 +57,8 @@ _kind_infos = { mojom.NULLABLE_STRING: KindInfo('string', 'String', 'String', 64), } +_imports = {} + def GetBitSize(kind): if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct)): return 64 @@ -66,14 +68,18 @@ def GetBitSize(kind): kind = mojom.INT32 return _kind_infos[kind].bit_size +# Returns go type corresponding to provided kind. If |nullable| is true +# and kind is nullable adds an '*' to type (example: ?string -> *string). def GetGoType(kind, nullable = True): if nullable and mojom.IsNullableKind(kind): return '*%s' % GetNonNullableGoType(kind) return GetNonNullableGoType(kind) +# Returns go type corresponding to provided kind. Ignores nullability of +# top-level kind. def GetNonNullableGoType(kind): if mojom.IsStructKind(kind): - return '%s' % FormatName(kind.name) + return '%s' % GetFullName(kind) if mojom.IsArrayKind(kind): if kind.length: return '[%s]%s' % (kind.length, GetGoType(kind.kind)) @@ -86,6 +92,8 @@ def GetNonNullableGoType(kind): return GetNameForNestedElement(kind) return _kind_infos[kind].go_type +# Splits name to lower-cased parts used for camel-casing +# (example: HTTPEntry2FooBar -> ['http', 'entry2', 'foo', 'bar']). def NameToComponent(name): # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar -> # HTTP_Entry2_FooBar) @@ -98,24 +106,44 @@ def NameToComponent(name): def UpperCamelCase(name): return ''.join([x.capitalize() for x in NameToComponent(name)]) +# Formats a name. If |exported| is true makes name camel-cased with first +# letter capital, otherwise does no camel-casing and makes first letter +# lower-cased (which is used for making internal names more readable). def FormatName(name, exported=True): if exported: return UpperCamelCase(name) # Leave '_' symbols for unexported names. return name[0].lower() + name[1:] +# Returns full name of an imported element based on prebuilt dict |_imports|. +# If the |element| is not imported returns formatted name of it. +# |element| should have attr 'name'. |exported| argument is used to make +# |FormatName()| calls only. +def GetFullName(element, exported=True): + if not hasattr(element, 'imported_from') or not element.imported_from: + return FormatName(element.name, exported) + path = 'gen/mojom' + if element.imported_from['namespace']: + path = '/'.join([path] + element.imported_from['namespace'].split('.')) + if path in _imports: + return '%s.%s' % (_imports[path], FormatName(element.name, exported)) + return FormatName(element.name, exported) + +# Returns a name for nested elements like enum field or constant. +# The returned name consists of camel-cased parts separated by '_'. def GetNameForNestedElement(element): if element.parent_kind: return "%s_%s" % (GetNameForElement(element.parent_kind), FormatName(element.name)) - return FormatName(element.name) + return GetFullName(element) def GetNameForElement(element, exported=True): - if (mojom.IsInterfaceKind(element) or mojom.IsStructKind(element) or - isinstance(element, (mojom.EnumField, - mojom.Field, - mojom.Method, - mojom.Parameter))): + if (mojom.IsInterfaceKind(element) or mojom.IsStructKind(element)): + return GetFullName(element, exported) + if isinstance(element, (mojom.EnumField, + mojom.Field, + mojom.Method, + mojom.Parameter)): return FormatName(element.name, exported) if isinstance(element, (mojom.Enum, mojom.Constant, @@ -147,14 +175,14 @@ def EncodeSuffix(kind): return EncodeSuffix(mojom.MSGPIPE) return _kind_infos[kind].encode_suffix -def GetPackage(module): - if module.namespace: - return module.namespace.split('.')[-1] +def GetPackage(namespace): + if namespace: + return namespace.split('.')[-1] return 'mojom' -def GetPackagePath(module): +def GetPackagePath(namespace): path = 'mojom' - for i in module.namespace.split('.'): + for i in namespace.split('.'): path = os.path.join(path, i) return path @@ -182,6 +210,74 @@ def GetResponseStructFromMethod(method): struct.versions = pack.GetVersionInfo(struct.packed) return struct +def GetAllConstants(module): + data = [module] + module.structs + module.interfaces + constants = [x.constants for x in data] + return [i for i in chain.from_iterable(constants)] + +def GetAllEnums(module): + data = [module] + module.structs + module.interfaces + enums = [x.enums for x in data] + return [i for i in chain.from_iterable(enums)] + +# Adds an import required to use the provided |element|. +# The required import is stored at '_imports'. +def AddImport(module, element): + if not hasattr(element, 'imported_from') or not element.imported_from: + return + if isinstance(element, mojom.Kind) and mojom.IsAnyHandleKind(element): + return + imported = element.imported_from + if imported['namespace'] == module.namespace: + return + path = 'gen/mojom' + name = 'mojom' + if imported['namespace']: + path = '/'.join([path] + imported['namespace'].split('.')) + name = '_'.join([name] + imported['namespace'].split('.')) + while (name in _imports.values() and _imports[path] != path): + name += '_' + _imports[path] = name + +# Scans |module| for elements that require imports and adds all found imports +# to '_imports' dict. Returns a list of imports that should include the +# generated go file. +def GetImports(module): + # Imports can only be used in structs, constants, enums, interfaces. + all_structs = list(module.structs) + for i in module.interfaces: + AddImport(module, i) + for method in i.methods: + all_structs.append(GetStructFromMethod(method)) + if method.response_parameters: + all_structs.append(GetResponseStructFromMethod(method)) + + if len(all_structs) > 0: + _imports['mojo/public/go/bindings'] = 'bindings' + for struct in all_structs: + for field in struct.fields: + AddImport(module, field.kind) +# TODO(rogulenko): add these after generating constants and struct defaults. +# if field.default: +# AddImport(module, field.default) + + for enum in GetAllEnums(module): + for field in enum.fields: + if field.value: + AddImport(module, field.value) + +# TODO(rogulenko): add these after generating constants and struct defaults. +# for constant in GetAllConstants(module): +# AddImport(module, constant.value) + + imports_list = [] + for i in _imports: + if i.split('/')[-1] == _imports[i]: + imports_list.append('"%s"' % i) + else: + imports_list.append('%s "%s"' % (_imports[i], i)) + return sorted(imports_list) + class Generator(generator.Generator): go_filters = { 'array': lambda kind: mojom.Array(kind), @@ -204,16 +300,12 @@ class Generator(generator.Generator): 'tab_indent': lambda s, size = 1: ('\n' + '\t' * size).join(s.splitlines()) } - def GetAllEnums(self): - data = [self.module] + self.GetStructs() + self.module.interfaces - enums = [x.enums for x in data] - return [i for i in chain.from_iterable(enums)] - def GetParameters(self): return { - 'enums': self.GetAllEnums(), + 'enums': GetAllEnums(self.module), + 'imports': GetImports(self.module), 'interfaces': self.module.interfaces, - 'package': GetPackage(self.module), + 'package': GetPackage(self.module.namespace), 'structs': self.GetStructs(), } @@ -223,7 +315,7 @@ class Generator(generator.Generator): def GenerateFiles(self, args): self.Write(self.GenerateSource(), os.path.join("go", "src", "gen", - GetPackagePath(self.module), '%s.go' % self.module.name)) + GetPackagePath(self.module.namespace), '%s.go' % self.module.name)) def GetJinjaParameters(self): return { diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_java_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_java_generator.py index f279ef8..d6712f3 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_java_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_java_generator.py @@ -422,15 +422,6 @@ class Generator(generator.Generator): def GetJinjaExportsForInterface(self, interface): exports = self.GetJinjaExports() exports.update({'interface': interface}) - if interface.client: - all_interfaces = [] + self.module.interfaces - for each in self.module.imports: - all_interfaces += each['module'].interfaces - interfaces_by_name = dict((x.name, x) for x in all_interfaces) - assert interface.client in interfaces_by_name, ( - 'Unable to find interface %s declared as client of %s.' % - (interface.client, interface.name)) - exports.update({'client': interfaces_by_name[interface.client]}) return exports @UseJinja('java_templates/enum.java.tmpl', filters=java_filters) diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_python_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_python_generator.py index d4d4ed7..cbb1962 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_python_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_python_generator.py @@ -176,7 +176,7 @@ def GetFieldDescriptor(packed_field): arguments = [ '%r' % GetNameForElement(field) ] arguments.append(GetFieldType(field.kind, field)) arguments.append(str(packed_field.index)) - arguments.append(str(packed_field.ordinal)) + arguments.append(str(packed_field.min_version)) if field.default: if mojom.IsStructKind(field.kind): arguments.append('default_value=True') diff --git a/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni b/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni index 3b2874f..0a8e0f2 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni +++ b/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni @@ -60,7 +60,6 @@ template("mojom") { "$generator_root/generators/cpp_templates/module.cc.tmpl", "$generator_root/generators/cpp_templates/module.h.tmpl", "$generator_root/generators/cpp_templates/module-internal.h.tmpl", - "$generator_root/generators/cpp_templates/params_definition.tmpl", "$generator_root/generators/cpp_templates/serialization_macros.tmpl", "$generator_root/generators/cpp_templates/struct_declaration.tmpl", "$generator_root/generators/cpp_templates/struct_definition.tmpl", diff --git a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/module.py index c2e59a6..8d052d9 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/module.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/module.py @@ -131,7 +131,6 @@ PRIMITIVES = ( ATTRIBUTE_MIN_VERSION = 'MinVersion' -ATTRIBUTE_CLIENT = 'Client' class NamedValue(object): @@ -271,16 +270,16 @@ class Map(ReferenceKind): 'm[' + key_kind.spec + '][' + value_kind.spec + ']') if IsNullableKind(key_kind): - raise Exception("Nullable kinds can not be keys in maps.") + raise Exception("Nullable kinds cannot be keys in maps.") if IsStructKind(key_kind): # TODO(erg): It would sometimes be nice if we could key on struct # values. However, what happens if the struct has a handle in it? Or # non-copyable data like an array? - raise Exception("Structs can not be keys in maps.") + raise Exception("Structs cannot be keys in maps.") if IsAnyHandleKind(key_kind): - raise Exception("Handles can not be keys in maps.") + raise Exception("Handles cannot be keys in maps.") if IsArrayKind(key_kind): - raise Exception("Arrays can not be keys in maps.") + raise Exception("Arrays cannot be keys in maps.") else: ReferenceKind.__init__(self) @@ -370,9 +369,10 @@ class Interface(ReferenceKind): self.methods.append(method) return method + # TODO(451323): Remove when the language backends no longer rely on this. @property def client(self): - return self.attributes.get(ATTRIBUTE_CLIENT) if self.attributes else None + return None class EnumField(object): diff --git a/third_party/mojo/src/mojo/public/tools/download_network_service.py b/third_party/mojo/src/mojo/public/tools/download_network_service.py new file mode 100755 index 0000000..43b1172 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/download_network_service.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# 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. + +import argparse +import os +import sys +import tempfile +import zipfile + +_PLATFORMS = ["linux-x64", "android-arm"] +_APPS = ["network_service", "network_service_apptests"] +_CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(_CURRENT_PATH, "pylib")) +import gs + +if not sys.platform.startswith("linux"): + print "Not supported for your platform" + sys.exit(0) + +script_dir = os.path.dirname(os.path.realpath(__file__)) + + +def download_app(app, version, tools_directory): + prebuilt_directory = os.path.join(script_dir, "prebuilt/%s" % app) + stamp_path = os.path.join(prebuilt_directory, "VERSION") + + try: + with open(stamp_path) as stamp_file: + current_version = stamp_file.read().strip() + if current_version == version: + return # Already have the right version. + except IOError: + pass # If the stamp file does not exist we need to download a new binary. + + for platform in _PLATFORMS: + download_app_for_platform(app, version, platform, tools_directory) + + with open(stamp_path, 'w') as stamp_file: + stamp_file.write(version) + +def download_app_for_platform(app, version, platform, tools_directory): + find_depot_tools_path = os.path.join(_CURRENT_PATH, tools_directory) + sys.path.insert(0, find_depot_tools_path) + # pylint: disable=F0401 + import find_depot_tools + depot_tools_path = find_depot_tools.add_depot_tools_to_path() + + binary_name = app + ".mojo" + gs_path = "gs://mojo/%s/%s/%s/%s.zip" % (app, version, platform, binary_name) + output_directory = os.path.join(script_dir, + "prebuilt/%s/%s" % (app, platform)) + + with tempfile.NamedTemporaryFile() as temp_zip_file: + gs.download_from_public_bucket(gs_path, temp_zip_file.name, + depot_tools_path) + with zipfile.ZipFile(temp_zip_file.name) as z: + zi = z.getinfo(binary_name) + mode = zi.external_attr >> 16 + z.extract(zi, output_directory) + os.chmod(os.path.join(output_directory, binary_name), mode) + +def main(): + parser = argparse.ArgumentParser( + description="Download prebuilt network service binaries from google " + + "storage") + parser.add_argument("--tools-directory", + dest="tools_directory", + metavar="<tools-directory>", + type=str, + required=True, + help="Path to the directory containing " + "find_depot_tools.py, specified as a relative path " + "from the location of this file.") + args = parser.parse_args() + + version_path = os.path.join(script_dir, "NETWORK_SERVICE_VERSION") + with open(version_path) as version_file: + version = version_file.read().strip() + + for app in _APPS: + download_app(app, version, args.tools_directory) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/third_party/mojo/src/mojo/public/tools/download_shell_binary.py b/third_party/mojo/src/mojo/public/tools/download_shell_binary.py index ee74231..512e3a6 100755 --- a/third_party/mojo/src/mojo/public/tools/download_shell_binary.py +++ b/third_party/mojo/src/mojo/public/tools/download_shell_binary.py @@ -20,7 +20,10 @@ if not sys.platform.startswith("linux"): sys.exit(0) CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) -PREBUILT_FILE_PATH = os.path.join(CURRENT_PATH, "prebuilt") +sys.path.insert(0, os.path.join(CURRENT_PATH, "pylib")) +import gs + +PREBUILT_FILE_PATH = os.path.join(CURRENT_PATH, "prebuilt", "shell") def download(tools_directory): @@ -50,44 +53,21 @@ def download_version_for_platform(version, platform, tools_directory): sys.path.insert(0, find_depot_tools_path) # pylint: disable=F0401 import find_depot_tools + depot_tools_path = find_depot_tools.add_depot_tools_to_path() basename = platform + ".zip" gs_path = "gs://mojo/shell/" + version + "/" + basename - depot_tools_path = find_depot_tools.add_depot_tools_to_path() - gsutil_exe = os.path.join(depot_tools_path, "third_party", "gsutil", "gsutil") - with tempfile.NamedTemporaryFile() as temp_zip_file: - # We're downloading from a public bucket which does not need authentication, - # but the user might have busted credential files somewhere such as ~/.boto - # that the gsutil script will try (and fail) to use. Setting these - # environment variables convinces gsutil not to attempt to use these, but - # also generates a useless warning about failing to load the file. We want - # to discard this warning but still preserve all output in the case of an - # actual failure. So, we run the script and capture all output and then - # throw the output away if the script succeeds (return code 0). - env = os.environ.copy() - env["AWS_CREDENTIAL_FILE"] = "" - env["BOTO_CONFIG"] = "" - try: - subprocess.check_output( - [gsutil_exe, - "--bypass_prodaccess", - "cp", - gs_path, - temp_zip_file.name], - stderr=subprocess.STDOUT, - env=env) - except subprocess.CalledProcessError as e: - print e.output - sys.exit(1) - + gs.download_from_public_bucket(gs_path, temp_zip_file.name, + depot_tools_path) binary_name = BINARY_FOR_PLATFORM[platform] + output_dir = os.path.join(PREBUILT_FILE_PATH, platform) with zipfile.ZipFile(temp_zip_file.name) as z: zi = z.getinfo(binary_name) mode = zi.external_attr >> 16 - z.extract(zi, PREBUILT_FILE_PATH) - os.chmod(os.path.join(PREBUILT_FILE_PATH, binary_name), mode) + z.extract(zi, output_dir) + os.chmod(os.path.join(output_dir, binary_name), mode) def main(): @@ -97,13 +77,11 @@ def main(): dest="tools_directory", metavar="<tools-directory>", type=str, - help="Path to the directory containing" - " find_depot_tools.py, specified as a relative path" - " from the location of this file.") + required=True, + help="Path to the directory containing " + "find_depot_tools.py, specified as a relative path " + "from the location of this file.") args = parser.parse_args() - if not args.tools_directory: - print "Must specify --tools_directory; please see help message." - sys.exit(1) return download(args.tools_directory) if __name__ == "__main__": diff --git a/third_party/mojo/src/mojo/public/tools/pylib/gs.py b/third_party/mojo/src/mojo/public/tools/pylib/gs.py new file mode 100644 index 0000000..f8761b5 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/pylib/gs.py @@ -0,0 +1,34 @@ +# 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. + +import os +import subprocess +import sys + +def download_from_public_bucket(gs_path, output_path, depot_tools_path): + gsutil_exe = os.path.join(depot_tools_path, "third_party", "gsutil", "gsutil") + + # We're downloading from a public bucket which does not need authentication, + # but the user might have busted credential files somewhere such as ~/.boto + # that the gsutil script will try (and fail) to use. Setting these + # environment variables convinces gsutil not to attempt to use these, but + # also generates a useless warning about failing to load the file. We want + # to discard this warning but still preserve all output in the case of an + # actual failure. So, we run the script and capture all output and then + # throw the output away if the script succeeds (return code 0). + env = os.environ.copy() + env["AWS_CREDENTIAL_FILE"] = "" + env["BOTO_CONFIG"] = "" + try: + subprocess.check_output( + [gsutil_exe, + "--bypass_prodaccess", + "cp", + gs_path, + output_path], + stderr=subprocess.STDOUT, + env=env) + except subprocess.CalledProcessError as e: + print e.output + sys.exit(1) diff --git a/third_party/mojo_services/src/public/js/application.js b/third_party/mojo_services/src/public/js/application.js index aea60fd..cadba0f 100644 --- a/third_party/mojo_services/src/public/js/application.js +++ b/third_party/mojo_services/src/public/js/application.js @@ -30,7 +30,7 @@ define("mojo/services/public/js/application", [ }; } - doInitialize(shellProxy, args) { + doInitialize(shellProxy, args, url) { this.shellProxy_ = shellProxy; this.shell = new Shell(shellProxy); this.initialize(args); diff --git a/third_party/mojo_services/src/public/js/service_exchange.js b/third_party/mojo_services/src/public/js/service_exchange.js index a424c12..f23c0ef 100644 --- a/third_party/mojo_services/src/public/js/service_exchange.js +++ b/third_party/mojo_services/src/public/js/service_exchange.js @@ -47,7 +47,7 @@ define("mojo/services/public/js/service_exchange", [ var provider = { service: service, // A JS bindings interface object. - factory: factory, // factory(clientProxy) => interface implemntation + factory: factory, // factory() => interface implemntation connections: [], }; this.providers_.set(service.name, provider); @@ -60,12 +60,10 @@ define("mojo/services/public/js/service_exchange", [ } // Outgoing requests - requestService(interfaceObject, clientImpl) { + requestService(interfaceObject) { checkServiceExchange(this); if (!interfaceObject.name) throw new Error("Invalid service parameter"); - if (!clientImpl && interfaceObject.client) - throw new Error("Client implementation must be provided"); var serviceProxy; var serviceHandle = connection.bindProxy( diff --git a/third_party/mojo_services/src/public/js/shell.js b/third_party/mojo_services/src/public/js/shell.js index 358f272..e8bab6f 100644 --- a/third_party/mojo_services/src/public/js/shell.js +++ b/third_party/mojo_services/src/public/js/shell.js @@ -41,8 +41,8 @@ define("mojo/services/public/js/shell", [ return application; } - connectToService(url, service, client) { - return this.connectToApplication(url).requestService(service, client); + connectToService(url, service) { + return this.connectToApplication(url).requestService(service); }; close() { diff --git a/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn b/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn index b1a0bdb..72cb55e 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn +++ b/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn @@ -45,6 +45,7 @@ mojo_sdk_source_set("cpp") { "mojo/public/c/gles2:headers", "mojo/public/cpp/application", "mojo/public/cpp/bindings:bindings", + "mojo/public/cpp/system", "mojo/public/interfaces/application", ] } diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view.cc b/third_party/mojo_services/src/view_manager/public/cpp/lib/view.cc index 1e8b2b0..9ed458c 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view.cc +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view.cc @@ -77,7 +77,7 @@ class ScopedTreeNotifier { private: ViewObserver::TreeChangeParams params_; - DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); + MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); }; void RemoveChildImpl(View* child, View::Children* children) { @@ -112,7 +112,7 @@ class ScopedOrderChangedNotifier { View* relative_view_; OrderDirection direction_; - DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier); + MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier); }; // Returns true if the order actually changed. @@ -168,7 +168,7 @@ class ScopedSetBoundsNotifier { const Rect old_bounds_; const Rect new_bounds_; - DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); + MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); }; // Some operations are only permitted in the connection that created the view. @@ -220,9 +220,7 @@ void View::SetVisible(bool value) { if (manager_) static_cast<ViewManagerClientImpl*>(manager_)->SetVisible(id_, value); - FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this)); - visible_ = value; - NotifyViewVisibilityChanged(this); + LocalSetVisible(value); } void View::SetSharedProperty(const std::string& name, @@ -531,6 +529,15 @@ void View::LocalSetDrawn(bool value) { FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanged(this)); } +void View::LocalSetVisible(bool visible) { + if (visible_ == visible) + return; + + FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this)); + visible_ = visible; + NotifyViewVisibilityChanged(this); +} + void View::NotifyViewVisibilityChanged(View* target) { if (!NotifyViewVisibilityChangedDown(target)) { return; // |this| has been deleted. diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_factory.cc b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_factory.cc index cfa2b79..bb39fc9 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_factory.cc +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_factory.cc @@ -19,17 +19,16 @@ ViewManagerClientFactory::~ViewManagerClientFactory() { } // static -scoped_ptr<ViewManagerClient> -ViewManagerClientFactory::WeakBindViewManagerToPipe( +ViewManagerClient* ViewManagerClientFactory::WeakBindViewManagerToPipe( InterfaceRequest<ViewManagerClient> request, ViewManagerServicePtr view_manager_service, Shell* shell, ViewManagerDelegate* delegate) { const bool delete_on_error = false; - scoped_ptr<ViewManagerClientImpl> client(new ViewManagerClientImpl( - delegate, shell, request.Pass(), delete_on_error)); + auto client = new ViewManagerClientImpl(delegate, shell, request.Pass(), + delete_on_error); client->SetViewManagerService(view_manager_service.Pass()); - return client.Pass(); + return client; } // InterfaceFactory<ViewManagerClient> implementation. diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.cc b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.cc index 574d300..9a3c022 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.cc +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.cc @@ -4,9 +4,6 @@ #include "view_manager/public/cpp/lib/view_manager_client_impl.h" -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/stl_util.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/application/service_provider_impl.h" @@ -90,7 +87,7 @@ class RootObserver : public ViewObserver { View* root_; - DISALLOW_COPY_AND_ASSIGN(RootObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(RootObserver); }; ViewManagerClientImpl::ViewManagerClientImpl( @@ -236,7 +233,9 @@ void ViewManagerClientImpl::SetViewManagerService( Id ViewManagerClientImpl::CreateViewOnServer() { DCHECK(service_); const Id view_id = MakeTransportId(connection_id_, ++next_id_); - service_->CreateView(view_id, ActionCompletedCallbackWithErrorCode()); + service_->CreateView(view_id, [this](ErrorCode code) { + OnActionCompleted(code == ERROR_CODE_NONE); + }); return view_id; } @@ -286,14 +285,21 @@ void ViewManagerClientImpl::OnEmbed( root_->AddObserver(new RootObserver(root_)); window_manager_.Bind(window_manager_pipe.Pass()); - // base::Unretained() is safe here as |window_manager_| is bound to our - // lifetime. WindowManagerObserverPtr observer; wm_observer_binding_.Bind(GetProxy(&observer)); + // binding to |this| is safe here as |window_manager_| is bound to our + // lifetime. window_manager_->GetFocusedAndActiveViews( observer.Pass(), - base::Bind(&ViewManagerClientImpl::OnGotFocusedAndActiveViews, - base::Unretained(this))); + [this](uint32_t capture_view_id, uint32_t focused_view_id, + uint32_t active_view_id) { + if (GetViewById(capture_view_id) != capture_view_) + OnCaptureChanged(capture_view_id); + if (GetViewById(focused_view_id) != focused_view_) + OnFocusChanged(focused_view_id); + if (GetViewById(active_view_id) != activated_view_) + OnActiveWindowChanged(active_view_id); + }); delegate_->OnEmbed(root_, services.Pass(), exposed_services.Pass()); } @@ -373,7 +379,7 @@ void ViewManagerClientImpl::OnViewVisibilityChanged(Id view_id, bool visible) { // Deal with this some how. View* view = GetViewById(view_id); if (view) - view->SetVisible(visible); + ViewPrivate(view).LocalSetVisible(visible); } void ViewManagerClientImpl::OnViewDrawnStateChanged(Id view_id, bool drawn) { @@ -483,31 +489,8 @@ void ViewManagerClientImpl::OnActionCompleted(bool success) { change_acked_callback_.Run(); } -void ViewManagerClientImpl::OnActionCompletedWithErrorCode(ErrorCode code) { - OnActionCompleted(code == ERROR_CODE_NONE); -} - -base::Callback<void(bool)> ViewManagerClientImpl::ActionCompletedCallback() { - return base::Bind(&ViewManagerClientImpl::OnActionCompleted, - base::Unretained(this)); -} - -base::Callback<void(ErrorCode)> - ViewManagerClientImpl::ActionCompletedCallbackWithErrorCode() { - return base::Bind(&ViewManagerClientImpl::OnActionCompletedWithErrorCode, - base::Unretained(this)); -} - -void ViewManagerClientImpl::OnGotFocusedAndActiveViews( - uint32_t capture_view_id, - uint32_t focused_view_id, - uint32_t active_view_id) { - if (GetViewById(capture_view_id) != capture_view_) - OnCaptureChanged(capture_view_id); - if (GetViewById(focused_view_id) != focused_view_) - OnFocusChanged(focused_view_id); - if (GetViewById(active_view_id) != activated_view_) - OnActiveWindowChanged(active_view_id); +Callback<void(bool)> ViewManagerClientImpl::ActionCompletedCallback() { + return [this](bool success) { OnActionCompleted(success); }; } } // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.h b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.h index c3a2ad5..94db93d 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.h @@ -5,9 +5,6 @@ #ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_MANAGER_CLIENT_IMPL_H_ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_MANAGER_CLIENT_IMPL_H_ -#include "base/callback.h" -#include "base/memory/scoped_vector.h" -#include "base/memory/weak_ptr.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "view_manager/public/cpp/types.h" #include "view_manager/public/cpp/view.h" @@ -66,12 +63,10 @@ class ViewManagerClientImpl : public ViewManager, ServiceProviderPtr exposed_services); void Embed(Id view_id, ViewManagerClientPtr client); - void set_change_acked_callback(const base::Callback<void(void)>& callback) { + void set_change_acked_callback(const Callback<void(void)>& callback) { change_acked_callback_ = callback; } - void ClearChangeAckedCallback() { - change_acked_callback_ = base::Callback<void(void)>(); - } + void ClearChangeAckedCallback() { change_acked_callback_.reset(); } // Start/stop tracking views. While tracked, they can be retrieved via // ViewManager::GetViewById. @@ -136,22 +131,15 @@ class ViewManagerClientImpl : public ViewManager, void RootDestroyed(View* root); void OnActionCompleted(bool success); - void OnActionCompletedWithErrorCode(ErrorCode code); - - base::Callback<void(bool)> ActionCompletedCallback(); - base::Callback<void(ErrorCode)> ActionCompletedCallbackWithErrorCode(); - // Callback from server for initial request of capture/focused/active views. - void OnGotFocusedAndActiveViews(uint32_t capture_view_id, - uint32_t focused_view_id, - uint32_t active_view_id); + Callback<void(bool)> ActionCompletedCallback(); ConnectionSpecificId connection_id_; ConnectionSpecificId next_id_; std::string creator_url_; - base::Callback<void(void)> change_acked_callback_; + Callback<void(void)> change_acked_callback_; ViewManagerDelegate* delegate_; @@ -170,7 +158,7 @@ class ViewManagerClientImpl : public ViewManager, ViewManagerServicePtr service_; const bool delete_on_error_; - DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl); + MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl); }; } // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_context.cc b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_context.cc index a47f59a..d1c2fd0 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_context.cc +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_context.cc @@ -28,7 +28,9 @@ class ViewManagerContext::InternalState { ViewManagerContext::ViewManagerContext(ApplicationImpl* application_impl) : state_(new InternalState(application_impl)) {} -ViewManagerContext::~ViewManagerContext() {} +ViewManagerContext::~ViewManagerContext() { + delete state_; +} void ViewManagerContext::Embed(const String& url) { Embed(url, nullptr, nullptr); diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_observer.cc b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_observer.cc index 456147c..7e6acc0 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_observer.cc +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_observer.cc @@ -4,17 +4,16 @@ #include "view_manager/public/cpp/view_observer.h" -#include "base/basictypes.h" - namespace mojo { //////////////////////////////////////////////////////////////////////////////// // ViewObserver, public: ViewObserver::TreeChangeParams::TreeChangeParams() - : target(NULL), - old_parent(NULL), - new_parent(NULL), - receiver(NULL) {} + : target(nullptr), + old_parent(nullptr), + new_parent(nullptr), + receiver(nullptr) { +} } // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_private.h b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_private.h index 1c54567..0b58280 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/lib/view_private.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/lib/view_private.h @@ -5,8 +5,6 @@ #ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_PRIVATE_H_ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_PRIVATE_H_ -#include "base/basictypes.h" - #include "view_manager/public/cpp/view.h" namespace mojo { @@ -61,11 +59,12 @@ class ViewPrivate { view_->LocalSetBounds(old_bounds, new_bounds); } void LocalSetDrawn(bool drawn) { view_->LocalSetDrawn(drawn); } + void LocalSetVisible(bool visible) { view_->LocalSetVisible(visible); } private: View* view_; - DISALLOW_COPY_AND_ASSIGN(ViewPrivate); + MOJO_DISALLOW_COPY_AND_ASSIGN(ViewPrivate); }; } // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/tests/BUILD.gn b/third_party/mojo_services/src/view_manager/public/cpp/tests/BUILD.gn index 3414d33..0adbb5d 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/tests/BUILD.gn +++ b/third_party/mojo_services/src/view_manager/public/cpp/tests/BUILD.gn @@ -10,7 +10,6 @@ test("mojo_view_manager_lib_unittests") { "run_all_unittests.cc", "view_manager_test_suite.cc", "view_manager_test_suite.h", - "view_manager_unittest.cc", "view_unittest.cc", ] @@ -18,11 +17,10 @@ test("mojo_view_manager_lib_unittests") { "//base", "//base/test:test_support", "//mojo/public/cpp/application", + "//mojo/public/cpp/system", "//mojo/services/geometry/public/cpp", "//mojo/services/geometry/public/interfaces", "//mojo/services/view_manager/public/cpp", - "//mojo/services/view_manager/public/interfaces", - "//shell/application_manager", "//shell:test_support", "//testing/gtest", ] diff --git a/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_test_suite.h b/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_test_suite.h index ef81661..547efc2 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_test_suite.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_test_suite.h @@ -6,6 +6,7 @@ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_ #include "base/test/test_suite.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { @@ -18,7 +19,7 @@ class ViewManagerTestSuite : public base::TestSuite { void Initialize() override; private: - DISALLOW_COPY_AND_ASSIGN(ViewManagerTestSuite); + MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTestSuite); }; } // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_unittest.cc b/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_unittest.cc deleted file mode 100644 index bc69464..0000000 --- a/third_party/mojo_services/src/view_manager/public/cpp/tests/view_manager_unittest.cc +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/view_manager/public/cpp/view_manager.h" - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/logging.h" -#include "mojo/public/cpp/application/application_connection.h" -#include "mojo/public/cpp/application/application_delegate.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/public/cpp/application/service_provider_impl.h" -#include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/services/geometry/public/cpp/geometry_util.h" -#include "mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h" -#include "mojo/services/view_manager/public/cpp/lib/view_private.h" -#include "mojo/services/view_manager/public/cpp/util.h" -#include "mojo/services/view_manager/public/cpp/view_manager_client_factory.h" -#include "mojo/services/view_manager/public/cpp/view_manager_delegate.h" -#include "mojo/services/view_manager/public/cpp/view_observer.h" -#include "shell/application_manager/application_manager.h" -#include "shell/shell_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { - -namespace { - -const char kWindowManagerURL[] = "mojo:window_manager"; -const char kEmbeddedApp1URL[] = "mojo:embedded_app_1"; - -base::RunLoop* current_run_loop = NULL; - -void DoRunLoop() { - base::RunLoop run_loop; - current_run_loop = &run_loop; - current_run_loop->Run(); - current_run_loop = NULL; -} - -void QuitRunLoop() { - current_run_loop->Quit(); -} - -class ConnectApplicationLoader : public ApplicationLoader, - public ApplicationDelegate, - public ViewManagerDelegate { - public: - typedef base::Callback<void(View*)> LoadedCallback; - - explicit ConnectApplicationLoader(const LoadedCallback& callback) - : callback_(callback) {} - ~ConnectApplicationLoader() override {} - - private: - // Overridden from ApplicationDelegate: - void Initialize(ApplicationImpl* app) override { - view_manager_client_factory_.reset( - new ViewManagerClientFactory(app->shell(), this)); - } - - // Overridden from ApplicationLoader: - void Load(ApplicationManager* manager, - const GURL& url, - InterfaceRequest<Application> application_request, - LoadCallback callback) override { - ASSERT_TRUE(application_request.is_pending()); - scoped_ptr<ApplicationImpl> app( - new ApplicationImpl(this, application_request.Pass())); - apps_.push_back(app.release()); - } - - void OnApplicationError(ApplicationManager* manager, - const GURL& url) override {} - - bool ConfigureIncomingConnection(ApplicationConnection* connection) override { - connection->AddService(view_manager_client_factory_.get()); - return true; - } - - // Overridden from ViewManagerDelegate: - void OnEmbed(View* root, - InterfaceRequest<ServiceProvider> services, - ServiceProviderPtr exposed_services) override { - callback_.Run(root); - } - void OnViewManagerDisconnected(ViewManager* view_manager) override {} - - ScopedVector<ApplicationImpl> apps_; - LoadedCallback callback_; - scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_; - - DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader); -}; - -class BoundsChangeObserver : public ViewObserver { - public: - explicit BoundsChangeObserver(View* view) : view_(view) {} - ~BoundsChangeObserver() override {} - - private: - // Overridden from ViewObserver: - void OnViewBoundsChanged(View* view, - const Rect& old_bounds, - const Rect& new_bounds) override { - DCHECK_EQ(view, view_); - QuitRunLoop(); - } - - View* view_; - - DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); -}; - -// Wait until the bounds of the supplied view change. -void WaitForBoundsToChange(View* view) { - BoundsChangeObserver observer(view); - view->AddObserver(&observer); - DoRunLoop(); - view->RemoveObserver(&observer); -} - -// Spins a runloop until the tree beginning at |root| has |tree_size| views -// (including |root|). -class TreeSizeMatchesObserver : public ViewObserver { - public: - TreeSizeMatchesObserver(View* tree, size_t tree_size) - : tree_(tree), - tree_size_(tree_size) {} - ~TreeSizeMatchesObserver() override {} - - bool IsTreeCorrectSize() { - return CountViews(tree_) == tree_size_; - } - - private: - // Overridden from ViewObserver: - void OnTreeChanged(const TreeChangeParams& params) override { - if (IsTreeCorrectSize()) - QuitRunLoop(); - } - - size_t CountViews(const View* view) const { - size_t count = 1; - View::Children::const_iterator it = view->children().begin(); - for (; it != view->children().end(); ++it) - count += CountViews(*it); - return count; - } - - View* tree_; - size_t tree_size_; - - DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver); -}; - -void WaitForTreeSizeToMatch(View* view, size_t tree_size) { - TreeSizeMatchesObserver observer(view, tree_size); - if (observer.IsTreeCorrectSize()) - return; - view->AddObserver(&observer); - DoRunLoop(); - view->RemoveObserver(&observer); -} - -// Utility class that waits for the destruction of some number of views and -// views. -class DestructionObserver : public ViewObserver { - public: - // |views| or |views| can be NULL. - explicit DestructionObserver(std::set<Id>* views) : views_(views) {} - - private: - // Overridden from ViewObserver: - void OnViewDestroyed(View* view) override { - std::set<Id>::iterator it = views_->find(view->id()); - if (it != views_->end()) - views_->erase(it); - if (CanQuit()) - QuitRunLoop(); - } - - bool CanQuit() { - return !views_ || views_->empty(); - } - - std::set<Id>* views_; - - DISALLOW_COPY_AND_ASSIGN(DestructionObserver); -}; - -void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) { - DestructionObserver observer(views); - DCHECK(views); - if (views) { - for (std::set<Id>::const_iterator it = views->begin(); - it != views->end(); ++it) { - view_manager->GetViewById(*it)->AddObserver(&observer); - } - } - DoRunLoop(); -} - -class OrderChangeObserver : public ViewObserver { - public: - OrderChangeObserver(View* view) : view_(view) { - view_->AddObserver(this); - } - ~OrderChangeObserver() override { view_->RemoveObserver(this); } - - private: - // Overridden from ViewObserver: - void OnViewReordered(View* view, - View* relative_view, - OrderDirection direction) override { - DCHECK_EQ(view, view_); - QuitRunLoop(); - } - - View* view_; - - DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); -}; - -void WaitForOrderChange(ViewManager* view_manager, View* view) { - OrderChangeObserver observer(view); - DoRunLoop(); -} - -// Tracks a view's destruction. Query is_valid() for current state. -class ViewTracker : public ViewObserver { - public: - explicit ViewTracker(View* view) : view_(view) { - view_->AddObserver(this); - } - ~ViewTracker() override { - if (view_) - view_->RemoveObserver(this); - } - - bool is_valid() const { return !!view_; } - - private: - // Overridden from ViewObserver: - void OnViewDestroyed(View* view) override { - DCHECK_EQ(view, view_); - view_ = NULL; - } - - int id_; - View* view_; - - DISALLOW_COPY_AND_ASSIGN(ViewTracker); -}; - -} // namespace - -// ViewManager ----------------------------------------------------------------- - -// These tests model synchronization of two peer connections to the view manager -// service, that are given access to some root view. - -class ViewManagerTest : public testing::Test { - public: - ViewManagerTest() - : connect_loop_(NULL), - loaded_view_manager_(NULL), - window_manager_(NULL), - commit_count_(0) {} - - protected: - ViewManager* window_manager() { return window_manager_; } - - View* CreateViewInParent(View* parent) { - ViewManager* parent_manager = parent->view_manager(); - View* view = parent_manager->CreateView(); - view->SetVisible(true); - parent->AddChild(view); - return view; - } - - // Embeds another version of the test app @ view. - ViewManager* Embed(ViewManager* view_manager, View* view) { - DCHECK_EQ(view_manager, view->view_manager()); - view->Embed(kEmbeddedApp1URL); - RunRunLoop(); - return GetLoadedViewManager(); - } - - ViewManager* GetLoadedViewManager() { - ViewManager* view_manager = loaded_view_manager_; - loaded_view_manager_ = NULL; - return view_manager; - } - - void UnloadApplication(const GURL& url) { - test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url); - } - - private: - // Overridden from testing::Test: - void SetUp() override { - ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind( - &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this)); - test_helper_.Init(); - test_helper_.SetLoaderForURL( - scoped_ptr<ApplicationLoader>( - new ConnectApplicationLoader(ready_callback)), - GURL(kWindowManagerURL)); - test_helper_.SetLoaderForURL( - scoped_ptr<ApplicationLoader>( - new ConnectApplicationLoader(ready_callback)), - GURL(kEmbeddedApp1URL)); - - // TODO(sky): resolve this. Need to establish initial connection. - } - - void OnViewManagerLoaded(View* root) { - loaded_view_manager_ = root->view_manager(); - connect_loop_->Quit(); - } - - void RunRunLoop() { - base::RunLoop run_loop; - connect_loop_ = &run_loop; - connect_loop_->Run(); - connect_loop_ = NULL; - } - - base::RunLoop* connect_loop_; - shell::ShellTestHelper test_helper_; - // Used to receive the most recent view manager loaded by an embed action. - ViewManager* loaded_view_manager_; - // The View Manager connection held by the window manager (app running at the - // root view). - ViewManager* window_manager_; - int commit_count_; - - DISALLOW_COPY_AND_ASSIGN(ViewManagerTest); -}; - -// TODO(sky): all of these tests are disabled as each test triggers running -// ViewsInit, which tries to register the same set of paths with the -// PathService, triggering a DCHECK. -TEST_F(ViewManagerTest, DISABLED_SetUp) {} - -TEST_F(ViewManagerTest, DISABLED_Embed) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - ViewManager* embedded = Embed(window_manager(), view); - EXPECT_TRUE(NULL != embedded); - - View* view_in_embedded = embedded->GetRoot(); - EXPECT_EQ(view->parent(), window_manager()->GetRoot()); - EXPECT_EQ(NULL, view_in_embedded->parent()); -} - -// Window manager has two views, N1 and N11. Embeds A at N1. A should not see -// N11. -// TODO(sky): Update client lib to match server. -TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - View* nested = window_manager()->CreateView(); - nested->SetVisible(true); - view->AddChild(nested); - - ViewManager* embedded = Embed(window_manager(), view); - EXPECT_EQ(embedded->GetRoot()->children().front()->id(), - nested->id()); - EXPECT_TRUE(embedded->GetRoot()->children().empty()); - EXPECT_TRUE(nested->parent() == NULL); -} - -// http://crbug.com/396300 -TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - ViewManager* embedded = Embed(window_manager(), view); - - Id view_id = view->id(); - - UnloadApplication(GURL(kWindowManagerURL)); - - std::set<Id> views; - views.insert(view_id); - WaitForDestruction(embedded, &views); - - EXPECT_EQ(nullptr, embedded->GetRoot()); -} - -// TODO(beng): write a replacement test for the one that once existed here: -// This test validates the following scenario: -// - a view originating from one connection -// - a view originating from a second connection -// + the connection originating the view is destroyed -// -> the view should still exist (since the second connection is live) but -// should be disconnected from any views. -// http://crbug.com/396300 -// -// TODO(beng): The new test should validate the scenario as described above -// except that the second connection still has a valid tree. - -// Verifies that bounds changes applied to a view hierarchy in one connection -// are reflected to another. -TEST_F(ViewManagerTest, DISABLED_SetBounds) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - ViewManager* embedded = Embed(window_manager(), view); - - View* view_in_embedded = embedded->GetViewById(view->id()); - EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); - - Rect rect; - rect.width = rect.height = 100; - view->SetBounds(rect); - EXPECT_NE(view->bounds(), view_in_embedded->bounds()); - WaitForBoundsToChange(view_in_embedded); - EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); -} - -// Verifies that bounds changes applied to a view owned by a different -// connection are refused. -TEST_F(ViewManagerTest, DISABLED_SetBoundsSecurity) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - ViewManager* embedded = Embed(window_manager(), view); - - View* view_in_embedded = embedded->GetViewById(view->id()); - Rect rect; - rect.width = 800; - rect.height = 600; - view->SetBounds(rect); - WaitForBoundsToChange(view_in_embedded); - - rect.width = 1024; - rect.height = 768; - view_in_embedded->SetBounds(rect); - // Bounds change should have been rejected. - EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); -} - -// Verifies that a view can only be destroyed by the connection that created it. -TEST_F(ViewManagerTest, DISABLED_DestroySecurity) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - ViewManager* embedded = Embed(window_manager(), view); - - View* view_in_embedded = embedded->GetViewById(view->id()); - - ViewTracker tracker2(view_in_embedded); - view_in_embedded->Destroy(); - // View should not have been destroyed. - EXPECT_TRUE(tracker2.is_valid()); - - ViewTracker tracker1(view); - view->Destroy(); - EXPECT_FALSE(tracker1.is_valid()); -} - -TEST_F(ViewManagerTest, DISABLED_MultiRoots) { - View* view1 = window_manager()->CreateView(); - view1->SetVisible(true); - window_manager()->GetRoot()->AddChild(view1); - View* view2 = window_manager()->CreateView(); - view2->SetVisible(true); - window_manager()->GetRoot()->AddChild(view2); - ViewManager* embedded1 = Embed(window_manager(), view1); - ViewManager* embedded2 = Embed(window_manager(), view2); - EXPECT_EQ(embedded1, embedded2); -} - -TEST_F(ViewManagerTest, DISABLED_EmbeddingIdentity) { - View* view = window_manager()->CreateView(); - view->SetVisible(true); - window_manager()->GetRoot()->AddChild(view); - ViewManager* embedded = Embed(window_manager(), view); - EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL()); -} - -TEST_F(ViewManagerTest, DISABLED_Reorder) { - View* view1 = window_manager()->CreateView(); - view1->SetVisible(true); - window_manager()->GetRoot()->AddChild(view1); - - ViewManager* embedded = Embed(window_manager(), view1); - - View* view11 = embedded->CreateView(); - view11->SetVisible(true); - embedded->GetRoot()->AddChild(view11); - View* view12 = embedded->CreateView(); - view12->SetVisible(true); - embedded->GetRoot()->AddChild(view12); - - View* view1_in_wm = window_manager()->GetViewById(view1->id()); - - { - WaitForTreeSizeToMatch(view1, 2u); - view11->MoveToFront(); - WaitForOrderChange(window_manager(), - window_manager()->GetViewById(view11->id())); - - EXPECT_EQ(view1_in_wm->children().front(), - window_manager()->GetViewById(view12->id())); - EXPECT_EQ(view1_in_wm->children().back(), - window_manager()->GetViewById(view11->id())); - } - - { - view11->MoveToBack(); - WaitForOrderChange(window_manager(), - window_manager()->GetViewById(view11->id())); - - EXPECT_EQ(view1_in_wm->children().front(), - window_manager()->GetViewById(view11->id())); - EXPECT_EQ(view1_in_wm->children().back(), - window_manager()->GetViewById(view12->id())); - } -} - -namespace { - -class VisibilityChangeObserver : public ViewObserver { - public: - explicit VisibilityChangeObserver(View* view) : view_(view) { - view_->AddObserver(this); - } - ~VisibilityChangeObserver() override { view_->RemoveObserver(this); } - - private: - // Overridden from ViewObserver: - void OnViewVisibilityChanged(View* view) override { - EXPECT_EQ(view, view_); - QuitRunLoop(); - } - - View* view_; - - DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver); -}; - -} // namespace - -TEST_F(ViewManagerTest, DISABLED_Visible) { - View* view1 = window_manager()->CreateView(); - view1->SetVisible(true); - window_manager()->GetRoot()->AddChild(view1); - - // Embed another app and verify initial state. - ViewManager* embedded = Embed(window_manager(), view1); - ASSERT_NE(nullptr, embedded->GetRoot()); - View* embedded_root = embedded->GetRoot(); - EXPECT_TRUE(embedded_root->visible()); - EXPECT_TRUE(embedded_root->IsDrawn()); - - // Change the visible state from the first connection and verify its mirrored - // correctly to the embedded app. - { - VisibilityChangeObserver observer(embedded_root); - view1->SetVisible(false); - DoRunLoop(); - } - - EXPECT_FALSE(view1->visible()); - EXPECT_FALSE(view1->IsDrawn()); - - EXPECT_FALSE(embedded_root->visible()); - EXPECT_FALSE(embedded_root->IsDrawn()); - - // Make the node visible again. - { - VisibilityChangeObserver observer(embedded_root); - view1->SetVisible(true); - DoRunLoop(); - } - - EXPECT_TRUE(view1->visible()); - EXPECT_TRUE(view1->IsDrawn()); - - EXPECT_TRUE(embedded_root->visible()); - EXPECT_TRUE(embedded_root->IsDrawn()); -} - -namespace { - -class DrawnChangeObserver : public ViewObserver { - public: - explicit DrawnChangeObserver(View* view) : view_(view) { - view_->AddObserver(this); - } - ~DrawnChangeObserver() override { view_->RemoveObserver(this); } - - private: - // Overridden from ViewObserver: - void OnViewDrawnChanged(View* view) override { - EXPECT_EQ(view, view_); - QuitRunLoop(); - } - - View* view_; - - DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver); -}; - -} // namespace - -TEST_F(ViewManagerTest, DISABLED_Drawn) { - View* view1 = window_manager()->CreateView(); - view1->SetVisible(true); - window_manager()->GetRoot()->AddChild(view1); - - // Embed another app and verify initial state. - ViewManager* embedded = Embed(window_manager(), view1); - ASSERT_NE(nullptr, embedded->GetRoot()); - View* embedded_root = embedded->GetRoot(); - EXPECT_TRUE(embedded_root->visible()); - EXPECT_TRUE(embedded_root->IsDrawn()); - - // Change the visibility of the root, this should propagate a drawn state - // change to |embedded|. - { - DrawnChangeObserver observer(embedded_root); - window_manager()->GetRoot()->SetVisible(false); - DoRunLoop(); - } - - EXPECT_TRUE(view1->visible()); - EXPECT_FALSE(view1->IsDrawn()); - - EXPECT_TRUE(embedded_root->visible()); - EXPECT_FALSE(embedded_root->IsDrawn()); -} - -// TODO(beng): tests for view event dispatcher. -// - verify that we see events for all views. - -// TODO(beng): tests for focus: -// - focus between two views known to a connection -// - focus between views unknown to one of the connections. -// - focus between views unknown to either connection. - -// TODO(sky): need test of root being destroyed with existing views. See -// 434555 for specific case. - -} // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/tests/view_unittest.cc b/third_party/mojo_services/src/view_manager/public/cpp/tests/view_unittest.cc index 149eb3a..abbd712 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/tests/view_unittest.cc +++ b/third_party/mojo_services/src/view_manager/public/cpp/tests/view_unittest.cc @@ -27,7 +27,7 @@ class TestView : public View { ~TestView() {} private: - DISALLOW_COPY_AND_ASSIGN(TestView); + MOJO_DISALLOW_COPY_AND_ASSIGN(TestView); }; TEST_F(ViewTest, AddChild) { @@ -148,7 +148,7 @@ class TestProperty { private: static TestProperty* last_deleted_; - DISALLOW_COPY_AND_ASSIGN(TestProperty); + MOJO_DISALLOW_COPY_AND_ASSIGN(TestProperty); }; TestProperty* TestProperty::last_deleted_ = NULL; @@ -222,7 +222,7 @@ class TreeChangeObserver : public ViewObserver { View* observee_; std::vector<TreeChangeParams> received_params_; - DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver); }; // Adds/Removes v11 to v1. @@ -463,7 +463,7 @@ class OrderChangeObserver : public ViewObserver { View* observee_; Changes changes_; - DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); }; } // namespace @@ -613,7 +613,7 @@ class BoundsChangeObserver : public ViewObserver { View* view_; Changes changes_; - DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); }; } // namespace @@ -669,7 +669,7 @@ class VisibilityChangeObserver : public ViewObserver { View* view_; Changes changes_; - DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver); }; } // namespace @@ -775,7 +775,7 @@ class SharedPropertyChangeObserver : public ViewObserver { View* view_; Changes changes_; - DISALLOW_COPY_AND_ASSIGN(SharedPropertyChangeObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(SharedPropertyChangeObserver); }; } // namespace @@ -856,7 +856,7 @@ class LocalPropertyChangeObserver : public ViewObserver { const void* property_key_; intptr_t old_property_value_; - DISALLOW_COPY_AND_ASSIGN(LocalPropertyChangeObserver); + MOJO_DISALLOW_COPY_AND_ASSIGN(LocalPropertyChangeObserver); }; } // namespace diff --git a/third_party/mojo_services/src/view_manager/public/cpp/view.h b/third_party/mojo_services/src/view_manager/public/cpp/view.h index d888ef0..fcb49bc 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/view.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/view.h @@ -5,12 +5,13 @@ #ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_H_ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_H_ +#include <stdint.h> #include <vector> -#include "base/basictypes.h" #include "base/observer_list.h" #include "geometry/public/interfaces/geometry.mojom.h" #include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/system/macros.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" #include "surfaces/public/interfaces/surface_id.mojom.h" #include "view_manager/public/cpp/types.h" @@ -87,7 +88,7 @@ class View { void ClearLocalProperty(const ViewProperty<T>* property); // Type of a function to delete a property that this view owns. - typedef void (*PropertyDeallocator)(int64 value); + typedef void (*PropertyDeallocator)(int64_t value); // A View is drawn if the View and all its ancestors are visible and the // View is attached to the root. @@ -141,12 +142,13 @@ class View { View(ViewManager* manager, Id id); // Called by the public {Set,Get,Clear}Property functions. - int64 SetLocalPropertyInternal(const void* key, - const char* name, - PropertyDeallocator deallocator, - int64 value, - int64 default_value); - int64 GetLocalPropertyInternal(const void* key, int64 default_value) const; + int64_t SetLocalPropertyInternal(const void* key, + const char* name, + PropertyDeallocator deallocator, + int64_t value, + int64_t default_value); + int64_t GetLocalPropertyInternal(const void* key, + int64_t default_value) const; void LocalDestroy(); void LocalAddChild(View* child); @@ -157,6 +159,7 @@ class View { void LocalSetViewportMetrics(const ViewportMetrics& old_metrics, const ViewportMetrics& new_metrics); void LocalSetDrawn(bool drawn); + void LocalSetVisible(bool visible); // Methods implementing visibility change notifications. See ViewObserver // for more details. @@ -193,13 +196,13 @@ class View { // WindowProperty<>. struct Value { const char* name; - int64 value; + int64_t value; PropertyDeallocator deallocator; }; std::map<const void*, Value> prop_map_; - DISALLOW_COPY_AND_ASSIGN(View); + MOJO_DISALLOW_COPY_AND_ASSIGN(View); }; } // namespace mojo diff --git a/third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h b/third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h index edb5ca3..e47da3d 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h @@ -5,7 +5,6 @@ #ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_MANAGER_CLIENT_FACTORY_H_ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_MANAGER_CLIENT_FACTORY_H_ -#include "base/memory/scoped_ptr.h" #include "mojo/public/cpp/application/interface_factory.h" #include "view_manager/public/interfaces/view_manager.mojom.h" @@ -22,8 +21,9 @@ class ViewManagerClientFactory : public InterfaceFactory<ViewManagerClient> { ViewManagerClientFactory(Shell* shell, ViewManagerDelegate* delegate); ~ViewManagerClientFactory() override; - // Creates a ViewManagerClient from the supplied arguments. - static scoped_ptr<ViewManagerClient> WeakBindViewManagerToPipe( + // Creates a ViewManagerClient from the supplied arguments. Returns ownership + // to the caller. + static ViewManagerClient* WeakBindViewManagerToPipe( InterfaceRequest<ViewManagerClient> request, ViewManagerServicePtr view_manager_service, Shell* shell, diff --git a/third_party/mojo_services/src/view_manager/public/cpp/view_manager_context.h b/third_party/mojo_services/src/view_manager/public/cpp/view_manager_context.h index 1976598..de7b84b 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/view_manager_context.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/view_manager_context.h @@ -8,9 +8,8 @@ #include <string> #include <vector> -#include "base/bind.h" -#include "base/memory/scoped_ptr.h" #include "mojo/public/cpp/application/service_provider_impl.h" +#include "mojo/public/cpp/system/macros.h" namespace mojo { class ApplicationImpl; @@ -37,7 +36,7 @@ class ViewManagerContext { private: class InternalState; - scoped_ptr<InternalState> state_; + InternalState* state_; MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerContext); }; diff --git a/third_party/mojo_services/src/view_manager/public/cpp/view_observer.h b/third_party/mojo_services/src/view_manager/public/cpp/view_observer.h index f059e11..341a4f0 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/view_observer.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/view_observer.h @@ -7,8 +7,6 @@ #include <vector> -#include "base/basictypes.h" - #include "input_events/public/interfaces/input_events.mojom.h" #include "view_manager/public/cpp/view.h" diff --git a/third_party/mojo_services/src/view_manager/public/cpp/view_property.h b/third_party/mojo_services/src/view_manager/public/cpp/view_property.h index d3a345f..2dbd317 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/view_property.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/view_property.h @@ -5,7 +5,7 @@ #ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_PROPERTY_H_ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_PROPERTY_H_ -#include "base/basictypes.h" +#include <stdint.h> // This header should be included by code that defines ViewProperties. It // should not be included by code that only gets and sets ViewProperties. @@ -43,27 +43,27 @@ namespace mojo { namespace { -// No single new-style cast works for every conversion to/from int64, so we +// No single new-style cast works for every conversion to/from int64_t, so we // need this helper class. A third specialization is needed for bool because // MSVC warning C4800 (forcing value to bool) is not suppressed by an explicit // cast (!). template <typename T> class ViewPropertyCaster { public: - static int64 ToInt64(T x) { return static_cast<int64>(x); } - static T FromInt64(int64 x) { return static_cast<T>(x); } + static int64_t ToInt64(T x) { return static_cast<int64_t>(x); } + static T FromInt64(int64_t x) { return static_cast<T>(x); } }; template <typename T> class ViewPropertyCaster<T*> { public: - static int64 ToInt64(T* x) { return reinterpret_cast<int64>(x); } - static T* FromInt64(int64 x) { return reinterpret_cast<T*>(x); } + static int64_t ToInt64(T* x) { return reinterpret_cast<int64_t>(x); } + static T* FromInt64(int64_t x) { return reinterpret_cast<T*>(x); } }; template <> class ViewPropertyCaster<bool> { public: - static int64 ToInt64(bool x) { return static_cast<int64>(x); } - static bool FromInt64(int64 x) { return x != 0; } + static int64_t ToInt64(bool x) { return static_cast<int64_t>(x); } + static bool FromInt64(int64_t x) { return x != 0; } }; } // namespace @@ -77,7 +77,7 @@ struct ViewProperty { template <typename T> void View::SetLocalProperty(const ViewProperty<T>* property, T value) { - int64 old = SetLocalPropertyInternal( + int64_t old = SetLocalPropertyInternal( property, property->name, value == property->default_value ? nullptr : property->deallocator, ViewPropertyCaster<T>::ToInt64(value), @@ -111,23 +111,23 @@ void View::ClearLocalProperty(const ViewProperty<T>* property) { const mojo::ViewProperty<T>*); #define DECLARE_VIEW_PROPERTY_TYPE(T) DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(, T) -#define DEFINE_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ - COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64), property_type_too_large); \ - namespace { \ - const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \ - } \ +#define DEFINE_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ + COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64_t), property_type_too_large); \ + namespace { \ + const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \ + } \ const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value; -#define DEFINE_LOCAL_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ - COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64), property_type_too_large); \ - namespace { \ - const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \ - const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value; \ +#define DEFINE_LOCAL_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ + COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64_t), property_type_too_large); \ + namespace { \ + const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \ + const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value; \ } #define DEFINE_OWNED_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ namespace { \ - void Deallocator##NAME(int64 p) { \ + void Deallocator##NAME(int64_t p) { \ enum { type_must_be_complete = sizeof(TYPE) }; \ delete mojo::ViewPropertyCaster<TYPE*>::FromInt64(p); \ } \ diff --git a/third_party/mojo_services/src/view_manager/public/cpp/view_tracker.h b/third_party/mojo_services/src/view_manager/public/cpp/view_tracker.h index 05325b7..1d6bf14 100644 --- a/third_party/mojo_services/src/view_manager/public/cpp/view_tracker.h +++ b/third_party/mojo_services/src/view_manager/public/cpp/view_tracker.h @@ -5,10 +5,10 @@ #ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_TRACKER_H_ #define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_VIEW_TRACKER_H_ +#include <stdint.h> #include <set> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "mojo/public/cpp/system/macros.h" #include "view_manager/public/cpp/view_observer.h" namespace mojo { @@ -39,7 +39,7 @@ class ViewTracker : public ViewObserver { private: Views views_; - DISALLOW_COPY_AND_ASSIGN(ViewTracker); + MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker); }; } // namespace mojo diff --git a/tools/ipc_fuzzer/replay/replay_process.cc b/tools/ipc_fuzzer/replay/replay_process.cc index 0a7b830..8f942c6 100644 --- a/tools/ipc_fuzzer/replay/replay_process.cc +++ b/tools/ipc_fuzzer/replay/replay_process.cc @@ -93,7 +93,7 @@ void ReplayProcess::OpenChannel() { content::ShouldUseMojoChannel(); if (should_use_mojo) { channel_ = IPC::ChannelProxy::Create( - IPC::ChannelMojo::CreateClientFactory(channel_name), this, + IPC::ChannelMojo::CreateClientFactory(nullptr, channel_name), this, io_thread_.message_loop_proxy()); } else { channel_ = |