summaryrefslogtreecommitdiffstats
path: root/chromium/mojo/services
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/mojo/services
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (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')
-rw-r--r--chromium/mojo/services/DEPS10
-rw-r--r--chromium/mojo/services/dbus_echo/DEPS5
-rw-r--r--chromium/mojo/services/dbus_echo/dbus_echo_service.cc58
-rw-r--r--chromium/mojo/services/dbus_echo/echo.mojom11
-rw-r--r--chromium/mojo/services/gles2/DEPS5
-rw-r--r--chromium/mojo/services/gles2/command_buffer.mojom49
-rw-r--r--chromium/mojo/services/gles2/command_buffer_impl.cc198
-rw-r--r--chromium/mojo/services/gles2/command_buffer_impl.h72
-rw-r--r--chromium/mojo/services/gles2/command_buffer_type_conversions.cc40
-rw-r--r--chromium/mojo/services/gles2/command_buffer_type_conversions.h27
-rw-r--r--chromium/mojo/services/gles2/mojo_buffer_backing.cc36
-rw-r--r--chromium/mojo/services/gles2/mojo_buffer_backing.h40
-rw-r--r--chromium/mojo/services/launcher/DEPS3
-rw-r--r--chromium/mojo/services/launcher/launcher.cc176
-rw-r--r--chromium/mojo/services/native_viewport/DEPS8
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport.h62
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_android.cc161
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_android.h71
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_export.h32
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_mac.mm88
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_service.cc165
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_service.h18
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_stub.cc50
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_win.cc167
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_x11.cc163
-rw-r--r--chromium/mojo/services/network/DEPS4
-rw-r--r--chromium/mojo/services/network/main.cc30
-rw-r--r--chromium/mojo/services/network/network_context.cc123
-rw-r--r--chromium/mojo/services/network/network_context.h42
-rw-r--r--chromium/mojo/services/network/network_service_impl.cc23
-rw-r--r--chromium/mojo/services/network/network_service_impl.h29
-rw-r--r--chromium/mojo/services/network/url_loader_impl.cc253
-rw-r--r--chromium/mojo/services/network/url_loader_impl.h61
-rw-r--r--chromium/mojo/services/public/cpp/geometry/DEPS3
-rw-r--r--chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h37
-rw-r--r--chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc58
-rw-r--r--chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h32
-rw-r--r--chromium/mojo/services/public/cpp/input_events/DEPS3
-rw-r--r--chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h30
-rw-r--r--chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc68
-rw-r--r--chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h32
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/DEPS4
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/DEPS11
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node.cc432
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc23
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc23
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node_private.h62
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view.cc97
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc841
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h162
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc24
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h28
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc14
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc23
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_private.h44
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/node.h108
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/node_observer.h66
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/types.h27
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/util.h32
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view.h60
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view_manager.h44
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h25
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view_observer.h37
-rw-r--r--chromium/mojo/services/public/interfaces/geometry/geometry.mojom34
-rw-r--r--chromium/mojo/services/public/interfaces/input_events/input_events.mojom27
-rw-r--r--chromium/mojo/services/public/interfaces/launcher/launcher.mojom36
-rw-r--r--chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom28
-rw-r--r--chromium/mojo/services/public/interfaces/navigation/navigation.mojom36
-rw-r--r--chromium/mojo/services/public/interfaces/network/network_error.mojom12
-rw-r--r--chromium/mojo/services/public/interfaces/network/network_service.mojom15
-rw-r--r--chromium/mojo/services/public/interfaces/network/url_loader.mojom94
-rw-r--r--chromium/mojo/services/public/interfaces/surfaces/quads.mojom165
-rw-r--r--chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom11
-rw-r--r--chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom57
-rw-r--r--chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom193
-rw-r--r--chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom12
-rw-r--r--chromium/mojo/services/test_service/test_service.mojom11
-rw-r--r--chromium/mojo/services/test_service/test_service_application.cc44
-rw-r--r--chromium/mojo/services/test_service/test_service_application.h33
-rw-r--r--chromium/mojo/services/test_service/test_service_impl.cc32
-rw-r--r--chromium/mojo/services/test_service/test_service_impl.h35
-rw-r--r--chromium/mojo/services/view_manager/DEPS22
-rw-r--r--chromium/mojo/services/view_manager/context_factory_impl.cc75
-rw-r--r--chromium/mojo/services/view_manager/context_factory_impl.h56
-rw-r--r--chromium/mojo/services/view_manager/ids.h94
-rw-r--r--chromium/mojo/services/view_manager/main.cc35
-rw-r--r--chromium/mojo/services/view_manager/node.cc189
-rw-r--r--chromium/mojo/services/view_manager/node.h112
-rw-r--r--chromium/mojo/services/view_manager/node_delegate.h45
-rw-r--r--chromium/mojo/services/view_manager/root_node_manager.cc265
-rw-r--r--chromium/mojo/services/view_manager/root_node_manager.h224
-rw-r--r--chromium/mojo/services/view_manager/root_view_manager.cc159
-rw-r--r--chromium/mojo/services/view_manager/root_view_manager.h72
-rw-r--r--chromium/mojo/services/view_manager/root_view_manager_delegate.h27
-rw-r--r--chromium/mojo/services/view_manager/screen_impl.cc80
-rw-r--r--chromium/mojo/services/view_manager/screen_impl.h58
-rw-r--r--chromium/mojo/services/view_manager/test_change_tracker.cc263
-rw-r--r--chromium/mojo/services/view_manager/test_change_tracker.h134
-rw-r--r--chromium/mojo/services/view_manager/view.cc28
-rw-r--r--chromium/mojo/services/view_manager/view.h50
-rw-r--r--chromium/mojo/services/view_manager/view_manager_export.h31
-rw-r--r--chromium/mojo/services/view_manager/view_manager_init_service_impl.cc62
-rw-r--r--chromium/mojo/services/view_manager/view_manager_init_service_impl.h79
-rw-r--r--chromium/mojo/services/view_manager/view_manager_service_impl.cc803
-rw-r--r--chromium/mojo/services/view_manager/view_manager_service_impl.h269
-rw-r--r--chromium/mojo/services/view_manager/view_manager_unittest.cc1370
-rw-r--r--chromium/mojo/services/view_manager/window_tree_host_impl.cc177
-rw-r--r--chromium/mojo/services/view_manager/window_tree_host_impl.h76
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_