diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/mojo/services | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/mojo/services')
108 files changed, 10460 insertions, 0 deletions
diff --git a/chromium/mojo/services/DEPS b/chromium/mojo/services/DEPS new file mode 100644 index 00000000000..2bbe62df877 --- /dev/null +++ b/chromium/mojo/services/DEPS @@ -0,0 +1,10 @@ +include_rules = [ + "-mojo", + "+mojo/common", + "+mojo/public", + "+jni", + + # TODO(abarth) Instead of having the services depend on the shell, we + # probably should create a layer below the services. + "+mojo/shell", +] diff --git a/chromium/mojo/services/dbus_echo/DEPS b/chromium/mojo/services/dbus_echo/DEPS new file mode 100644 index 00000000000..35a7b6c826f --- /dev/null +++ b/chromium/mojo/services/dbus_echo/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+base", + "+mojo/dbus", + "+mojo/embedder", +] diff --git a/chromium/mojo/services/dbus_echo/dbus_echo_service.cc b/chromium/mojo/services/dbus_echo/dbus_echo_service.cc new file mode 100644 index 00000000000..ae8029f435b --- /dev/null +++ b/chromium/mojo/services/dbus_echo/dbus_echo_service.cc @@ -0,0 +1,58 @@ +// 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 "base/at_exit.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "mojo/dbus/dbus_external_service.h" +#include "mojo/embedder/channel_init.h" +#include "mojo/embedder/embedder.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/services/dbus_echo/echo.mojom.h" + +namespace { +class EchoServiceImpl : public mojo::InterfaceImpl<mojo::EchoService> { + public: + EchoServiceImpl() {} + virtual ~EchoServiceImpl() {} + + protected: + virtual void Echo( + const mojo::String& in_to_echo, + const mojo::Callback<void(mojo::String)>& callback) OVERRIDE { + DVLOG(1) << "Asked to echo " << in_to_echo; + callback.Run(in_to_echo); + } +}; + +const char kServiceName[] = "org.chromium.EchoService"; +} // anonymous namespace + +int main(int argc, char** argv) { + base::AtExitManager exit_manager; + base::CommandLine::Init(argc, argv); + + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(settings); + logging::SetLogItems(false, // Process ID + false, // Thread ID + false, // Timestamp + false); // Tick count + + mojo::embedder::Init(); + + base::MessageLoopForIO message_loop; + base::RunLoop run_loop; + + mojo::DBusExternalService<EchoServiceImpl> echo_service(kServiceName); + echo_service.Start(); + + run_loop.Run(); + return 0; +} diff --git a/chromium/mojo/services/dbus_echo/echo.mojom b/chromium/mojo/services/dbus_echo/echo.mojom new file mode 100644 index 00000000000..937737c223c --- /dev/null +++ b/chromium/mojo/services/dbus_echo/echo.mojom @@ -0,0 +1,11 @@ +// 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 mojo { + +interface EchoService { + Echo(string to_echo) => (string echoed); +}; + +} diff --git a/chromium/mojo/services/gles2/DEPS b/chromium/mojo/services/gles2/DEPS new file mode 100644 index 00000000000..acd992d92a2 --- /dev/null +++ b/chromium/mojo/services/gles2/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+gpu/command_buffer", + "+ui/gfx", + "+ui/gl", +] diff --git a/chromium/mojo/services/gles2/command_buffer.mojom b/chromium/mojo/services/gles2/command_buffer.mojom new file mode 100644 index 00000000000..1f91362eb4d --- /dev/null +++ b/chromium/mojo/services/gles2/command_buffer.mojom @@ -0,0 +1,49 @@ +// 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 mojo { + +struct CommandBufferState { + int32 num_entries; + int32 get_offset; + int32 put_offset; + int32 token; + int32 error; // TODO(piman): enum + int32 context_lost_reason; // TODO(piman): enum + uint32 generation; +}; + +interface CommandBufferSyncClient { + DidInitialize(bool success); + DidMakeProgress(CommandBufferState state); +}; + +[Client=CommandBufferClient] +interface CommandBuffer { + Initialize(CommandBufferSyncClient sync_client, + handle<shared_buffer> shared_state); + SetGetBuffer(int32 buffer); + Flush(int32 put_offset); + MakeProgress(int32 last_get_offset); + RegisterTransferBuffer( + int32 id, handle<shared_buffer> transfer_buffer, uint32 size); + DestroyTransferBuffer(int32 id); + Echo() => (); + + // TODO(piman): move to somewhere else (native_viewport?). + RequestAnimationFrames(); + CancelAnimationFrames(); + + // TODO(piman): sync points +}; + +interface CommandBufferClient { + DidDestroy(); + LostContext(int32 lost_reason); // TODO(piman): enum + + // TODO(piman): move to somewhere else (native_viewport?). + DrawAnimationFrame(); +}; + +} diff --git a/chromium/mojo/services/gles2/command_buffer_impl.cc b/chromium/mojo/services/gles2/command_buffer_impl.cc new file mode 100644 index 00000000000..090b78c3707 --- /dev/null +++ b/chromium/mojo/services/gles2/command_buffer_impl.cc @@ -0,0 +1,198 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/gles2/command_buffer_impl.h" + +#include "base/bind.h" +#include "base/memory/shared_memory.h" + +#include "gpu/command_buffer/common/constants.h" +#include "gpu/command_buffer/service/command_buffer_service.h" +#include "gpu/command_buffer/service/context_group.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder.h" +#include "gpu/command_buffer/service/gpu_control_service.h" +#include "gpu/command_buffer/service/gpu_scheduler.h" +#include "gpu/command_buffer/service/image_manager.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/memory_tracking.h" +#include "mojo/services/gles2/command_buffer_type_conversions.h" +#include "mojo/services/gles2/mojo_buffer_backing.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_surface.h" + +namespace mojo { +namespace services { + +namespace { + +class MemoryTrackerStub : public gpu::gles2::MemoryTracker { + public: + MemoryTrackerStub() {} + + virtual void TrackMemoryAllocatedChange(size_t old_size, + size_t new_size, + gpu::gles2::MemoryTracker::Pool pool) + OVERRIDE {} + + virtual bool EnsureGPUMemoryAvailable(size_t size_needed) OVERRIDE { + return true; + }; + + private: + virtual ~MemoryTrackerStub() {} + + DISALLOW_COPY_AND_ASSIGN(MemoryTrackerStub); +}; + +} // anonymous namespace + +CommandBufferImpl::CommandBufferImpl(gfx::AcceleratedWidget widget, + const gfx::Size& size) + : widget_(widget), size_(size) {} + +CommandBufferImpl::~CommandBufferImpl() { + client()->DidDestroy(); + if (decoder_.get()) { + bool have_context = decoder_->MakeCurrent(); + decoder_->Destroy(have_context); + } +} + +void CommandBufferImpl::OnConnectionError() { + // TODO(darin): How should we handle this error? +} + +void CommandBufferImpl::Initialize( + CommandBufferSyncClientPtr sync_client, + mojo::ScopedSharedBufferHandle shared_state) { + sync_client_ = sync_client.Pass(); + sync_client_->DidInitialize(DoInitialize(shared_state.Pass())); +} + +bool CommandBufferImpl::DoInitialize( + mojo::ScopedSharedBufferHandle shared_state) { + // TODO(piman): offscreen surface. + scoped_refptr<gfx::GLSurface> surface = + gfx::GLSurface::CreateViewGLSurface(widget_); + if (!surface.get()) + return false; + + // TODO(piman): context sharing, virtual contexts, gpu preference. + scoped_refptr<gfx::GLContext> context = gfx::GLContext::CreateGLContext( + NULL, surface.get(), gfx::PreferIntegratedGpu); + if (!context.get()) + return false; + + if (!context->MakeCurrent(surface.get())) + return false; + + // TODO(piman): ShaderTranslatorCache is currently per-ContextGroup but + // only needs to be per-thread. + scoped_refptr<gpu::gles2::ContextGroup> context_group = + new gpu::gles2::ContextGroup(NULL, + NULL, + new MemoryTrackerStub, + new gpu::gles2::ShaderTranslatorCache, + NULL, + true); + + command_buffer_.reset( + new gpu::CommandBufferService(context_group->transfer_buffer_manager())); + bool result = command_buffer_->Initialize(); + DCHECK(result); + + decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group.get())); + scheduler_.reset(new gpu::GpuScheduler( + command_buffer_.get(), decoder_.get(), decoder_.get())); + decoder_->set_engine(scheduler_.get()); + + gpu::gles2::DisallowedFeatures disallowed_features; + + // TODO(piman): attributes. + std::vector<int32> attrib_vector; + if (!decoder_->Initialize(surface, + context, + false /* offscreen */, + size_, + disallowed_features, + attrib_vector)) + return false; + + gpu_control_.reset( + new gpu::GpuControlService(context_group->image_manager(), NULL)); + + command_buffer_->SetPutOffsetChangeCallback(base::Bind( + &gpu::GpuScheduler::PutChanged, base::Unretained(scheduler_.get()))); + command_buffer_->SetGetBufferChangeCallback(base::Bind( + &gpu::GpuScheduler::SetGetBuffer, base::Unretained(scheduler_.get()))); + command_buffer_->SetParseErrorCallback( + base::Bind(&CommandBufferImpl::OnParseError, base::Unretained(this))); + + // TODO(piman): other callbacks + + const size_t kSize = sizeof(gpu::CommandBufferSharedState); + scoped_ptr<gpu::BufferBacking> backing( + gles2::MojoBufferBacking::Create(shared_state.Pass(), kSize)); + if (!backing.get()) + return false; + + command_buffer_->SetSharedStateBuffer(backing.Pass()); + return true; +} + +void CommandBufferImpl::SetGetBuffer(int32_t buffer) { + command_buffer_->SetGetBuffer(buffer); +} + +void CommandBufferImpl::Flush(int32_t put_offset) { + command_buffer_->Flush(put_offset); +} + +void CommandBufferImpl::MakeProgress(int32_t last_get_offset) { + // TODO(piman): handle out-of-order. + sync_client_->DidMakeProgress( + CommandBufferState::From(command_buffer_->GetLastState())); +} + +void CommandBufferImpl::RegisterTransferBuffer( + int32_t id, + mojo::ScopedSharedBufferHandle transfer_buffer, + uint32_t size) { + // Take ownership of the memory and map it into this process. + // This validates the size. + scoped_ptr<gpu::BufferBacking> backing( + gles2::MojoBufferBacking::Create(transfer_buffer.Pass(), size)); + if (!backing.get()) { + DVLOG(0) << "Failed to map shared memory."; + return; + } + command_buffer_->RegisterTransferBuffer(id, backing.Pass()); +} + +void CommandBufferImpl::DestroyTransferBuffer(int32_t id) { + command_buffer_->DestroyTransferBuffer(id); +} + +void CommandBufferImpl::Echo(const Callback<void()>& callback) { + callback.Run(); +} + +void CommandBufferImpl::RequestAnimationFrames() { + timer_.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds(16), + this, + &CommandBufferImpl::DrawAnimationFrame); +} + +void CommandBufferImpl::CancelAnimationFrames() { timer_.Stop(); } + +void CommandBufferImpl::OnParseError() { + gpu::CommandBuffer::State state = command_buffer_->GetLastState(); + client()->LostContext(state.context_lost_reason); +} + +void CommandBufferImpl::DrawAnimationFrame() { client()->DrawAnimationFrame(); } + +} // namespace services +} // namespace mojo diff --git a/chromium/mojo/services/gles2/command_buffer_impl.h b/chromium/mojo/services/gles2/command_buffer_impl.h new file mode 100644 index 00000000000..5d1c7bdd205 --- /dev/null +++ b/chromium/mojo/services/gles2/command_buffer_impl.h @@ -0,0 +1,72 @@ +// 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_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_ +#define MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/timer/timer.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/services/gles2/command_buffer.mojom.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/size.h" + +namespace gpu { +class CommandBufferService; +class GpuScheduler; +class GpuControlService; +namespace gles2 { +class GLES2Decoder; +} +} + +namespace mojo { +namespace services { + +class CommandBufferImpl : public InterfaceImpl<CommandBuffer> { + public: + CommandBufferImpl(gfx::AcceleratedWidget widget, + const gfx::Size& size); + virtual ~CommandBufferImpl(); + + virtual void OnConnectionError() OVERRIDE; + virtual void Initialize(CommandBufferSyncClientPtr sync_client, + mojo::ScopedSharedBufferHandle shared_state) OVERRIDE; + virtual void SetGetBuffer(int32_t buffer) OVERRIDE; + virtual void Flush(int32_t put_offset) OVERRIDE; + virtual void MakeProgress(int32_t last_get_offset) OVERRIDE; + virtual void RegisterTransferBuffer( + int32_t id, + mojo::ScopedSharedBufferHandle transfer_buffer, + uint32_t size) OVERRIDE; + virtual void DestroyTransferBuffer(int32_t id) OVERRIDE; + virtual void Echo(const Callback<void()>& callback) OVERRIDE; + + virtual void RequestAnimationFrames() OVERRIDE; + virtual void CancelAnimationFrames() OVERRIDE; + + private: + bool DoInitialize(mojo::ScopedSharedBufferHandle shared_state); + + void OnParseError(); + + void DrawAnimationFrame(); + + CommandBufferSyncClientPtr sync_client_; + + gfx::AcceleratedWidget widget_; + gfx::Size size_; + scoped_ptr<gpu::CommandBufferService> command_buffer_; + scoped_ptr<gpu::gles2::GLES2Decoder> decoder_; + scoped_ptr<gpu::GpuScheduler> scheduler_; + scoped_ptr<gpu::GpuControlService> gpu_control_; + base::RepeatingTimer<CommandBufferImpl> timer_; + + DISALLOW_COPY_AND_ASSIGN(CommandBufferImpl); +}; + +} // namespace services +} // namespace mojo + +#endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_ diff --git a/chromium/mojo/services/gles2/command_buffer_type_conversions.cc b/chromium/mojo/services/gles2/command_buffer_type_conversions.cc new file mode 100644 index 00000000000..3952bef3179 --- /dev/null +++ b/chromium/mojo/services/gles2/command_buffer_type_conversions.cc @@ -0,0 +1,40 @@ +// 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/gles2/command_buffer_type_conversions.h" + +#include "mojo/services/gles2/command_buffer.mojom.h" + +namespace mojo { + +CommandBufferStatePtr +TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State>::ConvertFrom( + const gpu::CommandBuffer::State& input) { + CommandBufferStatePtr result(CommandBufferState::New()); + result->num_entries = input.num_entries; + result->get_offset = input.get_offset; + result->put_offset = input.put_offset; + result->token = input.token; + result->error = input.error; + result->context_lost_reason = input.context_lost_reason; + result->generation = input.generation; + return result.Pass(); +} + +gpu::CommandBuffer::State +TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State>::ConvertTo( + const CommandBufferStatePtr& input) { + gpu::CommandBuffer::State state; + state.num_entries = input->num_entries; + state.get_offset = input->get_offset; + state.put_offset = input->put_offset; + state.token = input->token; + state.error = static_cast<gpu::error::Error>(input->error); + state.context_lost_reason = + static_cast<gpu::error::ContextLostReason>(input->context_lost_reason); + state.generation = input->generation; + return state; +} + +} // namespace mojo diff --git a/chromium/mojo/services/gles2/command_buffer_type_conversions.h b/chromium/mojo/services/gles2/command_buffer_type_conversions.h new file mode 100644 index 00000000000..b2334d008ac --- /dev/null +++ b/chromium/mojo/services/gles2/command_buffer_type_conversions.h @@ -0,0 +1,27 @@ +// 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_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_ +#define MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_ + +#include "gpu/command_buffer/common/command_buffer.h" +#include "mojo/public/cpp/bindings/type_converter.h" +#include "mojo/services/gles2/command_buffer.mojom.h" + +namespace mojo { + +class CommandBufferState; + +template <> +class TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State> { + public: + static CommandBufferStatePtr ConvertFrom( + const gpu::CommandBuffer::State& input); + static gpu::CommandBuffer::State ConvertTo( + const CommandBufferStatePtr& input); +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_ diff --git a/chromium/mojo/services/gles2/mojo_buffer_backing.cc b/chromium/mojo/services/gles2/mojo_buffer_backing.cc new file mode 100644 index 00000000000..3c908787ff5 --- /dev/null +++ b/chromium/mojo/services/gles2/mojo_buffer_backing.cc @@ -0,0 +1,36 @@ +// 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/gles2/mojo_buffer_backing.h" + +#include "base/logging.h" + +namespace mojo { +namespace gles2 { + +MojoBufferBacking::MojoBufferBacking(mojo::ScopedSharedBufferHandle handle, + void* memory, + size_t size) + : handle_(handle.Pass()), memory_(memory), size_(size) {} + +MojoBufferBacking::~MojoBufferBacking() { mojo::UnmapBuffer(memory_); } + +// static +scoped_ptr<gpu::BufferBacking> MojoBufferBacking::Create( + mojo::ScopedSharedBufferHandle handle, + size_t size) { + void* memory = NULL; + MojoResult result = mojo::MapBuffer( + handle.get(), 0, size, &memory, MOJO_MAP_BUFFER_FLAG_NONE); + if (result != MOJO_RESULT_OK) + return scoped_ptr<BufferBacking>(); + DCHECK(memory); + return scoped_ptr<BufferBacking>( + new MojoBufferBacking(handle.Pass(), memory, size)); +} +void* MojoBufferBacking::GetMemory() const { return memory_; } +size_t MojoBufferBacking::GetSize() const { return size_; } + +} // namespace gles2 +} // namespace mojo diff --git a/chromium/mojo/services/gles2/mojo_buffer_backing.h b/chromium/mojo/services/gles2/mojo_buffer_backing.h new file mode 100644 index 00000000000..4048b6cbff2 --- /dev/null +++ b/chromium/mojo/services/gles2/mojo_buffer_backing.h @@ -0,0 +1,40 @@ +// 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_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_ +#define MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_ + +#include "base/memory/scoped_ptr.h" +#include "gpu/command_buffer/common/buffer.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +namespace gles2 { + +class MojoBufferBacking : public gpu::BufferBacking { + public: + MojoBufferBacking(mojo::ScopedSharedBufferHandle handle, + void* memory, + size_t size); + virtual ~MojoBufferBacking(); + + static scoped_ptr<gpu::BufferBacking> Create( + mojo::ScopedSharedBufferHandle handle, + size_t size); + + virtual void* GetMemory() const OVERRIDE; + virtual size_t GetSize() const OVERRIDE; + + private: + mojo::ScopedSharedBufferHandle handle_; + void* memory_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(MojoBufferBacking); +}; + +} // namespace gles2 +} // namespace mojo + +#endif // MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_ diff --git a/chromium/mojo/services/launcher/DEPS b/chromium/mojo/services/launcher/DEPS new file mode 100644 index 00000000000..28f1ec1d788 --- /dev/null +++ b/chromium/mojo/services/launcher/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+mojo/services/public", +] diff --git a/chromium/mojo/services/launcher/launcher.cc b/chromium/mojo/services/launcher/launcher.cc new file mode 100644 index 00000000000..eb4de409c8b --- /dev/null +++ b/chromium/mojo/services/launcher/launcher.cc @@ -0,0 +1,176 @@ +// 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 "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_tokenizer.h" +#include "mojo/public/cpp/application/application.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/interfaces/launcher/launcher.mojom.h" +#include "mojo/services/public/interfaces/network/network_service.mojom.h" +#include "mojo/services/public/interfaces/network/url_loader.mojom.h" +#include "url/gurl.h" + +namespace mojo { +namespace launcher { + +class LauncherApp; + +class LauncherConnection : public InterfaceImpl<Launcher> { + public: + explicit LauncherConnection(LauncherApp* app) : app_(app) {} + virtual ~LauncherConnection() {} + + private: + // Overridden from Launcher: + virtual void Launch(const String& url) OVERRIDE; + + LauncherApp* app_; + + DISALLOW_COPY_AND_ASSIGN(LauncherConnection); +}; + +class LaunchInstance : public URLLoaderClient { + public: + LaunchInstance(LauncherApp* app, + LauncherClient* client, + const String& url); + virtual ~LaunchInstance() {} + + private: + // Overridden from URLLoaderClient: + virtual void OnReceivedRedirect(URLResponsePtr response, + const String& new_url, + const String& new_method) OVERRIDE { + } + virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE; + virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE { + ScheduleDestroy(); + } + virtual void OnReceivedEndOfResponseBody() OVERRIDE { + ScheduleDestroy(); + } + + std::string GetContentType(const Array<String>& headers) { + for (size_t i = 0; i < headers.size(); ++i) { + base::StringTokenizer t(headers[i], ": ;="); + while (t.GetNext()) { + if (!t.token_is_delim() && t.token() == "Content-Type") { + while (t.GetNext()) { + if (!t.token_is_delim()) + return t.token(); + } + } + } + } + return ""; + } + + void ScheduleDestroy() { + if (destroy_scheduled_) + return; + destroy_scheduled_ = true; + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); + } + + LauncherApp* app_; + bool destroy_scheduled_; + LauncherClient* client_; + URLLoaderPtr url_loader_; + ScopedDataPipeConsumerHandle response_body_stream_; + + DISALLOW_COPY_AND_ASSIGN(LaunchInstance); +}; + +class LauncherApp : public Application { + public: + LauncherApp() { + handler_map_["text/html"] = "mojo:mojo_html_viewer"; + handler_map_["image/png"] = "mojo:mojo_image_viewer"; + } + virtual ~LauncherApp() {} + + URLLoaderPtr CreateURLLoader() { + URLLoaderPtr loader; + network_service_->CreateURLLoader(Get(&loader)); + return loader.Pass(); + } + + std::string GetHandlerForContentType(const std::string& content_type) { + HandlerMap::const_iterator it = handler_map_.find(content_type); + return it != handler_map_.end() ? it->second : ""; + } + + private: + typedef std::map<std::string, std::string> HandlerMap; + + // Overridden from Application: + virtual void Initialize() OVERRIDE { + AddService<LauncherConnection>(this); + ConnectTo("mojo:mojo_network_service", &network_service_); + } + + HandlerMap handler_map_; + + NetworkServicePtr network_service_; + + DISALLOW_COPY_AND_ASSIGN(LauncherApp); +}; + +void LauncherConnection::Launch(const String& url_string) { + GURL url(url_string.To<std::string>()); + + // For Mojo URLs, the handler can always be found at the origin. + // TODO(aa): Return error for invalid URL? + if (url.is_valid() && url.SchemeIs("mojo")) { + client()->OnLaunch(url_string, + url.GetOrigin().spec(), + navigation::ResponseDetailsPtr()); + return; + } + + new LaunchInstance(app_, client(), url_string); +} + +LaunchInstance::LaunchInstance(LauncherApp* app, + LauncherClient* client, + const String& url) + : app_(app), + destroy_scheduled_(false), + client_(client) { + url_loader_ = app_->CreateURLLoader(); + url_loader_.set_client(this); + + URLRequestPtr request(URLRequest::New()); + request->url = url; + request->method = "GET"; + request->auto_follow_redirects = true; + + DataPipe data_pipe; + response_body_stream_ = data_pipe.consumer_handle.Pass(); + + url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass()); +} + +void LaunchInstance::OnReceivedResponse(URLResponsePtr response) { + std::string content_type = GetContentType(response->headers); + std::string handler_url = app_->GetHandlerForContentType(content_type); + if (!handler_url.empty()) { + navigation::ResponseDetailsPtr nav_response( + navigation::ResponseDetails::New()); + nav_response->response = response.Pass(); + nav_response->response_body_stream = response_body_stream_.Pass(); + client_->OnLaunch(nav_response->response->url, handler_url, + nav_response.Pass()); + } +} + +} // namespace launcher + +// static +Application* Application::Create() { + return new launcher::LauncherApp; +} + +} // namespace mojo diff --git a/chromium/mojo/services/native_viewport/DEPS b/chromium/mojo/services/native_viewport/DEPS new file mode 100644 index 00000000000..be8903cd0fd --- /dev/null +++ b/chromium/mojo/services/native_viewport/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + "+mojo/services/public/cpp/geometry", + "+mojo/services/public/cpp/input_events", + "+mojo/services/public/interfaces/native_viewport", + "+mojo/services/gles2", + "+ui/events", + "+ui/gfx", +] diff --git a/chromium/mojo/services/native_viewport/native_viewport.h b/chromium/mojo/services/native_viewport/native_viewport.h new file mode 100644 index 00000000000..7ec54329b15 --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport.h @@ -0,0 +1,62 @@ +// 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_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_ + +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/size.h" + +namespace gfx { +class Rect; +} + +namespace ui { +class Event; +} + +namespace mojo { +namespace shell { +class Context; +} + +namespace services { + +class NativeViewportDelegate { + public: + virtual ~NativeViewportDelegate() {} + + virtual void OnBoundsChanged(const gfx::Rect& size) = 0; + virtual void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) = 0; + virtual bool OnEvent(ui::Event* ui_event) = 0; + virtual void OnDestroyed() = 0; +}; + +// Encapsulation of platform-specific Viewport. +// TODO(abarth): Rename this class so that it doesn't conflict with the name of +// the service. +class NativeViewport { + public: + virtual ~NativeViewport() {} + + virtual void Init(const gfx::Rect& bounds) = 0; + virtual void Show() = 0; + virtual void Hide() = 0; + virtual void Close() = 0; + virtual gfx::Size GetSize() = 0; + virtual void SetBounds(const gfx::Rect& bounds) = 0; + + virtual void SetCapture() = 0; + virtual void ReleaseCapture() = 0; + + // |context| is NULL when loaded into separate process. + static scoped_ptr<NativeViewport> Create(shell::Context* context, + NativeViewportDelegate* delegate); +}; + +} // namespace services +} // namespace mojo + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_ diff --git a/chromium/mojo/services/native_viewport/native_viewport_android.cc b/chromium/mojo/services/native_viewport/native_viewport_android.cc new file mode 100644 index 00000000000..1763f170ad3 --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_android.cc @@ -0,0 +1,161 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/native_viewport_android.h" + +#include <android/input.h> +#include <android/native_window_jni.h> + +#include "base/android/jni_android.h" +#include "jni/NativeViewportAndroid_jni.h" +#include "mojo/shell/context.h" +#include "ui/events/event.h" +#include "ui/gfx/point.h" + +namespace mojo { +namespace services { + +ui::EventType MotionEventActionToEventType(jint action) { + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: + return ui::ET_TOUCH_PRESSED; + case AMOTION_EVENT_ACTION_MOVE: + return ui::ET_TOUCH_MOVED; + case AMOTION_EVENT_ACTION_UP: + return ui::ET_TOUCH_RELEASED; + default: + NOTREACHED(); + } + return ui::ET_UNKNOWN; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeViewportAndroid, public: + +// static +bool NativeViewportAndroid::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +NativeViewportAndroid::NativeViewportAndroid(shell::Context* context, + NativeViewportDelegate* delegate) + : delegate_(delegate), + context_(context), + window_(NULL), + id_generator_(0), + weak_factory_(this) { +} + +NativeViewportAndroid::~NativeViewportAndroid() { + if (window_) + ReleaseWindow(); +} + +void NativeViewportAndroid::Destroy(JNIEnv* env, jobject obj) { + delegate_->OnDestroyed(); +} + +void NativeViewportAndroid::SurfaceCreated(JNIEnv* env, + jobject obj, + jobject jsurface) { + base::android::ScopedJavaLocalRef<jobject> protector(env, jsurface); + // Note: This ensures that any local references used by + // ANativeWindow_fromSurface are released immediately. This is needed as a + // workaround for https://code.google.com/p/android/issues/detail?id=68174 + { + base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env); + window_ = ANativeWindow_fromSurface(env, jsurface); + } + delegate_->OnAcceleratedWidgetAvailable(window_); +} + +void NativeViewportAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) { + DCHECK(window_); + ReleaseWindow(); +} + +void NativeViewportAndroid::SurfaceSetSize(JNIEnv* env, jobject obj, + jint width, jint height) { + bounds_ = gfx::Rect(width, height); + delegate_->OnBoundsChanged(bounds_); +} + +bool NativeViewportAndroid::TouchEvent(JNIEnv* env, jobject obj, + jint pointer_id, + jint action, + jfloat x, jfloat y, + jlong time_ms) { + gfx::Point location(static_cast<int>(x), static_cast<int>(y)); + ui::TouchEvent event(MotionEventActionToEventType(action), location, + id_generator_.GetGeneratedID(pointer_id), + base::TimeDelta::FromMilliseconds(time_ms)); + // TODO(beng): handle multiple touch-points. + delegate_->OnEvent(&event); + if (action == ui::ET_TOUCH_RELEASED) + id_generator_.ReleaseNumber(pointer_id); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeViewportAndroid, NativeViewport implementation: + +void NativeViewportAndroid::Init(const gfx::Rect& bounds) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_NativeViewportAndroid_createForActivity(env, context_->activity(), + reinterpret_cast<jlong>(this)); +} + +void NativeViewportAndroid::Show() { + // Nothing to do. View is created visible. +} + +void NativeViewportAndroid::Hide() { + // Nothing to do. View is always visible. +} + +void NativeViewportAndroid::Close() { + // TODO(beng): close activity containing MojoView? + + // TODO(beng): perform this in response to view destruction. + delegate_->OnDestroyed(); +} + +gfx::Size NativeViewportAndroid::GetSize() { + return bounds_.size(); +} + +void NativeViewportAndroid::SetBounds(const gfx::Rect& bounds) { + NOTIMPLEMENTED(); +} + +void NativeViewportAndroid::SetCapture() { + NOTIMPLEMENTED(); +} + +void NativeViewportAndroid::ReleaseCapture() { + NOTIMPLEMENTED(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeViewportAndroid, private: + +void NativeViewportAndroid::ReleaseWindow() { + ANativeWindow_release(window_); + window_ = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeViewport, public: + +// static +scoped_ptr<NativeViewport> NativeViewport::Create( + shell::Context* context, + NativeViewportDelegate* delegate) { + return scoped_ptr<NativeViewport>( + new NativeViewportAndroid(context, delegate)).Pass(); +} + +} // namespace services +} // namespace mojo diff --git a/chromium/mojo/services/native_viewport/native_viewport_android.h b/chromium/mojo/services/native_viewport/native_viewport_android.h new file mode 100644 index 00000000000..c24e38e7858 --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_android.h @@ -0,0 +1,71 @@ +// 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_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/android/scoped_java_ref.h" +#include "base/memory/weak_ptr.h" +#include "mojo/services/native_viewport/native_viewport.h" +#include "mojo/services/native_viewport/native_viewport_export.h" +#include "ui/events/event_constants.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/sequential_id_generator.h" +#include "ui/gfx/size.h" + +namespace gpu { +class GLInProcessContext; +} + +struct ANativeWindow; + +namespace mojo { +namespace services { + +class MOJO_NATIVE_VIEWPORT_EXPORT NativeViewportAndroid + : public NativeViewport { + public: + static MOJO_NATIVE_VIEWPORT_EXPORT bool Register(JNIEnv* env); + + explicit NativeViewportAndroid(shell::Context* context, + NativeViewportDelegate* delegate); + virtual ~NativeViewportAndroid(); + + void Destroy(JNIEnv* env, jobject obj); + void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface); + void SurfaceDestroyed(JNIEnv* env, jobject obj); + void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height); + bool TouchEvent(JNIEnv* env, jobject obj, jint pointer_id, jint action, + jfloat x, jfloat y, jlong time_ms); + + private: + // Overridden from NativeViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void Close() OVERRIDE; + virtual gfx::Size GetSize() OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + + void ReleaseWindow(); + + NativeViewportDelegate* delegate_; + shell::Context* context_; + ANativeWindow* window_; + gfx::Rect bounds_; + ui::SequentialIDGenerator id_generator_; + + base::WeakPtrFactory<NativeViewportAndroid> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewportAndroid); +}; + + +} // namespace services +} // namespace mojo + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_ diff --git a/chromium/mojo/services/native_viewport/native_viewport_export.h b/chromium/mojo/services/native_viewport/native_viewport_export.h new file mode 100644 index 00000000000..4870c566f9d --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_export.h @@ -0,0 +1,32 @@ +// 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_SERVICES_NATIVE_VIEWPORT_EXPORT_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_ + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(MOJO_NATIVE_VIEWPORT_IMPLEMENTATION) +#define MOJO_NATIVE_VIEWPORT_EXPORT __declspec(dllexport) +#else +#define MOJO_NATIVE_VIEWPORT_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_NATIVE_VIEWPORT_IMPLEMENTATION) +#define MOJO_NATIVE_VIEWPORT_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_NATIVE_VIEWPORT_EXPORT +#endif + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) +#define MOJO_NATIVE_VIEWPORT_EXPORT +#endif + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_ diff --git a/chromium/mojo/services/native_viewport/native_viewport_mac.mm b/chromium/mojo/services/native_viewport/native_viewport_mac.mm new file mode 100644 index 00000000000..9d7301f6767 --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_mac.mm @@ -0,0 +1,88 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/native_viewport.h" + +#import <AppKit/NSApplication.h> +#import <AppKit/NSView.h> +#import <AppKit/NSWindow.h> + +#include "base/bind.h" +#include "ui/gfx/rect.h" + +namespace mojo { +namespace services { + +class NativeViewportMac : public NativeViewport { + public: + NativeViewportMac(NativeViewportDelegate* delegate) + : delegate_(delegate), + window_(nil) { + } + + virtual ~NativeViewportMac() { + [window_ orderOut:nil]; + [window_ close]; + } + + private: + // Overridden from NativeViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + [NSApplication sharedApplication]; + + rect_ = bounds; + window_ = [[NSWindow alloc] + initWithContentRect:NSRectFromCGRect(rect_.ToCGRect()) + styleMask:NSTitledWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + delegate_->OnAcceleratedWidgetAvailable([window_ contentView]); + delegate_->OnBoundsChanged(rect_); + } + + virtual void Show() OVERRIDE { + [window_ orderFront:nil]; + } + + virtual void Hide() OVERRIDE { + [window_ orderOut:nil]; + } + + virtual void Close() OVERRIDE { + // TODO(beng): perform this in response to NSWindow destruction. + delegate_->OnDestroyed(); + } + + virtual gfx::Size GetSize() OVERRIDE { + return rect_.size(); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void SetCapture() OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void ReleaseCapture() OVERRIDE { + NOTIMPLEMENTED(); + } + + NativeViewportDelegate* delegate_; + NSWindow* window_; + gfx::Rect rect_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewportMac); +}; + +// static +scoped_ptr<NativeViewport> NativeViewport::Create( + shell::Context* context, + NativeViewportDelegate* delegate) { + return scoped_ptr<NativeViewport>(new NativeViewportMac(delegate)).Pass(); +} + +} // namespace services +} // namespace mojo diff --git a/chromium/mojo/services/native_viewport/native_viewport_service.cc b/chromium/mojo/services/native_viewport/native_viewport_service.cc new file mode 100644 index 00000000000..886c4dcb4b9 --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_service.cc @@ -0,0 +1,165 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/native_viewport_service.h" + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/time/time.h" +#include "mojo/public/interfaces/service_provider/service_provider.mojom.h" +#include "mojo/services/gles2/command_buffer_impl.h" +#include "mojo/services/native_viewport/native_viewport.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/public/cpp/input_events/input_events_type_converters.h" +#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" +#include "ui/events/event.h" + +namespace mojo { +namespace services { +namespace { + +bool IsRateLimitedEventType(ui::Event* event) { + return event->type() == ui::ET_MOUSE_MOVED || + event->type() == ui::ET_MOUSE_DRAGGED || + event->type() == ui::ET_TOUCH_MOVED; +} + +} + +class NativeViewportImpl + : public InterfaceImpl<mojo::NativeViewport>, + public NativeViewportDelegate { + public: + NativeViewportImpl(shell::Context* context) + : context_(context), + widget_(gfx::kNullAcceleratedWidget), + waiting_for_event_ack_(false), + weak_factory_(this) {} + virtual ~NativeViewportImpl() { + // Destroy the NativeViewport early on as it may call us back during + // destruction and we want to be in a known state. + native_viewport_.reset(); + } + + virtual void Create(RectPtr bounds) OVERRIDE { + native_viewport_ = + services::NativeViewport::Create(context_, this); + native_viewport_->Init(bounds.To<gfx::Rect>()); + client()->OnCreated(); + OnBoundsChanged(bounds.To<gfx::Rect>()); + } + + virtual void Show() OVERRIDE { + native_viewport_->Show(); + } + + virtual void Hide() OVERRIDE { + native_viewport_->Hide(); + } + + virtual void Close() OVERRIDE { + command_buffer_.reset(); + DCHECK(native_viewport_); + native_viewport_->Close(); + } + + virtual void SetBounds(RectPtr bounds) OVERRIDE { + native_viewport_->SetBounds(bounds.To<gfx::Rect>()); + } + + virtual void CreateGLES2Context( + InterfaceRequest<CommandBuffer> command_buffer_request) OVERRIDE { + if (command_buffer_.get() || command_buffer_request_.is_pending()) { + LOG(ERROR) << "Can't create multiple contexts on a NativeViewport"; + return; + } + command_buffer_request_ = command_buffer_request.Pass(); + CreateCommandBufferIfNeeded(); + } + + void AckEvent() { + waiting_for_event_ack_ = false; + } + + void CreateCommandBufferIfNeeded() { + if (!command_buffer_request_.is_pending()) + return; + DCHECK(!command_buffer_.get()); + if (widget_ == gfx::kNullAcceleratedWidget) + return; + gfx::Size size = native_viewport_->GetSize(); + if (size.IsEmpty()) + return; + command_buffer_.reset( + new CommandBufferImpl(widget_, native_viewport_->GetSize())); + BindToRequest(command_buffer_.get(), &command_buffer_request_); + } + + virtual bool OnEvent(ui::Event* ui_event) OVERRIDE { + // Must not return early before updating capture. + switch (ui_event->type()) { + case ui::ET_MOUSE_PRESSED: + case ui::ET_TOUCH_PRESSED: + native_viewport_->SetCapture(); + break; + case ui::ET_MOUSE_RELEASED: + case ui::ET_TOUCH_RELEASED: + native_viewport_->ReleaseCapture(); + break; + default: + break; + } + + if (waiting_for_event_ack_ && IsRateLimitedEventType(ui_event)) + return false; + + client()->OnEvent( + TypeConverter<EventPtr, ui::Event>::ConvertFrom(*ui_event), + base::Bind(&NativeViewportImpl::AckEvent, + weak_factory_.GetWeakPtr())); + waiting_for_event_ack_ = true; + return false; + } + + virtual void OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) OVERRIDE { + widget_ = widget; + CreateCommandBufferIfNeeded(); + } + + virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE { + CreateCommandBufferIfNeeded(); + client()->OnBoundsChanged(Rect::From(bounds)); + } + + virtual void OnDestroyed() OVERRIDE { + command_buffer_.reset(); + client()->OnDestroyed(); + base::MessageLoop::current()->Quit(); + } + + private: + shell::Context* context_; + gfx::AcceleratedWidget widget_; + scoped_ptr<services::NativeViewport> native_viewport_; + InterfaceRequest<CommandBuffer> command_buffer_request_; + scoped_ptr<CommandBufferImpl> command_buffer_; + bool waiting_for_event_ack_; + base::WeakPtrFactory<NativeViewportImpl> weak_factory_; +}; + +} // namespace services +} // namespace mojo + + +MOJO_NATIVE_VIEWPORT_EXPORT mojo::Application* + CreateNativeViewportService( + mojo::shell::Context* context, + mojo::ScopedMessagePipeHandle service_provider_handle) { + mojo::Application* app = new mojo::Application( + service_provider_handle.Pass()); + app->AddService<mojo::services::NativeViewportImpl>(context); + return app; +} diff --git a/chromium/mojo/services/native_viewport/native_viewport_service.h b/chromium/mojo/services/native_viewport/native_viewport_service.h new file mode 100644 index 00000000000..99b455c9bae --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_service.h @@ -0,0 +1,18 @@ +// 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_SERVICES_NATIVE_VIEWPORT_SERVICE_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_ + +#include "base/memory/scoped_vector.h" +#include "mojo/public/cpp/application/application.h" +#include "mojo/services/native_viewport/native_viewport_export.h" +#include "mojo/shell/context.h" + +MOJO_NATIVE_VIEWPORT_EXPORT mojo::Application* + CreateNativeViewportService( + mojo::shell::Context* context, + mojo::ScopedMessagePipeHandle service_provider_handle); + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_ diff --git a/chromium/mojo/services/native_viewport/native_viewport_stub.cc b/chromium/mojo/services/native_viewport/native_viewport_stub.cc new file mode 100644 index 00000000000..9dcb33a8806 --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_stub.cc @@ -0,0 +1,50 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/native_viewport.h" + +// Stub to build on platforms we don't fully support yet. + +namespace mojo { +namespace services { + +class NativeViewportStub : public NativeViewport { + public: + NativeViewportStub(NativeViewportDelegate* delegate) + : delegate_(delegate) { + } + virtual ~NativeViewportStub() { + } + + private: + // Overridden from NativeViewport: + virtual void Init() OVERRIDE { + } + virtual void Show() OVERRIDE { + } + virtual void Hide() OVERRIDE { + } + virtual void Close() OVERRIDE { + delegate_->OnDestroyed(); + } + virtual gfx::Size GetSize() OVERRIDE { + return gfx::Size(); + } + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + } + + NativeViewportDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewportStub); +}; + +// static +scoped_ptr<NativeViewport> NativeViewport::Create( + shell::Context* context, + NativeViewportDelegate* delegate) { + return scoped_ptr<NativeViewport>(new NativeViewportStub(delegate)).Pass(); +} + +} // namespace services +} // namespace mojo diff --git a/chromium/mojo/services/native_viewport/native_viewport_win.cc b/chromium/mojo/services/native_viewport/native_viewport_win.cc new file mode 100644 index 00000000000..52ff6cb6f0a --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_win.cc @@ -0,0 +1,167 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/native_viewport.h" + +#include "ui/events/event.h" +#include "ui/gfx/win/msg_util.h" +#include "ui/gfx/win/window_impl.h" + +namespace mojo { +namespace services { +namespace { + +gfx::Rect GetWindowBoundsForClientBounds(DWORD style, DWORD ex_style, + const gfx::Rect& bounds) { + RECT wr; + wr.left = bounds.x(); + wr.top = bounds.y(); + wr.right = bounds.x() + bounds.width(); + wr.bottom = bounds.y() + bounds.height(); + AdjustWindowRectEx(&wr, style, FALSE, ex_style); + + // Make sure to keep the window onscreen, as AdjustWindowRectEx() may have + // moved part of it offscreen. + gfx::Rect window_bounds(wr.left, wr.top, + wr.right - wr.left, wr.bottom - wr.top); + window_bounds.set_x(std::max(0, window_bounds.x())); + window_bounds.set_y(std::max(0, window_bounds.y())); + return window_bounds; +} + +} + +class NativeViewportWin : public gfx::WindowImpl, + public NativeViewport { + public: + explicit NativeViewportWin(NativeViewportDelegate* delegate) + : delegate_(delegate) { + } + virtual ~NativeViewportWin() { + if (IsWindow(hwnd())) + DestroyWindow(hwnd()); + } + + private: + // Overridden from NativeViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + gfx::Rect window_bounds = GetWindowBoundsForClientBounds( + WS_OVERLAPPEDWINDOW, window_ex_style(), bounds); + gfx::WindowImpl::Init(NULL, window_bounds); + SetWindowText(hwnd(), L"native_viewport::NativeViewportWin!"); + } + + virtual void Show() OVERRIDE { + ShowWindow(hwnd(), SW_SHOWNORMAL); + } + + virtual void Hide() OVERRIDE { + ShowWindow(hwnd(), SW_HIDE); + } + + virtual void Close() OVERRIDE { + DestroyWindow(hwnd()); + } + + virtual gfx::Size GetSize() OVERRIDE { + RECT cr; + GetClientRect(hwnd(), &cr); + return gfx::Size(cr.right - cr.left, cr.bottom - cr.top); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + gfx::Rect window_bounds = GetWindowBoundsForClientBounds( + GetWindowLong(hwnd(), GWL_STYLE), + GetWindowLong(hwnd(), GWL_EXSTYLE), + bounds); + SetWindowPos(hwnd(), NULL, window_bounds.x(), window_bounds.y(), + window_bounds.width(), window_bounds.height(), + SWP_NOREPOSITION); + } + + virtual void SetCapture() OVERRIDE { + DCHECK(::GetCapture() != hwnd()); + ::SetCapture(hwnd()); + } + + virtual void ReleaseCapture() OVERRIDE { + if (::GetCapture() == hwnd()) + ::ReleaseCapture(); + } + + CR_BEGIN_MSG_MAP_EX(NativeViewportWin) + CR_MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) + + CR_MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_CHAR, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_SYSCHAR, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_IME_CHAR, OnKeyEvent) + + CR_MSG_WM_CREATE(OnCreate) + CR_MSG_WM_DESTROY(OnDestroy) + CR_MSG_WM_PAINT(OnPaint) + CR_MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) + CR_END_MSG_MAP() + + LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param) { + MSG msg = { hwnd(), message, w_param, l_param, 0, + { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } }; + ui::MouseEvent event(msg); + SetMsgHandled(delegate_->OnEvent(&event)); + return 0; + } + LRESULT OnKeyEvent(UINT message, WPARAM w_param, LPARAM l_param) { + MSG msg = { hwnd(), message, w_param, l_param }; + ui::KeyEvent event(msg, message == WM_CHAR); + SetMsgHandled(delegate_->OnEvent(&event)); + return 0; + } + LRESULT OnCreate(CREATESTRUCT* create_struct) { + delegate_->OnAcceleratedWidgetAvailable(hwnd()); + return 0; + } + void OnDestroy() { + delegate_->OnDestroyed(); + } + void OnPaint(HDC) { + RECT cr; + GetClientRect(hwnd(), &cr); + + PAINTSTRUCT ps; + HDC dc = BeginPaint(hwnd(), &ps); + HBRUSH red_brush = CreateSolidBrush(RGB(255, 0, 0)); + HGDIOBJ old_object = SelectObject(dc, red_brush); + Rectangle(dc, cr.left, cr.top, cr.right, cr.bottom); + SelectObject(dc, old_object); + DeleteObject(red_brush); + EndPaint(hwnd(), &ps); + } + void OnWindowPosChanged(WINDOWPOS* window_pos) { + if (!(window_pos->flags & SWP_NOSIZE) || + !(window_pos->flags & SWP_NOMOVE)) { + RECT cr; + GetClientRect(hwnd(), &cr); + delegate_->OnBoundsChanged( + gfx::Rect(window_pos->x, window_pos->y, + cr.right - cr.left, cr.bottom - cr.top)); + } + } + + NativeViewportDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewportWin); +}; + +// static +scoped_ptr<NativeViewport> NativeViewport::Create( + shell::Context* context, + NativeViewportDelegate* delegate) { + return scoped_ptr<NativeViewport>(new NativeViewportWin(delegate)).Pass(); +} + +} // namespace services +} // namespace mojo diff --git a/chromium/mojo/services/native_viewport/native_viewport_x11.cc b/chromium/mojo/services/native_viewport/native_viewport_x11.cc new file mode 100644 index 00000000000..f1b4863529c --- /dev/null +++ b/chromium/mojo/services/native_viewport/native_viewport_x11.cc @@ -0,0 +1,163 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/native_viewport.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include "base/message_loop/message_loop.h" +#include "ui/events/event.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/x/x11_types.h" + +namespace mojo { +namespace services { + +class NativeViewportX11 : public NativeViewport, + public ui::PlatformEventDispatcher { + public: + NativeViewportX11(NativeViewportDelegate* delegate) + : delegate_(delegate) { + } + + virtual ~NativeViewportX11() { + event_source_->RemovePlatformEventDispatcher(this); + + XDestroyWindow(gfx::GetXDisplay(), window_); + } + + private: + // Overridden from NativeViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + XDisplay* display = gfx::GetXDisplay(); + + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.override_redirect = False; + + bounds_ = bounds; + window_ = XCreateWindow( + display, + DefaultRootWindow(display), + bounds_.x(), bounds_.y(), bounds_.width(), bounds_.height(), + 0, // border width + CopyFromParent, // depth + InputOutput, + CopyFromParent, // visual + CWBackPixmap | CWOverrideRedirect, + &swa); + + atom_wm_protocols_ = XInternAtom(display, "WM_PROTOCOLS", 1); + atom_wm_delete_window_ = XInternAtom(display, "WM_DELETE_WINDOW", 1); + XSetWMProtocols(display, window_, &atom_wm_delete_window_, 1); + + event_source_ = ui::PlatformEventSource::CreateDefault(); + event_source_->AddPlatformEventDispatcher(this); + + long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | + KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask | + ExposureMask | VisibilityChangeMask | StructureNotifyMask | + PropertyChangeMask | PointerMotionMask; + XSelectInput(display, window_, event_mask); + + // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with + // the desktop environment. + XSetWMProperties(display, window_, NULL, NULL, NULL, 0, NULL, NULL, NULL); + + // TODO(aa): Setup xinput2 events. + // See desktop_aura/desktop_window_tree_host_x11.cc. + + delegate_->OnAcceleratedWidgetAvailable(window_); + } + + virtual void Show() OVERRIDE { + XDisplay* display = gfx::GetXDisplay(); + XMapWindow(display, window_); + static_cast<ui::X11EventSource*>( + event_source_.get())->BlockUntilWindowMapped(window_); + XFlush(display); + } + + virtual void Hide() OVERRIDE { + XWithdrawWindow(gfx::GetXDisplay(), window_, 0); + } + + virtual void Close() OVERRIDE { + // TODO(beng): perform this in response to XWindow destruction. + delegate_->OnDestroyed(); + } + + virtual gfx::Size GetSize() OVERRIDE { + return bounds_.size(); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void SetCapture() OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void ReleaseCapture() OVERRIDE { + NOTIMPLEMENTED(); + } + + // ui::PlatformEventDispatcher: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE { + // TODO(aa): This is going to have to be thought through more carefully. + // Which events are appropriate to pass to clients? + switch (event->type) { + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: + case MotionNotify: + return true; + case ClientMessage: + return event->xclient.message_type == atom_wm_protocols_; + default: + return false; + } + } + + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE { + if (event->type == ClientMessage) { + Atom protocol = static_cast<Atom>(event->xclient.data.l[0]); + if (protocol == atom_wm_delete_window_) + delegate_->OnDestroyed(); + } else if (event->type == KeyPress || event->type == KeyRelease) { + ui::KeyEvent key_event(event, false); + delegate_->OnEvent(&key_event); + } else if (event->type == ButtonPress || event->type == ButtonRelease || + event->type == MotionNotify) { + ui::MouseEvent mouse_event(event); + delegate_->OnEvent(&mouse_event); + } + return ui::POST_DISPATCH_NONE; + } + + scoped_ptr<ui::PlatformEventSource> event_source_; + NativeViewportDelegate* delegate_; + gfx::Rect bounds_; + XID window_; + Atom atom_wm_protocols_; + Atom atom_wm_delete_window_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewportX11); +}; + +// static +scoped_ptr<NativeViewport> NativeViewport::Create( + shell::Context* context, + NativeViewportDelegate* delegate) { + return scoped_ptr<NativeViewport>(new NativeViewportX11(delegate)).Pass(); +} + +} // namespace services +} // namespace mojo diff --git a/chromium/mojo/services/network/DEPS b/chromium/mojo/services/network/DEPS new file mode 100644 index 00000000000..dc0a052ff7a --- /dev/null +++ b/chromium/mojo/services/network/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+mojo/services", + "+net", +] diff --git a/chromium/mojo/services/network/main.cc b/chromium/mojo/services/network/main.cc new file mode 100644 index 00000000000..1b69638a038 --- /dev/null +++ b/chromium/mojo/services/network/main.cc @@ -0,0 +1,30 @@ +// 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 "base/at_exit.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/application/application.h" +#include "mojo/services/network/network_context.h" +#include "mojo/services/network/network_service_impl.h" + +extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain( + MojoHandle service_provider_handle) { + base::CommandLine::Init(0, NULL); + base::AtExitManager at_exit; + + // The IO message loop allows us to use net::URLRequest on this thread. + base::MessageLoopForIO loop; + + mojo::NetworkContext context; + + mojo::Application app; + app.BindServiceProvider( + mojo::MakeScopedHandle(mojo::MessagePipeHandle(service_provider_handle))); + + app.AddService<mojo::NetworkServiceImpl>(&context); + + loop.Run(); + return MOJO_RESULT_OK; +} diff --git a/chromium/mojo/services/network/network_context.cc b/chromium/mojo/services/network/network_context.cc new file mode 100644 index 00000000000..099432ae9f2 --- /dev/null +++ b/chromium/mojo/services/network/network_context.cc @@ -0,0 +1,123 @@ +// 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/network/network_context.h" + +#include "base/base_paths.h" +#include "base/path_service.h" +#include "net/cert/cert_verifier.h" +#include "net/cookies/cookie_monster.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_session.h" +#include "net/http/http_server_properties_impl.h" +#include "net/http/transport_security_persister.h" +#include "net/http/transport_security_state.h" +#include "net/proxy/proxy_service.h" +#include "net/ssl/default_server_bound_cert_store.h" +#include "net/ssl/server_bound_cert_service.h" +#include "net/ssl/ssl_config_service_defaults.h" +#include "net/url_request/file_protocol_handler.h" +#include "net/url_request/static_http_user_agent_settings.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_storage.h" +#include "net/url_request/url_request_job_factory_impl.h" + +namespace mojo { + +NetworkContext::NetworkContext() + : file_thread_("network_file_thread"), + cache_thread_("network_cache_thread") { + file_thread_.Start(); + + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + cache_thread_.StartWithOptions(options); + + // TODO(darin): Need to figure out a better base path, obviously. + base::FilePath base_path; + PathService::Get(base::DIR_TEMP, &base_path); + base_path = base_path.Append(FILE_PATH_LITERAL("network_service")); + + url_request_context_.reset(new net::URLRequestContext()); + url_request_context_->set_net_log(net_log_.get()); + + storage_.reset( + new net::URLRequestContextStorage(url_request_context_.get())); + + storage_->set_cookie_store(new net::CookieMonster(NULL, NULL)); + + // TODO(darin): This is surely the wrong UA string. + storage_->set_http_user_agent_settings( + new net::StaticHttpUserAgentSettings("en-us,en", "Mojo/0.1")); + + storage_->set_proxy_service(net::ProxyService::CreateDirect()); + storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults); + storage_->set_cert_verifier(net::CertVerifier::CreateDefault()); + + net::TransportSecurityState* transport_security_state = + new net::TransportSecurityState(); + storage_->set_transport_security_state(transport_security_state); + + transport_security_persister_.reset( + new net::TransportSecurityPersister( + transport_security_state, + base_path, + file_thread_.message_loop_proxy(), + false)); + + storage_->set_server_bound_cert_service( + new net::ServerBoundCertService( + new net::DefaultServerBoundCertStore(NULL), + file_thread_.message_loop_proxy())); + storage_->set_http_server_properties( + scoped_ptr<net::HttpServerProperties>( + new net::HttpServerPropertiesImpl())); + storage_->set_host_resolver(net::HostResolver::CreateDefaultResolver( + url_request_context_->net_log())); + + net::HttpNetworkSession::Params network_session_params; + network_session_params.cert_verifier = + url_request_context_->cert_verifier(); + network_session_params.transport_security_state = + url_request_context_->transport_security_state(); + network_session_params.server_bound_cert_service = + url_request_context_->server_bound_cert_service(); + network_session_params.net_log = + url_request_context_->net_log(); + network_session_params.proxy_service = + url_request_context_->proxy_service(); + network_session_params.ssl_config_service = + url_request_context_->ssl_config_service(); + network_session_params.http_server_properties = + url_request_context_->http_server_properties(); + network_session_params.host_resolver = + url_request_context_->host_resolver(); + + base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache")); + + net::HttpCache::DefaultBackend* main_backend = + new net::HttpCache::DefaultBackend( + net::DISK_CACHE, + net::CACHE_BACKEND_DEFAULT, + cache_path, + 0, + cache_thread_.message_loop_proxy()); + + net::HttpCache* main_cache = new net::HttpCache( + network_session_params, main_backend); + storage_->set_http_transaction_factory(main_cache); + + scoped_ptr<net::URLRequestJobFactoryImpl> job_factory( + new net::URLRequestJobFactoryImpl()); + job_factory->SetProtocolHandler( + "file", + new net::FileProtocolHandler(file_thread_.message_loop_proxy())); + storage_->set_job_factory(job_factory.release()); +} + +NetworkContext::~NetworkContext() { + // TODO(darin): Be careful about destruction order of member variables? +} + +} // namespace mojo diff --git a/chromium/mojo/services/network/network_context.h b/chromium/mojo/services/network/network_context.h new file mode 100644 index 00000000000..b156c081213 --- /dev/null +++ b/chromium/mojo/services/network/network_context.h @@ -0,0 +1,42 @@ +// 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_SERVICES_NETWORK_NETWORK_CONTEXT_H_ +#define MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" + +namespace net { +class NetLog; +class URLRequestContext; +class URLRequestContextStorage; +class TransportSecurityPersister; +} + +namespace mojo { + +class NetworkContext { + public: + NetworkContext(); + ~NetworkContext(); + + net::URLRequestContext* url_request_context() { + return url_request_context_.get(); + } + + private: + base::Thread file_thread_; + base::Thread cache_thread_; + scoped_ptr<net::NetLog> net_log_; + scoped_ptr<net::URLRequestContextStorage> storage_; + scoped_ptr<net::URLRequestContext> url_request_context_; + scoped_ptr<net::TransportSecurityPersister> transport_security_persister_; + + DISALLOW_COPY_AND_ASSIGN(NetworkContext); +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_ diff --git a/chromium/mojo/services/network/network_service_impl.cc b/chromium/mojo/services/network/network_service_impl.cc new file mode 100644 index 00000000000..a216738ad46 --- /dev/null +++ b/chromium/mojo/services/network/network_service_impl.cc @@ -0,0 +1,23 @@ +// 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/network/network_service_impl.h" + +#include "mojo/services/network/url_loader_impl.h" + +namespace mojo { + +NetworkServiceImpl::NetworkServiceImpl(NetworkContext* context) + : context_(context) { +} + +NetworkServiceImpl::~NetworkServiceImpl() { +} + +void NetworkServiceImpl::CreateURLLoader( + InterfaceRequest<URLLoader> loader) { + BindToRequest(new URLLoaderImpl(context_), &loader); +} + +} // namespace mojo diff --git a/chromium/mojo/services/network/network_service_impl.h b/chromium/mojo/services/network/network_service_impl.h new file mode 100644 index 00000000000..32d1713cb12 --- /dev/null +++ b/chromium/mojo/services/network/network_service_impl.h @@ -0,0 +1,29 @@ +// 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_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_ +#define MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_ + +#include "base/compiler_specific.h" +#include "mojo/public/cpp/bindings/interface_impl.h" +#include "mojo/services/public/interfaces/network/network_service.mojom.h" + +namespace mojo { +class NetworkContext; + +class NetworkServiceImpl : public InterfaceImpl<NetworkService> { + public: + explicit NetworkServiceImpl(NetworkContext* context); + virtual ~NetworkServiceImpl(); + + // NetworkService methods: + virtual void CreateURLLoader(InterfaceRequest<URLLoader> loader) OVERRIDE; + + private: + NetworkContext* context_; +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_ diff --git a/chromium/mojo/services/network/url_loader_impl.cc b/chromium/mojo/services/network/url_loader_impl.cc new file mode 100644 index 00000000000..9b0cce9d525 --- /dev/null +++ b/chromium/mojo/services/network/url_loader_impl.cc @@ -0,0 +1,253 @@ +// 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/network/url_loader_impl.h" + +#include "mojo/common/common_type_converters.h" +#include "mojo/services/network/network_context.h" +#include "net/base/io_buffer.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_headers.h" + +namespace mojo { +namespace { + +const uint32_t kMaxReadSize = 64 * 1024; + +// Generates an URLResponsePtr from the response state of a net::URLRequest. +URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) { + URLResponsePtr response(URLResponse::New()); + response->url = url_request->url().spec(); + + const net::HttpResponseHeaders* headers = url_request->response_headers(); + if (headers) { + response->status_code = headers->response_code(); + response->status_line = headers->GetStatusLine(); + + std::vector<String> header_lines; + void* iter = NULL; + std::string name, value; + while (headers->EnumerateHeaderLines(&iter, &name, &value)) + header_lines.push_back(name + ": " + value); + if (!header_lines.empty()) + response->headers.Swap(&header_lines); + } + + return response.Pass(); +} + +} // namespace + +// Keeps track of a pending two-phase write on a DataPipeProducerHandle. +class URLLoaderImpl::PendingWriteToDataPipe : + public base::RefCountedThreadSafe<PendingWriteToDataPipe> { + public: + explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle) + : handle_(handle.Pass()), + buffer_(NULL) { + } + + bool BeginWrite(uint32_t* num_bytes) { + MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes, + MOJO_WRITE_DATA_FLAG_NONE); + if (*num_bytes > kMaxReadSize) + *num_bytes = kMaxReadSize; + + return result == MOJO_RESULT_OK; + } + + ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) { + EndWriteDataRaw(handle_.get(), num_bytes); + buffer_ = NULL; + return handle_.Pass(); + } + + char* buffer() { return static_cast<char*>(buffer_); } + + private: + friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>; + + ~PendingWriteToDataPipe() { + if (handle_.is_valid()) + EndWriteDataRaw(handle_.get(), 0); + } + + ScopedDataPipeProducerHandle handle_; + void* buffer_; + + DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe); +}; + +// Takes ownership of a pending two-phase write on a DataPipeProducerHandle, +// and makes its buffer available as a net::IOBuffer. +class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer { + public: + DependentIOBuffer(PendingWriteToDataPipe* pending_write) + : net::WrappedIOBuffer(pending_write->buffer()), + pending_write_(pending_write) { + } + private: + virtual ~DependentIOBuffer() {} + scoped_refptr<PendingWriteToDataPipe> pending_write_; +}; + +URLLoaderImpl::URLLoaderImpl(NetworkContext* context) + : context_(context), + auto_follow_redirects_(true), + weak_ptr_factory_(this) { +} + +URLLoaderImpl::~URLLoaderImpl() { +} + +void URLLoaderImpl::OnConnectionError() { + delete this; +} + +void URLLoaderImpl::Start(URLRequestPtr request, + ScopedDataPipeProducerHandle response_body_stream) { + // Do not allow starting another request. + if (url_request_) { + SendError(net::ERR_UNEXPECTED); + url_request_.reset(); + response_body_stream_.reset(); + return; + } + + if (!request) { + SendError(net::ERR_INVALID_ARGUMENT); + return; + } + + response_body_stream_ = response_body_stream.Pass(); + + GURL url(request->url); + url_request_.reset( + new net::URLRequest(url, + net::DEFAULT_PRIORITY, + this, + context_->url_request_context())); + url_request_->set_method(request->method); + if (request->headers) { + net::HttpRequestHeaders headers; + for (size_t i = 0; i < request->headers.size(); ++i) + headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>()); + url_request_->SetExtraRequestHeaders(headers); + } + if (request->bypass_cache) + url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE); + // TODO(darin): Handle request body. + + auto_follow_redirects_ = request->auto_follow_redirects; + + url_request_->Start(); +} + +void URLLoaderImpl::FollowRedirect() { + if (auto_follow_redirects_) { + DLOG(ERROR) << "Spurious call to FollowRedirect"; + } else { + if (url_request_) + url_request_->FollowDeferredRedirect(); + } +} + +void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request, + const GURL& new_url, + bool* defer_redirect) { + DCHECK(url_request == url_request_.get()); + DCHECK(url_request->status().is_success()); + + URLResponsePtr response = MakeURLResponse(url_request); + std::string redirect_method = + net::URLRequest::ComputeMethodForRedirect(url_request->method(), + response->status_code); + client()->OnReceivedRedirect( + response.Pass(), new_url.spec(), redirect_method); + + *defer_redirect = !auto_follow_redirects_; +} + +void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) { + DCHECK(url_request == url_request_.get()); + + if (!url_request->status().is_success()) { + SendError(url_request->status().error()); + return; + } + + client()->OnReceivedResponse(MakeURLResponse(url_request)); + + // Start reading... + ReadMore(); +} + +void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request, + int bytes_read) { + if (url_request->status().is_success()) { + DidRead(static_cast<uint32_t>(bytes_read), false); + } else { + pending_write_ = NULL; // This closes the data pipe. + // TODO(darin): Perhaps we should communicate this error to our client. + } +} + +void URLLoaderImpl::SendError(int error_code) { + NetworkErrorPtr error(NetworkError::New()); + error->code = error_code; + error->description = net::ErrorToString(error_code); + client()->OnReceivedError(error.Pass()); +} + +void URLLoaderImpl::ReadMore() { + DCHECK(!pending_write_); + + pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass()); + + uint32_t num_bytes; + if (!pending_write_->BeginWrite(&num_bytes)) + CHECK(false); // Oops! TODO(darin): crbug/386877: The pipe might be full! + if (num_bytes > static_cast<uint32_t>(std::numeric_limits<int>::max())) + CHECK(false); // Oops! + + scoped_refptr<net::IOBuffer> buf = new DependentIOBuffer(pending_write_); + + int bytes_read; + url_request_->Read(buf, static_cast<int>(num_bytes), &bytes_read); + + // Drop our reference to the buffer. + buf = NULL; + + if (url_request_->status().is_io_pending()) { + // Wait for OnReadCompleted. + } else if (url_request_->status().is_success() && bytes_read > 0) { + DidRead(static_cast<uint32_t>(bytes_read), true); + } else { + pending_write_->Complete(0); + pending_write_ = NULL; // This closes the data pipe. + if (bytes_read == 0) { + client()->OnReceivedEndOfResponseBody(); + } else { + DCHECK(!url_request_->status().is_success()); + SendError(url_request_->status().error()); + } + } +} + +void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) { + DCHECK(url_request_->status().is_success()); + + response_body_stream_ = pending_write_->Complete(num_bytes); + pending_write_ = NULL; + + if (completed_synchronously) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr())); + } else { + ReadMore(); + } +} + +} // namespace mojo diff --git a/chromium/mojo/services/network/url_loader_impl.h b/chromium/mojo/services/network/url_loader_impl.h new file mode 100644 index 00000000000..a76a9669738 --- /dev/null +++ b/chromium/mojo/services/network/url_loader_impl.h @@ -0,0 +1,61 @@ +// 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_SERVICES_NETWORK_URL_LOADER_IMPL_H_ +#define MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_ + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/interface_impl.h" +#include "mojo/services/public/interfaces/network/url_loader.mojom.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" + +namespace mojo { +class NetworkContext; + +class URLLoaderImpl : public InterfaceImpl<URLLoader>, + public net::URLRequest::Delegate { + public: + explicit URLLoaderImpl(NetworkContext* context); + virtual ~URLLoaderImpl(); + + private: + class PendingWriteToDataPipe; + class DependentIOBuffer; + + // InterfaceImpl<> methods: + virtual void OnConnectionError() OVERRIDE; + + // URLLoader methods: + virtual void Start( + URLRequestPtr request, + ScopedDataPipeProducerHandle response_body_stream) OVERRIDE; + virtual void FollowRedirect() OVERRIDE; + + // net::URLRequest::Delegate methods: + virtual void OnReceivedRedirect(net::URLRequest* url_request, + const GURL& new_url, + bool* defer_redirect) OVERRIDE; + virtual void OnResponseStarted(net::URLRequest* url_request) OVERRIDE; + virtual void OnReadCompleted(net::URLRequest* url_request, int bytes_read) + OVERRIDE; + + void SendError(int error); + void ReadMore(); + void DidRead(uint32_t num_bytes, bool completed_synchronously); + + NetworkContext* context_; + scoped_ptr<net::URLRequest> url_request_; + ScopedDataPipeProducerHandle response_body_stream_; + scoped_refptr<PendingWriteToDataPipe> pending_write_; + bool auto_follow_redirects_; + + base::WeakPtrFactory<URLLoaderImpl> weak_ptr_factory_; +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_ diff --git a/chromium/mojo/services/public/cpp/geometry/DEPS b/chromium/mojo/services/public/cpp/geometry/DEPS new file mode 100644 index 00000000000..194be1c46b7 --- /dev/null +++ b/chromium/mojo/services/public/cpp/geometry/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+ui/gfx/geometry", +] diff --git a/chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h b/chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h new file mode 100644 index 00000000000..3c09f6707be --- /dev/null +++ b/chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h @@ -0,0 +1,37 @@ +// 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_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_ +#define MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_ + +#include "mojo/services/public/cpp/geometry/mojo_geometry_export.h" +#include "mojo/services/public/interfaces/geometry/geometry.mojom.h" +#include "ui/gfx/geometry/rect.h" + +namespace mojo { + +template<> +class MOJO_GEOMETRY_EXPORT TypeConverter<PointPtr, gfx::Point> { + public: + static PointPtr ConvertFrom(const gfx::Point& input); + static gfx::Point ConvertTo(const PointPtr& input); +}; + +template<> +class MOJO_GEOMETRY_EXPORT TypeConverter<SizePtr, gfx::Size> { + public: + static SizePtr ConvertFrom(const gfx::Size& input); + static gfx::Size ConvertTo(const SizePtr& input); +}; + +template<> +class MOJO_GEOMETRY_EXPORT TypeConverter<RectPtr, gfx::Rect> { + public: + static RectPtr ConvertFrom(const gfx::Rect& input); + static gfx::Rect ConvertTo(const RectPtr& input); +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_ diff --git a/chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc b/chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc new file mode 100644 index 00000000000..6d942376ac9 --- /dev/null +++ b/chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc @@ -0,0 +1,58 @@ +// 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/public/cpp/geometry/geometry_type_converters.h" + +namespace mojo { + +// static +PointPtr TypeConverter<PointPtr, gfx::Point>::ConvertFrom( + const gfx::Point& input) { + PointPtr point(Point::New()); + point->x = input.x(); + point->y = input.y(); + return point.Pass(); +} + +// static +gfx::Point TypeConverter<PointPtr, gfx::Point>::ConvertTo( + const PointPtr& input) { + if (!input) + return gfx::Point(); + return gfx::Point(input->x, input->y); +} + +// static +SizePtr TypeConverter<SizePtr, gfx::Size>::ConvertFrom(const gfx::Size& input) { + SizePtr size(Size::New()); + size->width = input.width(); + size->height = input.height(); + return size.Pass(); +} + +// static +gfx::Size TypeConverter<SizePtr, gfx::Size>::ConvertTo(const SizePtr& input) { + if (!input) + return gfx::Size(); + return gfx::Size(input->width, input->height); +} + +// static +RectPtr TypeConverter<RectPtr, gfx::Rect>::ConvertFrom(const gfx::Rect& input) { + RectPtr rect(Rect::New()); + rect->x = input.x(); + rect->y = input.y(); + rect->width = input.width(); + rect->height = input.height(); + return rect.Pass(); +} + +// static +gfx::Rect TypeConverter<RectPtr, gfx::Rect>::ConvertTo(const RectPtr& input) { + if (!input) + return gfx::Rect(); + return gfx::Rect(input->x, input->y, input->width, input->height); +} + +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h b/chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h new file mode 100644 index 00000000000..f21aebce133 --- /dev/null +++ b/chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h @@ -0,0 +1,32 @@ +// 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_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_ +#define MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_ + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(MOJO_GEOMETRY_IMPLEMENTATION) +#define MOJO_GEOMETRY_EXPORT __declspec(dllexport) +#else +#define MOJO_GEOMETRY_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_GEOMETRY_IMPLEMENTATION) +#define MOJO_GEOMETRY_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_GEOMETRY_EXPORT +#endif + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) +#define MOJO_GEOMETRY_EXPORT +#endif + +#endif // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_ diff --git a/chromium/mojo/services/public/cpp/input_events/DEPS b/chromium/mojo/services/public/cpp/input_events/DEPS new file mode 100644 index 00000000000..fe1d98e366d --- /dev/null +++ b/chromium/mojo/services/public/cpp/input_events/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+ui/events", +] diff --git a/chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h b/chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h new file mode 100644 index 00000000000..7882f62ea30 --- /dev/null +++ b/chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h @@ -0,0 +1,30 @@ +// 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_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_ +#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_ + +#include "base/memory/scoped_ptr.h" +#include "mojo/services/public/cpp/input_events/mojo_input_events_export.h" +#include "mojo/services/public/interfaces/input_events/input_events.mojom.h" +#include "ui/events/event.h" + +namespace mojo { + +template<> +class MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, ui::Event> { + public: + static EventPtr ConvertFrom(const ui::Event& input); +}; + +template<> +class MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, + scoped_ptr<ui::Event> > { + public: + static scoped_ptr<ui::Event> ConvertTo(const EventPtr& input); +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_ diff --git a/chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc b/chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc new file mode 100644 index 00000000000..5a47d07e720 --- /dev/null +++ b/chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc @@ -0,0 +1,68 @@ +// 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/public/cpp/input_events/input_events_type_converters.h" + +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace mojo { + +// static +EventPtr TypeConverter<EventPtr, ui::Event>::ConvertFrom( + const ui::Event& input) { + EventPtr event(Event::New()); + event->action = input.type(); + event->flags = input.flags(); + event->time_stamp = input.time_stamp().ToInternalValue(); + + if (input.IsMouseEvent() || input.IsTouchEvent()) { + const ui::LocatedEvent* located_event = + static_cast<const ui::LocatedEvent*>(&input); + event->location = + TypeConverter<PointPtr, gfx::Point>::ConvertFrom( + located_event->location()); + } + + if (input.IsTouchEvent()) { + const ui::TouchEvent* touch_event = + static_cast<const ui::TouchEvent*>(&input); + TouchDataPtr touch_data(TouchData::New()); + touch_data->pointer_id = touch_event->touch_id(); + event->touch_data = touch_data.Pass(); + } else if (input.IsKeyEvent()) { + const ui::KeyEvent* key_event = static_cast<const ui::KeyEvent*>(&input); + KeyDataPtr key_data(KeyData::New()); + key_data->key_code = key_event->key_code(); + key_data->is_char = key_event->is_char(); + event->key_data = key_data.Pass(); + } + return event.Pass(); +} + +// static +scoped_ptr<ui::Event> +TypeConverter<EventPtr, scoped_ptr<ui::Event> >::ConvertTo( + const EventPtr& input) { + scoped_ptr<ui::Event> ui_event; + switch (input->action) { + case ui::ET_KEY_PRESSED: + case ui::ET_KEY_RELEASED: + ui_event.reset(new ui::KeyEvent( + static_cast<ui::EventType>(input->action), + static_cast<ui::KeyboardCode>( + input->key_data->key_code), + input->flags, + input->key_data->is_char)); + break; + default: + // TODO: support other types. + // NOTIMPLEMENTED(); + ; + } + // TODO: need to support time_stamp. + return ui_event.Pass(); +} + +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h b/chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h new file mode 100644 index 00000000000..d7b193e07cd --- /dev/null +++ b/chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h @@ -0,0 +1,32 @@ +// 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_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_ +#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_ + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(MOJO_INPUT_EVENTS_IMPLEMENTATION) +#define MOJO_INPUT_EVENTS_EXPORT __declspec(dllexport) +#else +#define MOJO_INPUT_EVENTS_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_INPUT_EVENTS_IMPLEMENTATION) +#define MOJO_INPUT_EVENTS_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_INPUT_EVENTS_EXPORT +#endif + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) +#define MOJO_INPUT_EVENTS_EXPORT +#endif + +#endif // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/DEPS b/chromium/mojo/services/public/cpp/view_manager/DEPS new file mode 100644 index 00000000000..4460c199e75 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+third_party/skia/include/core", + "+ui/gfx/geometry" +] diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/DEPS b/chromium/mojo/services/public/cpp/view_manager/lib/DEPS new file mode 100644 index 00000000000..7ae7fe7dbae --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/DEPS @@ -0,0 +1,11 @@ +include_rules = [ + "+mojo/geometry", + "+third_party/skia", + "+ui/gfx", +] + +specific_include_rules = { + "view_manager_test_suite.cc": [ + "+ui/gl", + ], +} diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node.cc b/chromium/mojo/services/public/cpp/view_manager/lib/node.cc new file mode 100644 index 00000000000..e3ce2c9b8dd --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/node.cc @@ -0,0 +1,432 @@ +// 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/public/cpp/view_manager/node.h" + +#include "mojo/services/public/cpp/view_manager/lib/node_private.h" +#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" +#include "mojo/services/public/cpp/view_manager/lib/view_private.h" +#include "mojo/services/public/cpp/view_manager/node_observer.h" +#include "mojo/services/public/cpp/view_manager/view.h" + +namespace mojo { +namespace view_manager { + +namespace { + +void NotifyViewTreeChangeAtReceiver( + Node* receiver, + const NodeObserver::TreeChangeParams& params) { + NodeObserver::TreeChangeParams local_params = params; + local_params.receiver = receiver; + FOR_EACH_OBSERVER(NodeObserver, + *NodePrivate(receiver).observers(), + OnTreeChange(local_params)); +} + +void NotifyViewTreeChangeUp( + Node* start_at, + const NodeObserver::TreeChangeParams& params) { + for (Node* current = start_at; current; current = current->parent()) + NotifyViewTreeChangeAtReceiver(current, params); +} + +void NotifyViewTreeChangeDown( + Node* start_at, + const NodeObserver::TreeChangeParams& params) { + NotifyViewTreeChangeAtReceiver(start_at, params); + Node::Children::const_iterator it = start_at->children().begin(); + for (; it != start_at->children().end(); ++it) + NotifyViewTreeChangeDown(*it, params); +} + +void NotifyViewTreeChange( + const NodeObserver::TreeChangeParams& params) { + NotifyViewTreeChangeDown(params.target, params); + if (params.old_parent) + NotifyViewTreeChangeUp(params.old_parent, params); + if (params.new_parent) + NotifyViewTreeChangeUp(params.new_parent, params); +} + +class ScopedTreeNotifier { + public: + ScopedTreeNotifier(Node* target, Node* old_parent, Node* new_parent) { + params_.target = target; + params_.old_parent = old_parent; + params_.new_parent = new_parent; + NotifyViewTreeChange(params_); + } + ~ScopedTreeNotifier() { + params_.phase = NodeObserver::DISPOSITION_CHANGED; + NotifyViewTreeChange(params_); + } + + private: + NodeObserver::TreeChangeParams params_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); +}; + +void RemoveChildImpl(Node* child, Node::Children* children) { + Node::Children::iterator it = + std::find(children->begin(), children->end(), child); + if (it != children->end()) { + children->erase(it); + NodePrivate(child).ClearParent(); + } +} + +class ScopedOrderChangedNotifier { + public: + ScopedOrderChangedNotifier(Node* node, + Node* relative_node, + OrderDirection direction) + : node_(node), + relative_node_(relative_node), + direction_(direction) { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeReordered(node_, + relative_node_, + direction_, + NodeObserver::DISPOSITION_CHANGING)); + + } + ~ScopedOrderChangedNotifier() { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeReordered(node_, + relative_node_, + direction_, + NodeObserver::DISPOSITION_CHANGED)); + } + + private: + Node* node_; + Node* relative_node_; + OrderDirection direction_; + + DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier); +}; + +// Returns true if the order actually changed. +bool ReorderImpl(Node::Children* children, + Node* node, + Node* relative, + OrderDirection direction) { + DCHECK(relative); + DCHECK_NE(node, relative); + DCHECK_EQ(node->parent(), relative->parent()); + + const size_t child_i = + std::find(children->begin(), children->end(), node) - children->begin(); + const size_t target_i = + std::find(children->begin(), children->end(), relative) - + children->begin(); + if ((direction == ORDER_ABOVE && child_i == target_i + 1) || + (direction == ORDER_BELOW && child_i + 1 == target_i)) { + return false; + } + + ScopedOrderChangedNotifier notifier(node, relative, direction); + + const size_t dest_i = + direction == ORDER_ABOVE ? + (child_i < target_i ? target_i : target_i + 1) : + (child_i < target_i ? target_i - 1 : target_i); + children->erase(children->begin() + child_i); + children->insert(children->begin() + dest_i, node); + + return true; +} + +class ScopedSetActiveViewNotifier { + public: + ScopedSetActiveViewNotifier(Node* node, View* old_view, View* new_view) + : node_(node), + old_view_(old_view), + new_view_(new_view) { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node).observers(), + OnNodeActiveViewChange(node_, + old_view_, + new_view_, + NodeObserver::DISPOSITION_CHANGING)); + } + ~ScopedSetActiveViewNotifier() { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeActiveViewChange(node_, + old_view_, + new_view_, + NodeObserver::DISPOSITION_CHANGED)); + } + + private: + Node* node_; + View* old_view_; + View* new_view_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSetActiveViewNotifier); +}; + +class ScopedSetBoundsNotifier { + public: + ScopedSetBoundsNotifier(Node* node, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) + : node_(node), + old_bounds_(old_bounds), + new_bounds_(new_bounds) { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeBoundsChange(node_, + old_bounds_, + new_bounds_, + NodeObserver::DISPOSITION_CHANGING)); + } + ~ScopedSetBoundsNotifier() { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeBoundsChange(node_, + old_bounds_, + new_bounds_, + NodeObserver::DISPOSITION_CHANGED)); + } + + private: + Node* node_; + const gfx::Rect old_bounds_; + const gfx::Rect new_bounds_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); +}; + +class ScopedDestructionNotifier { + public: + explicit ScopedDestructionNotifier(Node* node) + : node_(node) { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeDestroy(node_, NodeObserver::DISPOSITION_CHANGING)); + } + ~ScopedDestructionNotifier() { + FOR_EACH_OBSERVER( + NodeObserver, + *NodePrivate(node_).observers(), + OnNodeDestroy(node_, NodeObserver::DISPOSITION_CHANGED)); + } + + private: + Node* node_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDestructionNotifier); +}; + +// Some operations are only permitted in the connection that created the node. +bool OwnsNode(ViewManager* manager, Node* node) { + return !manager || + static_cast<ViewManagerClientImpl*>(manager)->OwnsNode(node->id()); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// Node, public: + +// static +Node* Node::Create(ViewManager* view_manager) { + Node* node = new Node(view_manager); + static_cast<ViewManagerClientImpl*>(view_manager)->AddNode(node); + return node; +} + +void Node::Destroy() { + if (!OwnsNode(manager_, this)) + return; + + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->DestroyNode(id_); + while (!children_.empty()) + children_.front()->Destroy(); + LocalDestroy(); +} + +void Node::SetBounds(const gfx::Rect& bounds) { + if (!OwnsNode(manager_, this)) + return; + + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->SetBounds(id_, bounds); + LocalSetBounds(bounds_, bounds); +} + +void Node::AddObserver(NodeObserver* observer) { + observers_.AddObserver(observer); +} + +void Node::RemoveObserver(NodeObserver* observer) { + observers_.RemoveObserver(observer); +} + +void Node::AddChild(Node* child) { + // TODO(beng): not necessarily valid to all connections, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (manager_) + CHECK_EQ(NodePrivate(child).view_manager(), manager_); + LocalAddChild(child); + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->AddChild(child->id(), id_); +} + +void Node::RemoveChild(Node* child) { + // TODO(beng): not necessarily valid to all connections, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (manager_) + CHECK_EQ(NodePrivate(child).view_manager(), manager_); + LocalRemoveChild(child); + if (manager_) { + static_cast<ViewManagerClientImpl*>(manager_)->RemoveChild(child->id(), + id_); + } +} + +void Node::MoveToFront() { + Reorder(parent_->children_.back(), ORDER_ABOVE); +} + +void Node::MoveToBack() { + Reorder(parent_->children_.front(), ORDER_BELOW); +} + +void Node::Reorder(Node* relative, OrderDirection direction) { + if (!LocalReorder(relative, direction)) + return; + if (manager_) { + static_cast<ViewManagerClientImpl*>(manager_)->Reorder(id_, + relative->id(), + direction); + } +} + +bool Node::Contains(Node* child) const { + if (manager_) + CHECK_EQ(NodePrivate(child).view_manager(), manager_); + for (Node* p = child->parent(); p; p = p->parent()) { + if (p == this) + return true; + } + return false; +} + +Node* Node::GetChildById(Id id) { + if (id == id_) + return this; + // TODO(beng): this could be improved depending on how we decide to own nodes. + Children::const_iterator it = children_.begin(); + for (; it != children_.end(); ++it) { + Node* node = (*it)->GetChildById(id); + if (node) + return node; + } + return NULL; +} + +void Node::SetActiveView(View* view) { + // TODO(beng): not necessarily valid to all connections, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (manager_) + CHECK_EQ(ViewPrivate(view).view_manager(), manager_); + LocalSetActiveView(view); + if (manager_) { + static_cast<ViewManagerClientImpl*>(manager_)->SetActiveView( + id_, active_view_->id()); + } +} + +void Node::SetFocus() { + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_); +} + +void Node::Embed(const String& url) { + static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_); +} + +//////////////////////////////////////////////////////////////////////////////// +// Node, protected: + +Node::Node() + : manager_(NULL), + id_(-1), + parent_(NULL), + active_view_(NULL) {} + +Node::~Node() { + ScopedDestructionNotifier notifier(this); + if (parent_) + parent_->LocalRemoveChild(this); + // TODO(beng): It'd be better to do this via a destruction observer in the + // ViewManagerClientImpl. + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->RemoveNode(id_); +} + +//////////////////////////////////////////////////////////////////////////////// +// Node, private: + +Node::Node(ViewManager* manager) + : manager_(manager), + id_(static_cast<ViewManagerClientImpl*>(manager_)->CreateNode()), + parent_(NULL), + active_view_(NULL) {} + +void Node::LocalDestroy() { + delete this; +} + +void Node::LocalAddChild(Node* child) { + ScopedTreeNotifier notifier(child, child->parent(), this); + if (child->parent()) + RemoveChildImpl(child, &child->parent_->children_); + children_.push_back(child); + child->parent_ = this; +} + +void Node::LocalRemoveChild(Node* child) { + DCHECK_EQ(this, child->parent()); + ScopedTreeNotifier notifier(child, this, NULL); + RemoveChildImpl(child, &children_); +} + +bool Node::LocalReorder(Node* relative, OrderDirection direction) { + return ReorderImpl(&parent_->children_, this, relative, direction); +} + +void Node::LocalSetActiveView(View* view) { + ScopedSetActiveViewNotifier notifier(this, active_view_, view); + if (active_view_) + ViewPrivate(active_view_).set_node(NULL); + active_view_ = view; + if (active_view_) + ViewPrivate(active_view_).set_node(this); +} + +void Node::LocalSetBounds(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + DCHECK(old_bounds == bounds_); + ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds); + bounds_ = new_bounds; +} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc b/chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc new file mode 100644 index 00000000000..a0f9f4e67c2 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc @@ -0,0 +1,23 @@ +// 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/public/cpp/view_manager/node_observer.h" + +#include "base/basictypes.h" + +namespace mojo { +namespace view_manager { + +//////////////////////////////////////////////////////////////////////////////// +// NodeObserver, public: + +NodeObserver::TreeChangeParams::TreeChangeParams() + : target(NULL), + old_parent(NULL), + new_parent(NULL), + receiver(NULL), + phase(NodeObserver::DISPOSITION_CHANGING) {} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc new file mode 100644 index 00000000000..92dfb537fee --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc @@ -0,0 +1,23 @@ +// 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/public/cpp/view_manager/lib/node_private.h" + +namespace mojo { +namespace view_manager { + +NodePrivate::NodePrivate(Node* node) + : node_(node) { +} + +NodePrivate::~NodePrivate() { +} + +// static +Node* NodePrivate::LocalCreate() { + return new Node; +} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node_private.h b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.h new file mode 100644 index 00000000000..6f634b3a95a --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.h @@ -0,0 +1,62 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_ + +#include "base/basictypes.h" + +#include "mojo/services/public/cpp/view_manager/node.h" + +namespace mojo { +namespace view_manager { + +class NodePrivate { + public: + explicit NodePrivate(Node* node); + ~NodePrivate(); + + static Node* LocalCreate(); + + ObserverList<NodeObserver>* observers() { return &node_->observers_; } + + void ClearParent() { node_->parent_ = NULL; } + + void set_id(Id id) { node_->id_ = id; } + + ViewManager* view_manager() { return node_->manager_; } + void set_view_manager(ViewManager* manager) { + node_->manager_ = manager; + } + + void LocalDestroy() { + node_->LocalDestroy(); + } + void LocalAddChild(Node* child) { + node_->LocalAddChild(child); + } + void LocalRemoveChild(Node* child) { + node_->LocalRemoveChild(child); + } + void LocalReorder(Node* relative, OrderDirection direction) { + node_->LocalReorder(relative, direction); + } + void LocalSetActiveView(View* view) { + node_->LocalSetActiveView(view); + } + void LocalSetBounds(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + node_->LocalSetBounds(old_bounds, new_bounds); + } + + private: + Node* node_; + + DISALLOW_COPY_AND_ASSIGN(NodePrivate); +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view.cc new file mode 100644 index 00000000000..4bcedfce080 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view.cc @@ -0,0 +1,97 @@ +// 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/public/cpp/view_manager/view.h" + +#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" +#include "mojo/services/public/cpp/view_manager/lib/view_private.h" +#include "mojo/services/public/cpp/view_manager/node.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" +#include "ui/gfx/canvas.h" + +namespace mojo { +namespace view_manager { + +namespace { +class ScopedDestructionNotifier { + public: + explicit ScopedDestructionNotifier(View* view) + : view_(view) { + FOR_EACH_OBSERVER( + ViewObserver, + *ViewPrivate(view_).observers(), + OnViewDestroy(view_, ViewObserver::DISPOSITION_CHANGING)); + } + ~ScopedDestructionNotifier() { + FOR_EACH_OBSERVER( + ViewObserver, + *ViewPrivate(view_).observers(), + OnViewDestroy(view_, ViewObserver::DISPOSITION_CHANGED)); + } + + private: + View* view_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDestructionNotifier); +}; +} // namespace + +// static +View* View::Create(ViewManager* manager) { + View* view = new View(manager); + static_cast<ViewManagerClientImpl*>(manager)->AddView(view); + return view; +} + +void View::Destroy() { + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->DestroyView(id_); + LocalDestroy(); +} + +void View::AddObserver(ViewObserver* observer) { + observers_.AddObserver(observer); +} + +void View::RemoveObserver(ViewObserver* observer) { + observers_.RemoveObserver(observer); +} + +void View::SetContents(const SkBitmap& contents) { + if (manager_) { + static_cast<ViewManagerClientImpl*>(manager_)->SetViewContents(id_, + contents); + } +} + +void View::SetColor(SkColor color) { + gfx::Canvas canvas(node_->bounds().size(), 1.0f, true); + canvas.DrawColor(color); + SetContents(skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(true)); +} + +View::View(ViewManager* manager) + : id_(static_cast<ViewManagerClientImpl*>(manager)->CreateView()), + node_(NULL), + manager_(manager) {} + +View::View() + : id_(-1), + node_(NULL), + manager_(NULL) {} + +View::~View() { + ScopedDestructionNotifier notifier(this); + // TODO(beng): It'd be better to do this via a destruction observer in the + // ViewManagerClientImpl. + if (manager_) + static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_); +} + +void View::LocalDestroy() { + delete this; +} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc new file mode 100644 index 00000000000..cb7b07136af --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc @@ -0,0 +1,841 @@ +// 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/public/cpp/view_manager/lib/view_manager_client_impl.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/application/application.h" +#include "mojo/public/cpp/application/connect.h" +#include "mojo/public/interfaces/service_provider/service_provider.mojom.h" +#include "mojo/services/public/cpp/view_manager/lib/node_private.h" +#include "mojo/services/public/cpp/view_manager/lib/view_private.h" +#include "mojo/services/public/cpp/view_manager/node_observer.h" +#include "mojo/services/public/cpp/view_manager/util.h" +#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" + +namespace mojo { +namespace view_manager { + +Id MakeTransportId(ConnectionSpecificId connection_id, + ConnectionSpecificId local_id) { + return (connection_id << 16) | local_id; +} + +// Helper called to construct a local node/view object from transport data. +Node* AddNodeToViewManager(ViewManagerClientImpl* client, + Node* parent, + Id node_id, + Id view_id, + const gfx::Rect& bounds) { + // We don't use the ctor that takes a ViewManager here, since it will call + // back to the service and attempt to create a new node. + Node* node = NodePrivate::LocalCreate(); + NodePrivate private_node(node); + private_node.set_view_manager(client); + private_node.set_id(node_id); + private_node.LocalSetBounds(gfx::Rect(), bounds); + if (parent) + NodePrivate(parent).LocalAddChild(node); + client->AddNode(node); + + // View. + if (view_id != 0) { + View* view = ViewPrivate::LocalCreate(); + ViewPrivate private_view(view); + private_view.set_view_manager(client); + private_view.set_id(view_id); + private_view.set_node(node); + // TODO(beng): this broadcasts notifications locally... do we want this? I + // don't think so. same story for LocalAddChild above! + private_node.LocalSetActiveView(view); + client->AddView(view); + } + return node; +} + +Node* BuildNodeTree(ViewManagerClientImpl* client, + const Array<NodeDataPtr>& nodes) { + std::vector<Node*> parents; + Node* root = NULL; + Node* last_node = NULL; + for (size_t i = 0; i < nodes.size(); ++i) { + if (last_node && nodes[i]->parent_id == last_node->id()) { + parents.push_back(last_node); + } else if (!parents.empty()) { + while (parents.back()->id() != nodes[i]->parent_id) + parents.pop_back(); + } + Node* node = AddNodeToViewManager( + client, + !parents.empty() ? parents.back() : NULL, + nodes[i]->node_id, + nodes[i]->view_id, + nodes[i]->bounds.To<gfx::Rect>()); + if (!last_node) + root = node; + last_node = node; + } + return root; +} + +// Responsible for removing a root from the ViewManager when that node is +// destroyed. +class RootObserver : public NodeObserver { + public: + explicit RootObserver(Node* root) : root_(root) {} + virtual ~RootObserver() {} + + private: + // Overridden from NodeObserver: + virtual void OnNodeDestroy(Node* node, + DispositionChangePhase phase) OVERRIDE { + DCHECK_EQ(node, root_); + if (phase != NodeObserver::DISPOSITION_CHANGED) + return; + static_cast<ViewManagerClientImpl*>( + NodePrivate(root_).view_manager())->RemoveRoot(root_); + delete this; + } + + Node* root_; + + DISALLOW_COPY_AND_ASSIGN(RootObserver); +}; + +class ViewManagerTransaction { + public: + virtual ~ViewManagerTransaction() {} + + void Commit() { + DCHECK(!committed_); + DoCommit(); + committed_ = true; + } + + bool committed() const { return committed_; } + + protected: + explicit ViewManagerTransaction(ViewManagerClientImpl* client) + : committed_(false), + client_(client) { + } + + // Overridden to perform transaction-specific commit actions. + virtual void DoCommit() = 0; + + // Overridden to perform transaction-specific cleanup on commit ack from the + // service. + virtual void DoActionCompleted(bool success) = 0; + + ViewManagerService* service() { return client_->service_; } + + Id GetAndAdvanceNextServerChangeId() { + return client_->next_server_change_id_++; + } + + base::Callback<void(bool)> ActionCompletedCallback() { + return base::Bind(&ViewManagerTransaction::OnActionCompleted, + base::Unretained(this)); + } + + private: + // General callback to be used for commits to the service. + void OnActionCompleted(bool success) { + DCHECK(success); + DoActionCompleted(success); + client_->RemoveFromPendingQueue(this); + } + + bool committed_; + ViewManagerClientImpl* client_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerTransaction); +}; + +class CreateViewTransaction : public ViewManagerTransaction { + public: + CreateViewTransaction(Id view_id, ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + view_id_(view_id) {} + virtual ~CreateViewTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->CreateView(view_id_, ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): failure. + } + + const Id view_id_; + + DISALLOW_COPY_AND_ASSIGN(CreateViewTransaction); +}; + +class DestroyViewTransaction : public ViewManagerTransaction { + public: + DestroyViewTransaction(Id view_id, ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + view_id_(view_id) {} + virtual ~DestroyViewTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->DeleteView(view_id_, ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id view_id_; + + DISALLOW_COPY_AND_ASSIGN(DestroyViewTransaction); +}; + +class CreateNodeTransaction : public ViewManagerTransaction { + public: + CreateNodeTransaction(Id node_id, ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + node_id_(node_id) {} + virtual ~CreateNodeTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->CreateNode(node_id_, ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): Failure means we tried to create with an extant id for this + // connection. It also could mean we tried to do something + // invalid, or we tried applying a change out of order. Figure + // out what to do. + } + + const Id node_id_; + + DISALLOW_COPY_AND_ASSIGN(CreateNodeTransaction); +}; + +class DestroyNodeTransaction : public ViewManagerTransaction { + public: + DestroyNodeTransaction(Id node_id, ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + node_id_(node_id) {} + virtual ~DestroyNodeTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->DeleteNode(node_id_, + GetAndAdvanceNextServerChangeId(), + ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id node_id_; + DISALLOW_COPY_AND_ASSIGN(DestroyNodeTransaction); +}; + +class AddChildTransaction : public ViewManagerTransaction { + public: + AddChildTransaction(Id child_id, + Id parent_id, + ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + child_id_(child_id), + parent_id_(parent_id) {} + virtual ~AddChildTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->AddNode(parent_id_, + child_id_, + GetAndAdvanceNextServerChangeId(), + ActionCompletedCallback()); + } + + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id child_id_; + const Id parent_id_; + + DISALLOW_COPY_AND_ASSIGN(AddChildTransaction); +}; + +class RemoveChildTransaction : public ViewManagerTransaction { + public: + RemoveChildTransaction(Id child_id, ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + child_id_(child_id) {} + virtual ~RemoveChildTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->RemoveNodeFromParent( + child_id_, + GetAndAdvanceNextServerChangeId(), + ActionCompletedCallback()); + } + + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id child_id_; + + DISALLOW_COPY_AND_ASSIGN(RemoveChildTransaction); +}; + +class ReorderNodeTransaction : public ViewManagerTransaction { + public: + ReorderNodeTransaction(Id node_id, + Id relative_id, + OrderDirection direction, + ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + node_id_(node_id), + relative_id_(relative_id), + direction_(direction) {} + virtual ~ReorderNodeTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->ReorderNode(node_id_, + relative_id_, + direction_, + GetAndAdvanceNextServerChangeId(), + ActionCompletedCallback()); + } + + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id node_id_; + const Id relative_id_; + const OrderDirection direction_; + + DISALLOW_COPY_AND_ASSIGN(ReorderNodeTransaction); +}; + +class SetActiveViewTransaction : public ViewManagerTransaction { + public: + SetActiveViewTransaction(Id node_id, + Id view_id, + ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + node_id_(node_id), + view_id_(view_id) {} + virtual ~SetActiveViewTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->SetView(node_id_, view_id_, ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id node_id_; + const Id view_id_; + + DISALLOW_COPY_AND_ASSIGN(SetActiveViewTransaction); +}; + +class SetBoundsTransaction : public ViewManagerTransaction { + public: + SetBoundsTransaction(Id node_id, + const gfx::Rect& bounds, + ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + node_id_(node_id), + bounds_(bounds) {} + virtual ~SetBoundsTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->SetNodeBounds( + node_id_, Rect::From(bounds_), ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id node_id_; + const gfx::Rect bounds_; + + DISALLOW_COPY_AND_ASSIGN(SetBoundsTransaction); +}; + +class SetViewContentsTransaction : public ViewManagerTransaction { + public: + SetViewContentsTransaction(Id view_id, + const SkBitmap& contents, + ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + view_id_(view_id), + contents_(contents) {} + virtual ~SetViewContentsTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + std::vector<unsigned char> data; + gfx::PNGCodec::EncodeBGRASkBitmap(contents_, false, &data); + + void* memory = NULL; + ScopedSharedBufferHandle duped; + bool result = CreateMapAndDupSharedBuffer(data.size(), + &memory, + &shared_state_handle_, + &duped); + if (!result) + return; + + memcpy(memory, &data[0], data.size()); + + service()->SetViewContents(view_id_, duped.Pass(), + static_cast<uint32_t>(data.size()), + ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + bool CreateMapAndDupSharedBuffer(size_t size, + void** memory, + ScopedSharedBufferHandle* handle, + ScopedSharedBufferHandle* duped) { + MojoResult result = CreateSharedBuffer(NULL, size, handle); + if (result != MOJO_RESULT_OK) + return false; + DCHECK(handle->is_valid()); + + result = DuplicateBuffer(handle->get(), NULL, duped); + if (result != MOJO_RESULT_OK) + return false; + DCHECK(duped->is_valid()); + + result = MapBuffer( + handle->get(), 0, size, memory, MOJO_MAP_BUFFER_FLAG_NONE); + if (result != MOJO_RESULT_OK) + return false; + DCHECK(*memory); + + return true; + } + + const Id view_id_; + const SkBitmap contents_; + ScopedSharedBufferHandle shared_state_handle_; + + DISALLOW_COPY_AND_ASSIGN(SetViewContentsTransaction); +}; + +class EmbedTransaction : public ViewManagerTransaction { + public: + EmbedTransaction(const String& url, + Id node_id, + ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + url_(url), + node_id_(node_id) {} + virtual ~EmbedTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + std::vector<Id> ids; + ids.push_back(node_id_); + service()->Embed(url_, Array<Id>::From(ids), ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const String url_; + const Id node_id_; + + DISALLOW_COPY_AND_ASSIGN(EmbedTransaction); +}; + +class SetFocusTransaction : public ViewManagerTransaction { + public: + SetFocusTransaction(Id node_id, ViewManagerClientImpl* client) + : ViewManagerTransaction(client), + node_id_(node_id) {} + virtual ~SetFocusTransaction() {} + + private: + // Overridden from ViewManagerTransaction: + virtual void DoCommit() OVERRIDE { + service()->SetFocus(node_id_, ActionCompletedCallback()); + } + virtual void DoActionCompleted(bool success) OVERRIDE { + // TODO(beng): recovery? + } + + const Id node_id_; + + DISALLOW_COPY_AND_ASSIGN(SetFocusTransaction); +}; + +ViewManagerClientImpl::ViewManagerClientImpl(ViewManagerDelegate* delegate) + : connected_(false), + connection_id_(0), + next_id_(1), + next_server_change_id_(0), + delegate_(delegate) {} + +ViewManagerClientImpl::~ViewManagerClientImpl() { + while (!nodes_.empty()) { + IdToNodeMap::iterator it = nodes_.begin(); + if (OwnsNode(it->second->id())) + it->second->Destroy(); + else + nodes_.erase(it); + } + while (!views_.empty()) { + IdToViewMap::iterator it = views_.begin(); + if (OwnsView(it->second->id())) + it->second->Destroy(); + else + views_.erase(it); + } +} + +Id ViewManagerClientImpl::CreateNode() { + DCHECK(connected_); + const Id node_id(MakeTransportId(connection_id_, ++next_id_)); + pending_transactions_.push_back(new CreateNodeTransaction(node_id, this)); + Sync(); + return node_id; +} + +void ViewManagerClientImpl::DestroyNode(Id node_id) { + DCHECK(connected_); + pending_transactions_.push_back(new DestroyNodeTransaction(node_id, this)); + Sync(); +} + +Id ViewManagerClientImpl::CreateView() { + DCHECK(connected_); + const Id view_id(MakeTransportId(connection_id_, ++next_id_)); + pending_transactions_.push_back(new CreateViewTransaction(view_id, this)); + Sync(); + return view_id; +} + +void ViewManagerClientImpl::DestroyView(Id view_id) { + DCHECK(connected_); + pending_transactions_.push_back(new DestroyViewTransaction(view_id, this)); + Sync(); +} + +void ViewManagerClientImpl::AddChild(Id child_id, + Id parent_id) { + DCHECK(connected_); + pending_transactions_.push_back( + new AddChildTransaction(child_id, parent_id, this)); + Sync(); +} + +void ViewManagerClientImpl::RemoveChild(Id child_id, Id parent_id) { + DCHECK(connected_); + pending_transactions_.push_back(new RemoveChildTransaction(child_id, this)); + Sync(); +} + +void ViewManagerClientImpl::Reorder( + Id node_id, + Id relative_node_id, + OrderDirection direction) { + DCHECK(connected_); + pending_transactions_.push_back( + new ReorderNodeTransaction(node_id, relative_node_id, direction, this)); + Sync(); +} + +bool ViewManagerClientImpl::OwnsNode(Id id) const { + return HiWord(id) == connection_id_; +} + +bool ViewManagerClientImpl::OwnsView(Id id) const { + return HiWord(id) == connection_id_; +} + +void ViewManagerClientImpl::SetActiveView(Id node_id, Id view_id) { + DCHECK(connected_); + pending_transactions_.push_back( + new SetActiveViewTransaction(node_id, view_id, this)); + Sync(); +} + +void ViewManagerClientImpl::SetBounds(Id node_id, const gfx::Rect& bounds) { + DCHECK(connected_); + pending_transactions_.push_back( + new SetBoundsTransaction(node_id, bounds, this)); + Sync(); +} + +void ViewManagerClientImpl::SetViewContents(Id view_id, + const SkBitmap& contents) { + DCHECK(connected_); + pending_transactions_.push_back( + new SetViewContentsTransaction(view_id, contents, this)); + Sync(); +} + +void ViewManagerClientImpl::SetFocus(Id node_id) { + DCHECK(connected_); + pending_transactions_.push_back(new SetFocusTransaction(node_id, this)); + Sync(); +} + +void ViewManagerClientImpl::Embed(const String& url, Id node_id) { + DCHECK(connected_); + pending_transactions_.push_back(new EmbedTransaction(url, node_id, this)); + Sync(); +} + +void ViewManagerClientImpl::AddNode(Node* node) { + DCHECK(nodes_.find(node->id()) == nodes_.end()); + nodes_[node->id()] = node; +} + +void ViewManagerClientImpl::RemoveNode(Id node_id) { + IdToNodeMap::iterator it = nodes_.find(node_id); + if (it != nodes_.end()) + nodes_.erase(it); +} + +void ViewManagerClientImpl::AddView(View* view) { + DCHECK(views_.find(view->id()) == views_.end()); + views_[view->id()] = view; +} + +void ViewManagerClientImpl::RemoveView(Id view_id) { + IdToViewMap::iterator it = views_.find(view_id); + if (it != views_.end()) + views_.erase(it); +} + +//////////////////////////////////////////////////////////////////////////////// +// ViewManagerClientImpl, ViewManager implementation: + +const std::string& ViewManagerClientImpl::GetEmbedderURL() const { + return creator_url_; +} + +const std::vector<Node*>& ViewManagerClientImpl::GetRoots() const { + return roots_; +} + +Node* ViewManagerClientImpl::GetNodeById(Id id) { + IdToNodeMap::const_iterator it = nodes_.find(id); + return it != nodes_.end() ? it->second : NULL; +} + +View* ViewManagerClientImpl::GetViewById(Id id) { + IdToViewMap::const_iterator it = views_.find(id); + return it != views_.end() ? it->second : NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// ViewManagerClientImpl, InterfaceImpl overrides: + +void ViewManagerClientImpl::OnConnectionEstablished() { + service_ = client(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ViewManagerClientImpl, ViewManagerClient implementation: + +void ViewManagerClientImpl::OnViewManagerConnectionEstablished( + ConnectionSpecificId connection_id, + const String& creator_url, + Id next_server_change_id, + Array<NodeDataPtr> nodes) { + connected_ = true; + connection_id_ = connection_id; + creator_url_ = TypeConverter<String, std::string>::ConvertFrom(creator_url); + next_server_change_id_ = next_server_change_id; + + DCHECK(pending_transactions_.empty()); + AddRoot(BuildNodeTree(this, nodes)); +} + +void ViewManagerClientImpl::OnRootsAdded(Array<NodeDataPtr> nodes) { + AddRoot(BuildNodeTree(this, nodes)); +} + +void ViewManagerClientImpl::OnServerChangeIdAdvanced( + Id next_server_change_id) { + next_server_change_id_ = next_server_change_id; +} + +void ViewManagerClientImpl::OnNodeBoundsChanged(Id node_id, + RectPtr old_bounds, + RectPtr new_bounds) { + Node* node = GetNodeById(node_id); + NodePrivate(node).LocalSetBounds(old_bounds.To<gfx::Rect>(), + new_bounds.To<gfx::Rect>()); +} + +void ViewManagerClientImpl::OnNodeHierarchyChanged( + Id node_id, + Id new_parent_id, + Id old_parent_id, + Id server_change_id, + mojo::Array<NodeDataPtr> nodes) { + next_server_change_id_ = server_change_id + 1; + + BuildNodeTree(this, nodes); + + Node* new_parent = GetNodeById(new_parent_id); + Node* old_parent = GetNodeById(old_parent_id); + Node* node = GetNodeById(node_id); + if (new_parent) + NodePrivate(new_parent).LocalAddChild(node); + else + NodePrivate(old_parent).LocalRemoveChild(node); +} + +void ViewManagerClientImpl::OnNodeReordered(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id) { + next_server_change_id_ = server_change_id + 1; + + Node* node = GetNodeById(node_id); + Node* relative_node = GetNodeById(relative_node_id); + if (node && relative_node) { + NodePrivate(node).LocalReorder(relative_node, direction); + } +} + +void ViewManagerClientImpl::OnNodeDeleted(Id node_id, Id server_change_id) { + next_server_change_id_ = server_change_id + 1; + + Node* node = GetNodeById(node_id); + if (node) + NodePrivate(node).LocalDestroy(); +} + +void ViewManagerClientImpl::OnNodeViewReplaced(Id node_id, + Id new_view_id, + Id old_view_id) { + Node* node = GetNodeById(node_id); + View* new_view = GetViewById(new_view_id); + if (!new_view && new_view_id != 0) { + // This client wasn't aware of this View until now. + new_view = ViewPrivate::LocalCreate(); + ViewPrivate private_view(new_view); + private_view.set_view_manager(this); + private_view.set_id(new_view_id); + private_view.set_node(node); + AddView(new_view); + } + View* old_view = GetViewById(old_view_id); + DCHECK_EQ(old_view, node->active_view()); + NodePrivate(node).LocalSetActiveView(new_view); +} + +void ViewManagerClientImpl::OnViewDeleted(Id view_id) { + View* view = GetViewById(view_id); + if (view) + ViewPrivate(view).LocalDestroy(); +} + +void ViewManagerClientImpl::OnViewInputEvent( + Id view_id, + EventPtr event, + const Callback<void()>& ack_callback) { + View* view = GetViewById(view_id); + if (view) { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(view).observers(), + OnViewInputEvent(view, event)); + } + ack_callback.Run(); +} + +void ViewManagerClientImpl::DispatchOnViewInputEvent(Id view_id, + EventPtr event) { + // For now blindly bounce the message back to the server. Doing this means the + // event is sent to the correct target (|view_id|). + // Note: This function is only invoked on the window manager. + service_->DispatchOnViewInputEvent(view_id, event.Pass()); +} + +//////////////////////////////////////////////////////////////////////////////// +// ViewManagerClientImpl, private: + +void ViewManagerClientImpl::Sync() { + // The service connection may not be set up yet. OnConnectionEstablished() + // will schedule another sync when it is. + if (!connected_) + return; + + Transactions::const_iterator it = pending_transactions_.begin(); + for (; it != pending_transactions_.end(); ++it) { + if (!(*it)->committed()) + (*it)->Commit(); + } +} + +void ViewManagerClientImpl::RemoveFromPendingQueue( + ViewManagerTransaction* transaction) { + DCHECK_EQ(transaction, pending_transactions_.front()); + pending_transactions_.erase(pending_transactions_.begin()); + if (pending_transactions_.empty() && !changes_acked_callback_.is_null()) + changes_acked_callback_.Run(); +} + +void ViewManagerClientImpl::AddRoot(Node* root) { + // A new root must not already exist as a root or be contained by an existing + // hierarchy visible to this view manager. + std::vector<Node*>::const_iterator it = roots_.begin(); + for (; it != roots_.end(); ++it) { + if (*it == root || (*it)->Contains(root)) + return; + } + roots_.push_back(root); + root->AddObserver(new RootObserver(root)); + delegate_->OnRootAdded(this, root); +} + +void ViewManagerClientImpl::RemoveRoot(Node* root) { + std::vector<Node*>::iterator it = + std::find(roots_.begin(), roots_.end(), root); + if (it != roots_.end()) + roots_.erase(it); +} + +//////////////////////////////////////////////////////////////////////////////// +// ViewManager, public: + +// static +void ViewManager::Create(Application* application, + ViewManagerDelegate* delegate) { + application->AddService<ViewManagerClientImpl>(delegate); +} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h new file mode 100644 index 00000000000..01cdc34110d --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h @@ -0,0 +1,162 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/public/cpp/view_manager/node.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/cpp/view_manager/view_manager.h" +#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" + +class SkBitmap; + +namespace mojo { +namespace view_manager { + +class ViewManager; +class ViewManagerTransaction; + +// Manages the connection with the View Manager service. +class ViewManagerClientImpl : public ViewManager, + public InterfaceImpl<ViewManagerClient> { + public: + explicit ViewManagerClientImpl(ViewManagerDelegate* delegate); + virtual ~ViewManagerClientImpl(); + + bool connected() const { return connected_; } + ConnectionSpecificId connection_id() const { return connection_id_; } + + // API exposed to the node/view implementations that pushes local changes to + // the service. + Id CreateNode(); + void DestroyNode(Id node_id); + + Id CreateView(); + void DestroyView(Id view_id); + + // These methods take TransportIds. For views owned by the current connection, + // the connection id high word can be zero. In all cases, the TransportId 0x1 + // refers to the root node. + void AddChild(Id child_id, Id parent_id); + void RemoveChild(Id child_id, Id parent_id); + + void Reorder(Id node_id, Id relative_node_id, OrderDirection direction); + + // Returns true if the specified node/view was created by this connection. + bool OwnsNode(Id id) const; + bool OwnsView(Id id) const; + + void SetActiveView(Id node_id, Id view_id); + void SetBounds(Id node_id, const gfx::Rect& bounds); + void SetViewContents(Id view_id, const SkBitmap& contents); + void SetFocus(Id node_id); + + void Embed(const String& url, Id node_id); + + void set_changes_acked_callback(const base::Callback<void(void)>& callback) { + changes_acked_callback_ = callback; + } + void ClearChangesAckedCallback() { + changes_acked_callback_ = base::Callback<void(void)>(); + } + + // Start/stop tracking nodes & views. While tracked, they can be retrieved via + // ViewManager::GetNode/ViewById. + void AddNode(Node* node); + void RemoveNode(Id node_id); + + void AddView(View* view); + void RemoveView(Id view_id); + + private: + friend class RootObserver; + friend class ViewManagerTransaction; + + typedef ScopedVector<ViewManagerTransaction> Transactions; + typedef std::map<Id, Node*> IdToNodeMap; + typedef std::map<Id, View*> IdToViewMap; + + // Overridden from ViewManager: + virtual const std::string& GetEmbedderURL() const OVERRIDE; + virtual const std::vector<Node*>& GetRoots() const OVERRIDE; + virtual Node* GetNodeById(Id id) OVERRIDE; + virtual View* GetViewById(Id id) OVERRIDE; + + // Overridden from InterfaceImpl: + virtual void OnConnectionEstablished() OVERRIDE; + + // Overridden from ViewManagerClient: + virtual void OnViewManagerConnectionEstablished( + ConnectionSpecificId connection_id, + const String& creator_url, + Id next_server_change_id, + Array<NodeDataPtr> nodes) OVERRIDE; + virtual void OnRootsAdded(Array<NodeDataPtr> nodes) OVERRIDE; + virtual void OnServerChangeIdAdvanced(Id next_server_change_id) OVERRIDE; + virtual void OnNodeBoundsChanged(Id node_id, + RectPtr old_bounds, + RectPtr new_bounds) OVERRIDE; + virtual void OnNodeHierarchyChanged(Id node_id, + Id new_parent_id, + Id old_parent_id, + Id server_change_id, + Array<NodeDataPtr> nodes) OVERRIDE; + virtual void OnNodeReordered(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id) OVERRIDE; + virtual void OnNodeDeleted(Id node_id, Id server_change_id) OVERRIDE; + virtual void OnNodeViewReplaced(Id node, + Id new_view_id, + Id old_view_id) OVERRIDE; + virtual void OnViewDeleted(Id view_id) OVERRIDE; + virtual void OnViewInputEvent(Id view, + EventPtr event, + const Callback<void()>& callback) OVERRIDE; + virtual void DispatchOnViewInputEvent(Id view_id, EventPtr event) OVERRIDE; + + // Sync the client model with the service by enumerating the pending + // transaction queue and applying them in order. + void Sync(); + + // Removes |transaction| from the pending queue. |transaction| must be at the + // front of the queue. + void RemoveFromPendingQueue(ViewManagerTransaction* transaction); + + void AddRoot(Node* root); + void RemoveRoot(Node* root); + + bool connected_; + ConnectionSpecificId connection_id_; + ConnectionSpecificId next_id_; + Id next_server_change_id_; + + std::string creator_url_; + + Transactions pending_transactions_; + + base::Callback<void(void)> changes_acked_callback_; + + ViewManagerDelegate* delegate_; + + std::vector<Node*> roots_; + + IdToNodeMap nodes_; + IdToViewMap views_; + + ViewManagerService* service_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl); +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc new file mode 100644 index 00000000000..565b469c5b6 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc @@ -0,0 +1,24 @@ +// 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/public/cpp/view_manager/lib/view_manager_test_suite.h" + +#include "ui/gl/gl_surface.h" + +namespace mojo { +namespace view_manager { + +ViewManagerTestSuite::ViewManagerTestSuite(int argc, char** argv) + : TestSuite(argc, argv) {} + +ViewManagerTestSuite::~ViewManagerTestSuite() { +} + +void ViewManagerTestSuite::Initialize() { + base::TestSuite::Initialize(); + gfx::GLSurface::InitializeOneOffForTests(); +} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h new file mode 100644 index 00000000000..d0744ade822 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h @@ -0,0 +1,28 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_ + +#include "base/test/test_suite.h" + +namespace mojo { +namespace view_manager { + +class ViewManagerTestSuite : public base::TestSuite { + public: + ViewManagerTestSuite(int argc, char** argv); + virtual ~ViewManagerTestSuite(); + + protected: + virtual void Initialize() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(ViewManagerTestSuite); +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc new file mode 100644 index 00000000000..9a831397ba8 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc @@ -0,0 +1,14 @@ +// 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 "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h" + +int main(int argc, char** argv) { + mojo::view_manager::ViewManagerTestSuite test_suite(argc, argv); + + return base::LaunchUnitTests( + argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc new file mode 100644 index 00000000000..14ecf435f0b --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc @@ -0,0 +1,23 @@ +// 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/public/cpp/view_manager/lib/view_private.h" + +namespace mojo { +namespace view_manager { + +ViewPrivate::ViewPrivate(View* view) + : view_(view) { +} + +ViewPrivate::~ViewPrivate() { +} + +// static +View* ViewPrivate::LocalCreate() { + return new View; +} + +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_private.h b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.h new file mode 100644 index 00000000000..2e38613dfad --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.h @@ -0,0 +1,44 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_ + +#include "base/basictypes.h" + +#include "mojo/services/public/cpp/view_manager/view.h" + +namespace mojo { +namespace view_manager { + +class ViewPrivate { + public: + explicit ViewPrivate(View* view); + ~ViewPrivate(); + + static View* LocalCreate(); + void LocalDestroy() { + view_->LocalDestroy(); + } + + void set_id(Id id) { view_->id_ = id; } + void set_node(Node* node) { view_->node_ = node; } + + ViewManager* view_manager() { return view_->manager_; } + void set_view_manager(ViewManager* manager) { + view_->manager_ = manager; + } + + ObserverList<ViewObserver>* observers() { return &view_->observers_; } + + private: + View* view_; + + DISALLOW_COPY_AND_ASSIGN(ViewPrivate); +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/node.h b/chromium/mojo/services/public/cpp/view_manager/node.h new file mode 100644 index 00000000000..03dbd376def --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/node.h @@ -0,0 +1,108 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h" +#include "ui/gfx/geometry/rect.h" + +namespace mojo { +namespace view_manager { + +class View; +class ViewManager; +class NodeObserver; + +// Nodes are owned by the ViewManager. +// TODO(beng): Right now, you'll have to implement a NodeObserver to track +// destruction and NULL any pointers you have. +// Investigate some kind of smart pointer or weak pointer for these. +class Node { + public: + typedef std::vector<Node*> Children; + + static Node* Create(ViewManager* view_manager); + + // Destroys this node and all its children. + void Destroy(); + + // Configuration. + Id id() const { return id_; } + + // Geometric disposition. + const gfx::Rect& bounds() { return bounds_; } + void SetBounds(const gfx::Rect& bounds); + + // Observation. + void AddObserver(NodeObserver* observer); + void RemoveObserver(NodeObserver* observer); + + // Tree. + Node* parent() { return parent_; } + const Node* parent() const { return parent_; } + const Children& children() const { return children_; } + + void AddChild(Node* child); + void RemoveChild(Node* child); + + void Reorder(Node* relative, OrderDirection direction); + void MoveToFront(); + void MoveToBack(); + + bool Contains(Node* child) const; + + Node* GetChildById(Id id); + + // View. + void SetActiveView(View* view); + View* active_view() { return active_view_; } + + // Focus. + void SetFocus(); + + // Embedding. + void Embed(const String& url); + + protected: + // This class is subclassed only by test classes that provide a public ctor. + Node(); + ~Node(); + + private: + friend class NodePrivate; + + explicit Node(ViewManager* manager); + + void LocalDestroy(); + void LocalAddChild(Node* child); + void LocalRemoveChild(Node* child); + // Returns true if the order actually changed. + bool LocalReorder(Node* relative, OrderDirection direction); + void LocalSetActiveView(View* view); + void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds); + + ViewManager* manager_; + Id id_; + Node* parent_; + Children children_; + + ObserverList<NodeObserver> observers_; + + gfx::Rect bounds_; + View* active_view_; + + DISALLOW_COPY_AND_ASSIGN(Node); +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/node_observer.h b/chromium/mojo/services/public/cpp/view_manager/node_observer.h new file mode 100644 index 00000000000..7b0cdb010b9 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/node_observer.h @@ -0,0 +1,66 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_ + +#include <vector> + +#include "base/basictypes.h" + +#include "mojo/services/public/cpp/view_manager/node.h" + +namespace gfx { +class Rect; +} + +namespace mojo { +namespace view_manager { + +class Node; +class View; + +class NodeObserver { + public: + enum DispositionChangePhase { + DISPOSITION_CHANGING, + DISPOSITION_CHANGED + }; + + struct TreeChangeParams { + TreeChangeParams(); + Node* target; + Node* old_parent; + Node* new_parent; + Node* receiver; + DispositionChangePhase phase; + }; + + virtual void OnTreeChange(const TreeChangeParams& params) {} + + virtual void OnNodeReordered(Node* node, + Node* relative_node, + OrderDirection direction, + DispositionChangePhase phase) {} + + virtual void OnNodeDestroy(Node* node, DispositionChangePhase phase) {} + + virtual void OnNodeActiveViewChange(Node* node, + View* old_view, + View* new_view, + DispositionChangePhase phase) {} + + virtual void OnNodeBoundsChange(Node* node, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + DispositionChangePhase phase) {} + + protected: + virtual ~NodeObserver() {} +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/types.h b/chromium/mojo/services/public/cpp/view_manager/types.h new file mode 100644 index 00000000000..c1f33f30b8d --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/types.h @@ -0,0 +1,27 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_ + +#include "base/basictypes.h" + +// Typedefs for the transport types. These typedefs match that of the mojom +// file, see it for specifics. + +namespace mojo { +namespace view_manager { + +// Used to identify nodes, views and change ids. +typedef uint32_t Id; + +// Used to identify a connection as well as a connection specific view or node +// id. For example, the Id for a node consists of the ConnectionSpecificId of +// the connection and the ConnectionSpecificId of the node. +typedef uint16_t ConnectionSpecificId; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/util.h b/chromium/mojo/services/public/cpp/view_manager/util.h new file mode 100644 index 00000000000..6716075afc3 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/util.h @@ -0,0 +1,32 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_ + +#include "mojo/services/public/cpp/view_manager/types.h" + +// TODO(beng): #$*&@#(@ MacOSX SDK! +#if defined(HiWord) +#undef HiWord +#endif +#if defined(LoWord) +#undef LoWord +#endif + +namespace mojo { +namespace view_manager { + +inline uint16_t HiWord(uint32_t id) { + return static_cast<uint16_t>((id >> 16) & 0xFFFF); +} + +inline uint16_t LoWord(uint32_t id) { + return static_cast<uint16_t>(id & 0xFFFF); +} + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/view.h b/chromium/mojo/services/public/cpp/view_manager/view.h new file mode 100644 index 00000000000..b10417b21ba --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/view.h @@ -0,0 +1,60 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_ + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "third_party/skia/include/core/SkColor.h" + +class SkBitmap; + +namespace mojo { +namespace view_manager { + +class Node; +class ViewManager; +class ViewObserver; + +// Views are owned by the ViewManager. +class View { + public: + static View* Create(ViewManager* manager); + + void Destroy(); + + Id id() const { return id_; } + Node* node() { return node_; } + + void AddObserver(ViewObserver* observer); + void RemoveObserver(ViewObserver* observer); + + // TODO(beng): temporary only. + void SetContents(const SkBitmap& contents); + void SetColor(SkColor color); + + private: + friend class ViewPrivate; + + explicit View(ViewManager* manager); + View(); + ~View(); + + void LocalDestroy(); + + Id id_; + Node* node_; + ViewManager* manager_; + + ObserverList<ViewObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(View); +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/view_manager.h b/chromium/mojo/services/public/cpp/view_manager/view_manager.h new file mode 100644 index 00000000000..6ed6e901d38 --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/view_manager.h @@ -0,0 +1,44 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_ + +#include <string> +#include <vector> + +#include "mojo/services/public/cpp/view_manager/types.h" + +namespace mojo { +class Application; +namespace view_manager { + +class Node; +class View; +class ViewManagerDelegate; + +class ViewManager { + public: + // Delegate is owned by the caller. + static void Create(Application* application, ViewManagerDelegate* delegate); + + // Returns the URL of the application that embedded this application. + virtual const std::string& GetEmbedderURL() const = 0; + + // Returns all root nodes known to this connection. + virtual const std::vector<Node*>& GetRoots() const = 0; + + // Returns a Node or View known to this connection. + virtual Node* GetNodeById(Id id) = 0; + virtual View* GetViewById(Id id) = 0; + + protected: + virtual ~ViewManager() {} + +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h b/chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h new file mode 100644 index 00000000000..bceee02d9be --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h @@ -0,0 +1,25 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_ + +namespace mojo { +namespace view_manager { + +class Node; +class ViewManager; + +class ViewManagerDelegate { + public: + virtual void OnRootAdded(ViewManager* view_manager, Node* root) {} + + protected: + virtual ~ViewManagerDelegate() {} +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_ diff --git a/chromium/mojo/services/public/cpp/view_manager/view_observer.h b/chromium/mojo/services/public/cpp/view_manager/view_observer.h new file mode 100644 index 00000000000..9e189fd609e --- /dev/null +++ b/chromium/mojo/services/public/cpp/view_manager/view_observer.h @@ -0,0 +1,37 @@ +// 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_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_ +#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_ + +#include <vector> + +#include "base/basictypes.h" + +#include "mojo/services/public/interfaces/input_events/input_events.mojom.h" + +namespace mojo { +namespace view_manager { + +class View; + +class ViewObserver { + public: + enum DispositionChangePhase { + DISPOSITION_CHANGING, + DISPOSITION_CHANGED + }; + + virtual void OnViewDestroy(View* view, DispositionChangePhase phase) {} + + virtual void OnViewInputEvent(View* view, const EventPtr& event) {} + + protected: + virtual ~ViewObserver() {} +}; + +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_ diff --git a/chromium/mojo/services/public/interfaces/geometry/geometry.mojom b/chromium/mojo/services/public/interfaces/geometry/geometry.mojom new file mode 100644 index 00000000000..ab68c8e61ce --- /dev/null +++ b/chromium/mojo/services/public/interfaces/geometry/geometry.mojom @@ -0,0 +1,34 @@ +// 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 mojo { + +struct Point { + int32 x; + int32 y; +}; + +struct PointF { + float x; + float y; +}; + +struct Size { + int32 width; + int32 height; +}; + +struct Rect { + int32 x; + int32 y; + int32 width; + int32 height; +}; + +struct Transform { + // Should have exactly 16 entries. + float[] matrix; +}; + +} diff --git a/chromium/mojo/services/public/interfaces/input_events/input_events.mojom b/chromium/mojo/services/public/interfaces/input_events/input_events.mojom new file mode 100644 index 00000000000..255fe284fe0 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/input_events/input_events.mojom @@ -0,0 +1,27 @@ +// 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. + +import "../geometry/geometry.mojom" + +module mojo { + +struct KeyData { + int32 key_code; + bool is_char; +}; + +struct TouchData { + int32 pointer_id; +}; + +struct Event { + int32 action; + int32 flags; + int64 time_stamp; + Point location; + KeyData key_data; + TouchData touch_data; +}; + +} diff --git a/chromium/mojo/services/public/interfaces/launcher/launcher.mojom b/chromium/mojo/services/public/interfaces/launcher/launcher.mojom new file mode 100644 index 00000000000..e35eb6746ac --- /dev/null +++ b/chromium/mojo/services/public/interfaces/launcher/launcher.mojom @@ -0,0 +1,36 @@ +// 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. + +import "../navigation/navigation.mojom" + +module mojo.launcher { + +[Client=LauncherClient] +interface Launcher { + // Determines the correct handler application that can render a given URL. + // + // Sometimes this is known statically from the URL (e.g., from its scheme, or + // because we already have an application locally that has said it can handle + // the URL. Other times, we will actually fetch |url| to examine its + // content-type and/or sniff it to determine the correct handler. + Launch(string url); +}; + +[Client=Launcher] +interface LauncherClient { + // Called when a handler has been resolved in response to Launch(). Receiving + // application should connect to the application at |handler_url| and navigate + // it to |view_url|. + // + // Note: |view_url| might not be the same as the URL that was initially + // requested, e.g., in the case of redirects. Applications should always + // navigate to view_url, not the initial URL. + // + // Note: |response| can be NULL in the case where a request was not needed to + // determine the correct handler. + OnLaunch(string handler_url, string view_url, + mojo.navigation.ResponseDetails response); +}; + +} diff --git a/chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom b/chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom new file mode 100644 index 00000000000..e1ce173c08f --- /dev/null +++ b/chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom @@ -0,0 +1,28 @@ +// 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. + +import "../../../gles2/command_buffer.mojom" +import "../geometry/geometry.mojom" +import "../input_events/input_events.mojom" + +module mojo { + +[Client=NativeViewportClient] +interface NativeViewport { + Create(Rect bounds); + Show(); + Hide(); + Close(); + SetBounds(Rect bounds); + CreateGLES2Context(CommandBuffer& gles2_client); +}; + +interface NativeViewportClient { + OnCreated(); + OnBoundsChanged(Rect bounds); + OnDestroyed(); + OnEvent(Event event) => (); +}; + +} diff --git a/chromium/mojo/services/public/interfaces/navigation/navigation.mojom b/chromium/mojo/services/public/interfaces/navigation/navigation.mojom new file mode 100644 index 00000000000..fbe082f06e4 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/navigation/navigation.mojom @@ -0,0 +1,36 @@ +// 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. + +import "../network/url_loader.mojom" + +module mojo.navigation { + +struct NavigationDetails { + string url; + // TODO(aa): method, data, etc. +}; + +struct ResponseDetails { + // TODO(beng): consider providing access to URLRequest too. Currently it is + // not possible to obtain from the URLLoader. + mojo.URLResponse response; + handle<data_pipe_consumer> response_body_stream; +}; + +// Embedders that support navigation of implement this interface. +interface NavigatorHost { + RequestNavigate(uint32 source_node_id, NavigationDetails details); +}; + +// Applications implement this interface to support navigation of their views +// by embedders. +// |response_details| can be NULL when a navigation was not the result of a +// network load. +interface Navigator { + Navigate(uint32 node_id, + NavigationDetails navigation_details, + ResponseDetails response_details); +}; + +} diff --git a/chromium/mojo/services/public/interfaces/network/network_error.mojom b/chromium/mojo/services/public/interfaces/network/network_error.mojom new file mode 100644 index 00000000000..d345e935504 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/network/network_error.mojom @@ -0,0 +1,12 @@ +// 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 mojo { + +struct NetworkError { + int32 code; + string description; +}; + +} diff --git a/chromium/mojo/services/public/interfaces/network/network_service.mojom b/chromium/mojo/services/public/interfaces/network/network_service.mojom new file mode 100644 index 00000000000..7d9cd178cf6 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/network/network_service.mojom @@ -0,0 +1,15 @@ +// 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. + +import "url_loader.mojom" + +module mojo { + +interface NetworkService { + CreateURLLoader(URLLoader& loader); + + // TODO(darin): Add other methods here (e.g., cookies). +}; + +} diff --git a/chromium/mojo/services/public/interfaces/network/url_loader.mojom b/chromium/mojo/services/public/interfaces/network/url_loader.mojom new file mode 100644 index 00000000000..b4bcf09f89d --- /dev/null +++ b/chromium/mojo/services/public/interfaces/network/url_loader.mojom @@ -0,0 +1,94 @@ +// 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. + +import "network_error.mojom" + +module mojo { + +struct URLRequest { + // The URL to load. + string url; + + // The HTTP method if applicable. + string method = "GET"; + + // Additional HTTP request headers. + string[] headers; + + // The payload for the request body. For HTTP requests, the method must be + // set to "POST" or "PUT". + handle<data_pipe_consumer> body; + + // The number of bytes to be read from |body|. A Content-Length header of + // this value will be sent. Set to -1 if length is unknown, which will cause + // |body| to be uploaded using a chunked encoding. + int64 body_length = 0; + + // If set to true, then redirects will be automatically followed. Otherwise, + // when a redirect is encounterd, FollowRedirect must be called to proceed. + bool auto_follow_redirects = false; + + // If set to true, then the HTTP request will bypass the local cache and will + // have a 'Cache-Control: nocache' header added in that causes any proxy + // servers to also not satisfy the request from their cache. This has the + // effect of forcing a full end-to-end fetch. + bool bypass_cache = false; +}; + +struct URLResponse { + // The final URL of the response, after redirects have been followed. + string url; + + // The HTTP status code. 0 if not applicable. + uint32 status_code; + + // The HTTP status line. + string status_line; + + // The HTTP response headers. + string[] headers; +}; + +[Client=URLLoaderClient] +interface URLLoader { + // Start loading the given |request|. When available, the response body will + // be copied to |response_body_stream|. + // + // The client's |OnReceivedResponse| method will run when response meta data + // becomes available, or if a redirect response is encountered and + // |auto_follow_redirects| is false, the client's |OnRecievedRedirect| method + // will called instead. + // + // NOTE: You may observe data being pushed to |response_body_stream| before + // you receive |OnReceivedResponse|. + Start(URLRequest request, handle<data_pipe_producer> response_body_stream); + + // If the request passed to |Start| had |auto_follow_redirects| set to false, + // then upon receiving a redirect, |OnReceivedRedirect| will be called. To + // follow the indicated redirect, call the |FollowRedirect| method. + FollowRedirect(); +}; + +interface URLLoaderClient { + // This method is called when a redirect is encountered, provided the + // request's |auto_follow_redirects| attribute was set to false. + OnReceivedRedirect(URLResponse response, string new_url, string new_method); + + // This method is called when response meta data becomes available. + OnReceivedResponse(URLResponse response); + + // This method is called when a network level error is encountered. This can + // happen before or after OnReceivedResponse, but cannot happen after + // OnReceivedEndOfResponseBody. + OnReceivedError(NetworkError error); + + // This method is called when the response body has been successfully + // downloaded and copied in its entirety to |response_body_stream|. + // + // NOTE: Because |response_body_stream| is limited in size, this event may be + // delayed until |response_body_stream| is consumed. + OnReceivedEndOfResponseBody(); +}; + +} diff --git a/chromium/mojo/services/public/interfaces/surfaces/quads.mojom b/chromium/mojo/services/public/interfaces/surfaces/quads.mojom new file mode 100644 index 00000000000..eb8898b0f36 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/surfaces/quads.mojom @@ -0,0 +1,165 @@ +// 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. + +import "../geometry/geometry.mojom" +import "surface_id.mojom" + +module mojo.surfaces { + +struct Color { + uint32 rgba; +}; + +// TODO(jamesr): Populate subtype fields. +struct CheckerboardQuadState {}; + +struct DebugBorderQuadState {}; + +struct IoSurfaceContentQuadState {}; + +struct RenderPassQuadState {}; + +struct SolidColorQuadState { + Color color; +}; + +struct SurfaceQuadState { + SurfaceId surface; +}; + +struct TextureQuadState { + uint32 resource_id; + bool premultiplied_alpha; + mojo.PointF uv_top_left; + mojo.PointF uv_bottom_right; + Color background_color; + // Should have exactly 4 entries. + float[] vertex_opacity; + bool flipped; +}; + +struct TiledContentQuadState {}; + +struct StreamVideoQuadState {}; + +struct YUVVideoQuadState {}; + +enum Material { + CHECKERBOARD = 1, + DEBUG_BORDER, + IO_SURFACE_CONTENT, + RENDER_PASS, + SOLID_COLOR, + STREAM_VIDEO_CONTENT, + SURFACE_CONTENT, + TEXTURE_CONTENT, + TILED_CONTENT, + YUV_VIDEO_CONTENT, +}; + +struct Quad { + Material material; + + // This rect, after applying the quad_transform(), gives the geometry that + // this quad should draw to. This rect lives in content space. + mojo.Rect rect; + + // This specifies the region of the quad that is opaque. This rect lives in + // content space. + mojo.Rect opaque_rect; + + // Allows changing the rect that gets drawn to make it smaller. This value + // should be clipped to |rect|. This rect lives in content space. + mojo.Rect visible_rect; + + // Allows changing the rect that gets drawn to make it smaller. This value + // should be clipped to |rect|. This rect lives in content space. + bool needs_blending; + + // Index into the containing pass' shared quad state array which has state + // (transforms etc) shared by multiple quads. + int32 shared_quad_state_index; + + // Only one of the following will be set, depending on the material. + CheckerboardQuadState checkerboard_quad_state; + DebugBorderQuadState debug_border_quad_state; + IoSurfaceContentQuadState io_surface_quad_state; + RenderPassQuadState render_pass_quad_state; + SolidColorQuadState solid_color_quad_state; + SurfaceQuadState surface_quad_state; + TextureQuadState texture_quad_state; + TiledContentQuadState tiled_content_quad_state; + StreamVideoQuadState stream_video_quad_state; + YUVVideoQuadState yuv_video_quad_state; +}; + +enum SkXfermode { + kClear_Mode = 0, //!< [0, 0] + kSrc_Mode, //!< [Sa, Sc] + kDst_Mode, //!< [Da, Dc] + kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc] + kDstOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc] + kSrcIn_Mode, //!< [Sa * Da, Sc * Da] + kDstIn_Mode, //!< [Sa * Da, Sa * Dc] + kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)] + kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)] + kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc] + kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] + kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] + kPlus_Mode, //!< [Sa + Da, Sc + Dc] + kModulate_Mode, // multiplies all components (= alpha and color) + + // Following blend modes are defined in the CSS Compositing standard: + // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blending + kScreen_Mode, + kLastCoeffMode = kScreen_Mode, + + kOverlay_Mode, + kDarken_Mode, + kLighten_Mode, + kColorDodge_Mode, + kColorBurn_Mode, + kHardLight_Mode, + kSoftLight_Mode, + kDifference_Mode, + kExclusion_Mode, + kMultiply_Mode, + kLastSeparableMode = kMultiply_Mode, + + kHue_Mode, + kSaturation_Mode, + kColor_Mode, + kLuminosity_Mode, + kLastMode = kLuminosity_Mode +}; + +struct SharedQuadState { + // Transforms from quad's original content space to its target content space. + mojo.Transform content_to_target_transform; + + // This size lives in the content space for the quad's originating layer. + mojo.Size content_bounds; + + // This rect lives in the content space for the quad's originating layer. + mojo.Rect visible_content_rect; + + // This rect lives in the target content space. + mojo.Rect clip_rect; + + bool is_clipped; + float opacity; + SkXfermode blend_mode; +}; + +struct Pass { + int32 id; + mojo.Rect output_rect; + mojo.Rect damage_rect; + mojo.Transform transform_to_root_target; + bool has_transparent_pixels; + Quad[] quads; + SharedQuadState[] shared_quad_states; +}; + +} diff --git a/chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom b/chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom new file mode 100644 index 00000000000..42ab9924471 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom @@ -0,0 +1,11 @@ +// 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 mojo.surfaces { + +struct SurfaceId { + int32 id; +}; + +} diff --git a/chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom b/chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom new file mode 100644 index 00000000000..6bb38b7e0b3 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom @@ -0,0 +1,57 @@ +// 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. + +import "../geometry/geometry.mojom" +import "quads.mojom" +import "surface_id.mojom" + +module mojo.surfaces { + +enum ResourceFormat { + RGBA_8888, + RGBA_4444, + BGRA_8888, + LUMINANCE_8, + RGB_565, + ETC1, +}; + +struct Mailbox { + // Should have exactly 64 entries. + int8[] name; +}; + +struct MailboxHolder { + Mailbox mailbox; + uint32 texture_target; + uint32 sync_point; +}; + +struct TransferableResource { + uint32 id; + ResourceFormat format; + uint32 filter; + mojo.Size size; + MailboxHolder mailbox_holder; + bool is_repeated; + bool is_software; +}; + +struct Frame { + TransferableResource[] resources; + Pass[] passes; +}; + +interface SurfaceClient { + ReturnResources(TransferableResource[] resources); +}; + +[client=SurfaceClient] +interface Surface { + CreateSurface(SurfaceId id, mojo.Size size); + SubmitFrame(SurfaceId id, Frame frame); + DestroySurface(SurfaceId id); +}; + +} diff --git a/chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom b/chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom new file mode 100644 index 00000000000..ea1d3b56265 --- /dev/null +++ b/chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom @@ -0,0 +1,193 @@ +// 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. + +import "../geometry/geometry.mojom" +import "../input_events/input_events.mojom" +import "view_manager_constants.mojom" + +module mojo.view_manager { + +struct NodeData { + uint32 parent_id; + uint32 node_id; + uint32 view_id; + mojo.Rect bounds; +}; + +// ViewManagerInitService is responsible for launching the client that controls +// the root node. mojo::view_manager returns an instance of this. All other +// connections are established by the client this creates. +interface ViewManagerInitService { + EmbedRoot(string url) => (bool success); +}; + +// Functions that mutate the hierarchy take a change id. This is an ever +// increasing integer used to identify the change. Every hierarchy change +// increases this value. The server only accepts changes where the supplied +// |server_change_id| matches the expected next value. This ensures changes are +// made in a well defined order. +// +// Nodes and Views are identified by a uint32. The upper 16 bits are the +// connection id, and the lower 16 the id assigned by the client. +// +// The root node is identified with a connection id of 0, and value of 1. +[Client=ViewManagerClient] +interface ViewManagerService { + // Creates a new node with the specified id. It is up to the client to ensure + // the id is unique to the connection (the id need not be globally unique). + // Additionally the connection id (embedded in |node_id|) must match that of + // the connection. + CreateNode(uint32 node_id) => (bool success); + + // Deletes a node. This does not recurse. No hierarchy change notifications + // are sent as a result of this. Only the connection that created the node can + // delete it. + DeleteNode(uint32 node_id, uint32 change_id) => (bool success); + + // Sets the specified bounds of the specified node. + SetNodeBounds(uint32 node_id, mojo.Rect bounds) => (bool success); + + // Reparents a node. See description above class for details of |change_id|. + // This fails for any of the following reasons: + // . |server_change_id| is not the expected id. + // . |parent| or |child| does not identify a valid node. + // . |child| is an ancestor of |parent|. + // . |child| is already a child of |parent|. + // + // This may result in a connection getting OnNodeDeleted(). See + // RemoveNodeFromParent for details. + AddNode(uint32 parent, + uint32 child, + uint32 server_change_id) => (bool success); + + // Removes a view from its current parent. See description above class for + // details of |change_id|. This fails if the node is not valid, + // |server_change_id| doesn't match, or the node already has no parent. + // + // Removing a node from a parent may result in OnNodeDeleted() being sent to + // other connections. For example, connection A has nodes 1 and 2, with 2 a + // child of 1. Connection B has a root 1. If 2 is removed from 1 then B gets + // OnNodeDeleted(). This is done as node 2 is effectively no longer visible to + // connection B. + RemoveNodeFromParent(uint32 node_id, + uint32 server_change_id) => (bool success); + + // Reorders a node in its parent, relative to |relative_node_id| according to + // |direction|. + // Only the connection that created the node's parent can reorder its + // children. + ReorderNode(uint32 node_id, + uint32 relative_node_id, + OrderDirection direction, + uint32 server_change_id) => (bool success); + + // Returns the nodes comprising the tree starting at |node_id|. |node_id| is + // the first result in the return value, unless |node_id| is invalid, in which + // case an empty vector is returned. The nodes are visited using a depth first + // search (pre-order). + GetNodeTree(uint32 node_id) => (NodeData[] nodes); + + // Creates a new view with the specified id. It is up to the client to ensure + // the id is unique to the connection (the id need not be globally unique). + // Additionally the connection id (embedded in |view_id|) must match that of + // the connection. + CreateView(uint32 view_id) => (bool success); + + // Deletes the view with the specified id. Only the connection that created + // the view can delete it. + DeleteView(uint32 view_id) => (bool success); + + // Sets the view a node is showing. + SetView(uint32 node_id, uint32 view_id) => (bool success); + + // Shows the specified image (png encoded) in the specified view. + SetViewContents(uint32 view_id, + handle<shared_buffer> buffer, + uint32 buffer_size) => (bool success); + + // Sets focus to the specified node. + SetFocus(uint32 node_id) => (bool success); + + // Embeds the app at |url| in the specified nodes. More specifically this + // creates a new connection to the specified url, expecting to get an + // ViewManagerClient and configures it with the root nodes |nodes|. Fails + // if |nodes| is empty or contains nodes that were not created by this + // connection. + // If a particular client invokes Embed() multiple times with the same url, + // the connection is reused. When this happens the ViewManagerClient is + // notified of the additional roots by way of OnRootsAdded(). + Embed(string url, uint32[] nodes) => (bool success); + + // TODO(sky): move these to a separate interface when FIFO works. + + // Sends OnViewInputEvent() to the owner of the specified view. + DispatchOnViewInputEvent(uint32 view_id, mojo.Event event); +}; + +// Changes to nodes/views are not sent to the connection that originated the +// change. For example, if connection 1 attaches a view to a node (SetView()) +// connection 1 does not receive OnNodeViewReplaced(). +[Client=ViewManagerService] +interface ViewManagerClient { + // Invoked once the connection has been established. |connection_id| is the id + // that uniquely identifies this connection. |next_server_change_id| is the + // id of the next change the server is expecting. |nodes| are the nodes + // parented to the root. + OnViewManagerConnectionEstablished(uint16 connection_id, + string creator_url, + uint32 next_server_change_id, + NodeData[] nodes); + + // See description of ViewManagerService::Connect() for details as to when + // this is invoked. + OnRootsAdded(NodeData[] nodes); + + // This is sent to clients when a change is made to the server that results + // in the |server_change_id| changing but the client isn't notified. This is + // not sent if the client receives a callback giving a new + // |server_change_id|. For example, if a client 1 changes the hierarchy in + // some way but client 2 isn't notified of the change, then client 2 gets + // OnServerChangeIdAdvanced(). + OnServerChangeIdAdvanced(uint32 next_server_change_id); + + // Invoked when a node's bounds have changed. + OnNodeBoundsChanged(uint32 node, mojo.Rect old_bounds, mojo.Rect new_bounds); + + // Invoked when a change is done to the hierarchy. A value of 0 is used to + // identify a null node. For example, if the old_parent is NULL, 0 is + // supplied. See description above ViewManager for details on the change ids. + // |nodes| contains any nodes that are that the client has not been told + // about. This is not sent for hierarchy changes of nodes not known to this + // client or not attached to the tree. + OnNodeHierarchyChanged(uint32 node, + uint32 new_parent, + uint32 old_parent, + uint32 server_change_id, + NodeData[] nodes); + + // Invoked when the order of nodes within a parent changes. + OnNodeReordered(uint32 node_id, + uint32 relative_node_id, + OrderDirection direction, + uint32 server_change_id); + + // Invoked when a node is deleted. + OnNodeDeleted(uint32 node, uint32 server_change_id); + + // Invoked when the view associated with a node is replaced by another view. + // 0 is used to identify a null view. + OnNodeViewReplaced(uint32 node, uint32 new_view_id, uint32 old_view_id); + + // Invoked when a view is deleted. + OnViewDeleted(uint32 view); + + // Invoked when an event is targeted at the specified view. + OnViewInputEvent(uint32 view, mojo.Event event) => (); + + // TODO(sky): move to separate interface when FIFO sorted out. + + DispatchOnViewInputEvent(uint32 view, mojo.Event event); +}; + +} diff --git a/chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom b/chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom new file mode 100644 index 00000000000..5c73190b70f --- /dev/null +++ b/chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom @@ -0,0 +1,12 @@ +// 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 mojo.view_manager { + +enum OrderDirection { + ORDER_ABOVE = 1, + ORDER_BELOW, +}; + +} diff --git a/chromium/mojo/services/test_service/test_service.mojom b/chromium/mojo/services/test_service/test_service.mojom new file mode 100644 index 00000000000..1065b20ab30 --- /dev/null +++ b/chromium/mojo/services/test_service/test_service.mojom @@ -0,0 +1,11 @@ +// 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 mojo.test { + +interface ITestService { + Ping() => (); +}; + +} // module mojo.test diff --git a/chromium/mojo/services/test_service/test_service_application.cc b/chromium/mojo/services/test_service/test_service_application.cc new file mode 100644 index 00000000000..3737823c9d2 --- /dev/null +++ b/chromium/mojo/services/test_service/test_service_application.cc @@ -0,0 +1,44 @@ +// 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/test_service/test_service_application.h" + +#include <assert.h> + +#include "mojo/public/cpp/utility/run_loop.h" +#include "mojo/services/test_service/test_service_impl.h" + +namespace mojo { +namespace test { + +TestServiceApplication::TestServiceApplication() : ref_count_(0) { +} + +TestServiceApplication::~TestServiceApplication() { +} + +void TestServiceApplication::Initialize() { + AddService<TestServiceImpl>(this); +} + +void TestServiceApplication::AddRef() { + assert(ref_count_ >= 0); + ref_count_++; +} + +void TestServiceApplication::ReleaseRef() { + assert(ref_count_ > 0); + ref_count_--; + if (ref_count_ <= 0) + RunLoop::current()->Quit(); +} + +} // namespace test + +// static +Application* Application::Create() { + return new mojo::test::TestServiceApplication(); +} + +} // namespace mojo diff --git a/chromium/mojo/services/test_service/test_service_application.h b/chromium/mojo/services/test_service/test_service_application.h new file mode 100644 index 00000000000..9f042c1987a --- /dev/null +++ b/chromium/mojo/services/test_service/test_service_application.h @@ -0,0 +1,33 @@ +// 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_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_ +#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_ + +#include "mojo/public/cpp/application/application.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace test { + +class TestServiceApplication : public Application { + public: + TestServiceApplication(); + virtual ~TestServiceApplication(); + + virtual void Initialize() MOJO_OVERRIDE; + + void AddRef(); + void ReleaseRef(); + + private: + int ref_count_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceApplication); +}; + +} // namespace test +} // namespace mojo + +#endif // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_ diff --git a/chromium/mojo/services/test_service/test_service_impl.cc b/chromium/mojo/services/test_service/test_service_impl.cc new file mode 100644 index 00000000000..a38d619d90e --- /dev/null +++ b/chromium/mojo/services/test_service/test_service_impl.cc @@ -0,0 +1,32 @@ +// 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/test_service/test_service_impl.h" + +#include "mojo/services/test_service/test_service_application.h" + +namespace mojo { +namespace test { + +TestServiceImpl::TestServiceImpl(TestServiceApplication* application) + : application_(application) { +} + +TestServiceImpl::~TestServiceImpl() { +} + +void TestServiceImpl::OnConnectionEstablished() { + application_->AddRef(); +} + +void TestServiceImpl::OnConnectionError() { + application_->ReleaseRef(); +} + +void TestServiceImpl::Ping(const mojo::Callback<void()>& callback) { + callback.Run(); +} + +} // namespace test +} // namespace mojo diff --git a/chromium/mojo/services/test_service/test_service_impl.h b/chromium/mojo/services/test_service/test_service_impl.h new file mode 100644 index 00000000000..df2d4a160be --- /dev/null +++ b/chromium/mojo/services/test_service/test_service_impl.h @@ -0,0 +1,35 @@ +// 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_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_ +#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_ + +#include "mojo/public/cpp/system/macros.h" +#include "mojo/services/test_service/test_service.mojom.h" + +namespace mojo { +namespace test { + +class TestServiceApplication; + +class TestServiceImpl : public InterfaceImpl<ITestService> { + public: + explicit TestServiceImpl(TestServiceApplication* application); + virtual ~TestServiceImpl(); + + // |ITestService| methods: + virtual void OnConnectionEstablished() MOJO_OVERRIDE; + virtual void OnConnectionError() MOJO_OVERRIDE; + virtual void Ping(const mojo::Callback<void()>& callback) MOJO_OVERRIDE; + + private: + TestServiceApplication* const application_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceImpl); +}; + +} // namespace test +} // namespace mojo + +#endif // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_ diff --git a/chromium/mojo/services/view_manager/DEPS b/chromium/mojo/services/view_manager/DEPS new file mode 100644 index 00000000000..e6f1501ffe1 --- /dev/null +++ b/chromium/mojo/services/view_manager/DEPS @@ -0,0 +1,22 @@ +include_rules = [ + "+cc", + "+mojo/aura", + "+mojo/cc", + "+mojo/geometry", + "+mojo/services", + "+third_party/skia", + "+ui/aura", + "+ui/base/cursor/cursor.h", + "+ui/base/hit_test.h", + "+ui/compositor", + "+ui/events", + "+ui/gfx", + "+ui/gl", + "+webkit/common/gpu", +] + +specific_include_rules = { + "view_manager_unittest.cc": [ + "+mojo/service_manager/service_manager.h", + ], +} diff --git a/chromium/mojo/services/view_manager/context_factory_impl.cc b/chromium/mojo/services/view_manager/context_factory_impl.cc new file mode 100644 index 00000000000..c3977e7d2e4 --- /dev/null +++ b/chromium/mojo/services/view_manager/context_factory_impl.cc @@ -0,0 +1,75 @@ +// 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/context_factory_impl.h" + +#include "cc/output/output_surface.h" +#include "mojo/cc/context_provider_mojo.h" +#include "ui/compositor/reflector.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface.h" +#include "webkit/common/gpu/context_provider_in_process.h" +#include "webkit/common/gpu/grcontext_for_webgraphicscontext3d.h" +#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" + +namespace mojo { +namespace view_manager { +namespace service { + +ContextFactoryImpl::ContextFactoryImpl( + ScopedMessagePipeHandle command_buffer_handle) + : command_buffer_handle_(command_buffer_handle.Pass()) { +} + +ContextFactoryImpl::~ContextFactoryImpl() { +} + +scoped_ptr<cc::OutputSurface> ContextFactoryImpl::CreateOutputSurface( + ui::Compositor* compositor, bool software_fallback) { + return make_scoped_ptr( + new cc::OutputSurface( + new ContextProviderMojo(command_buffer_handle_.Pass()))); +} + +scoped_refptr<ui::Reflector> ContextFactoryImpl::CreateReflector( + ui::Compositor* mirroed_compositor, + ui::Layer* mirroring_layer) { + return NULL; +} + +void ContextFactoryImpl::RemoveReflector( + scoped_refptr<ui::Reflector> reflector) { +} + +scoped_refptr<cc::ContextProvider> +ContextFactoryImpl::SharedMainThreadContextProvider() { + if (!shared_main_thread_contexts_ || + shared_main_thread_contexts_->DestroyedOnMainThread()) { + bool lose_context_when_out_of_memory = false; + shared_main_thread_contexts_ = + webkit::gpu::ContextProviderInProcess::CreateOffscreen( + lose_context_when_out_of_memory); + if (shared_main_thread_contexts_ && + !shared_main_thread_contexts_->BindToCurrentThread()) + shared_main_thread_contexts_ = NULL; + } + return shared_main_thread_contexts_; +} + +void ContextFactoryImpl::RemoveCompositor(ui::Compositor* compositor) { +} + +bool ContextFactoryImpl::DoesCreateTestContexts() { return false; } + +cc::SharedBitmapManager* ContextFactoryImpl::GetSharedBitmapManager() { + return NULL; +} + +base::MessageLoopProxy* ContextFactoryImpl::GetCompositorMessageLoop() { + return NULL; +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/context_factory_impl.h b/chromium/mojo/services/view_manager/context_factory_impl.h new file mode 100644 index 00000000000..30cd26754b8 --- /dev/null +++ b/chromium/mojo/services/view_manager/context_factory_impl.h @@ -0,0 +1,56 @@ +// 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_SERVICES_VIEW_MANAGER_CONTEXT_FACTORY_IMPL_H_ +#define MOJO_SERVICES_VIEW_MANAGER_CONTEXT_FACTORY_IMPL_H_ + +#include "mojo/public/cpp/system/core.h" +#include "ui/compositor/compositor.h" + +namespace webkit { +namespace gpu { +class ContextProviderInProcess; +} +} + +namespace mojo { +namespace view_manager { +namespace service { + +// The default factory that creates in-process contexts. +class ContextFactoryImpl : public ui::ContextFactory { + public: + explicit ContextFactoryImpl(ScopedMessagePipeHandle command_buffer_handle); + virtual ~ContextFactoryImpl(); + + // ContextFactory implementation + virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface( + ui::Compositor* compositor, bool software_fallback) OVERRIDE; + + virtual scoped_refptr<ui::Reflector> CreateReflector( + ui::Compositor* compositor, + ui::Layer* layer) OVERRIDE; + virtual void RemoveReflector(scoped_refptr<ui::Reflector> reflector) OVERRIDE; + + virtual scoped_refptr<cc::ContextProvider> + SharedMainThreadContextProvider() OVERRIDE; + virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE; + virtual bool DoesCreateTestContexts() OVERRIDE; + virtual cc::SharedBitmapManager* GetSharedBitmapManager() OVERRIDE; + virtual base::MessageLoopProxy* GetCompositorMessageLoop() OVERRIDE; + + private: + scoped_refptr<webkit::gpu::ContextProviderInProcess> + shared_main_thread_contexts_; + + ScopedMessagePipeHandle command_buffer_handle_; + + DISALLOW_COPY_AND_ASSIGN(ContextFactoryImpl); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_CONTEXT_FACTORY_IMPL_H_ diff --git a/chromium/mojo/services/view_manager/ids.h b/chromium/mojo/services/view_manager/ids.h new file mode 100644 index 00000000000..8d906beecb6 --- /dev/null +++ b/chromium/mojo/services/view_manager/ids.h @@ -0,0 +1,94 @@ +// 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_SERVICES_VIEW_MANAGER_IDS_H_ +#define MOJO_SERVICES_VIEW_MANAGER_IDS_H_ + +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/cpp/view_manager/util.h" +#include "mojo/services/view_manager/view_manager_export.h" + +namespace mojo { +namespace view_manager { +namespace service { + +// Connection id reserved for the root. +const ConnectionSpecificId kRootConnection = 0; + +// TODO(sky): remove this, temporary while window manager API is in existing +// api. +const ConnectionSpecificId kWindowManagerConnection = 1; + +// Adds a bit of type safety to node ids. +struct MOJO_VIEW_MANAGER_EXPORT NodeId { + NodeId(ConnectionSpecificId connection_id, ConnectionSpecificId node_id) + : connection_id(connection_id), + node_id(node_id) {} + NodeId() : connection_id(0), node_id(0) {} + + bool operator==(const NodeId& other) const { + return other.connection_id == connection_id && + other.node_id == node_id; + } + + bool operator!=(const NodeId& other) const { + return !(*this == other); + } + + ConnectionSpecificId connection_id; + ConnectionSpecificId node_id; +}; + +// Adds a bit of type safety to view ids. +struct MOJO_VIEW_MANAGER_EXPORT ViewId { + ViewId(ConnectionSpecificId connection_id, ConnectionSpecificId view_id) + : connection_id(connection_id), + view_id(view_id) {} + ViewId() : connection_id(0), view_id(0) {} + + bool operator==(const ViewId& other) const { + return other.connection_id == connection_id && + other.view_id == view_id; + } + + bool operator!=(const ViewId& other) const { + return !(*this == other); + } + + ConnectionSpecificId connection_id; + ConnectionSpecificId view_id; +}; + +// Functions for converting to/from structs and transport values. +inline NodeId NodeIdFromTransportId(Id id) { + return NodeId(HiWord(id), LoWord(id)); +} + +inline Id NodeIdToTransportId(const NodeId& id) { + return (id.connection_id << 16) | id.node_id; +} + +inline ViewId ViewIdFromTransportId(Id id) { + return ViewId(HiWord(id), LoWord(id)); +} + +inline Id ViewIdToTransportId(const ViewId& id) { + return (id.connection_id << 16) | id.view_id; +} + +inline NodeId RootNodeId() { + return NodeId(kRootConnection, 1); +} + +// Returns a NodeId that is reserved to indicate no node. That is, no node will +// ever be created with this id. +inline NodeId InvalidNodeId() { + return NodeId(kRootConnection, 0); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_IDS_H_ diff --git a/chromium/mojo/services/view_manager/main.cc b/chromium/mojo/services/view_manager/main.cc new file mode 100644 index 00000000000..de0a7684095 --- /dev/null +++ b/chromium/mojo/services/view_manager/main.cc @@ -0,0 +1,35 @@ +// 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/public/cpp/application/application.h" +#include "mojo/services/view_manager/view_manager_init_service_impl.h" + +namespace mojo { +namespace view_manager { +namespace service { + +class ViewManagerApp : public Application { + public: + ViewManagerApp() {} + virtual ~ViewManagerApp() {} + + virtual void Initialize() MOJO_OVERRIDE { + // TODO(sky): this needs some sort of authentication as well as making sure + // we only ever have one active at a time. + AddService<ViewManagerInitServiceImpl>(service_provider()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ViewManagerApp); +}; + +} // namespace service +} // namespace view_manager + +// static +Application* Application::Create() { + return new mojo::view_manager::service::ViewManagerApp(); +} + +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/node.cc b/chromium/mojo/services/view_manager/node.cc new file mode 100644 index 00000000000..cbdd16d712b --- /dev/null +++ b/chromium/mojo/services/view_manager/node.cc @@ -0,0 +1,189 @@ +// 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/node.h" + +#include "mojo/services/view_manager/node_delegate.h" +#include "mojo/services/view_manager/view.h" +#include "ui/aura/window_property.h" +#include "ui/base/cursor/cursor.h" +#include "ui/base/hit_test.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/native_widget_types.h" + +DECLARE_WINDOW_PROPERTY_TYPE(mojo::view_manager::service::Node*); + +namespace mojo { +namespace view_manager { +namespace service { + +DEFINE_WINDOW_PROPERTY_KEY(Node*, kNodeKey, NULL); + +Node::Node(NodeDelegate* delegate, const NodeId& id) + : delegate_(delegate), + id_(id), + view_(NULL), + window_(this) { + DCHECK(delegate); // Must provide a delegate. + window_.set_owned_by_parent(false); + window_.AddObserver(this); + window_.SetProperty(kNodeKey, this); + window_.Init(aura::WINDOW_LAYER_TEXTURED); + + // TODO(sky): this likely needs to be false and add a visibility API. + window_.Show(); +} + +Node::~Node() { + SetView(NULL); + // This is implicitly done during deletion of the window, but we do it here so + // that we're in a known state. + if (window_.parent()) + window_.parent()->RemoveChild(&window_); +} + +const Node* Node::GetParent() const { + if (!window_.parent()) + return NULL; + return window_.parent()->GetProperty(kNodeKey); +} + +void Node::Add(Node* child) { + window_.AddChild(&child->window_); +} + +void Node::Remove(Node* child) { + window_.RemoveChild(&child->window_); +} + +void Node::Reorder(Node* child, Node* relative, OrderDirection direction) { + if (direction == ORDER_ABOVE) + window_.StackChildAbove(child->window(), relative->window()); + else if (direction == ORDER_BELOW) + window_.StackChildBelow(child->window(), relative->window()); +} + +const Node* Node::GetRoot() const { + const aura::Window* window = &window_; + while (window && window->parent()) + window = window->parent(); + return window->GetProperty(kNodeKey); +} + +std::vector<const Node*> Node::GetChildren() const { + std::vector<const Node*> children; + children.reserve(window_.children().size()); + for (size_t i = 0; i < window_.children().size(); ++i) + children.push_back(window_.children()[i]->GetProperty(kNodeKey)); + return children; +} + +std::vector<Node*> Node::GetChildren() { + std::vector<Node*> children; + children.reserve(window_.children().size()); + for (size_t i = 0; i < window_.children().size(); ++i) + children.push_back(window_.children()[i]->GetProperty(kNodeKey)); + return children; +} + +bool Node::Contains(const Node* node) const { + return node && window_.Contains(&(node->window_)); +} + +void Node::SetView(View* view) { + if (view == view_) + return; + + // Detach view from existing node. This way notifications are sent out. + if (view && view->node()) + view->node()->SetView(NULL); + + View* old_view = view_; + if (view_) + view_->set_node(NULL); + view_ = view; + if (view) + view->set_node(this); + delegate_->OnNodeViewReplaced(this, view, old_view); +} + +void Node::OnWindowHierarchyChanged( + const aura::WindowObserver::HierarchyChangeParams& params) { + if (params.target != &window_ || params.receiver != &window_) + return; + const Node* new_parent = params.new_parent ? + params.new_parent->GetProperty(kNodeKey) : NULL; + const Node* old_parent = params.old_parent ? + params.old_parent->GetProperty(kNodeKey) : NULL; + delegate_->OnNodeHierarchyChanged(this, new_parent, old_parent); +} + +gfx::Size Node::GetMinimumSize() const { + return gfx::Size(); +} + +gfx::Size Node::GetMaximumSize() const { + return gfx::Size(); +} + +void Node::OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { +} + +gfx::NativeCursor Node::GetCursor(const gfx::Point& point) { + return gfx::kNullCursor; +} + +int Node::GetNonClientComponent(const gfx::Point& point) const { + return HTCAPTION; +} + +bool Node::ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) { + return true; +} + +bool Node::CanFocus() { + return true; +} + +void Node::OnCaptureLost() { +} + +void Node::OnPaint(gfx::Canvas* canvas) { + if (view_) { + canvas->DrawImageInt( + gfx::ImageSkia::CreateFrom1xBitmap(view_->bitmap()), 0, 0); + } +} + +void Node::OnDeviceScaleFactorChanged(float device_scale_factor) { +} + +void Node::OnWindowDestroying(aura::Window* window) { +} + +void Node::OnWindowDestroyed(aura::Window* window) { +} + +void Node::OnWindowTargetVisibilityChanged(bool visible) { +} + +bool Node::HasHitTestMask() const { + return false; +} + +void Node::GetHitTestMask(gfx::Path* mask) const { +} + +void Node::OnEvent(ui::Event* event) { + if (view_) + delegate_->OnViewInputEvent(view_, event); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/node.h b/chromium/mojo/services/view_manager/node.h new file mode 100644 index 00000000000..bfee6752378 --- /dev/null +++ b/chromium/mojo/services/view_manager/node.h @@ -0,0 +1,112 @@ +// 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_SERVICES_VIEW_MANAGER_NODE_H_ +#define MOJO_SERVICES_VIEW_MANAGER_NODE_H_ + +#include <vector> + +#include "base/logging.h" +#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" +#include "mojo/services/view_manager/ids.h" +#include "mojo/services/view_manager/view_manager_export.h" +#include "ui/aura/window.h" +#include "ui/aura/window_delegate.h" +#include "ui/aura/window_observer.h" + +namespace mojo { +namespace view_manager { +namespace service { + +class NodeDelegate; +class View; + +// Represents a node in the graph. Delegate is informed of interesting events. +class MOJO_VIEW_MANAGER_EXPORT Node + : public aura::WindowObserver, + public aura::WindowDelegate { + public: + Node(NodeDelegate* delegate, const NodeId& id); + virtual ~Node(); + + void set_view_id(const ViewId& view_id) { view_id_ = view_id; } + const ViewId& view_id() const { return view_id_; } + + const NodeId& id() const { return id_; } + + void Add(Node* child); + void Remove(Node* child); + + void Reorder(Node* child, Node* relative, OrderDirection direction); + + aura::Window* window() { return &window_; } + + const gfx::Rect& bounds() const { return window_.bounds(); } + + const Node* GetParent() const; + Node* GetParent() { + return const_cast<Node*>(const_cast<const Node*>(this)->GetParent()); + } + + const Node* GetRoot() const; + Node* GetRoot() { + return const_cast<Node*>(const_cast<const Node*>(this)->GetRoot()); + } + + std::vector<const Node*> GetChildren() const; + std::vector<Node*> GetChildren(); + + bool Contains(const Node* node) const; + + // Sets the view associated with this node. Node does not own its View. + void SetView(View* view); + View* view() { return view_; } + const View* view() const { return view_; } + + private: + // WindowObserver overrides: + virtual void OnWindowHierarchyChanged( + const aura::WindowObserver::HierarchyChangeParams& params) OVERRIDE; + + // WindowDelegate overrides: + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; + virtual void OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE; + virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE; + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE; + virtual bool ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) OVERRIDE; + virtual bool CanFocus() OVERRIDE; + virtual void OnCaptureLost() OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; + virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; + virtual bool HasHitTestMask() const OVERRIDE; + virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; + + // ui::EventHandler overrides: + virtual void OnEvent(ui::Event* event) OVERRIDE; + + NodeDelegate* delegate_; + const NodeId id_; + + // Weak pointer to view associated with this node. + View* view_; + + ViewId view_id_; + + aura::Window window_; + + DISALLOW_COPY_AND_ASSIGN(Node); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_NODE_H_ diff --git a/chromium/mojo/services/view_manager/node_delegate.h b/chromium/mojo/services/view_manager/node_delegate.h new file mode 100644 index 00000000000..d63acc91998 --- /dev/null +++ b/chromium/mojo/services/view_manager/node_delegate.h @@ -0,0 +1,45 @@ +// 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_SERVICES_VIEW_MANAGER_NODE_DELEGATE_H_ +#define MOJO_SERVICES_VIEW_MANAGER_NODE_DELEGATE_H_ + +#include "mojo/services/view_manager/view_manager_export.h" + +namespace ui { +class Event; +} + +namespace mojo { +namespace view_manager { +namespace service { + +class Node; +class View; + +class MOJO_VIEW_MANAGER_EXPORT NodeDelegate { + public: + // Invoked when the hierarchy has changed. + virtual void OnNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent) = 0; + + // Invoked when the View associated with a node changes. + virtual void OnNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view) = 0; + + // Invoked when an input event is received by the View at this node. + virtual void OnViewInputEvent(const View* view, + const ui::Event* event) = 0; + + protected: + virtual ~NodeDelegate() {} +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_NODE_DELEGATE_H_ diff --git a/chromium/mojo/services/view_manager/root_node_manager.cc b/chromium/mojo/services/view_manager/root_node_manager.cc new file mode 100644 index 00000000000..f5c681fc7a1 --- /dev/null +++ b/chromium/mojo/services/view_manager/root_node_manager.cc @@ -0,0 +1,265 @@ +// 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/root_node_manager.h" + +#include "base/logging.h" +#include "mojo/public/interfaces/service_provider/service_provider.mojom.h" +#include "mojo/services/public/cpp/input_events/input_events_type_converters.h" +#include "mojo/services/view_manager/view.h" +#include "mojo/services/view_manager/view_manager_service_impl.h" +#include "ui/aura/env.h" + +namespace mojo { +namespace view_manager { +namespace service { + +RootNodeManager::ScopedChange::ScopedChange( + ViewManagerServiceImpl* connection, + RootNodeManager* root, + RootNodeManager::ChangeType change_type, + bool is_delete_node) + : root_(root), + connection_id_(connection->id()), + change_type_(change_type), + is_delete_node_(is_delete_node) { + root_->PrepareForChange(this); +} + +RootNodeManager::ScopedChange::~ScopedChange() { + root_->FinishChange(); +} + +RootNodeManager::Context::Context() { + // Pass in false as native viewport creates the PlatformEventSource. + aura::Env::CreateInstance(false); +} + +RootNodeManager::Context::~Context() { + aura::Env::DeleteInstance(); +} + +RootNodeManager::RootNodeManager(ServiceProvider* service_provider, + RootViewManagerDelegate* view_manager_delegate) + : service_provider_(service_provider), + next_connection_id_(1), + next_server_change_id_(1), + root_view_manager_(service_provider, this, view_manager_delegate), + root_(this, RootNodeId()), + current_change_(NULL) { +} + +RootNodeManager::~RootNodeManager() { + while (!connections_created_by_connect_.empty()) + delete *(connections_created_by_connect_.begin()); + // All the connections should have been destroyed. + DCHECK(connection_map_.empty()); +} + +ConnectionSpecificId RootNodeManager::GetAndAdvanceNextConnectionId() { + const ConnectionSpecificId id = next_connection_id_++; + DCHECK_LT(id, next_connection_id_); + return id; +} + +void RootNodeManager::AddConnection(ViewManagerServiceImpl* connection) { + DCHECK_EQ(0u, connection_map_.count(connection->id())); + connection_map_[connection->id()] = connection; +} + +void RootNodeManager::RemoveConnection(ViewManagerServiceImpl* connection) { + connection_map_.erase(connection->id()); + connections_created_by_connect_.erase(connection); + + // Notify remaining connections so that they can cleanup. + for (ConnectionMap::const_iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->OnViewManagerServiceImplDestroyed(connection->id()); + } +} + +void RootNodeManager::EmbedRoot(const std::string& url) { + CHECK(connection_map_.empty()); + Array<Id> roots(0); + EmbedImpl(kRootConnection, String::From(url), roots); +} + +void RootNodeManager::Embed(ConnectionSpecificId creator_id, + const String& url, + const Array<Id>& node_ids) { + CHECK_GT(node_ids.size(), 0u); + EmbedImpl(creator_id, url, node_ids)->set_delete_on_connection_error(); +} + +ViewManagerServiceImpl* RootNodeManager::GetConnection( + ConnectionSpecificId connection_id) { + ConnectionMap::iterator i = connection_map_.find(connection_id); + return i == connection_map_.end() ? NULL : i->second; +} + +Node* RootNodeManager::GetNode(const NodeId& id) { + if (id == root_.id()) + return &root_; + ConnectionMap::iterator i = connection_map_.find(id.connection_id); + return i == connection_map_.end() ? NULL : i->second->GetNode(id); +} + +View* RootNodeManager::GetView(const ViewId& id) { + ConnectionMap::iterator i = connection_map_.find(id.connection_id); + return i == connection_map_.end() ? NULL : i->second->GetView(id); +} + +void RootNodeManager::OnConnectionMessagedClient(ConnectionSpecificId id) { + if (current_change_) + current_change_->MarkConnectionAsMessaged(id); +} + +bool RootNodeManager::DidConnectionMessageClient( + ConnectionSpecificId id) const { + return current_change_ && current_change_->DidMessageConnection(id); +} + +ViewManagerServiceImpl* RootNodeManager::GetConnectionByCreator( + ConnectionSpecificId creator_id, + const std::string& url) const { + for (ConnectionMap::const_iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + if (i->second->creator_id() == creator_id && i->second->url() == url) + return i->second; + } + return NULL; +} + +void RootNodeManager::DispatchViewInputEventToWindowManager( + const View* view, + const ui::Event* event) { + // Input events are forwarded to the WindowManager. The WindowManager + // eventually calls back to us with DispatchOnViewInputEvent(). + ViewManagerServiceImpl* connection = GetConnection(kWindowManagerConnection); + if (!connection) + return; + connection->client()->DispatchOnViewInputEvent( + ViewIdToTransportId(view->id()), + TypeConverter<EventPtr, ui::Event>::ConvertFrom(*event)); +} + +void RootNodeManager::ProcessNodeBoundsChanged(const Node* node, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->ProcessNodeBoundsChanged(node, old_bounds, new_bounds, + IsChangeSource(i->first)); + } +} + +void RootNodeManager::ProcessNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent) { + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->ProcessNodeHierarchyChanged( + node, new_parent, old_parent, next_server_change_id_, + IsChangeSource(i->first)); + } +} + +void RootNodeManager::ProcessNodeReorder(const Node* node, + const Node* relative_node, + const OrderDirection direction) { + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->ProcessNodeReorder( + node, relative_node, direction, next_server_change_id_, + IsChangeSource(i->first)); + } +} + +void RootNodeManager::ProcessNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view) { + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->ProcessNodeViewReplaced(node, new_view, old_view, + IsChangeSource(i->first)); + } +} + +void RootNodeManager::ProcessNodeDeleted(const NodeId& node) { + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->ProcessNodeDeleted(node, next_server_change_id_, + IsChangeSource(i->first)); + } +} + +void RootNodeManager::ProcessViewDeleted(const ViewId& view) { + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + i->second->ProcessViewDeleted(view, IsChangeSource(i->first)); + } +} + +void RootNodeManager::PrepareForChange(ScopedChange* change) { + // Should only ever have one change in flight. + CHECK(!current_change_); + current_change_ = change; +} + +void RootNodeManager::FinishChange() { + // PrepareForChange/FinishChange should be balanced. + CHECK(current_change_); + if (current_change_->change_type() == CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID) + next_server_change_id_++; + current_change_ = NULL; +} + +ViewManagerServiceImpl* RootNodeManager::EmbedImpl( + const ConnectionSpecificId creator_id, + const String& url, + const Array<Id>& node_ids) { + MessagePipe pipe; + service_provider_->ConnectToService( + url, + ViewManagerServiceImpl::Client::Name_, + pipe.handle1.Pass(), + String()); + + std::string creator_url; + ConnectionMap::const_iterator it = connection_map_.find(creator_id); + if (it != connection_map_.end()) + creator_url = it->second->url(); + + ViewManagerServiceImpl* connection = + new ViewManagerServiceImpl(this, + creator_id, + creator_url, + url.To<std::string>()); + connection->SetRoots(node_ids); + BindToPipe(connection, pipe.handle0.Pass()); + connections_created_by_connect_.insert(connection); + return connection; +} + +void RootNodeManager::OnNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent) { + if (!root_view_manager_.in_setup()) + ProcessNodeHierarchyChanged(node, new_parent, old_parent); +} + +void RootNodeManager::OnNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view) { + ProcessNodeViewReplaced(node, new_view, old_view); +} + +void RootNodeManager::OnViewInputEvent(const View* view, + const ui::Event* event) { + DispatchViewInputEventToWindowManager(view, event); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/root_node_manager.h b/chromium/mojo/services/view_manager/root_node_manager.h new file mode 100644 index 00000000000..dba45d922c9 --- /dev/null +++ b/chromium/mojo/services/view_manager/root_node_manager.h @@ -0,0 +1,224 @@ +// 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_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_ +#define MOJO_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_ + +#include <map> +#include <set> + +#include "base/basictypes.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/services/view_manager/ids.h" +#include "mojo/services/view_manager/node.h" +#include "mojo/services/view_manager/node_delegate.h" +#include "mojo/services/view_manager/root_view_manager.h" +#include "mojo/services/view_manager/view_manager_export.h" + +namespace ui { +class Event; +} + +namespace mojo { + +class ServiceProvider; + +namespace view_manager { +namespace service { + +class RootViewManagerDelegate; +class View; +class ViewManagerServiceImpl; + +// RootNodeManager is responsible for managing the set of +// ViewManagerServiceImpls as well as providing the root of the node hierarchy. +class MOJO_VIEW_MANAGER_EXPORT RootNodeManager : public NodeDelegate { + public: + // Used to indicate if the server id should be incremented after notifiying + // clients of the change. + enum ChangeType { + CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, + CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, + }; + + // Create when a ViewManagerServiceImpl is about to make a change. Ensures + // clients are notified of the correct change id. + class ScopedChange { + public: + ScopedChange(ViewManagerServiceImpl* connection, + RootNodeManager* root, + RootNodeManager::ChangeType change_type, + bool is_delete_node); + ~ScopedChange(); + + ConnectionSpecificId connection_id() const { return connection_id_; } + ChangeType change_type() const { return change_type_; } + bool is_delete_node() const { return is_delete_node_; } + + // Marks the connection with the specified id as having seen a message. + void MarkConnectionAsMessaged(ConnectionSpecificId connection_id) { + message_ids_.insert(connection_id); + } + + // Returns true if MarkConnectionAsMessaged(connection_id) was invoked. + bool DidMessageConnection(ConnectionSpecificId connection_id) const { + return message_ids_.count(connection_id) > 0; + } + + private: + RootNodeManager* root_; + const ConnectionSpecificId connection_id_; + const ChangeType change_type_; + const bool is_delete_node_; + + // See description of MarkConnectionAsMessaged/DidMessageConnection. + std::set<ConnectionSpecificId> message_ids_; + + DISALLOW_COPY_AND_ASSIGN(ScopedChange); + }; + + RootNodeManager(ServiceProvider* service_provider, + RootViewManagerDelegate* view_manager_delegate); + virtual ~RootNodeManager(); + + // Returns the id for the next ViewManagerServiceImpl. + ConnectionSpecificId GetAndAdvanceNextConnectionId(); + + Id next_server_change_id() const { + return next_server_change_id_; + } + + void AddConnection(ViewManagerServiceImpl* connection); + void RemoveConnection(ViewManagerServiceImpl* connection); + + // Establishes the initial client. Similar to Connect(), but the resulting + // client is allowed to do anything. + void EmbedRoot(const std::string& url); + + // See description of ViewManagerService::Embed() for details. This assumes + // |node_ids| has been validated. + void Embed(ConnectionSpecificId creator_id, + const String& url, + const Array<Id>& node_ids); + + // Returns the connection by id. + ViewManagerServiceImpl* GetConnection(ConnectionSpecificId connection_id); + + // Returns the Node identified by |id|. + Node* GetNode(const NodeId& id); + + // Returns the View identified by |id|. + View* GetView(const ViewId& id); + + Node* root() { return &root_; } + + bool IsProcessingChange() const { return current_change_ != NULL; } + + bool is_processing_delete_node() const { + return current_change_ && current_change_->is_delete_node(); } + + // Invoked when a connection messages a client about the change. This is used + // to avoid sending ServerChangeIdAdvanced() unnecessarily. + void OnConnectionMessagedClient(ConnectionSpecificId id); + + // Returns true if OnConnectionMessagedClient() was invoked for id. + bool DidConnectionMessageClient(ConnectionSpecificId id) const; + + ViewManagerServiceImpl* GetConnectionByCreator( + ConnectionSpecificId creator_id, + const std::string& url) const; + + void DispatchViewInputEventToWindowManager(const View* view, + const ui::Event* event); + + // These functions trivially delegate to all ViewManagerServiceImpls, which in + // term notify their clients. + void ProcessNodeBoundsChanged(const Node* node, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds); + void ProcessNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent); + void ProcessNodeReorder(const Node* node, + const Node* relative_node, + const OrderDirection direction); + void ProcessNodeViewReplaced(const Node* node, + const View* new_view_id, + const View* old_view_id); + void ProcessNodeDeleted(const NodeId& node); + void ProcessViewDeleted(const ViewId& view); + + private: + // Used to setup any static state needed by RootNodeManager. + struct Context { + Context(); + ~Context(); + }; + + typedef std::map<ConnectionSpecificId, ViewManagerServiceImpl*> ConnectionMap; + + // Invoked when a connection is about to make a change. Subsequently followed + // by FinishChange() once the change is done. + // + // Changes should never nest, meaning each PrepareForChange() must be + // balanced with a call to FinishChange() with no PrepareForChange() + // in between. + void PrepareForChange(ScopedChange* change); + + // Balances a call to PrepareForChange(). + void FinishChange(); + + // Returns true if the specified connection originated the current change. + bool IsChangeSource(ConnectionSpecificId connection_id) const { + return current_change_ && current_change_->connection_id() == connection_id; + } + + // Implementation of the two embed variants. + ViewManagerServiceImpl* EmbedImpl(ConnectionSpecificId creator_id, + const String& url, + const Array<Id>& node_ids); + + // Overridden from NodeDelegate: + virtual void OnNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent) OVERRIDE; + virtual void OnNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view) OVERRIDE; + virtual void OnViewInputEvent(const View* view, + const ui::Event* event) OVERRIDE; + + Context context_; + + ServiceProvider* service_provider_; + + // ID to use for next ViewManagerServiceImpl. + ConnectionSpecificId next_connection_id_; + + Id next_server_change_id_; + + // Set of ViewManagerServiceImpls. + ConnectionMap connection_map_; + + RootViewManager root_view_manager_; + + // Root node. + Node root_; + + // Set of ViewManagerServiceImpls created by way of Connect(). These have to + // be explicitly destroyed. + std::set<ViewManagerServiceImpl*> connections_created_by_connect_; + + // If non-null we're processing a change. The ScopedChange is not owned by us + // (it's created on the stack by ViewManagerServiceImpl). + ScopedChange* current_change_; + + DISALLOW_COPY_AND_ASSIGN(RootNodeManager); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_ diff --git a/chromium/mojo/services/view_manager/root_view_manager.cc b/chromium/mojo/services/view_manager/root_view_manager.cc new file mode 100644 index 00000000000..0bcdbfbb20e --- /dev/null +++ b/chromium/mojo/services/view_manager/root_view_manager.cc @@ -0,0 +1,159 @@ +// 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/root_view_manager.h" + +#include "base/auto_reset.h" +#include "base/scoped_observer.h" +#include "mojo/public/cpp/application/connect.h" +#include "mojo/services/view_manager/root_node_manager.h" +#include "mojo/services/view_manager/root_view_manager_delegate.h" +#include "mojo/services/view_manager/screen_impl.h" +#include "mojo/services/view_manager/window_tree_host_impl.h" +#include "ui/aura/client/default_capture_client.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/aura/client/focus_client.h" +#include "ui/aura/client/window_tree_client.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" + +namespace mojo { +namespace view_manager { +namespace service { + +// TODO(sky): revisit this, we may need a more sophisticated FocusClient +// implementation. +class FocusClientImpl : public aura::client::FocusClient, + public aura::WindowObserver { + public: + FocusClientImpl() + : focused_window_(NULL), + observer_manager_(this) { + } + virtual ~FocusClientImpl() {} + + private: + // Overridden from aura::client::FocusClient: + virtual void AddObserver(aura::client::FocusChangeObserver* observer) + OVERRIDE { + } + virtual void RemoveObserver(aura::client::FocusChangeObserver* observer) + OVERRIDE { + } + virtual void FocusWindow(aura::Window* window) OVERRIDE { + if (window && !window->CanFocus()) + return; + if (focused_window_) + observer_manager_.Remove(focused_window_); + aura::Window* old_focused_window = focused_window_; + focused_window_ = window; + if (focused_window_) + observer_manager_.Add(focused_window_); + + aura::client::FocusChangeObserver* observer = + aura::client::GetFocusChangeObserver(old_focused_window); + if (observer) + observer->OnWindowFocused(focused_window_, old_focused_window); + observer = aura::client::GetFocusChangeObserver(focused_window_); + if (observer) + observer->OnWindowFocused(focused_window_, old_focused_window); + } + virtual void ResetFocusWithinActiveWindow(aura::Window* window) OVERRIDE { + if (!window->Contains(focused_window_)) + FocusWindow(window); + } + virtual aura::Window* GetFocusedWindow() OVERRIDE { + return focused_window_; + } + + // Overridden from WindowObserver: + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { + DCHECK_EQ(window, focused_window_); + FocusWindow(NULL); + } + + aura::Window* focused_window_; + ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_; + + DISALLOW_COPY_AND_ASSIGN(FocusClientImpl); +}; + +class WindowTreeClientImpl : public aura::client::WindowTreeClient { + public: + explicit WindowTreeClientImpl(aura::Window* window) : window_(window) { + aura::client::SetWindowTreeClient(window_, this); + } + + virtual ~WindowTreeClientImpl() { + aura::client::SetWindowTreeClient(window_, NULL); + } + + // Overridden from aura::client::WindowTreeClient: + virtual aura::Window* GetDefaultParent(aura::Window* context, + aura::Window* window, + const gfx::Rect& bounds) OVERRIDE { + if (!capture_client_) { + capture_client_.reset( + new aura::client::DefaultCaptureClient(window_->GetRootWindow())); + } + return window_; + } + + private: + aura::Window* window_; + + scoped_ptr<aura::client::DefaultCaptureClient> capture_client_; + + DISALLOW_COPY_AND_ASSIGN(WindowTreeClientImpl); +}; + +RootViewManager::RootViewManager(ServiceProvider* service_provider, + RootNodeManager* root_node, + RootViewManagerDelegate* delegate) + : delegate_(delegate), + root_node_manager_(root_node), + in_setup_(false) { + screen_.reset(ScreenImpl::Create()); + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get()); + NativeViewportPtr viewport; + ConnectToService(service_provider, + "mojo:mojo_native_viewport_service", + &viewport); + window_tree_host_.reset(new WindowTreeHostImpl( + viewport.Pass(), + gfx::Rect(800, 600), + base::Bind(&RootViewManager::OnCompositorCreated, + base::Unretained(this)))); +} + +RootViewManager::~RootViewManager() { + window_tree_client_.reset(); + window_tree_host_.reset(); + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, NULL); +} + +void RootViewManager::OnCompositorCreated() { + base::AutoReset<bool> resetter(&in_setup_, true); + window_tree_host_->InitHost(); + + aura::Window* root = root_node_manager_->root()->window(); + window_tree_host_->window()->AddChild(root); + root->SetBounds(gfx::Rect(window_tree_host_->window()->bounds().size())); + root_node_manager_->root()->window()->Show(); + + window_tree_client_.reset( + new WindowTreeClientImpl(window_tree_host_->window())); + + focus_client_.reset(new FocusClientImpl()); + aura::client::SetFocusClient(window_tree_host_->window(), + focus_client_.get()); + + window_tree_host_->Show(); + + delegate_->OnRootViewManagerWindowTreeHostCreated(); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/root_view_manager.h b/chromium/mojo/services/view_manager/root_view_manager.h new file mode 100644 index 00000000000..cfec2291f95 --- /dev/null +++ b/chromium/mojo/services/view_manager/root_view_manager.h @@ -0,0 +1,72 @@ +// 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_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_ +#define MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/public/cpp/gles2/gles2.h" +#include "mojo/services/view_manager/view_manager_export.h" + +namespace aura { +namespace client { +class FocusClient; +class WindowTreeClient; +} +class WindowTreeHost; +} + +namespace gfx { +class Screen; +} + +namespace mojo { + +class ServiceProvider; + +namespace view_manager { +namespace service { + +class RootNodeManager; +class RootViewManagerDelegate; + +// RootViewManager binds the root node to an actual display. +class MOJO_VIEW_MANAGER_EXPORT RootViewManager { + public: + RootViewManager(ServiceProvider* service_provider, + RootNodeManager* root_node, + RootViewManagerDelegate* delegate); + virtual ~RootViewManager(); + + // See description above field for details. + bool in_setup() const { return in_setup_; } + + private: + void OnCompositorCreated(); + + RootViewManagerDelegate* delegate_; + + RootNodeManager* root_node_manager_; + + GLES2Initializer gles_initializer_; + + // Returns true if adding the root node's window to |window_tree_host_|. + bool in_setup_; + + scoped_ptr<gfx::Screen> screen_; + scoped_ptr<aura::WindowTreeHost> window_tree_host_; + scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; + scoped_ptr<aura::client::FocusClient> focus_client_; + + DISALLOW_COPY_AND_ASSIGN(RootViewManager); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_ diff --git a/chromium/mojo/services/view_manager/root_view_manager_delegate.h b/chromium/mojo/services/view_manager/root_view_manager_delegate.h new file mode 100644 index 00000000000..2920f51c0d0 --- /dev/null +++ b/chromium/mojo/services/view_manager/root_view_manager_delegate.h @@ -0,0 +1,27 @@ +// 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_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_DELEGATE_H_ +#define MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_DELEGATE_H_ + +#include "mojo/services/view_manager/view_manager_export.h" + +namespace mojo { +namespace view_manager { +namespace service { + +class MOJO_VIEW_MANAGER_EXPORT RootViewManagerDelegate { + public: + // Invoked when the WindowTreeHost is ready. + virtual void OnRootViewManagerWindowTreeHostCreated() = 0; + + protected: + virtual ~RootViewManagerDelegate() {} +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_ diff --git a/chromium/mojo/services/view_manager/screen_impl.cc b/chromium/mojo/services/view_manager/screen_impl.cc new file mode 100644 index 00000000000..6339626af97 --- /dev/null +++ b/chromium/mojo/services/view_manager/screen_impl.cc @@ -0,0 +1,80 @@ +// 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/screen_impl.h" + +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/rect_conversions.h" + +namespace mojo { +namespace view_manager { +namespace service { + +// static +gfx::Screen* ScreenImpl::Create() { + return new ScreenImpl(gfx::Rect(0, 0, 800, 600)); +} + +ScreenImpl::~ScreenImpl() { +} + +bool ScreenImpl::IsDIPEnabled() { + NOTIMPLEMENTED(); + return true; +} + +gfx::Point ScreenImpl::GetCursorScreenPoint() { + NOTIMPLEMENTED(); + return gfx::Point(); +} + +gfx::NativeWindow ScreenImpl::GetWindowUnderCursor() { + return GetWindowAtScreenPoint(GetCursorScreenPoint()); +} + +gfx::NativeWindow ScreenImpl::GetWindowAtScreenPoint(const gfx::Point& point) { + NOTIMPLEMENTED(); + return NULL; +} + +int ScreenImpl::GetNumDisplays() const { + return 1; +} + +std::vector<gfx::Display> ScreenImpl::GetAllDisplays() const { + return std::vector<gfx::Display>(1, display_); +} + +gfx::Display ScreenImpl::GetDisplayNearestWindow( + gfx::NativeWindow window) const { + return display_; +} + +gfx::Display ScreenImpl::GetDisplayNearestPoint(const gfx::Point& point) const { + return display_; +} + +gfx::Display ScreenImpl::GetDisplayMatching(const gfx::Rect& match_rect) const { + return display_; +} + +gfx::Display ScreenImpl::GetPrimaryDisplay() const { + return display_; +} + +void ScreenImpl::AddObserver(gfx::DisplayObserver* observer) { +} + +void ScreenImpl::RemoveObserver(gfx::DisplayObserver* observer) { +} + +ScreenImpl::ScreenImpl(const gfx::Rect& screen_bounds) { + static int64 synthesized_display_id = 2000; + display_.set_id(synthesized_display_id++); + display_.SetScaleAndBounds(1.0f, screen_bounds); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/screen_impl.h b/chromium/mojo/services/view_manager/screen_impl.h new file mode 100644 index 00000000000..08a6b04bb41 --- /dev/null +++ b/chromium/mojo/services/view_manager/screen_impl.h @@ -0,0 +1,58 @@ +// 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_SERVICES_VIEW_MANAGER_SCREEN_IMPL_H_ +#define MOJO_SERVICES_VIEW_MANAGER_SCREEN_IMPL_H_ + +#include "base/compiler_specific.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" + +namespace gfx { +class Rect; +class Transform; +} + +namespace mojo { +namespace view_manager { +namespace service { + +// A minimal implementation of gfx::Screen for the view manager. +class ScreenImpl : public gfx::Screen { + public: + static gfx::Screen* Create(); + virtual ~ScreenImpl(); + + protected: + // gfx::Screen overrides: + virtual bool IsDIPEnabled() OVERRIDE; + virtual gfx::Point GetCursorScreenPoint() OVERRIDE; + virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE; + virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) + OVERRIDE; + virtual int GetNumDisplays() const OVERRIDE; + virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE; + virtual gfx::Display GetDisplayNearestWindow( + gfx::NativeView view) const OVERRIDE; + virtual gfx::Display GetDisplayNearestPoint( + const gfx::Point& point) const OVERRIDE; + virtual gfx::Display GetDisplayMatching( + const gfx::Rect& match_rect) const OVERRIDE; + virtual gfx::Display GetPrimaryDisplay() const OVERRIDE; + virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE; + virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE; + + private: + explicit ScreenImpl(const gfx::Rect& screen_bounds); + + gfx::Display display_; + + DISALLOW_COPY_AND_ASSIGN(ScreenImpl); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_SCREEN_IMPL_H_ diff --git a/chromium/mojo/services/view_manager/test_change_tracker.cc b/chromium/mojo/services/view_manager/test_change_tracker.cc new file mode 100644 index 00000000000..89e7fbb4445 --- /dev/null +++ b/chromium/mojo/services/view_manager/test_change_tracker.cc @@ -0,0 +1,263 @@ +// 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/test_change_tracker.h" + +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "mojo/common/common_type_converters.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/public/cpp/view_manager/util.h" + +namespace mojo { +namespace view_manager { +namespace service { + +std::string NodeIdToString(Id id) { + return (id == 0) ? "null" : + base::StringPrintf("%d,%d", HiWord(id), LoWord(id)); +} + +namespace { + +std::string RectToString(const gfx::Rect& rect) { + return base::StringPrintf("%d,%d %dx%d", rect.x(), rect.y(), rect.width(), + rect.height()); +} + +std::string DirectionToString(OrderDirection direction) { + return direction == ORDER_ABOVE ? "above" : "below"; +} + +std::string ChangeToDescription1(const Change& change) { + switch (change.type) { + case CHANGE_TYPE_CONNECTION_ESTABLISHED: + return base::StringPrintf("OnConnectionEstablished creator=%s", + change.creator_url.data()); + + case CHANGE_TYPE_ROOTS_ADDED: + return "OnRootsAdded"; + + case CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED: + return base::StringPrintf( + "ServerChangeIdAdvanced %d", static_cast<int>(change.change_id)); + + + case CHANGE_TYPE_NODE_BOUNDS_CHANGED: + return base::StringPrintf( + "BoundsChanged node=%s old_bounds=%s new_bounds=%s", + NodeIdToString(change.node_id).c_str(), + RectToString(change.bounds).c_str(), + RectToString(change.bounds2).c_str()); + + case CHANGE_TYPE_NODE_HIERARCHY_CHANGED: + return base::StringPrintf( + "HierarchyChanged change_id=%d node=%s new_parent=%s old_parent=%s", + static_cast<int>(change.change_id), + NodeIdToString(change.node_id).c_str(), + NodeIdToString(change.node_id2).c_str(), + NodeIdToString(change.node_id3).c_str()); + + case CHANGE_TYPE_NODE_REORDERED: + return base::StringPrintf( + "Reordered change_id=%d node=%s relative=%s direction=%s", + static_cast<int>(change.change_id), + NodeIdToString(change.node_id).c_str(), + NodeIdToString(change.node_id2).c_str(), + DirectionToString(change.direction).c_str()); + + case CHANGE_TYPE_NODE_DELETED: + return base::StringPrintf("NodeDeleted change_id=%d node=%s", + static_cast<int>(change.change_id), + NodeIdToString(change.node_id).c_str()); + + case CHANGE_TYPE_VIEW_DELETED: + return base::StringPrintf("ViewDeleted view=%s", + NodeIdToString(change.view_id).c_str()); + + case CHANGE_TYPE_VIEW_REPLACED: + return base::StringPrintf( + "ViewReplaced node=%s new_view=%s old_view=%s", + NodeIdToString(change.node_id).c_str(), + NodeIdToString(change.view_id).c_str(), + NodeIdToString(change.view_id2).c_str()); + + case CHANGE_TYPE_INPUT_EVENT: + return base::StringPrintf( + "InputEvent view=%s event_action=%d", + NodeIdToString(change.view_id).c_str(), + change.event_action); + } + return std::string(); +} + +} // namespace + +std::vector<std::string> ChangesToDescription1( + const std::vector<Change>& changes) { + std::vector<std::string> strings(changes.size()); + for (size_t i = 0; i < changes.size(); ++i) + strings[i] = ChangeToDescription1(changes[i]); + return strings; +} + +std::string ChangeNodeDescription(const std::vector<Change>& changes) { + if (changes.size() != 1) + return std::string(); + std::vector<std::string> node_strings(changes[0].nodes.size()); + for (size_t i = 0; i < changes[0].nodes.size(); ++i) + node_strings[i] = "[" + changes[0].nodes[i].ToString() + "]"; + return JoinString(node_strings, ','); +} + +void NodeDatasToTestNodes(const Array<NodeDataPtr>& data, + std::vector<TestNode>* test_nodes) { + for (size_t i = 0; i < data.size(); ++i) { + TestNode node; + node.parent_id = data[i]->parent_id; + node.node_id = data[i]->node_id; + node.view_id = data[i]->view_id; + test_nodes->push_back(node); + } +} + +Change::Change() + : type(CHANGE_TYPE_CONNECTION_ESTABLISHED), + connection_id(0), + change_id(0), + node_id(0), + node_id2(0), + node_id3(0), + view_id(0), + view_id2(0), + event_action(0), + direction(ORDER_ABOVE) {} + +Change::~Change() { +} + +TestChangeTracker::TestChangeTracker() + : delegate_(NULL) { +} + +TestChangeTracker::~TestChangeTracker() { +} + +void TestChangeTracker::OnViewManagerConnectionEstablished( + ConnectionSpecificId connection_id, + const String& creator_url, + Id next_server_change_id, + Array<NodeDataPtr> nodes) { + Change change; + change.type = CHANGE_TYPE_CONNECTION_ESTABLISHED; + change.connection_id = connection_id; + change.change_id = next_server_change_id; + change.creator_url = creator_url; + NodeDatasToTestNodes(nodes, &change.nodes); + AddChange(change); +} + +void TestChangeTracker::OnRootsAdded(Array<NodeDataPtr> nodes) { + Change change; + change.type = CHANGE_TYPE_ROOTS_ADDED; + NodeDatasToTestNodes(nodes, &change.nodes); + AddChange(change); +} + +void TestChangeTracker::OnServerChangeIdAdvanced(Id change_id) { + Change change; + change.type = CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED; + change.change_id = change_id; + AddChange(change); +} + +void TestChangeTracker::OnNodeBoundsChanged(Id node_id, + RectPtr old_bounds, + RectPtr new_bounds) { + Change change; + change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED; + change.node_id = node_id; + change.bounds = old_bounds.To<gfx::Rect>(); + change.bounds2 = new_bounds.To<gfx::Rect>(); + AddChange(change); +} + +void TestChangeTracker::OnNodeHierarchyChanged(Id node_id, + Id new_parent_id, + Id old_parent_id, + Id server_change_id, + Array<NodeDataPtr> nodes) { + Change change; + change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED; + change.node_id = node_id; + change.node_id2 = new_parent_id; + change.node_id3 = old_parent_id; + change.change_id = server_change_id; + NodeDatasToTestNodes(nodes, &change.nodes); + AddChange(change); +} + +void TestChangeTracker::OnNodeReordered(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id) { + Change change; + change.type = CHANGE_TYPE_NODE_REORDERED; + change.node_id = node_id; + change.node_id2 = relative_node_id; + change.direction = direction; + change.change_id = server_change_id; + AddChange(change); +} + +void TestChangeTracker::OnNodeDeleted(Id node_id, Id server_change_id) { + Change change; + change.type = CHANGE_TYPE_NODE_DELETED; + change.node_id = node_id; + change.change_id = server_change_id; + AddChange(change); +} + +void TestChangeTracker::OnViewDeleted(Id view_id) { + Change change; + change.type = CHANGE_TYPE_VIEW_DELETED; + change.view_id = view_id; + AddChange(change); +} + +void TestChangeTracker::OnNodeViewReplaced(Id node_id, + Id new_view_id, + Id old_view_id) { + Change change; + change.type = CHANGE_TYPE_VIEW_REPLACED; + change.node_id = node_id; + change.view_id = new_view_id; + change.view_id2 = old_view_id; + AddChange(change); +} + +void TestChangeTracker::OnViewInputEvent(Id view_id, EventPtr event) { + Change change; + change.type = CHANGE_TYPE_INPUT_EVENT; + change.view_id = view_id; + change.event_action = event->action; + AddChange(change); +} + +void TestChangeTracker::AddChange(const Change& change) { + changes_.push_back(change); + if (delegate_) + delegate_->OnChangeAdded(); +} + +std::string TestNode::ToString() const { + return base::StringPrintf("node=%s parent=%s view=%s", + NodeIdToString(node_id).c_str(), + NodeIdToString(parent_id).c_str(), + NodeIdToString(view_id).c_str()); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/test_change_tracker.h b/chromium/mojo/services/view_manager/test_change_tracker.h new file mode 100644 index 00000000000..5a5edf7d126 --- /dev/null +++ b/chromium/mojo/services/view_manager/test_change_tracker.h @@ -0,0 +1,134 @@ +// 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_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_ +#define MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_ + +#include <string> +#include <vector> + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" +#include "ui/gfx/rect.h" + +namespace mojo { +namespace view_manager { +namespace service { + +enum ChangeType { + CHANGE_TYPE_CONNECTION_ESTABLISHED, + CHANGE_TYPE_ROOTS_ADDED, + CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED, + CHANGE_TYPE_NODE_BOUNDS_CHANGED, + CHANGE_TYPE_NODE_HIERARCHY_CHANGED, + CHANGE_TYPE_NODE_REORDERED, + CHANGE_TYPE_NODE_DELETED, + CHANGE_TYPE_VIEW_DELETED, + CHANGE_TYPE_VIEW_REPLACED, + CHANGE_TYPE_INPUT_EVENT, +}; + +// TODO(sky): consider nuking and converting directly to NodeData. +struct TestNode { + // Returns a string description of this. + std::string ToString() const; + + Id parent_id; + Id node_id; + Id view_id; +}; + +// Tracks a call to ViewManagerClient. See the individual functions for the +// fields that are used. +struct Change { + Change(); + ~Change(); + + ChangeType type; + ConnectionSpecificId connection_id; + Id change_id; + std::vector<TestNode> nodes; + Id node_id; + Id node_id2; + Id node_id3; + Id view_id; + Id view_id2; + gfx::Rect bounds; + gfx::Rect bounds2; + int32 event_action; + String creator_url; + OrderDirection direction; +}; + +// Converts Changes to string descriptions. +std::vector<std::string> ChangesToDescription1( + const std::vector<Change>& changes); + +// Returns a string description of |changes[0].nodes|. Returns an empty string +// if change.size() != 1. +std::string ChangeNodeDescription(const std::vector<Change>& changes); + +// Converts NodeDatas to TestNodes. +void NodeDatasToTestNodes(const Array<NodeDataPtr>& data, + std::vector<TestNode>* test_nodes); + +// TestChangeTracker is used to record ViewManagerClient functions. It notifies +// a delegate any time a change is added. +class TestChangeTracker { + public: + // Used to notify the delegate when a change is added. A change corresponds to + // a single ViewManagerClient function. + class Delegate { + public: + virtual void OnChangeAdded() = 0; + + protected: + virtual ~Delegate() {} + }; + + TestChangeTracker(); + ~TestChangeTracker(); + + void set_delegate(Delegate* delegate) { delegate_ = delegate; } + + std::vector<Change>* changes() { return &changes_; } + + // Each of these functions generate a Change. There is one per + // ViewManagerClient function. + void OnViewManagerConnectionEstablished(ConnectionSpecificId connection_id, + const String& creator_url, + Id next_server_change_id, + Array<NodeDataPtr> nodes); + void OnRootsAdded(Array<NodeDataPtr> nodes); + void OnServerChangeIdAdvanced(Id change_id); + void OnNodeBoundsChanged(Id node_id, RectPtr old_bounds, RectPtr new_bounds); + void OnNodeHierarchyChanged(Id node_id, + Id new_parent_id, + Id old_parent_id, + Id server_change_id, + Array<NodeDataPtr> nodes); + void OnNodeReordered(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id); + void OnNodeDeleted(Id node_id, Id server_change_id); + void OnViewDeleted(Id view_id); + void OnNodeViewReplaced(Id node_id, Id new_view_id, Id old_view_id); + void OnViewInputEvent(Id view_id, EventPtr event); + + private: + void AddChange(const Change& change); + + Delegate* delegate_; + std::vector<Change> changes_; + + DISALLOW_COPY_AND_ASSIGN(TestChangeTracker); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_ diff --git a/chromium/mojo/services/view_manager/view.cc b/chromium/mojo/services/view_manager/view.cc new file mode 100644 index 00000000000..c33f5f847fc --- /dev/null +++ b/chromium/mojo/services/view_manager/view.cc @@ -0,0 +1,28 @@ +// 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/view.h" + +#include "mojo/services/view_manager/node.h" + +namespace mojo { +namespace view_manager { +namespace service { + +View::View(const ViewId& id) : id_(id), node_(NULL) {} + +View::~View() { +} + +void View::SetBitmap(const SkBitmap& bitmap) { + bitmap_ = bitmap; + if (node_) { + node_->window()->SchedulePaintInRect( + gfx::Rect(node_->window()->bounds().size())); + } +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/view.h b/chromium/mojo/services/view_manager/view.h new file mode 100644 index 00000000000..c4b0c0521b6 --- /dev/null +++ b/chromium/mojo/services/view_manager/view.h @@ -0,0 +1,50 @@ +// 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_SERVICES_VIEW_MANAGER_VIEW_H_ +#define MOJO_SERVICES_VIEW_MANAGER_VIEW_H_ + +#include <vector> + +#include "base/logging.h" +#include "mojo/services/view_manager/ids.h" +#include "mojo/services/view_manager/view_manager_export.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace mojo { +namespace view_manager { +namespace service { +class Node; + +// Represents a view. A view may be associated with a single Node. +class MOJO_VIEW_MANAGER_EXPORT View { + public: + explicit View(const ViewId& id); + ~View(); + + const ViewId& id() const { return id_; } + + Node* node() { return node_; } + + void SetBitmap(const SkBitmap& contents); + const SkBitmap& bitmap() const { return bitmap_; } + + private: + // Node is responsible for maintaining |node_|. + friend class Node; + + void set_node(Node* node) { node_ = node; } + + const ViewId id_; + Node* node_; + SkBitmap bitmap_; + + DISALLOW_COPY_AND_ASSIGN(View); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_H_ diff --git a/chromium/mojo/services/view_manager/view_manager_export.h b/chromium/mojo/services/view_manager/view_manager_export.h new file mode 100644 index 00000000000..29b0e6219c9 --- /dev/null +++ b/chromium/mojo/services/view_manager/view_manager_export.h @@ -0,0 +1,31 @@ +// 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_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_ +#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION) +#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllexport) +#else +#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION) +#define MOJO_VIEW_MANAGER_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_VIEW_MANAGER_EXPORT +#endif + +#endif // defined(WIN32) + +#else // defined(COMPONENT_BUILD) +#define MOJO_VIEW_MANAGER_EXPORT +#endif + +#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_ diff --git a/chromium/mojo/services/view_manager/view_manager_init_service_impl.cc b/chromium/mojo/services/view_manager/view_manager_init_service_impl.cc new file mode 100644 index 00000000000..be2ce15935b --- /dev/null +++ b/chromium/mojo/services/view_manager/view_manager_init_service_impl.cc @@ -0,0 +1,62 @@ +// 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/view_manager_init_service_impl.h" + +#include "mojo/public/interfaces/service_provider/service_provider.mojom.h" +#include "mojo/services/view_manager/ids.h" +#include "mojo/services/view_manager/view_manager_service_impl.h" + +namespace mojo { +namespace view_manager { +namespace service { + +ViewManagerInitServiceImpl::ConnectParams::ConnectParams() {} + +ViewManagerInitServiceImpl::ConnectParams::~ConnectParams() {} + +ViewManagerInitServiceImpl::ViewManagerInitServiceImpl( + ServiceProvider* service_provider) + : service_provider_(service_provider), + root_node_manager_(service_provider, this), + is_tree_host_ready_(false) { +} + +ViewManagerInitServiceImpl::~ViewManagerInitServiceImpl() { +} + +void ViewManagerInitServiceImpl::MaybeEmbedRoot( + const std::string& url, + const Callback<void(bool)>& callback) { + if (!is_tree_host_ready_) + return; + + root_node_manager_.EmbedRoot(url); + callback.Run(true); +} + +void ViewManagerInitServiceImpl::EmbedRoot( + const String& url, + const Callback<void(bool)>& callback) { + if (connect_params_.get()) { + DVLOG(1) << "Ignoring second connect"; + callback.Run(false); + return; + } + connect_params_.reset(new ConnectParams); + connect_params_->url = url.To<std::string>(); + connect_params_->callback = callback; + MaybeEmbedRoot(url.To<std::string>(), callback); +} + +void ViewManagerInitServiceImpl::OnRootViewManagerWindowTreeHostCreated() { + DCHECK(!is_tree_host_ready_); + is_tree_host_ready_ = true; + if (connect_params_.get()) + MaybeEmbedRoot(connect_params_->url, connect_params_->callback); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/view_manager_init_service_impl.h b/chromium/mojo/services/view_manager/view_manager_init_service_impl.h new file mode 100644 index 00000000000..59d438ef666 --- /dev/null +++ b/chromium/mojo/services/view_manager/view_manager_init_service_impl.h @@ -0,0 +1,79 @@ +// 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_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_ +#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" +#include "mojo/services/view_manager/root_node_manager.h" +#include "mojo/services/view_manager/root_view_manager_delegate.h" +#include "mojo/services/view_manager/view_manager_export.h" + +namespace mojo { + +class ServiceProvider; + +namespace view_manager { +namespace service { + +#if defined(OS_WIN) +// Equivalent of NON_EXPORTED_BASE which does not work with the template snafu +// below. +#pragma warning(push) +#pragma warning(disable : 4275) +#endif + +// Used to create the initial ViewManagerClient. Doesn't initiate the Connect() +// until the WindowTreeHost has been created. +class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceImpl + : public InterfaceImpl<ViewManagerInitService>, + public RootViewManagerDelegate { + public: + explicit ViewManagerInitServiceImpl(ServiceProvider* service_provider); + virtual ~ViewManagerInitServiceImpl(); + + private: + struct ConnectParams { + ConnectParams(); + ~ConnectParams(); + + std::string url; + Callback<void(bool)> callback; + }; + + void MaybeEmbedRoot(const std::string& url, + const Callback<void(bool)>& callback); + + // ViewManagerInitService overrides: + virtual void EmbedRoot(const String& url, + const Callback<void(bool)>& callback) OVERRIDE; + + // RootViewManagerDelegate overrides: + virtual void OnRootViewManagerWindowTreeHostCreated() OVERRIDE; + + ServiceProvider* service_provider_; + + RootNodeManager root_node_manager_; + + // Parameters passed to Connect(). If non-null Connect() has been invoked. + scoped_ptr<ConnectParams> connect_params_; + + bool is_tree_host_ready_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceImpl); +}; + +#if defined(OS_WIN) +#pragma warning(pop) +#endif + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_ diff --git a/chromium/mojo/services/view_manager/view_manager_service_impl.cc b/chromium/mojo/services/view_manager/view_manager_service_impl.cc new file mode 100644 index 00000000000..0af11c3f167 --- /dev/null +++ b/chromium/mojo/services/view_manager/view_manager_service_impl.cc @@ -0,0 +1,803 @@ +// 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/view_manager_service_impl.h" + +#include "base/bind.h" +#include "base/stl_util.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/public/cpp/input_events/input_events_type_converters.h" +#include "mojo/services/view_manager/node.h" +#include "mojo/services/view_manager/root_node_manager.h" +#include "mojo/services/view_manager/view.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/aura/window.h" +#include "ui/gfx/codec/png_codec.h" + +namespace mojo { +namespace view_manager { +namespace service { +namespace { + +// Places |node| in |nodes| and recurses through the children. +void GetDescendants(const Node* node, std::vector<const Node*>* nodes) { + if (!node) + return; + + nodes->push_back(node); + + std::vector<const Node*> children(node->GetChildren()); + for (size_t i = 0 ; i < children.size(); ++i) + GetDescendants(children[i], nodes); +} + +} // namespace + +ViewManagerServiceImpl::ViewManagerServiceImpl( + RootNodeManager* root_node_manager, + ConnectionSpecificId creator_id, + const std::string& creator_url, + const std::string& url) + : root_node_manager_(root_node_manager), + id_(root_node_manager_->GetAndAdvanceNextConnectionId()), + url_(url), + creator_id_(creator_id), + creator_url_(creator_url), + delete_on_connection_error_(false) { +} + +ViewManagerServiceImpl::~ViewManagerServiceImpl() { + // Delete any views we own. + while (!view_map_.empty()) { + bool result = DeleteViewImpl(this, view_map_.begin()->second->id()); + DCHECK(result); + } + + // We're about to destroy all our nodes. Detach any views from them. + for (NodeMap::iterator i = node_map_.begin(); i != node_map_.end(); ++i) { + if (i->second->view()) { + bool result = SetViewImpl(i->second, ViewId()); + DCHECK(result); + } + } + + if (!node_map_.empty()) { + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, true); + while (!node_map_.empty()) { + scoped_ptr<Node> node(node_map_.begin()->second); + Node* parent = node->GetParent(); + const NodeId node_id(node->id()); + if (parent) + parent->Remove(node.get()); + root_node_manager_->ProcessNodeDeleted(node_id); + node_map_.erase(NodeIdToTransportId(node_id)); + } + } + + root_node_manager_->RemoveConnection(this); +} + +const Node* ViewManagerServiceImpl::GetNode(const NodeId& id) const { + if (id_ == id.connection_id) { + NodeMap::const_iterator i = node_map_.find(id.node_id); + return i == node_map_.end() ? NULL : i->second; + } + return root_node_manager_->GetNode(id); +} + +const View* ViewManagerServiceImpl::GetView(const ViewId& id) const { + if (id_ == id.connection_id) { + ViewMap::const_iterator i = view_map_.find(id.view_id); + return i == view_map_.end() ? NULL : i->second; + } + return root_node_manager_->GetView(id); +} + +void ViewManagerServiceImpl::SetRoots(const Array<Id>& node_ids) { + DCHECK(roots_.empty()); + NodeIdSet roots; + for (size_t i = 0; i < node_ids.size(); ++i) { + DCHECK(GetNode(NodeIdFromTransportId(node_ids[i]))); + roots.insert(node_ids[i]); + } + roots_.swap(roots); +} + +void ViewManagerServiceImpl::OnViewManagerServiceImplDestroyed( + ConnectionSpecificId id) { + if (creator_id_ == id) + creator_id_ = kRootConnection; +} + +void ViewManagerServiceImpl::ProcessNodeBoundsChanged( + const Node* node, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + bool originated_change) { + if (originated_change) + return; + Id node_id = NodeIdToTransportId(node->id()); + if (known_nodes_.count(node_id) > 0) { + client()->OnNodeBoundsChanged(node_id, + Rect::From(old_bounds), + Rect::From(new_bounds)); + } +} + +void ViewManagerServiceImpl::ProcessNodeHierarchyChanged( + const Node* node, + const Node* new_parent, + const Node* old_parent, + Id server_change_id, + bool originated_change) { + if (known_nodes_.count(NodeIdToTransportId(node->id())) > 0) { + if (originated_change) + return; + if (node->id().connection_id != id_ && !IsNodeDescendantOfRoots(node)) { + // Node was a descendant of roots and is no longer, treat it as though the + // node was deleted. + RemoveFromKnown(node); + client()->OnNodeDeleted(NodeIdToTransportId(node->id()), + server_change_id); + root_node_manager_->OnConnectionMessagedClient(id_); + return; + } + } + + if (originated_change || root_node_manager_->is_processing_delete_node()) + return; + std::vector<const Node*> to_send; + if (!ShouldNotifyOnHierarchyChange(node, &new_parent, &old_parent, + &to_send)) { + if (root_node_manager_->IsProcessingChange()) { + client()->OnServerChangeIdAdvanced( + root_node_manager_->next_server_change_id() + 1); + } + return; + } + const NodeId new_parent_id(new_parent ? new_parent->id() : NodeId()); + const NodeId old_parent_id(old_parent ? old_parent->id() : NodeId()); + DCHECK((node->id().connection_id == id_) || + (roots_.count(NodeIdToTransportId(node->id())) > 0) || + (new_parent && IsNodeDescendantOfRoots(new_parent)) || + (old_parent && IsNodeDescendantOfRoots(old_parent))); + client()->OnNodeHierarchyChanged(NodeIdToTransportId(node->id()), + NodeIdToTransportId(new_parent_id), + NodeIdToTransportId(old_parent_id), + server_change_id, + NodesToNodeDatas(to_send)); +} + +void ViewManagerServiceImpl::ProcessNodeReorder(const Node* node, + const Node* relative_node, + OrderDirection direction, + Id server_change_id, + bool originated_change) { + if (originated_change || + !known_nodes_.count(NodeIdToTransportId(node->id())) || + !known_nodes_.count(NodeIdToTransportId(relative_node->id()))) { + return; + } + + client()->OnNodeReordered(NodeIdToTransportId(node->id()), + NodeIdToTransportId(relative_node->id()), + direction, + server_change_id); +} + +void ViewManagerServiceImpl::ProcessNodeViewReplaced( + const Node* node, + const View* new_view, + const View* old_view, + bool originated_change) { + if (originated_change || !known_nodes_.count(NodeIdToTransportId(node->id()))) + return; + const Id new_view_id = new_view ? + ViewIdToTransportId(new_view->id()) : 0; + const Id old_view_id = old_view ? + ViewIdToTransportId(old_view->id()) : 0; + client()->OnNodeViewReplaced(NodeIdToTransportId(node->id()), + new_view_id, old_view_id); +} + +void ViewManagerServiceImpl::ProcessNodeDeleted(const NodeId& node, + Id server_change_id, + bool originated_change) { + const bool in_known = known_nodes_.erase(NodeIdToTransportId(node)) > 0; + const bool in_roots = roots_.erase(NodeIdToTransportId(node)) > 0; + + if (in_roots && roots_.empty()) + roots_.insert(NodeIdToTransportId(InvalidNodeId())); + + if (originated_change) + return; + + if (in_known) { + client()->OnNodeDeleted(NodeIdToTransportId(node), server_change_id); + root_node_manager_->OnConnectionMessagedClient(id_); + } else if (root_node_manager_->IsProcessingChange() && + !root_node_manager_->DidConnectionMessageClient(id_)) { + client()->OnServerChangeIdAdvanced( + root_node_manager_->next_server_change_id() + 1); + root_node_manager_->OnConnectionMessagedClient(id_); + } +} + +void ViewManagerServiceImpl::ProcessViewDeleted(const ViewId& view, + bool originated_change) { + if (originated_change) + return; + client()->OnViewDeleted(ViewIdToTransportId(view)); +} + +void ViewManagerServiceImpl::OnConnectionError() { + if (delete_on_connection_error_) + delete this; +} + +bool ViewManagerServiceImpl::CanRemoveNodeFromParent(const Node* node) const { + if (!node) + return false; + + const Node* parent = node->GetParent(); + if (!parent) + return false; + + // Always allow the remove if there are no roots. Otherwise the remove is + // allowed if the parent is a descendant of the roots, or the node and its + // parent were created by this connection. We explicitly disallow removal of + // the node from its parent if the parent isn't visible to this connection + // (not in roots). + return (roots_.empty() || + (IsNodeDescendantOfRoots(parent) || + (node->id().connection_id == id_ && + parent->id().connection_id == id_))); +} + +bool ViewManagerServiceImpl::CanAddNode(const Node* parent, + const Node* child) const { + if (!parent || !child) + return false; // Both nodes must be valid. + + if (child->GetParent() == parent || child->Contains(parent)) + return false; // Would result in an invalid hierarchy. + + if (roots_.empty()) + return true; // No restriction if there are no roots. + + if (!IsNodeDescendantOfRoots(parent) && parent->id().connection_id != id_) + return false; // |parent| is not visible to this connection. + + // Allow the add if the child is already a descendant of the roots or was + // created by this connection. + return (IsNodeDescendantOfRoots(child) || child->id().connection_id == id_); +} + +bool ViewManagerServiceImpl::CanReorderNode(const Node* node, + const Node* relative_node, + OrderDirection direction) const { + if (!node || !relative_node) + return false; + + if (node->id().connection_id != id_) + return false; + + const Node* parent = node->GetParent(); + if (!parent || parent != relative_node->GetParent()) + return false; + + if (known_nodes_.count(NodeIdToTransportId(parent->id())) == 0) + return false; + + std::vector<const Node*> children = parent->GetChildren(); + const size_t child_i = + std::find(children.begin(), children.end(), node) - children.begin(); + const size_t target_i = + std::find(children.begin(), children.end(), relative_node) - + children.begin(); + if ((direction == ORDER_ABOVE && child_i == target_i + 1) || + (direction == ORDER_BELOW && child_i + 1 == target_i)) { + return false; + } + + return true; +} + +bool ViewManagerServiceImpl::CanDeleteNode(const NodeId& node_id) const { + return node_id.connection_id == id_; +} + +bool ViewManagerServiceImpl::CanDeleteView(const ViewId& view_id) const { + return view_id.connection_id == id_; +} + +bool ViewManagerServiceImpl::CanSetView(const Node* node, + const ViewId& view_id) const { + if (!node || !IsNodeDescendantOfRoots(node)) + return false; + + const View* view = GetView(view_id); + return (view && view_id.connection_id == id_) || view_id == ViewId(); +} + +bool ViewManagerServiceImpl::CanSetFocus(const Node* node) const { + // TODO(beng): security. + return true; +} + +bool ViewManagerServiceImpl::CanGetNodeTree(const Node* node) const { + return node && + (IsNodeDescendantOfRoots(node) || node->id().connection_id == id_); +} + +bool ViewManagerServiceImpl::CanEmbed( + const mojo::Array<uint32_t>& node_ids) const { + for (size_t i = 0; i < node_ids.size(); ++i) { + const Node* node = GetNode(NodeIdFromTransportId(node_ids[i])); + if (!node || node->id().connection_id != id_) + return false; + } + return node_ids.size() > 0; +} + +bool ViewManagerServiceImpl::DeleteNodeImpl(ViewManagerServiceImpl* source, + const NodeId& node_id) { + DCHECK_EQ(node_id.connection_id, id_); + Node* node = GetNode(node_id); + if (!node) + return false; + RootNodeManager::ScopedChange change( + source, root_node_manager_, + RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, true); + if (node->GetParent()) + node->GetParent()->Remove(node); + std::vector<Node*> children(node->GetChildren()); + for (size_t i = 0; i < children.size(); ++i) + node->Remove(children[i]); + DCHECK(node->GetChildren().empty()); + node_map_.erase(node_id.node_id); + delete node; + node = NULL; + root_node_manager_->ProcessNodeDeleted(node_id); + return true; +} + +bool ViewManagerServiceImpl::DeleteViewImpl(ViewManagerServiceImpl* source, + const ViewId& view_id) { + DCHECK_EQ(view_id.connection_id, id_); + View* view = GetView(view_id); + if (!view) + return false; + RootNodeManager::ScopedChange change( + source, root_node_manager_, + RootNodeManager::CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, false); + if (view->node()) + view->node()->SetView(NULL); + view_map_.erase(view_id.view_id); + // Make a copy of |view_id| as once we delete view |view_id| may no longer be + // valid. + const ViewId view_id_copy(view_id); + delete view; + root_node_manager_->ProcessViewDeleted(view_id_copy); + return true; +} + +bool ViewManagerServiceImpl::SetViewImpl(Node* node, const ViewId& view_id) { + DCHECK(node); // CanSetView() should have verified node exists. + View* view = GetView(view_id); + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, false); + node->SetView(view); + + // TODO(sky): this is temporary, need a real focus API. + if (view && root_node_manager_->root()->Contains(node)) + node->window()->Focus(); + + return true; +} + +void ViewManagerServiceImpl::GetUnknownNodesFrom( + const Node* node, + std::vector<const Node*>* nodes) { + const Id transport_id = NodeIdToTransportId(node->id()); + if (known_nodes_.count(transport_id) == 1) + return; + nodes->push_back(node); + known_nodes_.insert(transport_id); + std::vector<const Node*> children(node->GetChildren()); + for (size_t i = 0 ; i < children.size(); ++i) + GetUnknownNodesFrom(children[i], nodes); +} + +void ViewManagerServiceImpl::RemoveFromKnown(const Node* node) { + if (node->id().connection_id == id_) + return; + known_nodes_.erase(NodeIdToTransportId(node->id())); + std::vector<const Node*> children = node->GetChildren(); + for (size_t i = 0; i < children.size(); ++i) + RemoveFromKnown(children[i]); +} + +bool ViewManagerServiceImpl::AddRoots( + const std::vector<Id>& node_ids) { + std::vector<const Node*> to_send; + bool did_add_root = false; + for (size_t i = 0; i < node_ids.size(); ++i) { + CHECK_EQ(creator_id_, NodeIdFromTransportId(node_ids[i]).connection_id); + if (roots_.count(node_ids[i]) > 0) + continue; + + did_add_root = true; + roots_.insert(node_ids[i]); + Node* node = GetNode(NodeIdFromTransportId(node_ids[i])); + DCHECK(node); + if (known_nodes_.count(node_ids[i]) == 0) { + GetUnknownNodesFrom(node, &to_send); + } else { + // Even though the connection knows about the new root we need to tell it + // |node| is now a root. + to_send.push_back(node); + } + } + + if (!did_add_root) + return false; + + client()->OnRootsAdded(NodesToNodeDatas(to_send)); + return true; +} + +bool ViewManagerServiceImpl::IsNodeDescendantOfRoots(const Node* node) const { + if (roots_.empty()) + return true; + if (!node) + return false; + const Id invalid_node_id = + NodeIdToTransportId(InvalidNodeId()); + for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i) { + if (*i == invalid_node_id) + continue; + const Node* root = GetNode(NodeIdFromTransportId(*i)); + DCHECK(root); + if (root->Contains(node)) + return true; + } + return false; +} + +bool ViewManagerServiceImpl::ShouldNotifyOnHierarchyChange( + const Node* node, + const Node** new_parent, + const Node** old_parent, + std::vector<const Node*>* to_send) { + // If the node is not in |roots_| or was never known to this connection then + // don't notify the client about it. + if (node->id().connection_id != id_ && + known_nodes_.count(NodeIdToTransportId(node->id())) == 0 && + !IsNodeDescendantOfRoots(node)) { + return false; + } + if (!IsNodeDescendantOfRoots(*new_parent)) + *new_parent = NULL; + if (!IsNodeDescendantOfRoots(*old_parent)) + *old_parent = NULL; + + if (*new_parent) { + // On getting a new parent we may need to communicate new nodes to the + // client. We do that in the following cases: + // . New parent is a descendant of the roots. In this case the client + // already knows all ancestors, so we only have to communicate descendants + // of node the client doesn't know about. + // . If the client knew about the parent, we have to do the same. + // . If the client knows about the node and is added to a tree the client + // doesn't know about we have to communicate from the root down (the + // client is learning about a new root). + if (root_node_manager_->root()->Contains(*new_parent) || + known_nodes_.count(NodeIdToTransportId((*new_parent)->id()))) { + GetUnknownNodesFrom(node, to_send); + return true; + } + // If parent wasn't known we have to communicate from the root down. + if (known_nodes_.count(NodeIdToTransportId(node->id()))) { + // No need to check against |roots_| as client should always know it's + // |roots_|. + GetUnknownNodesFrom((*new_parent)->GetRoot(), to_send); + return true; + } + } + // Otherwise only communicate the change if the node was known. We shouldn't + // need to communicate any nodes on a remove. + return known_nodes_.count(NodeIdToTransportId(node->id())) > 0; +} + +Array<NodeDataPtr> ViewManagerServiceImpl::NodesToNodeDatas( + const std::vector<const Node*>& nodes) { + Array<NodeDataPtr> array(nodes.size()); + for (size_t i = 0; i < nodes.size(); ++i) { + const Node* node = nodes[i]; + DCHECK(known_nodes_.count(NodeIdToTransportId(node->id())) > 0); + const Node* parent = node->GetParent(); + // If the parent isn't known, it means the parent is not visible to us (not + // in roots), and should not be sent over. + if (parent && known_nodes_.count(NodeIdToTransportId(parent->id())) == 0) + parent = NULL; + NodeDataPtr inode(NodeData::New()); + inode->parent_id = NodeIdToTransportId(parent ? parent->id() : NodeId()); + inode->node_id = NodeIdToTransportId(node->id()); + inode->view_id = + ViewIdToTransportId(node->view() ? node->view()->id() : ViewId()); + inode->bounds = Rect::From(node->bounds()); + array[i] = inode.Pass(); + } + return array.Pass(); +} + +void ViewManagerServiceImpl::CreateNode( + Id transport_node_id, + const Callback<void(bool)>& callback) { + const NodeId node_id(NodeIdFromTransportId(transport_node_id)); + if (node_id.connection_id != id_ || + node_map_.find(node_id.node_id) != node_map_.end()) { + callback.Run(false); + return; + } + node_map_[node_id.node_id] = new Node(this, node_id); + known_nodes_.insert(transport_node_id); + callback.Run(true); +} + +void ViewManagerServiceImpl::DeleteNode( + Id transport_node_id, + Id server_change_id, + const Callback<void(bool)>& callback) { + const NodeId node_id(NodeIdFromTransportId(transport_node_id)); + bool success = false; + if (server_change_id == root_node_manager_->next_server_change_id() && + CanDeleteNode(node_id)) { + ViewManagerServiceImpl* connection = root_node_manager_->GetConnection( + node_id.connection_id); + success = connection && connection->DeleteNodeImpl(this, node_id); + } + callback.Run(success); +} + +void ViewManagerServiceImpl::AddNode( + Id parent_id, + Id child_id, + Id server_change_id, + const Callback<void(bool)>& callback) { + bool success = false; + if (server_change_id == root_node_manager_->next_server_change_id()) { + Node* parent = GetNode(NodeIdFromTransportId(parent_id)); + Node* child = GetNode(NodeIdFromTransportId(child_id)); + if (CanAddNode(parent, child)) { + success = true; + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false); + parent->Add(child); + } + } + callback.Run(success); +} + +void ViewManagerServiceImpl::RemoveNodeFromParent( + Id node_id, + Id server_change_id, + const Callback<void(bool)>& callback) { + bool success = false; + if (server_change_id == root_node_manager_->next_server_change_id()) { + Node* node = GetNode(NodeIdFromTransportId(node_id)); + if (CanRemoveNodeFromParent(node)) { + success = true; + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false); + node->GetParent()->Remove(node); + } + } + callback.Run(success); +} + +void ViewManagerServiceImpl::ReorderNode(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id, + const Callback<void(bool)>& callback) { + bool success = false; + if (server_change_id == root_node_manager_->next_server_change_id()) { + Node* node = GetNode(NodeIdFromTransportId(node_id)); + Node* relative_node = GetNode(NodeIdFromTransportId(relative_node_id)); + if (CanReorderNode(node, relative_node, direction)) { + success = true; + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false); + node->GetParent()->Reorder(node, relative_node, direction); + root_node_manager_->ProcessNodeReorder(node, relative_node, direction); + } + } + callback.Run(success); +} + +void ViewManagerServiceImpl::GetNodeTree( + Id node_id, + const Callback<void(Array<NodeDataPtr>)>& callback) { + Node* node = GetNode(NodeIdFromTransportId(node_id)); + std::vector<const Node*> nodes; + if (CanGetNodeTree(node)) { + GetDescendants(node, &nodes); + for (size_t i = 0; i < nodes.size(); ++i) + known_nodes_.insert(NodeIdToTransportId(nodes[i]->id())); + } + callback.Run(NodesToNodeDatas(nodes)); +} + +void ViewManagerServiceImpl::CreateView( + Id transport_view_id, + const Callback<void(bool)>& callback) { + const ViewId view_id(ViewIdFromTransportId(transport_view_id)); + if (view_id.connection_id != id_ || view_map_.count(view_id.view_id)) { + callback.Run(false); + return; + } + view_map_[view_id.view_id] = new View(view_id); + callback.Run(true); +} + +void ViewManagerServiceImpl::DeleteView( + Id transport_view_id, + const Callback<void(bool)>& callback) { + const ViewId view_id(ViewIdFromTransportId(transport_view_id)); + bool did_delete = CanDeleteView(view_id); + if (did_delete) { + ViewManagerServiceImpl* connection = root_node_manager_->GetConnection( + view_id.connection_id); + did_delete = (connection && connection->DeleteViewImpl(this, view_id)); + } + callback.Run(did_delete); +} + +void ViewManagerServiceImpl::SetView(Id transport_node_id, + Id transport_view_id, + const Callback<void(bool)>& callback) { + Node* node = GetNode(NodeIdFromTransportId(transport_node_id)); + const ViewId view_id(ViewIdFromTransportId(transport_view_id)); + callback.Run(CanSetView(node, view_id) && SetViewImpl(node, view_id)); +} + +void ViewManagerServiceImpl::SetViewContents( + Id view_id, + ScopedSharedBufferHandle buffer, + uint32_t buffer_size, + const Callback<void(bool)>& callback) { + View* view = GetView(ViewIdFromTransportId(view_id)); + if (!view) { + callback.Run(false); + return; + } + void* handle_data; + if (MapBuffer(buffer.get(), 0, buffer_size, &handle_data, + MOJO_MAP_BUFFER_FLAG_NONE) != MOJO_RESULT_OK) { + callback.Run(false); + return; + } + SkBitmap bitmap; + gfx::PNGCodec::Decode(static_cast<const unsigned char*>(handle_data), + buffer_size, &bitmap); + view->SetBitmap(bitmap); + UnmapBuffer(handle_data); + callback.Run(true); +} + +void ViewManagerServiceImpl::SetFocus(Id node_id, + const Callback<void(bool)> & callback) { + bool success = false; + Node* node = GetNode(NodeIdFromTransportId(node_id)); + if (CanSetFocus(node)) { + success = true; + node->window()->Focus(); + } + callback.Run(success); +} + +void ViewManagerServiceImpl::SetNodeBounds( + Id node_id, + RectPtr bounds, + const Callback<void(bool)>& callback) { + if (NodeIdFromTransportId(node_id).connection_id != id_) { + callback.Run(false); + return; + } + + Node* node = GetNode(NodeIdFromTransportId(node_id)); + if (!node) { + callback.Run(false); + return; + } + + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, false); + gfx::Rect old_bounds = node->window()->bounds(); + node->window()->SetBounds(bounds.To<gfx::Rect>()); + root_node_manager_->ProcessNodeBoundsChanged( + node, old_bounds, bounds.To<gfx::Rect>()); + callback.Run(true); +} + +void ViewManagerServiceImpl::Embed(const String& url, + Array<uint32_t> node_ids, + const Callback<void(bool)>& callback) { + bool success = CanEmbed(node_ids); + if (success) { + // We may already have this connection, if so reuse it. + ViewManagerServiceImpl* existing_connection = + root_node_manager_->GetConnectionByCreator(id_, url.To<std::string>()); + if (existing_connection) + success = existing_connection->AddRoots(node_ids.storage()); + else + root_node_manager_->Embed(id_, url, node_ids); + } + callback.Run(success); +} + +void ViewManagerServiceImpl::DispatchOnViewInputEvent(Id transport_view_id, + EventPtr event) { + // We only allow the WM to dispatch events. At some point this function will + // move to a separate interface and the check can go away. + if (id_ != kWindowManagerConnection) + return; + + const ViewId view_id(ViewIdFromTransportId(transport_view_id)); + ViewManagerServiceImpl* connection = root_node_manager_->GetConnection( + view_id.connection_id); + if (connection) + connection->client()->OnViewInputEvent( + transport_view_id, + event.Pass(), + base::Bind(&base::DoNothing)); +} + +void ViewManagerServiceImpl::OnNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent) { + root_node_manager_->ProcessNodeHierarchyChanged(node, new_parent, old_parent); +} + +void ViewManagerServiceImpl::OnNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view) { + root_node_manager_->ProcessNodeViewReplaced(node, new_view, old_view); +} + +void ViewManagerServiceImpl::OnViewInputEvent(const View* view, + const ui::Event* event) { + root_node_manager_->DispatchViewInputEventToWindowManager(view, event); +} + +void ViewManagerServiceImpl::OnConnectionEstablished() { + root_node_manager_->AddConnection(this); + + std::vector<const Node*> to_send; + if (roots_.empty()) { + GetUnknownNodesFrom(root_node_manager_->root(), &to_send); + } else { + for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i) + GetUnknownNodesFrom(GetNode(NodeIdFromTransportId(*i)), &to_send); + } + + client()->OnViewManagerConnectionEstablished( + id_, + creator_url_, + root_node_manager_->next_server_change_id(), + NodesToNodeDatas(to_send)); +} + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/view_manager_service_impl.h b/chromium/mojo/services/view_manager/view_manager_service_impl.h new file mode 100644 index 00000000000..53950dc3c11 --- /dev/null +++ b/chromium/mojo/services/view_manager/view_manager_service_impl.h @@ -0,0 +1,269 @@ +// 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_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_ +#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" +#include "mojo/services/view_manager/ids.h" +#include "mojo/services/view_manager/node_delegate.h" +#include "mojo/services/view_manager/view_manager_export.h" + +namespace gfx { +class Rect; +} + +namespace mojo { +namespace view_manager { +namespace service { + +class Node; +class RootNodeManager; +class View; + +#if defined(OS_WIN) +// Equivalent of NON_EXPORTED_BASE which does not work with the template snafu +// below. +#pragma warning(push) +#pragma warning(disable : 4275) +#endif + +// Manages a connection from the client. +class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl + : public InterfaceImpl<ViewManagerService>, + public NodeDelegate { + public: + ViewManagerServiceImpl(RootNodeManager* root_node_manager, + ConnectionSpecificId creator_id, + const std::string& creator_url, + const std::string& url); + virtual ~ViewManagerServiceImpl(); + + // Used to mark this connection as originating from a call to + // ViewManagerService::Connect(). When set OnConnectionError() deletes |this|. + void set_delete_on_connection_error() { delete_on_connection_error_ = true; } + + ConnectionSpecificId id() const { return id_; } + ConnectionSpecificId creator_id() const { return creator_id_; } + const std::string& url() const { return url_; } + + // Returns the Node with the specified id. + Node* GetNode(const NodeId& id) { + return const_cast<Node*>( + const_cast<const ViewManagerServiceImpl*>(this)->GetNode(id)); + } + const Node* GetNode(const NodeId& id) const; + + // Returns the View with the specified id. + View* GetView(const ViewId& id) { + return const_cast<View*>( + const_cast<const ViewManagerServiceImpl*>(this)->GetView(id)); + } + const View* GetView(const ViewId& id) const; + + void SetRoots(const Array<Id>& node_ids); + + // Invoked when a connection is destroyed. + void OnViewManagerServiceImplDestroyed(ConnectionSpecificId id); + + // The following methods are invoked after the corresponding change has been + // processed. They do the appropriate bookkeeping and update the client as + // necessary. + void ProcessNodeBoundsChanged(const Node* node, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + bool originated_change); + void ProcessNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent, + Id server_change_id, + bool originated_change); + void ProcessNodeReorder(const Node* node, + const Node* relative_node, + OrderDirection direction, + Id server_change_id, + bool originated_change); + void ProcessNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view, + bool originated_change); + void ProcessNodeDeleted(const NodeId& node, + Id server_change_id, + bool originated_change); + void ProcessViewDeleted(const ViewId& view, bool originated_change); + + // TODO(sky): move this to private section (currently can't because of + // bindings). + // InterfaceImp overrides: + virtual void OnConnectionError() MOJO_OVERRIDE; + + private: + typedef std::map<ConnectionSpecificId, Node*> NodeMap; + typedef std::map<ConnectionSpecificId, View*> ViewMap; + typedef base::hash_set<Id> NodeIdSet; + + // These functions return true if the corresponding mojom function is allowed + // for this connection. + bool CanRemoveNodeFromParent(const Node* node) const; + bool CanAddNode(const Node* parent, const Node* child) const; + bool CanReorderNode(const Node* node, + const Node* relative_node, + OrderDirection direction) const; + bool CanDeleteNode(const NodeId& node_id) const; + bool CanDeleteView(const ViewId& view_id) const; + bool CanSetView(const Node* node, const ViewId& view_id) const; + bool CanSetFocus(const Node* node) const; + bool CanGetNodeTree(const Node* node) const; + bool CanEmbed(const mojo::Array<uint32_t>& node_ids) const; + + // Deletes a node owned by this connection. Returns true on success. |source| + // is the connection that originated the change. + bool DeleteNodeImpl(ViewManagerServiceImpl* source, const NodeId& node_id); + + // Deletes a view owned by this connection. Returns true on success. |source| + // is the connection that originated the change. + bool DeleteViewImpl(ViewManagerServiceImpl* source, const ViewId& view_id); + + // Sets the view associated with a node. + bool SetViewImpl(Node* node, const ViewId& view_id); + + // If |node| is known (in |known_nodes_|) does nothing. Otherwise adds |node| + // to |nodes|, marks |node| as known and recurses. + void GetUnknownNodesFrom(const Node* node, std::vector<const Node*>* nodes); + + // Removes |node| and all its descendants from |known_nodes_|. This does not + // recurse through nodes that were created by this connection. + void RemoveFromKnown(const Node* node); + + // Adds |node_ids| to roots, returning true if at least one of the nodes was + // not already a root. If at least one of the nodes was not already a root + // the client is told of the new roots. + bool AddRoots(const std::vector<Id>& node_ids); + + // Returns true if |node| is a non-null and a descendant of |roots_| (or + // |roots_| is empty). + bool IsNodeDescendantOfRoots(const Node* node) const; + + // Returns true if notification should be sent of a hierarchy change. If true + // is returned, any nodes that need to be sent to the client are added to + // |to_send|. + bool ShouldNotifyOnHierarchyChange(const Node* node, + const Node** new_parent, + const Node** old_parent, + std::vector<const Node*>* to_send); + + // Converts an array of Nodes to NodeDatas. This assumes all the nodes are + // valid for the client. The parent of nodes the client is not allowed to see + // are set to NULL (in the returned NodeDatas). + Array<NodeDataPtr> NodesToNodeDatas(const std::vector<const Node*>& nodes); + + // Overridden from ViewManagerService: + virtual void CreateNode(Id transport_node_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void DeleteNode(Id transport_node_id, + Id server_change_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void AddNode(Id parent_id, + Id child_id, + Id server_change_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void RemoveNodeFromParent( + Id node_id, + Id server_change_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void ReorderNode(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void GetNodeTree( + Id node_id, + const Callback<void(Array<NodeDataPtr>)>& callback) OVERRIDE; + virtual void CreateView(Id transport_view_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void DeleteView(Id transport_view_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void SetView(Id transport_node_id, + Id transport_view_id, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void SetViewContents(Id view_id, + ScopedSharedBufferHandle buffer, + uint32_t buffer_size, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void SetFocus(Id node_id, + const Callback<void(bool)> & callback) OVERRIDE; + virtual void SetNodeBounds(Id node_id, + RectPtr bounds, + const Callback<void(bool)>& callback) OVERRIDE; + virtual void Embed(const mojo::String& url, + mojo::Array<uint32_t> node_ids, + const mojo::Callback<void(bool)>& callback) OVERRIDE; + virtual void DispatchOnViewInputEvent(Id transport_view_id, + EventPtr event) OVERRIDE; + + // Overridden from NodeDelegate: + virtual void OnNodeHierarchyChanged(const Node* node, + const Node* new_parent, + const Node* old_parent) OVERRIDE; + virtual void OnNodeViewReplaced(const Node* node, + const View* new_view, + const View* old_view) OVERRIDE; + virtual void OnViewInputEvent(const View* view, + const ui::Event* event) OVERRIDE; + + // InterfaceImp overrides: + virtual void OnConnectionEstablished() MOJO_OVERRIDE; + + RootNodeManager* root_node_manager_; + + // Id of this connection as assigned by RootNodeManager. + const ConnectionSpecificId id_; + + // URL this connection was created for. + const std::string url_; + + // ID of the connection that created us. If 0 it indicates either we were + // created by the root, or the connection that created us has been destroyed. + ConnectionSpecificId creator_id_; + + // The URL of the app that embedded the app this connection was created for. + const std::string creator_url_; + + NodeMap node_map_; + + ViewMap view_map_; + + // The set of nodes that has been communicated to the client. + NodeIdSet known_nodes_; + + // This is the set of nodes the connection can parent nodes to (in addition to + // any nodes created by this connection). If empty the connection can + // manipulate any nodes (except for deleting other connections nodes/views). + // The connection can not delete or move these. If this is set to a non-empty + // value and all the nodes are deleted (by another connection), then an + // invalid node is added here to ensure this connection is still constrained. + NodeIdSet roots_; + + // See description above setter. + bool delete_on_connection_error_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceImpl); +}; + +#if defined(OS_WIN) +#pragma warning(pop) +#endif + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_ diff --git a/chromium/mojo/services/view_manager/view_manager_unittest.cc b/chromium/mojo/services/view_manager/view_manager_unittest.cc new file mode 100644 index 00000000000..ed540926a89 --- /dev/null +++ b/chromium/mojo/services/view_manager/view_manager_unittest.cc @@ -0,0 +1,1370 @@ +// 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 <string> +#include <vector> + +#include "base/at_exit.h" +#include "base/auto_reset.h" +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "mojo/common/common_type_converters.h" +#include "mojo/public/cpp/application/application.h" +#include "mojo/public/cpp/application/connect.h" +#include "mojo/public/cpp/bindings/lib/router.h" +#include "mojo/service_manager/service_manager.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/cpp/view_manager/util.h" +#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" +#include "mojo/services/view_manager/ids.h" +#include "mojo/services/view_manager/test_change_tracker.h" +#include "mojo/shell/shell_test_helper.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/rect.h" + +namespace mojo { +namespace view_manager { +namespace service { + +namespace { + +const char kTestServiceURL[] = "mojo:test_url"; + +// ViewManagerProxy is a proxy to an ViewManagerService. It handles invoking +// ViewManagerService functions on the right thread in a synchronous manner +// (each ViewManagerService cover function blocks until the response from the +// ViewManagerService is returned). In addition it tracks the set of +// ViewManagerClient messages received by way of a vector of Changes. Use +// DoRunLoopUntilChangesCount() to wait for a certain number of messages to be +// received. +class ViewManagerProxy : public TestChangeTracker::Delegate { + public: + explicit ViewManagerProxy(TestChangeTracker* tracker) + : tracker_(tracker), + view_manager_(NULL), + quit_count_(0), + router_(NULL) { + SetInstance(this); + } + + virtual ~ViewManagerProxy() {} + + // Runs a message loop until the single instance has been created. + static ViewManagerProxy* WaitForInstance() { + if (!instance_) + RunMainLoop(); + ViewManagerProxy* instance = instance_; + instance_ = NULL; + return instance; + } + + ViewManagerService* view_manager() { return view_manager_; } + + // Runs the main loop until |count| changes have been received. + std::vector<Change> DoRunLoopUntilChangesCount(size_t count) { + DCHECK_EQ(0u, quit_count_); + if (tracker_->changes()->size() >= count) { + CopyChangesFromTracker(); + return changes_; + } + quit_count_ = count - tracker_->changes()->size(); + // Run the current message loop. When |count| Changes have been received, + // we'll quit. + RunMainLoop(); + return changes_; + } + + const std::vector<Change>& changes() const { return changes_; } + + // Destroys the connection, blocking until done. + void Destroy() { + router_->CloseMessagePipe(); + } + + // The following functions are cover methods for ViewManagerService. They + // block until the result is received. + bool CreateNode(Id node_id) { + changes_.clear(); + bool result = false; + view_manager_->CreateNode(node_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool AddNode(Id parent, Id child, Id server_change_id) { + changes_.clear(); + bool result = false; + view_manager_->AddNode(parent, child, server_change_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool RemoveNodeFromParent(Id node_id, Id server_change_id) { + changes_.clear(); + bool result = false; + view_manager_->RemoveNodeFromParent(node_id, server_change_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool ReorderNode(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id) { + changes_.clear(); + bool result = false; + view_manager_->ReorderNode(node_id, relative_node_id, direction, + server_change_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool SetView(Id node_id, Id view_id) { + changes_.clear(); + bool result = false; + view_manager_->SetView(node_id, view_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool CreateView(Id view_id) { + changes_.clear(); + bool result = false; + view_manager_->CreateView(view_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + void GetNodeTree(Id node_id, std::vector<TestNode>* nodes) { + changes_.clear(); + view_manager_->GetNodeTree(node_id, + base::Bind(&ViewManagerProxy::GotNodeTree, + base::Unretained(this), nodes)); + RunMainLoop(); + } + bool Embed(const std::vector<Id>& nodes) { + changes_.clear(); + base::AutoReset<bool> auto_reset(&in_embed_, true); + bool result = false; + view_manager_->Embed(kTestServiceURL, Array<Id>::From(nodes), + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool DeleteNode(Id node_id, Id server_change_id) { + changes_.clear(); + bool result = false; + view_manager_->DeleteNode(node_id, + server_change_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool DeleteView(Id view_id) { + changes_.clear(); + bool result = false; + view_manager_->DeleteView(view_id, + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + bool SetNodeBounds(Id node_id, const gfx::Rect& bounds) { + changes_.clear(); + bool result = false; + view_manager_->SetNodeBounds(node_id, Rect::From(bounds), + base::Bind(&ViewManagerProxy::GotResult, + base::Unretained(this), &result)); + RunMainLoop(); + return result; + } + + private: + friend class TestViewManagerClientConnection; + + void set_router(mojo::internal::Router* router) { router_ = router; } + + void set_view_manager(ViewManagerService* view_manager) { + view_manager_ = view_manager; + } + + static void RunMainLoop() { + DCHECK(!main_run_loop_); + main_run_loop_ = new base::RunLoop; + main_run_loop_->Run(); + delete main_run_loop_; + main_run_loop_ = NULL; + } + + void QuitCountReached() { + CopyChangesFromTracker(); + main_run_loop_->Quit(); + } + + void CopyChangesFromTracker() { + std::vector<Change> changes; + tracker_->changes()->swap(changes); + changes_.swap(changes); + } + + static void SetInstance(ViewManagerProxy* instance) { + DCHECK(!instance_); + instance_ = instance; + // Embed() runs its own run loop that is quit when the result is + // received. Embed() also results in a new instance. If we quit here while + // waiting for a Embed() we would prematurely return before we got the + // result from Embed(). + if (!in_embed_ && main_run_loop_) + main_run_loop_->Quit(); + } + + // Callbacks from the various ViewManagerService functions. + void GotResult(bool* result_cache, bool result) { + *result_cache = result; + DCHECK(main_run_loop_); + main_run_loop_->Quit(); + } + + void GotNodeTree(std::vector<TestNode>* nodes, Array<NodeDataPtr> results) { + NodeDatasToTestNodes(results, nodes); + DCHECK(main_run_loop_); + main_run_loop_->Quit(); + } + + // TestChangeTracker::Delegate: + virtual void OnChangeAdded() OVERRIDE { + if (quit_count_ > 0 && --quit_count_ == 0) + QuitCountReached(); + } + + static ViewManagerProxy* instance_; + static base::RunLoop* main_run_loop_; + static bool in_embed_; + + TestChangeTracker* tracker_; + + // MessageLoop of the test. + base::MessageLoop* main_loop_; + + ViewManagerService* view_manager_; + + // Number of changes we're waiting on until we quit the current loop. + size_t quit_count_; + + std::vector<Change> changes_; + + mojo::internal::Router* router_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerProxy); +}; + +// static +ViewManagerProxy* ViewManagerProxy::instance_ = NULL; + +// static +base::RunLoop* ViewManagerProxy::main_run_loop_ = NULL; + +// static +bool ViewManagerProxy::in_embed_ = false; + +class TestViewManagerClientConnection + : public InterfaceImpl<ViewManagerClient> { + public: + TestViewManagerClientConnection() : connection_(&tracker_) { + tracker_.set_delegate(&connection_); + } + + // InterfaceImp: + virtual void OnConnectionEstablished() OVERRIDE { + connection_.set_router(internal_state()->router()); + connection_.set_view_manager(client()); + } + + // ViewMangerClient: + virtual void OnViewManagerConnectionEstablished( + ConnectionSpecificId connection_id, + const String& creator_url, + Id next_server_change_id, + Array<NodeDataPtr> nodes) OVERRIDE { + tracker_.OnViewManagerConnectionEstablished( + connection_id, creator_url, next_server_change_id, nodes.Pass()); + } + virtual void OnRootsAdded(Array<NodeDataPtr> nodes) OVERRIDE { + tracker_.OnRootsAdded(nodes.Pass()); + } + virtual void OnServerChangeIdAdvanced( + Id next_server_change_id) OVERRIDE { + tracker_.OnServerChangeIdAdvanced(next_server_change_id); + } + virtual void OnNodeBoundsChanged(Id node_id, + RectPtr old_bounds, + RectPtr new_bounds) OVERRIDE { + tracker_.OnNodeBoundsChanged(node_id, old_bounds.Pass(), new_bounds.Pass()); + } + virtual void OnNodeHierarchyChanged(Id node, + Id new_parent, + Id old_parent, + Id server_change_id, + Array<NodeDataPtr> nodes) OVERRIDE { + tracker_.OnNodeHierarchyChanged(node, new_parent, old_parent, + server_change_id, nodes.Pass()); + } + virtual void OnNodeReordered(Id node_id, + Id relative_node_id, + OrderDirection direction, + Id server_change_id) OVERRIDE { + tracker_.OnNodeReordered(node_id, relative_node_id, direction, + server_change_id); + } + virtual void OnNodeDeleted(Id node, Id server_change_id) OVERRIDE { + tracker_.OnNodeDeleted(node, server_change_id); + } + virtual void OnViewDeleted(Id view) OVERRIDE { + tracker_.OnViewDeleted(view); + } + virtual void OnNodeViewReplaced(Id node, + Id new_view_id, + Id old_view_id) OVERRIDE { + tracker_.OnNodeViewReplaced(node, new_view_id, old_view_id); + } + virtual void OnViewInputEvent(Id view_id, + EventPtr event, + const Callback<void()>& callback) OVERRIDE { + tracker_.OnViewInputEvent(view_id, event.Pass()); + } + virtual void DispatchOnViewInputEvent(Id view_id, + mojo::EventPtr event) OVERRIDE { + } + + private: + TestChangeTracker tracker_; + ViewManagerProxy connection_; + + DISALLOW_COPY_AND_ASSIGN(TestViewManagerClientConnection); +}; + +// Used with ViewManagerService::Embed(). Creates a +// TestViewManagerClientConnection, which creates and owns the ViewManagerProxy. +class EmbedServiceLoader : public ServiceLoader { + public: + EmbedServiceLoader() {} + virtual ~EmbedServiceLoader() {} + + // ServiceLoader: + virtual void LoadService(ServiceManager* manager, + const GURL& url, + ScopedMessagePipeHandle shell_handle) OVERRIDE { + scoped_ptr<Application> app(new Application(shell_handle.Pass())); + app->AddService<TestViewManagerClientConnection>(); + apps_.push_back(app.release()); + } + virtual void OnServiceError(ServiceManager* manager, + const GURL& url) OVERRIDE { + } + + private: + ScopedVector<Application> apps_; + + DISALLOW_COPY_AND_ASSIGN(EmbedServiceLoader); +}; + +// Creates an id used for transport from the specified parameters. +Id BuildNodeId(ConnectionSpecificId connection_id, + ConnectionSpecificId node_id) { + return (connection_id << 16) | node_id; +} + +// Creates an id used for transport from the specified parameters. +Id BuildViewId(ConnectionSpecificId connection_id, + ConnectionSpecificId view_id) { + return (connection_id << 16) | view_id; +} + +// Callback from EmbedRoot(). |result| is the result of the +// Embed() call and |run_loop| the nested RunLoop. +void EmbedRootCallback(bool* result_cache, + base::RunLoop* run_loop, + bool result) { + *result_cache = result; + run_loop->Quit(); +} + +// Resposible for establishing the initial ViewManagerService connection. Blocks +// until result is determined. +bool EmbedRoot(ViewManagerInitService* view_manager_init, + const std::string& url) { + bool result = false; + base::RunLoop run_loop; + view_manager_init->EmbedRoot(url, base::Bind(&EmbedRootCallback, + &result, &run_loop)); + run_loop.Run(); + return result; +} + +} // namespace + +typedef std::vector<std::string> Changes; + +class ViewManagerTest : public testing::Test { + public: + ViewManagerTest() : connection_(NULL), connection2_(NULL) {} + + virtual void SetUp() OVERRIDE { + test_helper_.Init(); + + test_helper_.SetLoaderForURL( + scoped_ptr<ServiceLoader>(new EmbedServiceLoader()), + GURL(kTestServiceURL)); + + ConnectToService(test_helper_.service_provider(), + "mojo:mojo_view_manager", + &view_manager_init_); + ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kTestServiceURL)); + + connection_ = ViewManagerProxy::WaitForInstance(); + ASSERT_TRUE(connection_ != NULL); + connection_->DoRunLoopUntilChangesCount(1); + } + + virtual void TearDown() OVERRIDE { + if (connection2_) + connection2_->Destroy(); + if (connection_) + connection_->Destroy(); + } + + protected: + void EstablishSecondConnectionWithRoots(Id id1, Id id2) { + std::vector<Id> node_ids; + node_ids.push_back(id1); + if (id2 != 0) + node_ids.push_back(id2); + ASSERT_TRUE(connection_->Embed(node_ids)); + connection2_ = ViewManagerProxy::WaitForInstance(); + ASSERT_TRUE(connection2_ != NULL); + connection2_->DoRunLoopUntilChangesCount(1); + ASSERT_EQ(1u, connection2_->changes().size()); + } + + // Creates a second connection to the viewmanager. + void EstablishSecondConnection(bool create_initial_node) { + if (create_initial_node) + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_NO_FATAL_FAILURE( + EstablishSecondConnectionWithRoots(BuildNodeId(1, 1), 0)); + const std::vector<Change>& changes(connection2_->changes()); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url", + ChangesToDescription1(changes)[0]); + if (create_initial_node) { + EXPECT_EQ("[node=1,1 parent=null view=null]", + ChangeNodeDescription(changes)); + } + } + + void DestroySecondConnection() { + connection2_->Destroy(); + connection2_ = NULL; + } + + base::ShadowingAtExitManager at_exit_; + base::MessageLoop loop_; + shell::ShellTestHelper test_helper_; + + ViewManagerInitServicePtr view_manager_init_; + + ViewManagerProxy* connection_; + ViewManagerProxy* connection2_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerTest); +}; + +// TODO(sky): reenable tests: http://crbug.com/385475 + +// Verifies client gets a valid id. +TEST_F(ViewManagerTest, DISABLED_ValidId) { + // TODO(beng): this should really have the URL of the application that + // connected to ViewManagerInit. + EXPECT_EQ("OnConnectionEstablished creator=", + ChangesToDescription1(connection_->changes())[0]); + + // All these tests assume 1 for the client id. The only real assertion here is + // the client id is not zero, but adding this as rest of code here assumes 1. + EXPECT_EQ(1, connection_->changes()[0].connection_id); + + // Change ids start at 1 as well. + EXPECT_EQ(static_cast<Id>(1), connection_->changes()[0].change_id); +} + +// Verifies two clients/connections get different ids. +TEST_F(ViewManagerTest, DISABLED_TwoClientsGetDifferentConnectionIds) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url", + ChangesToDescription1(connection2_->changes())[0]); + + // It isn't strickly necessary that the second connection gets 2, but these + // tests are written assuming that is the case. The key thing is the + // connection ids of |connection_| and |connection2_| differ. + EXPECT_EQ(2, connection2_->changes()[0].connection_id); + + // Change ids start at 1 as well. + EXPECT_EQ(static_cast<Id>(1), connection2_->changes()[0].change_id); +} + +// Verifies client gets a valid id. +TEST_F(ViewManagerTest, DISABLED_CreateNode) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + EXPECT_TRUE(connection_->changes().empty()); + + // Can't create a node with the same id. + ASSERT_FALSE(connection_->CreateNode(BuildNodeId(1, 1))); + EXPECT_TRUE(connection_->changes().empty()); + + // Can't create a node with a bogus connection id. + EXPECT_FALSE(connection_->CreateNode(BuildNodeId(2, 1))); + EXPECT_TRUE(connection_->changes().empty()); +} + +TEST_F(ViewManagerTest, DISABLED_CreateViewFailsWithBogusConnectionId) { + EXPECT_FALSE(connection_->CreateView(BuildViewId(2, 1))); + EXPECT_TRUE(connection_->changes().empty()); +} + +// Verifies hierarchy changes. +TEST_F(ViewManagerTest, DISABLED_AddRemoveNotify) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Make 3 a child of 2. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1)); + EXPECT_TRUE(connection_->changes().empty()); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + } + + // Remove 3 from its parent. + { + ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 3), 2)); + EXPECT_TRUE(connection_->changes().empty()); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); + } +} + +// Verifies AddNode fails when node is already in position. +TEST_F(ViewManagerTest, DISABLED_AddNodeWithNoChange) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Make 3 a child of 2. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + } + + // Try again, this should fail. + { + EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 2)); + } +} + +// Verifies AddNode fails when node is already in position. +TEST_F(ViewManagerTest, DISABLED_AddAncestorFails) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Make 3 a child of 2. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1)); + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + } + + // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3. + { + EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 2), 2)); + } +} + +// Verifies adding with an invalid id fails. +TEST_F(ViewManagerTest, DISABLED_AddWithInvalidServerId) { + // Create two nodes. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + // Make 2 a child of 1. Supply an invalid change id, which should fail. + ASSERT_FALSE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 0)); +} + +// Verifies adding to root sends right notifications. +TEST_F(ViewManagerTest, DISABLED_AddToRoot) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 21))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Make 3 a child of 21. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 21), BuildNodeId(1, 3), 1)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + } + + // Make 21 a child of 1. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 21), 2)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=2 node=1,21 new_parent=1,1 old_parent=null", + changes[0]); + } +} + +// Verifies HierarchyChanged is correctly sent for various adds/removes. +TEST_F(ViewManagerTest, DISABLED_NodeHierarchyChangedNodes) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11))); + // Make 11 a child of 2. + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 11), 1)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Make 2 a child of 1. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 2)); + + // Client 2 should get a hierarchy change that includes the new nodes as it + // has not yet seen them. + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=2 node=1,2 new_parent=1,1 old_parent=null", + changes[0]); + EXPECT_EQ("[node=1,2 parent=1,1 view=null]," + "[node=1,11 parent=1,2 view=null]", + ChangeNodeDescription(connection2_->changes())); + } + + // Add 1 to the root. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 3)); + + // Client 2 should get a hierarchy change that includes the new nodes as it + // has not yet seen them. + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=3 node=1,1 new_parent=null old_parent=null", + changes[0]); + EXPECT_EQ(std::string(), ChangeNodeDescription(connection2_->changes())); + } + + // Remove 1 from its parent. + { + ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 1), 4)); + EXPECT_TRUE(connection_->changes().empty()); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=4 node=1,1 new_parent=null old_parent=null", + changes[0]); + } + + // Create another node, 111, parent it to 11. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 111))); + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 11), BuildNodeId(1, 111), + 5)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=5 node=1,111 new_parent=1,11 " + "old_parent=null", changes[0]); + EXPECT_EQ("[node=1,111 parent=1,11 view=null]", + ChangeNodeDescription(connection2_->changes())); + } + + // Reattach 1 to the root. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 6)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=6 node=1,1 new_parent=null old_parent=null", + changes[0]); + EXPECT_EQ(std::string(), ChangeNodeDescription(connection2_->changes())); + } +} + +TEST_F(ViewManagerTest, DISABLED_NodeHierarchyChangedAddingKnownToUnknown) { + // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no + // parent). + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 21))); + + // Set up the hierarchy. + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 11), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 21), 3)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + { + EXPECT_EQ("[node=1,1 parent=null view=null]," + "[node=1,11 parent=1,1 view=null]", + ChangeNodeDescription(connection2_->changes())); + } + + // Remove 11, should result in a delete (since 11 is no longer in connection + // 2's root). + { + ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 11), 4)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("NodeDeleted change_id=4 node=1,11", changes[0]); + } + + // Add 2 to 1. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 5)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=5 node=1,2 new_parent=1,1 old_parent=null", + changes[0]); + EXPECT_EQ("[node=1,2 parent=1,1 view=null]," + "[node=1,21 parent=1,2 view=null]", + ChangeNodeDescription(connection2_->changes())); + } +} + +TEST_F(ViewManagerTest, DISABLED_ReorderNode) { + Id node1_id = BuildNodeId(1, 1); + Id node2_id = BuildNodeId(1, 2); + Id node3_id = BuildNodeId(1, 3); + Id node4_id = BuildNodeId(1, 4); // Peer to 1,1 + Id node5_id = BuildNodeId(1, 5); // Peer to 1,1 + Id node6_id = BuildNodeId(1, 6); // Child of 1,2. + Id node7_id = BuildNodeId(1, 7); // Unparented. + Id node8_id = BuildNodeId(1, 8); // Unparented. + ASSERT_TRUE(connection_->CreateNode(node1_id)); + ASSERT_TRUE(connection_->CreateNode(node2_id)); + ASSERT_TRUE(connection_->CreateNode(node3_id)); + ASSERT_TRUE(connection_->CreateNode(node4_id)); + ASSERT_TRUE(connection_->CreateNode(node5_id)); + ASSERT_TRUE(connection_->CreateNode(node6_id)); + ASSERT_TRUE(connection_->CreateNode(node7_id)); + ASSERT_TRUE(connection_->CreateNode(node8_id)); + ASSERT_TRUE(connection_->AddNode(node1_id, node2_id, 1)); + ASSERT_TRUE(connection_->AddNode(node2_id, node6_id, 2)); + ASSERT_TRUE(connection_->AddNode(node1_id, node3_id, 3)); + ASSERT_TRUE(connection_->AddNode( + NodeIdToTransportId(RootNodeId()), node4_id, 4)); + ASSERT_TRUE(connection_->AddNode( + NodeIdToTransportId(RootNodeId()), node5_id, 5)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + { + connection_->ReorderNode(node2_id, node3_id, ORDER_ABOVE, 6); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "Reordered change_id=6 node=1,2 relative=1,3 direction=above", + changes[0]); + } + + { + connection_->ReorderNode(node2_id, node3_id, ORDER_BELOW, 7); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "Reordered change_id=7 node=1,2 relative=1,3 direction=below", + changes[0]); + } + + { + // node2 is already below node3. + EXPECT_FALSE(connection_->ReorderNode(node2_id, node3_id, ORDER_BELOW, 8)); + } + + { + // node4 & 5 are unknown to connection2_. + EXPECT_FALSE(connection2_->ReorderNode(node4_id, node5_id, ORDER_ABOVE, 8)); + } + + { + // node6 & node3 have different parents. + EXPECT_FALSE(connection_->ReorderNode(node3_id, node6_id, ORDER_ABOVE, 8)); + } + + { + // Non-existent node-ids + EXPECT_FALSE(connection_->ReorderNode(BuildNodeId(1, 27), + BuildNodeId(1, 28), + ORDER_ABOVE, + 8)); + } + + { + // node7 & node8 are un-parented. + EXPECT_FALSE(connection_->ReorderNode(node7_id, node8_id, ORDER_ABOVE, 8)); + } +} + +// Verifies DeleteNode works. +TEST_F(ViewManagerTest, DISABLED_DeleteNode) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Make 2 a child of 1. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("HierarchyChanged change_id=1 node=1,2 new_parent=1,1 " + "old_parent=null", changes[0]); + } + + // Delete 2. + { + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2)); + EXPECT_TRUE(connection_->changes().empty()); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("NodeDeleted change_id=2 node=1,2", changes[0]); + } +} + +// Verifies DeleteNode isn't allowed from a separate connection. +TEST_F(ViewManagerTest, DISABLED_DeleteNodeFromAnotherConnectionDisallowed) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + EXPECT_FALSE(connection2_->DeleteNode(BuildNodeId(1, 1), 1)); +} + +// Verifies DeleteView isn't allowed from a separate connection. +TEST_F(ViewManagerTest, DISABLED_DeleteViewFromAnotherConnectionDisallowed) { + ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1))); + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + EXPECT_FALSE(connection2_->DeleteView(BuildViewId(1, 1))); +} + +// Verifies if a node was deleted and then reused that other clients are +// properly notified. +TEST_F(ViewManagerTest, DISABLED_ReuseDeletedNodeId) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + // Add 2 to 1. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + EXPECT_EQ( + "HierarchyChanged change_id=1 node=1,2 new_parent=1,1 old_parent=null", + changes[0]); + EXPECT_EQ("[node=1,2 parent=1,1 view=null]", + ChangeNodeDescription(connection2_->changes())); + } + + // Delete 2. + { + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("NodeDeleted change_id=2 node=1,2", changes[0]); + } + + // Create 2 again, and add it back to 1. Should get the same notification. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 3)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + EXPECT_EQ( + "HierarchyChanged change_id=3 node=1,2 new_parent=1,1 old_parent=null", + changes[0]); + EXPECT_EQ("[node=1,2 parent=1,1 view=null]", + ChangeNodeDescription(connection2_->changes())); + } +} + +// Assertions around setting a view. +TEST_F(ViewManagerTest, DISABLED_SetView) { + // Create nodes 1, 2 and 3 and the view 11. Nodes 2 and 3 are parented to 1. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11))); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 3), 2)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + // Set view 11 on node 1. + { + ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 1), + BuildViewId(1, 11))); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ViewReplaced node=1,1 new_view=1,11 old_view=null", + changes[0]); + } + + // Set view 11 on node 2. + { + ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 2), BuildViewId(1, 11))); + + connection2_->DoRunLoopUntilChangesCount(2); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(2u, changes.size()); + EXPECT_EQ("ViewReplaced node=1,1 new_view=null old_view=1,11", + changes[0]); + EXPECT_EQ("ViewReplaced node=1,2 new_view=1,11 old_view=null", + changes[1]); + } +} + +// Verifies deleting a node with a view sends correct notifications. +TEST_F(ViewManagerTest, DISABLED_DeleteNodeWithView) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11))); + + // Set view 11 on node 2. + ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 2), BuildViewId(1, 11))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + // Delete node 2. The second connection should not see this because the node + // was not known to it. + { + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 1)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + } + + // Parent 3 to 1. + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 3), 2)); + connection2_->DoRunLoopUntilChangesCount(1); + + // Set view 11 on node 3. + { + ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 3), BuildViewId(1, 11))); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ViewReplaced node=1,3 new_view=1,11 old_view=null", changes[0]); + } + + // Delete 3. + { + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 3), 3)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("NodeDeleted change_id=3 node=1,3", changes[0]); + } +} + +// Sets view from one connection on another. +TEST_F(ViewManagerTest, DISABLED_SetViewFromSecondConnection) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + // Create a view in the second connection. + ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 51))); + + // Attach view to node 1 in the first connection. + { + ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 51))); + connection_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ViewReplaced node=1,1 new_view=2,51 old_view=null", changes[0]); + } + + // Shutdown the second connection and verify view is removed. + { + DestroySecondConnection(); + connection_->DoRunLoopUntilChangesCount(2); + const Changes changes(ChangesToDescription1(connection_->changes())); + ASSERT_EQ(2u, changes.size()); + EXPECT_EQ("ViewReplaced node=1,1 new_view=null old_view=2,51", changes[0]); + EXPECT_EQ("ViewDeleted view=2,51", changes[1]); + } +} + +// Assertions for GetNodeTree. +TEST_F(ViewManagerTest, DISABLED_GetNodeTree) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Create 11 in first connection and make it a child of 1. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11))); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 11), 2)); + + // Create two nodes in second connection, 2 and 3, both children of 1. + ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 2))); + ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 3))); + ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 2), 3)); + ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3), 4)); + + // Attach view to node 11 in the first connection. + ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 51))); + ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 11), BuildViewId(1, 51))); + + // Verifies GetNodeTree() on the root. + { + std::vector<TestNode> nodes; + connection_->GetNodeTree(BuildNodeId(0, 1), &nodes); + ASSERT_EQ(5u, nodes.size()); + EXPECT_EQ("node=0,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes[1].ToString()); + EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes[2].ToString()); + EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[3].ToString()); + EXPECT_EQ("node=2,3 parent=1,1 view=null", nodes[4].ToString()); + } + + // Verifies GetNodeTree() on the node 1,1. + { + std::vector<TestNode> nodes; + connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes); + ASSERT_EQ(4u, nodes.size()); + EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes[1].ToString()); + EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[2].ToString()); + EXPECT_EQ("node=2,3 parent=1,1 view=null", nodes[3].ToString()); + } + + // Connection 2 shouldn't be able to get the root tree. + { + std::vector<TestNode> nodes; + connection2_->GetNodeTree(BuildNodeId(0, 1), &nodes); + ASSERT_EQ(0u, nodes.size()); + } +} + +TEST_F(ViewManagerTest, DISABLED_SetNodeBounds) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + ASSERT_TRUE(connection_->SetNodeBounds(BuildNodeId(1, 1), + gfx::Rect(0, 0, 100, 100))); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("BoundsChanged node=1,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100", + changes[0]); + + // Should not be possible to change the bounds of a node created by another + // connection. + ASSERT_FALSE(connection2_->SetNodeBounds(BuildNodeId(1, 1), + gfx::Rect(0, 0, 0, 0))); +} + +// Various assertions around SetRoots. +TEST_F(ViewManagerTest, DISABLED_SetRoots) { + // Create 1, 2, and 3 in the first connection. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3))); + + // Parent 1 to the root. + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + + // Establish the second connection and give it the roots 1 and 3. + { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoots( + BuildNodeId(1, 1), BuildNodeId(1, 3))); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url", changes[0]); + EXPECT_EQ("[node=1,1 parent=null view=null]," + "[node=1,3 parent=null view=null]", + ChangeNodeDescription(connection2_->changes())); + } + + // Create 4 and add it to the root, connection 2 should only get id advanced. + { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 4))); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 4), 2)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); + } + + // Move 4 under 3, this should expose 4 to the client. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 4), 3)); + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=3 node=1,4 new_parent=1,3 " + "old_parent=null", changes[0]); + EXPECT_EQ("[node=1,4 parent=1,3 view=null]", + ChangeNodeDescription(connection2_->changes())); + } + + // Move 4 under 2, since 2 isn't a root client should get a delete. + { + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 4), 4)); + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("NodeDeleted change_id=4 node=1,4", changes[0]); + } + + // Delete 4, client shouldn't receive a delete since it should no longer know + // about 4. + { + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 4), 5)); + + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("ServerChangeIdAdvanced 6", changes[0]); + } +} + +// Verify AddNode fails when trying to manipulate nodes in other roots. +TEST_F(ViewManagerTest, DISABLED_CantMoveNodesFromOtherRoot) { + // Create 1 and 2 in the first connection. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + // Try to move 2 to be a child of 1 from connection 2. This should fail as 2 + // should not be able to access 1. + ASSERT_FALSE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + + // Try to reparent 1 to the root. A connection is not allowed to reparent its + // roots. + ASSERT_FALSE(connection2_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); +} + +// Verify RemoveNodeFromParent fails for nodes that are descendants of the +// roots. +TEST_F(ViewManagerTest, DISABLED_CantRemoveNodesInOtherRoots) { + // Create 1 and 2 in the first connection and parent both to the root. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2), 2)); + + // Establish the second connection and give it the root 1. + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + // Connection 2 should not be able to remove node 2 or 1 from its parent. + ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 2), 3)); + ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 1), 3)); + + // Create nodes 10 and 11 in 2. + ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 10))); + ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 11))); + + // Parent 11 to 10. + ASSERT_TRUE(connection2_->AddNode(BuildNodeId(2, 10), BuildNodeId(2, 11), 3)); + // Remove 11 from 10. + ASSERT_TRUE(connection2_->RemoveNodeFromParent( BuildNodeId(2, 11), 4)); + + // Verify nothing was actually removed. + { + std::vector<TestNode> nodes; + connection_->GetNodeTree(BuildNodeId(0, 1), &nodes); + ASSERT_EQ(3u, nodes.size()); + EXPECT_EQ("node=0,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes[1].ToString()); + EXPECT_EQ("node=1,2 parent=0,1 view=null", nodes[2].ToString()); + } +} + +// Verify SetView fails for nodes that are not descendants of the roots. +TEST_F(ViewManagerTest, DISABLED_CantRemoveSetViewInOtherRoots) { + // Create 1 and 2 in the first connection and parent both to the root. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2), 2)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + // Create a view in the second connection. + ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 51))); + + // Connection 2 should be able to set the view on node 1 (it's root), but not + // on 2. + ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 51))); + ASSERT_FALSE(connection2_->SetView(BuildNodeId(1, 2), BuildViewId(2, 51))); +} + +// Verify GetNodeTree fails for nodes that are not descendants of the roots. +TEST_F(ViewManagerTest, DISABLED_CantGetNodeTreeOfOtherRoots) { + // Create 1 and 2 in the first connection and parent both to the root. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2), 2)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + std::vector<TestNode> nodes; + + // Should get nothing for the root. + connection2_->GetNodeTree(BuildNodeId(0, 1), &nodes); + ASSERT_TRUE(nodes.empty()); + + // Should get nothing for node 2. + connection2_->GetNodeTree(BuildNodeId(1, 2), &nodes); + ASSERT_TRUE(nodes.empty()); + + // Should get node 1 if asked for. + connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes); + ASSERT_EQ(1u, nodes.size()); + EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); +} + +TEST_F(ViewManagerTest, DISABLED_ConnectTwice) { + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); + + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + + // Try to connect again to 1,1, this should fail as already connected to that + // root. + { + std::vector<Id> node_ids; + node_ids.push_back(BuildNodeId(1, 1)); + ASSERT_FALSE(connection_->Embed(node_ids)); + } + + // Connecting to 1,2 should succeed and end up in connection2. + { + std::vector<Id> node_ids; + node_ids.push_back(BuildNodeId(1, 2)); + ASSERT_TRUE(connection_->Embed(node_ids)); + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("OnRootsAdded", changes[0]); + EXPECT_EQ("[node=1,2 parent=1,1 view=null]", + ChangeNodeDescription(connection2_->changes())); + } +} + +TEST_F(ViewManagerTest, DISABLED_OnViewInput) { + // Create node 1 and assign a view from connection 2 to it. + ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); + ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11))); + ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 11))); + + // Dispatch an event to the view and verify its received. + { + EventPtr event(Event::New()); + event->action = 1; + connection_->view_manager()->DispatchOnViewInputEvent( + BuildViewId(2, 11), + event.Pass()); + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("InputEvent view=2,11 event_action=1", changes[0]); + } +} + +// TODO(sky): add coverage of test that destroys connections and ensures other +// connections get deletion notification (or advanced server id). + +// TODO(sky): need to better track changes to initial connection. For example, +// that SetBounsdNodes/AddNode and the like don't result in messages to the +// originating connection. + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/window_tree_host_impl.cc b/chromium/mojo/services/view_manager/window_tree_host_impl.cc new file mode 100644 index 00000000000..c344ba4bfe2 --- /dev/null +++ b/chromium/mojo/services/view_manager/window_tree_host_impl.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/view_manager/window_tree_host_impl.h" + +#include "mojo/public/c/gles2/gles2.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/view_manager/context_factory_impl.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/compositor/compositor.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" + +namespace mojo { +namespace view_manager { +namespace service { + +// TODO(sky): nuke this. It shouldn't be static. +// static +ContextFactoryImpl* WindowTreeHostImpl::context_factory_ = NULL; + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostImpl, public: + +WindowTreeHostImpl::WindowTreeHostImpl( + NativeViewportPtr viewport, + const gfx::Rect& bounds, + const base::Callback<void()>& compositor_created_callback) + : native_viewport_(viewport.Pass()), + compositor_created_callback_(compositor_created_callback), + bounds_(bounds) { + native_viewport_.set_client(this); + native_viewport_->Create(Rect::From(bounds)); + + MessagePipe pipe; + native_viewport_->CreateGLES2Context( + MakeRequest<CommandBuffer>(pipe.handle0.Pass())); + + // The ContextFactory must exist before any Compositors are created. + if (context_factory_) { + delete context_factory_; + context_factory_ = NULL; + } + context_factory_ = new ContextFactoryImpl(pipe.handle1.Pass()); + aura::Env::GetInstance()->set_context_factory(context_factory_); + CHECK(context_factory_) << "No GL bindings."; +} + +WindowTreeHostImpl::~WindowTreeHostImpl() { + DestroyCompositor(); + DestroyDispatcher(); +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostImpl, aura::WindowTreeHost implementation: + +ui::EventSource* WindowTreeHostImpl::GetEventSource() { + return this; +} + +gfx::AcceleratedWidget WindowTreeHostImpl::GetAcceleratedWidget() { + NOTIMPLEMENTED() << "GetAcceleratedWidget"; + return gfx::kNullAcceleratedWidget; +} + +void WindowTreeHostImpl::Show() { + window()->Show(); + native_viewport_->Show(); +} + +void WindowTreeHostImpl::Hide() { + native_viewport_->Hide(); + window()->Hide(); +} + +gfx::Rect WindowTreeHostImpl::GetBounds() const { + return bounds_; +} + +void WindowTreeHostImpl::SetBounds(const gfx::Rect& bounds) { + native_viewport_->SetBounds(Rect::From(bounds)); +} + +gfx::Point WindowTreeHostImpl::GetLocationOnNativeScreen() const { + return gfx::Point(0, 0); +} + +void WindowTreeHostImpl::SetCapture() { + NOTIMPLEMENTED(); +} + +void WindowTreeHostImpl::ReleaseCapture() { + NOTIMPLEMENTED(); +} + +void WindowTreeHostImpl::PostNativeEvent( + const base::NativeEvent& native_event) { + NOTIMPLEMENTED(); +} + +void WindowTreeHostImpl::OnDeviceScaleFactorChanged(float device_scale_factor) { + NOTIMPLEMENTED(); +} + +void WindowTreeHostImpl::SetCursorNative(gfx::NativeCursor cursor) { + NOTIMPLEMENTED(); +} + +void WindowTreeHostImpl::MoveCursorToNative(const gfx::Point& location) { + NOTIMPLEMENTED(); +} + +void WindowTreeHostImpl::OnCursorVisibilityChangedNative(bool show) { + NOTIMPLEMENTED(); +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostImpl, ui::EventSource implementation: + +ui::EventProcessor* WindowTreeHostImpl::GetEventProcessor() { + return dispatcher(); +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostImpl, NativeViewportClient implementation: + +void WindowTreeHostImpl::OnCreated() { + CreateCompositor(GetAcceleratedWidget()); + compositor_created_callback_.Run(); +} + +void WindowTreeHostImpl::OnBoundsChanged(RectPtr bounds) { + bounds_ = bounds.To<gfx::Rect>(); + OnHostResized(bounds_.size()); +} + +void WindowTreeHostImpl::OnDestroyed() { + base::MessageLoop::current()->Quit(); +} + +void WindowTreeHostImpl::OnEvent(EventPtr event, + const mojo::Callback<void()>& callback) { + switch (event->action) { + case ui::ET_MOUSE_PRESSED: + case ui::ET_MOUSE_DRAGGED: + case ui::ET_MOUSE_RELEASED: + case ui::ET_MOUSE_MOVED: + case ui::ET_MOUSE_ENTERED: + case ui::ET_MOUSE_EXITED: { + gfx::Point location(event->location->x, event->location->y); + ui::MouseEvent ev(static_cast<ui::EventType>(event->action), location, + location, event->flags, 0); + SendEventToProcessor(&ev); + break; + } + case ui::ET_KEY_PRESSED: + case ui::ET_KEY_RELEASED: { + ui::KeyEvent ev( + static_cast<ui::EventType>(event->action), + static_cast<ui::KeyboardCode>(event->key_data->key_code), + event->flags, event->key_data->is_char); + SendEventToProcessor(&ev); + break; + } + // TODO(beng): touch, etc. + } + callback.Run(); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo diff --git a/chromium/mojo/services/view_manager/window_tree_host_impl.h b/chromium/mojo/services/view_manager/window_tree_host_impl.h new file mode 100644 index 00000000000..6c11f9a9441 --- /dev/null +++ b/chromium/mojo/services/view_manager/window_tree_host_impl.h @@ -0,0 +1,76 @@ +// Copyright (c) 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_AURA_WINDOW_TREE_HOST_MOJO_H_ +#define MOJO_AURA_WINDOW_TREE_HOST_MOJO_H_ + +#include "base/bind.h" +#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" +#include "ui/aura/window_tree_host.h" +#include "ui/events/event_source.h" +#include "ui/gfx/rect.h" + +namespace ui { +class ContextFactory; +} + +namespace mojo { +namespace view_manager { +namespace service { + +class ContextFactoryImpl; + +class WindowTreeHostImpl : public aura::WindowTreeHost, + public ui::EventSource, + public NativeViewportClient { + public: + WindowTreeHostImpl(NativeViewportPtr viewport, + const gfx::Rect& bounds, + const base::Callback<void()>& compositor_created_callback); + virtual ~WindowTreeHostImpl(); + + gfx::Rect bounds() const { return bounds_; } + + private: + // WindowTreeHost: + virtual ui::EventSource* GetEventSource() OVERRIDE; + virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual gfx::Rect GetBounds() const OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE; + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; + virtual void SetCursorNative(gfx::NativeCursor cursor) OVERRIDE; + virtual void MoveCursorToNative(const gfx::Point& location) OVERRIDE; + virtual void OnCursorVisibilityChangedNative(bool show) OVERRIDE; + + // ui::EventSource: + virtual ui::EventProcessor* GetEventProcessor() OVERRIDE; + + // Overridden from NativeViewportClient: + virtual void OnCreated() OVERRIDE; + virtual void OnDestroyed() OVERRIDE; + virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE; + virtual void OnEvent(EventPtr event, + const mojo::Callback<void()>& callback) OVERRIDE; + + static ContextFactoryImpl* context_factory_; + + NativeViewportPtr native_viewport_; + base::Callback<void()> compositor_created_callback_; + + gfx::Rect bounds_; + + DISALLOW_COPY_AND_ASSIGN(WindowTreeHostImpl); +}; + +} // namespace service +} // namespace view_manager +} // namespace mojo + +#endif // MOJO_AURA_WINDOW_TREE_HOST_MOJO_H_ |