Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn
new file mode 100644
index 0000000..5b4498c
--- /dev/null
+++ b/mojo/services/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+group("services") {
+ deps = [
+ "//mojo/services/clipboard",
+ "//mojo/services/gles2:bindings",
+ "//mojo/services/html_viewer",
+ "//mojo/services/network",
+ "//mojo/services/public/interfaces/clipboard",
+ "//mojo/services/public/interfaces/content_handler",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/input_events",
+ "//mojo/services/public/interfaces/native_viewport",
+ "//mojo/services/public/interfaces/navigation",
+ "//mojo/services/public/interfaces/network",
+ "//mojo/services/public/interfaces/surfaces",
+ "//mojo/services/surfaces",
+ "//mojo/services/test_service:bindings",
+ ]
+ if (!is_android) {
+ deps += ["//mojo/services/native_viewport"]
+ }
+ if (use_aura) {
+ deps += [
+ "//mojo/services/public/interfaces/view_manager",
+ "//mojo/services/public/interfaces/window_manager",
+ "//mojo/services/view_manager",
+ "//mojo/services/window_manager",
+ ]
+ }
+}
diff --git a/mojo/services/DEPS b/mojo/services/DEPS
new file mode 100644
index 0000000..2bbe62d
--- /dev/null
+++ b/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/mojo/services/clipboard/BUILD.gn b/mojo/services/clipboard/BUILD.gn
new file mode 100644
index 0000000..5c3b78e
--- /dev/null
+++ b/mojo/services/clipboard/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_clipboard
+component("clipboard") {
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_component",
+ "//mojo/public/cpp/bindings",
+ "//mojo/services/public/interfaces/clipboard",
+ "//ui/base",
+ ]
+
+ sources = [
+ "clipboard_standalone_impl.cc",
+ "clipboard_standalone_impl.h",
+ "main.cc",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_clipboard_unittests
+test("mojo_clipboard_unittests") {
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//mojo/application",
+ "//mojo/application_manager",
+ "//mojo/common",
+ "//mojo/common/test:run_all_unittests",
+ "//mojo/edk/system",
+ "//mojo/environment:chromium",
+ "//mojo/services/public/interfaces/clipboard:clipboard",
+ "//mojo/shell:test_support",
+ "//testing/gtest",
+ ]
+
+ sources = [
+ "clipboard_standalone_unittest.cc",
+ ]
+}
diff --git a/mojo/services/clipboard/DEPS b/mojo/services/clipboard/DEPS
new file mode 100644
index 0000000..79cfc5d
--- /dev/null
+++ b/mojo/services/clipboard/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/application",
+ "+mojo/services",
+]
diff --git a/mojo/services/clipboard/clipboard_standalone_impl.cc b/mojo/services/clipboard/clipboard_standalone_impl.cc
new file mode 100644
index 0000000..e1c7e2c
--- /dev/null
+++ b/mojo/services/clipboard/clipboard_standalone_impl.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 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/clipboard/clipboard_standalone_impl.h"
+
+namespace mojo {
+
+typedef std::vector<uint8_t> ByteVector;
+
+// ClipboardData contains data copied to the Clipboard for a variety of formats.
+// It mostly just provides APIs to cleanly access and manipulate this data.
+class ClipboardStandaloneImpl::ClipboardData {
+ public:
+ ClipboardData() {}
+ ~ClipboardData() {}
+
+ std::vector<std::string> GetMimeTypes() const {
+ std::vector<std::string> types;
+ for (std::map<std::string, ByteVector>::const_iterator it =
+ data_types_.begin();
+ it != data_types_.end();
+ ++it) {
+ types.push_back(it->first);
+ }
+
+ return types;
+ }
+
+ void SetData(std::map<std::string, ByteVector>* data) {
+ std::swap(data_types_, *data);
+ }
+
+ bool GetData(const std::string& mime_type, ByteVector* data) const {
+ std::map<std::string, ByteVector>::const_iterator it =
+ data_types_.find(mime_type);
+ if (it != data_types_.end()) {
+ *data = it->second;
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ std::map<std::string, ByteVector> data_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardData);
+};
+
+ClipboardStandaloneImpl::ClipboardStandaloneImpl() {
+ for (int i = 0; i < kNumClipboards; ++i) {
+ sequence_number_[i] = 0;
+ clipboard_state_[i].reset(new ClipboardData);
+ }
+}
+
+ClipboardStandaloneImpl::~ClipboardStandaloneImpl() {
+}
+
+void ClipboardStandaloneImpl::GetSequenceNumber(
+ Clipboard::Type clipboard_type,
+ const mojo::Callback<void(uint64_t)>& callback) {
+ callback.Run(sequence_number_[clipboard_type]);
+}
+
+void ClipboardStandaloneImpl::GetAvailableMimeTypes(
+ Clipboard::Type clipboard_type,
+ const mojo::Callback<void(mojo::Array<mojo::String>)>& callback) {
+ mojo::Array<mojo::String> types = mojo::Array<mojo::String>::From(
+ clipboard_state_[clipboard_type]->GetMimeTypes());
+ callback.Run(types.Pass());
+}
+
+void ClipboardStandaloneImpl::ReadMimeType(
+ Clipboard::Type clipboard_type,
+ const mojo::String& mime_type,
+ const mojo::Callback<void(mojo::Array<uint8_t>)>& callback) {
+ ByteVector mime_data;
+ if (clipboard_state_[clipboard_type]->GetData(
+ mime_type.To<std::string>(), &mime_data)) {
+ callback.Run(mojo::Array<uint8_t>::From(mime_data).Pass());
+ return;
+ }
+
+ callback.Run(mojo::Array<uint8_t>().Pass());
+}
+
+void ClipboardStandaloneImpl::WriteClipboardData(
+ Clipboard::Type clipboard_type,
+ mojo::Array<MimeTypePairPtr> data) {
+ std::map<std::string, ByteVector> mime_data;
+ for (size_t i = 0; i < data.size(); ++i)
+ mime_data[data[i]->mime_type] = data[i]->data;
+
+ sequence_number_[clipboard_type]++;
+ clipboard_state_[clipboard_type]->SetData(&mime_data);
+}
+
+} // namespace mojo
diff --git a/mojo/services/clipboard/clipboard_standalone_impl.h b/mojo/services/clipboard/clipboard_standalone_impl.h
new file mode 100644
index 0000000..09d7424
--- /dev/null
+++ b/mojo/services/clipboard/clipboard_standalone_impl.h
@@ -0,0 +1,58 @@
+// Copyright (c) 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_CLIPBOARD_CLIPBOARD_STANDALONE_IMPL_H_
+#define MOJO_SERVICES_CLIPBOARD_CLIPBOARD_STANDALONE_IMPL_H_
+
+#include <base/memory/scoped_ptr.h>
+#include <string>
+
+#include "mojo/services/public/interfaces/clipboard/clipboard.mojom.h"
+
+namespace mojo {
+
+// Stub clipboard implementation.
+//
+// Eventually, we'll actually want to interact with the system clipboard, but
+// that's hard today because the system clipboard is asynchronous (on X11), the
+// ui::Clipboard interface is synchronous (which is what we'd use), mojo is
+// asynchronous across processes, and the WebClipboard interface is synchronous
+// (which is at least tractable).
+class ClipboardStandaloneImpl : public InterfaceImpl<mojo::Clipboard> {
+ public:
+ // mojo::Clipboard exposes three possible clipboards.
+ static const int kNumClipboards = 3;
+
+ ClipboardStandaloneImpl();
+ virtual ~ClipboardStandaloneImpl();
+
+ // InterfaceImpl<mojo::Clipboard> implementation.
+ virtual void GetSequenceNumber(
+ Clipboard::Type clipboard_type,
+ const mojo::Callback<void(uint64_t)>& callback) override;
+ virtual void GetAvailableMimeTypes(
+ Clipboard::Type clipboard_types,
+ const mojo::Callback<void(mojo::Array<mojo::String>)>& callback) override;
+ virtual void ReadMimeType(
+ Clipboard::Type clipboard_type,
+ const mojo::String& mime_type,
+ const mojo::Callback<void(mojo::Array<uint8_t>)>& callback) override;
+ virtual void WriteClipboardData(Clipboard::Type clipboard_type,
+ mojo::Array<MimeTypePairPtr> data) override;
+
+ private:
+ uint64_t sequence_number_[kNumClipboards];
+
+ // Internal struct which stores the current state of the clipboard.
+ class ClipboardData;
+
+ // The current clipboard state. This is what is read from.
+ scoped_ptr<ClipboardData> clipboard_state_[kNumClipboards];
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardStandaloneImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_CLIPBOARD_CLIPBOARD_STANDALONE_IMPL_H_
diff --git a/mojo/services/clipboard/clipboard_standalone_unittest.cc b/mojo/services/clipboard/clipboard_standalone_unittest.cc
new file mode 100644
index 0000000..c297b94
--- /dev/null
+++ b/mojo/services/clipboard/clipboard_standalone_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 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/bind.h"
+#include "base/macros.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/public/interfaces/clipboard/clipboard.mojom.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void CopyUint64AndEndRunloop(uint64_t* output,
+ base::RunLoop* run_loop,
+ uint64_t input) {
+ *output = input;
+ run_loop->Quit();
+}
+
+void CopyStringAndEndRunloop(std::string* output,
+ bool* string_is_null,
+ base::RunLoop* run_loop,
+ const mojo::Array<uint8_t>& input) {
+ *string_is_null = input.is_null();
+ *output = input.is_null() ? "" : input.To<std::string>();
+ run_loop->Quit();
+}
+
+void CopyVectorStringAndEndRunloop(std::vector<std::string>* output,
+ base::RunLoop* run_loop,
+ const mojo::Array<mojo::String>& input) {
+ *output = input.To<std::vector<std::string> >();
+ run_loop->Quit();
+}
+
+const char* kUninitialized = "Uninitialized data";
+const char* kPlainTextData = "Some plain data";
+const char* kHtmlData = "<html>data</html>";
+
+} // namespace
+
+namespace mojo {
+namespace service {
+
+class ClipboardStandaloneTest : public testing::Test {
+ public:
+ ClipboardStandaloneTest() {}
+ virtual ~ClipboardStandaloneTest() {}
+
+ virtual void SetUp() override {
+ test_helper_.Init();
+
+ test_helper_.application_manager()->ConnectToService(
+ GURL("mojo:mojo_clipboard"), &clipboard_);
+ }
+
+ uint64_t GetSequenceNumber() {
+ base::RunLoop run_loop;
+ uint64_t sequence_num = 999999;
+ clipboard_->GetSequenceNumber(
+ mojo::Clipboard::TYPE_COPY_PASTE,
+ base::Bind(&CopyUint64AndEndRunloop, &sequence_num, &run_loop));
+ run_loop.Run();
+ return sequence_num;
+ }
+
+ std::vector<std::string> GetAvailableFormatMimeTypes() {
+ base::RunLoop run_loop;
+ std::vector<std::string> types;
+ types.push_back(kUninitialized);
+ clipboard_->GetAvailableMimeTypes(
+ mojo::Clipboard::TYPE_COPY_PASTE,
+ base::Bind(&CopyVectorStringAndEndRunloop, &types, &run_loop));
+ run_loop.Run();
+ return types;
+ }
+
+ bool GetDataOfType(const std::string& mime_type, std::string* data) {
+ base::RunLoop run_loop;
+ bool is_null = false;
+ clipboard_->ReadMimeType(
+ mojo::Clipboard::TYPE_COPY_PASTE,
+ mime_type,
+ base::Bind(&CopyStringAndEndRunloop, data, &is_null, &run_loop));
+ run_loop.Run();
+ return !is_null;
+ }
+
+ void SetStringText(const std::string& data) {
+ Array<MimeTypePairPtr> mime_data;
+ MimeTypePairPtr text_data(MimeTypePair::New());
+ text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+ text_data->data = Array<uint8_t>::From(data).Pass();
+ mime_data.push_back(text_data.Pass());
+ clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+ mime_data.Pass());
+ }
+
+ protected:
+ base::ShadowingAtExitManager at_exit_;
+ shell::ShellTestHelper test_helper_;
+
+ ClipboardPtr clipboard_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardStandaloneTest);
+};
+
+TEST_F(ClipboardStandaloneTest, EmptyClipboardOK) {
+ EXPECT_EQ(0ul, GetSequenceNumber());
+ EXPECT_TRUE(GetAvailableFormatMimeTypes().empty());
+ std::string data;
+ EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+}
+
+TEST_F(ClipboardStandaloneTest, CanReadBackText) {
+ std::string data;
+ EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+ EXPECT_EQ(0ul, GetSequenceNumber());
+
+ SetStringText(kPlainTextData);
+ EXPECT_EQ(1ul, GetSequenceNumber());
+
+ EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+ EXPECT_EQ(kPlainTextData, data);
+}
+
+TEST_F(ClipboardStandaloneTest, CanSetMultipleDataTypesAtOnce) {
+ Array<MimeTypePairPtr> mime_data;
+ MimeTypePairPtr text_data(MimeTypePair::New());
+ text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+ text_data->data = Array<uint8_t>::From(std::string(kPlainTextData)).Pass();
+ mime_data.push_back(text_data.Pass());
+
+ MimeTypePairPtr html_data(MimeTypePair::New());
+ html_data->mime_type = mojo::Clipboard::MIME_TYPE_HTML;
+ html_data->data = Array<uint8_t>::From(std::string(kHtmlData)).Pass();
+ mime_data.push_back(html_data.Pass());
+
+ clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+ mime_data.Pass());
+
+ EXPECT_EQ(1ul, GetSequenceNumber());
+
+ std::string data;
+ EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+ EXPECT_EQ(kPlainTextData, data);
+ EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_HTML, &data));
+ EXPECT_EQ(kHtmlData, data);
+}
+
+TEST_F(ClipboardStandaloneTest, CanClearClipboardWithNull) {
+ std::string data;
+ SetStringText(kPlainTextData);
+ EXPECT_EQ(1ul, GetSequenceNumber());
+
+ EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+ EXPECT_EQ(kPlainTextData, data);
+
+ Array<MimeTypePairPtr> mime_data;
+ clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+ mime_data.Pass());
+
+ EXPECT_EQ(2ul, GetSequenceNumber());
+ EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+}
+
+TEST_F(ClipboardStandaloneTest, CanClearClipboardWithZeroArray) {
+ std::string data;
+ SetStringText(kPlainTextData);
+ EXPECT_EQ(1ul, GetSequenceNumber());
+
+ EXPECT_TRUE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+ EXPECT_EQ(kPlainTextData, data);
+
+ Array<MimeTypePairPtr> mime_data(0);
+ clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE,
+ mime_data.Pass());
+
+ EXPECT_EQ(2ul, GetSequenceNumber());
+ EXPECT_FALSE(GetDataOfType(mojo::Clipboard::MIME_TYPE_TEXT, &data));
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/clipboard/main.cc b/mojo/services/clipboard/main.cc
new file mode 100644
index 0000000..fcc33d1
--- /dev/null
+++ b/mojo/services/clipboard/main.cc
@@ -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.
+
+#include "base/at_exit.h"
+#include "base/base_paths.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/services/clipboard/clipboard_standalone_impl.h"
+
+class Delegate : public mojo::ApplicationDelegate,
+ public mojo::InterfaceFactory<mojo::Clipboard> {
+ public:
+ Delegate() {}
+ virtual ~Delegate() {}
+
+ // mojo::ApplicationDelegate implementation.
+ virtual bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override {
+ connection->AddService(this);
+ return true;
+ }
+
+ // mojo::InterfaceFactory<mojo::Clipboard> implementation.
+ virtual void Create(
+ mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::Clipboard> request) override {
+ // TODO(erg): Write native implementations of the clipboard. For now, we
+ // just build a clipboard which doesn't interact with the system.
+ mojo::BindToRequest(new mojo::ClipboardStandaloneImpl(), &request);
+ }
+};
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new Delegate);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/gles2/BUILD.gn b/mojo/services/gles2/BUILD.gn
new file mode 100644
index 0000000..173d7df
--- /dev/null
+++ b/mojo/services/gles2/BUILD.gn
@@ -0,0 +1,51 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_gles2_service
+source_set("gles2") {
+ public_deps = [
+ ":bindings",
+ ]
+ deps = [
+ "//base",
+ "//gpu/command_buffer/service",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/gl",
+ ]
+
+ sources = [
+ "command_buffer_impl.cc",
+ "command_buffer_impl.h",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_gles2_bindings
+mojom("interfaces") {
+ sources = [
+ "command_buffer.mojom",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_gles2_bindings
+source_set("bindings") {
+ sources = [
+ "command_buffer_type_conversions.cc",
+ "command_buffer_type_conversions.h",
+ "mojo_buffer_backing.cc",
+ "mojo_buffer_backing.h",
+ ]
+
+ public_deps = [
+ ":interfaces",
+ ]
+ deps = [
+ "//base",
+ "//gpu/command_buffer/common",
+ "//mojo/public/cpp/bindings",
+ "//mojo/services/gles2:interfaces",
+ ]
+}
diff --git a/mojo/services/gles2/DEPS b/mojo/services/gles2/DEPS
new file mode 100644
index 0000000..acd992d
--- /dev/null
+++ b/mojo/services/gles2/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+gpu/command_buffer",
+ "+ui/gfx",
+ "+ui/gl",
+]
diff --git a/mojo/services/gles2/command_buffer.mojom b/mojo/services/gles2/command_buffer.mojom
new file mode 100644
index 0000000..302dcf4
--- /dev/null
+++ b/mojo/services/gles2/command_buffer.mojom
@@ -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.
+
+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): sync points
+};
+
+interface CommandBufferClient {
+ DidDestroy();
+ LostContext(int32 lost_reason); // TODO(piman): enum
+};
+
+}
diff --git a/mojo/services/gles2/command_buffer_impl.cc b/mojo/services/gles2/command_buffer_impl.cc
new file mode 100644
index 0000000..d221750
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_impl.cc
@@ -0,0 +1,204 @@
+// 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/macros.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_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 {
+
+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::GLShareGroup* share_group,
+ gpu::gles2::MailboxManager* mailbox_manager)
+ : widget_(gfx::kNullAcceleratedWidget),
+ size_(1, 1),
+ share_group_(share_group),
+ mailbox_manager_(mailbox_manager) {
+}
+
+CommandBufferImpl::CommandBufferImpl(
+ gfx::AcceleratedWidget widget,
+ const gfx::Size& size,
+ gfx::GLShareGroup* share_group,
+ gpu::gles2::MailboxManager* mailbox_manager)
+ : widget_(widget),
+ size_(size),
+ share_group_(share_group),
+ mailbox_manager_(mailbox_manager) {
+}
+
+CommandBufferImpl::~CommandBufferImpl() {
+ client()->DidDestroy();
+ if (decoder_) {
+ bool have_context = decoder_->MakeCurrent();
+ decoder_->Destroy(have_context);
+ }
+}
+
+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) {
+ if (widget_ == gfx::kNullAcceleratedWidget)
+ surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size_);
+ else
+ surface_ = gfx::GLSurface::CreateViewGLSurface(widget_);
+ if (!surface_.get())
+ return false;
+
+ // TODO(piman): virtual contexts, gpu preference.
+ context_ = gfx::GLContext::CreateGLContext(
+ share_group_.get(), 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(mailbox_manager_.get(),
+ 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());
+ decoder_->SetResizeCallback(
+ base::Bind(&CommandBufferImpl::OnResize, base::Unretained(this)));
+
+ 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;
+
+ 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)
+ 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) {
+ if (!context_->MakeCurrent(surface_.get())) {
+ DLOG(WARNING) << "Context lost";
+ client()->LostContext(gpu::error::kUnknown);
+ return;
+ }
+ 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) {
+ 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::OnParseError() {
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState();
+ client()->LostContext(state.context_lost_reason);
+}
+
+void CommandBufferImpl::OnResize(gfx::Size size, float scale_factor) {
+ surface_->Resize(size);
+}
+
+} // namespace mojo
diff --git a/mojo/services/gles2/command_buffer_impl.h b/mojo/services/gles2/command_buffer_impl.h
new file mode 100644
index 0000000..5233e90
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_impl.h
@@ -0,0 +1,82 @@
+// 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/macros.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;
+class MailboxManager;
+}
+}
+
+namespace gfx {
+class GLContext;
+class GLShareGroup;
+class GLSurface;
+}
+
+namespace mojo {
+
+class CommandBufferImpl : public InterfaceImpl<CommandBuffer> {
+ public:
+ // Offscreen.
+ CommandBufferImpl(gfx::GLShareGroup* share_group,
+ gpu::gles2::MailboxManager* mailbox_manager);
+ // Onscreen.
+ CommandBufferImpl(gfx::AcceleratedWidget widget,
+ const gfx::Size& size,
+ gfx::GLShareGroup* share_group,
+ gpu::gles2::MailboxManager* mailbox_manager);
+ virtual ~CommandBufferImpl();
+
+ 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;
+
+ private:
+ bool DoInitialize(mojo::ScopedSharedBufferHandle shared_state);
+
+ void OnResize(gfx::Size size, float scale_factor);
+
+ void OnParseError();
+
+ 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_refptr<gfx::GLContext> context_;
+ scoped_refptr<gfx::GLSurface> surface_;
+ scoped_refptr<gfx::GLShareGroup> share_group_;
+ scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
diff --git a/mojo/services/gles2/command_buffer_type_conversions.cc b/mojo/services/gles2/command_buffer_type_conversions.cc
new file mode 100644
index 0000000..a144f88
--- /dev/null
+++ b/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>::Convert(
+ 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<gpu::CommandBuffer::State, CommandBufferStatePtr>::Convert(
+ 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/mojo/services/gles2/command_buffer_type_conversions.h b/mojo/services/gles2/command_buffer_type_conversions.h
new file mode 100644
index 0000000..2579681
--- /dev/null
+++ b/mojo/services/gles2/command_buffer_type_conversions.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_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 <>
+struct TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State> {
+ static CommandBufferStatePtr Convert(const gpu::CommandBuffer::State& input);
+};
+
+template <>
+struct TypeConverter<gpu::CommandBuffer::State, CommandBufferStatePtr> {
+ static gpu::CommandBuffer::State Convert(const CommandBufferStatePtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
diff --git a/mojo/services/gles2/mojo_buffer_backing.cc b/mojo/services/gles2/mojo_buffer_backing.cc
new file mode 100644
index 0000000..3c90878
--- /dev/null
+++ b/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/mojo/services/gles2/mojo_buffer_backing.h b/mojo/services/gles2/mojo_buffer_backing.h
new file mode 100644
index 0000000..9304df7
--- /dev/null
+++ b/mojo/services/gles2/mojo_buffer_backing.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
+#define MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
+
+#include "base/macros.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/mojo/services/html_viewer/BUILD.gn b/mojo/services/html_viewer/BUILD.gn
new file mode 100644
index 0000000..ca20a24
--- /dev/null
+++ b/mojo/services/html_viewer/BUILD.gn
@@ -0,0 +1,78 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_html_viewer
+shared_library("html_viewer") {
+ output_name = "mojo_html_viewer"
+
+ sources = [
+ "blink_basic_type_converters.cc",
+ "blink_basic_type_converters.h",
+ "blink_input_events_type_converters.cc",
+ "blink_input_events_type_converters.h",
+ "blink_platform_impl.cc",
+ "blink_platform_impl.h",
+ "blink_url_request_type_converters.cc",
+ "blink_url_request_type_converters.h",
+ "html_viewer.cc",
+ "html_document_view.cc",
+ "html_document_view.h",
+ "webclipboard_impl.cc",
+ "webclipboard_impl.h",
+ "webcookiejar_impl.cc",
+ "webcookiejar_impl.h",
+ "webmediaplayer_factory.cc",
+ "webmediaplayer_factory.h",
+ "webmimeregistry_impl.cc",
+ "webmimeregistry_impl.h",
+ "websockethandle_impl.cc",
+ "websockethandle_impl.h",
+ "webstoragenamespace_impl.cc",
+ "webstoragenamespace_impl.h",
+ "webthemeengine_impl.cc",
+ "webthemeengine_impl.h",
+ "webthread_impl.cc",
+ "webthread_impl.h",
+ "weburlloader_impl.cc",
+ "weburlloader_impl.h",
+ "weblayertreeview_impl.cc",
+ "weblayertreeview_impl.h",
+ ]
+
+ include_dirs = [ "third_party/WebKit" ]
+
+ deps = [
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ "//cc",
+ "//cc/blink",
+ "//cc/surfaces",
+ "//media",
+ "//media/audio",
+ "//media/base",
+ "//media/blink",
+ "//mojo/cc",
+ "//mojo/common",
+ "//mojo/application",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/utility",
+ "//mojo/public/interfaces/application",
+ "//mojo/services/public/cpp/network",
+ "//mojo/services/public/cpp/view_manager",
+ "//mojo/services/public/interfaces/clipboard",
+ "//mojo/services/public/interfaces/content_handler",
+ "//mojo/services/public/interfaces/gpu",
+ "//mojo/services/public/interfaces/input_events:input_events",
+ "//mojo/services/public/interfaces/navigation",
+ "//mojo/services/public/interfaces/network",
+ "//mojo/services/public/interfaces/surfaces",
+ "//mojo/services/public/cpp/surfaces",
+ "//net",
+ "//skia",
+ "//third_party/WebKit/public:blink",
+ "//ui/native_theme",
+ "//url",
+ ]
+}
diff --git a/mojo/services/html_viewer/DEPS b/mojo/services/html_viewer/DEPS
new file mode 100644
index 0000000..ca538ee
--- /dev/null
+++ b/mojo/services/html_viewer/DEPS
@@ -0,0 +1,14 @@
+include_rules = [
+ "+cc",
+ "+media",
+ "+mojo/cc",
+ "+mojo/application",
+ "+mojo/services",
+ "+net/base",
+ "+skia",
+ "+third_party/WebKit/public",
+ "+third_party/skia/include",
+ "+ui/base",
+ "+ui/events",
+ "+ui/native_theme",
+]
diff --git a/mojo/services/html_viewer/blink_basic_type_converters.cc b/mojo/services/html_viewer/blink_basic_type_converters.cc
new file mode 100644
index 0000000..eb52652
--- /dev/null
+++ b/mojo/services/html_viewer/blink_basic_type_converters.cc
@@ -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.
+
+#include "mojo/services/html_viewer/blink_basic_type_converters.h"
+
+#include "mojo/public/cpp/bindings/string.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+using blink::WebString;
+
+namespace mojo {
+
+// static
+String TypeConverter<String, WebString>::Convert(const WebString& str) {
+ return String(str.utf8());
+}
+
+// static
+WebString TypeConverter<WebString, String>::Convert(const String& str) {
+ return WebString::fromUTF8(str.get());
+}
+
+// static
+Array<uint8_t> TypeConverter<Array<uint8_t>, blink::WebString>::Convert(
+ const blink::WebString& input) {
+ std::string utf8 = input.utf8();
+ Array<uint8_t> result(utf8.size());
+ for (size_t i = 0; i < utf8.size(); ++i)
+ result[i] = utf8[i];
+ return result.Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/blink_basic_type_converters.h b/mojo/services/html_viewer/blink_basic_type_converters.h
new file mode 100644
index 0000000..60ddeee
--- /dev/null
+++ b/mojo/services/html_viewer/blink_basic_type_converters.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_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
+
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+
+namespace blink {
+class WebString;
+}
+
+namespace mojo {
+class String;
+
+template<>
+struct TypeConverter<String, blink::WebString> {
+ static String Convert(const blink::WebString& str);
+};
+template<>
+struct TypeConverter<blink::WebString, String> {
+ static blink::WebString Convert(const String& str);
+};
+template <>
+struct TypeConverter<Array<uint8_t>, blink::WebString> {
+ static Array<uint8_t> Convert(const blink::WebString& input);
+};
+
+template<typename T, typename U>
+struct TypeConverter<Array<T>, blink::WebVector<U> > {
+ static Array<T> Convert(const blink::WebVector<U>& vector) {
+ Array<T> array(vector.size());
+ for (size_t i = 0; i < vector.size(); ++i)
+ array[i] = TypeConverter<T, U>::Convert(vector[i]);
+ return array.Pass();
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.cc b/mojo/services/html_viewer/blink_input_events_type_converters.cc
new file mode 100644
index 0000000..ff1a2cb
--- /dev/null
+++ b/mojo/services/html_viewer/blink_input_events_type_converters.cc
@@ -0,0 +1,203 @@
+// 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/html_viewer/blink_input_events_type_converters.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "mojo/services/public/interfaces/input_events/input_event_constants.mojom.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace mojo {
+namespace {
+
+// Used for scrolling. This matches Firefox behavior.
+const int kPixelsPerTick = 53;
+
+int EventFlagsToWebEventModifiers(int flags) {
+ int modifiers = 0;
+
+ if (flags & mojo::EVENT_FLAGS_SHIFT_DOWN)
+ modifiers |= blink::WebInputEvent::ShiftKey;
+ if (flags & mojo::EVENT_FLAGS_CONTROL_DOWN)
+ modifiers |= blink::WebInputEvent::ControlKey;
+ if (flags & mojo::EVENT_FLAGS_ALT_DOWN)
+ modifiers |= blink::WebInputEvent::AltKey;
+ // TODO(beng): MetaKey/META_MASK
+ if (flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON)
+ modifiers |= blink::WebInputEvent::LeftButtonDown;
+ if (flags & mojo::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON)
+ modifiers |= blink::WebInputEvent::MiddleButtonDown;
+ if (flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)
+ modifiers |= blink::WebInputEvent::RightButtonDown;
+ if (flags & mojo::EVENT_FLAGS_CAPS_LOCK_DOWN)
+ modifiers |= blink::WebInputEvent::CapsLockOn;
+ return modifiers;
+}
+
+int EventFlagsToWebInputEventModifiers(int flags) {
+ return
+ (flags & mojo::EVENT_FLAGS_SHIFT_DOWN ?
+ blink::WebInputEvent::ShiftKey : 0) |
+ (flags & mojo::EVENT_FLAGS_CONTROL_DOWN ?
+ blink::WebInputEvent::ControlKey : 0) |
+ (flags & mojo::EVENT_FLAGS_CAPS_LOCK_DOWN ?
+ blink::WebInputEvent::CapsLockOn : 0) |
+ (flags & mojo::EVENT_FLAGS_ALT_DOWN ?
+ blink::WebInputEvent::AltKey : 0);
+}
+
+int GetClickCount(int flags) {
+ if (flags & mojo::MOUSE_EVENT_FLAGS_IS_TRIPLE_CLICK)
+ return 3;
+ else if (flags & mojo::MOUSE_EVENT_FLAGS_IS_DOUBLE_CLICK)
+ return 2;
+
+ return 1;
+}
+
+scoped_ptr<blink::WebInputEvent> BuildWebMouseEventFrom(const EventPtr& event) {
+ scoped_ptr<blink::WebMouseEvent> web_event(new blink::WebMouseEvent);
+ web_event->x = event->location_data->in_view_location->x;
+ web_event->y = event->location_data->in_view_location->y;
+
+ // TODO(erg): Remove this if check once we can rely on screen_location
+ // actually being passed to us. As written today, getting the screen
+ // location from ui::Event objects can only be done by querying the
+ // underlying native events, so all synthesized events don't have screen
+ // locations.
+ if (!event->location_data->screen_location.is_null()) {
+ web_event->globalX = event->location_data->screen_location->x;
+ web_event->globalY = event->location_data->screen_location->y;
+ }
+
+ web_event->modifiers = EventFlagsToWebEventModifiers(event->flags);
+ web_event->timeStampSeconds =
+ base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+
+ web_event->button = blink::WebMouseEvent::ButtonNone;
+ if (event->flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON)
+ web_event->button = blink::WebMouseEvent::ButtonLeft;
+ if (event->flags & mojo::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON)
+ web_event->button = blink::WebMouseEvent::ButtonMiddle;
+ if (event->flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)
+ web_event->button = blink::WebMouseEvent::ButtonRight;
+
+ switch (event->action) {
+ case EVENT_TYPE_MOUSE_PRESSED:
+ web_event->type = blink::WebInputEvent::MouseDown;
+ break;
+ case EVENT_TYPE_MOUSE_RELEASED:
+ web_event->type = blink::WebInputEvent::MouseUp;
+ break;
+ case EVENT_TYPE_MOUSE_ENTERED:
+ web_event->type = blink::WebInputEvent::MouseLeave;
+ web_event->button = blink::WebMouseEvent::ButtonNone;
+ break;
+ case EVENT_TYPE_MOUSE_EXITED:
+ case EVENT_TYPE_MOUSE_MOVED:
+ case EVENT_TYPE_MOUSE_DRAGGED:
+ web_event->type = blink::WebInputEvent::MouseMove;
+ break;
+ default:
+ NOTIMPLEMENTED() << "Received unexpected event: " << event->action;
+ break;
+ }
+
+ web_event->clickCount = GetClickCount(event->flags);
+
+ return web_event.PassAs<blink::WebInputEvent>();
+}
+
+scoped_ptr<blink::WebInputEvent> BuildWebKeyboardEvent(
+ const EventPtr& event) {
+ scoped_ptr<blink::WebKeyboardEvent> web_event(new blink::WebKeyboardEvent);
+
+ web_event->modifiers = EventFlagsToWebInputEventModifiers(event->flags);
+ web_event->timeStampSeconds =
+ base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+
+ switch (event->action) {
+ case EVENT_TYPE_KEY_PRESSED:
+ web_event->type = event->key_data->is_char ? blink::WebInputEvent::Char :
+ blink::WebInputEvent::RawKeyDown;
+ break;
+ case EVENT_TYPE_KEY_RELEASED:
+ web_event->type = blink::WebInputEvent::KeyUp;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ if (web_event->modifiers & blink::WebInputEvent::AltKey)
+ web_event->isSystemKey = true;
+
+ web_event->windowsKeyCode = event->key_data->windows_key_code;
+ web_event->nativeKeyCode = event->key_data->native_key_code;
+ web_event->text[0] = event->key_data->text;
+ web_event->unmodifiedText[0] = event->key_data->unmodified_text;
+
+ web_event->setKeyIdentifierFromWindowsKeyCode();
+ return web_event.PassAs<blink::WebInputEvent>();
+}
+
+scoped_ptr<blink::WebInputEvent> BuildWebMouseWheelEventFrom(
+ const EventPtr& event) {
+ scoped_ptr<blink::WebMouseWheelEvent> web_event(
+ new blink::WebMouseWheelEvent);
+ web_event->type = blink::WebInputEvent::MouseWheel;
+ web_event->button = blink::WebMouseEvent::ButtonNone;
+ web_event->modifiers = EventFlagsToWebEventModifiers(event->flags);
+ web_event->timeStampSeconds =
+ base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+
+ web_event->x = event->location_data->in_view_location->x;
+ web_event->y = event->location_data->in_view_location->y;
+
+ // TODO(erg): Remove this null check as parallel to above.
+ if (!event->location_data->screen_location.is_null()) {
+ web_event->globalX = event->location_data->screen_location->x;
+ web_event->globalY = event->location_data->screen_location->y;
+ }
+
+ if ((event->flags & mojo::EVENT_FLAGS_SHIFT_DOWN) != 0 &&
+ event->wheel_data->x_offset == 0) {
+ web_event->deltaX = event->wheel_data->y_offset;
+ web_event->deltaY = 0;
+ } else {
+ web_event->deltaX = event->wheel_data->x_offset;
+ web_event->deltaY = event->wheel_data->y_offset;
+ }
+
+ web_event->wheelTicksX = web_event->deltaX / kPixelsPerTick;
+ web_event->wheelTicksY = web_event->deltaY / kPixelsPerTick;
+
+ return web_event.PassAs<blink::WebInputEvent>();
+}
+
+} // namespace
+
+// static
+scoped_ptr<blink::WebInputEvent>
+TypeConverter<scoped_ptr<blink::WebInputEvent>, EventPtr>::Convert(
+ const EventPtr& event) {
+ if (event->action == EVENT_TYPE_MOUSE_PRESSED ||
+ event->action == EVENT_TYPE_MOUSE_RELEASED ||
+ event->action == EVENT_TYPE_MOUSE_ENTERED ||
+ event->action == EVENT_TYPE_MOUSE_EXITED ||
+ event->action == EVENT_TYPE_MOUSE_MOVED ||
+ event->action == EVENT_TYPE_MOUSE_DRAGGED) {
+ return BuildWebMouseEventFrom(event);
+ } else if ((event->action == EVENT_TYPE_KEY_PRESSED ||
+ event->action == EVENT_TYPE_KEY_RELEASED) &&
+ event->key_data) {
+ return BuildWebKeyboardEvent(event);
+ } else if (event->action == EVENT_TYPE_MOUSEWHEEL) {
+ return BuildWebMouseWheelEventFrom(event);
+ }
+
+ return scoped_ptr<blink::WebInputEvent>();
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.h b/mojo/services/html_viewer/blink_input_events_type_converters.h
new file mode 100644
index 0000000..3d5860d
--- /dev/null
+++ b/mojo/services/html_viewer/blink_input_events_type_converters.h
@@ -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.
+
+#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace mojo {
+
+template <>
+struct TypeConverter<scoped_ptr<blink::WebInputEvent>, EventPtr> {
+ static scoped_ptr<blink::WebInputEvent> Convert(const EventPtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/blink_platform_impl.cc b/mojo/services/html_viewer/blink_platform_impl.cc
new file mode 100644
index 0000000..8a7555b
--- /dev/null
+++ b/mojo/services/html_viewer/blink_platform_impl.cc
@@ -0,0 +1,251 @@
+// 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/html_viewer/blink_platform_impl.h"
+
+#include <cmath>
+
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/html_viewer/webclipboard_impl.h"
+#include "mojo/services/html_viewer/webcookiejar_impl.h"
+#include "mojo/services/html_viewer/websockethandle_impl.h"
+#include "mojo/services/html_viewer/webthread_impl.h"
+#include "mojo/services/html_viewer/weburlloader_impl.h"
+#include "net/base/data_url.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/public/platform/WebWaitableEvent.h"
+
+namespace mojo {
+namespace {
+
+// TODO(darin): Figure out what our UA should really be.
+const char kUserAgentString[] =
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/35.0.1916.153 Safari/537.36";
+
+class WebWaitableEventImpl : public blink::WebWaitableEvent {
+ public:
+ WebWaitableEventImpl() : impl_(new base::WaitableEvent(false, false)) {}
+ virtual ~WebWaitableEventImpl() {}
+
+ virtual void wait() { impl_->Wait(); }
+ virtual void signal() { impl_->Signal(); }
+
+ base::WaitableEvent* impl() {
+ return impl_.get();
+ }
+
+ private:
+ scoped_ptr<base::WaitableEvent> impl_;
+ DISALLOW_COPY_AND_ASSIGN(WebWaitableEventImpl);
+};
+
+} // namespace
+
+BlinkPlatformImpl::BlinkPlatformImpl(ApplicationImpl* app)
+ : main_loop_(base::MessageLoop::current()),
+ shared_timer_func_(NULL),
+ shared_timer_fire_time_(0.0),
+ shared_timer_fire_time_was_set_while_suspended_(false),
+ shared_timer_suspended_(0),
+ current_thread_slot_(&DestroyCurrentThread) {
+ app->ConnectToService("mojo:mojo_network_service", &network_service_);
+
+ CookieStorePtr cookie_store;
+ network_service_->GetCookieStore(Get(&cookie_store));
+ cookie_jar_.reset(new WebCookieJarImpl(cookie_store.Pass()));
+
+ ClipboardPtr clipboard;
+ app->ConnectToService("mojo:mojo_clipboard", &clipboard);
+ clipboard_.reset(new WebClipboardImpl(clipboard.Pass()));
+}
+
+BlinkPlatformImpl::~BlinkPlatformImpl() {
+}
+
+blink::WebCookieJar* BlinkPlatformImpl::cookieJar() {
+ return cookie_jar_.get();
+}
+
+blink::WebClipboard* BlinkPlatformImpl::clipboard() {
+ return clipboard_.get();
+}
+
+blink::WebMimeRegistry* BlinkPlatformImpl::mimeRegistry() {
+ return &mime_registry_;
+}
+
+blink::WebThemeEngine* BlinkPlatformImpl::themeEngine() {
+ return &theme_engine_;
+}
+
+blink::WebString BlinkPlatformImpl::defaultLocale() {
+ return blink::WebString::fromUTF8("en-US");
+}
+
+double BlinkPlatformImpl::currentTime() {
+ return base::Time::Now().ToDoubleT();
+}
+
+double BlinkPlatformImpl::monotonicallyIncreasingTime() {
+ return base::TimeTicks::Now().ToInternalValue() /
+ static_cast<double>(base::Time::kMicrosecondsPerSecond);
+}
+
+void BlinkPlatformImpl::cryptographicallyRandomValues(unsigned char* buffer,
+ size_t length) {
+ base::RandBytes(buffer, length);
+}
+
+void BlinkPlatformImpl::setSharedTimerFiredFunction(void (*func)()) {
+ shared_timer_func_ = func;
+}
+
+void BlinkPlatformImpl::setSharedTimerFireInterval(
+ double interval_seconds) {
+ shared_timer_fire_time_ = interval_seconds + monotonicallyIncreasingTime();
+ if (shared_timer_suspended_) {
+ shared_timer_fire_time_was_set_while_suspended_ = true;
+ return;
+ }
+
+ // By converting between double and int64 representation, we run the risk
+ // of losing precision due to rounding errors. Performing computations in
+ // microseconds reduces this risk somewhat. But there still is the potential
+ // of us computing a fire time for the timer that is shorter than what we
+ // need.
+ // As the event loop will check event deadlines prior to actually firing
+ // them, there is a risk of needlessly rescheduling events and of
+ // needlessly looping if sleep times are too short even by small amounts.
+ // This results in measurable performance degradation unless we use ceil() to
+ // always round up the sleep times.
+ int64 interval = static_cast<int64>(
+ ceil(interval_seconds * base::Time::kMillisecondsPerSecond)
+ * base::Time::kMicrosecondsPerMillisecond);
+
+ if (interval < 0)
+ interval = 0;
+
+ shared_timer_.Stop();
+ shared_timer_.Start(FROM_HERE, base::TimeDelta::FromMicroseconds(interval),
+ this, &BlinkPlatformImpl::DoTimeout);
+}
+
+void BlinkPlatformImpl::stopSharedTimer() {
+ shared_timer_.Stop();
+}
+
+void BlinkPlatformImpl::callOnMainThread(
+ void (*func)(void*), void* context) {
+ main_loop_->PostTask(FROM_HERE, base::Bind(func, context));
+}
+
+bool BlinkPlatformImpl::isThreadedCompositingEnabled() {
+ return true;
+}
+
+blink::WebCompositorSupport* BlinkPlatformImpl::compositorSupport() {
+ return &compositor_support_;
+}
+
+blink::WebScrollbarBehavior* BlinkPlatformImpl::scrollbarBehavior() {
+ return &scrollbar_behavior_;
+}
+
+const unsigned char* BlinkPlatformImpl::getTraceCategoryEnabledFlag(
+ const char* category_name) {
+ static const unsigned char buf[] = "*";
+ return buf;
+}
+
+blink::WebURLLoader* BlinkPlatformImpl::createURLLoader() {
+ return new WebURLLoaderImpl(network_service_.get());
+}
+
+blink::WebSocketHandle* BlinkPlatformImpl::createWebSocketHandle() {
+ return new WebSocketHandleImpl(network_service_.get());
+}
+
+blink::WebString BlinkPlatformImpl::userAgent() {
+ return blink::WebString::fromUTF8(kUserAgentString);
+}
+
+blink::WebData BlinkPlatformImpl::parseDataURL(
+ const blink::WebURL& url,
+ blink::WebString& mimetype_out,
+ blink::WebString& charset_out) {
+ std::string mimetype, charset, data;
+ if (net::DataURL::Parse(url, &mimetype, &charset, &data)
+ && net::IsSupportedMimeType(mimetype)) {
+ mimetype_out = blink::WebString::fromUTF8(mimetype);
+ charset_out = blink::WebString::fromUTF8(charset);
+ return data;
+ }
+ return blink::WebData();
+}
+
+blink::WebURLError BlinkPlatformImpl::cancelledError(const blink::WebURL& url)
+ const {
+ blink::WebURLError error;
+ error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_ABORTED;
+ error.unreachableURL = url;
+ error.staleCopyInCache = false;
+ error.isCancellation = true;
+ return error;
+}
+
+blink::WebThread* BlinkPlatformImpl::createThread(const char* name) {
+ return new WebThreadImpl(name);
+}
+
+blink::WebThread* BlinkPlatformImpl::currentThread() {
+ WebThreadImplForMessageLoop* thread =
+ static_cast<WebThreadImplForMessageLoop*>(current_thread_slot_.Get());
+ if (thread)
+ return (thread);
+
+ scoped_refptr<base::MessageLoopProxy> message_loop =
+ base::MessageLoopProxy::current();
+ if (!message_loop.get())
+ return NULL;
+
+ thread = new WebThreadImplForMessageLoop(message_loop.get());
+ current_thread_slot_.Set(thread);
+ return thread;
+}
+
+void BlinkPlatformImpl::yieldCurrentThread() {
+ base::PlatformThread::YieldCurrentThread();
+}
+
+blink::WebWaitableEvent* BlinkPlatformImpl::createWaitableEvent() {
+ return new WebWaitableEventImpl();
+}
+
+blink::WebWaitableEvent* BlinkPlatformImpl::waitMultipleEvents(
+ const blink::WebVector<blink::WebWaitableEvent*>& web_events) {
+ std::vector<base::WaitableEvent*> events;
+ for (size_t i = 0; i < web_events.size(); ++i)
+ events.push_back(static_cast<WebWaitableEventImpl*>(web_events[i])->impl());
+ size_t idx = base::WaitableEvent::WaitMany(
+ vector_as_array(&events), events.size());
+ DCHECK_LT(idx, web_events.size());
+ return web_events[idx];
+}
+
+// static
+void BlinkPlatformImpl::DestroyCurrentThread(void* thread) {
+ WebThreadImplForMessageLoop* impl =
+ static_cast<WebThreadImplForMessageLoop*>(thread);
+ delete impl;
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/blink_platform_impl.h b/mojo/services/html_viewer/blink_platform_impl.h
new file mode 100644
index 0000000..9abd336
--- /dev/null
+++ b/mojo/services/html_viewer/blink_platform_impl.h
@@ -0,0 +1,93 @@
+// 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_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/timer/timer.h"
+#include "cc/blink/web_compositor_support_impl.h"
+#include "mojo/services/html_viewer/webmimeregistry_impl.h"
+#include "mojo/services/html_viewer/webthemeengine_impl.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/platform/WebScrollbarBehavior.h"
+
+namespace mojo {
+class ApplicationImpl;
+class WebClipboardImpl;
+class WebCookieJarImpl;
+
+class BlinkPlatformImpl : public blink::Platform {
+ public:
+ explicit BlinkPlatformImpl(ApplicationImpl* app);
+ virtual ~BlinkPlatformImpl();
+
+ // blink::Platform methods:
+ virtual blink::WebCookieJar* cookieJar();
+ virtual blink::WebClipboard* clipboard();
+ virtual blink::WebMimeRegistry* mimeRegistry();
+ virtual blink::WebThemeEngine* themeEngine();
+ virtual blink::WebString defaultLocale();
+ virtual double currentTime();
+ virtual double monotonicallyIncreasingTime();
+ virtual void cryptographicallyRandomValues(
+ unsigned char* buffer, size_t length);
+ virtual void setSharedTimerFiredFunction(void (*func)());
+ virtual void setSharedTimerFireInterval(double interval_seconds);
+ virtual void stopSharedTimer();
+ virtual void callOnMainThread(void (*func)(void*), void* context);
+ virtual bool isThreadedCompositingEnabled();
+ virtual blink::WebCompositorSupport* compositorSupport();
+ virtual blink::WebURLLoader* createURLLoader();
+ virtual blink::WebSocketHandle* createWebSocketHandle();
+ virtual blink::WebString userAgent();
+ virtual blink::WebData parseDataURL(
+ const blink::WebURL& url, blink::WebString& mime_type,
+ blink::WebString& charset);
+ virtual blink::WebURLError cancelledError(const blink::WebURL& url) const;
+ virtual blink::WebThread* createThread(const char* name);
+ virtual blink::WebThread* currentThread();
+ virtual void yieldCurrentThread();
+ virtual blink::WebWaitableEvent* createWaitableEvent();
+ virtual blink::WebWaitableEvent* waitMultipleEvents(
+ const blink::WebVector<blink::WebWaitableEvent*>& events);
+ virtual blink::WebScrollbarBehavior* scrollbarBehavior();
+ virtual const unsigned char* getTraceCategoryEnabledFlag(
+ const char* category_name);
+
+ private:
+ void SuspendSharedTimer();
+ void ResumeSharedTimer();
+
+ void DoTimeout() {
+ if (shared_timer_func_ && !shared_timer_suspended_)
+ shared_timer_func_();
+ }
+
+ static void DestroyCurrentThread(void*);
+
+ NetworkServicePtr network_service_;
+ base::MessageLoop* main_loop_;
+ base::OneShotTimer<BlinkPlatformImpl> shared_timer_;
+ void (*shared_timer_func_)();
+ double shared_timer_fire_time_;
+ bool shared_timer_fire_time_was_set_while_suspended_;
+ int shared_timer_suspended_; // counter
+ base::ThreadLocalStorage::Slot current_thread_slot_;
+ cc_blink::WebCompositorSupportImpl compositor_support_;
+ WebThemeEngineImpl theme_engine_;
+ scoped_ptr<WebCookieJarImpl> cookie_jar_;
+ scoped_ptr<WebClipboardImpl> clipboard_;
+ WebMimeRegistryImpl mime_registry_;
+ blink::WebScrollbarBehavior scrollbar_behavior_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlinkPlatformImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.cc b/mojo/services/html_viewer/blink_url_request_type_converters.cc
new file mode 100644
index 0000000..9428d92
--- /dev/null
+++ b/mojo/services/html_viewer/blink_url_request_type_converters.cc
@@ -0,0 +1,110 @@
+// 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/html_viewer/blink_url_request_type_converters.h"
+
+#include "base/strings/string_util.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+
+namespace mojo {
+namespace {
+
+// Ripped from web_url_loader_impl.cc.
+class HeaderFlattener : public blink::WebHTTPHeaderVisitor {
+ public:
+ HeaderFlattener() : has_accept_header_(false) {}
+
+ virtual void visitHeader(const blink::WebString& name,
+ const blink::WebString& value) {
+ // Headers are latin1.
+ const std::string& name_latin1 = name.latin1();
+ const std::string& value_latin1 = value.latin1();
+
+ // Skip over referrer headers found in the header map because we already
+ // pulled it out as a separate parameter.
+ if (LowerCaseEqualsASCII(name_latin1, "referer"))
+ return;
+
+ if (LowerCaseEqualsASCII(name_latin1, "accept"))
+ has_accept_header_ = true;
+
+ buffer_.push_back(name_latin1 + ": " + value_latin1);
+ }
+
+ Array<String> GetBuffer() {
+ // In some cases, WebKit doesn't add an Accept header, but not having the
+ // header confuses some web servers. See bug 808613.
+ if (!has_accept_header_) {
+ buffer_.push_back("Accept: */*");
+ has_accept_header_ = true;
+ }
+ return buffer_.Pass();
+ }
+
+ private:
+ Array<String> buffer_;
+ bool has_accept_header_;
+};
+
+void AddRequestBody(URLRequest* url_request,
+ const blink::WebURLRequest& request) {
+ if (request.httpBody().isNull())
+ return;
+
+ uint32_t i = 0;
+ blink::WebHTTPBody::Element element;
+ while (request.httpBody().elementAt(i++, element)) {
+ switch (element.type) {
+ case blink::WebHTTPBody::Element::TypeData:
+ if (!element.data.isEmpty()) {
+ // WebKit sometimes gives up empty data to append. These aren't
+ // necessary so we just optimize those out here.
+ uint32_t num_bytes = static_cast<uint32_t>(element.data.size());
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = num_bytes;
+ DataPipe data_pipe(options);
+ url_request->body.push_back(
+ data_pipe.consumer_handle.Pass());
+ WriteDataRaw(data_pipe.producer_handle.get(),
+ element.data.data(),
+ &num_bytes,
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+ }
+ break;
+ case blink::WebHTTPBody::Element::TypeFile:
+ case blink::WebHTTPBody::Element::TypeFileSystemURL:
+ case blink::WebHTTPBody::Element::TypeBlob:
+ // TODO(mpcomplete): handle these.
+ NOTIMPLEMENTED();
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+}
+
+} // namespace
+
+URLRequestPtr TypeConverter<URLRequestPtr, blink::WebURLRequest>::Convert(
+ const blink::WebURLRequest& request) {
+ URLRequestPtr url_request(URLRequest::New());
+ url_request->url = request.url().string().utf8();
+ url_request->method = request.httpMethod().utf8();
+
+ HeaderFlattener flattener;
+ request.visitHTTPHeaderFields(&flattener);
+ url_request->headers = flattener.GetBuffer().Pass();
+
+ AddRequestBody(url_request.get(), request);
+
+ return url_request.Pass();
+}
+
+} // namespace mojo
+
diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.h b/mojo/services/html_viewer/blink_url_request_type_converters.h
new file mode 100644
index 0000000..583d0ba
--- /dev/null
+++ b/mojo/services/html_viewer/blink_url_request_type_converters.h
@@ -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.
+
+#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
+
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+
+namespace blink {
+class WebURLRequest;
+}
+
+namespace mojo {
+
+template <>
+struct TypeConverter<URLRequestPtr, blink::WebURLRequest> {
+ static URLRequestPtr Convert(const blink::WebURLRequest& request);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/html_document_view.cc b/mojo/services/html_viewer/html_document_view.cc
new file mode 100644
index 0000000..7056501
--- /dev/null
+++ b/mojo/services/html_viewer/html_document_view.cc
@@ -0,0 +1,260 @@
+// 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/html_viewer/html_document_view.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/html_viewer/blink_input_events_type_converters.h"
+#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
+#include "mojo/services/html_viewer/weblayertreeview_impl.h"
+#include "mojo/services/html_viewer/webmediaplayer_factory.h"
+#include "mojo/services/html_viewer/webstoragenamespace_impl.h"
+#include "mojo/services/html_viewer/weburlloader_impl.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/public/web/WebConsoleMessage.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+#include "third_party/WebKit/public/web/WebSettings.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+
+namespace mojo {
+namespace {
+
+void ConfigureSettings(blink::WebSettings* settings) {
+ settings->setCookieEnabled(true);
+ settings->setDefaultFixedFontSize(13);
+ settings->setDefaultFontSize(16);
+ settings->setLoadsImagesAutomatically(true);
+ settings->setJavaScriptEnabled(true);
+}
+
+Target WebNavigationPolicyToNavigationTarget(
+ blink::WebNavigationPolicy policy) {
+ switch (policy) {
+ case blink::WebNavigationPolicyCurrentTab:
+ return TARGET_SOURCE_NODE;
+ case blink::WebNavigationPolicyNewBackgroundTab:
+ case blink::WebNavigationPolicyNewForegroundTab:
+ case blink::WebNavigationPolicyNewWindow:
+ case blink::WebNavigationPolicyNewPopup:
+ return TARGET_NEW_NODE;
+ default:
+ return TARGET_DEFAULT;
+ }
+}
+
+bool CanNavigateLocally(blink::WebFrame* frame,
+ const blink::WebURLRequest& request) {
+ // For now, we just load child frames locally.
+ // TODO(aa): In the future, this should use embedding to connect to a
+ // different instance of Blink if the frame is cross-origin.
+ if (frame->parent())
+ return true;
+
+ // If we have extraData() it means we already have the url response
+ // (presumably because we are being called via Navigate()). In that case we
+ // can go ahead and navigate locally.
+ if (request.extraData())
+ return true;
+
+ // Otherwise we don't know if we're the right app to handle this request. Ask
+ // host to do the navigation for us.
+ return false;
+}
+
+} // namespace
+
+HTMLDocumentView::HTMLDocumentView(
+ URLResponsePtr response,
+ InterfaceRequest<ServiceProvider> service_provider_request,
+ Shell* shell,
+ scoped_refptr<base::MessageLoopProxy> compositor_thread,
+ WebMediaPlayerFactory* web_media_player_factory)
+ : shell_(shell),
+ web_view_(NULL),
+ root_(NULL),
+ view_manager_client_factory_(shell, this),
+ compositor_thread_(compositor_thread),
+ web_media_player_factory_(web_media_player_factory),
+ weak_factory_(this) {
+ ServiceProviderImpl* exported_services = new ServiceProviderImpl();
+ exported_services->AddService(&view_manager_client_factory_);
+ BindToRequest(exported_services, &service_provider_request);
+ Load(response.Pass());
+}
+
+HTMLDocumentView::~HTMLDocumentView() {
+ if (web_view_)
+ web_view_->close();
+ if (root_)
+ root_->RemoveObserver(this);
+}
+
+void HTMLDocumentView::OnEmbed(
+ ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* embedee_service_provider_impl,
+ scoped_ptr<ServiceProvider> embedder_service_provider) {
+ root_ = root;
+ embedder_service_provider_ = embedder_service_provider.Pass();
+ navigator_host_.set_service_provider(embedder_service_provider_.get());
+
+ web_view_->resize(root_->bounds().size());
+ web_layer_tree_view_impl_->setViewportSize(root_->bounds().size());
+ web_layer_tree_view_impl_->set_view(root_);
+ root_->AddObserver(this);
+}
+
+void HTMLDocumentView::OnViewManagerDisconnected(ViewManager* view_manager) {
+ // TODO(aa): Need to figure out how shutdown works.
+}
+
+void HTMLDocumentView::Load(URLResponsePtr response) {
+ web_view_ = blink::WebView::create(this);
+ web_layer_tree_view_impl_->set_widget(web_view_);
+ ConfigureSettings(web_view_->settings());
+ web_view_->setMainFrame(blink::WebLocalFrame::create(this));
+
+ GURL url(response->url);
+
+ WebURLRequestExtraData* extra_data = new WebURLRequestExtraData;
+ extra_data->synthetic_response = response.Pass();
+
+ blink::WebURLRequest web_request;
+ web_request.initialize();
+ web_request.setURL(url);
+ web_request.setExtraData(extra_data);
+
+ web_view_->mainFrame()->loadRequest(web_request);
+}
+
+blink::WebStorageNamespace* HTMLDocumentView::createSessionStorageNamespace() {
+ return new WebStorageNamespaceImpl();
+}
+
+void HTMLDocumentView::initializeLayerTreeView() {
+ ServiceProviderPtr surfaces_service_provider;
+ shell_->ConnectToApplication("mojo:mojo_surfaces_service",
+ Get(&surfaces_service_provider));
+ InterfacePtr<SurfacesService> surfaces_service;
+ ConnectToService(surfaces_service_provider.get(), &surfaces_service);
+
+ ServiceProviderPtr gpu_service_provider;
+ // TODO(jamesr): Should be mojo:mojo_gpu_service
+ shell_->ConnectToApplication("mojo:mojo_native_viewport_service",
+ Get(&gpu_service_provider));
+ InterfacePtr<Gpu> gpu_service;
+ ConnectToService(gpu_service_provider.get(), &gpu_service);
+ web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
+ compositor_thread_, surfaces_service.Pass(), gpu_service.Pass()));
+}
+
+blink::WebLayerTreeView* HTMLDocumentView::layerTreeView() {
+ return web_layer_tree_view_impl_.get();
+}
+
+blink::WebMediaPlayer* HTMLDocumentView::createMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client) {
+ return web_media_player_factory_->CreateMediaPlayer(frame, url, client);
+}
+
+blink::WebMediaPlayer* HTMLDocumentView::createMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client,
+ blink::WebContentDecryptionModule* initial_cdm) {
+ return createMediaPlayer(frame, url, client);
+}
+
+blink::WebFrame* HTMLDocumentView::createChildFrame(
+ blink::WebLocalFrame* parent,
+ const blink::WebString& frameName) {
+ blink::WebLocalFrame* web_frame = blink::WebLocalFrame::create(this);
+ parent->appendChild(web_frame);
+ return web_frame;
+}
+
+void HTMLDocumentView::frameDetached(blink::WebFrame* frame) {
+ if (frame->parent())
+ frame->parent()->removeChild(frame);
+
+ // |frame| is invalid after here.
+ frame->close();
+}
+
+blink::WebCookieJar* HTMLDocumentView::cookieJar(blink::WebLocalFrame* frame) {
+ // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
+ // Either it should, as it once did, or we should find another solution here.
+ return blink::Platform::current()->cookieJar();
+}
+
+blink::WebNavigationPolicy HTMLDocumentView::decidePolicyForNavigation(
+ blink::WebLocalFrame* frame, blink::WebDataSource::ExtraData* data,
+ const blink::WebURLRequest& request, blink::WebNavigationType nav_type,
+ blink::WebNavigationPolicy default_policy, bool is_redirect) {
+ if (CanNavigateLocally(frame, request))
+ return default_policy;
+
+ navigator_host_->RequestNavigate(
+ WebNavigationPolicyToNavigationTarget(default_policy),
+ URLRequest::From(request).Pass());
+
+ return blink::WebNavigationPolicyIgnore;
+}
+
+void HTMLDocumentView::didAddMessageToConsole(
+ const blink::WebConsoleMessage& message,
+ const blink::WebString& source_name,
+ unsigned source_line,
+ const blink::WebString& stack_trace) {
+}
+
+void HTMLDocumentView::didNavigateWithinPage(
+ blink::WebLocalFrame* frame, const blink::WebHistoryItem& history_item,
+ blink::WebHistoryCommitType commit_type) {
+ navigator_host_->DidNavigateLocally(history_item.urlString().utf8());
+}
+
+void HTMLDocumentView::OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ DCHECK_EQ(view, root_);
+ web_view_->resize(view->bounds().size());
+}
+
+void HTMLDocumentView::OnViewDestroyed(View* view) {
+ DCHECK_EQ(view, root_);
+ view->RemoveObserver(this);
+ root_ = NULL;
+}
+
+void HTMLDocumentView::OnViewInputEvent(View* view, const EventPtr& event) {
+ scoped_ptr<blink::WebInputEvent> web_event =
+ event.To<scoped_ptr<blink::WebInputEvent> >();
+ if (web_event)
+ web_view_->handleInputEvent(*web_event);
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/html_document_view.h b/mojo/services/html_viewer/html_document_view.h
new file mode 100644
index 0000000..e2407bd
--- /dev/null
+++ b/mojo/services/html_viewer/html_document_view.h
@@ -0,0 +1,122 @@
+// 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_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
+#define MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/application/lazy_interface_ptr.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "third_party/WebKit/public/web/WebFrameClient.h"
+#include "third_party/WebKit/public/web/WebViewClient.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace mojo {
+
+class WebMediaPlayerFactory;
+class ViewManager;
+class View;
+class WebLayerTreeViewImpl;
+
+// A view for a single HTML document.
+class HTMLDocumentView : public blink::WebViewClient,
+ public blink::WebFrameClient,
+ public ViewManagerDelegate,
+ public ViewObserver {
+ public:
+ // Load a new HTMLDocument with |response|.
+ //
+ // |service_provider_request| should be used to implement a
+ // ServiceProvider which exposes services to the connecting application.
+ // Commonly, the connecting application is the ViewManager and it will
+ // request ViewManagerClient.
+ //
+ // |shell| is the Shell connection for this mojo::Application.
+ HTMLDocumentView(URLResponsePtr response,
+ InterfaceRequest<ServiceProvider> service_provider_request,
+ Shell* shell,
+ scoped_refptr<base::MessageLoopProxy> compositor_thread,
+ WebMediaPlayerFactory* web_media_player_factory);
+ virtual ~HTMLDocumentView();
+
+ private:
+ // WebViewClient methods:
+ virtual blink::WebStorageNamespace* createSessionStorageNamespace();
+
+ // WebWidgetClient methods:
+ virtual void initializeLayerTreeView();
+ virtual blink::WebLayerTreeView* layerTreeView();
+
+ // WebFrameClient methods:
+ virtual blink::WebMediaPlayer* createMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client);
+ virtual blink::WebMediaPlayer* createMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client,
+ blink::WebContentDecryptionModule* initial_cdm);
+ virtual blink::WebFrame* createChildFrame(blink::WebLocalFrame* parent,
+ const blink::WebString& frameName);
+ virtual void frameDetached(blink::WebFrame*);
+ virtual blink::WebCookieJar* cookieJar(blink::WebLocalFrame* frame);
+ virtual blink::WebNavigationPolicy decidePolicyForNavigation(
+ blink::WebLocalFrame* frame, blink::WebDataSource::ExtraData* data,
+ const blink::WebURLRequest& request, blink::WebNavigationType nav_type,
+ blink::WebNavigationPolicy default_policy, bool isRedirect);
+ virtual void didAddMessageToConsole(
+ const blink::WebConsoleMessage& message,
+ const blink::WebString& source_name,
+ unsigned source_line,
+ const blink::WebString& stack_trace);
+ virtual void didNavigateWithinPage(
+ blink::WebLocalFrame* frame,
+ const blink::WebHistoryItem& history_item,
+ blink::WebHistoryCommitType commit_type);
+
+ // ViewManagerDelegate methods:
+ virtual void OnEmbed(
+ ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* embedee_service_provider_impl,
+ scoped_ptr<ServiceProvider> embedder_service_provider) override;
+ virtual void OnViewManagerDisconnected(ViewManager* view_manager) override;
+
+ // ViewObserver methods:
+ virtual void OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override;
+ virtual void OnViewDestroyed(View* view) override;
+ virtual void OnViewInputEvent(View* view, const EventPtr& event) override;
+
+ void Load(URLResponsePtr response);
+
+ URLResponsePtr response_;
+ scoped_ptr<ServiceProvider> embedder_service_provider_;
+ Shell* shell_;
+ LazyInterfacePtr<NavigatorHost> navigator_host_;
+ blink::WebView* web_view_;
+ View* root_;
+ ViewManagerClientFactory view_manager_client_factory_;
+ scoped_ptr<WebLayerTreeViewImpl> web_layer_tree_view_impl_;
+ scoped_refptr<base::MessageLoopProxy> compositor_thread_;
+ WebMediaPlayerFactory* web_media_player_factory_;
+
+ base::WeakPtrFactory<HTMLDocumentView> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(HTMLDocumentView);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
diff --git a/mojo/services/html_viewer/html_viewer.cc b/mojo/services/html_viewer/html_viewer.cc
new file mode 100644
index 0000000..ebde85f
--- /dev/null
+++ b/mojo/services/html_viewer/html_viewer.cc
@@ -0,0 +1,117 @@
+// 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/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/services/html_viewer/blink_platform_impl.h"
+#include "mojo/services/html_viewer/html_document_view.h"
+#include "mojo/services/html_viewer/webmediaplayer_factory.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+#include "third_party/WebKit/public/web/WebKit.h"
+
+#if !defined(COMPONENT_BUILD)
+#include "base/i18n/icu_util.h"
+#include "base/path_service.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+#endif
+
+namespace mojo {
+
+class HTMLViewer;
+
+class ContentHandlerImpl : public InterfaceImpl<ContentHandler> {
+ public:
+ ContentHandlerImpl(Shell* shell,
+ scoped_refptr<base::MessageLoopProxy> compositor_thread,
+ WebMediaPlayerFactory* web_media_player_factory)
+ : shell_(shell),
+ compositor_thread_(compositor_thread),
+ web_media_player_factory_(web_media_player_factory) {}
+ virtual ~ContentHandlerImpl() {}
+
+ private:
+ // Overridden from ContentHandler:
+ virtual void OnConnect(
+ const mojo::String& url,
+ URLResponsePtr response,
+ InterfaceRequest<ServiceProvider> service_provider_request) override {
+ new HTMLDocumentView(response.Pass(),
+ service_provider_request.Pass(),
+ shell_,
+ compositor_thread_,
+ web_media_player_factory_);
+ }
+
+ Shell* shell_;
+ scoped_refptr<base::MessageLoopProxy> compositor_thread_;
+ WebMediaPlayerFactory* web_media_player_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl);
+};
+
+class HTMLViewer : public ApplicationDelegate,
+ public InterfaceFactory<ContentHandler> {
+ public:
+ HTMLViewer() : compositor_thread_("compositor thread") {}
+
+ virtual ~HTMLViewer() { blink::shutdown(); }
+
+ private:
+ // Overridden from ApplicationDelegate:
+ virtual void Initialize(ApplicationImpl* app) override {
+ shell_ = app->shell();
+ blink_platform_impl_.reset(new BlinkPlatformImpl(app));
+ blink::initialize(blink_platform_impl_.get());
+#if !defined(COMPONENT_BUILD)
+ base::i18n::InitializeICU();
+
+ ui::RegisterPathProvider();
+
+ base::FilePath ui_test_pak_path;
+ CHECK(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+ ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
+#endif
+
+ compositor_thread_.Start();
+ web_media_player_factory_.reset(new WebMediaPlayerFactory(
+ compositor_thread_.message_loop_proxy()));
+ }
+
+ virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+ override {
+ connection->AddService(this);
+ return true;
+ }
+
+ // Overridden from InterfaceFactory<ContentHandler>
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<ContentHandler> request) override {
+ BindToRequest(
+ new ContentHandlerImpl(shell_, compositor_thread_.message_loop_proxy(),
+ web_media_player_factory_.get()),
+ &request);
+ }
+
+ scoped_ptr<BlinkPlatformImpl> blink_platform_impl_;
+ Shell* shell_;
+ base::Thread compositor_thread_;
+ scoped_ptr<WebMediaPlayerFactory> web_media_player_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTMLViewer);
+};
+
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new mojo::HTMLViewer);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/html_viewer/webclipboard_impl.cc b/mojo/services/html_viewer/webclipboard_impl.cc
new file mode 100644
index 0000000..94b16ca
--- /dev/null
+++ b/mojo/services/html_viewer/webclipboard_impl.cc
@@ -0,0 +1,222 @@
+// 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/html_viewer/webclipboard_impl.h"
+
+#include "base/bind.h"
+#include "mojo/services/html_viewer/blink_basic_type_converters.h"
+
+namespace mojo {
+namespace {
+
+void CopyUint64(uint64_t* output, uint64_t input) {
+ *output = input;
+}
+
+void CopyWebString(blink::WebString* output,
+ const mojo::Array<uint8_t>& input) {
+ // blink does not differentiate between the requested data type not existing
+ // and the empty string.
+ if (input.is_null()) {
+ output->reset();
+ } else {
+ *output = blink::WebString::fromUTF8(
+ reinterpret_cast<const char*>(&input.front()),
+ input.size());
+ }
+}
+
+void CopyURL(blink::WebURL* pageURL,
+ const mojo::Array<uint8_t>& input) {
+ if (input.is_null()) {
+ *pageURL = blink::WebURL();
+ } else {
+ *pageURL = GURL(std::string(reinterpret_cast<const char*>(&input.front()),
+ input.size()));
+ }
+}
+
+void CopyVectorString(std::vector<std::string>* output,
+ const Array<String>& input) {
+ *output = input.To<std::vector<std::string> >();
+}
+
+template <typename T, typename U>
+bool Contains(const std::vector<T>& v, const U& item) {
+ return std::find(v.begin(), v.end(), item) != v.end();
+}
+
+const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
+
+} // namespace
+
+WebClipboardImpl::WebClipboardImpl(ClipboardPtr clipboard)
+ : clipboard_(clipboard.Pass()) {
+}
+
+WebClipboardImpl::~WebClipboardImpl() {
+}
+
+uint64_t WebClipboardImpl::sequenceNumber(Buffer buffer) {
+ mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+ uint64_t number = 0;
+ clipboard_->GetSequenceNumber(clipboard_type,
+ base::Bind(&CopyUint64, &number));
+
+ // Force this to be synchronous.
+ clipboard_.WaitForIncomingMethodCall();
+ return number;
+}
+
+bool WebClipboardImpl::isFormatAvailable(Format format, Buffer buffer) {
+ mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+ std::vector<std::string> types;
+ clipboard_->GetAvailableMimeTypes(
+ clipboard_type, base::Bind(&CopyVectorString, &types));
+
+ // Force this to be synchronous.
+ clipboard_.WaitForIncomingMethodCall();
+
+ switch (format) {
+ case FormatPlainText:
+ return Contains(types, mojo::Clipboard::MIME_TYPE_TEXT);
+ case FormatHTML:
+ return Contains(types, mojo::Clipboard::MIME_TYPE_HTML);
+ case FormatSmartPaste:
+ return Contains(types, kMimeTypeWebkitSmartPaste);
+ case FormatBookmark:
+ // This might be difficult.
+ return false;
+ }
+
+ return false;
+}
+
+blink::WebVector<blink::WebString> WebClipboardImpl::readAvailableTypes(
+ Buffer buffer,
+ bool* containsFilenames) {
+ mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+ std::vector<std::string> types;
+ clipboard_->GetAvailableMimeTypes(
+ clipboard_type, base::Bind(&CopyVectorString, &types));
+
+ // Force this to be synchronous.
+ clipboard_.WaitForIncomingMethodCall();
+
+ // AFAICT, every instance of setting containsFilenames is false.
+ *containsFilenames = false;
+
+ blink::WebVector<blink::WebString> output(types.size());
+ for (size_t i = 0; i < types.size(); ++i) {
+ output[i] = blink::WebString::fromUTF8(types[i]);
+ }
+
+ return output;
+}
+
+blink::WebString WebClipboardImpl::readPlainText(Buffer buffer) {
+ mojo::Clipboard::Type type = ConvertBufferType(buffer);
+
+ blink::WebString text;
+ clipboard_->ReadMimeType(
+ type, mojo::Clipboard::MIME_TYPE_TEXT, base::Bind(&CopyWebString, &text));
+
+ // Force this to be synchronous.
+ clipboard_.WaitForIncomingMethodCall();
+
+ return text;
+}
+
+blink::WebString WebClipboardImpl::readHTML(Buffer buffer,
+ blink::WebURL* pageURL,
+ unsigned* fragmentStart,
+ unsigned* fragmentEnd) {
+ mojo::Clipboard::Type type = ConvertBufferType(buffer);
+
+ blink::WebString html;
+ clipboard_->ReadMimeType(
+ type, mojo::Clipboard::MIME_TYPE_HTML, base::Bind(&CopyWebString, &html));
+ clipboard_.WaitForIncomingMethodCall();
+
+ *fragmentStart = 0;
+ *fragmentEnd = static_cast<unsigned>(html.length());
+
+ clipboard_->ReadMimeType(
+ type, mojo::Clipboard::MIME_TYPE_URL, base::Bind(&CopyURL, pageURL));
+ clipboard_.WaitForIncomingMethodCall();
+
+ return html;
+}
+
+blink::WebString WebClipboardImpl::readCustomData(
+ Buffer buffer,
+ const blink::WebString& mime_type) {
+ mojo::Clipboard::Type clipboard_type = ConvertBufferType(buffer);
+
+ blink::WebString data;
+ clipboard_->ReadMimeType(
+ clipboard_type, mime_type.utf8(), base::Bind(&CopyWebString, &data));
+
+ // Force this to be synchronous.
+ clipboard_.WaitForIncomingMethodCall();
+
+ return data;
+}
+
+void WebClipboardImpl::writePlainText(const blink::WebString& text) {
+ Array<MimeTypePairPtr> data;
+ MimeTypePairPtr text_data(MimeTypePair::New());
+ text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+ text_data->data = Array<uint8_t>::From(text).Pass();
+ data.push_back(text_data.Pass());
+
+ clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE, data.Pass());
+}
+
+void WebClipboardImpl::writeHTML(const blink::WebString& htmlText,
+ const blink::WebURL& url,
+ const blink::WebString& plainText,
+ bool writeSmartPaste) {
+ Array<MimeTypePairPtr> data;
+ MimeTypePairPtr text_data(MimeTypePair::New());
+ text_data->mime_type = mojo::Clipboard::MIME_TYPE_TEXT;
+ text_data->data = Array<uint8_t>::From(plainText).Pass();
+ data.push_back(text_data.Pass());
+
+ MimeTypePairPtr html_data(MimeTypePair::New());
+ text_data->mime_type = mojo::Clipboard::MIME_TYPE_HTML;
+ text_data->data = Array<uint8_t>::From(htmlText).Pass();
+ data.push_back(html_data.Pass());
+
+ MimeTypePairPtr url_data(MimeTypePair::New());
+ url_data->mime_type = mojo::Clipboard::MIME_TYPE_URL;
+ url_data->data = Array<uint8_t>::From(url.string()).Pass();
+ data.push_back(url_data.Pass());
+
+ if (writeSmartPaste) {
+ MimeTypePairPtr smart_paste(MimeTypePair::New());
+ url_data->mime_type = kMimeTypeWebkitSmartPaste;
+ url_data->data = Array<uint8_t>::From(blink::WebString()).Pass();
+ data.push_back(smart_paste.Pass());
+ }
+
+ clipboard_->WriteClipboardData(mojo::Clipboard::TYPE_COPY_PASTE, data.Pass());
+}
+
+mojo::Clipboard::Type WebClipboardImpl::ConvertBufferType(Buffer buffer) {
+ switch (buffer) {
+ case BufferStandard:
+ return mojo::Clipboard::TYPE_COPY_PASTE;
+ case BufferSelection:
+ return mojo::Clipboard::TYPE_SELECTION;
+ }
+
+ NOTREACHED();
+ return mojo::Clipboard::TYPE_COPY_PASTE;
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webclipboard_impl.h b/mojo/services/html_viewer/webclipboard_impl.h
new file mode 100644
index 0000000..0c307c5
--- /dev/null
+++ b/mojo/services/html_viewer/webclipboard_impl.h
@@ -0,0 +1,48 @@
+// 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_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_
+
+#include "mojo/services/public/interfaces/clipboard/clipboard.mojom.h"
+#include "third_party/WebKit/public/platform/WebClipboard.h"
+
+namespace mojo {
+
+class WebClipboardImpl : public blink::WebClipboard {
+ public:
+ WebClipboardImpl(ClipboardPtr clipboard);
+ virtual ~WebClipboardImpl();
+
+ // blink::WebClipboard methods:
+ virtual uint64_t sequenceNumber(Buffer);
+ virtual bool isFormatAvailable(Format, Buffer);
+ virtual blink::WebVector<blink::WebString> readAvailableTypes(
+ Buffer,
+ bool* containsFilenames);
+ virtual blink::WebString readPlainText(Buffer);
+ virtual blink::WebString readHTML(Buffer buffer,
+ blink::WebURL* pageURL,
+ unsigned* fragmentStart,
+ unsigned* fragmentEnd);
+ // TODO(erg): readImage()
+ virtual blink::WebString readCustomData(Buffer, const blink::WebString& type);
+ virtual void writePlainText(const blink::WebString&);
+ virtual void writeHTML(const blink::WebString& htmlText,
+ const blink::WebURL&,
+ const blink::WebString& plainText,
+ bool writeSmartPaste);
+
+ private:
+ // Changes webkit buffers to mojo Clipboard::Types.
+ mojo::Clipboard::Type ConvertBufferType(Buffer buffer);
+
+ ClipboardPtr clipboard_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebClipboardImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_
diff --git a/mojo/services/html_viewer/webcookiejar_impl.cc b/mojo/services/html_viewer/webcookiejar_impl.cc
new file mode 100644
index 0000000..0d86480
--- /dev/null
+++ b/mojo/services/html_viewer/webcookiejar_impl.cc
@@ -0,0 +1,69 @@
+// 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/html_viewer/webcookiejar_impl.h"
+
+#include "base/bind.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+
+namespace mojo {
+namespace {
+
+void CopyBool(bool* output, bool input) {
+ *output = input;
+}
+
+void CopyString(String* output, const String& input) {
+ *output = input;
+}
+
+} // namespace
+
+WebCookieJarImpl::WebCookieJarImpl(CookieStorePtr store)
+ : store_(store.Pass()) {
+}
+
+WebCookieJarImpl::~WebCookieJarImpl() {
+}
+
+void WebCookieJarImpl::setCookie(const blink::WebURL& url,
+ const blink::WebURL& first_party_for_cookies,
+ const blink::WebString& cookie) {
+ bool success;
+ store_->Set(url.string().utf8(), cookie.utf8(),
+ base::Bind(&CopyBool, &success));
+
+ // Wait to ensure the cookie was set before advancing. That way any
+ // subsequent URL request will see the changes to the cookie store.
+ //
+ // TODO(darin): Consider using associated message pipes for the CookieStore
+ // and URLLoader, so that we could let this method call run asynchronously
+ // without suffering an ordering problem. See crbug/386825.
+ //
+ store_.WaitForIncomingMethodCall();
+}
+
+blink::WebString WebCookieJarImpl::cookies(
+ const blink::WebURL& url,
+ const blink::WebURL& first_party_for_cookies) {
+ String result;
+ store_->Get(url.string().utf8(), base::Bind(&CopyString, &result));
+
+ // Wait for the result. Since every outbound request we make to the cookie
+ // store is followed up with WaitForIncomingMethodCall, we can be sure that
+ // the next incoming method call will be the response to our request.
+ store_.WaitForIncomingMethodCall();
+ if (!result)
+ return blink::WebString();
+
+ return blink::WebString::fromUTF8(result);
+}
+
+blink::WebString WebCookieJarImpl::cookieRequestHeaderFieldValue(
+ const blink::WebURL& url,
+ const blink::WebURL& first_party_for_cookies) {
+ return cookies(url, first_party_for_cookies);
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webcookiejar_impl.h b/mojo/services/html_viewer/webcookiejar_impl.h
new file mode 100644
index 0000000..cfa0055
--- /dev/null
+++ b/mojo/services/html_viewer/webcookiejar_impl.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_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_
+
+#include "mojo/services/public/interfaces/network/cookie_store.mojom.h"
+#include "third_party/WebKit/public/platform/WebCookieJar.h"
+
+namespace mojo {
+
+class WebCookieJarImpl : public blink::WebCookieJar {
+ public:
+ explicit WebCookieJarImpl(CookieStorePtr store);
+ virtual ~WebCookieJarImpl();
+
+ // blink::WebCookieJar methods:
+ virtual void setCookie(
+ const blink::WebURL& url,
+ const blink::WebURL& first_party_for_cookies,
+ const blink::WebString& cookie);
+ virtual blink::WebString cookies(
+ const blink::WebURL& url,
+ const blink::WebURL& first_party_for_cookies);
+ virtual blink::WebString cookieRequestHeaderFieldValue(
+ const blink::WebURL& url,
+ const blink::WebURL& first_party_for_cookies);
+
+ private:
+ CookieStorePtr store_;
+ DISALLOW_COPY_AND_ASSIGN(WebCookieJarImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.cc b/mojo/services/html_viewer/weblayertreeview_impl.cc
new file mode 100644
index 0000000..56ebc34
--- /dev/null
+++ b/mojo/services/html_viewer/weblayertreeview_impl.cc
@@ -0,0 +1,243 @@
+// 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/html_viewer/weblayertreeview_impl.h"
+
+#include "base/message_loop/message_loop_proxy.h"
+#include "cc/blink/web_layer_impl.h"
+#include "cc/layers/layer.h"
+#include "cc/output/begin_frame_args.h"
+#include "cc/trees/layer_tree_host.h"
+#include "mojo/cc/context_provider_mojo.h"
+#include "mojo/cc/output_surface_mojo.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "third_party/WebKit/public/web/WebWidget.h"
+
+namespace mojo {
+
+WebLayerTreeViewImpl::WebLayerTreeViewImpl(
+ scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy,
+ SurfacesServicePtr surfaces_service,
+ GpuPtr gpu_service)
+ : widget_(NULL),
+ view_(NULL),
+ surfaces_service_(surfaces_service.Pass()),
+ gpu_service_(gpu_service.Pass()),
+ main_thread_compositor_task_runner_(base::MessageLoopProxy::current()),
+ weak_factory_(this) {
+ main_thread_bound_weak_ptr_ = weak_factory_.GetWeakPtr();
+ surfaces_service_->CreateSurfaceConnection(
+ base::Bind(&WebLayerTreeViewImpl::OnSurfaceConnectionCreated,
+ main_thread_bound_weak_ptr_));
+
+ cc::LayerTreeSettings settings;
+
+ // For web contents, layer transforms should scale up the contents of layers
+ // to keep content always crisp when possible.
+ settings.layer_transforms_should_scale_layer_contents = true;
+
+ cc::SharedBitmapManager* shared_bitmap_manager = NULL;
+
+ layer_tree_host_ =
+ cc::LayerTreeHost::CreateThreaded(this,
+ shared_bitmap_manager,
+ settings,
+ base::MessageLoopProxy::current(),
+ compositor_message_loop_proxy);
+ DCHECK(layer_tree_host_);
+}
+
+WebLayerTreeViewImpl::~WebLayerTreeViewImpl() {
+}
+
+void WebLayerTreeViewImpl::WillBeginMainFrame(int frame_id) {
+}
+
+void WebLayerTreeViewImpl::DidBeginMainFrame() {
+}
+
+void WebLayerTreeViewImpl::BeginMainFrame(const cc::BeginFrameArgs& args) {
+ VLOG(2) << "WebLayerTreeViewImpl::BeginMainFrame";
+ double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
+ double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF();
+ double interval_sec = args.interval.InSecondsF();
+ blink::WebBeginFrameArgs web_begin_frame_args(
+ frame_time_sec, deadline_sec, interval_sec);
+ widget_->beginFrame(web_begin_frame_args);
+}
+
+void WebLayerTreeViewImpl::Layout() {
+ widget_->layout();
+}
+
+void WebLayerTreeViewImpl::ApplyViewportDeltas(
+ const gfx::Vector2d& inner_delta,
+ const gfx::Vector2d& outer_delta,
+ float page_scale,
+ float top_controls_delta) {
+ widget_->applyViewportDeltas(
+ inner_delta,
+ outer_delta,
+ page_scale,
+ top_controls_delta);
+}
+
+void WebLayerTreeViewImpl::ApplyViewportDeltas(
+ const gfx::Vector2d& scroll_delta,
+ float page_scale,
+ float top_controls_delta) {
+ widget_->applyViewportDeltas(scroll_delta, page_scale, top_controls_delta);
+}
+
+void WebLayerTreeViewImpl::RequestNewOutputSurface(bool fallback) {
+ layer_tree_host_->SetOutputSurface(output_surface_.Pass());
+}
+
+void WebLayerTreeViewImpl::DidInitializeOutputSurface() {
+}
+
+void WebLayerTreeViewImpl::WillCommit() {
+}
+
+void WebLayerTreeViewImpl::DidCommit() {
+ widget_->didCommitFrameToCompositor();
+}
+
+void WebLayerTreeViewImpl::DidCommitAndDrawFrame() {
+}
+
+void WebLayerTreeViewImpl::DidCompleteSwapBuffers() {
+}
+
+void WebLayerTreeViewImpl::setSurfaceReady() {
+}
+
+void WebLayerTreeViewImpl::setRootLayer(const blink::WebLayer& layer) {
+ layer_tree_host_->SetRootLayer(
+ static_cast<const cc_blink::WebLayerImpl*>(&layer)->layer());
+}
+
+void WebLayerTreeViewImpl::clearRootLayer() {
+ layer_tree_host_->SetRootLayer(scoped_refptr<cc::Layer>());
+}
+
+void WebLayerTreeViewImpl::setViewportSize(
+ const blink::WebSize& device_viewport_size) {
+ layer_tree_host_->SetViewportSize(device_viewport_size);
+}
+
+blink::WebSize WebLayerTreeViewImpl::deviceViewportSize() const {
+ return layer_tree_host_->device_viewport_size();
+}
+
+void WebLayerTreeViewImpl::setDeviceScaleFactor(float device_scale_factor) {
+ layer_tree_host_->SetDeviceScaleFactor(device_scale_factor);
+}
+
+float WebLayerTreeViewImpl::deviceScaleFactor() const {
+ return layer_tree_host_->device_scale_factor();
+}
+
+void WebLayerTreeViewImpl::setBackgroundColor(blink::WebColor color) {
+ layer_tree_host_->set_background_color(color);
+}
+
+void WebLayerTreeViewImpl::setHasTransparentBackground(
+ bool has_transparent_background) {
+ layer_tree_host_->set_has_transparent_background(has_transparent_background);
+}
+
+void WebLayerTreeViewImpl::setOverhangBitmap(const SkBitmap& bitmap) {
+ layer_tree_host_->SetOverhangBitmap(bitmap);
+}
+
+void WebLayerTreeViewImpl::setVisible(bool visible) {
+ layer_tree_host_->SetVisible(visible);
+}
+
+void WebLayerTreeViewImpl::setPageScaleFactorAndLimits(float page_scale_factor,
+ float minimum,
+ float maximum) {
+ layer_tree_host_->SetPageScaleFactorAndLimits(
+ page_scale_factor, minimum, maximum);
+}
+
+void WebLayerTreeViewImpl::registerForAnimations(blink::WebLayer* layer) {
+ cc::Layer* cc_layer = static_cast<cc_blink::WebLayerImpl*>(layer)->layer();
+ cc_layer->layer_animation_controller()->SetAnimationRegistrar(
+ layer_tree_host_->animation_registrar());
+}
+
+void WebLayerTreeViewImpl::registerViewportLayers(
+ const blink::WebLayer* pageScaleLayer,
+ const blink::WebLayer* innerViewportScrollLayer,
+ const blink::WebLayer* outerViewportScrollLayer) {
+ layer_tree_host_->RegisterViewportLayers(
+ static_cast<const cc_blink::WebLayerImpl*>(pageScaleLayer)->layer(),
+ static_cast<const cc_blink::WebLayerImpl*>(innerViewportScrollLayer)
+ ->layer(),
+ // The outer viewport layer will only exist when using pinch virtual
+ // viewports.
+ outerViewportScrollLayer ? static_cast<const cc_blink::WebLayerImpl*>(
+ outerViewportScrollLayer)->layer()
+ : NULL);
+}
+
+void WebLayerTreeViewImpl::clearViewportLayers() {
+ layer_tree_host_->RegisterViewportLayers(scoped_refptr<cc::Layer>(),
+ scoped_refptr<cc::Layer>(),
+ scoped_refptr<cc::Layer>());
+}
+
+void WebLayerTreeViewImpl::startPageScaleAnimation(
+ const blink::WebPoint& destination,
+ bool use_anchor,
+ float new_page_scale,
+ double duration_sec) {
+ base::TimeDelta duration = base::TimeDelta::FromMicroseconds(
+ duration_sec * base::Time::kMicrosecondsPerSecond);
+ layer_tree_host_->StartPageScaleAnimation(
+ gfx::Vector2d(destination.x, destination.y),
+ use_anchor,
+ new_page_scale,
+ duration);
+}
+
+void WebLayerTreeViewImpl::setNeedsAnimate() {
+ layer_tree_host_->SetNeedsAnimate();
+}
+
+bool WebLayerTreeViewImpl::commitRequested() const {
+ return layer_tree_host_->CommitRequested();
+}
+
+void WebLayerTreeViewImpl::finishAllRendering() {
+ layer_tree_host_->FinishAllRendering();
+}
+
+void WebLayerTreeViewImpl::OnSurfaceConnectionCreated(SurfacePtr surface,
+ uint32_t id_namespace) {
+ CommandBufferPtr cb;
+ gpu_service_->CreateOffscreenGLES2Context(Get(&cb));
+ scoped_refptr<cc::ContextProvider> context_provider(
+ new ContextProviderMojo(cb.PassMessagePipe()));
+ output_surface_.reset(new OutputSurfaceMojo(
+ this, context_provider, surface.Pass(), id_namespace));
+ layer_tree_host_->SetLayerTreeHostClientReady();
+}
+
+void WebLayerTreeViewImpl::DidCreateSurface(cc::SurfaceId id) {
+ main_thread_compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&WebLayerTreeViewImpl::DidCreateSurfaceOnMainThread,
+ main_thread_bound_weak_ptr_,
+ id));
+}
+
+void WebLayerTreeViewImpl::DidCreateSurfaceOnMainThread(cc::SurfaceId id) {
+ view_->SetSurfaceId(SurfaceId::From(id));
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.h b/mojo/services/html_viewer/weblayertreeview_impl.h
new file mode 100644
index 0000000..5221902
--- /dev/null
+++ b/mojo/services/html_viewer/weblayertreeview_impl.h
@@ -0,0 +1,136 @@
+// 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_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/trees/layer_tree_host_client.h"
+#include "mojo/cc/output_surface_mojo.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "third_party/WebKit/public/platform/WebLayerTreeView.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace blink {
+class WebWidget;
+}
+
+namespace cc {
+class LayerTreeHost;
+}
+
+namespace mojo {
+class View;
+
+class WebLayerTreeViewImpl : public blink::WebLayerTreeView,
+ public cc::LayerTreeHostClient,
+ public OutputSurfaceMojoClient {
+ public:
+ WebLayerTreeViewImpl(
+ scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy,
+ SurfacesServicePtr surfaces_service,
+ GpuPtr gpu_service);
+ virtual ~WebLayerTreeViewImpl();
+
+ void set_widget(blink::WebWidget* widget) { widget_ = widget; }
+ void set_view(View* view) { view_ = view; }
+
+ // cc::LayerTreeHostClient implementation.
+ virtual void WillBeginMainFrame(int frame_id) override;
+ virtual void DidBeginMainFrame() override;
+ virtual void BeginMainFrame(const cc::BeginFrameArgs& args) override;
+ virtual void Layout() override;
+ virtual void ApplyViewportDeltas(const gfx::Vector2d& inner_delta,
+ const gfx::Vector2d& outer_delta,
+ float page_scale,
+ float top_controls_delta) override;
+ virtual void ApplyViewportDeltas(const gfx::Vector2d& scroll_delta,
+ float page_scale,
+ float top_controls_delta) override;
+ virtual void RequestNewOutputSurface(bool fallback) override;
+ virtual void DidInitializeOutputSurface() override;
+ virtual void WillCommit() override;
+ virtual void DidCommit() override;
+ virtual void DidCommitAndDrawFrame() override;
+ virtual void DidCompleteSwapBuffers() override;
+ virtual void RateLimitSharedMainThreadContext() override {}
+
+ // blink::WebLayerTreeView implementation.
+ virtual void setSurfaceReady() override;
+ virtual void setRootLayer(const blink::WebLayer& layer) override;
+ virtual void clearRootLayer() override;
+ virtual void setViewportSize(
+ const blink::WebSize& device_viewport_size) override;
+ virtual blink::WebSize deviceViewportSize() const override;
+ virtual void setDeviceScaleFactor(float) override;
+ virtual float deviceScaleFactor() const override;
+ virtual void setBackgroundColor(blink::WebColor color) override;
+ virtual void setHasTransparentBackground(
+ bool has_transparent_background) override;
+ virtual void setOverhangBitmap(const SkBitmap& bitmap) override;
+ virtual void setVisible(bool visible) override;
+ virtual void setPageScaleFactorAndLimits(float page_scale_factor,
+ float minimum,
+ float maximum) override;
+ virtual void startPageScaleAnimation(const blink::WebPoint& destination,
+ bool use_anchor,
+ float new_page_scale,
+ double duration_sec) override;
+ virtual void heuristicsForGpuRasterizationUpdated(bool matches_heuristic) {}
+ virtual void setTopControlsContentOffset(float offset) {}
+ virtual void setNeedsAnimate() override;
+ virtual bool commitRequested() const override;
+ virtual void didStopFlinging() {}
+ virtual void compositeAndReadbackAsync(
+ blink::WebCompositeAndReadbackAsyncCallback* callback) {}
+ virtual void finishAllRendering() override;
+ virtual void setDeferCommits(bool defer_commits) {}
+ virtual void registerForAnimations(blink::WebLayer* layer) override;
+ virtual void registerViewportLayers(
+ const blink::WebLayer* page_scale_layer,
+ const blink::WebLayer* inner_viewport_scroll_layer,
+ const blink::WebLayer* outer_viewport_scroll_layer) override;
+ virtual void clearViewportLayers() override;
+ virtual void registerSelection(const blink::WebSelectionBound& start,
+ const blink::WebSelectionBound& end) {}
+ virtual void clearSelection() {}
+ virtual void setShowFPSCounter(bool) {}
+ virtual void setShowPaintRects(bool) {}
+ virtual void setShowDebugBorders(bool) {}
+ virtual void setContinuousPaintingEnabled(bool) {}
+ virtual void setShowScrollBottleneckRects(bool) {}
+
+ // OutputSurfaceMojoClient implementation.
+ virtual void DidCreateSurface(cc::SurfaceId id) override;
+
+ private:
+ void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+ void DidCreateSurfaceOnMainThread(cc::SurfaceId id);
+
+ // widget_ and view_ will outlive us.
+ blink::WebWidget* widget_;
+ View* view_;
+ scoped_ptr<cc::LayerTreeHost> layer_tree_host_;
+ SurfacesServicePtr surfaces_service_;
+ scoped_ptr<cc::OutputSurface> output_surface_;
+ GpuPtr gpu_service_;
+ scoped_refptr<base::SingleThreadTaskRunner>
+ main_thread_compositor_task_runner_;
+ base::WeakPtr<WebLayerTreeViewImpl> main_thread_bound_weak_ptr_;
+
+ base::WeakPtrFactory<WebLayerTreeViewImpl> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(WebLayerTreeViewImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_
diff --git a/mojo/services/html_viewer/webmediaplayer_factory.cc b/mojo/services/html_viewer/webmediaplayer_factory.cc
new file mode 100644
index 0000000..bbf8c81
--- /dev/null
+++ b/mojo/services/html_viewer/webmediaplayer_factory.cc
@@ -0,0 +1,87 @@
+// 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/html_viewer/webmediaplayer_factory.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/threading/thread.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/null_audio_sink.h"
+#include "media/base/audio_hardware_config.h"
+#include "media/base/media.h"
+#include "media/base/media_log.h"
+#include "media/blink/null_encrypted_media_player_support.h"
+#include "media/blink/webmediaplayer_impl.h"
+#include "media/blink/webmediaplayer_params.h"
+#include "media/filters/gpu_video_accelerator_factories.h"
+
+namespace mojo {
+
+WebMediaPlayerFactory::WebMediaPlayerFactory(
+ const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner)
+ : compositor_task_runner_(compositor_task_runner),
+ media_thread_("Media"),
+ audio_manager_(media::AudioManager::Create(&fake_audio_log_factory_)),
+ audio_hardware_config_(
+ audio_manager_->GetInputStreamParameters(
+ media::AudioManagerBase::kDefaultDeviceId),
+ audio_manager_->GetDefaultOutputStreamParameters()) {
+
+ if (!media::IsMediaLibraryInitialized()) {
+ base::FilePath module_dir;
+ CHECK(PathService::Get(base::DIR_EXE, &module_dir));
+ CHECK(media::InitializeMediaLibrary(module_dir));
+ }
+}
+
+WebMediaPlayerFactory::~WebMediaPlayerFactory() {
+}
+
+blink::WebMediaPlayer* WebMediaPlayerFactory::CreateMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client) {
+#if defined(OS_ANDROID)
+ return NULL;
+#else
+
+ media::WebMediaPlayerParams params(
+ media::WebMediaPlayerParams::DeferLoadCB(),
+ CreateAudioRendererSink(),
+ GetAudioHardwareConfig(),
+ new media::MediaLog(),
+ scoped_refptr<media::GpuVideoAcceleratorFactories>(),
+ GetMediaThreadTaskRunner(),
+ compositor_task_runner_,
+ base::Bind(&media::NullEncryptedMediaPlayerSupport::Create),
+ NULL);
+ base::WeakPtr<media::WebMediaPlayerDelegate> delegate;
+
+ return new media::WebMediaPlayerImpl(frame, client, delegate, params);
+#endif
+}
+
+const media::AudioHardwareConfig&
+WebMediaPlayerFactory::GetAudioHardwareConfig() {
+ return audio_hardware_config_;
+}
+
+scoped_refptr<media::AudioRendererSink>
+WebMediaPlayerFactory::CreateAudioRendererSink() {
+ // TODO(acolwell): Replace this with an AudioRendererSink implementation
+ // that actually talks to the audio device or an audio mojo service.
+ return new media::NullAudioSink(GetMediaThreadTaskRunner());
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WebMediaPlayerFactory::GetMediaThreadTaskRunner() {
+ if (!media_thread_.IsRunning())
+ media_thread_.Start();
+
+ return media_thread_.message_loop_proxy();
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webmediaplayer_factory.h b/mojo/services/html_viewer/webmediaplayer_factory.h
new file mode 100644
index 0000000..db85370
--- /dev/null
+++ b/mojo/services/html_viewer/webmediaplayer_factory.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_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/base/audio_hardware_config.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace blink {
+class WebMediaPlayer;
+class WebLocalFrame;
+class WebURL;
+class WebMediaPlayerClient;
+}
+
+namespace media {
+class AudioManager;
+class AudioRendererSink;
+}
+
+namespace mojo {
+
+// Helper class used to create blink::WebMediaPlayer objects.
+// This class stores the "global state" shared across all WebMediaPlayer
+// instances.
+class WebMediaPlayerFactory {
+ public:
+ explicit WebMediaPlayerFactory(const scoped_refptr<
+ base::SingleThreadTaskRunner>& compositor_task_runner);
+ ~WebMediaPlayerFactory();
+
+ blink::WebMediaPlayer* CreateMediaPlayer(blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client);
+
+ private:
+ const media::AudioHardwareConfig& GetAudioHardwareConfig();
+ scoped_refptr<media::AudioRendererSink> CreateAudioRendererSink();
+ scoped_refptr<base::SingleThreadTaskRunner> GetMediaThreadTaskRunner();
+
+ scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+ base::Thread media_thread_;
+ media::FakeAudioLogFactory fake_audio_log_factory_;
+ scoped_ptr<media::AudioManager> audio_manager_;
+ media::AudioHardwareConfig audio_hardware_config_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerFactory);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_
diff --git a/mojo/services/html_viewer/webmimeregistry_impl.cc b/mojo/services/html_viewer/webmimeregistry_impl.cc
new file mode 100644
index 0000000..2faac17
--- /dev/null
+++ b/mojo/services/html_viewer/webmimeregistry_impl.cc
@@ -0,0 +1,130 @@
+// 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/html_viewer/webmimeregistry_impl.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "net/base/mime_util.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace mojo {
+namespace {
+
+std::string ToASCIIOrEmpty(const blink::WebString& string) {
+ return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
+ : std::string();
+}
+
+} // namespace
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsImageMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedImageMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType
+WebMimeRegistryImpl::supportsImagePrefixedMIMEType(
+ const blink::WebString& mime_type) {
+ std::string ascii_mime_type = ToASCIIOrEmpty(mime_type);
+ return (net::IsSupportedImageMimeType(ascii_mime_type) ||
+ (StartsWithASCII(ascii_mime_type, "image/", true) &&
+ net::IsSupportedNonImageMimeType(ascii_mime_type)))
+ ? WebMimeRegistry::IsSupported
+ : WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType
+ WebMimeRegistryImpl::supportsJavaScriptMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedJavascriptMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsMediaMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs,
+ const blink::WebString& key_system) {
+ const std::string mime_type_ascii = ToASCIIOrEmpty(mime_type);
+ // Not supporting the container is a flat-out no.
+ if (!net::IsSupportedMediaMimeType(mime_type_ascii))
+ return IsNotSupported;
+
+ // Mojo does not currently support any key systems.
+ if (!key_system.isEmpty())
+ return IsNotSupported;
+
+ // Check list of strict codecs to see if it is supported.
+ if (net::IsStrictMediaMimeType(mime_type_ascii)) {
+ // Check if the codecs are a perfect match.
+ std::vector<std::string> strict_codecs;
+ net::ParseCodecString(ToASCIIOrEmpty(codecs), &strict_codecs, false);
+ return static_cast<WebMimeRegistry::SupportsType>(
+ net::IsSupportedStrictMediaMimeType(mime_type_ascii, strict_codecs));
+ }
+
+ // If we don't recognize the codec, it's possible we support it.
+ std::vector<std::string> parsed_codecs;
+ net::ParseCodecString(ToASCIIOrEmpty(codecs), &parsed_codecs, true);
+ if (!net::AreSupportedMediaCodecs(parsed_codecs))
+ return MayBeSupported;
+
+ // Otherwise we have a perfect match.
+ return IsSupported;
+}
+
+bool WebMimeRegistryImpl::supportsMediaSourceMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool WebMimeRegistryImpl::supportsEncryptedMediaMIMEType(
+ const blink::WebString& key_system,
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+blink::WebMimeRegistry::SupportsType
+ WebMimeRegistryImpl::supportsNonImageMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedNonImageMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebString WebMimeRegistryImpl::mimeTypeForExtension(
+ const blink::WebString& file_extension) {
+ NOTIMPLEMENTED();
+ return blink::WebString();
+}
+
+blink::WebString WebMimeRegistryImpl::wellKnownMimeTypeForExtension(
+ const blink::WebString& file_extension) {
+ NOTIMPLEMENTED();
+ return blink::WebString();
+}
+
+blink::WebString WebMimeRegistryImpl::mimeTypeFromFile(
+ const blink::WebString& file_path) {
+ NOTIMPLEMENTED();
+ return blink::WebString();
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webmimeregistry_impl.h b/mojo/services/html_viewer/webmimeregistry_impl.h
new file mode 100644
index 0000000..ba25ec9
--- /dev/null
+++ b/mojo/services/html_viewer/webmimeregistry_impl.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_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "third_party/WebKit/public/platform/WebMimeRegistry.h"
+
+namespace mojo {
+
+class WebMimeRegistryImpl : public blink::WebMimeRegistry {
+ public:
+ WebMimeRegistryImpl() {}
+ virtual ~WebMimeRegistryImpl() {}
+
+ // WebMimeRegistry methods:
+ virtual blink::WebMimeRegistry::SupportsType supportsMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsImageMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsImagePrefixedMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsJavaScriptMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsMediaMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs,
+ const blink::WebString& key_system);
+ virtual bool supportsMediaSourceMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs);
+ virtual bool supportsEncryptedMediaMIMEType(
+ const blink::WebString& key_system,
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs);
+ virtual blink::WebMimeRegistry::SupportsType supportsNonImageMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebString mimeTypeForExtension(
+ const blink::WebString& extension);
+ virtual blink::WebString wellKnownMimeTypeForExtension(
+ const blink::WebString& extension);
+ virtual blink::WebString mimeTypeFromFile(
+ const blink::WebString& path);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
diff --git a/mojo/services/html_viewer/websockethandle_impl.cc b/mojo/services/html_viewer/websockethandle_impl.cc
new file mode 100644
index 0000000..b927b10
--- /dev/null
+++ b/mojo/services/html_viewer/websockethandle_impl.cc
@@ -0,0 +1,218 @@
+// 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/html_viewer/websockethandle_impl.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/services/html_viewer/blink_basic_type_converters.h"
+#include "mojo/services/public/cpp/network/web_socket_read_queue.h"
+#include "mojo/services/public/cpp/network/web_socket_write_queue.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "third_party/WebKit/public/platform/WebSerializedOrigin.h"
+#include "third_party/WebKit/public/platform/WebSocketHandleClient.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+
+using blink::WebSerializedOrigin;
+using blink::WebSocketHandle;
+using blink::WebSocketHandleClient;
+using blink::WebString;
+using blink::WebURL;
+using blink::WebVector;
+
+namespace mojo {
+
+template<>
+struct TypeConverter<WebSocket::MessageType, WebSocketHandle::MessageType> {
+ static WebSocket::MessageType Convert(WebSocketHandle::MessageType type) {
+ DCHECK(type == WebSocketHandle::MessageTypeContinuation ||
+ type == WebSocketHandle::MessageTypeText ||
+ type == WebSocketHandle::MessageTypeBinary);
+ typedef WebSocket::MessageType MessageType;
+ COMPILE_ASSERT(
+ static_cast<MessageType>(WebSocketHandle::MessageTypeContinuation) ==
+ WebSocket::MESSAGE_TYPE_CONTINUATION,
+ enum_values_must_match_for_message_type);
+ COMPILE_ASSERT(
+ static_cast<MessageType>(WebSocketHandle::MessageTypeText) ==
+ WebSocket::MESSAGE_TYPE_TEXT,
+ enum_values_must_match_for_message_type);
+ COMPILE_ASSERT(
+ static_cast<MessageType>(WebSocketHandle::MessageTypeBinary) ==
+ WebSocket::MESSAGE_TYPE_BINARY,
+ enum_values_must_match_for_message_type);
+ return static_cast<WebSocket::MessageType>(type);
+ }
+};
+
+template<>
+struct TypeConverter<WebSocketHandle::MessageType, WebSocket::MessageType> {
+ static WebSocketHandle::MessageType Convert(WebSocket::MessageType type) {
+ DCHECK(type == WebSocket::MESSAGE_TYPE_CONTINUATION ||
+ type == WebSocket::MESSAGE_TYPE_TEXT ||
+ type == WebSocket::MESSAGE_TYPE_BINARY);
+ return static_cast<WebSocketHandle::MessageType>(type);
+ }
+};
+
+// This class forms a bridge from the mojo WebSocketClient interface and the
+// Blink WebSocketHandleClient interface.
+class WebSocketClientImpl : public InterfaceImpl<WebSocketClient> {
+ public:
+ explicit WebSocketClientImpl(WebSocketHandleImpl* handle,
+ blink::WebSocketHandleClient* client)
+ : handle_(handle), client_(client) {}
+ virtual ~WebSocketClientImpl() {}
+
+ private:
+ // WebSocketClient methods:
+ virtual void DidConnect(bool fail,
+ const String& selected_subprotocol,
+ const String& extensions,
+ ScopedDataPipeConsumerHandle receive_stream)
+ override {
+ blink::WebSocketHandleClient* client = client_;
+ WebSocketHandleImpl* handle = handle_;
+ receive_stream_ = receive_stream.Pass();
+ read_queue_.reset(new WebSocketReadQueue(receive_stream_.get()));
+ if (fail)
+ handle->Disconnect(); // deletes |this|
+ client->didConnect(handle,
+ fail,
+ selected_subprotocol.To<WebString>(),
+ extensions.To<WebString>());
+ // |handle| can be deleted here.
+ }
+
+ virtual void DidReceiveData(bool fin,
+ WebSocket::MessageType type,
+ uint32_t num_bytes) override {
+ read_queue_->Read(num_bytes,
+ base::Bind(&WebSocketClientImpl::DidReadFromReceiveStream,
+ base::Unretained(this),
+ fin, type, num_bytes));
+ }
+
+ virtual void DidReceiveFlowControl(int64_t quota) override {
+ client_->didReceiveFlowControl(handle_, quota);
+ // |handle| can be deleted here.
+ }
+
+ virtual void DidFail(const String& message) override {
+ blink::WebSocketHandleClient* client = client_;
+ WebSocketHandleImpl* handle = handle_;
+ handle->Disconnect(); // deletes |this|
+ client->didFail(handle, message.To<WebString>());
+ // |handle| can be deleted here.
+ }
+
+ virtual void DidClose(bool was_clean,
+ uint16_t code,
+ const String& reason) override {
+ blink::WebSocketHandleClient* client = client_;
+ WebSocketHandleImpl* handle = handle_;
+ handle->Disconnect(); // deletes |this|
+ client->didClose(handle, was_clean, code, reason.To<WebString>());
+ // |handle| can be deleted here.
+ }
+
+ void DidReadFromReceiveStream(bool fin,
+ WebSocket::MessageType type,
+ uint32_t num_bytes,
+ const char* data) {
+ client_->didReceiveData(handle_,
+ fin,
+ ConvertTo<WebSocketHandle::MessageType>(type),
+ data,
+ num_bytes);
+ // |handle_| can be deleted here.
+ }
+
+ // |handle_| owns this object, so it is guaranteed to outlive us.
+ WebSocketHandleImpl* handle_;
+ blink::WebSocketHandleClient* client_;
+ ScopedDataPipeConsumerHandle receive_stream_;
+ scoped_ptr<WebSocketReadQueue> read_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketClientImpl);
+};
+
+WebSocketHandleImpl::WebSocketHandleImpl(NetworkService* network_service)
+ : did_close_(false) {
+ network_service->CreateWebSocket(Get(&web_socket_));
+}
+
+WebSocketHandleImpl::~WebSocketHandleImpl() {
+ if (!did_close_) {
+ // The connection is abruptly disconnected by the renderer without
+ // closing handshake.
+ web_socket_->Close(WebSocket::kAbnormalCloseCode, String());
+ }
+}
+
+void WebSocketHandleImpl::connect(const WebURL& url,
+ const WebVector<WebString>& protocols,
+ const WebSerializedOrigin& origin,
+ WebSocketHandleClient* client) {
+ client_.reset(new WebSocketClientImpl(this, client));
+ WebSocketClientPtr client_ptr;
+ // TODO(mpcomplete): Is this the right ownership model? Or should mojo own
+ // |client_|?
+ WeakBindToProxy(client_.get(), &client_ptr);
+
+ DataPipe data_pipe;
+ send_stream_ = data_pipe.producer_handle.Pass();
+ write_queue_.reset(new WebSocketWriteQueue(send_stream_.get()));
+ web_socket_->Connect(url.string().utf8(),
+ Array<String>::From(protocols),
+ origin.string().utf8(),
+ data_pipe.consumer_handle.Pass(),
+ client_ptr.Pass());
+}
+
+void WebSocketHandleImpl::send(bool fin,
+ WebSocketHandle::MessageType type,
+ const char* data,
+ size_t size) {
+ if (!client_)
+ return;
+
+ uint32_t size32 = static_cast<uint32_t>(size);
+ write_queue_->Write(
+ data, size32,
+ base::Bind(&WebSocketHandleImpl::DidWriteToSendStream,
+ base::Unretained(this),
+ fin, type, size32));
+}
+
+void WebSocketHandleImpl::flowControl(int64_t quota) {
+ if (!client_)
+ return;
+
+ web_socket_->FlowControl(quota);
+}
+
+void WebSocketHandleImpl::close(unsigned short code, const WebString& reason) {
+ web_socket_->Close(code, reason.utf8());
+}
+
+void WebSocketHandleImpl::DidWriteToSendStream(
+ bool fin,
+ WebSocketHandle::MessageType type,
+ uint32_t num_bytes,
+ const char* data) {
+ web_socket_->Send(fin, ConvertTo<WebSocket::MessageType>(type), num_bytes);
+}
+
+void WebSocketHandleImpl::Disconnect() {
+ did_close_ = true;
+ client_.reset();
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/websockethandle_impl.h b/mojo/services/html_viewer/websockethandle_impl.h
new file mode 100644
index 0000000..f035dd9
--- /dev/null
+++ b/mojo/services/html_viewer/websockethandle_impl.h
@@ -0,0 +1,63 @@
+// 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_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/public/interfaces/network/web_socket.mojom.h"
+#include "third_party/WebKit/public/platform/WebSocketHandle.h"
+
+namespace mojo {
+class NetworkService;
+class WebSocketClientImpl;
+class WebSocketWriteQueue;
+
+// Implements WebSocketHandle by talking to the mojo WebSocket interface.
+class WebSocketHandleImpl : public blink::WebSocketHandle {
+ public:
+ explicit WebSocketHandleImpl(NetworkService* network_service);
+
+ private:
+ friend class WebSocketClientImpl;
+
+ virtual ~WebSocketHandleImpl();
+
+ // blink::WebSocketHandle methods:
+ virtual void connect(const blink::WebURL& url,
+ const blink::WebVector<blink::WebString>& protocols,
+ const blink::WebSerializedOrigin& origin,
+ blink::WebSocketHandleClient*) override;
+ virtual void send(bool fin,
+ MessageType,
+ const char* data,
+ size_t size) override;
+ virtual void flowControl(int64_t quota) override;
+ virtual void close(unsigned short code,
+ const blink::WebString& reason) override;
+
+ // Called when we finished writing to |send_stream_|.
+ void DidWriteToSendStream(bool fin,
+ WebSocketHandle::MessageType type,
+ uint32_t num_bytes,
+ const char* data);
+
+ // Called when the socket is closed.
+ void Disconnect();
+
+ WebSocketPtr web_socket_;
+ scoped_ptr<WebSocketClientImpl> client_;
+ ScopedDataPipeProducerHandle send_stream_;
+ scoped_ptr<WebSocketWriteQueue> write_queue_;
+ // True if close() was called.
+ bool did_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHandleImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_
diff --git a/mojo/services/html_viewer/webstoragenamespace_impl.cc b/mojo/services/html_viewer/webstoragenamespace_impl.cc
new file mode 100644
index 0000000..ea1f0eb
--- /dev/null
+++ b/mojo/services/html_viewer/webstoragenamespace_impl.cc
@@ -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.
+
+#include "mojo/services/html_viewer/webstoragenamespace_impl.h"
+
+#include <stdio.h>
+
+#include "third_party/WebKit/public/platform/WebStorageArea.h"
+
+namespace mojo {
+namespace {
+
+class DummyWebStorageAreaImpl : public blink::WebStorageArea {
+ public:
+ virtual unsigned length() {
+ return 0;
+ }
+ virtual blink::WebString key(unsigned index) {
+ return blink::WebString();
+ }
+ virtual blink::WebString getItem(const blink::WebString& key) {
+ return blink::WebString();
+ }
+};
+
+} // namespace
+
+WebStorageNamespaceImpl::WebStorageNamespaceImpl() {
+}
+
+WebStorageNamespaceImpl::~WebStorageNamespaceImpl() {
+}
+
+blink::WebStorageArea* WebStorageNamespaceImpl::createStorageArea(
+ const blink::WebString& origin) {
+ return new DummyWebStorageAreaImpl();
+}
+
+bool WebStorageNamespaceImpl::isSameNamespace(
+ const blink::WebStorageNamespace&) const {
+ return false;
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webstoragenamespace_impl.h b/mojo/services/html_viewer/webstoragenamespace_impl.h
new file mode 100644
index 0000000..7e11069
--- /dev/null
+++ b/mojo/services/html_viewer/webstoragenamespace_impl.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_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_
+
+#include "base/macros.h"
+#include "third_party/WebKit/public/platform/WebStorageNamespace.h"
+
+namespace mojo {
+
+class WebStorageNamespaceImpl : public blink::WebStorageNamespace {
+ public:
+ WebStorageNamespaceImpl();
+
+ private:
+ virtual ~WebStorageNamespaceImpl();
+
+ // blink::WebStorageNamespace methods:
+ virtual blink::WebStorageArea* createStorageArea(
+ const blink::WebString& origin);
+ virtual bool isSameNamespace(const blink::WebStorageNamespace&) const;
+
+ DISALLOW_COPY_AND_ASSIGN(WebStorageNamespaceImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_
diff --git a/mojo/services/html_viewer/webthemeengine_impl.cc b/mojo/services/html_viewer/webthemeengine_impl.cc
new file mode 100644
index 0000000..24906e5
--- /dev/null
+++ b/mojo/services/html_viewer/webthemeengine_impl.cc
@@ -0,0 +1,202 @@
+// 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/html_viewer/webthemeengine_impl.h"
+
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
+#include "third_party/WebKit/public/platform/WebSize.h"
+#include "ui/native_theme/native_theme.h"
+
+using blink::WebCanvas;
+using blink::WebColor;
+using blink::WebRect;
+using blink::WebThemeEngine;
+
+namespace mojo {
+
+static ui::NativeTheme::Part NativeThemePart(
+ WebThemeEngine::Part part) {
+ switch (part) {
+ case WebThemeEngine::PartScrollbarDownArrow:
+ return ui::NativeTheme::kScrollbarDownArrow;
+ case WebThemeEngine::PartScrollbarLeftArrow:
+ return ui::NativeTheme::kScrollbarLeftArrow;
+ case WebThemeEngine::PartScrollbarRightArrow:
+ return ui::NativeTheme::kScrollbarRightArrow;
+ case WebThemeEngine::PartScrollbarUpArrow:
+ return ui::NativeTheme::kScrollbarUpArrow;
+ case WebThemeEngine::PartScrollbarHorizontalThumb:
+ return ui::NativeTheme::kScrollbarHorizontalThumb;
+ case WebThemeEngine::PartScrollbarVerticalThumb:
+ return ui::NativeTheme::kScrollbarVerticalThumb;
+ case WebThemeEngine::PartScrollbarHorizontalTrack:
+ return ui::NativeTheme::kScrollbarHorizontalTrack;
+ case WebThemeEngine::PartScrollbarVerticalTrack:
+ return ui::NativeTheme::kScrollbarVerticalTrack;
+ case WebThemeEngine::PartScrollbarCorner:
+ return ui::NativeTheme::kScrollbarCorner;
+ case WebThemeEngine::PartCheckbox:
+ return ui::NativeTheme::kCheckbox;
+ case WebThemeEngine::PartRadio:
+ return ui::NativeTheme::kRadio;
+ case WebThemeEngine::PartButton:
+ return ui::NativeTheme::kPushButton;
+ case WebThemeEngine::PartTextField:
+ return ui::NativeTheme::kTextField;
+ case WebThemeEngine::PartMenuList:
+ return ui::NativeTheme::kMenuList;
+ case WebThemeEngine::PartSliderTrack:
+ return ui::NativeTheme::kSliderTrack;
+ case WebThemeEngine::PartSliderThumb:
+ return ui::NativeTheme::kSliderThumb;
+ case WebThemeEngine::PartInnerSpinButton:
+ return ui::NativeTheme::kInnerSpinButton;
+ case WebThemeEngine::PartProgressBar:
+ return ui::NativeTheme::kProgressBar;
+ default:
+ return ui::NativeTheme::kScrollbarDownArrow;
+ }
+}
+
+static ui::NativeTheme::State NativeThemeState(
+ WebThemeEngine::State state) {
+ switch (state) {
+ case WebThemeEngine::StateDisabled:
+ return ui::NativeTheme::kDisabled;
+ case WebThemeEngine::StateHover:
+ return ui::NativeTheme::kHovered;
+ case WebThemeEngine::StateNormal:
+ return ui::NativeTheme::kNormal;
+ case WebThemeEngine::StatePressed:
+ return ui::NativeTheme::kPressed;
+ default:
+ return ui::NativeTheme::kDisabled;
+ }
+}
+
+static void GetNativeThemeExtraParams(
+ WebThemeEngine::Part part,
+ WebThemeEngine::State state,
+ const WebThemeEngine::ExtraParams* extra_params,
+ ui::NativeTheme::ExtraParams* native_theme_extra_params) {
+ switch (part) {
+ case WebThemeEngine::PartScrollbarHorizontalTrack:
+ case WebThemeEngine::PartScrollbarVerticalTrack:
+ native_theme_extra_params->scrollbar_track.track_x =
+ extra_params->scrollbarTrack.trackX;
+ native_theme_extra_params->scrollbar_track.track_y =
+ extra_params->scrollbarTrack.trackY;
+ native_theme_extra_params->scrollbar_track.track_width =
+ extra_params->scrollbarTrack.trackWidth;
+ native_theme_extra_params->scrollbar_track.track_height =
+ extra_params->scrollbarTrack.trackHeight;
+ break;
+ case WebThemeEngine::PartCheckbox:
+ native_theme_extra_params->button.checked = extra_params->button.checked;
+ native_theme_extra_params->button.indeterminate =
+ extra_params->button.indeterminate;
+ break;
+ case WebThemeEngine::PartRadio:
+ native_theme_extra_params->button.checked = extra_params->button.checked;
+ break;
+ case WebThemeEngine::PartButton:
+ native_theme_extra_params->button.is_default =
+ extra_params->button.isDefault;
+ native_theme_extra_params->button.has_border =
+ extra_params->button.hasBorder;
+ // Native buttons have a different focus style.
+ native_theme_extra_params->button.is_focused = false;
+ native_theme_extra_params->button.background_color =
+ extra_params->button.backgroundColor;
+ break;
+ case WebThemeEngine::PartTextField:
+ native_theme_extra_params->text_field.is_text_area =
+ extra_params->textField.isTextArea;
+ native_theme_extra_params->text_field.is_listbox =
+ extra_params->textField.isListbox;
+ native_theme_extra_params->text_field.background_color =
+ extra_params->textField.backgroundColor;
+ break;
+ case WebThemeEngine::PartMenuList:
+ native_theme_extra_params->menu_list.has_border =
+ extra_params->menuList.hasBorder;
+ native_theme_extra_params->menu_list.has_border_radius =
+ extra_params->menuList.hasBorderRadius;
+ native_theme_extra_params->menu_list.arrow_x =
+ extra_params->menuList.arrowX;
+ native_theme_extra_params->menu_list.arrow_y =
+ extra_params->menuList.arrowY;
+ native_theme_extra_params->menu_list.background_color =
+ extra_params->menuList.backgroundColor;
+ break;
+ case WebThemeEngine::PartSliderTrack:
+ case WebThemeEngine::PartSliderThumb:
+ native_theme_extra_params->slider.vertical =
+ extra_params->slider.vertical;
+ native_theme_extra_params->slider.in_drag = extra_params->slider.inDrag;
+ break;
+ case WebThemeEngine::PartInnerSpinButton:
+ native_theme_extra_params->inner_spin.spin_up =
+ extra_params->innerSpin.spinUp;
+ native_theme_extra_params->inner_spin.read_only =
+ extra_params->innerSpin.readOnly;
+ break;
+ case WebThemeEngine::PartProgressBar:
+ native_theme_extra_params->progress_bar.determinate =
+ extra_params->progressBar.determinate;
+ native_theme_extra_params->progress_bar.value_rect_x =
+ extra_params->progressBar.valueRectX;
+ native_theme_extra_params->progress_bar.value_rect_y =
+ extra_params->progressBar.valueRectY;
+ native_theme_extra_params->progress_bar.value_rect_width =
+ extra_params->progressBar.valueRectWidth;
+ native_theme_extra_params->progress_bar.value_rect_height =
+ extra_params->progressBar.valueRectHeight;
+ break;
+ default:
+ break; // Parts that have no extra params get here.
+ }
+}
+
+blink::WebSize WebThemeEngineImpl::getSize(WebThemeEngine::Part part) {
+ ui::NativeTheme::ExtraParams extra;
+ return ui::NativeTheme::instance()->GetPartSize(NativeThemePart(part),
+ ui::NativeTheme::kNormal,
+ extra);
+}
+
+void WebThemeEngineImpl::paint(
+ blink::WebCanvas* canvas,
+ WebThemeEngine::Part part,
+ WebThemeEngine::State state,
+ const blink::WebRect& rect,
+ const WebThemeEngine::ExtraParams* extra_params) {
+ ui::NativeTheme::ExtraParams native_theme_extra_params;
+ GetNativeThemeExtraParams(
+ part, state, extra_params, &native_theme_extra_params);
+ ui::NativeTheme::instance()->Paint(
+ canvas,
+ NativeThemePart(part),
+ NativeThemeState(state),
+ gfx::Rect(rect),
+ native_theme_extra_params);
+}
+
+void WebThemeEngineImpl::paintStateTransition(blink::WebCanvas* canvas,
+ WebThemeEngine::Part part,
+ WebThemeEngine::State startState,
+ WebThemeEngine::State endState,
+ double progress,
+ const blink::WebRect& rect) {
+ ui::NativeTheme::instance()->PaintStateTransition(
+ canvas,
+ NativeThemePart(part),
+ NativeThemeState(startState),
+ NativeThemeState(endState),
+ progress,
+ gfx::Rect(rect));
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webthemeengine_impl.h b/mojo/services/html_viewer/webthemeengine_impl.h
new file mode 100644
index 0000000..a3eda9e
--- /dev/null
+++ b/mojo/services/html_viewer/webthemeengine_impl.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_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_
+
+#include "third_party/WebKit/public/platform/WebThemeEngine.h"
+
+namespace mojo {
+
+class WebThemeEngineImpl : public blink::WebThemeEngine {
+ public:
+ // WebThemeEngine methods:
+ virtual blink::WebSize getSize(blink::WebThemeEngine::Part);
+ virtual void paint(
+ blink::WebCanvas* canvas,
+ blink::WebThemeEngine::Part part,
+ blink::WebThemeEngine::State state,
+ const blink::WebRect& rect,
+ const blink::WebThemeEngine::ExtraParams* extra_params);
+ virtual void paintStateTransition(blink::WebCanvas* canvas,
+ blink::WebThemeEngine::Part part,
+ blink::WebThemeEngine::State startState,
+ blink::WebThemeEngine::State endState,
+ double progress,
+ const blink::WebRect& rect);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_
diff --git a/mojo/services/html_viewer/webthread_impl.cc b/mojo/services/html_viewer/webthread_impl.cc
new file mode 100644
index 0000000..678d22e
--- /dev/null
+++ b/mojo/services/html_viewer/webthread_impl.cc
@@ -0,0 +1,139 @@
+// 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.
+
+// An implementation of WebThread in terms of base::MessageLoop and
+// base::Thread
+
+#include "mojo/services/html_viewer/webthread_impl.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/threading/platform_thread.h"
+
+namespace mojo {
+
+WebThreadBase::WebThreadBase() {}
+WebThreadBase::~WebThreadBase() {}
+
+class WebThreadBase::TaskObserverAdapter
+ : public base::MessageLoop::TaskObserver {
+ public:
+ TaskObserverAdapter(WebThread::TaskObserver* observer)
+ : observer_(observer) {}
+
+ virtual void WillProcessTask(const base::PendingTask& pending_task) override {
+ observer_->willProcessTask();
+ }
+
+ virtual void DidProcessTask(const base::PendingTask& pending_task) override {
+ observer_->didProcessTask();
+ }
+
+private:
+ WebThread::TaskObserver* observer_;
+};
+
+void WebThreadBase::addTaskObserver(TaskObserver* observer) {
+ CHECK(isCurrentThread());
+ std::pair<TaskObserverMap::iterator, bool> result = task_observer_map_.insert(
+ std::make_pair(observer, static_cast<TaskObserverAdapter*>(NULL)));
+ if (result.second)
+ result.first->second = new TaskObserverAdapter(observer);
+ base::MessageLoop::current()->AddTaskObserver(result.first->second);
+}
+
+void WebThreadBase::removeTaskObserver(TaskObserver* observer) {
+ CHECK(isCurrentThread());
+ TaskObserverMap::iterator iter = task_observer_map_.find(observer);
+ if (iter == task_observer_map_.end())
+ return;
+ base::MessageLoop::current()->RemoveTaskObserver(iter->second);
+ delete iter->second;
+ task_observer_map_.erase(iter);
+}
+
+WebThreadImpl::WebThreadImpl(const char* name)
+ : thread_(new base::Thread(name)) {
+ thread_->Start();
+}
+
+void WebThreadImpl::postTask(Task* task) {
+ thread_->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&blink::WebThread::Task::run, base::Owned(task)));
+}
+
+void WebThreadImpl::postDelayedTask(Task* task, long long delay_ms) {
+ thread_->message_loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&blink::WebThread::Task::run, base::Owned(task)),
+ base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void WebThreadImpl::enterRunLoop() {
+ CHECK(isCurrentThread());
+ CHECK(!thread_->message_loop()->is_running()); // We don't support nesting.
+ thread_->message_loop()->Run();
+}
+
+void WebThreadImpl::exitRunLoop() {
+ CHECK(isCurrentThread());
+ CHECK(thread_->message_loop()->is_running());
+ thread_->message_loop()->Quit();
+}
+
+bool WebThreadImpl::isCurrentThread() const {
+ return thread_->thread_id() == base::PlatformThread::CurrentId();
+}
+
+blink::PlatformThreadId WebThreadImpl::threadId() const {
+ return thread_->thread_id();
+}
+
+WebThreadImpl::~WebThreadImpl() {
+ thread_->Stop();
+}
+
+WebThreadImplForMessageLoop::WebThreadImplForMessageLoop(
+ base::MessageLoopProxy* message_loop)
+ : message_loop_(message_loop) {}
+
+void WebThreadImplForMessageLoop::postTask(Task* task) {
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&blink::WebThread::Task::run, base::Owned(task)));
+}
+
+void WebThreadImplForMessageLoop::postDelayedTask(Task* task,
+ long long delay_ms) {
+ message_loop_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&blink::WebThread::Task::run, base::Owned(task)),
+ base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void WebThreadImplForMessageLoop::enterRunLoop() {
+ CHECK(isCurrentThread());
+ // We don't support nesting.
+ CHECK(!base::MessageLoop::current()->is_running());
+ base::MessageLoop::current()->Run();
+}
+
+void WebThreadImplForMessageLoop::exitRunLoop() {
+ CHECK(isCurrentThread());
+ CHECK(base::MessageLoop::current()->is_running());
+ base::MessageLoop::current()->Quit();
+}
+
+bool WebThreadImplForMessageLoop::isCurrentThread() const {
+ return message_loop_->BelongsToCurrentThread();
+}
+
+blink::PlatformThreadId WebThreadImplForMessageLoop::threadId() const {
+ return thread_id_;
+}
+
+WebThreadImplForMessageLoop::~WebThreadImplForMessageLoop() {}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/webthread_impl.h b/mojo/services/html_viewer/webthread_impl.h
new file mode 100644
index 0000000..93d7aeb
--- /dev/null
+++ b/mojo/services/html_viewer/webthread_impl.h
@@ -0,0 +1,78 @@
+// 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_HTML_VIEWER_WEBTHREAD_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "third_party/WebKit/public/platform/WebThread.h"
+
+namespace mojo {
+
+class WebThreadBase : public blink::WebThread {
+ public:
+ virtual ~WebThreadBase();
+
+ virtual void addTaskObserver(TaskObserver* observer);
+ virtual void removeTaskObserver(TaskObserver* observer);
+
+ virtual bool isCurrentThread() const = 0;
+ virtual blink::PlatformThreadId threadId() const = 0;
+
+ protected:
+ WebThreadBase();
+
+ private:
+ class TaskObserverAdapter;
+
+ typedef std::map<TaskObserver*, TaskObserverAdapter*> TaskObserverMap;
+ TaskObserverMap task_observer_map_;
+};
+
+class WebThreadImpl : public WebThreadBase {
+ public:
+ explicit WebThreadImpl(const char* name);
+ virtual ~WebThreadImpl();
+
+ virtual void postTask(Task* task);
+ virtual void postDelayedTask(Task* task, long long delay_ms);
+
+ virtual void enterRunLoop();
+ virtual void exitRunLoop();
+
+ base::MessageLoop* message_loop() const { return thread_->message_loop(); }
+
+ virtual bool isCurrentThread() const;
+ virtual blink::PlatformThreadId threadId() const;
+
+ private:
+ scoped_ptr<base::Thread> thread_;
+};
+
+class WebThreadImplForMessageLoop : public WebThreadBase {
+ public:
+ explicit WebThreadImplForMessageLoop(
+ base::MessageLoopProxy* message_loop);
+ virtual ~WebThreadImplForMessageLoop();
+
+ virtual void postTask(Task* task);
+ virtual void postDelayedTask(Task* task, long long delay_ms);
+
+ virtual void enterRunLoop();
+ virtual void exitRunLoop();
+
+ private:
+ virtual bool isCurrentThread() const;
+ virtual blink::PlatformThreadId threadId() const;
+
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ blink::PlatformThreadId thread_id_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_
diff --git a/mojo/services/html_viewer/weburlloader_impl.cc b/mojo/services/html_viewer/weburlloader_impl.cc
new file mode 100644
index 0000000..fb82bcf
--- /dev/null
+++ b/mojo/services/html_viewer/weburlloader_impl.cc
@@ -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.
+
+#include "mojo/services/html_viewer/weburlloader_impl.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/public/platform/WebURLError.h"
+#include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
+#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+
+namespace mojo {
+namespace {
+
+static blink::WebURLResponse::HTTPVersion StatusLineToHTTPVersion(
+ const String& status_line) {
+ if (status_line.is_null())
+ return blink::WebURLResponse::HTTP_0_9;
+
+ if (StartsWithASCII(status_line, "HTTP/1.0", true))
+ return blink::WebURLResponse::HTTP_1_0;
+
+ if (StartsWithASCII(status_line, "HTTP/1.1", true))
+ return blink::WebURLResponse::HTTP_1_1;
+
+ return blink::WebURLResponse::Unknown;
+}
+
+blink::WebURLResponse ToWebURLResponse(const URLResponsePtr& url_response) {
+ blink::WebURLResponse result;
+ result.initialize();
+ result.setURL(GURL(url_response->url));
+ result.setMIMEType(blink::WebString::fromUTF8(url_response->mime_type));
+ result.setTextEncodingName(blink::WebString::fromUTF8(url_response->charset));
+ result.setHTTPVersion(StatusLineToHTTPVersion(url_response->status_line));
+ result.setHTTPStatusCode(url_response->status_code);
+
+ // TODO(darin): Initialize timing properly.
+ blink::WebURLLoadTiming timing;
+ timing.initialize();
+ result.setLoadTiming(timing);
+
+ for (size_t i = 0; i < url_response->headers.size(); ++i) {
+ const std::string& header_line = url_response->headers[i];
+ size_t first_colon = header_line.find(":");
+
+ if (first_colon == std::string::npos || first_colon == 0)
+ continue;
+
+ std::string value;
+ TrimWhitespaceASCII(header_line.substr(first_colon + 1),
+ base::TRIM_LEADING,
+ &value);
+ result.setHTTPHeaderField(
+ blink::WebString::fromUTF8(header_line.substr(0, first_colon)),
+ blink::WebString::fromUTF8(value));
+ }
+
+ return result;
+}
+
+} // namespace
+
+WebURLRequestExtraData::WebURLRequestExtraData() {
+}
+
+WebURLRequestExtraData::~WebURLRequestExtraData() {
+}
+
+WebURLLoaderImpl::WebURLLoaderImpl(NetworkService* network_service)
+ : client_(NULL),
+ weak_factory_(this) {
+ network_service->CreateURLLoader(Get(&url_loader_));
+}
+
+WebURLLoaderImpl::~WebURLLoaderImpl() {
+}
+
+void WebURLLoaderImpl::loadSynchronously(
+ const blink::WebURLRequest& request,
+ blink::WebURLResponse& response,
+ blink::WebURLError& error,
+ blink::WebData& data) {
+ NOTIMPLEMENTED();
+}
+
+void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request,
+ blink::WebURLLoaderClient* client) {
+ client_ = client;
+ url_ = request.url();
+
+ URLRequestPtr url_request = URLRequest::From(request);
+ url_request->auto_follow_redirects = false;
+
+ if (request.extraData()) {
+ WebURLRequestExtraData* extra_data =
+ static_cast<WebURLRequestExtraData*>(request.extraData());
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+ weak_factory_.GetWeakPtr(),
+ base::Passed(&extra_data->synthetic_response)));
+ } else {
+ url_loader_->Start(url_request.Pass(),
+ base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void WebURLLoaderImpl::cancel() {
+ url_loader_.reset();
+ response_body_stream_.reset();
+
+ URLResponsePtr failed_response(URLResponse::New());
+ failed_response->url = String::From(url_);
+ failed_response->error = NetworkError::New();
+ failed_response->error->code = net::ERR_ABORTED;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+ weak_factory_.GetWeakPtr(),
+ base::Passed(&failed_response)));
+}
+
+void WebURLLoaderImpl::setDefersLoading(bool defers_loading) {
+ NOTIMPLEMENTED();
+}
+
+void WebURLLoaderImpl::OnReceivedResponse(URLResponsePtr url_response) {
+ url_ = GURL(url_response->url);
+
+ if (url_response->error) {
+ OnReceivedError(url_response.Pass());
+ } else if (url_response->redirect_url) {
+ OnReceivedRedirect(url_response.Pass());
+ } else {
+ base::WeakPtr<WebURLLoaderImpl> self(weak_factory_.GetWeakPtr());
+ client_->didReceiveResponse(this, ToWebURLResponse(url_response));
+
+ // We may have been deleted during didReceiveResponse.
+ if (!self)
+ return;
+
+ // Start streaming data
+ response_body_stream_ = url_response->body.Pass();
+ ReadMore();
+ }
+}
+
+void WebURLLoaderImpl::OnReceivedError(URLResponsePtr url_response) {
+ blink::WebURLError web_error;
+ web_error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
+ web_error.reason = url_response->error->code;
+ web_error.unreachableURL = GURL(url_response->url);
+ web_error.staleCopyInCache = false;
+ web_error.isCancellation =
+ url_response->error->code == net::ERR_ABORTED ? true : false;
+
+ client_->didFail(this, web_error);
+}
+
+void WebURLLoaderImpl::OnReceivedRedirect(URLResponsePtr url_response) {
+ blink::WebURLRequest new_request;
+ new_request.initialize();
+ new_request.setURL(GURL(url_response->redirect_url));
+ new_request.setHTTPMethod(
+ blink::WebString::fromUTF8(url_response->redirect_method));
+
+ client_->willSendRequest(this, new_request, ToWebURLResponse(url_response));
+ // TODO(darin): Check if new_request was rejected.
+
+ url_loader_->FollowRedirect(
+ base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
+ weak_factory_.GetWeakPtr()));
+}
+
+void WebURLLoaderImpl::ReadMore() {
+ const void* buf;
+ uint32_t buf_size;
+ MojoResult rv = BeginReadDataRaw(response_body_stream_.get(),
+ &buf,
+ &buf_size,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (rv == MOJO_RESULT_OK) {
+ client_->didReceiveData(this, static_cast<const char*>(buf), buf_size, -1);
+ EndReadDataRaw(response_body_stream_.get(), buf_size);
+ WaitToReadMore();
+ } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ WaitToReadMore();
+ } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ // We reached end-of-file.
+ double finish_time = base::Time::Now().ToDoubleT();
+ client_->didFinishLoading(
+ this,
+ finish_time,
+ blink::WebURLLoaderClient::kUnknownEncodedDataLength);
+ } else {
+ // TODO(darin): Oops!
+ }
+}
+
+void WebURLLoaderImpl::WaitToReadMore() {
+ handle_watcher_.Start(
+ response_body_stream_.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&WebURLLoaderImpl::OnResponseBodyStreamReady,
+ weak_factory_.GetWeakPtr()));
+}
+
+void WebURLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
+ ReadMore();
+}
+
+} // namespace mojo
diff --git a/mojo/services/html_viewer/weburlloader_impl.h b/mojo/services/html_viewer/weburlloader_impl.h
new file mode 100644
index 0000000..6bb30df
--- /dev/null
+++ b/mojo/services/html_viewer/weburlloader_impl.h
@@ -0,0 +1,63 @@
+// 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_HTML_VIEWER_WEBURLLOADER_IMPL_H_
+#define MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "third_party/WebKit/public/platform/WebURLLoader.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+
+namespace mojo {
+class NetworkService;
+
+// The concrete type of WebURLRequest::ExtraData.
+class WebURLRequestExtraData : public blink::WebURLRequest::ExtraData {
+ public:
+ WebURLRequestExtraData();
+ virtual ~WebURLRequestExtraData();
+
+ URLResponsePtr synthetic_response;
+};
+
+class WebURLLoaderImpl : public blink::WebURLLoader {
+ public:
+ explicit WebURLLoaderImpl(NetworkService* network_service);
+
+ private:
+ virtual ~WebURLLoaderImpl();
+
+ // blink::WebURLLoader methods:
+ virtual void loadSynchronously(
+ const blink::WebURLRequest& request, blink::WebURLResponse& response,
+ blink::WebURLError& error, blink::WebData& data) override;
+ virtual void loadAsynchronously(
+ const blink::WebURLRequest&, blink::WebURLLoaderClient* client) override;
+ virtual void cancel() override;
+ virtual void setDefersLoading(bool defers_loading) override;
+
+ void OnReceivedResponse(URLResponsePtr response);
+ void OnReceivedError(URLResponsePtr response);
+ void OnReceivedRedirect(URLResponsePtr response);
+ void ReadMore();
+ void WaitToReadMore();
+ void OnResponseBodyStreamReady(MojoResult result);
+
+ blink::WebURLLoaderClient* client_;
+ GURL url_;
+ URLLoaderPtr url_loader_;
+ ScopedDataPipeConsumerHandle response_body_stream_;
+ common::HandleWatcher handle_watcher_;
+
+ base::WeakPtrFactory<WebURLLoaderImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebURLLoaderImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_
diff --git a/mojo/services/native_viewport/BUILD.gn b/mojo/services/native_viewport/BUILD.gn
new file mode 100644
index 0000000..036e8e9
--- /dev/null
+++ b/mojo/services/native_viewport/BUILD.gn
@@ -0,0 +1,84 @@
+# 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("//build/config/ui.gni")
+
+if (!is_android) {
+ shared_library("native_viewport") {
+ output_name = "mojo_native_viewport_service"
+
+ deps = [
+ ":lib",
+ "//base",
+ "//mojo/application",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/bindings:bindings",
+ "//mojo/services/public/interfaces/native_viewport",
+ "//ui/gl",
+ ]
+
+ sources = [ "main.cc" ]
+ }
+}
+
+source_set("lib") {
+ deps = [
+ "//base",
+ "//cc/surfaces",
+ "//gpu/command_buffer/service",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/services/gles2",
+ "//mojo/services/gles2:interfaces",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/services/public/cpp/input_events",
+ "//mojo/services/public/cpp/surfaces",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/native_viewport",
+ "//mojo/services/public/interfaces/surfaces",
+ "//ui/events",
+ "//ui/events/platform",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/gl",
+ "//ui/platform_window",
+ ]
+
+ sources = [
+ "gpu_impl.cc",
+ "gpu_impl.h",
+ "native_viewport_impl.cc",
+ "native_viewport_impl.h",
+ "platform_viewport.h",
+ "platform_viewport_android.cc",
+ "platform_viewport_android.h",
+ "platform_viewport_mac.mm",
+ "platform_viewport_headless.cc",
+ "platform_viewport_headless.h",
+ "platform_viewport_win.cc",
+ "viewport_surface.cc",
+ "viewport_surface.h",
+ ]
+
+ if (is_ios) {
+ sources += [ "platform_viewport_stub.cc" ]
+ }
+
+ if (is_android) {
+ deps += [ "//mojo:jni_headers" ]
+ }
+
+ if (use_x11) {
+ sources += [ "platform_viewport_x11.cc" ]
+ deps += [
+ "//ui/events/platform/x11",
+ "//ui/platform_window/x11",
+ ]
+ }
+
+ if (use_ozone) {
+ sources += [ "platform_viewport_ozone.cc" ]
+ }
+}
diff --git a/mojo/services/native_viewport/DEPS b/mojo/services/native_viewport/DEPS
new file mode 100644
index 0000000..abf7ef8
--- /dev/null
+++ b/mojo/services/native_viewport/DEPS
@@ -0,0 +1,18 @@
+include_rules = [
+ "+cc/surfaces",
+ "+gpu/command_buffer/service/mailbox_manager.h",
+ "+mojo/application",
+ "+mojo/services/public/cpp/geometry",
+ "+mojo/services/public/cpp/input_events",
+ "+mojo/services/public/cpp/surfaces",
+ "+mojo/services/public/interfaces/gpu",
+ "+mojo/services/public/interfaces/native_viewport",
+ "+mojo/services/public/interfaces/geometry",
+ "+mojo/services/public/interfaces/surfaces",
+ "+mojo/services/gles2",
+ "+ui/events",
+ "+ui/gfx",
+ "+ui/gl",
+ "+ui/ozone/public",
+ "+ui/platform_window",
+]
diff --git a/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java b/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
new file mode 100644
index 0000000..c3f8075
--- /dev/null
+++ b/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java
@@ -0,0 +1,91 @@
+// 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.
+
+package org.chromium.mojo;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * Exposes SurfaceView to native code.
+ */
+@JNINamespace("mojo")
+public class PlatformViewportAndroid extends SurfaceView {
+
+ private long mNativeMojoViewport;
+ private final SurfaceHolder.Callback mSurfaceCallback;
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ public static void createForActivity(Activity activity, long nativeViewport) {
+ activity.setContentView(new PlatformViewportAndroid(activity, nativeViewport));
+ }
+
+ public PlatformViewportAndroid(Context context, long nativeViewport) {
+ super(context);
+
+ mNativeMojoViewport = nativeViewport;
+ assert mNativeMojoViewport != 0;
+
+ mSurfaceCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ assert mNativeMojoViewport != 0;
+ nativeSurfaceSetSize(mNativeMojoViewport, width, height);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ assert mNativeMojoViewport != 0;
+ nativeSurfaceCreated(mNativeMojoViewport, holder.getSurface());
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ assert mNativeMojoViewport != 0;
+ nativeSurfaceDestroyed(mNativeMojoViewport);
+ }
+ };
+ getHolder().addCallback(mSurfaceCallback);
+
+ }
+
+ // TODO(abarth): Someone needs to call destroy at some point.
+ public void destroy() {
+ getHolder().removeCallback(mSurfaceCallback);
+ nativeDestroy(mNativeMojoViewport);
+ mNativeMojoViewport = 0;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return nativeTouchEvent(mNativeMojoViewport,
+ event.getPointerId(0),
+ event.getAction(),
+ event.getX(), event.getY(),
+ event.getEventTime());
+ }
+
+ private static native void nativeDestroy(long nativePlatformViewportAndroid);
+ private static native void nativeSurfaceCreated(
+ long nativePlatformViewportAndroid, Surface surface);
+ private static native void nativeSurfaceDestroyed(
+ long nativePlatformViewportAndroid);
+ private static native void nativeSurfaceSetSize(
+ long nativePlatformViewportAndroid,
+ int width, int height);
+ private static native boolean nativeTouchEvent(
+ long nativePlatformViewportAndroid,
+ int pointerId,
+ int action,
+ float x, float y,
+ long timeMs);
+};
diff --git a/mojo/services/native_viewport/gpu_impl.cc b/mojo/services/native_viewport/gpu_impl.cc
new file mode 100644
index 0000000..de0b69e
--- /dev/null
+++ b/mojo/services/native_viewport/gpu_impl.cc
@@ -0,0 +1,43 @@
+// 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/native_viewport/gpu_impl.h"
+
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "mojo/services/gles2/command_buffer_impl.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "ui/gl/gl_share_group.h"
+
+namespace mojo {
+
+GpuImpl::GpuImpl(
+ const scoped_refptr<gfx::GLShareGroup>& share_group,
+ const scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager)
+ : share_group_(share_group), mailbox_manager_(mailbox_manager) {
+}
+
+GpuImpl::~GpuImpl() {
+}
+
+void GpuImpl::CreateOnscreenGLES2Context(
+ uint64_t native_viewport_id,
+ SizePtr size,
+ InterfaceRequest<CommandBuffer> command_buffer_request) {
+ gfx::AcceleratedWidget widget = bit_cast<gfx::AcceleratedWidget>(
+ static_cast<uintptr_t>(native_viewport_id));
+ BindToRequest(new CommandBufferImpl(widget,
+ size.To<gfx::Size>(),
+ share_group_.get(),
+ mailbox_manager_.get()),
+ &command_buffer_request);
+}
+
+void GpuImpl::CreateOffscreenGLES2Context(
+ InterfaceRequest<CommandBuffer> command_buffer_request) {
+ BindToRequest(
+ new CommandBufferImpl(share_group_.get(), mailbox_manager_.get()),
+ &command_buffer_request);
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/gpu_impl.h b/mojo/services/native_viewport/gpu_impl.h
new file mode 100644
index 0000000..ac0f435
--- /dev/null
+++ b/mojo/services/native_viewport/gpu_impl.h
@@ -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.
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+#include "mojo/services/public/interfaces/geometry/geometry.mojom.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+
+namespace gfx {
+class GLShareGroup;
+}
+
+namespace gpu {
+namespace gles2 {
+class MailboxManager;
+}
+}
+
+namespace mojo {
+
+class GpuImpl : public InterfaceImpl<Gpu> {
+ public:
+ GpuImpl(const scoped_refptr<gfx::GLShareGroup>& share_group,
+ const scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager);
+
+ virtual ~GpuImpl();
+
+ virtual void CreateOnscreenGLES2Context(
+ uint64_t native_viewport_id,
+ SizePtr size,
+ InterfaceRequest<CommandBuffer> command_buffer_request) override;
+
+ virtual void CreateOffscreenGLES2Context(
+ InterfaceRequest<CommandBuffer> command_buffer_request) override;
+
+ private:
+ // We need to share these across all NativeViewport instances so that contexts
+ // they create can share resources with each other via mailboxes.
+ scoped_refptr<gfx::GLShareGroup> share_group_;
+ scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuImpl);
+};
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/main.cc b/mojo/services/native_viewport/main.cc
new file mode 100644
index 0000000..0185e67
--- /dev/null
+++ b/mojo/services/native_viewport/main.cc
@@ -0,0 +1,111 @@
+// 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/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/services/native_viewport/gpu_impl.h"
+#include "mojo/services/native_viewport/native_viewport_impl.h"
+#include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_surface.h"
+
+namespace mojo {
+
+class NativeViewportAppDelegate
+ : public ApplicationDelegate,
+ public InterfaceFactory<NativeViewport>,
+ public InterfaceFactory<Gpu>,
+ public InterfaceFactory<NativeViewportConfig> {
+ public:
+ NativeViewportAppDelegate()
+ : share_group_(new gfx::GLShareGroup),
+ mailbox_manager_(new gpu::gles2::MailboxManager),
+ is_test_(false),
+ is_headless_(false),
+ is_initialized_(false) {}
+ virtual ~NativeViewportAppDelegate() {}
+
+ private:
+ class NativeViewportConfigImpl : public InterfaceImpl<NativeViewportConfig> {
+ public:
+ NativeViewportConfigImpl(NativeViewportAppDelegate* app_delegate)
+ : app_delegate_(app_delegate) {}
+
+ virtual void UseTestConfig(
+ const Callback<void()>& callback) override {
+ app_delegate_->is_test_ = true;
+ callback.Run();
+ }
+
+ virtual void UseHeadlessConfig(
+ const Callback<void()>& callback) override {
+ app_delegate_->is_headless_ = true;
+ callback.Run();
+ }
+
+ private:
+ NativeViewportAppDelegate* app_delegate_;
+ };
+
+ // ApplicationDelegate implementation.
+ virtual void Initialize(ApplicationImpl* application) override {
+ app_ = application;
+ }
+
+ virtual bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override {
+ connection->AddService<NativeViewport>(this);
+ connection->AddService<Gpu>(this);
+ connection->AddService<NativeViewportConfig>(this);
+ return true;
+ }
+
+ // InterfaceFactory<NativeViewport> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<NativeViewport> request) override {
+#if !defined(COMPONENT_BUILD)
+ if (!is_initialized_) {
+ if (is_test_)
+ gfx::GLSurface::InitializeOneOffForTests();
+ else
+ gfx::GLSurface::InitializeOneOff();
+ is_initialized_ = true;
+ }
+#endif
+ BindToRequest(new NativeViewportImpl(app_, is_headless_), &request);
+ }
+
+ // InterfaceFactory<Gpu> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<Gpu> request) override {
+ BindToRequest(new GpuImpl(share_group_.get(), mailbox_manager_.get()),
+ &request);
+ }
+
+ // InterfaceFactory<NVTestConfig> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<NativeViewportConfig> request) override {
+ BindToRequest(new NativeViewportConfigImpl(this), &request);
+ }
+
+ ApplicationImpl* app_;
+ scoped_refptr<gfx::GLShareGroup> share_group_;
+ scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+ bool is_test_;
+ bool is_headless_;
+ bool is_initialized_;
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportAppDelegate);
+};
+}
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new mojo::NativeViewportAppDelegate);
+ runner.set_message_loop_type(base::MessageLoop::TYPE_UI);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/native_viewport/native_viewport_impl.cc b/mojo/services/native_viewport/native_viewport_impl.cc
new file mode 100644
index 0000000..3006612
--- /dev/null
+++ b/mojo/services/native_viewport/native_viewport_impl.cc
@@ -0,0 +1,160 @@
+// 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/native_viewport/native_viewport_impl.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/native_viewport/platform_viewport_headless.h"
+#include "mojo/services/native_viewport/viewport_surface.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/cpp/surfaces/surfaces_type_converters.h"
+#include "ui/events/event.h"
+
+namespace mojo {
+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;
+}
+
+} // namespace
+
+NativeViewportImpl::NativeViewportImpl(ApplicationImpl* app, bool is_headless)
+ : is_headless_(is_headless),
+ widget_id_(0u),
+ waiting_for_event_ack_(false),
+ weak_factory_(this) {
+ app->ConnectToService("mojo:mojo_surfaces_service", &surfaces_service_);
+ // TODO(jamesr): Should be mojo_gpu_service
+ app->ConnectToService("mojo:mojo_native_viewport_service", &gpu_service_);
+}
+
+NativeViewportImpl::~NativeViewportImpl() {
+ // Destroy the NativeViewport early on as it may call us back during
+ // destruction and we want to be in a known state.
+ platform_viewport_.reset();
+}
+
+void NativeViewportImpl::Create(SizePtr size,
+ const Callback<void(uint64_t)>& callback) {
+ create_callback_ = callback;
+ size_ = size.To<gfx::Size>();
+ if (is_headless_)
+ platform_viewport_ = PlatformViewportHeadless::Create(this);
+ else
+ platform_viewport_ = PlatformViewport::Create(this);
+ platform_viewport_->Init(gfx::Rect(size.To<gfx::Size>()));
+}
+
+void NativeViewportImpl::Show() {
+ platform_viewport_->Show();
+}
+
+void NativeViewportImpl::Hide() {
+ platform_viewport_->Hide();
+}
+
+void NativeViewportImpl::Close() {
+ DCHECK(platform_viewport_);
+ platform_viewport_->Close();
+}
+
+void NativeViewportImpl::SetSize(SizePtr size) {
+ platform_viewport_->SetBounds(gfx::Rect(size.To<gfx::Size>()));
+}
+
+void NativeViewportImpl::SubmittedFrame(SurfaceIdPtr child_surface_id) {
+ if (child_surface_id_.is_null()) {
+ // If this is the first indication that the client will use surfaces,
+ // initialize that system.
+ // TODO(jamesr): When everything is converted to surfaces initialize this
+ // eagerly.
+ viewport_surface_.reset(
+ new ViewportSurface(surfaces_service_.get(),
+ gpu_service_.get(),
+ size_,
+ child_surface_id.To<cc::SurfaceId>()));
+ if (widget_id_)
+ viewport_surface_->SetWidgetId(widget_id_);
+ }
+ child_surface_id_ = child_surface_id.To<cc::SurfaceId>();
+ if (viewport_surface_)
+ viewport_surface_->SetChildId(child_surface_id_);
+}
+
+void NativeViewportImpl::OnBoundsChanged(const gfx::Rect& bounds) {
+ if (size_ == bounds.size())
+ return;
+
+ size_ = bounds.size();
+
+ // Wait for the accelerated widget before telling the client of the bounds.
+ if (create_callback_.is_null())
+ ProcessOnBoundsChanged();
+}
+
+void NativeViewportImpl::OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) {
+ widget_id_ = static_cast<uint64_t>(bit_cast<uintptr_t>(widget));
+ // TODO(jamesr): Remove once everything is converted to surfaces.
+ create_callback_.Run(widget_id_);
+ create_callback_.reset();
+ // Immediately tell the client of the size. The size may be wrong, if so we'll
+ // get the right one in the next OnBoundsChanged() call.
+ ProcessOnBoundsChanged();
+ if (viewport_surface_)
+ viewport_surface_->SetWidgetId(widget_id_);
+}
+
+bool NativeViewportImpl::OnEvent(ui::Event* ui_event) {
+ // Must not return early before updating capture.
+ switch (ui_event->type()) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_TOUCH_PRESSED:
+ platform_viewport_->SetCapture();
+ break;
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_TOUCH_RELEASED:
+ platform_viewport_->ReleaseCapture();
+ break;
+ default:
+ break;
+ }
+
+ if (waiting_for_event_ack_ && IsRateLimitedEventType(ui_event))
+ return false;
+
+ client()->OnEvent(
+ Event::From(*ui_event),
+ base::Bind(&NativeViewportImpl::AckEvent, weak_factory_.GetWeakPtr()));
+ waiting_for_event_ack_ = true;
+ return false;
+}
+
+void NativeViewportImpl::OnDestroyed() {
+ client()->OnDestroyed();
+}
+
+void NativeViewportImpl::AckEvent() {
+ waiting_for_event_ack_ = false;
+}
+
+void NativeViewportImpl::ProcessOnBoundsChanged() {
+ client()->OnSizeChanged(Size::From(size_));
+ if (viewport_surface_)
+ viewport_surface_->SetSize(size_);
+}
+
+} // namespace mojo
+
diff --git a/mojo/services/native_viewport/native_viewport_impl.h b/mojo/services/native_viewport/native_viewport_impl.h
new file mode 100644
index 0000000..03918cb
--- /dev/null
+++ b/mojo/services/native_viewport/native_viewport_impl.h
@@ -0,0 +1,69 @@
+// 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_NATIVE_VIEWPORT_IMPL_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/native_viewport/platform_viewport.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+class ApplicationImpl;
+class ViewportSurface;
+
+class NativeViewportImpl : public InterfaceImpl<NativeViewport>,
+ public PlatformViewport::Delegate {
+ public:
+ NativeViewportImpl(ApplicationImpl* app, bool is_headless);
+ virtual ~NativeViewportImpl();
+
+ // InterfaceImpl<NativeViewport> implementation.
+ virtual void Create(SizePtr size,
+ const Callback<void(uint64_t)>& callback) override;
+ virtual void Show() override;
+ virtual void Hide() override;
+ virtual void Close() override;
+ virtual void SetSize(SizePtr size) override;
+ virtual void SubmittedFrame(SurfaceIdPtr surface_id) override;
+
+ // PlatformViewport::Delegate implementation.
+ virtual void OnBoundsChanged(const gfx::Rect& bounds) override;
+ virtual void OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) override;
+ virtual bool OnEvent(ui::Event* ui_event) override;
+ virtual void OnDestroyed() override;
+
+ void AckEvent();
+
+ private:
+ void ProcessOnBoundsChanged();
+
+ bool is_headless_;
+ scoped_ptr<PlatformViewport> platform_viewport_;
+ scoped_ptr<ViewportSurface> viewport_surface_;
+ uint64_t widget_id_;
+ gfx::Size size_;
+ GpuPtr gpu_service_;
+ SurfacesServicePtr surfaces_service_;
+ cc::SurfaceId child_surface_id_;
+ bool waiting_for_event_ack_;
+ Callback<void(uint64_t)> create_callback_;
+ base::WeakPtrFactory<NativeViewportImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_
diff --git a/mojo/services/native_viewport/platform_viewport.h b/mojo/services/native_viewport/platform_viewport.h
new file mode 100644
index 0000000..a3ba3dc
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport.h
@@ -0,0 +1,53 @@
+// 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_PLATFORM_VIEWPORT_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_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 {
+
+// Encapsulation of platform-specific Viewport.
+class PlatformViewport {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ virtual void OnBoundsChanged(const gfx::Rect& rect) = 0;
+ virtual void OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) = 0;
+ virtual bool OnEvent(ui::Event* ui_event) = 0;
+ virtual void OnDestroyed() = 0;
+ };
+
+ virtual ~PlatformViewport() {}
+
+ 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;
+
+ static scoped_ptr<PlatformViewport> Create(Delegate* delegate);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_
diff --git a/mojo/services/native_viewport/platform_viewport_android.cc b/mojo/services/native_viewport/platform_viewport_android.cc
new file mode 100644
index 0000000..bbe22c9
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_android.cc
@@ -0,0 +1,156 @@
+// 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/platform_viewport_android.h"
+
+#include <android/input.h>
+#include <android/native_window_jni.h>
+
+#include "base/android/jni_android.h"
+#include "jni/PlatformViewportAndroid_jni.h"
+#include "ui/events/event.h"
+#include "ui/gfx/point.h"
+
+namespace mojo {
+
+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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewportAndroid, public:
+
+// static
+bool PlatformViewportAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+PlatformViewportAndroid::PlatformViewportAndroid(Delegate* delegate)
+ : delegate_(delegate),
+ window_(NULL),
+ id_generator_(0),
+ weak_factory_(this) {
+}
+
+PlatformViewportAndroid::~PlatformViewportAndroid() {
+ if (window_)
+ ReleaseWindow();
+}
+
+void PlatformViewportAndroid::Destroy(JNIEnv* env, jobject obj) {
+ delegate_->OnDestroyed();
+}
+
+void PlatformViewportAndroid::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 PlatformViewportAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) {
+ DCHECK(window_);
+ ReleaseWindow();
+}
+
+void PlatformViewportAndroid::SurfaceSetSize(JNIEnv* env, jobject obj,
+ jint width, jint height) {
+ bounds_ = gfx::Rect(width, height);
+ delegate_->OnBoundsChanged(bounds_);
+}
+
+bool PlatformViewportAndroid::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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewportAndroid, PlatformViewport implementation:
+
+void PlatformViewportAndroid::Init(const gfx::Rect& bounds) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_PlatformViewportAndroid_createForActivity(
+ env,
+ base::android::GetApplicationContext(),
+ reinterpret_cast<jlong>(this));
+}
+
+void PlatformViewportAndroid::Show() {
+ // Nothing to do. View is created visible.
+}
+
+void PlatformViewportAndroid::Hide() {
+ // Nothing to do. View is always visible.
+}
+
+void PlatformViewportAndroid::Close() {
+ // TODO(beng): close activity containing MojoView?
+
+ // TODO(beng): perform this in response to view destruction.
+ delegate_->OnDestroyed();
+}
+
+gfx::Size PlatformViewportAndroid::GetSize() {
+ return bounds_.size();
+}
+
+void PlatformViewportAndroid::SetBounds(const gfx::Rect& bounds) {
+ NOTIMPLEMENTED();
+}
+
+void PlatformViewportAndroid::SetCapture() {
+ NOTIMPLEMENTED();
+}
+
+void PlatformViewportAndroid::ReleaseCapture() {
+ NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewportAndroid, private:
+
+void PlatformViewportAndroid::ReleaseWindow() {
+ ANativeWindow_release(window_);
+ window_ = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlatformViewport, public:
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+ return scoped_ptr<PlatformViewport>(
+ new PlatformViewportAndroid(delegate)).Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_android.h b/mojo/services/native_viewport/platform_viewport_android.h
new file mode 100644
index 0000000..31b0312
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_android.h
@@ -0,0 +1,66 @@
+// 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_PLATFORM_VIEWPORT_ANDROID_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/native_viewport/platform_viewport.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 {
+
+class PlatformViewportAndroid : public PlatformViewport {
+ public:
+ static bool Register(JNIEnv* env);
+
+ explicit PlatformViewportAndroid(Delegate* delegate);
+ virtual ~PlatformViewportAndroid();
+
+ 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 PlatformViewport:
+ 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();
+
+ Delegate* delegate_;
+ ANativeWindow* window_;
+ gfx::Rect bounds_;
+ ui::SequentialIDGenerator id_generator_;
+
+ base::WeakPtrFactory<PlatformViewportAndroid> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformViewportAndroid);
+};
+
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_
diff --git a/mojo/services/native_viewport/platform_viewport_headless.cc b/mojo/services/native_viewport/platform_viewport_headless.cc
new file mode 100644
index 0000000..1c34711
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_headless.cc
@@ -0,0 +1,52 @@
+// 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/platform_viewport_headless.h"
+
+namespace mojo {
+
+PlatformViewportHeadless::PlatformViewportHeadless(Delegate* delegate)
+ : delegate_(delegate) {
+}
+
+PlatformViewportHeadless::~PlatformViewportHeadless() {
+}
+
+void PlatformViewportHeadless::Init(const gfx::Rect& bounds) {
+ bounds_ = bounds;
+}
+
+void PlatformViewportHeadless::Show() {
+}
+
+void PlatformViewportHeadless::Hide() {
+}
+
+void PlatformViewportHeadless::Close() {
+ delegate_->OnDestroyed();
+}
+
+gfx::Size PlatformViewportHeadless::GetSize() {
+ return bounds_.size();
+}
+
+void PlatformViewportHeadless::SetBounds(const gfx::Rect& bounds) {
+ bounds_ = bounds;
+ delegate_->OnBoundsChanged(bounds_);
+}
+
+void PlatformViewportHeadless::SetCapture() {
+}
+
+void PlatformViewportHeadless::ReleaseCapture() {
+}
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewportHeadless::Create(
+ Delegate* delegate) {
+ return scoped_ptr<PlatformViewport>(
+ new PlatformViewportHeadless(delegate)).Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_headless.h b/mojo/services/native_viewport/platform_viewport_headless.h
new file mode 100644
index 0000000..9b9743f
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_headless.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "mojo/services/native_viewport/platform_viewport.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+
+class PlatformViewportHeadless : public PlatformViewport {
+ public:
+ virtual ~PlatformViewportHeadless();
+
+ static scoped_ptr<PlatformViewport> Create(Delegate* delegate);
+
+ private:
+ explicit PlatformViewportHeadless(Delegate* delegate);
+
+ // Overridden from PlatformViewport:
+ 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;
+
+ Delegate* delegate_;
+ gfx::Rect bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformViewportHeadless);
+};
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_mac.mm b/mojo/services/native_viewport/platform_viewport_mac.mm
new file mode 100644
index 0000000..9d11052
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_mac.mm
@@ -0,0 +1,84 @@
+// 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/platform_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 {
+
+class PlatformViewportMac : public PlatformViewport {
+ public:
+ PlatformViewportMac(Delegate* delegate)
+ : delegate_(delegate),
+ window_(nil) {
+ }
+
+ virtual ~PlatformViewportMac() {
+ [window_ orderOut:nil];
+ [window_ close];
+ }
+
+ private:
+ // Overridden from PlatformViewport:
+ 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();
+ }
+
+ Delegate* delegate_;
+ NSWindow* window_;
+ gfx::Rect rect_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformViewportMac);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+ return scoped_ptr<PlatformViewport>(new PlatformViewportMac(delegate)).Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_ozone.cc b/mojo/services/native_viewport/platform_viewport_ozone.cc
new file mode 100644
index 0000000..b8076fb
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_ozone.cc
@@ -0,0 +1,95 @@
+// 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/native_viewport/platform_viewport.h"
+
+#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace mojo {
+
+// TODO(spang): Deduplicate with PlatformViewportX11.. but there's a hack
+// in there that prevents this.
+class PlatformViewportOzone : public PlatformViewport,
+ public ui::PlatformWindowDelegate {
+ public:
+ explicit PlatformViewportOzone(Delegate* delegate) : delegate_(delegate) {
+ ui::OzonePlatform::InitializeForUI();
+ }
+
+ virtual ~PlatformViewportOzone() {
+ // Destroy the platform-window while |this| is still alive.
+ platform_window_.reset();
+ }
+
+ private:
+ // Overridden from PlatformViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+ platform_window_ =
+ ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds);
+ }
+
+ virtual void Show() OVERRIDE { platform_window_->Show(); }
+
+ virtual void Hide() OVERRIDE { platform_window_->Hide(); }
+
+ virtual void Close() OVERRIDE { platform_window_->Close(); }
+
+ virtual gfx::Size GetSize() OVERRIDE {
+ return platform_window_->GetBounds().size();
+ }
+
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ platform_window_->SetBounds(bounds);
+ }
+
+ virtual void SetCapture() OVERRIDE { platform_window_->SetCapture(); }
+
+ virtual void ReleaseCapture() OVERRIDE { platform_window_->ReleaseCapture(); }
+
+ // ui::PlatformWindowDelegate:
+ virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {
+ delegate_->OnBoundsChanged(new_bounds);
+ }
+
+ virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {}
+
+ virtual void DispatchEvent(ui::Event* event) OVERRIDE {
+ delegate_->OnEvent(event);
+ }
+
+ virtual void OnCloseRequest() OVERRIDE { platform_window_->Close(); }
+
+ virtual void OnClosed() OVERRIDE { delegate_->OnDestroyed(); }
+
+ virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {}
+
+ virtual void OnLostCapture() OVERRIDE {}
+
+ virtual void OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) OVERRIDE {
+ delegate_->OnAcceleratedWidgetAvailable(widget);
+ }
+
+ virtual void OnActivationChanged(bool active) OVERRIDE {}
+
+ scoped_ptr<ui::PlatformWindow> platform_window_;
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformViewportOzone);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+ return scoped_ptr<PlatformViewport>(
+ new PlatformViewportOzone(delegate)).Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_stub.cc b/mojo/services/native_viewport/platform_viewport_stub.cc
new file mode 100644
index 0000000..80a7e94
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_stub.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 "mojo/services/native_viewport/platform_viewport_headless.h"
+
+namespace mojo {
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+ return PlatformViewportHeadless::Create(delegate);
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_win.cc b/mojo/services/native_viewport/platform_viewport_win.cc
new file mode 100644
index 0000000..2227112
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_win.cc
@@ -0,0 +1,104 @@
+// 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/platform_viewport.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/rect.h"
+#include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/win/win_window.h"
+
+namespace mojo {
+
+class PlatformViewportWin : public PlatformViewport,
+ public ui::PlatformWindowDelegate {
+ public:
+ explicit PlatformViewportWin(Delegate* delegate)
+ : delegate_(delegate) {
+ }
+
+ virtual ~PlatformViewportWin() {
+ // Destroy the platform-window while |this| is still alive.
+ platform_window_.reset();
+ }
+
+ private:
+ // Overridden from PlatformViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+ platform_window_.reset(new ui::WinWindow(this, bounds));
+ }
+
+ virtual void Show() OVERRIDE {
+ platform_window_->Show();
+ }
+
+ virtual void Hide() OVERRIDE {
+ platform_window_->Hide();
+ }
+
+ virtual void Close() OVERRIDE {
+ platform_window_->Close();
+ }
+
+ virtual gfx::Size GetSize() OVERRIDE {
+ return platform_window_->GetBounds().size();
+ }
+
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ platform_window_->SetBounds(bounds);
+ }
+
+ virtual void SetCapture() OVERRIDE {
+ platform_window_->SetCapture();
+ }
+
+ virtual void ReleaseCapture() OVERRIDE {
+ platform_window_->ReleaseCapture();
+ }
+
+ // ui::PlatformWindowDelegate:
+ virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {
+ delegate_->OnBoundsChanged(new_bounds);
+ }
+
+ virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {
+ }
+
+ virtual void DispatchEvent(ui::Event* event) OVERRIDE {
+ delegate_->OnEvent(event);
+ }
+
+ virtual void OnCloseRequest() OVERRIDE {
+ platform_window_->Close();
+ }
+
+ virtual void OnClosed() OVERRIDE {
+ delegate_->OnDestroyed();
+ }
+
+ virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {
+ }
+
+ virtual void OnLostCapture() OVERRIDE {
+ }
+
+ virtual void OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) OVERRIDE {
+ delegate_->OnAcceleratedWidgetAvailable(widget);
+ }
+
+ virtual void OnActivationChanged(bool active) OVERRIDE {}
+
+ scoped_ptr<ui::PlatformWindow> platform_window_;
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformViewportWin);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+ return scoped_ptr<PlatformViewport>(new PlatformViewportWin(delegate)).Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/platform_viewport_x11.cc b/mojo/services/native_viewport/platform_viewport_x11.cc
new file mode 100644
index 0000000..d4914c7
--- /dev/null
+++ b/mojo/services/native_viewport/platform_viewport_x11.cc
@@ -0,0 +1,150 @@
+// 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/platform_viewport.h"
+
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h"
+#include "ui/events/event.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/gfx/rect.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/x11/x11_window.h"
+
+namespace mojo {
+
+class PlatformViewportX11 : public PlatformViewport,
+ public ui::PlatformWindowDelegate {
+ public:
+ explicit PlatformViewportX11(Delegate* delegate) : delegate_(delegate) {
+ }
+
+ virtual ~PlatformViewportX11() {
+ // Destroy the platform-window while |this| is still alive.
+ platform_window_.reset();
+ }
+
+ private:
+ // Overridden from PlatformViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+ CHECK(!event_source_);
+ CHECK(!platform_window_);
+
+ event_source_ = ui::PlatformEventSource::CreateDefault();
+
+ platform_window_.reset(new ui::X11Window(this));
+ platform_window_->SetBounds(bounds);
+ }
+
+ virtual void Show() OVERRIDE {
+ platform_window_->Show();
+ }
+
+ virtual void Hide() OVERRIDE {
+ platform_window_->Hide();
+ }
+
+ virtual void Close() OVERRIDE {
+ platform_window_->Close();
+ }
+
+ virtual gfx::Size GetSize() OVERRIDE {
+ return bounds_.size();
+ }
+
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ platform_window_->SetBounds(bounds);
+ }
+
+ virtual void SetCapture() OVERRIDE {
+ platform_window_->SetCapture();
+ }
+
+ virtual void ReleaseCapture() OVERRIDE {
+ platform_window_->ReleaseCapture();
+ }
+
+ // ui::PlatformWindowDelegate:
+ virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {
+ bounds_ = new_bounds;
+ delegate_->OnBoundsChanged(new_bounds);
+ }
+
+ virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {
+ }
+
+ virtual void DispatchEvent(ui::Event* event) OVERRIDE {
+ delegate_->OnEvent(event);
+
+ // We want to emulate the WM_CHAR generation behaviour of Windows.
+ //
+ // On Linux, we've previously inserted characters by having
+ // InputMethodAuraLinux take all key down events and send a character event
+ // to the TextInputClient. This causes a mismatch in code that has to be
+ // shared between Windows and Linux, including blink code. Now that we're
+ // trying to have one way of doing things, we need to standardize on and
+ // emulate Windows character events.
+ //
+ // This is equivalent to what we're doing in the current Linux port, but
+ // done once instead of done multiple times in different places.
+ if (event->type() == ui::ET_KEY_PRESSED) {
+ ui::KeyEvent* key_press_event = static_cast<ui::KeyEvent*>(event);
+ ui::KeyEvent char_event(key_press_event->GetCharacter(),
+ key_press_event->key_code(),
+ key_press_event->flags());
+
+ DCHECK_EQ(key_press_event->GetCharacter(), char_event.GetCharacter());
+ DCHECK_EQ(key_press_event->key_code(), char_event.key_code());
+ DCHECK_EQ(key_press_event->flags(), char_event.flags());
+
+ char_event.SetExtendedKeyEventData(scoped_ptr<ui::ExtendedKeyEventData>(
+ new MojoExtendedKeyEventData(
+ key_press_event->GetLocatedWindowsKeyboardCode(),
+ key_press_event->GetText(),
+ key_press_event->GetUnmodifiedText())));
+ char_event.set_platform_keycode(key_press_event->platform_keycode());
+
+ delegate_->OnEvent(&char_event);
+ }
+ }
+
+ virtual void OnCloseRequest() OVERRIDE {
+ platform_window_->Close();
+ }
+
+ virtual void OnClosed() OVERRIDE {
+ delegate_->OnDestroyed();
+ }
+
+ virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {
+ }
+
+ virtual void OnLostCapture() OVERRIDE {
+ }
+
+ virtual void OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) OVERRIDE {
+ delegate_->OnAcceleratedWidgetAvailable(widget);
+ }
+
+ virtual void OnActivationChanged(bool active) OVERRIDE {}
+
+ scoped_ptr<ui::PlatformEventSource> event_source_;
+ scoped_ptr<ui::PlatformWindow> platform_window_;
+ Delegate* delegate_;
+ gfx::Rect bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformViewportX11);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(Delegate* delegate) {
+ return scoped_ptr<PlatformViewport>(new PlatformViewportX11(delegate)).Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/viewport_surface.cc b/mojo/services/native_viewport/viewport_surface.cc
new file mode 100644
index 0000000..8786e8c
--- /dev/null
+++ b/mojo/services/native_viewport/viewport_surface.cc
@@ -0,0 +1,111 @@
+// 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/native_viewport/viewport_surface.h"
+
+#include "base/bind.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+
+ViewportSurface::ViewportSurface(SurfacesService* surfaces_service,
+ Gpu* gpu_service,
+ const gfx::Size& size,
+ cc::SurfaceId child_id)
+ : gpu_service_(gpu_service),
+ widget_id_(0u),
+ size_(size),
+ child_id_(child_id),
+ weak_factory_(this) {
+ surfaces_service->CreateSurfaceConnection(
+ base::Bind(&ViewportSurface::OnSurfaceConnectionCreated,
+ weak_factory_.GetWeakPtr()));
+}
+
+ViewportSurface::~ViewportSurface() {
+}
+
+void ViewportSurface::SetWidgetId(uint64_t widget_id) {
+ widget_id_ = widget_id;
+ if (id_allocator_)
+ BindSurfaceToNativeViewport();
+}
+
+void ViewportSurface::SetSize(const gfx::Size& size) {
+ if (size_ == size)
+ return;
+
+ if (id_.is_null())
+ return;
+
+ surface_->DestroySurface(SurfaceId::From(id_));
+ if (widget_id_)
+ BindSurfaceToNativeViewport();
+}
+
+void ViewportSurface::SetChildId(cc::SurfaceId child_id) {
+ child_id_ = child_id;
+ SubmitFrame();
+}
+
+void ViewportSurface::OnSurfaceConnectionCreated(SurfacePtr surface,
+ uint32_t id_namespace) {
+ surface_ = surface.Pass();
+ surface_.set_client(this);
+ id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+ if (widget_id_ != 0u)
+ BindSurfaceToNativeViewport();
+}
+
+void ViewportSurface::BindSurfaceToNativeViewport() {
+ CommandBufferPtr cb;
+ gpu_service_->CreateOnscreenGLES2Context(
+ widget_id_, Size::From(size_), Get(&cb));
+
+ id_ = id_allocator_->GenerateId();
+ surface_->CreateGLES2BoundSurface(
+ cb.Pass(), SurfaceId::From(id_), Size::From(size_));
+
+ SubmitFrame();
+}
+
+void ViewportSurface::SubmitFrame() {
+ if (child_id_.is_null() || id_.is_null())
+ return;
+
+ SurfaceQuadStatePtr surface_quad_state = SurfaceQuadState::New();
+ surface_quad_state->surface = SurfaceId::From(child_id_);
+
+ gfx::Rect bounds(size_);
+
+ QuadPtr surface_quad = Quad::New();
+ surface_quad->material = Material::MATERIAL_SURFACE_CONTENT;
+ surface_quad->rect = Rect::From(bounds);
+ surface_quad->opaque_rect = Rect::From(bounds);
+ surface_quad->visible_rect = Rect::From(bounds);
+ surface_quad->needs_blending = true;
+ surface_quad->shared_quad_state_index = 0;
+ surface_quad->surface_quad_state = surface_quad_state.Pass();
+
+ PassPtr pass = CreateDefaultPass(1, bounds);
+
+ pass->quads.push_back(surface_quad.Pass());
+ pass->shared_quad_states.push_back(CreateDefaultSQS(size_));
+
+ FramePtr frame = Frame::New();
+ frame->passes.push_back(pass.Pass());
+ frame->resources.resize(0u);
+ surface_->SubmitFrame(SurfaceId::From(id_), frame.Pass());
+}
+
+void ViewportSurface::ReturnResources(Array<ReturnedResourcePtr> resources) {
+ // We never submit resources so we should never get any back.
+ DCHECK_EQ(0u, resources.size());
+}
+
+} // namespace mojo
diff --git a/mojo/services/native_viewport/viewport_surface.h b/mojo/services/native_viewport/viewport_surface.h
new file mode 100644
index 0000000..2ca8105
--- /dev/null
+++ b/mojo/services/native_viewport/viewport_surface.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_NATIVE_VIEWPORT_VIEWPORT_SURFACE_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_VIEWPORT_SURFACE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+
+// This manages the surface that draws to a particular NativeViewport instance.
+class ViewportSurface : public SurfaceClient {
+ public:
+ ViewportSurface(SurfacesService* surfaces_service,
+ Gpu* gpu_service,
+ const gfx::Size& size,
+ cc::SurfaceId child_id);
+ virtual ~ViewportSurface();
+
+ void SetWidgetId(uint64_t widget_id);
+ void SetSize(const gfx::Size& size);
+ void SetChildId(cc::SurfaceId child_id);
+
+ private:
+ void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+ void BindSurfaceToNativeViewport();
+ void SubmitFrame();
+
+ // SurfaceClient implementation.
+ virtual void ReturnResources(Array<ReturnedResourcePtr> resources) OVERRIDE;
+
+ SurfacePtr surface_;
+ Gpu* gpu_service_;
+ uint64_t widget_id_;
+ gfx::Size size_;
+ scoped_ptr<cc::SurfaceIdAllocator> id_allocator_;
+ cc::SurfaceId id_;
+ cc::SurfaceId child_id_;
+ base::WeakPtrFactory<ViewportSurface> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewportSurface);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_VIEWPORT_SURFACE_H_
diff --git a/mojo/services/network/BUILD.gn b/mojo/services/network/BUILD.gn
new file mode 100644
index 0000000..d6ae427
--- /dev/null
+++ b/mojo/services/network/BUILD.gn
@@ -0,0 +1,47 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_service
+shared_library("network") {
+ output_name = "mojo_network_service"
+
+ deps = [
+ ":lib",
+ "//base",
+ "//mojo/application",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/bindings:bindings",
+ "//mojo/services/public/cpp/network",
+ "//mojo/services/public/interfaces/network",
+ ]
+
+ sources = [ "main.cc" ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_service_lib
+source_set("lib") {
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/services/public/cpp/network",
+ "//mojo/services/public/interfaces/network",
+ "//net",
+ "//url",
+ ]
+
+ sources = [
+ "cookie_store_impl.cc",
+ "cookie_store_impl.h",
+ "network_context.cc",
+ "network_context.h",
+ "network_service_impl.cc",
+ "network_service_impl.h",
+ "url_loader_impl.cc",
+ "url_loader_impl.h",
+ "web_socket_impl.cc",
+ "web_socket_impl.h",
+ ]
+}
diff --git a/mojo/services/network/DEPS b/mojo/services/network/DEPS
new file mode 100644
index 0000000..cb237df
--- /dev/null
+++ b/mojo/services/network/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+mojo/application",
+ "+mojo/services",
+ "+net",
+]
diff --git a/mojo/services/network/cookie_store_impl.cc b/mojo/services/network/cookie_store_impl.cc
new file mode 100644
index 0000000..0340933
--- /dev/null
+++ b/mojo/services/network/cookie_store_impl.cc
@@ -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.
+
+#include "mojo/services/network/cookie_store_impl.h"
+
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/network/network_context.h"
+#include "net/cookies/cookie_store.h"
+#include "net/url_request/url_request_context.h"
+
+namespace mojo {
+namespace {
+
+void AdaptGetCookiesCallback(const Callback<void(String)>& callback,
+ const std::string& cookies) {
+ callback.Run(cookies);
+}
+
+void AdaptSetCookieCallback(const Callback<void(bool)>& callback,
+ bool success) {
+ callback.Run(success);
+}
+
+} // namespace
+
+CookieStoreImpl::CookieStoreImpl(NetworkContext* context,
+ const GURL& origin)
+ : context_(context),
+ origin_(origin) {
+}
+
+CookieStoreImpl::~CookieStoreImpl() {
+}
+
+void CookieStoreImpl::Get(const String& url,
+ const Callback<void(String)>& callback) {
+ // TODO(darin): Perform origin restriction.
+ net::CookieStore* store = context_->url_request_context()->cookie_store();
+ if (!store) {
+ callback.Run(String());
+ return;
+ }
+ store->GetCookiesWithOptionsAsync(
+ url.To<GURL>(),
+ net::CookieOptions(),
+ base::Bind(&AdaptGetCookiesCallback, callback));
+}
+
+void CookieStoreImpl::Set(const String& url,
+ const String& cookie,
+ const Callback<void(bool)>& callback) {
+ // TODO(darin): Perform origin restriction.
+ net::CookieStore* store = context_->url_request_context()->cookie_store();
+ if (!store) {
+ callback.Run(false);
+ return;
+ }
+ store->SetCookieWithOptionsAsync(
+ url.To<GURL>(),
+ cookie,
+ net::CookieOptions(),
+ base::Bind(&AdaptSetCookieCallback, callback));
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/cookie_store_impl.h b/mojo/services/network/cookie_store_impl.h
new file mode 100644
index 0000000..a2ff0b2
--- /dev/null
+++ b/mojo/services/network/cookie_store_impl.h
@@ -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.
+
+#ifndef MOJO_SERVICES_NETWORK_COOKIE_STORE_IMPL_H_
+#define MOJO_SERVICES_NETWORK_COOKIE_STORE_IMPL_H_
+
+#include "mojo/services/public/interfaces/network/cookie_store.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+class NetworkContext;
+
+class CookieStoreImpl : public InterfaceImpl<CookieStore> {
+ public:
+ CookieStoreImpl(NetworkContext* context, const GURL& origin);
+ virtual ~CookieStoreImpl();
+
+ private:
+ // CookieStore methods:
+ virtual void Get(const String& url,
+ const Callback<void(String)>& callback) OVERRIDE;
+ virtual void Set(const String& url, const String& cookie,
+ const Callback<void(bool)>& callback) OVERRIDE;
+
+ NetworkContext* context_;
+ GURL origin_;
+
+ DISALLOW_COPY_AND_ASSIGN(CookieStoreImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_COOKIE_STORE_IMPL_H_
diff --git a/mojo/services/network/main.cc b/mojo/services/network/main.cc
new file mode 100644
index 0000000..8d637ac
--- /dev/null
+++ b/mojo/services/network/main.cc
@@ -0,0 +1,56 @@
+// 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/base_paths.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/services/network/network_context.h"
+#include "mojo/services/network/network_service_impl.h"
+
+class Delegate : public mojo::ApplicationDelegate,
+ public mojo::InterfaceFactory<mojo::NetworkService> {
+ public:
+ Delegate() {}
+
+ virtual void Initialize(mojo::ApplicationImpl* app) OVERRIDE {
+ base::FilePath base_path;
+ CHECK(PathService::Get(base::DIR_TEMP, &base_path));
+ base_path = base_path.Append(FILE_PATH_LITERAL("network_service"));
+ context_.reset(new mojo::NetworkContext(base_path));
+ }
+
+ // mojo::ApplicationDelegate implementation.
+ virtual bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) OVERRIDE {
+ DCHECK(context_);
+ connection->AddService(this);
+ return true;
+ }
+
+ // mojo::InterfaceFactory<mojo::NetworkService> implementation.
+ virtual void Create(
+ mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::NetworkService> request) OVERRIDE {
+ mojo::BindToRequest(
+ new mojo::NetworkServiceImpl(connection, context_.get()), &request);
+ }
+
+ private:
+ scoped_ptr<mojo::NetworkContext> context_;
+};
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new Delegate);
+ runner.set_message_loop_type(base::MessageLoop::TYPE_IO);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/network/network_context.cc b/mojo/services/network/network_context.cc
new file mode 100644
index 0000000..a996522
--- /dev/null
+++ b/mojo/services/network/network_context.cc
@@ -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.
+
+#include "mojo/services/network/network_context.h"
+
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "net/proxy/proxy_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace mojo {
+
+NetworkContext::NetworkContext(const base::FilePath& base_path) {
+ net::URLRequestContextBuilder builder;
+ builder.set_accept_language("en-us,en");
+ // TODO(darin): This is surely the wrong UA string.
+ builder.set_user_agent("Mojo/0.1");
+ builder.set_proxy_service(net::ProxyService::CreateDirect());
+ builder.set_transport_security_persister_path(base_path);
+
+ net::URLRequestContextBuilder::HttpCacheParams cache_params;
+ cache_params.path = base_path.Append(FILE_PATH_LITERAL("Cache"));
+ cache_params.type = net::URLRequestContextBuilder::HttpCacheParams::DISK;
+ builder.EnableHttpCache(cache_params);
+
+ builder.set_file_enabled(true);
+
+ url_request_context_.reset(builder.Build());
+}
+
+NetworkContext::~NetworkContext() {
+ // TODO(darin): Be careful about destruction order of member variables?
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/network_context.h b/mojo/services/network/network_context.h
new file mode 100644
index 0000000..19245f4
--- /dev/null
+++ b/mojo/services/network/network_context.h
@@ -0,0 +1,38 @@
+// 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/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace net {
+class URLRequestContext;
+}
+
+namespace mojo {
+
+class NetworkContext {
+ public:
+ explicit NetworkContext(const base::FilePath& base_path);
+ ~NetworkContext();
+
+ net::URLRequestContext* url_request_context() {
+ return url_request_context_.get();
+ }
+
+ private:
+ scoped_ptr<net::URLRequestContext> url_request_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkContext);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
diff --git a/mojo/services/network/network_service_impl.cc b/mojo/services/network/network_service_impl.cc
new file mode 100644
index 0000000..a576975
--- /dev/null
+++ b/mojo/services/network/network_service_impl.cc
@@ -0,0 +1,54 @@
+// 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/public/cpp/application/application_connection.h"
+#include "mojo/services/network/cookie_store_impl.h"
+#include "mojo/services/network/url_loader_impl.h"
+#include "mojo/services/network/web_socket_impl.h"
+
+namespace mojo {
+
+NetworkServiceImpl::NetworkServiceImpl(ApplicationConnection* connection,
+ NetworkContext* context)
+ : context_(context),
+ origin_(GURL(connection->GetRemoteApplicationURL()).GetOrigin()) {
+}
+
+NetworkServiceImpl::~NetworkServiceImpl() {
+}
+
+void NetworkServiceImpl::CreateURLLoader(InterfaceRequest<URLLoader> loader) {
+ // TODO(darin): Plumb origin_. Use for CORS.
+ BindToRequest(new URLLoaderImpl(context_), &loader);
+}
+
+void NetworkServiceImpl::GetCookieStore(InterfaceRequest<CookieStore> store) {
+ BindToRequest(new CookieStoreImpl(context_, origin_), &store);
+}
+
+void NetworkServiceImpl::CreateWebSocket(InterfaceRequest<WebSocket> socket) {
+ BindToRequest(new WebSocketImpl(context_), &socket);
+}
+
+void NetworkServiceImpl::CreateTCPBoundSocket(
+ NetAddressPtr local_address,
+ InterfaceRequest<TCPBoundSocket> bound_socket,
+ const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) {
+ // TODO(brettw) implement this.
+ callback.Run(NetworkErrorPtr(), NetAddressPtr());
+}
+
+void NetworkServiceImpl::CreateTCPClientSocket(
+ NetAddressPtr remote_address,
+ ScopedDataPipeConsumerHandle send_stream,
+ ScopedDataPipeProducerHandle receive_stream,
+ InterfaceRequest<TCPClientSocket> client_socket,
+ const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) {
+ // TODO(brettw) implement this.
+ callback.Run(NetworkErrorPtr(), NetAddressPtr());
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/network_service_impl.h b/mojo/services/network/network_service_impl.h
new file mode 100644
index 0000000..9088e36
--- /dev/null
+++ b/mojo/services/network/network_service_impl.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_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"
+#include "url/gurl.h"
+
+namespace mojo {
+class ApplicationConnection;
+class NetworkContext;
+
+class NetworkServiceImpl : public InterfaceImpl<NetworkService> {
+ public:
+ NetworkServiceImpl(ApplicationConnection* connection,
+ NetworkContext* context);
+ virtual ~NetworkServiceImpl();
+
+ // NetworkService methods:
+ virtual void CreateURLLoader(InterfaceRequest<URLLoader> loader) override;
+ virtual void GetCookieStore(InterfaceRequest<CookieStore> store) override;
+ virtual void CreateWebSocket(InterfaceRequest<WebSocket> socket) override;
+ virtual void CreateTCPBoundSocket(
+ NetAddressPtr local_address,
+ InterfaceRequest<TCPBoundSocket> bound_socket,
+ const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) override;
+ virtual void CreateTCPClientSocket(
+ NetAddressPtr remote_address,
+ ScopedDataPipeConsumerHandle send_stream,
+ ScopedDataPipeProducerHandle receive_stream,
+ InterfaceRequest<TCPClientSocket> client_socket,
+ const Callback<void(NetworkErrorPtr, NetAddressPtr)>& callback) override;
+
+ private:
+ NetworkContext* context_;
+ GURL origin_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc
new file mode 100644
index 0000000..b5cbd0f
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.cc
@@ -0,0 +1,384 @@
+// 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 "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.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/base/upload_bytes_element_reader.h"
+#include "net/base/upload_data_stream.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/redirect_info.h"
+#include "net/url_request/url_request_context.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 = String::From(url_request->url());
+
+ 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);
+ }
+
+ std::string mime_type;
+ url_request->GetMimeType(&mime_type);
+ response->mime_type = mime_type;
+
+ std::string charset;
+ url_request->GetCharset(&charset);
+ response->charset = charset;
+
+ return response.Pass();
+}
+
+NetworkErrorPtr MakeNetworkError(int error_code) {
+ NetworkErrorPtr error = NetworkError::New();
+ error->code = error_code;
+ error->description = net::ErrorToString(error_code);
+ return error.Pass();
+}
+
+// Reads the request body upload data from a DataPipe.
+class UploadDataPipeElementReader : public net::UploadElementReader {
+ public:
+ UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
+ : pipe_(pipe.Pass()), num_bytes_(0) {}
+ virtual ~UploadDataPipeElementReader() {}
+
+ // UploadElementReader overrides:
+ virtual int Init(const net::CompletionCallback& callback) OVERRIDE {
+ offset_ = 0;
+ ReadDataRaw(pipe_.get(), NULL, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
+ return net::OK;
+ }
+ virtual uint64 GetContentLength() const OVERRIDE {
+ return num_bytes_;
+ }
+ virtual uint64 BytesRemaining() const OVERRIDE {
+ return num_bytes_ - offset_;
+ }
+ virtual bool IsInMemory() const OVERRIDE {
+ return false;
+ }
+ virtual int Read(net::IOBuffer* buf,
+ int buf_length,
+ const net::CompletionCallback& callback) OVERRIDE {
+ uint32_t bytes_read =
+ std::min(static_cast<uint32_t>(BytesRemaining()),
+ static_cast<uint32_t>(buf_length));
+ if (bytes_read > 0) {
+ ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
+ MOJO_READ_DATA_FLAG_NONE);
+ }
+
+ offset_ += bytes_read;
+ return bytes_read;
+ }
+
+ private:
+ ScopedDataPipeConsumerHandle pipe_;
+ uint32_t num_bytes_;
+ uint32_t offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
+};
+
+} // 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) {
+ }
+
+ MojoResult 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;
+ }
+
+ 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),
+ response_body_buffer_size_(0),
+ auto_follow_redirects_(true),
+ weak_ptr_factory_(this) {
+}
+
+URLLoaderImpl::~URLLoaderImpl() {
+}
+
+void URLLoaderImpl::Start(URLRequestPtr request,
+ const Callback<void(URLResponsePtr)>& callback) {
+ if (url_request_) {
+ SendError(net::ERR_UNEXPECTED, callback);
+ return;
+ }
+
+ if (!request) {
+ SendError(net::ERR_INVALID_ARGUMENT, callback);
+ return;
+ }
+
+ url_request_ = context_->url_request_context()->CreateRequest(
+ GURL(request->url),
+ net::DEFAULT_PRIORITY,
+ this,
+ NULL);
+ 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->body) {
+ ScopedVector<net::UploadElementReader> element_readers;
+ for (size_t i = 0; i < request->body.size(); ++i) {
+ element_readers.push_back(
+ new UploadDataPipeElementReader(request->body[i].Pass()));
+ }
+ url_request_->set_upload(make_scoped_ptr(
+ new net::UploadDataStream(element_readers.Pass(), 0)));
+ }
+ if (request->bypass_cache)
+ url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
+
+ callback_ = callback;
+ response_body_buffer_size_ = request->response_body_buffer_size;
+ auto_follow_redirects_ = request->auto_follow_redirects;
+
+ url_request_->Start();
+}
+
+void URLLoaderImpl::FollowRedirect(
+ const Callback<void(URLResponsePtr)>& callback) {
+ if (!url_request_) {
+ SendError(net::ERR_UNEXPECTED, callback);
+ return;
+ }
+
+ if (auto_follow_redirects_) {
+ DLOG(ERROR) << "Spurious call to FollowRedirect";
+ SendError(net::ERR_UNEXPECTED, callback);
+ return;
+ }
+
+ // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
+ url_request_->FollowDeferredRedirect();
+}
+
+void URLLoaderImpl::QueryStatus(
+ const Callback<void(URLLoaderStatusPtr)>& callback) {
+ URLLoaderStatusPtr status(URLLoaderStatus::New());
+ if (url_request_) {
+ status->is_loading = url_request_->is_pending();
+ if (!url_request_->status().is_success())
+ status->error = MakeNetworkError(url_request_->status().error());
+ } else {
+ status->is_loading = false;
+ }
+ // TODO(darin): Populate more status fields.
+ callback.Run(status.Pass());
+}
+
+void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
+ const net::RedirectInfo& redirect_info,
+ bool* defer_redirect) {
+ DCHECK(url_request == url_request_.get());
+ DCHECK(url_request->status().is_success());
+
+ if (auto_follow_redirects_)
+ return;
+
+ // Send the redirect response to the client, allowing them to inspect it and
+ // optionally follow the redirect.
+ *defer_redirect = true;
+
+ URLResponsePtr response = MakeURLResponse(url_request);
+ response->redirect_method = redirect_info.new_method;
+ response->redirect_url = String::From(redirect_info.new_url);
+
+ SendResponse(response.Pass());
+}
+
+void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
+ DCHECK(url_request == url_request_.get());
+
+ if (!url_request->status().is_success()) {
+ SendError(url_request->status().error(), callback_);
+ callback_ = Callback<void(URLResponsePtr)>();
+ return;
+ }
+
+ // TODO(darin): Add support for optional MIME sniffing.
+
+ DataPipe data_pipe;
+ // TODO(darin): Honor given buffer size.
+
+ URLResponsePtr response = MakeURLResponse(url_request);
+ response->body = data_pipe.consumer_handle.Pass();
+ response_body_stream_ = data_pipe.producer_handle.Pass();
+
+ SendResponse(response.Pass());
+
+ // Start reading...
+ ReadMore();
+}
+
+void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
+ int bytes_read) {
+ DCHECK(url_request == url_request_.get());
+
+ if (url_request->status().is_success()) {
+ DidRead(static_cast<uint32_t>(bytes_read), false);
+ } else {
+ pending_write_ = NULL; // This closes the data pipe.
+ }
+}
+
+void URLLoaderImpl::SendError(
+ int error_code,
+ const Callback<void(URLResponsePtr)>& callback) {
+ URLResponsePtr response(URLResponse::New());
+ if (url_request_)
+ response->url = String::From(url_request_->url());
+ response->error = MakeNetworkError(error_code);
+ callback.Run(response.Pass());
+}
+
+void URLLoaderImpl::SendResponse(URLResponsePtr response) {
+ Callback<void(URLResponsePtr)> callback;
+ std::swap(callback_, callback);
+ callback.Run(response.Pass());
+}
+
+void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
+ // TODO(darin): Handle a bad |result| value.
+ ReadMore();
+}
+
+void URLLoaderImpl::WaitToReadMore() {
+ handle_watcher_.Start(response_body_stream_.get(),
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void URLLoaderImpl::ReadMore() {
+ DCHECK(!pending_write_.get());
+
+ pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
+
+ uint32_t num_bytes;
+ MojoResult result = pending_write_->BeginWrite(&num_bytes);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ // The pipe is full. We need to wait for it to have more space.
+ response_body_stream_ = pending_write_->Complete(num_bytes);
+ pending_write_ = NULL;
+ WaitToReadMore();
+ return;
+ }
+ if (result != MOJO_RESULT_OK) {
+ // The response body stream is in a bad state. Bail.
+ // TODO(darin): How should this be communicated to our client?
+ return;
+ }
+ CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
+
+ scoped_refptr<net::IOBuffer> buf =
+ new DependentIOBuffer(pending_write_.get());
+
+ int bytes_read;
+ url_request_->Read(buf.get(), 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.
+ }
+}
+
+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/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h
new file mode 100644
index 0000000..5c5af8a
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.h
@@ -0,0 +1,70 @@
+// 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/common/handle_watcher.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;
+
+ // URLLoader methods:
+ virtual void Start(
+ URLRequestPtr request,
+ const Callback<void(URLResponsePtr)>& callback) OVERRIDE;
+ virtual void FollowRedirect(
+ const Callback<void(URLResponsePtr)>& callback) OVERRIDE;
+ virtual void QueryStatus(
+ const Callback<void(URLLoaderStatusPtr)>& callback) OVERRIDE;
+
+ // net::URLRequest::Delegate methods:
+ virtual void OnReceivedRedirect(net::URLRequest* url_request,
+ const net::RedirectInfo& redirect_info,
+ 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,
+ const Callback<void(URLResponsePtr)>& callback);
+ void SendResponse(URLResponsePtr response);
+ void OnResponseBodyStreamReady(MojoResult result);
+ void WaitToReadMore();
+ void ReadMore();
+ void DidRead(uint32_t num_bytes, bool completed_synchronously);
+
+ NetworkContext* context_;
+ scoped_ptr<net::URLRequest> url_request_;
+ Callback<void(URLResponsePtr)> callback_;
+ ScopedDataPipeProducerHandle response_body_stream_;
+ scoped_refptr<PendingWriteToDataPipe> pending_write_;
+ common::HandleWatcher handle_watcher_;
+ uint32 response_body_buffer_size_;
+ bool auto_follow_redirects_;
+
+ base::WeakPtrFactory<URLLoaderImpl> weak_ptr_factory_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
diff --git a/mojo/services/network/web_socket_impl.cc b/mojo/services/network/web_socket_impl.cc
new file mode 100644
index 0000000..7ba9b22
--- /dev/null
+++ b/mojo/services/network/web_socket_impl.cc
@@ -0,0 +1,239 @@
+// 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/web_socket_impl.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/network/network_context.h"
+#include "mojo/services/public/cpp/network/web_socket_read_queue.h"
+#include "mojo/services/public/cpp/network/web_socket_write_queue.h"
+#include "net/websockets/websocket_channel.h"
+#include "net/websockets/websocket_errors.h"
+#include "net/websockets/websocket_event_interface.h"
+#include "net/websockets/websocket_frame.h" // for WebSocketFrameHeader::OpCode
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
+#include "url/origin.h"
+
+namespace mojo {
+
+template <>
+struct TypeConverter<net::WebSocketFrameHeader::OpCode,
+ WebSocket::MessageType> {
+ static net::WebSocketFrameHeader::OpCode Convert(
+ WebSocket::MessageType type) {
+ DCHECK(type == WebSocket::MESSAGE_TYPE_CONTINUATION ||
+ type == WebSocket::MESSAGE_TYPE_TEXT ||
+ type == WebSocket::MESSAGE_TYPE_BINARY);
+ typedef net::WebSocketFrameHeader::OpCode OpCode;
+ // These compile asserts verify that the same underlying values are used for
+ // both types, so we can simply cast between them.
+ COMPILE_ASSERT(static_cast<OpCode>(WebSocket::MESSAGE_TYPE_CONTINUATION) ==
+ net::WebSocketFrameHeader::kOpCodeContinuation,
+ enum_values_must_match_for_opcode_continuation);
+ COMPILE_ASSERT(static_cast<OpCode>(WebSocket::MESSAGE_TYPE_TEXT) ==
+ net::WebSocketFrameHeader::kOpCodeText,
+ enum_values_must_match_for_opcode_text);
+ COMPILE_ASSERT(static_cast<OpCode>(WebSocket::MESSAGE_TYPE_BINARY) ==
+ net::WebSocketFrameHeader::kOpCodeBinary,
+ enum_values_must_match_for_opcode_binary);
+ return static_cast<OpCode>(type);
+ }
+};
+
+template <>
+struct TypeConverter<WebSocket::MessageType,
+ net::WebSocketFrameHeader::OpCode> {
+ static WebSocket::MessageType Convert(
+ net::WebSocketFrameHeader::OpCode type) {
+ DCHECK(type == net::WebSocketFrameHeader::kOpCodeContinuation ||
+ type == net::WebSocketFrameHeader::kOpCodeText ||
+ type == net::WebSocketFrameHeader::kOpCodeBinary);
+ return static_cast<WebSocket::MessageType>(type);
+ }
+};
+
+namespace {
+
+typedef net::WebSocketEventInterface::ChannelState ChannelState;
+
+struct WebSocketEventHandler : public net::WebSocketEventInterface {
+ public:
+ WebSocketEventHandler(WebSocketClientPtr client)
+ : client_(client.Pass()) {
+ }
+ virtual ~WebSocketEventHandler() {}
+
+ private:
+ // net::WebSocketEventInterface methods:
+ virtual ChannelState OnAddChannelResponse(
+ bool fail,
+ const std::string& selected_subprotocol,
+ const std::string& extensions) OVERRIDE;
+ virtual ChannelState OnDataFrame(bool fin,
+ WebSocketMessageType type,
+ const std::vector<char>& data) OVERRIDE;
+ virtual ChannelState OnClosingHandshake() OVERRIDE;
+ virtual ChannelState OnFlowControl(int64 quota) OVERRIDE;
+ virtual ChannelState OnDropChannel(bool was_clean,
+ uint16 code,
+ const std::string& reason) OVERRIDE;
+ virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE;
+ virtual ChannelState OnStartOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeRequestInfo> request) OVERRIDE;
+ virtual ChannelState OnFinishOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeResponseInfo> response) OVERRIDE;
+ virtual ChannelState OnSSLCertificateError(
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
+ const GURL& url,
+ const net::SSLInfo& ssl_info,
+ bool fatal) OVERRIDE;
+
+ // Called once we've written to |receive_stream_|.
+ void DidWriteToReceiveStream(bool fin,
+ net::WebSocketFrameHeader::OpCode type,
+ uint32_t num_bytes,
+ const char* buffer);
+ WebSocketClientPtr client_;
+ ScopedDataPipeProducerHandle receive_stream_;
+ scoped_ptr<WebSocketWriteQueue> write_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketEventHandler);
+};
+
+ChannelState WebSocketEventHandler::OnAddChannelResponse(
+ bool fail,
+ const std::string& selected_protocol,
+ const std::string& extensions) {
+ DataPipe data_pipe;
+ receive_stream_ = data_pipe.producer_handle.Pass();
+ write_queue_.reset(new WebSocketWriteQueue(receive_stream_.get()));
+ client_->DidConnect(
+ fail, selected_protocol, extensions, data_pipe.consumer_handle.Pass());
+ if (fail)
+ return WebSocketEventInterface::CHANNEL_DELETED;
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnDataFrame(
+ bool fin,
+ net::WebSocketFrameHeader::OpCode type,
+ const std::vector<char>& data) {
+ uint32_t size = static_cast<uint32_t>(data.size());
+ write_queue_->Write(
+ &data[0], size,
+ base::Bind(&WebSocketEventHandler::DidWriteToReceiveStream,
+ base::Unretained(this),
+ fin, type, size));
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnClosingHandshake() {
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnFlowControl(int64 quota) {
+ client_->DidReceiveFlowControl(quota);
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnDropChannel(bool was_clean,
+ uint16 code,
+ const std::string& reason) {
+ client_->DidClose(was_clean, code, reason);
+ return WebSocketEventInterface::CHANNEL_DELETED;
+}
+
+ChannelState WebSocketEventHandler::OnFailChannel(const std::string& message) {
+ client_->DidFail(message);
+ return WebSocketEventInterface::CHANNEL_DELETED;
+}
+
+ChannelState WebSocketEventHandler::OnStartOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeRequestInfo> request) {
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnFinishOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeResponseInfo> response) {
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+ChannelState WebSocketEventHandler::OnSSLCertificateError(
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
+ const GURL& url,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ client_->DidFail("SSL Error");
+ return WebSocketEventInterface::CHANNEL_DELETED;
+}
+
+void WebSocketEventHandler::DidWriteToReceiveStream(
+ bool fin,
+ net::WebSocketFrameHeader::OpCode type,
+ uint32_t num_bytes,
+ const char* buffer) {
+ client_->DidReceiveData(
+ fin, ConvertTo<WebSocket::MessageType>(type), num_bytes);
+}
+
+} // namespace mojo
+
+WebSocketImpl::WebSocketImpl(NetworkContext* context) : context_(context) {
+}
+
+WebSocketImpl::~WebSocketImpl() {
+}
+
+void WebSocketImpl::Connect(const String& url,
+ Array<String> protocols,
+ const String& origin,
+ ScopedDataPipeConsumerHandle send_stream,
+ WebSocketClientPtr client) {
+ DCHECK(!channel_);
+ send_stream_ = send_stream.Pass();
+ read_queue_.reset(new WebSocketReadQueue(send_stream_.get()));
+ scoped_ptr<net::WebSocketEventInterface> event_interface(
+ new WebSocketEventHandler(client.Pass()));
+ channel_.reset(new net::WebSocketChannel(event_interface.Pass(),
+ context_->url_request_context()));
+ channel_->SendAddChannelRequest(GURL(url.get()),
+ protocols.To<std::vector<std::string> >(),
+ url::Origin(origin.get()));
+}
+
+void WebSocketImpl::Send(bool fin,
+ WebSocket::MessageType type,
+ uint32_t num_bytes) {
+ DCHECK(channel_);
+ read_queue_->Read(num_bytes,
+ base::Bind(&WebSocketImpl::DidReadFromSendStream,
+ base::Unretained(this),
+ fin, type, num_bytes));
+}
+
+void WebSocketImpl::FlowControl(int64_t quota) {
+ DCHECK(channel_);
+ channel_->SendFlowControl(quota);
+}
+
+void WebSocketImpl::Close(uint16_t code, const String& reason) {
+ DCHECK(channel_);
+ channel_->StartClosingHandshake(code, reason);
+}
+
+void WebSocketImpl::DidReadFromSendStream(bool fin,
+ WebSocket::MessageType type,
+ uint32_t num_bytes,
+ const char* data) {
+ std::vector<char> buffer(num_bytes);
+ memcpy(&buffer[0], data, num_bytes);
+ DCHECK(channel_);
+ channel_->SendFrame(
+ fin, ConvertTo<net::WebSocketFrameHeader::OpCode>(type), buffer);
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/web_socket_impl.h b/mojo/services/network/web_socket_impl.h
new file mode 100644
index 0000000..bd8a27d
--- /dev/null
+++ b/mojo/services/network/web_socket_impl.h
@@ -0,0 +1,56 @@
+// 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_WEB_SOCKET_IMPL_H_
+#define MOJO_SERVICES_NETWORK_WEB_SOCKET_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/web_socket.mojom.h"
+
+namespace net {
+class WebSocketChannel;
+} // namespace net
+
+namespace mojo {
+class NetworkContext;
+class WebSocketReadQueue;
+
+// Forms a bridge between the WebSocket mojo interface and the net::WebSocket
+// implementation.
+class WebSocketImpl : public InterfaceImpl<WebSocket> {
+ public:
+ explicit WebSocketImpl(NetworkContext* context);
+ virtual ~WebSocketImpl();
+
+ private:
+ // WebSocket methods:
+ virtual void Connect(const String& url,
+ Array<String> protocols,
+ const String& origin,
+ ScopedDataPipeConsumerHandle send_stream,
+ WebSocketClientPtr client) OVERRIDE;
+ virtual void Send(bool fin,
+ WebSocket::MessageType type,
+ uint32_t num_bytes) OVERRIDE;
+ virtual void FlowControl(int64_t quota) OVERRIDE;
+ virtual void Close(uint16_t code, const String& reason) OVERRIDE;
+
+ // Called with the data to send once it has been read from |send_stream_|.
+ void DidReadFromSendStream(bool fin,
+ WebSocket::MessageType type,
+ uint32_t num_bytes,
+ const char* data);
+
+ // The channel we use to send events to the network.
+ scoped_ptr<net::WebSocketChannel> channel_;
+ ScopedDataPipeConsumerHandle send_stream_;
+ scoped_ptr<WebSocketReadQueue> read_queue_;
+ NetworkContext* context_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_WEB_SOCKET_IMPL_H_
diff --git a/mojo/services/public/cpp/geometry/BUILD.gn b/mojo/services/public/cpp/geometry/BUILD.gn
new file mode 100644
index 0000000..c87adf3
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_geometry_lib
+component("geometry") {
+ output_name = "mojo_geometry_lib"
+
+ public_deps = [
+ "//ui/gfx",
+ ]
+ deps = [
+ "//skia",
+ "//ui/gfx/geometry",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_component",
+ "//mojo/services/public/interfaces/geometry",
+ ]
+
+ defines = [
+ "MOJO_GEOMETRY_IMPLEMENTATION",
+ ]
+
+ sources = [
+ "lib/geometry_type_converters.cc",
+ "geometry_type_converters.h",
+ "mojo_geometry_export.h",
+ ]
+}
diff --git a/mojo/services/public/cpp/geometry/DEPS b/mojo/services/public/cpp/geometry/DEPS
new file mode 100644
index 0000000..3e03810
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+ui/gfx/geometry",
+ "+ui/gfx/transform.h",
+]
diff --git a/mojo/services/public/cpp/geometry/geometry_type_converters.h b/mojo/services/public/cpp/geometry/geometry_type_converters.h
new file mode 100644
index 0000000..2f2cc1b
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/geometry_type_converters.h
@@ -0,0 +1,74 @@
+// 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/point.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<PointPtr, gfx::Point> {
+ static PointPtr Convert(const gfx::Point& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Point, PointPtr> {
+ static gfx::Point Convert(const PointPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<PointFPtr, gfx::PointF> {
+ static PointFPtr Convert(const gfx::PointF& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::PointF, PointFPtr> {
+ static gfx::PointF Convert(const PointFPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<SizePtr, gfx::Size> {
+ static SizePtr Convert(const gfx::Size& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Size, SizePtr> {
+ static gfx::Size Convert(const SizePtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<RectPtr, gfx::Rect> {
+ static RectPtr Convert(const gfx::Rect& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Rect, RectPtr> {
+ static gfx::Rect Convert(const RectPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<RectFPtr, gfx::RectF> {
+ static RectFPtr Convert(const gfx::RectF& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::RectF, RectFPtr> {
+ static gfx::RectF Convert(const RectFPtr& input);
+};
+
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<TransformPtr, gfx::Transform> {
+ static TransformPtr Convert(const gfx::Transform& input);
+};
+template <>
+struct MOJO_GEOMETRY_EXPORT TypeConverter<gfx::Transform, TransformPtr> {
+ static gfx::Transform Convert(const TransformPtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
diff --git a/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc b/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc
new file mode 100644
index 0000000..9a40c15
--- /dev/null
+++ b/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc
@@ -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.
+
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+
+namespace mojo {
+
+// static
+PointPtr TypeConverter<PointPtr, gfx::Point>::Convert(const gfx::Point& input) {
+ PointPtr point(Point::New());
+ point->x = input.x();
+ point->y = input.y();
+ return point.Pass();
+}
+
+// static
+gfx::Point TypeConverter<gfx::Point, PointPtr>::Convert(const PointPtr& input) {
+ if (input.is_null())
+ return gfx::Point();
+ return gfx::Point(input->x, input->y);
+}
+
+// static
+PointFPtr TypeConverter<PointFPtr, gfx::PointF>::Convert(
+ const gfx::PointF& input) {
+ PointFPtr point(PointF::New());
+ point->x = input.x();
+ point->y = input.y();
+ return point.Pass();
+}
+
+// static
+gfx::PointF TypeConverter<gfx::PointF, PointFPtr>::Convert(
+ const PointFPtr& input) {
+ if (input.is_null())
+ return gfx::PointF();
+ return gfx::PointF(input->x, input->y);
+}
+
+// static
+SizePtr TypeConverter<SizePtr, gfx::Size>::Convert(const gfx::Size& input) {
+ SizePtr size(Size::New());
+ size->width = input.width();
+ size->height = input.height();
+ return size.Pass();
+}
+
+// static
+gfx::Size TypeConverter<gfx::Size, SizePtr>::Convert(const SizePtr& input) {
+ if (input.is_null())
+ return gfx::Size();
+ return gfx::Size(input->width, input->height);
+}
+
+// static
+RectPtr TypeConverter<RectPtr, gfx::Rect>::Convert(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<gfx::Rect, RectPtr>::Convert(const RectPtr& input) {
+ if (input.is_null())
+ return gfx::Rect();
+ return gfx::Rect(input->x, input->y, input->width, input->height);
+}
+
+// static
+RectFPtr TypeConverter<RectFPtr, gfx::RectF>::Convert(const gfx::RectF& input) {
+ RectFPtr rect(RectF::New());
+ rect->x = input.x();
+ rect->y = input.y();
+ rect->width = input.width();
+ rect->height = input.height();
+ return rect.Pass();
+}
+
+// static
+gfx::RectF TypeConverter<gfx::RectF, RectFPtr>::Convert(const RectFPtr& input) {
+ if (input.is_null())
+ return gfx::RectF();
+ return gfx::RectF(input->x, input->y, input->width, input->height);
+}
+
+// static
+TransformPtr TypeConverter<TransformPtr, gfx::Transform>::Convert(
+ const gfx::Transform& input) {
+ std::vector<float> storage(16);
+ input.matrix().asRowMajorf(&storage[0]);
+ mojo::Array<float> matrix;
+ matrix.Swap(&storage);
+ TransformPtr transform(Transform::New());
+ transform->matrix = matrix.Pass();
+ return transform.Pass();
+}
+
+// static
+gfx::Transform TypeConverter<gfx::Transform, TransformPtr>::Convert(
+ const TransformPtr& input) {
+ if (input.is_null())
+ return gfx::Transform();
+ gfx::Transform transform(gfx::Transform::kSkipInitialization);
+ transform.matrix().setRowMajorf(&input->matrix.storage()[0]);
+ return transform;
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/geometry/mojo_geometry_export.h b/mojo/services/public/cpp/geometry/mojo_geometry_export.h
new file mode 100644
index 0000000..f21aebc
--- /dev/null
+++ b/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/mojo/services/public/cpp/input_events/BUILD.gn b/mojo/services/public/cpp/input_events/BUILD.gn
new file mode 100644
index 0000000..2b47ece
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/BUILD.gn
@@ -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.
+
+component("input_events") {
+ sources = [
+ "lib/input_events_type_converters.cc",
+ "lib/mojo_extended_key_event_data.cc",
+ "lib/mojo_extended_key_event_data.h",
+ "mojo_input_events_export.h",
+ ]
+
+ defines = [
+ "MOJO_INPUT_EVENTS_IMPLEMENTATION",
+ ]
+
+ deps = [
+ "//base",
+ "//ui/events",
+ "//ui/gfx/geometry",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_component",
+ "//mojo/services/public/interfaces/input_events",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/cpp/geometry",
+ ]
+}
diff --git a/mojo/services/public/cpp/input_events/DEPS b/mojo/services/public/cpp/input_events/DEPS
new file mode 100644
index 0000000..fe1d98e
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/events",
+]
diff --git a/mojo/services/public/cpp/input_events/input_events_type_converters.h b/mojo/services/public/cpp/input_events/input_events_type_converters.h
new file mode 100644
index 0000000..bbbbe10
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/input_events_type_converters.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_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 <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventType, ui::EventType> {
+ static EventType Convert(ui::EventType type);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<ui::EventType, EventType> {
+ static ui::EventType Convert(EventType type);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, ui::Event> {
+ static EventPtr Convert(const ui::Event& input);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, ui::KeyEvent> {
+ static EventPtr Convert(const ui::KeyEvent& input);
+};
+
+template <>
+struct MOJO_INPUT_EVENTS_EXPORT TypeConverter<scoped_ptr<ui::Event>, EventPtr> {
+ static scoped_ptr<ui::Event> Convert(const EventPtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/services/public/cpp/input_events/lib/input_event_names.h b/mojo/services/public/cpp/input_events/lib/input_event_names.h
new file mode 100644
index 0000000..3fd6ef6
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/input_event_names.h
@@ -0,0 +1,46 @@
+// 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.
+
+MOJO_INPUT_EVENT_NAME(UNKNOWN);
+MOJO_INPUT_EVENT_NAME(MOUSE_PRESSED);
+MOJO_INPUT_EVENT_NAME(MOUSE_DRAGGED);
+MOJO_INPUT_EVENT_NAME(MOUSE_RELEASED);
+MOJO_INPUT_EVENT_NAME(MOUSE_MOVED);
+MOJO_INPUT_EVENT_NAME(MOUSE_ENTERED);
+MOJO_INPUT_EVENT_NAME(MOUSE_EXITED);
+MOJO_INPUT_EVENT_NAME(KEY_PRESSED);
+MOJO_INPUT_EVENT_NAME(KEY_RELEASED);
+MOJO_INPUT_EVENT_NAME(MOUSEWHEEL);
+MOJO_INPUT_EVENT_NAME(MOUSE_CAPTURE_CHANGED);
+MOJO_INPUT_EVENT_NAME(TOUCH_RELEASED);
+MOJO_INPUT_EVENT_NAME(TOUCH_PRESSED);
+MOJO_INPUT_EVENT_NAME(TOUCH_MOVED);
+MOJO_INPUT_EVENT_NAME(TOUCH_CANCELLED);
+MOJO_INPUT_EVENT_NAME(DROP_TARGET_EVENT);
+MOJO_INPUT_EVENT_NAME(TRANSLATED_KEY_PRESS);
+MOJO_INPUT_EVENT_NAME(TRANSLATED_KEY_RELEASE);
+MOJO_INPUT_EVENT_NAME(GESTURE_SCROLL_BEGIN);
+MOJO_INPUT_EVENT_NAME(GESTURE_SCROLL_END);
+MOJO_INPUT_EVENT_NAME(GESTURE_SCROLL_UPDATE);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP_DOWN);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP_CANCEL);
+MOJO_INPUT_EVENT_NAME(GESTURE_TAP_UNCONFIRMED);
+MOJO_INPUT_EVENT_NAME(GESTURE_DOUBLE_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_BEGIN);
+MOJO_INPUT_EVENT_NAME(GESTURE_END);
+MOJO_INPUT_EVENT_NAME(GESTURE_TWO_FINGER_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_PINCH_BEGIN);
+MOJO_INPUT_EVENT_NAME(GESTURE_PINCH_END);
+MOJO_INPUT_EVENT_NAME(GESTURE_PINCH_UPDATE);
+MOJO_INPUT_EVENT_NAME(GESTURE_LONG_PRESS);
+MOJO_INPUT_EVENT_NAME(GESTURE_LONG_TAP);
+MOJO_INPUT_EVENT_NAME(GESTURE_SWIPE);
+MOJO_INPUT_EVENT_NAME(GESTURE_SHOW_PRESS);
+MOJO_INPUT_EVENT_NAME(GESTURE_WIN8_EDGE_SWIPE);
+MOJO_INPUT_EVENT_NAME(SCROLL);
+MOJO_INPUT_EVENT_NAME(SCROLL_FLING_START);
+MOJO_INPUT_EVENT_NAME(SCROLL_FLING_CANCEL);
+MOJO_INPUT_EVENT_NAME(CANCEL_MODE);
+MOJO_INPUT_EVENT_NAME(UMA_DATA);
diff --git a/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc b/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc
new file mode 100644
index 0000000..340e200
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc
@@ -0,0 +1,245 @@
+// 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"
+
+#if defined(USE_X11)
+#include <X11/extensions/XInput2.h>
+#include <X11/Xlib.h>
+#endif
+
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace mojo {
+
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_NONE) ==
+ static_cast<int32>(ui::EF_NONE),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_CAPS_LOCK_DOWN) ==
+ static_cast<int32>(ui::EF_CAPS_LOCK_DOWN),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_SHIFT_DOWN) ==
+ static_cast<int32>(ui::EF_SHIFT_DOWN),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_CONTROL_DOWN) ==
+ static_cast<int32>(ui::EF_CONTROL_DOWN),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_ALT_DOWN) ==
+ static_cast<int32>(ui::EF_ALT_DOWN),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_LEFT_MOUSE_BUTTON) ==
+ static_cast<int32>(ui::EF_LEFT_MOUSE_BUTTON),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_MIDDLE_MOUSE_BUTTON) ==
+ static_cast<int32>(ui::EF_MIDDLE_MOUSE_BUTTON),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_RIGHT_MOUSE_BUTTON) ==
+ static_cast<int32>(ui::EF_RIGHT_MOUSE_BUTTON),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_COMMAND_DOWN) ==
+ static_cast<int32>(ui::EF_COMMAND_DOWN),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_EXTENDED) ==
+ static_cast<int32>(ui::EF_EXTENDED),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_IS_SYNTHESIZED) ==
+ static_cast<int32>(ui::EF_IS_SYNTHESIZED),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_ALTGR_DOWN) ==
+ static_cast<int32>(ui::EF_ALTGR_DOWN),
+ event_flags_should_match);
+COMPILE_ASSERT(static_cast<int32>(EVENT_FLAGS_MOD3_DOWN) ==
+ static_cast<int32>(ui::EF_MOD3_DOWN),
+ event_flags_should_match);
+
+
+// static
+EventType TypeConverter<EventType, ui::EventType>::Convert(ui::EventType type) {
+#define MOJO_INPUT_EVENT_NAME(name) case ui::ET_##name: return EVENT_TYPE_##name
+
+ switch (type) {
+#include "mojo/services/public/cpp/input_events/lib/input_event_names.h"
+ case ui::ET_LAST:
+ NOTREACHED();
+ break;
+ }
+
+#undef MOJO_INPUT_EVENT_NAME
+
+ NOTREACHED();
+ return EVENT_TYPE_UNKNOWN;
+}
+
+// static
+ui::EventType TypeConverter<ui::EventType, EventType>::Convert(EventType type) {
+#define MOJO_INPUT_EVENT_NAME(name) case EVENT_TYPE_##name: return ui::ET_##name
+
+ switch (type) {
+#include "mojo/services/public/cpp/input_events/lib/input_event_names.h"
+ }
+
+#undef MOJO_INPUT_EVENT_NAME
+
+ NOTREACHED();
+ return ui::ET_UNKNOWN;
+}
+
+// static
+EventPtr TypeConverter<EventPtr, ui::Event>::Convert(const ui::Event& input) {
+ EventPtr event(Event::New());
+ event->action = ConvertTo<EventType>(input.type());
+ event->flags = EventFlags(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);
+
+ LocationDataPtr location_data(LocationData::New());
+ location_data->in_view_location = Point::From(located_event->location());
+ if (input.HasNativeEvent()) {
+ location_data->screen_location =
+ Point::From(ui::EventSystemLocationFromNative(input.native_event()));
+ }
+
+ event->location_data = location_data.Pass();
+ }
+
+ 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->GetConflatedWindowsKeyCode();
+ key_data->native_key_code = key_event->platform_keycode();
+ key_data->is_char = key_event->is_char();
+ key_data->character = key_event->GetCharacter();
+
+ if (key_event->extended_key_event_data()) {
+ const MojoExtendedKeyEventData* data =
+ static_cast<const MojoExtendedKeyEventData*>(
+ key_event->extended_key_event_data());
+ key_data->windows_key_code = static_cast<mojo::KeyboardCode>(
+ data->windows_key_code());
+ key_data->text = data->text();
+ key_data->unmodified_text = data->unmodified_text();
+ } else {
+ key_data->windows_key_code = static_cast<mojo::KeyboardCode>(
+ key_event->GetLocatedWindowsKeyboardCode());
+ key_data->text = key_event->GetText();
+ key_data->unmodified_text = key_event->GetUnmodifiedText();
+ }
+
+ event->key_data = key_data.Pass();
+ } else if (input.IsMouseWheelEvent()) {
+ const ui::MouseWheelEvent* wheel_event =
+ static_cast<const ui::MouseWheelEvent*>(&input);
+ MouseWheelDataPtr wheel_data(MouseWheelData::New());
+ wheel_data->x_offset = wheel_event->x_offset();
+ wheel_data->y_offset = wheel_event->y_offset();
+ event->wheel_data = wheel_data.Pass();
+ }
+ return event.Pass();
+}
+
+// static
+EventPtr TypeConverter<EventPtr, ui::KeyEvent>::Convert(
+ const ui::KeyEvent& input) {
+ return Event::From(static_cast<const ui::Event&>(input));
+}
+
+// static
+scoped_ptr<ui::Event> TypeConverter<scoped_ptr<ui::Event>, EventPtr>::Convert(
+ const EventPtr& input) {
+ scoped_ptr<ui::Event> ui_event;
+ ui::EventType ui_event_type = ConvertTo<ui::EventType>(input->action);
+
+ gfx::Point location;
+ if (!input->location_data.is_null() &&
+ !input->location_data->in_view_location.is_null()) {
+ location = input->location_data->in_view_location.To<gfx::Point>();
+ }
+
+ switch (input->action) {
+ case ui::ET_KEY_PRESSED:
+ case ui::ET_KEY_RELEASED: {
+ scoped_ptr<ui::KeyEvent> key_event;
+ if (input->key_data->is_char) {
+ key_event.reset(new ui::KeyEvent(
+ static_cast<base::char16>(input->key_data->character),
+ static_cast<ui::KeyboardCode>(
+ input->key_data->key_code),
+ input->flags));
+ } else {
+ key_event.reset(new ui::KeyEvent(
+ ui_event_type,
+ static_cast<ui::KeyboardCode>(
+ input->key_data->key_code),
+ input->flags));
+ }
+ key_event->SetExtendedKeyEventData(scoped_ptr<ui::ExtendedKeyEventData>(
+ new MojoExtendedKeyEventData(
+ static_cast<int32_t>(input->key_data->windows_key_code),
+ input->key_data->text,
+ input->key_data->unmodified_text)));
+ key_event->set_platform_keycode(input->key_data->native_key_code);
+ ui_event = key_event.PassAs<ui::KeyEvent>();
+ break;
+ }
+ case EVENT_TYPE_MOUSE_PRESSED:
+ case EVENT_TYPE_MOUSE_DRAGGED:
+ case EVENT_TYPE_MOUSE_RELEASED:
+ case EVENT_TYPE_MOUSE_MOVED:
+ case EVENT_TYPE_MOUSE_ENTERED:
+ case EVENT_TYPE_MOUSE_EXITED: {
+ // TODO: last flags isn't right. Need to send changed_flags.
+ ui_event.reset(new ui::MouseEvent(
+ ui_event_type,
+ location,
+ location,
+ ui::EventFlags(input->flags),
+ ui::EventFlags(input->flags)));
+ break;
+ }
+ case EVENT_TYPE_MOUSEWHEEL: {
+ const gfx::Vector2d offset(input->wheel_data->x_offset,
+ input->wheel_data->y_offset);
+ ui_event.reset(new ui::MouseWheelEvent(offset,
+ location,
+ location,
+ ui::EventFlags(input->flags),
+ ui::EventFlags(input->flags)));
+ break;
+ }
+ case EVENT_TYPE_TOUCH_MOVED:
+ case EVENT_TYPE_TOUCH_PRESSED:
+ case EVENT_TYPE_TOUCH_CANCELLED:
+ case EVENT_TYPE_TOUCH_RELEASED: {
+ ui_event.reset(new ui::TouchEvent(
+ ui_event_type,
+ location,
+ ui::EventFlags(input->flags),
+ input->touch_data->pointer_id,
+ base::TimeDelta::FromInternalValue(input->time_stamp),
+ 0.f, 0.f, 0.f, 0.f));
+ break;
+ }
+ default:
+ // TODO: support other types.
+ // NOTIMPLEMENTED();
+ ;
+ }
+ // TODO: need to support time_stamp.
+ return ui_event.Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc
new file mode 100644
index 0000000..fa93751
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.cc
@@ -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.
+
+#include "mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h"
+
+namespace mojo {
+
+MojoExtendedKeyEventData::MojoExtendedKeyEventData(int32_t windows_key_code,
+ uint16_t text,
+ uint16_t unmodified_text)
+ : windows_key_code_(windows_key_code),
+ text_(text),
+ unmodified_text_(unmodified_text) {
+}
+
+MojoExtendedKeyEventData::~MojoExtendedKeyEventData() {}
+
+ui::ExtendedKeyEventData* MojoExtendedKeyEventData::Clone() const {
+ return new MojoExtendedKeyEventData(windows_key_code_,
+ text_,
+ unmodified_text_);
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h
new file mode 100644
index 0000000..cd5be0f
--- /dev/null
+++ b/mojo/services/public/cpp/input_events/lib/mojo_extended_key_event_data.h
@@ -0,0 +1,39 @@
+// 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_LIB_MOJO_EXTENDED_KEY_EVENT_DATA_H_
+#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_LIB_MOJO_EXTENDED_KEY_EVENT_DATA_H_
+
+#include "ui/events/event.h"
+#include "mojo/services/public/cpp/input_events/mojo_input_events_export.h"
+
+namespace mojo {
+
+// A structure to store all mojo specific data on a KeyEvent.
+class MOJO_INPUT_EVENTS_EXPORT MojoExtendedKeyEventData
+ : public ui::ExtendedKeyEventData {
+ public:
+ MojoExtendedKeyEventData(int32_t windows_key_code,
+ uint16_t text,
+ uint16_t unmodified_text);
+ virtual ~MojoExtendedKeyEventData();
+
+ int32_t windows_key_code() const { return windows_key_code_; }
+ uint16_t text() const { return text_; }
+ uint16_t unmodified_text() const { return unmodified_text_; }
+
+ // ui::ExtendedKeyEventData:
+ virtual ui::ExtendedKeyEventData* Clone() const OVERRIDE;
+
+ private:
+ const int32_t windows_key_code_;
+ const uint16_t text_;
+ const uint16_t unmodified_text_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoExtendedKeyEventData);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_LIB_MOJO_EXTENDED_KEY_EVENT_DATA_H_
diff --git a/mojo/services/public/cpp/input_events/mojo_input_events_export.h b/mojo/services/public/cpp/input_events/mojo_input_events_export.h
new file mode 100644
index 0000000..d7b193e
--- /dev/null
+++ b/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/mojo/services/public/cpp/network/BUILD.gn b/mojo/services/public/cpp/network/BUILD.gn
new file mode 100644
index 0000000..81e64b4
--- /dev/null
+++ b/mojo/services/public/cpp/network/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_utility
+source_set("network") {
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_component",
+ ]
+
+ sources = [
+ "web_socket_read_queue.cc",
+ "web_socket_read_queue.h",
+ "web_socket_write_queue.cc",
+ "web_socket_write_queue.h",
+ ]
+}
diff --git a/mojo/services/public/cpp/network/web_socket_read_queue.cc b/mojo/services/public/cpp/network/web_socket_read_queue.cc
new file mode 100644
index 0000000..66b5a66
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_read_queue.cc
@@ -0,0 +1,70 @@
+// 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/network/web_socket_read_queue.h"
+
+#include "base/bind.h"
+
+namespace mojo {
+
+struct WebSocketReadQueue::Operation {
+ uint32_t num_bytes_;
+ base::Callback<void(const char*)> callback_;
+};
+
+WebSocketReadQueue::WebSocketReadQueue(DataPipeConsumerHandle handle)
+ : handle_(handle), is_waiting_(false) {
+}
+
+WebSocketReadQueue::~WebSocketReadQueue() {
+}
+
+void WebSocketReadQueue::Read(uint32_t num_bytes,
+ base::Callback<void(const char*)> callback) {
+ Operation* op = new Operation;
+ op->num_bytes_ = num_bytes;
+ op->callback_ = callback;
+ queue_.push_back(op);
+
+ if (!is_waiting_)
+ TryToRead();
+}
+
+void WebSocketReadQueue::TryToRead() {
+ Operation* op = queue_[0];
+ const void* buffer = NULL;
+ uint32_t bytes_read = op->num_bytes_;
+ MojoResult result = BeginReadDataRaw(
+ handle_, &buffer, &bytes_read, MOJO_READ_DATA_FLAG_ALL_OR_NONE);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ EndReadDataRaw(handle_, bytes_read);
+ Wait();
+ return;
+ }
+
+ // Ensure |op| is deleted, whether or not |this| goes away.
+ scoped_ptr<Operation> op_deleter(op);
+ queue_.weak_erase(queue_.begin());
+ if (result != MOJO_RESULT_OK)
+ return;
+ DataPipeConsumerHandle handle = handle_;
+ op->callback_.Run(static_cast<const char*>(buffer)); // may delete |this|
+ EndReadDataRaw(handle, bytes_read);
+}
+
+void WebSocketReadQueue::Wait() {
+ is_waiting_ = true;
+ handle_watcher_.Start(
+ handle_,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&WebSocketReadQueue::OnHandleReady, base::Unretained(this)));
+}
+
+void WebSocketReadQueue::OnHandleReady(MojoResult result) {
+ is_waiting_ = false;
+ TryToRead();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/network/web_socket_read_queue.h b/mojo/services/public/cpp/network/web_socket_read_queue.h
new file mode 100644
index 0000000..6108e49
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_read_queue.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_PUBLIC_CPP_NETWORK_WEB_SOCKET_READ_QUEUE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_READ_QUEUE_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+
+namespace mojo {
+
+// This class simplifies the handling of multiple Reads on a DataPipe. It reads
+// the data in the expected chunk size, calling the callback once a full chunk
+// is ready. Callbacks are owned by this class, and are guaranteed not to be
+// called after this class is destroyed.
+// See also: WebSocketWriteQueue
+class WebSocketReadQueue {
+ public:
+ WebSocketReadQueue(DataPipeConsumerHandle handle);
+ ~WebSocketReadQueue();
+
+ void Read(uint32_t num_bytes, base::Callback<void(const char*)> callback);
+
+ private:
+ struct Operation;
+
+ void TryToRead();
+ void Wait();
+ void OnHandleReady(MojoResult result);
+
+ DataPipeConsumerHandle handle_;
+ common::HandleWatcher handle_watcher_;
+ ScopedVector<Operation> queue_;
+ bool is_waiting_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_READ_QUEUE_H_
diff --git a/mojo/services/public/cpp/network/web_socket_write_queue.cc b/mojo/services/public/cpp/network/web_socket_write_queue.cc
new file mode 100644
index 0000000..fe3f674
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_write_queue.cc
@@ -0,0 +1,84 @@
+// 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/network/web_socket_write_queue.h"
+
+#include "base/bind.h"
+
+namespace mojo {
+
+struct WebSocketWriteQueue::Operation {
+ uint32_t num_bytes_;
+ base::Callback<void(const char*)> callback_;
+
+ const char* data_;
+ // Only initialized if the initial Write fails. This saves a copy in
+ // the common case.
+ std::vector<char> data_copy_;
+};
+
+WebSocketWriteQueue::WebSocketWriteQueue(DataPipeProducerHandle handle)
+ : handle_(handle), is_waiting_(false) {
+}
+
+WebSocketWriteQueue::~WebSocketWriteQueue() {
+}
+
+void WebSocketWriteQueue::Write(const char* data,
+ uint32_t num_bytes,
+ base::Callback<void(const char*)> callback) {
+ Operation* op = new Operation;
+ op->num_bytes_ = num_bytes;
+ op->callback_ = callback;
+ op->data_ = data;
+ queue_.push_back(op);
+
+ MojoResult result = MOJO_RESULT_SHOULD_WAIT;
+ if (!is_waiting_)
+ result = TryToWrite();
+
+ // If we have to wait, make a local copy of the data so we know it will
+ // live until we need it.
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ op->data_copy_.resize(num_bytes);
+ memcpy(&op->data_copy_[0], data, num_bytes);
+ op->data_ = &op->data_copy_[0];
+ }
+}
+
+MojoResult WebSocketWriteQueue::TryToWrite() {
+ Operation* op = queue_[0];
+ uint32_t bytes_written = op->num_bytes_;
+ MojoResult result = WriteDataRaw(
+ handle_, op->data_, &bytes_written, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ Wait();
+ return result;
+ }
+
+ // Ensure |op| is deleted, whether or not |this| goes away.
+ scoped_ptr<Operation> op_deleter(op);
+ queue_.weak_erase(queue_.begin());
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ op->callback_.Run(op->data_); // may delete |this|
+ return result;
+}
+
+void WebSocketWriteQueue::Wait() {
+ is_waiting_ = true;
+ handle_watcher_.Start(handle_,
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&WebSocketWriteQueue::OnHandleReady,
+ base::Unretained(this)));
+}
+
+void WebSocketWriteQueue::OnHandleReady(MojoResult result) {
+ is_waiting_ = false;
+ TryToWrite();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/network/web_socket_write_queue.h b/mojo/services/public/cpp/network/web_socket_write_queue.h
new file mode 100644
index 0000000..7d5f5fa
--- /dev/null
+++ b/mojo/services/public/cpp/network/web_socket_write_queue.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_NETWORK_WEB_SOCKET_WRITE_QUEUE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_WRITE_QUEUE_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+
+namespace mojo {
+
+// This class simplifies the handling of multiple Writes on a DataPipe. It
+// writes each chunk all at once (or waits until the pipe is ready before
+// writing), calling the callback when finished. Callbacks are owned by this
+// class, and are guaranteed not to be called after this class is destroyed.
+// See also: WebSocketReadQueue
+class WebSocketWriteQueue {
+ public:
+ WebSocketWriteQueue(DataPipeProducerHandle handle);
+ ~WebSocketWriteQueue();
+
+ void Write(const char* data,
+ uint32_t num_bytes,
+ base::Callback<void(const char*)> callback);
+
+ private:
+ struct Operation;
+
+ MojoResult TryToWrite();
+ void Wait();
+ void OnHandleReady(MojoResult result);
+
+ DataPipeProducerHandle handle_;
+ common::HandleWatcher handle_watcher_;
+ ScopedVector<Operation> queue_;
+ bool is_waiting_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_NETWORK_WEB_SOCKET_WRITE_QUEUE_H_
diff --git a/mojo/services/public/cpp/surfaces/BUILD.gn b/mojo/services/public/cpp/surfaces/BUILD.gn
new file mode 100644
index 0000000..5bd6729
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_lib
+component("surfaces") {
+ output_name = "mojo_surfaces_lib"
+
+ sources = [
+ "lib/surfaces_type_converters.cc",
+ "lib/surfaces_utils.cc",
+ "mojo_surfaces_export.h",
+ "surfaces_type_converters.h",
+ "surfaces_utils.h",
+ ]
+
+ defines = [ "MOJO_SURFACES_IMPLEMENTATION" ]
+
+ public_deps = [
+ "//mojo/services/public/cpp/geometry",
+ "//ui/gfx",
+ ]
+
+ deps = [
+ "//base",
+ "//cc",
+ "//cc/surfaces",
+ "//gpu",
+ "//ui/gfx/geometry",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_component",
+ "//mojo/services/public/interfaces/surfaces:surface_id",
+ "//mojo/services/public/interfaces/surfaces",
+ ]
+}
diff --git a/mojo/services/public/cpp/surfaces/DEPS b/mojo/services/public/cpp/surfaces/DEPS
new file mode 100644
index 0000000..bce2d34
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+cc/output",
+ "+cc/quads",
+ "+cc/resources",
+ "+cc/surfaces",
+ "+gpu/command_buffer/common/mailbox.h",
+ "+gpu/command_buffer/common/mailbox_holder.h",
+ "+third_party/skia/include",
+ "+ui/gfx/geometry",
+ "+ui/gfx/transform.h",
+]
diff --git a/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc b/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc
new file mode 100644
index 0000000..7d1798e
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc
@@ -0,0 +1,576 @@
+// 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/surfaces/surfaces_type_converters.h"
+
+#include "base/macros.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/draw_quad.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/quads/tile_draw_quad.h"
+#include "cc/quads/yuv_video_draw_quad.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+
+namespace mojo {
+
+#define ASSERT_ENUM_VALUES_EQUAL(value) \
+ COMPILE_ASSERT(cc::DrawQuad::value == static_cast<cc::DrawQuad::Material>( \
+ MATERIAL_##value), \
+ value##_enum_value_matches)
+
+ASSERT_ENUM_VALUES_EQUAL(CHECKERBOARD);
+ASSERT_ENUM_VALUES_EQUAL(DEBUG_BORDER);
+ASSERT_ENUM_VALUES_EQUAL(IO_SURFACE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(PICTURE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(RENDER_PASS);
+ASSERT_ENUM_VALUES_EQUAL(SOLID_COLOR);
+ASSERT_ENUM_VALUES_EQUAL(STREAM_VIDEO_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(SURFACE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(TEXTURE_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(TILED_CONTENT);
+ASSERT_ENUM_VALUES_EQUAL(YUV_VIDEO_CONTENT);
+
+COMPILE_ASSERT(
+ cc::YUVVideoDrawQuad::REC_601 ==
+ static_cast<cc::YUVVideoDrawQuad::ColorSpace>(YUV_COLOR_SPACE_REC_601),
+ rec_601_enum_matches);
+COMPILE_ASSERT(cc::YUVVideoDrawQuad::REC_601_JPEG ==
+ static_cast<cc::YUVVideoDrawQuad::ColorSpace>(
+ YUV_COLOR_SPACE_REC_601_JPEG),
+ rec_601_jpeg_enum_matches);
+
+namespace {
+
+cc::SharedQuadState* ConvertSharedQuadState(const SharedQuadStatePtr& input,
+ cc::RenderPass* render_pass) {
+ cc::SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
+ state->SetAll(input->content_to_target_transform.To<gfx::Transform>(),
+ input->content_bounds.To<gfx::Size>(),
+ input->visible_content_rect.To<gfx::Rect>(),
+ input->clip_rect.To<gfx::Rect>(),
+ input->is_clipped,
+ input->opacity,
+ static_cast<::SkXfermode::Mode>(input->blend_mode),
+ input->sorting_context_id);
+ return state;
+}
+
+bool ConvertDrawQuad(const QuadPtr& input,
+ cc::SharedQuadState* sqs,
+ cc::RenderPass* render_pass) {
+ switch (input->material) {
+ case MATERIAL_RENDER_PASS: {
+ cc::RenderPassDrawQuad* render_pass_quad =
+ render_pass->CreateAndAppendDrawQuad<cc::RenderPassDrawQuad>();
+ RenderPassQuadState* render_pass_quad_state =
+ input->render_pass_quad_state.get();
+ gfx::PointF filter_scale_as_point =
+ render_pass_quad_state->filters_scale.To<gfx::PointF>();
+ render_pass_quad->SetAll(
+ sqs,
+ input->rect.To<gfx::Rect>(),
+ input->opaque_rect.To<gfx::Rect>(),
+ input->visible_rect.To<gfx::Rect>(),
+ input->needs_blending,
+ render_pass_quad_state->render_pass_id.To<cc::RenderPassId>(),
+ render_pass_quad_state->mask_resource_id,
+ render_pass_quad_state->mask_uv_rect.To<gfx::RectF>(),
+ cc::FilterOperations(), // TODO(jamesr): filters
+ gfx::Vector2dF(filter_scale_as_point.x(), filter_scale_as_point.y()),
+ cc::FilterOperations()); // TODO(jamesr): background_filters
+ break;
+ }
+ case MATERIAL_SOLID_COLOR: {
+ if (input->solid_color_quad_state.is_null())
+ return false;
+ cc::SolidColorDrawQuad* color_quad =
+ render_pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ color_quad->SetAll(
+ sqs,
+ input->rect.To<gfx::Rect>(),
+ input->opaque_rect.To<gfx::Rect>(),
+ input->visible_rect.To<gfx::Rect>(),
+ input->needs_blending,
+ input->solid_color_quad_state->color.To<SkColor>(),
+ input->solid_color_quad_state->force_anti_aliasing_off);
+ break;
+ }
+ case MATERIAL_SURFACE_CONTENT: {
+ if (input->surface_quad_state.is_null())
+ return false;
+ cc::SurfaceDrawQuad* surface_quad =
+ render_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ surface_quad->SetAll(
+ sqs,
+ input->rect.To<gfx::Rect>(),
+ input->opaque_rect.To<gfx::Rect>(),
+ input->visible_rect.To<gfx::Rect>(),
+ input->needs_blending,
+ input->surface_quad_state->surface.To<cc::SurfaceId>());
+ break;
+ }
+ case MATERIAL_TEXTURE_CONTENT: {
+ TextureQuadStatePtr& texture_quad_state =
+ input->texture_quad_state;
+ if (texture_quad_state.is_null() ||
+ texture_quad_state->vertex_opacity.is_null() ||
+ texture_quad_state->background_color.is_null())
+ return false;
+ cc::TextureDrawQuad* texture_quad =
+ render_pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+ texture_quad->SetAll(
+ sqs,
+ input->rect.To<gfx::Rect>(),
+ input->opaque_rect.To<gfx::Rect>(),
+ input->visible_rect.To<gfx::Rect>(),
+ input->needs_blending,
+ texture_quad_state->resource_id,
+ texture_quad_state->premultiplied_alpha,
+ texture_quad_state->uv_top_left.To<gfx::PointF>(),
+ texture_quad_state->uv_bottom_right.To<gfx::PointF>(),
+ texture_quad_state->background_color.To<SkColor>(),
+ &texture_quad_state->vertex_opacity.storage()[0],
+ texture_quad_state->flipped);
+ break;
+ }
+ case MATERIAL_TILED_CONTENT: {
+ TileQuadStatePtr& tile_state = input->tile_quad_state;
+ if (tile_state.is_null())
+ return false;
+ cc::TileDrawQuad* tile_quad =
+ render_pass->CreateAndAppendDrawQuad<cc::TileDrawQuad>();
+ tile_quad->SetAll(sqs,
+ input->rect.To<gfx::Rect>(),
+ input->opaque_rect.To<gfx::Rect>(),
+ input->visible_rect.To<gfx::Rect>(),
+ input->needs_blending,
+ tile_state->resource_id,
+ tile_state->tex_coord_rect.To<gfx::RectF>(),
+ tile_state->texture_size.To<gfx::Size>(),
+ tile_state->swizzle_contents);
+ break;
+ }
+ case MATERIAL_YUV_VIDEO_CONTENT: {
+ YUVVideoQuadStatePtr& yuv_state = input->yuv_video_quad_state;
+ if (yuv_state.is_null())
+ return false;
+ cc::YUVVideoDrawQuad* yuv_quad =
+ render_pass->CreateAndAppendDrawQuad<cc::YUVVideoDrawQuad>();
+ yuv_quad->SetAll(sqs,
+ input->rect.To<gfx::Rect>(),
+ input->opaque_rect.To<gfx::Rect>(),
+ input->visible_rect.To<gfx::Rect>(),
+ input->needs_blending,
+ yuv_state->tex_coord_rect.To<gfx::RectF>(),
+ yuv_state->y_plane_resource_id,
+ yuv_state->u_plane_resource_id,
+ yuv_state->v_plane_resource_id,
+ yuv_state->a_plane_resource_id,
+ static_cast<cc::YUVVideoDrawQuad::ColorSpace>(
+ yuv_state->color_space));
+ break;
+ }
+ default:
+ NOTREACHED() << "Unsupported material " << input->material;
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+// static
+SurfaceIdPtr TypeConverter<SurfaceIdPtr, cc::SurfaceId>::Convert(
+ const cc::SurfaceId& input) {
+ SurfaceIdPtr id(SurfaceId::New());
+ id->id = input.id;
+ return id.Pass();
+}
+
+// static
+cc::SurfaceId TypeConverter<cc::SurfaceId, SurfaceIdPtr>::Convert(
+ const SurfaceIdPtr& input) {
+ return cc::SurfaceId(input->id);
+}
+
+// static
+ColorPtr TypeConverter<ColorPtr, SkColor>::Convert(const SkColor& input) {
+ ColorPtr color(Color::New());
+ color->rgba = input;
+ return color.Pass();
+}
+
+// static
+SkColor TypeConverter<SkColor, ColorPtr>::Convert(const ColorPtr& input) {
+ return input->rgba;
+}
+
+// static
+RenderPassIdPtr TypeConverter<RenderPassIdPtr, cc::RenderPassId>::Convert(
+ const cc::RenderPassId& input) {
+ RenderPassIdPtr pass_id(RenderPassId::New());
+ pass_id->layer_id = input.layer_id;
+ pass_id->index = input.index;
+ return pass_id.Pass();
+}
+
+// static
+cc::RenderPassId TypeConverter<cc::RenderPassId, RenderPassIdPtr>::Convert(
+ const RenderPassIdPtr& input) {
+ return cc::RenderPassId(input->layer_id, input->index);
+}
+
+// static
+QuadPtr TypeConverter<QuadPtr, cc::DrawQuad>::Convert(
+ const cc::DrawQuad& input) {
+ QuadPtr quad = Quad::New();
+ quad->material = static_cast<Material>(input.material);
+ quad->rect = Rect::From(input.rect);
+ quad->opaque_rect = Rect::From(input.opaque_rect);
+ quad->visible_rect = Rect::From(input.visible_rect);
+ quad->needs_blending = input.needs_blending;
+ // This is intentionally left set to an invalid value here. It's set when
+ // converting an entire pass since it's an index into the pass' shared quad
+ // state list.
+ quad->shared_quad_state_index = -1;
+ switch (input.material) {
+ case cc::DrawQuad::RENDER_PASS: {
+ const cc::RenderPassDrawQuad* render_pass_quad =
+ cc::RenderPassDrawQuad::MaterialCast(&input);
+ RenderPassQuadStatePtr pass_state = RenderPassQuadState::New();
+ pass_state->render_pass_id =
+ RenderPassId::From(render_pass_quad->render_pass_id);
+ pass_state->mask_resource_id = render_pass_quad->mask_resource_id;
+ pass_state->mask_uv_rect = RectF::From(render_pass_quad->mask_uv_rect);
+ // TODO(jamesr): pass_state->filters
+ pass_state->filters_scale = PointF::From(
+ gfx::PointAtOffsetFromOrigin(render_pass_quad->filters_scale));
+ // TODO(jamesr): pass_state->background_filters
+ quad->render_pass_quad_state = pass_state.Pass();
+ break;
+ }
+ case cc::DrawQuad::SOLID_COLOR: {
+ const cc::SolidColorDrawQuad* color_quad =
+ cc::SolidColorDrawQuad::MaterialCast(&input);
+ SolidColorQuadStatePtr color_state = SolidColorQuadState::New();
+ color_state->color = Color::From(color_quad->color);
+ color_state->force_anti_aliasing_off =
+ color_quad->force_anti_aliasing_off;
+ quad->solid_color_quad_state = color_state.Pass();
+ break;
+ }
+ case cc::DrawQuad::SURFACE_CONTENT: {
+ const cc::SurfaceDrawQuad* surface_quad =
+ cc::SurfaceDrawQuad::MaterialCast(&input);
+ SurfaceQuadStatePtr surface_state =
+ SurfaceQuadState::New();
+ surface_state->surface = SurfaceId::From(surface_quad->surface_id);
+ quad->surface_quad_state = surface_state.Pass();
+ break;
+ }
+ case cc::DrawQuad::TEXTURE_CONTENT: {
+ const cc::TextureDrawQuad* texture_quad =
+ cc::TextureDrawQuad::MaterialCast(&input);
+ TextureQuadStatePtr texture_state = TextureQuadState::New();
+ texture_state->resource_id = texture_quad->resource_id;
+ texture_state->premultiplied_alpha = texture_quad->premultiplied_alpha;
+ texture_state->uv_top_left = PointF::From(texture_quad->uv_top_left);
+ texture_state->uv_bottom_right =
+ PointF::From(texture_quad->uv_bottom_right);
+ texture_state->background_color =
+ Color::From(texture_quad->background_color);
+ Array<float> vertex_opacity(4);
+ for (size_t i = 0; i < 4; ++i) {
+ vertex_opacity[i] = texture_quad->vertex_opacity[i];
+ }
+ texture_state->vertex_opacity = vertex_opacity.Pass();
+ texture_state->flipped = texture_quad->flipped;
+ quad->texture_quad_state = texture_state.Pass();
+ break;
+ }
+ case cc::DrawQuad::TILED_CONTENT: {
+ const cc::TileDrawQuad* tile_quad =
+ cc::TileDrawQuad::MaterialCast(&input);
+ TileQuadStatePtr tile_state = TileQuadState::New();
+ tile_state->tex_coord_rect = RectF::From(tile_quad->tex_coord_rect);
+ tile_state->texture_size = Size::From(tile_quad->texture_size);
+ tile_state->swizzle_contents = tile_quad->swizzle_contents;
+ tile_state->resource_id = tile_quad->resource_id;
+ quad->tile_quad_state = tile_state.Pass();
+ break;
+ }
+ case cc::DrawQuad::YUV_VIDEO_CONTENT: {
+ const cc::YUVVideoDrawQuad* yuv_quad =
+ cc::YUVVideoDrawQuad::MaterialCast(&input);
+ YUVVideoQuadStatePtr yuv_state = YUVVideoQuadState::New();
+ yuv_state->tex_coord_rect = RectF::From(yuv_quad->tex_coord_rect);
+ yuv_state->y_plane_resource_id = yuv_quad->y_plane_resource_id;
+ yuv_state->u_plane_resource_id = yuv_quad->u_plane_resource_id;
+ yuv_state->v_plane_resource_id = yuv_quad->v_plane_resource_id;
+ yuv_state->a_plane_resource_id = yuv_quad->a_plane_resource_id;
+ yuv_state->color_space =
+ static_cast<YUVColorSpace>(yuv_quad->color_space);
+ quad->yuv_video_quad_state = yuv_state.Pass();
+ break;
+ }
+
+ default:
+ NOTREACHED() << "Unsupported material " << input.material;
+ }
+ return quad.Pass();
+}
+
+// static
+SharedQuadStatePtr
+TypeConverter<SharedQuadStatePtr, cc::SharedQuadState>::Convert(
+ const cc::SharedQuadState& input) {
+ SharedQuadStatePtr state = SharedQuadState::New();
+ state->content_to_target_transform =
+ Transform::From(input.content_to_target_transform);
+ state->content_bounds = Size::From(input.content_bounds);
+ state->visible_content_rect = Rect::From(input.visible_content_rect);
+ state->clip_rect = Rect::From(input.clip_rect);
+ state->is_clipped = input.is_clipped;
+ state->opacity = input.opacity;
+ state->blend_mode = static_cast<SkXfermode>(input.blend_mode);
+ state->sorting_context_id = input.sorting_context_id;
+ return state.Pass();
+}
+
+// static
+PassPtr TypeConverter<PassPtr, cc::RenderPass>::Convert(
+ const cc::RenderPass& input) {
+ PassPtr pass = Pass::New();
+ pass->id = input.id.index;
+ pass->output_rect = Rect::From(input.output_rect);
+ pass->damage_rect = Rect::From(input.damage_rect);
+ pass->transform_to_root_target =
+ Transform::From(input.transform_to_root_target);
+ pass->has_transparent_background = input.has_transparent_background;
+ Array<QuadPtr> quads(input.quad_list.size());
+ Array<SharedQuadStatePtr> shared_quad_state(
+ input.shared_quad_state_list.size());
+ int sqs_i = -1;
+ const cc::SharedQuadState* last_sqs = NULL;
+ size_t i = 0;
+ for (cc::QuadList::ConstIterator iter = input.quad_list.begin();
+ iter != input.quad_list.end();
+ ++iter) {
+ const cc::DrawQuad& quad = *iter;
+ quads[i] = Quad::From(quad);
+ if (quad.shared_quad_state != last_sqs) {
+ sqs_i++;
+ shared_quad_state[sqs_i] =
+ SharedQuadState::From(*input.shared_quad_state_list[sqs_i]);
+ last_sqs = quad.shared_quad_state;
+ }
+ quads[i]->shared_quad_state_index = sqs_i;
+ ++i;
+ }
+ // We should copy all shared quad states.
+ DCHECK_EQ(static_cast<size_t>(sqs_i + 1), shared_quad_state.size());
+ pass->quads = quads.Pass();
+ pass->shared_quad_states = shared_quad_state.Pass();
+ return pass.Pass();
+}
+
+// static
+scoped_ptr<cc::RenderPass>
+TypeConverter<scoped_ptr<cc::RenderPass>, PassPtr>::Convert(
+ const PassPtr& input) {
+ // TODO(weiliangc): RenderPass will have a constructor that takes in preset
+ // size of quad list and shared quad state list size in upcoming CL.
+ scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
+ pass->SetAll(cc::RenderPassId(1, input->id),
+ input->output_rect.To<gfx::Rect>(),
+ input->damage_rect.To<gfx::Rect>(),
+ input->transform_to_root_target.To<gfx::Transform>(),
+ input->has_transparent_background);
+ cc::SharedQuadStateList& sqs_list = pass->shared_quad_state_list;
+ sqs_list.reserve(input->shared_quad_states.size());
+ for (size_t i = 0; i < input->shared_quad_states.size(); ++i) {
+ ConvertSharedQuadState(input->shared_quad_states[i], pass.get());
+ }
+ for (size_t i = 0; i < input->quads.size(); ++i) {
+ QuadPtr quad = input->quads[i].Pass();
+ if (!ConvertDrawQuad(
+ quad, sqs_list[quad->shared_quad_state_index], pass.get()))
+ return scoped_ptr<cc::RenderPass>();
+ }
+ return pass.Pass();
+}
+
+// static
+MailboxPtr TypeConverter<MailboxPtr, gpu::Mailbox>::Convert(
+ const gpu::Mailbox& input) {
+ Array<int8_t> name(64);
+ for (int i = 0; i < 64; ++i) {
+ name[i] = input.name[i];
+ }
+ MailboxPtr mailbox(Mailbox::New());
+ mailbox->name = name.Pass();
+ return mailbox.Pass();
+}
+
+// static
+gpu::Mailbox TypeConverter<gpu::Mailbox, MailboxPtr>::Convert(
+ const MailboxPtr& input) {
+ gpu::Mailbox mailbox;
+ if (!input->name.is_null())
+ mailbox.SetName(&input->name.storage()[0]);
+ return mailbox;
+}
+
+// static
+MailboxHolderPtr TypeConverter<MailboxHolderPtr, gpu::MailboxHolder>::Convert(
+ const gpu::MailboxHolder& input) {
+ MailboxHolderPtr holder(MailboxHolder::New());
+ holder->mailbox = Mailbox::From<gpu::Mailbox>(input.mailbox);
+ holder->texture_target = input.texture_target;
+ holder->sync_point = input.sync_point;
+ return holder.Pass();
+}
+
+// static
+gpu::MailboxHolder TypeConverter<gpu::MailboxHolder, MailboxHolderPtr>::Convert(
+ const MailboxHolderPtr& input) {
+ gpu::MailboxHolder holder;
+ holder.mailbox = input->mailbox.To<gpu::Mailbox>();
+ holder.texture_target = input->texture_target;
+ holder.sync_point = input->sync_point;
+ return holder;
+}
+
+// static
+TransferableResourcePtr
+TypeConverter<TransferableResourcePtr, cc::TransferableResource>::Convert(
+ const cc::TransferableResource& input) {
+ TransferableResourcePtr transferable = TransferableResource::New();
+ transferable->id = input.id;
+ transferable->format = static_cast<ResourceFormat>(input.format);
+ transferable->filter = input.filter;
+ transferable->size = Size::From(input.size);
+ transferable->mailbox_holder = MailboxHolder::From(input.mailbox_holder);
+ transferable->is_repeated = input.is_repeated;
+ transferable->is_software = input.is_software;
+ return transferable.Pass();
+}
+
+// static
+cc::TransferableResource
+TypeConverter<cc::TransferableResource, TransferableResourcePtr>::Convert(
+ const TransferableResourcePtr& input) {
+ cc::TransferableResource transferable;
+ transferable.id = input->id;
+ transferable.format = static_cast<cc::ResourceFormat>(input->format);
+ transferable.filter = input->filter;
+ transferable.size = input->size.To<gfx::Size>();
+ transferable.mailbox_holder = input->mailbox_holder.To<gpu::MailboxHolder>();
+ transferable.is_repeated = input->is_repeated;
+ transferable.is_software = input->is_software;
+ return transferable;
+}
+
+// static
+Array<TransferableResourcePtr> TypeConverter<
+ Array<TransferableResourcePtr>,
+ cc::TransferableResourceArray>::Convert(const cc::TransferableResourceArray&
+ input) {
+ Array<TransferableResourcePtr> resources(input.size());
+ for (size_t i = 0; i < input.size(); ++i) {
+ resources[i] = TransferableResource::From(input[i]);
+ }
+ return resources.Pass();
+}
+
+// static
+cc::TransferableResourceArray
+TypeConverter<cc::TransferableResourceArray, Array<TransferableResourcePtr> >::
+ Convert(const Array<TransferableResourcePtr>& input) {
+ cc::TransferableResourceArray resources(input.size());
+ for (size_t i = 0; i < input.size(); ++i) {
+ resources[i] = input[i].To<cc::TransferableResource>();
+ }
+ return resources;
+}
+
+// static
+ReturnedResourcePtr
+TypeConverter<ReturnedResourcePtr, cc::ReturnedResource>::Convert(
+ const cc::ReturnedResource& input) {
+ ReturnedResourcePtr returned = ReturnedResource::New();
+ returned->id = input.id;
+ returned->sync_point = input.sync_point;
+ returned->count = input.count;
+ returned->lost = input.lost;
+ return returned.Pass();
+}
+
+// static
+cc::ReturnedResource
+TypeConverter<cc::ReturnedResource, ReturnedResourcePtr>::Convert(
+ const ReturnedResourcePtr& input) {
+ cc::ReturnedResource returned;
+ returned.id = input->id;
+ returned.sync_point = input->sync_point;
+ returned.count = input->count;
+ returned.lost = input->lost;
+ return returned;
+}
+
+// static
+Array<ReturnedResourcePtr>
+TypeConverter<Array<ReturnedResourcePtr>, cc::ReturnedResourceArray>::Convert(
+ const cc::ReturnedResourceArray& input) {
+ Array<ReturnedResourcePtr> resources(input.size());
+ for (size_t i = 0; i < input.size(); ++i) {
+ resources[i] = ReturnedResource::From(input[i]);
+ }
+ return resources.Pass();
+}
+
+// static
+FramePtr TypeConverter<FramePtr, cc::CompositorFrame>::Convert(
+ const cc::CompositorFrame& input) {
+ FramePtr frame = Frame::New();
+ DCHECK(input.delegated_frame_data);
+ cc::DelegatedFrameData* frame_data = input.delegated_frame_data.get();
+ frame->resources =
+ Array<TransferableResourcePtr>::From(frame_data->resource_list);
+ const cc::RenderPassList& pass_list = frame_data->render_pass_list;
+ frame->passes = Array<PassPtr>::New(pass_list.size());
+ for (size_t i = 0; i < pass_list.size(); ++i) {
+ frame->passes[i] = Pass::From(*pass_list[i]);
+ }
+ return frame.Pass();
+}
+
+// static
+scoped_ptr<cc::CompositorFrame>
+TypeConverter<scoped_ptr<cc::CompositorFrame>, FramePtr>::Convert(
+ const FramePtr& input) {
+ scoped_ptr<cc::DelegatedFrameData> frame_data(new cc::DelegatedFrameData);
+ frame_data->device_scale_factor = 1.f;
+ frame_data->resource_list =
+ input->resources.To<cc::TransferableResourceArray>();
+ frame_data->render_pass_list.reserve(input->passes.size());
+ for (size_t i = 0; i < input->passes.size(); ++i) {
+ scoped_ptr<cc::RenderPass> pass =
+ input->passes[i].To<scoped_ptr<cc::RenderPass> >();
+ if (!pass)
+ return scoped_ptr<cc::CompositorFrame>();
+ frame_data->render_pass_list.push_back(pass.Pass());
+ }
+ scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
+ frame->delegated_frame_data = frame_data.Pass();
+ return frame.Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/surfaces/lib/surfaces_utils.cc b/mojo/services/public/cpp/surfaces/lib/surfaces_utils.cc
new file mode 100644
index 0000000..c35a77a
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/lib/surfaces_utils.cc
@@ -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.
+
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mojo {
+
+SharedQuadStatePtr CreateDefaultSQS(const gfx::Size& size) {
+ SharedQuadStatePtr sqs = SharedQuadState::New();
+ sqs->content_to_target_transform = Transform::From(gfx::Transform());
+ sqs->content_bounds = Size::From(size);
+ sqs->visible_content_rect = Rect::From(gfx::Rect(size));
+ sqs->clip_rect = Rect::From(gfx::Rect(size));
+ sqs->is_clipped = false;
+ sqs->opacity = 1.f;
+ sqs->blend_mode = mojo::SK_XFERMODE_kSrc_Mode;
+ sqs->sorting_context_id = 0;
+ return sqs.Pass();
+}
+
+PassPtr CreateDefaultPass(int id, const gfx::Rect& rect) {
+ PassPtr pass = Pass::New();
+ pass->id = id;
+ pass->output_rect = Rect::From(rect);
+ pass->damage_rect = Rect::From(rect);
+ pass->transform_to_root_target = Transform::From(gfx::Transform());
+ pass->has_transparent_background = false;
+ return pass.Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/surfaces/mojo_surfaces_export.h b/mojo/services/public/cpp/surfaces/mojo_surfaces_export.h
new file mode 100644
index 0000000..31b408f
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/mojo_surfaces_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_SURFACES_MOJO_SURFACES_EXPORT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_SURFACES_MOJO_SURFACES_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_SURFACES_IMPLEMENTATION)
+#define MOJO_SURFACES_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SURFACES_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_SURFACES_IMPLEMENTATION)
+#define MOJO_SURFACES_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SURFACES_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_SURFACES_EXPORT
+#endif
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_SURFACES_MOJO_SURFACES_EXPORT_H_
diff --git a/mojo/services/public/cpp/surfaces/surfaces_type_converters.h b/mojo/services/public/cpp/surfaces/surfaces_type_converters.h
new file mode 100644
index 0000000..4ef0e2c
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/surfaces_type_converters.h
@@ -0,0 +1,157 @@
+// 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_SURFACES_SURFACES_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_TYPE_CONVERTERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/resources/transferable_resource.h"
+#include "cc/surfaces/surface_id.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/services/public/cpp/surfaces/mojo_surfaces_export.h"
+#include "mojo/services/public/interfaces/surfaces/quads.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace cc {
+class CompositorFrame;
+class DrawQuad;
+class RenderPass;
+class RenderPassId;
+class SharedQuadState;
+} // namespace cc
+
+namespace mojo {
+
+// Types from surface_id.mojom
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<SurfaceIdPtr, cc::SurfaceId> {
+ static SurfaceIdPtr Convert(const cc::SurfaceId& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<cc::SurfaceId, SurfaceIdPtr> {
+ static cc::SurfaceId Convert(const SurfaceIdPtr& input);
+};
+
+// Types from quads.mojom
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<ColorPtr, SkColor> {
+ static ColorPtr Convert(const SkColor& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<SkColor, ColorPtr> {
+ static SkColor Convert(const ColorPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<RenderPassIdPtr, cc::RenderPassId> {
+ static RenderPassIdPtr Convert(const cc::RenderPassId& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<cc::RenderPassId, RenderPassIdPtr> {
+ static cc::RenderPassId Convert(const RenderPassIdPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<QuadPtr, cc::DrawQuad> {
+ static QuadPtr Convert(const cc::DrawQuad& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<SharedQuadStatePtr, cc::SharedQuadState> {
+ static SharedQuadStatePtr Convert(const cc::SharedQuadState& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<PassPtr, cc::RenderPass> {
+ static PassPtr Convert(const cc::RenderPass& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<scoped_ptr<cc::RenderPass>, PassPtr> {
+ static scoped_ptr<cc::RenderPass> Convert(const PassPtr& input);
+};
+
+// Types from surfaces.mojom
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<MailboxPtr, gpu::Mailbox> {
+ static MailboxPtr Convert(const gpu::Mailbox& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<gpu::Mailbox, MailboxPtr> {
+ static gpu::Mailbox Convert(const MailboxPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<MailboxHolderPtr, gpu::MailboxHolder> {
+ static MailboxHolderPtr Convert(const gpu::MailboxHolder& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<gpu::MailboxHolder, MailboxHolderPtr> {
+ static gpu::MailboxHolder Convert(const MailboxHolderPtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<TransferableResourcePtr, cc::TransferableResource> {
+ static TransferableResourcePtr Convert(const cc::TransferableResource& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<cc::TransferableResource, TransferableResourcePtr> {
+ static cc::TransferableResource Convert(const TransferableResourcePtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<Array<TransferableResourcePtr>, cc::TransferableResourceArray> {
+ static Array<TransferableResourcePtr> Convert(
+ const cc::TransferableResourceArray& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<cc::TransferableResourceArray, Array<TransferableResourcePtr> > {
+ static cc::TransferableResourceArray Convert(
+ const Array<TransferableResourcePtr>& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<ReturnedResourcePtr, cc::ReturnedResource> {
+ static ReturnedResourcePtr Convert(const cc::ReturnedResource& input);
+};
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<cc::ReturnedResource, ReturnedResourcePtr> {
+ static cc::ReturnedResource Convert(const ReturnedResourcePtr& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<Array<ReturnedResourcePtr>, cc::ReturnedResourceArray> {
+ static Array<ReturnedResourcePtr> Convert(
+ const cc::ReturnedResourceArray& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT TypeConverter<FramePtr, cc::CompositorFrame> {
+ static FramePtr Convert(const cc::CompositorFrame& input);
+};
+
+template <>
+struct MOJO_SURFACES_EXPORT
+TypeConverter<scoped_ptr<cc::CompositorFrame>, FramePtr> {
+ static scoped_ptr<cc::CompositorFrame> Convert(const FramePtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_TYPE_CONVERTERS_H_
diff --git a/mojo/services/public/cpp/surfaces/surfaces_utils.h b/mojo/services/public/cpp/surfaces/surfaces_utils.h
new file mode 100644
index 0000000..e4c3c83
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/surfaces_utils.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_SURFACES_SURFACES_UTILS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_UTILS_H_
+
+#include "mojo/services/public/cpp/surfaces/mojo_surfaces_export.h"
+#include "mojo/services/public/interfaces/surfaces/quads.mojom.h"
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace mojo {
+
+MOJO_SURFACES_EXPORT SharedQuadStatePtr CreateDefaultSQS(const gfx::Size& size);
+
+// Constructs a pass with the given id, output_rect and damage_rect set to rect,
+// transform_to_root_target set to identity and has_transparent_background set
+// to false.
+MOJO_SURFACES_EXPORT PassPtr CreateDefaultPass(int id, const gfx::Rect& rect);
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_SURFACES_SURFACES_UTILS_H_
diff --git a/mojo/services/public/cpp/surfaces/tests/BUILD.gn b/mojo/services/public/cpp/surfaces/tests/BUILD.gn
new file mode 100644
index 0000000..10810fe
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/tests/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_lib_unittests
+test("mojo_surfaces_lib_unittests") {
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//cc",
+ "//cc/surfaces",
+ "//gpu",
+ "//skia",
+ "//testing/gtest",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/gfx:test_support",
+ "//mojo/environment:chromium",
+ "//mojo/services/public/interfaces/surfaces",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/services/public/cpp/surfaces",
+ "//mojo/common/test:run_all_unittests",
+ ]
+
+ sources = [ "surface_unittest.cc" ]
+}
diff --git a/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc b/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc
new file mode 100644
index 0000000..93cf571
--- /dev/null
+++ b/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc
@@ -0,0 +1,460 @@
+// 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 "cc/quads/render_pass.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+
+namespace mojo {
+namespace {
+
+TEST(SurfaceLibTest, SurfaceIdConverterNullId) {
+ cc::SurfaceId null_id;
+ cc::SurfaceId round_trip = SurfaceId::From(null_id).To<cc::SurfaceId>();
+ EXPECT_TRUE(round_trip.is_null());
+}
+
+TEST(SurfaceLibTest, SurfaceIdConverterValidId) {
+ cc::SurfaceId valid_id(7);
+ cc::SurfaceId round_trip = SurfaceId::From(valid_id).To<cc::SurfaceId>();
+ EXPECT_FALSE(round_trip.is_null());
+ EXPECT_EQ(valid_id, round_trip);
+}
+
+TEST(SurfaceLibTest, Color) {
+ SkColor arbitrary_color = SK_ColorMAGENTA;
+ SkColor round_trip = Color::From(arbitrary_color).To<SkColor>();
+ EXPECT_EQ(arbitrary_color, round_trip);
+}
+
+class SurfaceLibQuadTest : public testing::Test {
+ public:
+ SurfaceLibQuadTest()
+ : rect(5, 7, 13, 19),
+ opaque_rect(rect),
+ visible_rect(9, 11, 5, 7),
+ needs_blending(false) {
+ pass = cc::RenderPass::Create();
+ sqs = pass->CreateAndAppendSharedQuadState();
+ }
+
+ protected:
+ gfx::Rect rect;
+ gfx::Rect opaque_rect;
+ gfx::Rect visible_rect;
+ bool needs_blending;
+ scoped_ptr<cc::RenderPass> pass;
+ cc::SharedQuadState* sqs;
+};
+
+TEST_F(SurfaceLibQuadTest, ColorQuad) {
+ cc::SolidColorDrawQuad* color_quad =
+ pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ SkColor arbitrary_color = SK_ColorGREEN;
+ bool force_anti_aliasing_off = true;
+ color_quad->SetAll(sqs,
+ rect,
+ opaque_rect,
+ visible_rect,
+ needs_blending,
+ arbitrary_color,
+ force_anti_aliasing_off);
+
+ QuadPtr mojo_quad = Quad::From<cc::DrawQuad>(*color_quad);
+ ASSERT_FALSE(mojo_quad.is_null());
+ EXPECT_EQ(MATERIAL_SOLID_COLOR, mojo_quad->material);
+ EXPECT_EQ(Rect::From(rect), mojo_quad->rect);
+ EXPECT_EQ(Rect::From(opaque_rect), mojo_quad->opaque_rect);
+ EXPECT_EQ(Rect::From(visible_rect), mojo_quad->visible_rect);
+ EXPECT_EQ(needs_blending, mojo_quad->needs_blending);
+ ASSERT_TRUE(mojo_quad->solid_color_quad_state);
+ SolidColorQuadStatePtr& mojo_color_state = mojo_quad->solid_color_quad_state;
+ EXPECT_EQ(Color::From(arbitrary_color), mojo_color_state->color);
+ EXPECT_EQ(force_anti_aliasing_off, mojo_color_state->force_anti_aliasing_off);
+}
+
+TEST_F(SurfaceLibQuadTest, SurfaceQuad) {
+ cc::SurfaceDrawQuad* surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ cc::SurfaceId arbitrary_id(5);
+ surface_quad->SetAll(
+ sqs, rect, opaque_rect, visible_rect, needs_blending, arbitrary_id);
+
+ QuadPtr mojo_quad = Quad::From<cc::DrawQuad>(*surface_quad);
+ ASSERT_FALSE(mojo_quad.is_null());
+ EXPECT_EQ(MATERIAL_SURFACE_CONTENT, mojo_quad->material);
+ ASSERT_TRUE(mojo_quad->surface_quad_state);
+ SurfaceQuadStatePtr& mojo_surface_state = mojo_quad->surface_quad_state;
+ EXPECT_EQ(SurfaceId::From(arbitrary_id),
+ mojo_surface_state->surface);
+}
+
+TEST_F(SurfaceLibQuadTest, TextureQuad) {
+ cc::TextureDrawQuad* texture_quad =
+ pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+ unsigned resource_id = 9;
+ bool premultiplied_alpha = true;
+ gfx::PointF uv_top_left(1.7f, 2.1f);
+ gfx::PointF uv_bottom_right(-7.f, 16.3f);
+ SkColor background_color = SK_ColorYELLOW;
+ float vertex_opacity[4] = {0.1f, 0.5f, 0.4f, 0.8f};
+ bool flipped = false;
+ texture_quad->SetAll(sqs,
+ rect,
+ opaque_rect,
+ visible_rect,
+ needs_blending,
+ resource_id,
+ premultiplied_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ background_color,
+ vertex_opacity,
+ flipped);
+
+ QuadPtr mojo_quad = Quad::From<cc::DrawQuad>(*texture_quad);
+ ASSERT_FALSE(mojo_quad.is_null());
+ EXPECT_EQ(MATERIAL_TEXTURE_CONTENT, mojo_quad->material);
+ ASSERT_TRUE(mojo_quad->texture_quad_state);
+ TextureQuadStatePtr& mojo_texture_state = mojo_quad->texture_quad_state;
+ EXPECT_EQ(resource_id, mojo_texture_state->resource_id);
+ EXPECT_EQ(premultiplied_alpha, mojo_texture_state->premultiplied_alpha);
+ EXPECT_EQ(PointF::From(uv_top_left), mojo_texture_state->uv_top_left);
+ EXPECT_EQ(PointF::From(uv_bottom_right), mojo_texture_state->uv_bottom_right);
+ EXPECT_EQ(Color::From(background_color),
+ mojo_texture_state->background_color);
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(vertex_opacity[i], mojo_texture_state->vertex_opacity[i]) << i;
+ }
+ EXPECT_EQ(flipped, mojo_texture_state->flipped);
+}
+
+TEST_F(SurfaceLibQuadTest, TextureQuadEmptyVertexOpacity) {
+ QuadPtr mojo_texture_quad = Quad::New();
+ mojo_texture_quad->material = MATERIAL_TEXTURE_CONTENT;
+ TextureQuadStatePtr mojo_texture_state = TextureQuadState::New();
+ mojo_texture_state->background_color = Color::New();
+ mojo_texture_quad->texture_quad_state = mojo_texture_state.Pass();
+ PassPtr mojo_pass = Pass::New();
+ mojo_pass->quads.push_back(mojo_texture_quad.Pass());
+ SharedQuadStatePtr mojo_sqs = SharedQuadState::New();
+ mojo_pass->shared_quad_states.push_back(mojo_sqs.Pass());
+
+ scoped_ptr<cc::RenderPass> pass = mojo_pass.To<scoped_ptr<cc::RenderPass> >();
+
+ EXPECT_FALSE(pass);
+}
+
+TEST_F(SurfaceLibQuadTest, TextureQuadEmptyBackgroundColor) {
+ QuadPtr mojo_texture_quad = Quad::New();
+ mojo_texture_quad->material = MATERIAL_TEXTURE_CONTENT;
+ TextureQuadStatePtr mojo_texture_state = TextureQuadState::New();
+ mojo_texture_state->vertex_opacity = mojo::Array<float>::New(4);
+ mojo_texture_quad->texture_quad_state = mojo_texture_state.Pass();
+ PassPtr mojo_pass = Pass::New();
+ mojo_pass->quads.push_back(mojo_texture_quad.Pass());
+ SharedQuadStatePtr mojo_sqs = SharedQuadState::New();
+ mojo_pass->shared_quad_states.push_back(mojo_sqs.Pass());
+
+ scoped_ptr<cc::RenderPass> pass = mojo_pass.To<scoped_ptr<cc::RenderPass> >();
+ EXPECT_FALSE(pass);
+}
+
+TEST(SurfaceLibTest, SharedQuadState) {
+ gfx::Transform content_to_target_transform;
+ content_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
+ gfx::Size content_bounds(57, 39);
+ gfx::Rect visible_content_rect(3, 7, 28, 42);
+ gfx::Rect clip_rect(9, 12, 21, 31);
+ bool is_clipped = true;
+ float opacity = 0.65f;
+ int sorting_context_id = 13;
+ ::SkXfermode::Mode blend_mode = ::SkXfermode::kSrcOver_Mode;
+ scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
+ cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->SetAll(content_to_target_transform,
+ content_bounds,
+ visible_content_rect,
+ clip_rect,
+ is_clipped,
+ opacity,
+ blend_mode,
+ sorting_context_id);
+
+ SharedQuadStatePtr mojo_sqs = SharedQuadState::From(*sqs);
+ ASSERT_FALSE(mojo_sqs.is_null());
+ EXPECT_EQ(Transform::From(content_to_target_transform),
+ mojo_sqs->content_to_target_transform);
+ EXPECT_EQ(Size::From(content_bounds), mojo_sqs->content_bounds);
+ EXPECT_EQ(Rect::From(visible_content_rect), mojo_sqs->visible_content_rect);
+ EXPECT_EQ(Rect::From(clip_rect), mojo_sqs->clip_rect);
+ EXPECT_EQ(is_clipped, mojo_sqs->is_clipped);
+ EXPECT_EQ(opacity, mojo_sqs->opacity);
+ EXPECT_EQ(sorting_context_id, mojo_sqs->sorting_context_id);
+}
+
+TEST(SurfaceLibTest, RenderPass) {
+ scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
+ cc::RenderPassId pass_id(1, 6);
+ gfx::Rect output_rect(4, 9, 13, 71);
+ gfx::Rect damage_rect(9, 17, 41, 45);
+ gfx::Transform transform_to_root_target;
+ transform_to_root_target.SkewY(43.0);
+ bool has_transparent_background = false;
+ pass->SetAll(pass_id,
+ output_rect,
+ damage_rect,
+ transform_to_root_target,
+ has_transparent_background);
+
+ gfx::Transform content_to_target_transform;
+ content_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
+ gfx::Size content_bounds(57, 39);
+ gfx::Rect visible_content_rect(3, 7, 28, 42);
+ gfx::Rect clip_rect(9, 12, 21, 31);
+ bool is_clipped = true;
+ float opacity = 0.65f;
+ int sorting_context_id = 13;
+ ::SkXfermode::Mode blend_mode = ::SkXfermode::kSrcOver_Mode;
+ cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->SetAll(content_to_target_transform,
+ content_bounds,
+ visible_content_rect,
+ clip_rect,
+ is_clipped,
+ opacity,
+ blend_mode,
+ sorting_context_id);
+
+ gfx::Rect rect(5, 7, 13, 19);
+ gfx::Rect opaque_rect(rect);
+ gfx::Rect visible_rect(9, 11, 5, 7);
+ bool needs_blending = false;
+
+ cc::SolidColorDrawQuad* color_quad =
+ pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ SkColor arbitrary_color = SK_ColorGREEN;
+ bool force_anti_aliasing_off = true;
+ color_quad->SetAll(pass->shared_quad_state_list.back(),
+ rect,
+ opaque_rect,
+ visible_rect,
+ needs_blending,
+ arbitrary_color,
+ force_anti_aliasing_off);
+
+ cc::SurfaceDrawQuad* surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ cc::SurfaceId arbitrary_id(5);
+ surface_quad->SetAll(
+ sqs, rect, opaque_rect, visible_rect, needs_blending, arbitrary_id);
+
+ cc::TextureDrawQuad* texture_quad =
+ pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+ unsigned resource_id = 9;
+ bool premultiplied_alpha = true;
+ gfx::PointF uv_top_left(1.7f, 2.1f);
+ gfx::PointF uv_bottom_right(-7.f, 16.3f);
+ SkColor background_color = SK_ColorYELLOW;
+ float vertex_opacity[4] = {0.1f, 0.5f, 0.4f, 0.8f};
+ bool flipped = false;
+ texture_quad->SetAll(sqs,
+ rect,
+ opaque_rect,
+ visible_rect,
+ needs_blending,
+ resource_id,
+ premultiplied_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ background_color,
+ vertex_opacity,
+ flipped);
+
+ PassPtr mojo_pass = Pass::From(*pass);
+ ASSERT_FALSE(mojo_pass.is_null());
+ EXPECT_EQ(6, mojo_pass->id);
+ EXPECT_EQ(Rect::From(output_rect), mojo_pass->output_rect);
+ EXPECT_EQ(Rect::From(damage_rect), mojo_pass->damage_rect);
+ EXPECT_EQ(Transform::From(transform_to_root_target),
+ mojo_pass->transform_to_root_target);
+ EXPECT_EQ(has_transparent_background, mojo_pass->has_transparent_background);
+ ASSERT_EQ(1u, mojo_pass->shared_quad_states.size());
+ ASSERT_EQ(3u, mojo_pass->quads.size());
+ EXPECT_EQ(0, mojo_pass->quads[0]->shared_quad_state_index);
+
+ scoped_ptr<cc::RenderPass> round_trip_pass =
+ mojo_pass.To<scoped_ptr<cc::RenderPass> >();
+ EXPECT_EQ(pass_id, round_trip_pass->id);
+ EXPECT_EQ(output_rect, round_trip_pass->output_rect);
+ EXPECT_EQ(damage_rect, round_trip_pass->damage_rect);
+ EXPECT_EQ(transform_to_root_target,
+ round_trip_pass->transform_to_root_target);
+ EXPECT_EQ(has_transparent_background,
+ round_trip_pass->has_transparent_background);
+ ASSERT_EQ(1u, round_trip_pass->shared_quad_state_list.size());
+ ASSERT_EQ(3u, round_trip_pass->quad_list.size());
+ EXPECT_EQ(round_trip_pass->shared_quad_state_list.front(),
+ round_trip_pass->quad_list.front()->shared_quad_state);
+
+ cc::SharedQuadState* round_trip_sqs =
+ round_trip_pass->shared_quad_state_list.front();
+ EXPECT_EQ(content_to_target_transform,
+ round_trip_sqs->content_to_target_transform);
+ EXPECT_EQ(content_bounds, round_trip_sqs->content_bounds);
+ EXPECT_EQ(visible_content_rect, round_trip_sqs->visible_content_rect);
+ EXPECT_EQ(clip_rect, round_trip_sqs->clip_rect);
+ EXPECT_EQ(is_clipped, round_trip_sqs->is_clipped);
+ EXPECT_EQ(opacity, round_trip_sqs->opacity);
+ EXPECT_EQ(sorting_context_id, round_trip_sqs->sorting_context_id);
+
+ cc::QuadList::Iterator dq_iter = round_trip_pass->quad_list.begin();
+ // First is solid color quad.
+ ASSERT_EQ(cc::DrawQuad::SOLID_COLOR, dq_iter->material);
+ EXPECT_EQ(rect, dq_iter->rect);
+ EXPECT_EQ(opaque_rect, dq_iter->opaque_rect);
+ EXPECT_EQ(visible_rect, dq_iter->visible_rect);
+ EXPECT_EQ(needs_blending, dq_iter->needs_blending);
+ const cc::SolidColorDrawQuad* round_trip_color_quad =
+ cc::SolidColorDrawQuad::MaterialCast(&*dq_iter);
+ EXPECT_EQ(arbitrary_color, round_trip_color_quad->color);
+ EXPECT_EQ(force_anti_aliasing_off,
+ round_trip_color_quad->force_anti_aliasing_off);
+
+ ++dq_iter;
+ // Second is surface quad.
+ ASSERT_EQ(cc::DrawQuad::SURFACE_CONTENT, dq_iter->material);
+ const cc::SurfaceDrawQuad* round_trip_surface_quad =
+ cc::SurfaceDrawQuad::MaterialCast(&*dq_iter);
+ EXPECT_EQ(arbitrary_id, round_trip_surface_quad->surface_id);
+
+ ++dq_iter;
+ // Third is texture quad.
+ ASSERT_EQ(cc::DrawQuad::TEXTURE_CONTENT, dq_iter->material);
+ const cc::TextureDrawQuad* round_trip_texture_quad =
+ cc::TextureDrawQuad::MaterialCast(&*dq_iter);
+ EXPECT_EQ(resource_id, round_trip_texture_quad->resource_id);
+ EXPECT_EQ(premultiplied_alpha, round_trip_texture_quad->premultiplied_alpha);
+ EXPECT_EQ(uv_top_left, round_trip_texture_quad->uv_top_left);
+ EXPECT_EQ(uv_bottom_right, round_trip_texture_quad->uv_bottom_right);
+ EXPECT_EQ(background_color, round_trip_texture_quad->background_color);
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(vertex_opacity[i], round_trip_texture_quad->vertex_opacity[i])
+ << i;
+ }
+ EXPECT_EQ(flipped, round_trip_texture_quad->flipped);
+}
+
+TEST(SurfaceLibTest, Mailbox) {
+ gpu::Mailbox mailbox;
+ mailbox.Generate();
+
+ MailboxPtr mojo_mailbox = Mailbox::From(mailbox);
+ EXPECT_EQ(0, memcmp(mailbox.name, &mojo_mailbox->name.storage()[0], 64));
+
+ gpu::Mailbox round_trip_mailbox = mojo_mailbox.To<gpu::Mailbox>();
+ EXPECT_EQ(mailbox, round_trip_mailbox);
+}
+
+TEST(SurfaceLibTest, MailboxEmptyName) {
+ MailboxPtr mojo_mailbox = Mailbox::New();
+
+ gpu::Mailbox converted_mailbox = mojo_mailbox.To<gpu::Mailbox>();
+ EXPECT_TRUE(converted_mailbox.IsZero());
+}
+
+TEST(SurfaceLibTest, MailboxHolder) {
+ gpu::Mailbox mailbox;
+ mailbox.Generate();
+ uint32_t texture_target = GL_TEXTURE_2D;
+ uint32_t sync_point = 7u;
+ gpu::MailboxHolder holder(mailbox, texture_target, sync_point);
+
+ MailboxHolderPtr mojo_holder = MailboxHolder::From(holder);
+ EXPECT_EQ(texture_target, mojo_holder->texture_target);
+ EXPECT_EQ(sync_point, mojo_holder->sync_point);
+
+ gpu::MailboxHolder round_trip_holder = mojo_holder.To<gpu::MailboxHolder>();
+ EXPECT_EQ(mailbox, round_trip_holder.mailbox);
+ EXPECT_EQ(texture_target, round_trip_holder.texture_target);
+ EXPECT_EQ(sync_point, round_trip_holder.sync_point);
+}
+
+TEST(SurfaceLibTest, TransferableResource) {
+ uint32_t id = 7u;
+ cc::ResourceFormat format = cc::BGRA_8888;
+ uint32_t filter = 123u;
+ gfx::Size size(17, 18);
+ gpu::MailboxHolder mailbox_holder;
+ bool is_repeated = false;
+ ;
+ bool is_software = false;
+ cc::TransferableResource resource;
+ resource.id = id;
+ resource.format = format;
+ resource.filter = filter;
+ resource.size = size;
+ resource.mailbox_holder = mailbox_holder;
+ resource.is_repeated = is_repeated;
+ resource.is_software = is_software;
+
+ TransferableResourcePtr mojo_resource = TransferableResource::From(resource);
+ EXPECT_EQ(id, mojo_resource->id);
+ EXPECT_EQ(static_cast<ResourceFormat>(format),
+ mojo_resource->format);
+ EXPECT_EQ(filter, mojo_resource->filter);
+ EXPECT_EQ(Size::From(size), mojo_resource->size);
+ EXPECT_EQ(is_repeated, mojo_resource->is_repeated);
+ EXPECT_EQ(is_software, mojo_resource->is_software);
+
+ cc::TransferableResource round_trip_resource =
+ mojo_resource.To<cc::TransferableResource>();
+ EXPECT_EQ(id, round_trip_resource.id);
+ EXPECT_EQ(format, round_trip_resource.format);
+ EXPECT_EQ(filter, round_trip_resource.filter);
+ EXPECT_EQ(size, round_trip_resource.size);
+ EXPECT_EQ(mailbox_holder.mailbox, round_trip_resource.mailbox_holder.mailbox);
+ EXPECT_EQ(mailbox_holder.texture_target,
+ round_trip_resource.mailbox_holder.texture_target);
+ EXPECT_EQ(mailbox_holder.sync_point,
+ round_trip_resource.mailbox_holder.sync_point);
+ EXPECT_EQ(is_repeated, round_trip_resource.is_repeated);
+ EXPECT_EQ(is_software, round_trip_resource.is_software);
+}
+
+TEST(SurfaceLibTest, ReturnedResource) {
+ uint32_t id = 5u;
+ uint32_t sync_point = 24u;
+ int count = 2;
+ bool lost = false;
+ cc::ReturnedResource resource;
+ resource.id = id;
+ resource.sync_point = sync_point;
+ resource.count = count;
+ resource.lost = lost;
+
+ ReturnedResourcePtr mojo_resource = ReturnedResource::From(resource);
+ EXPECT_EQ(id, mojo_resource->id);
+ EXPECT_EQ(sync_point, mojo_resource->sync_point);
+ EXPECT_EQ(count, mojo_resource->count);
+ EXPECT_EQ(lost, mojo_resource->lost);
+
+ cc::ReturnedResource round_trip_resource =
+ mojo_resource.To<cc::ReturnedResource>();
+ EXPECT_EQ(id, round_trip_resource.id);
+ EXPECT_EQ(sync_point, round_trip_resource.sync_point);
+ EXPECT_EQ(count, round_trip_resource.count);
+ EXPECT_EQ(lost, round_trip_resource.lost);
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/BUILD.gn b/mojo/services/public/cpp/view_manager/BUILD.gn
new file mode 100644
index 0000000..cab9b73
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/BUILD.gn
@@ -0,0 +1,64 @@
+# 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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_lib
+source_set("view_manager") {
+ sources = [
+ "lib/bitmap_uploader.cc",
+ "lib/bitmap_uploader.h",
+ "lib/view.cc",
+ "lib/view_manager_client_factory.cc",
+ "lib/view_manager_client_impl.cc",
+ "lib/view_manager_client_impl.h",
+ "lib/view_manager_context.cc",
+ "lib/view_observer.cc",
+ "lib/view_private.cc",
+ "lib/view_private.h",
+ "view.h",
+ "view_manager.h",
+ "view_manager_client_factory.h",
+ "view_manager_context.h",
+ "view_manager_delegate.h",
+ "view_observer.h",
+ "window_manager_delegate.h",
+ ]
+
+ public_deps = [
+ ":common",
+ "//skia",
+ ]
+ deps = [
+ "//base",
+ "//cc/surfaces",
+ "//gpu",
+ "//mojo/application",
+ "//mojo/public/c/gles2",
+ "//mojo/public/cpp/bindings:bindings",
+ "//mojo/public/interfaces/application",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/services/public/cpp/surfaces",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/gpu",
+ "//mojo/services/public/interfaces/input_events:input_events",
+ "//mojo/services/public/interfaces/native_viewport",
+ "//mojo/services/public/interfaces/surfaces:surface_id",
+ "//mojo/services/public/interfaces/surfaces",
+ "//mojo/services/public/interfaces/view_manager",
+ "//mojo/services/public/interfaces/window_manager",
+ "//skia",
+ "//ui/events",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ ]
+}
+
+source_set("common") {
+ sources = [
+ "ids.h",
+ "types.h",
+ ]
+ public_deps = [
+ "//base",
+ ]
+}
diff --git a/mojo/services/public/cpp/view_manager/DEPS b/mojo/services/public/cpp/view_manager/DEPS
new file mode 100644
index 0000000..4460c19
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/skia/include/core",
+ "+ui/gfx/geometry"
+]
diff --git a/mojo/services/public/cpp/view_manager/lib/BUILD.gn b/mojo/services/public/cpp/view_manager/lib/BUILD.gn
new file mode 100644
index 0000000..df44a34
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_run_unittests
+source_set("run_unittests") {
+ testonly = true
+ sources = [
+ "view_manager_test_suite.cc",
+ "view_manager_test_suite.h",
+ "view_manager_unittests.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ ]
+
+ if (use_x11) {
+ deps += ["//ui/gfx/x"]
+ }
+
+ if (is_component_build) {
+ deps += ["//ui/gl"]
+ }
+
+}
diff --git a/mojo/services/public/cpp/view_manager/lib/DEPS b/mojo/services/public/cpp/view_manager/lib/DEPS
new file mode 100644
index 0000000..0af5fad
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+ "+cc/surfaces/surface_id.h",
+ "+cc/surfaces/surface_id_allocator.h",
+ "+gpu/GLES2",
+ "+gpu/command_buffer/common/mailbox.h",
+ "+mojo/geometry",
+ "+third_party/khronos/GLES2",
+ "+third_party/skia",
+ "+ui/gfx",
+]
+
+specific_include_rules = {
+ "view_manager_test_suite.cc": [
+ "+mojo/services/native_viewport/native_viewport.h",
+ "+ui/gl",
+ ],
+}
diff --git a/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.cc b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.cc
new file mode 100644
index 0000000..80447ed
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.cc
@@ -0,0 +1,232 @@
+// 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/bitmap_uploader.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif // GL_GLEXT_PROTOTYPES
+
+#include "base/bind.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "gpu/GLES2/gl2chromium.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+
+namespace {
+void LostContext(void*) {
+ DCHECK(false);
+}
+
+uint32_t TextureFormat() {
+ return SK_B32_SHIFT ? GL_RGBA : GL_BGRA_EXT;
+}
+}
+
+BitmapUploader::BitmapUploader(ViewManagerClientImpl* client,
+ Id view_id,
+ SurfacesServicePtr surfaces_service,
+ GpuPtr gpu_service)
+ : client_(client),
+ view_id_(view_id),
+ surfaces_service_(surfaces_service.Pass()),
+ gpu_service_(gpu_service.Pass()),
+ color_(SK_ColorTRANSPARENT),
+ next_resource_id_(1u),
+ weak_factory_(this) {
+ surfaces_service_->CreateSurfaceConnection(base::Bind(
+ &BitmapUploader::OnSurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
+ CommandBufferPtr gles2_client;
+ gpu_service_->CreateOffscreenGLES2Context(Get(&gles2_client));
+ gles2_context_ =
+ MojoGLES2CreateContext(gles2_client.PassMessagePipe().release().value(),
+ &LostContext,
+ NULL,
+ Environment::GetDefaultAsyncWaiter());
+ MojoGLES2MakeCurrent(gles2_context_);
+}
+
+BitmapUploader::~BitmapUploader() {
+ MojoGLES2DestroyContext(gles2_context_);
+}
+
+void BitmapUploader::SetSize(const gfx::Size& size) {
+ if (size_ == size)
+ return;
+ size_ = size;
+}
+
+void BitmapUploader::SetColor(SkColor color) {
+ if (color_ == color)
+ return;
+ color_ = color;
+ if (surface_)
+ Upload();
+}
+
+void BitmapUploader::SetBitmap(SkBitmap bitmap) {
+ bitmap_ = bitmap;
+ if (surface_)
+ Upload();
+}
+
+void BitmapUploader::Upload() {
+ if (size_.width() == 0 || size_.height() == 0) {
+ client_->SetSurfaceId(view_id_, SurfaceId::New());
+ return;
+ }
+ if (!surface_) { // Can't upload yet, store for later.
+ done_callback_ = done_callback_;
+ return;
+ }
+ if (id_.is_null() || size_ != surface_size_) {
+ if (!id_.is_null())
+ surface_->DestroySurface(SurfaceId::From(id_));
+ id_ = id_allocator_->GenerateId();
+ surface_->CreateSurface(SurfaceId::From(id_), Size::From(size_));
+ client_->SetSurfaceId(view_id_, SurfaceId::From(id_));
+ surface_size_ = size_;
+ }
+
+ gfx::Rect bounds(size_);
+ PassPtr pass = CreateDefaultPass(1, bounds);
+ FramePtr frame = Frame::New();
+ frame->resources.resize(0u);
+
+ pass->quads.resize(0u);
+ pass->shared_quad_states.push_back(CreateDefaultSQS(size_));
+
+ MojoGLES2MakeCurrent(gles2_context_);
+ if (!bitmap_.isNull()) {
+ gfx::Size bitmap_size(bitmap_.width(), bitmap_.height());
+ GLuint texture_id = BindTextureForSize(bitmap_size);
+ bitmap_.lockPixels();
+ glTexSubImage2D(GL_TEXTURE_2D,
+ 0,
+ 0,
+ 0,
+ bitmap_size.width(),
+ bitmap_size.height(),
+ TextureFormat(),
+ GL_UNSIGNED_BYTE,
+ bitmap_.getPixels());
+ bitmap_.unlockPixels();
+
+ gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+ glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+ GLuint sync_point = glInsertSyncPointCHROMIUM();
+
+ TransferableResourcePtr resource = TransferableResource::New();
+ resource->id = next_resource_id_++;
+ resource_to_texture_id_map_[resource->id] = texture_id;
+ resource->format = mojo::RESOURCE_FORMAT_RGBA_8888;
+ resource->filter = GL_LINEAR;
+ resource->size = Size::From(bitmap_size);
+ MailboxHolderPtr mailbox_holder = MailboxHolder::New();
+ mailbox_holder->mailbox = Mailbox::From(mailbox);
+ mailbox_holder->texture_target = GL_TEXTURE_2D;
+ mailbox_holder->sync_point = sync_point;
+ resource->mailbox_holder = mailbox_holder.Pass();
+ resource->is_repeated = false;
+ resource->is_software = false;
+
+ QuadPtr quad = Quad::New();
+ quad->material = MATERIAL_TEXTURE_CONTENT;
+ quad->rect = Rect::From(bounds);
+ quad->opaque_rect = Rect::From(bounds);
+ quad->visible_rect = Rect::From(bounds);
+ quad->needs_blending = true;
+ quad->shared_quad_state_index = 0u;
+
+ TextureQuadStatePtr texture_state = TextureQuadState::New();
+ texture_state->resource_id = resource->id;
+ texture_state->premultiplied_alpha = true;
+ texture_state->uv_top_left = PointF::From(gfx::PointF(0.f, 0.f));
+ texture_state->uv_bottom_right = PointF::From(gfx::PointF(1.f, 1.f));
+ texture_state->background_color = Color::From(SkColor(SK_ColorTRANSPARENT));
+ for (int i = 0; i < 4; ++i)
+ texture_state->vertex_opacity.push_back(1.f);
+ texture_state->flipped = false;
+
+ frame->resources.push_back(resource.Pass());
+ quad->texture_quad_state = texture_state.Pass();
+ pass->quads.push_back(quad.Pass());
+ }
+
+ if (color_ != SK_ColorTRANSPARENT) {
+ QuadPtr quad = Quad::New();
+ quad->material = MATERIAL_SOLID_COLOR;
+ quad->rect = Rect::From(bounds);
+ quad->opaque_rect = Rect::From(gfx::Rect());
+ quad->visible_rect = Rect::From(bounds);
+ quad->needs_blending = true;
+ quad->shared_quad_state_index = 0u;
+
+ SolidColorQuadStatePtr color_state = SolidColorQuadState::New();
+ color_state->color = Color::From(color_);
+ color_state->force_anti_aliasing_off = false;
+
+ quad->solid_color_quad_state = color_state.Pass();
+ pass->quads.push_back(quad.Pass());
+ }
+
+ frame->passes.push_back(pass.Pass());
+
+ surface_->SubmitFrame(SurfaceId::From(id_), frame.Pass());
+}
+
+void BitmapUploader::ReturnResources(Array<ReturnedResourcePtr> resources) {
+ if (!resources.size())
+ return;
+ MojoGLES2MakeCurrent(gles2_context_);
+ // TODO(jamesr): Recycle.
+ for (size_t i = 0; i < resources.size(); ++i) {
+ ReturnedResourcePtr resource = resources[i].Pass();
+ DCHECK_EQ(1, resource->count);
+ glWaitSyncPointCHROMIUM(resource->sync_point);
+ uint32_t texture_id = resource_to_texture_id_map_[resource->id];
+ DCHECK_NE(0u, texture_id);
+ resource_to_texture_id_map_.erase(resource->id);
+ glDeleteTextures(1, &texture_id);
+ }
+}
+
+void BitmapUploader::OnSurfaceConnectionCreated(SurfacePtr surface,
+ uint32_t id_namespace) {
+ surface_ = surface.Pass();
+ surface_.set_client(this);
+ id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+ if (color_ != SK_ColorTRANSPARENT || !bitmap_.isNull())
+ Upload();
+}
+
+uint32_t BitmapUploader::BindTextureForSize(const gfx::Size size) {
+ // TODO(jamesr): Recycle textures.
+ GLuint texture = 0u;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ TextureFormat(),
+ size.width(),
+ size.height(),
+ 0,
+ TextureFormat(),
+ GL_UNSIGNED_BYTE,
+ 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.h b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.h
new file mode 100644
index 0000000..f049409
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/bitmap_uploader.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_PUBLIC_CPP_VIEW_MANAGER_LIB_BITMAP_UPLOADER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_BITMAP_UPLOADER_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+class ViewManagerClientImpl;
+
+class BitmapUploader : public SurfaceClient {
+ public:
+ BitmapUploader(ViewManagerClientImpl* client,
+ Id view_id,
+ SurfacesServicePtr surfaces_service,
+ GpuPtr gpu_service);
+ virtual ~BitmapUploader();
+
+ void SetSize(const gfx::Size& size);
+ void SetColor(SkColor color);
+ void SetBitmap(SkBitmap bitmap);
+ void SetDoneCallback(const base::Callback<void(SurfaceIdPtr)>& done_callback);
+
+ private:
+ void Upload();
+ void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+ uint32_t BindTextureForSize(const gfx::Size size);
+
+ // SurfaceClient implementation.
+ virtual void ReturnResources(Array<ReturnedResourcePtr> resources) OVERRIDE;
+
+ ViewManagerClientImpl* client_;
+ Id view_id_;
+ SurfacesServicePtr surfaces_service_;
+ GpuPtr gpu_service_;
+ MojoGLES2Context gles2_context_;
+
+ gfx::Size size_;
+ SkColor color_;
+ SkBitmap bitmap_;
+ base::Callback<void(SurfaceIdPtr)> done_callback_;
+ SurfacePtr surface_;
+ cc::SurfaceId id_;
+ scoped_ptr<cc::SurfaceIdAllocator> id_allocator_;
+ gfx::Size surface_size_;
+ uint32_t next_resource_id_;
+ base::hash_map<uint32_t, uint32_t> resource_to_texture_id_map_;
+
+ base::WeakPtrFactory<BitmapUploader> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BitmapUploader);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_BITMAP_UPLOADER_H_
diff --git a/mojo/services/public/cpp/view_manager/lib/view.cc b/mojo/services/public/cpp/view_manager/lib/view.cc
new file mode 100644
index 0000000..fe45ffc
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view.cc
@@ -0,0 +1,456 @@
+// 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/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/bitmap_uploader.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/view_observer.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "ui/gfx/canvas.h"
+
+namespace mojo {
+
+namespace {
+
+void NotifyViewTreeChangeAtReceiver(
+ View* receiver,
+ const ViewObserver::TreeChangeParams& params,
+ bool change_applied) {
+ ViewObserver::TreeChangeParams local_params = params;
+ local_params.receiver = receiver;
+ if (change_applied) {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(receiver).observers(),
+ OnTreeChanged(local_params));
+ } else {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(receiver).observers(),
+ OnTreeChanging(local_params));
+ }
+}
+
+void NotifyViewTreeChangeUp(
+ View* start_at,
+ const ViewObserver::TreeChangeParams& params,
+ bool change_applied) {
+ for (View* current = start_at; current; current = current->parent())
+ NotifyViewTreeChangeAtReceiver(current, params, change_applied);
+}
+
+void NotifyViewTreeChangeDown(
+ View* start_at,
+ const ViewObserver::TreeChangeParams& params,
+ bool change_applied) {
+ NotifyViewTreeChangeAtReceiver(start_at, params, change_applied);
+ View::Children::const_iterator it = start_at->children().begin();
+ for (; it != start_at->children().end(); ++it)
+ NotifyViewTreeChangeDown(*it, params, change_applied);
+}
+
+void NotifyViewTreeChange(
+ const ViewObserver::TreeChangeParams& params,
+ bool change_applied) {
+ NotifyViewTreeChangeDown(params.target, params, change_applied);
+ if (params.old_parent)
+ NotifyViewTreeChangeUp(params.old_parent, params, change_applied);
+ if (params.new_parent)
+ NotifyViewTreeChangeUp(params.new_parent, params, change_applied);
+}
+
+class ScopedTreeNotifier {
+ public:
+ ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) {
+ params_.target = target;
+ params_.old_parent = old_parent;
+ params_.new_parent = new_parent;
+ NotifyViewTreeChange(params_, false);
+ }
+ ~ScopedTreeNotifier() {
+ NotifyViewTreeChange(params_, true);
+ }
+
+ private:
+ ViewObserver::TreeChangeParams params_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
+};
+
+void RemoveChildImpl(View* child, View::Children* children) {
+ View::Children::iterator it =
+ std::find(children->begin(), children->end(), child);
+ if (it != children->end()) {
+ children->erase(it);
+ ViewPrivate(child).ClearParent();
+ }
+}
+
+class ScopedOrderChangedNotifier {
+ public:
+ ScopedOrderChangedNotifier(View* view,
+ View* relative_view,
+ OrderDirection direction)
+ : view_(view),
+ relative_view_(relative_view),
+ direction_(direction) {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(view_).observers(),
+ OnViewReordering(view_, relative_view_, direction_));
+ }
+ ~ScopedOrderChangedNotifier() {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(view_).observers(),
+ OnViewReordered(view_, relative_view_, direction_));
+ }
+
+ private:
+ View* view_;
+ View* relative_view_;
+ OrderDirection direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
+};
+
+// Returns true if the order actually changed.
+bool ReorderImpl(View::Children* children,
+ View* view,
+ View* relative,
+ OrderDirection direction) {
+ DCHECK(relative);
+ DCHECK_NE(view, relative);
+ DCHECK_EQ(view->parent(), relative->parent());
+
+ const size_t child_i =
+ std::find(children->begin(), children->end(), view) - children->begin();
+ const size_t target_i =
+ std::find(children->begin(), children->end(), relative) -
+ children->begin();
+ if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
+ (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ ScopedOrderChangedNotifier notifier(view, relative, direction);
+
+ const size_t dest_i = direction == ORDER_DIRECTION_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, view);
+
+ return true;
+}
+
+class ScopedSetBoundsNotifier {
+ public:
+ ScopedSetBoundsNotifier(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds)
+ : view_(view),
+ old_bounds_(old_bounds),
+ new_bounds_(new_bounds) {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(view_).observers(),
+ OnViewBoundsChanging(view_, old_bounds_, new_bounds_));
+ }
+ ~ScopedSetBoundsNotifier() {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(view_).observers(),
+ OnViewBoundsChanged(view_, old_bounds_, new_bounds_));
+ }
+
+ private:
+ View* view_;
+ const gfx::Rect old_bounds_;
+ const gfx::Rect new_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
+};
+
+// Some operations are only permitted in the connection that created the view.
+bool OwnsView(ViewManager* manager, View* view) {
+ return !manager ||
+ static_cast<ViewManagerClientImpl*>(manager)->OwnsView(view->id());
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// View, public:
+
+// static
+View* View::Create(ViewManager* view_manager) {
+ View* view = new View(view_manager);
+ static_cast<ViewManagerClientImpl*>(view_manager)->AddView(view);
+ return view;
+}
+
+void View::Destroy() {
+ if (!OwnsView(manager_, this))
+ return;
+
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->DestroyView(id_);
+ while (!children_.empty()) {
+ View* child = children_.front();
+ if (!OwnsView(manager_, child)) {
+ ViewPrivate(child).ClearParent();
+ children_.erase(children_.begin());
+ } else {
+ child->Destroy();
+ DCHECK(std::find(children_.begin(), children_.end(), child) ==
+ children_.end());
+ }
+ }
+ LocalDestroy();
+}
+
+void View::SetBounds(const gfx::Rect& bounds) {
+ if (!OwnsView(manager_, this))
+ return;
+
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->SetBounds(id_, bounds);
+ LocalSetBounds(bounds_, bounds);
+}
+
+void View::SetVisible(bool value) {
+ if (visible_ == value)
+ return;
+
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->SetVisible(id_, value);
+ FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this));
+ visible_ = value;
+ FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(this));
+}
+
+bool View::IsDrawn() const {
+ if (!visible_)
+ return false;
+ return parent_ ? parent_->IsDrawn() : drawn_;
+}
+
+void View::AddObserver(ViewObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void View::RemoveObserver(ViewObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void View::AddChild(View* child) {
+ // TODO(beng): not necessarily valid to all connections, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (manager_)
+ CHECK_EQ(ViewPrivate(child).view_manager(), manager_);
+ LocalAddChild(child);
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->AddChild(child->id(), id_);
+}
+
+void View::RemoveChild(View* child) {
+ // TODO(beng): not necessarily valid to all connections, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (manager_)
+ CHECK_EQ(ViewPrivate(child).view_manager(), manager_);
+ LocalRemoveChild(child);
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->RemoveChild(child->id(),
+ id_);
+ }
+}
+
+void View::MoveToFront() {
+ if (!parent_ || parent_->children_.back() == this)
+ return;
+ Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE);
+}
+
+void View::MoveToBack() {
+ if (!parent_ || parent_->children_.front() == this)
+ return;
+ Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW);
+}
+
+void View::Reorder(View* relative, OrderDirection direction) {
+ if (!LocalReorder(relative, direction))
+ return;
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->Reorder(id_,
+ relative->id(),
+ direction);
+ }
+}
+
+bool View::Contains(View* child) const {
+ if (manager_)
+ CHECK_EQ(ViewPrivate(child).view_manager(), manager_);
+ for (View* p = child->parent(); p; p = p->parent()) {
+ if (p == this)
+ return true;
+ }
+ return false;
+}
+
+View* View::GetChildById(Id id) {
+ if (id == id_)
+ return this;
+ // TODO(beng): this could be improved depending on how we decide to own views.
+ Children::const_iterator it = children_.begin();
+ for (; it != children_.end(); ++it) {
+ View* view = (*it)->GetChildById(id);
+ if (view)
+ return view;
+ }
+ return NULL;
+}
+
+void View::SetSurfaceId(SurfaceIdPtr id) {
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->SetSurfaceId(id_, id.Pass());
+ }
+}
+
+void View::SetContents(const SkBitmap& contents) {
+ if (manager_) {
+ if (!bitmap_uploader_)
+ CreateBitmapUploader();
+ bitmap_uploader_->SetSize(bounds_.size());
+ bitmap_uploader_->SetBitmap(contents);
+ }
+}
+
+void View::SetColor(SkColor color) {
+ if (manager_) {
+ if (!bitmap_uploader_)
+ CreateBitmapUploader();
+ bitmap_uploader_->SetSize(bounds_.size());
+ bitmap_uploader_->SetColor(color);
+ }
+}
+
+void View::SetFocus() {
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_);
+}
+
+void View::Embed(const String& url) {
+ static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_);
+}
+
+scoped_ptr<ServiceProvider>
+ View::Embed(const String& url,
+ scoped_ptr<ServiceProviderImpl> exported_services) {
+ scoped_ptr<ServiceProvider> imported_services;
+ // BindToProxy() takes ownership of |exported_services|.
+ ServiceProviderImpl* registry = exported_services.release();
+ ServiceProviderPtr sp;
+ if (registry) {
+ BindToProxy(registry, &sp);
+ imported_services.reset(registry->CreateRemoteServiceProvider());
+ }
+ static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_, sp.Pass());
+ return imported_services.Pass();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, protected:
+
+View::View()
+ : manager_(NULL),
+ id_(static_cast<Id>(-1)),
+ parent_(NULL),
+ visible_(true),
+ drawn_(false) {
+}
+
+View::~View() {
+ FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(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_)->RemoveView(id_);
+ FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, private:
+
+View::View(ViewManager* manager)
+ : manager_(manager),
+ id_(static_cast<ViewManagerClientImpl*>(manager_)->CreateView()),
+ parent_(NULL),
+ visible_(true),
+ drawn_(false) {
+}
+
+void View::LocalDestroy() {
+ delete this;
+}
+
+void View::LocalAddChild(View* child) {
+ ScopedTreeNotifier notifier(child, child->parent(), this);
+ if (child->parent())
+ RemoveChildImpl(child, &child->parent_->children_);
+ children_.push_back(child);
+ child->parent_ = this;
+}
+
+void View::LocalRemoveChild(View* child) {
+ DCHECK_EQ(this, child->parent());
+ ScopedTreeNotifier notifier(child, this, NULL);
+ RemoveChildImpl(child, &children_);
+}
+
+bool View::LocalReorder(View* relative, OrderDirection direction) {
+ return ReorderImpl(&parent_->children_, this, relative, direction);
+}
+
+void View::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;
+}
+
+void View::LocalSetDrawn(bool value) {
+ if (drawn_ == value)
+ return;
+
+ // As IsDrawn() is derived from |visible_| and |drawn_|, only send drawn
+ // notification is the value of IsDrawn() is really changing.
+ if (IsDrawn() == value) {
+ drawn_ = value;
+ return;
+ }
+ FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanging(this));
+ drawn_ = value;
+ FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanged(this));
+}
+
+void View::CreateBitmapUploader() {
+ ViewManagerClientImpl* vmci = static_cast<ViewManagerClientImpl*>(manager_);
+ SurfacesServicePtr surfaces_service;
+ InterfacePtr<ServiceProvider> surfaces_service_provider;
+ vmci->shell()->ConnectToApplication("mojo:mojo_surfaces_service",
+ Get(&surfaces_service_provider));
+ ConnectToService(surfaces_service_provider.get(), &surfaces_service);
+ GpuPtr gpu_service;
+ InterfacePtr<ServiceProvider> gpu_service_provider;
+ vmci->shell()->ConnectToApplication("mojo:mojo_native_viewport_service",
+ Get(&gpu_service_provider));
+ ConnectToService(gpu_service_provider.get(), &gpu_service);
+ bitmap_uploader_.reset(new BitmapUploader(
+ vmci, id_, surfaces_service.Pass(), gpu_service.Pass()));
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc
new file mode 100644
index 0000000..8b8ada1
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.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/public/cpp/view_manager/view_manager_client_factory.h"
+
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+
+namespace mojo {
+
+ViewManagerClientFactory::ViewManagerClientFactory(
+ Shell* shell,
+ ViewManagerDelegate* delegate)
+ : shell_(shell), delegate_(delegate) {
+}
+
+ViewManagerClientFactory::~ViewManagerClientFactory() {
+}
+
+// InterfaceFactory<ViewManagerClient> implementation.
+void ViewManagerClientFactory::Create(
+ ApplicationConnection* connection,
+ InterfaceRequest<ViewManagerClient> request) {
+ BindToRequest(new ViewManagerClientImpl(delegate_, shell_), &request);
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
new file mode 100644
index 0000000..19955b2
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
@@ -0,0 +1,428 @@
+// 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/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.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 "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace mojo {
+
+Id MakeTransportId(ConnectionSpecificId connection_id,
+ ConnectionSpecificId local_id) {
+ return (connection_id << 16) | local_id;
+}
+
+// Helper called to construct a local view object from transport data.
+View* AddViewToViewManager(ViewManagerClientImpl* client,
+ View* parent,
+ const ViewDataPtr& view_data) {
+ // 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 view.
+ View* view = ViewPrivate::LocalCreate();
+ ViewPrivate private_view(view);
+ private_view.set_view_manager(client);
+ private_view.set_id(view_data->view_id);
+ private_view.set_visible(view_data->visible);
+ private_view.set_drawn(view_data->drawn);
+ client->AddView(view);
+ private_view.LocalSetBounds(gfx::Rect(), view_data->bounds.To<gfx::Rect>());
+ if (parent)
+ ViewPrivate(parent).LocalAddChild(view);
+ return view;
+}
+
+View* BuildViewTree(ViewManagerClientImpl* client,
+ const Array<ViewDataPtr>& views,
+ View* initial_parent) {
+ std::vector<View*> parents;
+ View* root = NULL;
+ View* last_view = NULL;
+ if (initial_parent)
+ parents.push_back(initial_parent);
+ for (size_t i = 0; i < views.size(); ++i) {
+ if (last_view && views[i]->parent_id == last_view->id()) {
+ parents.push_back(last_view);
+ } else if (!parents.empty()) {
+ while (parents.back()->id() != views[i]->parent_id)
+ parents.pop_back();
+ }
+ View* view = AddViewToViewManager(
+ client, !parents.empty() ? parents.back() : NULL, views[i]);
+ if (!last_view)
+ root = view;
+ last_view = view;
+ }
+ return root;
+}
+
+// Responsible for removing a root from the ViewManager when that view is
+// destroyed.
+class RootObserver : public ViewObserver {
+ public:
+ explicit RootObserver(View* root) : root_(root) {}
+ virtual ~RootObserver() {}
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewDestroyed(View* view) OVERRIDE {
+ DCHECK_EQ(view, root_);
+ static_cast<ViewManagerClientImpl*>(
+ ViewPrivate(root_).view_manager())->RemoveRoot(root_);
+ view->RemoveObserver(this);
+ delete this;
+ }
+
+ View* root_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootObserver);
+};
+
+ViewManagerClientImpl::ViewManagerClientImpl(ViewManagerDelegate* delegate,
+ Shell* shell)
+ : connected_(false),
+ connection_id_(0),
+ next_id_(1),
+ delegate_(delegate),
+ window_manager_delegate_(NULL),
+ shell_(shell) {
+ // TODO(beng): Come up with a better way of establishing a configuration for
+ // what the active window manager is.
+ std::string window_manager_url = "mojo:mojo_window_manager";
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch("window-manager")) {
+ window_manager_url =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ "window-manager");
+ }
+ InterfacePtr<ServiceProvider> sp;
+ shell->ConnectToApplication(window_manager_url, Get(&sp));
+ ConnectToService(sp.get(), &window_manager_);
+ window_manager_.set_client(this);
+}
+
+ViewManagerClientImpl::~ViewManagerClientImpl() {
+ std::vector<View*> non_owned;
+ while (!views_.empty()) {
+ IdToViewMap::iterator it = views_.begin();
+ if (OwnsView(it->second->id())) {
+ it->second->Destroy();
+ } else {
+ non_owned.push_back(it->second);
+ views_.erase(it);
+ }
+ }
+ // Delete the non-owned views last. In the typical case these are roots. The
+ // exception is the window manager, which may know aboutother random views
+ // that it doesn't own.
+ // NOTE: we manually delete as we're a friend.
+ for (size_t i = 0; i < non_owned.size(); ++i)
+ delete non_owned[i];
+
+ delegate_->OnViewManagerDisconnected(this);
+}
+
+Id ViewManagerClientImpl::CreateView() {
+ DCHECK(connected_);
+ const Id view_id = MakeTransportId(connection_id_, ++next_id_);
+ service_->CreateView(view_id, ActionCompletedCallbackWithErrorCode());
+ return view_id;
+}
+
+void ViewManagerClientImpl::DestroyView(Id view_id) {
+ DCHECK(connected_);
+ service_->DeleteView(view_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::AddChild(Id child_id, Id parent_id) {
+ DCHECK(connected_);
+ service_->AddView(parent_id, child_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::RemoveChild(Id child_id, Id parent_id) {
+ DCHECK(connected_);
+ service_->RemoveViewFromParent(child_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Reorder(
+ Id view_id,
+ Id relative_view_id,
+ OrderDirection direction) {
+ DCHECK(connected_);
+ service_->ReorderView(view_id, relative_view_id, direction,
+ ActionCompletedCallback());
+}
+
+bool ViewManagerClientImpl::OwnsView(Id id) const {
+ return HiWord(id) == connection_id_;
+}
+
+void ViewManagerClientImpl::SetBounds(Id view_id, const gfx::Rect& bounds) {
+ DCHECK(connected_);
+ service_->SetViewBounds(view_id, Rect::From(bounds),
+ ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetSurfaceId(Id view_id, SurfaceIdPtr surface_id) {
+ DCHECK(connected_);
+ if (surface_id.is_null())
+ return;
+ service_->SetViewSurfaceId(
+ view_id, surface_id.Pass(), ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetFocus(Id view_id) {
+ window_manager_->FocusWindow(view_id, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::SetVisible(Id view_id, bool visible) {
+ DCHECK(connected_);
+ service_->SetViewVisibility(view_id, visible, ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Embed(const String& url, Id view_id) {
+ ServiceProviderPtr sp;
+ BindToProxy(new ServiceProviderImpl, &sp);
+ Embed(url, view_id, sp.Pass());
+}
+
+void ViewManagerClientImpl::Embed(
+ const String& url,
+ Id view_id,
+ ServiceProviderPtr service_provider) {
+ DCHECK(connected_);
+ service_->Embed(url, view_id, service_provider.Pass(),
+ ActionCompletedCallback());
+}
+
+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:
+
+void ViewManagerClientImpl::SetWindowManagerDelegate(
+ WindowManagerDelegate* window_manager_delegate) {
+ CHECK(NULL != GetViewById(1));
+ CHECK(!window_manager_delegate_);
+ window_manager_delegate_ = window_manager_delegate;
+}
+
+void ViewManagerClientImpl::DispatchEvent(View* target, EventPtr event) {
+ CHECK(window_manager_delegate_);
+ service_->DispatchOnViewInputEvent(target->id(), event.Pass());
+}
+
+const std::string& ViewManagerClientImpl::GetEmbedderURL() const {
+ return creator_url_;
+}
+
+const std::vector<View*>& ViewManagerClientImpl::GetRoots() const {
+ return roots_;
+}
+
+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::OnEmbed(
+ ConnectionSpecificId connection_id,
+ const String& creator_url,
+ ViewDataPtr root_data,
+ InterfaceRequest<ServiceProvider> service_provider) {
+ if (!connected_) {
+ connected_ = true;
+ connection_id_ = connection_id;
+ creator_url_ = String::From(creator_url);
+ } else {
+ DCHECK_EQ(connection_id_, connection_id);
+ DCHECK_EQ(creator_url_, creator_url);
+ }
+
+ // A new root must not already exist as a root or be contained by an existing
+ // hierarchy visible to this view manager.
+ View* root = AddViewToViewManager(this, NULL, root_data);
+ roots_.push_back(root);
+ root->AddObserver(new RootObserver(root));
+
+ // BindToRequest() binds the lifetime of |exported_services| to the pipe.
+ ServiceProviderImpl* exported_services = new ServiceProviderImpl;
+ BindToRequest(exported_services, &service_provider);
+ scoped_ptr<ServiceProvider> remote(
+ exported_services->CreateRemoteServiceProvider());
+ delegate_->OnEmbed(this, root, exported_services, remote.Pass());
+}
+
+void ViewManagerClientImpl::OnViewBoundsChanged(Id view_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) {
+ View* view = GetViewById(view_id);
+ ViewPrivate(view).LocalSetBounds(old_bounds.To<gfx::Rect>(),
+ new_bounds.To<gfx::Rect>());
+}
+
+void ViewManagerClientImpl::OnViewHierarchyChanged(
+ Id view_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ mojo::Array<ViewDataPtr> views) {
+ View* initial_parent = views.size() ?
+ GetViewById(views[0]->parent_id) : NULL;
+
+ BuildViewTree(this, views, initial_parent);
+
+ View* new_parent = GetViewById(new_parent_id);
+ View* old_parent = GetViewById(old_parent_id);
+ View* view = GetViewById(view_id);
+ if (new_parent)
+ ViewPrivate(new_parent).LocalAddChild(view);
+ else
+ ViewPrivate(old_parent).LocalRemoveChild(view);
+}
+
+void ViewManagerClientImpl::OnViewReordered(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction) {
+ View* view = GetViewById(view_id);
+ View* relative_view = GetViewById(relative_view_id);
+ if (view && relative_view)
+ ViewPrivate(view).LocalReorder(relative_view, direction);
+}
+
+void ViewManagerClientImpl::OnViewDeleted(Id view_id) {
+ View* view = GetViewById(view_id);
+ if (view)
+ ViewPrivate(view).LocalDestroy();
+}
+
+void ViewManagerClientImpl::OnViewVisibilityChanged(Id view_id, bool visible) {
+ // TODO(sky): there is a race condition here. If this client and another
+ // client change the visibility at the same time the wrong value may be set.
+ // Deal with this some how.
+ View* view = GetViewById(view_id);
+ if (view)
+ view->SetVisible(visible);
+}
+
+void ViewManagerClientImpl::OnViewDrawnStateChanged(Id view_id, bool drawn) {
+ View* view = GetViewById(view_id);
+ if (view)
+ ViewPrivate(view).LocalSetDrawn(drawn);
+}
+
+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::Embed(
+ const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) {
+ if (window_manager_delegate_)
+ window_manager_delegate_->Embed(url, service_provider.Pass());
+}
+
+void ViewManagerClientImpl::DispatchOnViewInputEvent(EventPtr event) {
+ if (window_manager_delegate_)
+ window_manager_delegate_->DispatchEvent(event.Pass());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, WindowManagerClient implementation:
+
+void ViewManagerClientImpl::OnWindowManagerReady() {}
+
+void ViewManagerClientImpl::OnCaptureChanged(Id old_capture_view_id,
+ Id new_capture_view_id) {}
+
+void ViewManagerClientImpl::OnFocusChanged(Id old_focused_view_id,
+ Id new_focused_view_id) {
+ View* focused = GetViewById(new_focused_view_id);
+ View* blurred = GetViewById(old_focused_view_id);
+ if (blurred) {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(blurred).observers(),
+ OnViewFocusChanged(focused, blurred));
+ }
+ if (focused) {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(focused).observers(),
+ OnViewFocusChanged(focused, blurred));
+ }
+}
+
+void ViewManagerClientImpl::OnActiveWindowChanged(Id old_focused_window,
+ Id new_focused_window) {}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, private:
+
+void ViewManagerClientImpl::RemoveRoot(View* root) {
+ std::vector<View*>::iterator it =
+ std::find(roots_.begin(), roots_.end(), root);
+ if (it != roots_.end())
+ roots_.erase(it);
+}
+
+void ViewManagerClientImpl::OnActionCompleted(bool success) {
+ if (!change_acked_callback_.is_null())
+ change_acked_callback_.Run();
+}
+
+void ViewManagerClientImpl::OnActionCompletedWithErrorCode(ErrorCode code) {
+ OnActionCompleted(code == ERROR_CODE_NONE);
+}
+
+base::Callback<void(bool)> ViewManagerClientImpl::ActionCompletedCallback() {
+ return base::Bind(&ViewManagerClientImpl::OnActionCompleted,
+ base::Unretained(this));
+}
+
+base::Callback<void(ErrorCode)>
+ ViewManagerClientImpl::ActionCompletedCallbackWithErrorCode() {
+ return base::Bind(&ViewManagerClientImpl::OnActionCompletedWithErrorCode,
+ base::Unretained(this));
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
new file mode 100644
index 0000000..bd09a58
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
@@ -0,0 +1,167 @@
+// 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/types.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
+
+class SkBitmap;
+
+namespace mojo {
+class ViewManager;
+class ViewManagerDelegate;
+class ViewManagerTransaction;
+class Shell;
+
+// Manages the connection with the View Manager service.
+class ViewManagerClientImpl : public ViewManager,
+ public InterfaceImpl<ViewManagerClient>,
+ public WindowManagerClient {
+ public:
+ ViewManagerClientImpl(ViewManagerDelegate* delegate, Shell* shell);
+ virtual ~ViewManagerClientImpl();
+
+ bool connected() const { return connected_; }
+ ConnectionSpecificId connection_id() const { return connection_id_; }
+
+ // API exposed to the view implementations that pushes local changes to the
+ // service.
+ 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 view.
+ void AddChild(Id child_id, Id parent_id);
+ void RemoveChild(Id child_id, Id parent_id);
+
+ void Reorder(Id view_id, Id relative_view_id, OrderDirection direction);
+
+ // Returns true if the specified view was created by this connection.
+ bool OwnsView(Id id) const;
+
+ void SetBounds(Id view_id, const gfx::Rect& bounds);
+ void SetSurfaceId(Id view_id, SurfaceIdPtr surface_id);
+ void SetFocus(Id view_id);
+ void SetVisible(Id view_id, bool visible);
+
+ void Embed(const String& url, Id view_id);
+ void Embed(const String& url,
+ Id view_id,
+ ServiceProviderPtr service_provider);
+
+ void set_change_acked_callback(const base::Callback<void(void)>& callback) {
+ change_acked_callback_ = callback;
+ }
+ void ClearChangeAckedCallback() {
+ change_acked_callback_ = base::Callback<void(void)>();
+ }
+
+ // Start/stop tracking views. While tracked, they can be retrieved via
+ // ViewManager::GetViewById.
+ void AddView(View* view);
+ void RemoveView(Id view_id);
+
+ Shell* shell() { return shell_; }
+
+ private:
+ friend class RootObserver;
+
+ typedef std::map<Id, View*> IdToViewMap;
+
+ // Overridden from ViewManager:
+ virtual void SetWindowManagerDelegate(
+ WindowManagerDelegate* delegate) OVERRIDE;
+ virtual void DispatchEvent(View* target, EventPtr event) OVERRIDE;
+ virtual const std::string& GetEmbedderURL() const OVERRIDE;
+ virtual const std::vector<View*>& GetRoots() const OVERRIDE;
+ virtual View* GetViewById(Id id) OVERRIDE;
+
+ // Overridden from InterfaceImpl:
+ virtual void OnConnectionEstablished() OVERRIDE;
+
+ // Overridden from ViewManagerClient:
+ virtual void OnEmbed(ConnectionSpecificId connection_id,
+ const String& creator_url,
+ ViewDataPtr root,
+ InterfaceRequest<ServiceProvider> services) OVERRIDE;
+ virtual void OnViewBoundsChanged(Id view_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) OVERRIDE;
+ virtual void OnViewHierarchyChanged(Id view_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Array<ViewDataPtr> views) OVERRIDE;
+ virtual void OnViewReordered(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction) OVERRIDE;
+ virtual void OnViewDeleted(Id view_id) OVERRIDE;
+ virtual void OnViewVisibilityChanged(Id view_id, bool visible) OVERRIDE;
+ virtual void OnViewDrawnStateChanged(Id view_id, bool drawn) OVERRIDE;
+ virtual void OnViewInputEvent(Id view_id,
+ EventPtr event,
+ const Callback<void()>& callback) OVERRIDE;
+ virtual void Embed(
+ const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) OVERRIDE;
+ virtual void DispatchOnViewInputEvent(EventPtr event) OVERRIDE;
+
+ // Overridden from WindowManagerClient:
+ virtual void OnWindowManagerReady() OVERRIDE;
+ virtual void OnCaptureChanged(Id old_capture_view_id,
+ Id new_capture_view_id) OVERRIDE;
+ virtual void OnFocusChanged(Id old_focused_view_id,
+ Id new_focused_view_id) OVERRIDE;
+ virtual void OnActiveWindowChanged(Id old_focused_window,
+ Id new_focused_window) OVERRIDE;
+
+ void RemoveRoot(View* root);
+
+ void OnActionCompleted(bool success);
+ void OnActionCompletedWithErrorCode(ErrorCode code);
+
+ BitmapUploader* BitmapUploaderForView(Id view_id);
+
+ base::Callback<void(bool)> ActionCompletedCallback();
+ base::Callback<void(ErrorCode)> ActionCompletedCallbackWithErrorCode();
+
+ bool connected_;
+ ConnectionSpecificId connection_id_;
+ ConnectionSpecificId next_id_;
+
+ std::string creator_url_;
+
+ base::Callback<void(void)> change_acked_callback_;
+
+ ViewManagerDelegate* delegate_;
+ WindowManagerDelegate* window_manager_delegate_;
+
+ std::vector<View*> roots_;
+
+ IdToViewMap views_;
+
+ ViewManagerService* service_;
+
+ WindowManagerServicePtr window_manager_;
+
+ // TODO(jamesr): Remove once all callers switch from SetContents to
+ // SetSurfaceId.
+ Shell* shell_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_context.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_context.cc
new file mode 100644
index 0000000..284286e
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_context.cc
@@ -0,0 +1,55 @@
+// 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_manager_context.h"
+
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+
+namespace mojo {
+class ApplicationImpl;
+
+void ConnectCallback(bool success) {}
+
+class ViewManagerContext::InternalState {
+ public:
+ InternalState(ApplicationImpl* application_impl) {
+ application_impl->ConnectToService("mojo:mojo_view_manager",
+ &init_service_);
+ }
+ ~InternalState() {}
+
+ ViewManagerInitService* init_service() { return init_service_.get(); }
+
+ private:
+ ViewManagerInitServicePtr init_service_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InternalState);
+};
+
+ViewManagerContext::ViewManagerContext(ApplicationImpl* application_impl)
+ : state_(new InternalState(application_impl)) {}
+ViewManagerContext::~ViewManagerContext() {}
+
+void ViewManagerContext::Embed(const String& url) {
+ scoped_ptr<ServiceProviderImpl> spi(new ServiceProviderImpl);
+ Embed(url, spi.Pass());
+}
+
+scoped_ptr<ServiceProvider> ViewManagerContext::Embed(
+ const String& url,
+ scoped_ptr<ServiceProviderImpl> exported_services) {
+ scoped_ptr<ServiceProvider> imported_services;
+ // BindToProxy() takes ownership of |exported_services|.
+ ServiceProviderImpl* registry = exported_services.release();
+ ServiceProviderPtr sp;
+ if (registry) {
+ BindToProxy(registry, &sp);
+ imported_services.reset(registry->CreateRemoteServiceProvider());
+ }
+ state_->init_service()->Embed(url, sp.Pass(), base::Bind(&ConnectCallback));
+ return imported_services.Pass();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc
new file mode 100644
index 0000000..abe0906
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc
@@ -0,0 +1,39 @@
+// 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 "base/i18n/icu_util.h"
+#include "ui/gl/gl_surface.h"
+
+#if defined(USE_X11)
+#include "ui/gfx/x/x11_connection.h"
+#endif
+
+namespace mojo {
+
+ViewManagerTestSuite::ViewManagerTestSuite(int argc, char** argv)
+ : TestSuite(argc, argv) {}
+
+ViewManagerTestSuite::~ViewManagerTestSuite() {
+}
+
+void ViewManagerTestSuite::Initialize() {
+#if defined(USE_X11)
+ // Each test ends up creating a new thread for the native viewport service.
+ // In other words we'll use X on different threads, so tell it that.
+ gfx::InitializeThreadedX11();
+#endif
+
+#if defined(COMPONENT_BUILD)
+ gfx::GLSurface::InitializeOneOffForTests();
+#endif
+
+ base::TestSuite::Initialize();
+
+ // base::TestSuite and ViewsInit both try to load icu. That's ok for tests.
+ base::i18n::AllowMultipleInitializeCallsForTesting();
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h
new file mode 100644
index 0000000..e073bbd
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h
@@ -0,0 +1,26 @@
+// 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 {
+
+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 mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc
new file mode 100644
index 0000000..9f4f109
--- /dev/null
+++ b/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::ViewManagerTestSuite test_suite(argc, argv);
+
+ return base::LaunchUnitTests(
+ argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/services/public/cpp/view_manager/lib/view_observer.cc b/mojo/services/public/cpp/view_manager/lib/view_observer.cc
new file mode 100644
index 0000000..812c028
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_observer.cc
@@ -0,0 +1,20 @@
+// 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_observer.h"
+
+#include "base/basictypes.h"
+
+namespace mojo {
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewObserver, public:
+
+ViewObserver::TreeChangeParams::TreeChangeParams()
+ : target(NULL),
+ old_parent(NULL),
+ new_parent(NULL),
+ receiver(NULL) {}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_private.cc b/mojo/services/public/cpp/view_manager/lib/view_private.cc
new file mode 100644
index 0000000..5125c7d
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_private.cc
@@ -0,0 +1,21 @@
+// 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 {
+
+ViewPrivate::ViewPrivate(View* view)
+ : view_(view) {
+}
+
+ViewPrivate::~ViewPrivate() {
+}
+
+// static
+View* ViewPrivate::LocalCreate() {
+ return new View;
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/lib/view_private.h b/mojo/services/public/cpp/view_manager/lib/view_private.h
new file mode 100644
index 0000000..73138e2
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/lib/view_private.h
@@ -0,0 +1,65 @@
+// 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 {
+
+// This class is a friend of a View and contains functions to mutate internal
+// state of View.
+class ViewPrivate {
+ public:
+ explicit ViewPrivate(View* view);
+ ~ViewPrivate();
+
+ // Creates and returns a new View. Caller owns the return value.
+ static View* LocalCreate();
+
+ ObserverList<ViewObserver>* observers() { return &view_->observers_; }
+
+ void ClearParent() { view_->parent_ = NULL; }
+
+ void set_visible(bool visible) { view_->visible_ = visible; }
+
+ void set_drawn(bool drawn) { view_->drawn_ = drawn; }
+
+ void set_id(Id id) { view_->id_ = id; }
+
+ ViewManager* view_manager() { return view_->manager_; }
+ void set_view_manager(ViewManager* manager) {
+ view_->manager_ = manager;
+ }
+
+ void LocalDestroy() {
+ view_->LocalDestroy();
+ }
+ void LocalAddChild(View* child) {
+ view_->LocalAddChild(child);
+ }
+ void LocalRemoveChild(View* child) {
+ view_->LocalRemoveChild(child);
+ }
+ void LocalReorder(View* relative, OrderDirection direction) {
+ view_->LocalReorder(relative, direction);
+ }
+ void LocalSetBounds(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ view_->LocalSetBounds(old_bounds, new_bounds);
+ }
+ void LocalSetDrawn(bool drawn) { view_->LocalSetDrawn(drawn); }
+
+ private:
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewPrivate);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
diff --git a/mojo/services/public/cpp/view_manager/tests/BUILD.gn b/mojo/services/public/cpp/view_manager/tests/BUILD.gn
new file mode 100644
index 0000000..a468a92
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_lib_unittests
+test("mojo_view_manager_lib_unittests") {
+ sources = [
+ "view_unittest.cc",
+ "view_manager_unittest.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//testing/gtest",
+ "//ui/gfx",
+ "//ui/gfx:test_support",
+ "//mojo/application_manager",
+ "//mojo/environment:chromium",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/shell:test_support",
+ "//mojo/services/public/interfaces/view_manager",
+ "//mojo/services/public/cpp/view_manager",
+ ]
+ if (use_aura) {
+ deps += [ "//mojo/services/public/cpp/view_manager/lib:run_unittests" ]
+ } else {
+ deps += [ "//mojo/common/test:run_all_unittests" ]
+ }
+}
diff --git a/mojo/services/public/cpp/view_manager/tests/DEPS b/mojo/services/public/cpp/view_manager/tests/DEPS
new file mode 100644
index 0000000..2ecf3db
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/application_manager",
+]
diff --git a/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc
new file mode 100644
index 0000000..693e91a
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc
@@ -0,0 +1,650 @@
+// 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_manager.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/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/util.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kWindowManagerURL[] = "mojo:window_manager";
+const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
+
+base::RunLoop* current_run_loop = NULL;
+
+void DoRunLoop() {
+ base::RunLoop run_loop;
+ current_run_loop = &run_loop;
+ current_run_loop->Run();
+ current_run_loop = NULL;
+}
+
+void QuitRunLoop() {
+ current_run_loop->Quit();
+}
+
+class ConnectApplicationLoader : public ApplicationLoader,
+ public ApplicationDelegate,
+ public ViewManagerDelegate {
+ public:
+ typedef base::Callback<void(ViewManager*, View*)> LoadedCallback;
+
+ explicit ConnectApplicationLoader(const LoadedCallback& callback)
+ : callback_(callback) {}
+ virtual ~ConnectApplicationLoader() {}
+
+ private:
+ // Overridden from ApplicationDelegate:
+ virtual void Initialize(ApplicationImpl* app) override {
+ view_manager_client_factory_.reset(
+ new ViewManagerClientFactory(app->shell(), this));
+ }
+
+ // Overridden from ApplicationLoader:
+ virtual void Load(ApplicationManager* manager,
+ const GURL& url,
+ scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
+ ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+ if (!shell_handle.is_valid())
+ return;
+ scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
+ shell_handle.Pass()));
+ apps_.push_back(app.release());
+ }
+
+ virtual void OnApplicationError(ApplicationManager* manager,
+ const GURL& url) OVERRIDE {}
+
+ virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+ OVERRIDE {
+ connection->AddService(view_manager_client_factory_.get());
+ return true;
+ }
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnEmbed(ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* exported_services,
+ scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
+ callback_.Run(view_manager, root);
+ }
+ virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {}
+
+ ScopedVector<ApplicationImpl> apps_;
+ LoadedCallback callback_;
+ scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader);
+};
+
+class BoundsChangeObserver : public ViewObserver {
+ public:
+ explicit BoundsChangeObserver(View* view) : view_(view) {}
+ virtual ~BoundsChangeObserver() {}
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {
+ DCHECK_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+// Wait until the bounds of the supplied view change.
+void WaitForBoundsToChange(View* view) {
+ BoundsChangeObserver observer(view);
+ view->AddObserver(&observer);
+ DoRunLoop();
+ view->RemoveObserver(&observer);
+}
+
+// Spins a runloop until the tree beginning at |root| has |tree_size| views
+// (including |root|).
+class TreeSizeMatchesObserver : public ViewObserver {
+ public:
+ TreeSizeMatchesObserver(View* tree, size_t tree_size)
+ : tree_(tree),
+ tree_size_(tree_size) {}
+ virtual ~TreeSizeMatchesObserver() {}
+
+ bool IsTreeCorrectSize() {
+ return CountViews(tree_) == tree_size_;
+ }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE {
+ if (IsTreeCorrectSize())
+ QuitRunLoop();
+ }
+
+ size_t CountViews(const View* view) const {
+ size_t count = 1;
+ View::Children::const_iterator it = view->children().begin();
+ for (; it != view->children().end(); ++it)
+ count += CountViews(*it);
+ return count;
+ }
+
+ View* tree_;
+ size_t tree_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
+};
+
+void WaitForTreeSizeToMatch(View* view, size_t tree_size) {
+ TreeSizeMatchesObserver observer(view, tree_size);
+ if (observer.IsTreeCorrectSize())
+ return;
+ view->AddObserver(&observer);
+ DoRunLoop();
+ view->RemoveObserver(&observer);
+}
+
+// Utility class that waits for the destruction of some number of views and
+// views.
+class DestructionObserver : public ViewObserver {
+ public:
+ // |views| or |views| can be NULL.
+ explicit DestructionObserver(std::set<Id>* views) : views_(views) {}
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewDestroyed(View* view) OVERRIDE {
+ std::set<Id>::iterator it = views_->find(view->id());
+ if (it != views_->end())
+ views_->erase(it);
+ if (CanQuit())
+ QuitRunLoop();
+ }
+
+ bool CanQuit() {
+ return !views_ || views_->empty();
+ }
+
+ std::set<Id>* views_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
+};
+
+void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) {
+ DestructionObserver observer(views);
+ DCHECK(views);
+ if (views) {
+ for (std::set<Id>::const_iterator it = views->begin();
+ it != views->end(); ++it) {
+ view_manager->GetViewById(*it)->AddObserver(&observer);
+ }
+ }
+ DoRunLoop();
+}
+
+class OrderChangeObserver : public ViewObserver {
+ public:
+ OrderChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ virtual ~OrderChangeObserver() {
+ view_->RemoveObserver(this);
+ }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewReordered(View* view,
+ View* relative_view,
+ OrderDirection direction) OVERRIDE {
+ DCHECK_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+void WaitForOrderChange(ViewManager* view_manager, View* view) {
+ OrderChangeObserver observer(view);
+ DoRunLoop();
+}
+
+// Tracks a view's destruction. Query is_valid() for current state.
+class ViewTracker : public ViewObserver {
+ public:
+ explicit ViewTracker(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ virtual ~ViewTracker() {
+ if (view_)
+ view_->RemoveObserver(this);
+ }
+
+ bool is_valid() const { return !!view_; }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewDestroyed(View* view) OVERRIDE {
+ DCHECK_EQ(view, view_);
+ view_ = NULL;
+ }
+
+ int id_;
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewTracker);
+};
+
+} // namespace
+
+// ViewManager -----------------------------------------------------------------
+
+// These tests model synchronization of two peer connections to the view manager
+// service, that are given access to some root view.
+
+class ViewManagerTest : public testing::Test {
+ public:
+ ViewManagerTest()
+ : connect_loop_(NULL),
+ loaded_view_manager_(NULL),
+ window_manager_(NULL),
+ commit_count_(0) {}
+
+ protected:
+ ViewManager* window_manager() { return window_manager_; }
+
+ View* CreateViewInParent(View* parent) {
+ ViewManager* parent_manager = ViewPrivate(parent).view_manager();
+ View* view = View::Create(parent_manager);
+ parent->AddChild(view);
+ return view;
+ }
+
+ // Embeds another version of the test app @ view.
+ ViewManager* Embed(ViewManager* view_manager, View* view) {
+ DCHECK_EQ(view_manager, ViewPrivate(view).view_manager());
+ view->Embed(kEmbeddedApp1URL);
+ RunRunLoop();
+ return GetLoadedViewManager();
+ }
+
+ ViewManager* GetLoadedViewManager() {
+ ViewManager* view_manager = loaded_view_manager_;
+ loaded_view_manager_ = NULL;
+ return view_manager;
+ }
+
+ void UnloadApplication(const GURL& url) {
+ test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url);
+ }
+
+ private:
+ // Overridden from testing::Test:
+ virtual void SetUp() OVERRIDE {
+ ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind(
+ &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this));
+ test_helper_.Init();
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(
+ new ConnectApplicationLoader(ready_callback)),
+ GURL(kWindowManagerURL));
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(
+ new ConnectApplicationLoader(ready_callback)),
+ GURL(kEmbeddedApp1URL));
+
+ test_helper_.application_manager()->ConnectToService(
+ GURL("mojo:mojo_view_manager"), &view_manager_init_);
+ ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL));
+ }
+
+ void EmbedRootCallback(bool* result_cache, bool result) {
+ *result_cache = result;
+ }
+
+ bool EmbedRoot(ViewManagerInitService* view_manager_init,
+ const std::string& url) {
+ bool result = false;
+ ServiceProviderPtr sp;
+ BindToProxy(new ServiceProviderImpl, &sp);
+ view_manager_init->Embed(
+ url, sp.Pass(),
+ base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this),
+ &result));
+ RunRunLoop();
+ window_manager_ = GetLoadedViewManager();
+ return result;
+ }
+
+ void OnViewManagerLoaded(ViewManager* view_manager, View* root) {
+ loaded_view_manager_ = view_manager;
+ connect_loop_->Quit();
+ }
+
+ void RunRunLoop() {
+ base::RunLoop run_loop;
+ connect_loop_ = &run_loop;
+ connect_loop_->Run();
+ connect_loop_ = NULL;
+ }
+
+ base::RunLoop* connect_loop_;
+ shell::ShellTestHelper test_helper_;
+ ViewManagerInitServicePtr view_manager_init_;
+ // Used to receive the most recent view manager loaded by an embed action.
+ ViewManager* loaded_view_manager_;
+ // The View Manager connection held by the window manager (app running at the
+ // root view).
+ ViewManager* window_manager_;
+ int commit_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+// TODO(sky): all of these tests are disabled as each test triggers running
+// ViewsInit, which tries to register the same set of paths with the
+// PathService, triggering a DCHECK.
+TEST_F(ViewManagerTest, DISABLED_SetUp) {}
+
+TEST_F(ViewManagerTest, DISABLED_Embed) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ EXPECT_TRUE(NULL != embedded);
+
+ View* view_in_embedded = embedded->GetRoots().front();
+ EXPECT_EQ(view->parent(), window_manager()->GetRoots().front());
+ EXPECT_EQ(NULL, view_in_embedded->parent());
+}
+
+// Window manager has two views, N1 and N11. Embeds A at N1. A should not see
+// N11.
+// TODO(sky): Update client lib to match server.
+TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ View* nested = View::Create(window_manager());
+ view->AddChild(nested);
+
+ ViewManager* embedded = Embed(window_manager(), view);
+ EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(),
+ nested->id());
+ EXPECT_TRUE(embedded->GetRoots().front()->children().empty());
+ EXPECT_TRUE(nested->parent() == NULL);
+}
+
+// http://crbug.com/396300
+TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+
+ Id view_id = view->id();
+
+ UnloadApplication(GURL(kWindowManagerURL));
+
+ std::set<Id> views;
+ views.insert(view_id);
+ WaitForDestruction(embedded, &views);
+
+ EXPECT_TRUE(embedded->GetRoots().empty());
+}
+
+// TODO(beng): write a replacement test for the one that once existed here:
+// This test validates the following scenario:
+// - a view originating from one connection
+// - a view originating from a second connection
+// + the connection originating the view is destroyed
+// -> the view should still exist (since the second connection is live) but
+// should be disconnected from any views.
+// http://crbug.com/396300
+//
+// TODO(beng): The new test should validate the scenario as described above
+// except that the second connection still has a valid tree.
+
+// Verifies that bounds changes applied to a view hierarchy in one connection
+// are reflected to another.
+TEST_F(ViewManagerTest, DISABLED_SetBounds) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+
+ View* view_in_embedded = embedded->GetViewById(view->id());
+ EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+
+ view->SetBounds(gfx::Rect(100, 100));
+ EXPECT_NE(view->bounds(), view_in_embedded->bounds());
+ WaitForBoundsToChange(view_in_embedded);
+ EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+}
+
+// Verifies that bounds changes applied to a view owned by a different
+// connection are refused.
+TEST_F(ViewManagerTest, DISABLED_SetBoundsSecurity) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+
+ View* view_in_embedded = embedded->GetViewById(view->id());
+ view->SetBounds(gfx::Rect(800, 600));
+ WaitForBoundsToChange(view_in_embedded);
+
+ view_in_embedded->SetBounds(gfx::Rect(1024, 768));
+ // Bounds change should have been rejected.
+ EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+}
+
+// Verifies that a view can only be destroyed by the connection that created it.
+TEST_F(ViewManagerTest, DISABLED_DestroySecurity) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+
+ View* view_in_embedded = embedded->GetViewById(view->id());
+
+ ViewTracker tracker2(view_in_embedded);
+ view_in_embedded->Destroy();
+ // View should not have been destroyed.
+ EXPECT_TRUE(tracker2.is_valid());
+
+ ViewTracker tracker1(view);
+ view->Destroy();
+ EXPECT_FALSE(tracker1.is_valid());
+}
+
+TEST_F(ViewManagerTest, DISABLED_MultiRoots) {
+ View* view1 = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view1);
+ View* view2 = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view2);
+ ViewManager* embedded1 = Embed(window_manager(), view1);
+ ViewManager* embedded2 = Embed(window_manager(), view2);
+ EXPECT_EQ(embedded1, embedded2);
+}
+
+TEST_F(ViewManagerTest, DISABLED_EmbeddingIdentity) {
+ View* view = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
+}
+
+TEST_F(ViewManagerTest, DISABLED_Reorder) {
+ View* view1 = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view1);
+
+ ViewManager* embedded = Embed(window_manager(), view1);
+
+ View* view11 = View::Create(embedded);
+ embedded->GetRoots().front()->AddChild(view11);
+ View* view12 = View::Create(embedded);
+ embedded->GetRoots().front()->AddChild(view12);
+
+ View* view1_in_wm = window_manager()->GetViewById(view1->id());
+
+ {
+ WaitForTreeSizeToMatch(view1, 2u);
+ view11->MoveToFront();
+ WaitForOrderChange(window_manager(),
+ window_manager()->GetViewById(view11->id()));
+
+ EXPECT_EQ(view1_in_wm->children().front(),
+ window_manager()->GetViewById(view12->id()));
+ EXPECT_EQ(view1_in_wm->children().back(),
+ window_manager()->GetViewById(view11->id()));
+ }
+
+ {
+ view11->MoveToBack();
+ WaitForOrderChange(window_manager(),
+ window_manager()->GetViewById(view11->id()));
+
+ EXPECT_EQ(view1_in_wm->children().front(),
+ window_manager()->GetViewById(view11->id()));
+ EXPECT_EQ(view1_in_wm->children().back(),
+ window_manager()->GetViewById(view12->id()));
+ }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public ViewObserver {
+ public:
+ explicit VisibilityChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ virtual ~VisibilityChangeObserver() { view_->RemoveObserver(this); }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewVisibilityChanged(View* view) OVERRIDE {
+ EXPECT_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewManagerTest, DISABLED_Visible) {
+ View* view1 = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view1);
+
+ // Embed another app and verify initial state.
+ ViewManager* embedded = Embed(window_manager(), view1);
+ ASSERT_EQ(1u, embedded->GetRoots().size());
+ View* embedded_root = embedded->GetRoots().front();
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+
+ // Change the visible state from the first connection and verify its mirrored
+ // correctly to the embedded app.
+ {
+ VisibilityChangeObserver observer(embedded_root);
+ view1->SetVisible(false);
+ DoRunLoop();
+ }
+
+ EXPECT_FALSE(view1->visible());
+ EXPECT_FALSE(view1->IsDrawn());
+
+ EXPECT_FALSE(embedded_root->visible());
+ EXPECT_FALSE(embedded_root->IsDrawn());
+
+ // Make the node visible again.
+ {
+ VisibilityChangeObserver observer(embedded_root);
+ view1->SetVisible(true);
+ DoRunLoop();
+ }
+
+ EXPECT_TRUE(view1->visible());
+ EXPECT_TRUE(view1->IsDrawn());
+
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+}
+
+namespace {
+
+class DrawnChangeObserver : public ViewObserver {
+ public:
+ explicit DrawnChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ virtual ~DrawnChangeObserver() { view_->RemoveObserver(this); }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewDrawnChanged(View* view) OVERRIDE {
+ EXPECT_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewManagerTest, DISABLED_Drawn) {
+ View* view1 = View::Create(window_manager());
+ window_manager()->GetRoots().front()->AddChild(view1);
+
+ // Embed another app and verify initial state.
+ ViewManager* embedded = Embed(window_manager(), view1);
+ ASSERT_EQ(1u, embedded->GetRoots().size());
+ View* embedded_root = embedded->GetRoots().front();
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+
+ // Change the visibility of the root, this should propagate a drawn state
+ // change to |embedded|.
+ {
+ DrawnChangeObserver observer(embedded_root);
+ window_manager()->GetRoots().front()->SetVisible(false);
+ DoRunLoop();
+ }
+
+ EXPECT_TRUE(view1->visible());
+ EXPECT_FALSE(view1->IsDrawn());
+
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_FALSE(embedded_root->IsDrawn());
+}
+
+// TODO(beng): tests for view event dispatcher.
+// - verify that we see events for all views.
+
+// TODO(beng): tests for focus:
+// - focus between two views known to a connection
+// - focus between views unknown to one of the connections.
+// - focus between views unknown to either connection.
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/tests/view_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
new file mode 100644
index 0000000..3f08075
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
@@ -0,0 +1,619 @@
+// 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 "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+// View ------------------------------------------------------------------------
+
+typedef testing::Test ViewTest;
+
+// Subclass with public ctor/dtor.
+class TestView : public View {
+ public:
+ TestView() {
+ ViewPrivate(this).set_id(1);
+ }
+ ~TestView() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+TEST_F(ViewTest, AddChild) {
+ TestView v1;
+ TestView v11;
+ v1.AddChild(&v11);
+ EXPECT_EQ(1U, v1.children().size());
+}
+
+TEST_F(ViewTest, RemoveChild) {
+ TestView v1;
+ TestView v11;
+ v1.AddChild(&v11);
+ EXPECT_EQ(1U, v1.children().size());
+ v1.RemoveChild(&v11);
+ EXPECT_EQ(0U, v1.children().size());
+}
+
+TEST_F(ViewTest, Reparent) {
+ TestView v1;
+ TestView v2;
+ TestView v11;
+ v1.AddChild(&v11);
+ EXPECT_EQ(1U, v1.children().size());
+ v2.AddChild(&v11);
+ EXPECT_EQ(1U, v2.children().size());
+ EXPECT_EQ(0U, v1.children().size());
+}
+
+TEST_F(ViewTest, Contains) {
+ TestView v1;
+
+ // Direct descendant.
+ TestView v11;
+ v1.AddChild(&v11);
+ EXPECT_TRUE(v1.Contains(&v11));
+
+ // Indirect descendant.
+ TestView v111;
+ v11.AddChild(&v111);
+ EXPECT_TRUE(v1.Contains(&v111));
+}
+
+TEST_F(ViewTest, GetChildById) {
+ TestView v1;
+ ViewPrivate(&v1).set_id(1);
+ TestView v11;
+ ViewPrivate(&v11).set_id(11);
+ v1.AddChild(&v11);
+ TestView v111;
+ ViewPrivate(&v111).set_id(111);
+ v11.AddChild(&v111);
+
+ // Find direct & indirect descendents.
+ EXPECT_EQ(&v11, v1.GetChildById(v11.id()));
+ EXPECT_EQ(&v111, v1.GetChildById(v111.id()));
+}
+
+TEST_F(ViewTest, DrawnAndVisible) {
+ TestView v1;
+ EXPECT_TRUE(v1.visible());
+ EXPECT_FALSE(v1.IsDrawn());
+
+ ViewPrivate(&v1).set_drawn(true);
+
+ TestView v11;
+ v1.AddChild(&v11);
+ EXPECT_TRUE(v11.visible());
+ EXPECT_TRUE(v11.IsDrawn());
+
+ v1.RemoveChild(&v11);
+ EXPECT_TRUE(v11.visible());
+ EXPECT_FALSE(v11.IsDrawn());
+}
+
+// ViewObserver --------------------------------------------------------
+
+typedef testing::Test ViewObserverTest;
+
+bool TreeChangeParamsMatch(const ViewObserver::TreeChangeParams& lhs,
+ const ViewObserver::TreeChangeParams& rhs) {
+ return lhs.target == rhs.target && lhs.old_parent == rhs.old_parent &&
+ lhs.new_parent == rhs.new_parent && lhs.receiver == rhs.receiver;
+}
+
+class TreeChangeObserver : public ViewObserver {
+ public:
+ explicit TreeChangeObserver(View* observee) : observee_(observee) {
+ observee_->AddObserver(this);
+ }
+ virtual ~TreeChangeObserver() {
+ observee_->RemoveObserver(this);
+ }
+
+ void Reset() {
+ received_params_.clear();
+ }
+
+ const std::vector<TreeChangeParams>& received_params() {
+ return received_params_;
+ }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnTreeChanging(const TreeChangeParams& params) OVERRIDE {
+ received_params_.push_back(params);
+ }
+ virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE {
+ received_params_.push_back(params);
+ }
+
+ View* observee_;
+ std::vector<TreeChangeParams> received_params_;
+
+ DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver);
+};
+
+// Adds/Removes v11 to v1.
+TEST_F(ViewObserverTest, TreeChange_SimpleAddRemove) {
+ TestView v1;
+ TreeChangeObserver o1(&v1);
+ EXPECT_TRUE(o1.received_params().empty());
+
+ TestView v11;
+ TreeChangeObserver o11(&v11);
+ EXPECT_TRUE(o11.received_params().empty());
+
+ // Add.
+
+ v1.AddChild(&v11);
+
+ EXPECT_EQ(2U, o1.received_params().size());
+ ViewObserver::TreeChangeParams p1;
+ p1.target = &v11;
+ p1.receiver = &v1;
+ p1.old_parent = NULL;
+ p1.new_parent = &v1;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
+
+ EXPECT_EQ(2U, o11.received_params().size());
+ ViewObserver::TreeChangeParams p11 = p1;
+ p11.receiver = &v11;
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
+
+ o1.Reset();
+ o11.Reset();
+ EXPECT_TRUE(o1.received_params().empty());
+ EXPECT_TRUE(o11.received_params().empty());
+
+ // Remove.
+
+ v1.RemoveChild(&v11);
+
+ EXPECT_EQ(2U, o1.received_params().size());
+ p1.target = &v11;
+ p1.receiver = &v1;
+ p1.old_parent = &v1;
+ p1.new_parent = NULL;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
+
+ EXPECT_EQ(2U, o11.received_params().size());
+ p11 = p1;
+ p11.receiver = &v11;
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
+}
+
+// Creates these two trees:
+// v1
+// +- v11
+// v111
+// +- v1111
+// +- v1112
+// Then adds/removes v111 from v11.
+TEST_F(ViewObserverTest, TreeChange_NestedAddRemove) {
+ TestView v1, v11, v111, v1111, v1112;
+
+ // Root tree.
+ v1.AddChild(&v11);
+
+ // Tree to be attached.
+ v111.AddChild(&v1111);
+ v111.AddChild(&v1112);
+
+ TreeChangeObserver o1(&v1), o11(&v11), o111(&v111), o1111(&v1111),
+ o1112(&v1112);
+ ViewObserver::TreeChangeParams p1, p11, p111, p1111, p1112;
+
+ // Add.
+
+ v11.AddChild(&v111);
+
+ EXPECT_EQ(2U, o1.received_params().size());
+ p1.target = &v111;
+ p1.receiver = &v1;
+ p1.old_parent = NULL;
+ p1.new_parent = &v11;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
+
+ EXPECT_EQ(2U, o11.received_params().size());
+ p11 = p1;
+ p11.receiver = &v11;
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
+
+ EXPECT_EQ(2U, o111.received_params().size());
+ p111 = p11;
+ p111.receiver = &v111;
+ EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
+
+ EXPECT_EQ(2U, o1111.received_params().size());
+ p1111 = p111;
+ p1111.receiver = &v1111;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back()));
+
+ EXPECT_EQ(2U, o1112.received_params().size());
+ p1112 = p111;
+ p1112.receiver = &v1112;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back()));
+
+ // Remove.
+ o1.Reset();
+ o11.Reset();
+ o111.Reset();
+ o1111.Reset();
+ o1112.Reset();
+ EXPECT_TRUE(o1.received_params().empty());
+ EXPECT_TRUE(o11.received_params().empty());
+ EXPECT_TRUE(o111.received_params().empty());
+ EXPECT_TRUE(o1111.received_params().empty());
+ EXPECT_TRUE(o1112.received_params().empty());
+
+ v11.RemoveChild(&v111);
+
+ EXPECT_EQ(2U, o1.received_params().size());
+ p1.target = &v111;
+ p1.receiver = &v1;
+ p1.old_parent = &v11;
+ p1.new_parent = NULL;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
+
+ EXPECT_EQ(2U, o11.received_params().size());
+ p11 = p1;
+ p11.receiver = &v11;
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+
+ EXPECT_EQ(2U, o111.received_params().size());
+ p111 = p11;
+ p111.receiver = &v111;
+ EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
+
+ EXPECT_EQ(2U, o1111.received_params().size());
+ p1111 = p111;
+ p1111.receiver = &v1111;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back()));
+
+ EXPECT_EQ(2U, o1112.received_params().size());
+ p1112 = p111;
+ p1112.receiver = &v1112;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back()));
+}
+
+TEST_F(ViewObserverTest, TreeChange_Reparent) {
+ TestView v1, v11, v12, v111;
+ v1.AddChild(&v11);
+ v1.AddChild(&v12);
+ v11.AddChild(&v111);
+
+ TreeChangeObserver o1(&v1), o11(&v11), o12(&v12), o111(&v111);
+
+ // Reparent.
+ v12.AddChild(&v111);
+
+ // v1 (root) should see both changing and changed notifications.
+ EXPECT_EQ(4U, o1.received_params().size());
+ ViewObserver::TreeChangeParams p1;
+ p1.target = &v111;
+ p1.receiver = &v1;
+ p1.old_parent = &v11;
+ p1.new_parent = &v12;
+ EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
+
+ // v11 should see changing notifications.
+ EXPECT_EQ(2U, o11.received_params().size());
+ ViewObserver::TreeChangeParams p11;
+ p11 = p1;
+ p11.receiver = &v11;
+ EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
+
+ // v12 should see changed notifications.
+ EXPECT_EQ(2U, o12.received_params().size());
+ ViewObserver::TreeChangeParams p12;
+ p12 = p1;
+ p12.receiver = &v12;
+ EXPECT_TRUE(TreeChangeParamsMatch(p12, o12.received_params().back()));
+
+ // v111 should see both changing and changed notifications.
+ EXPECT_EQ(2U, o111.received_params().size());
+ ViewObserver::TreeChangeParams p111;
+ p111 = p1;
+ p111.receiver = &v111;
+ EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
+ EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
+}
+
+namespace {
+
+class OrderChangeObserver : public ViewObserver {
+ public:
+ struct Change {
+ View* view;
+ View* relative_view;
+ OrderDirection direction;
+ };
+ typedef std::vector<Change> Changes;
+
+ explicit OrderChangeObserver(View* observee) : observee_(observee) {
+ observee_->AddObserver(this);
+ }
+ virtual ~OrderChangeObserver() {
+ observee_->RemoveObserver(this);
+ }
+
+ Changes GetAndClearChanges() {
+ Changes changes;
+ changes_.swap(changes);
+ return changes;
+ }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewReordering(View* view,
+ View* relative_view,
+ OrderDirection direction) OVERRIDE {
+ OnViewReordered(view, relative_view, direction);
+ }
+
+ virtual void OnViewReordered(View* view,
+ View* relative_view,
+ OrderDirection direction) OVERRIDE {
+ Change change;
+ change.view = view;
+ change.relative_view = relative_view;
+ change.direction = direction;
+ changes_.push_back(change);
+ }
+
+ View* observee_;
+ Changes changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewObserverTest, Order) {
+ TestView v1, v11, v12, v13;
+ v1.AddChild(&v11);
+ v1.AddChild(&v12);
+ v1.AddChild(&v13);
+
+ // Order: v11, v12, v13
+ EXPECT_EQ(3U, v1.children().size());
+ EXPECT_EQ(&v11, v1.children().front());
+ EXPECT_EQ(&v13, v1.children().back());
+
+ {
+ OrderChangeObserver observer(&v11);
+
+ // Move v11 to front.
+ // Resulting order: v12, v13, v11
+ v11.MoveToFront();
+ EXPECT_EQ(&v12, v1.children().front());
+ EXPECT_EQ(&v11, v1.children().back());
+
+ OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(&v11, changes[0].view);
+ EXPECT_EQ(&v13, changes[0].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction);
+
+ EXPECT_EQ(&v11, changes[1].view);
+ EXPECT_EQ(&v13, changes[1].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction);
+ }
+
+ {
+ OrderChangeObserver observer(&v11);
+
+ // Move v11 to back.
+ // Resulting order: v11, v12, v13
+ v11.MoveToBack();
+ EXPECT_EQ(&v11, v1.children().front());
+ EXPECT_EQ(&v13, v1.children().back());
+
+ OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(&v11, changes[0].view);
+ EXPECT_EQ(&v12, changes[0].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction);
+
+ EXPECT_EQ(&v11, changes[1].view);
+ EXPECT_EQ(&v12, changes[1].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction);
+ }
+
+ {
+ OrderChangeObserver observer(&v11);
+
+ // Move v11 above v12.
+ // Resulting order: v12. v11, v13
+ v11.Reorder(&v12, ORDER_DIRECTION_ABOVE);
+ EXPECT_EQ(&v12, v1.children().front());
+ EXPECT_EQ(&v13, v1.children().back());
+
+ OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(&v11, changes[0].view);
+ EXPECT_EQ(&v12, changes[0].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction);
+
+ EXPECT_EQ(&v11, changes[1].view);
+ EXPECT_EQ(&v12, changes[1].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction);
+ }
+
+ {
+ OrderChangeObserver observer(&v11);
+
+ // Move v11 below v12.
+ // Resulting order: v11, v12, v13
+ v11.Reorder(&v12, ORDER_DIRECTION_BELOW);
+ EXPECT_EQ(&v11, v1.children().front());
+ EXPECT_EQ(&v13, v1.children().back());
+
+ OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(&v11, changes[0].view);
+ EXPECT_EQ(&v12, changes[0].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction);
+
+ EXPECT_EQ(&v11, changes[1].view);
+ EXPECT_EQ(&v12, changes[1].relative_view);
+ EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction);
+ }
+}
+
+namespace {
+
+typedef std::vector<std::string> Changes;
+
+std::string ViewIdToString(Id id) {
+ return (id == 0) ? "null" :
+ base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
+}
+
+std::string RectToString(const gfx::Rect& rect) {
+ return base::StringPrintf("%d,%d %dx%d",
+ rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+class BoundsChangeObserver : public ViewObserver {
+ public:
+ explicit BoundsChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ virtual ~BoundsChangeObserver() {
+ view_->RemoveObserver(this);
+ }
+
+ Changes GetAndClearChanges() {
+ Changes changes;
+ changes.swap(changes_);
+ return changes;
+ }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewBoundsChanging(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {
+ changes_.push_back(
+ base::StringPrintf(
+ "view=%s old_bounds=%s new_bounds=%s phase=changing",
+ ViewIdToString(view->id()).c_str(),
+ RectToString(old_bounds).c_str(),
+ RectToString(new_bounds).c_str()));
+ }
+ virtual void OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {
+ changes_.push_back(
+ base::StringPrintf(
+ "view=%s old_bounds=%s new_bounds=%s phase=changed",
+ ViewIdToString(view->id()).c_str(),
+ RectToString(old_bounds).c_str(),
+ RectToString(new_bounds).c_str()));
+ }
+
+ View* view_;
+ Changes changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewObserverTest, SetBounds) {
+ TestView v1;
+ {
+ BoundsChangeObserver observer(&v1);
+ v1.SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ Changes changes = observer.GetAndClearChanges();
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(
+ "view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changing",
+ changes[0]);
+ EXPECT_EQ(
+ "view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changed",
+ changes[1]);
+ }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public ViewObserver {
+ public:
+ explicit VisibilityChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ virtual ~VisibilityChangeObserver() { view_->RemoveObserver(this); }
+
+ Changes GetAndClearChanges() {
+ Changes changes;
+ changes.swap(changes_);
+ return changes;
+ }
+
+ private:
+ // Overridden from ViewObserver:
+ virtual void OnViewVisibilityChanging(View* view) OVERRIDE {
+ changes_.push_back(
+ base::StringPrintf("view=%s phase=changing visibility=%s",
+ ViewIdToString(view->id()).c_str(),
+ view->visible() ? "true" : "false"));
+ }
+ virtual void OnViewVisibilityChanged(View* view) OVERRIDE {
+ changes_.push_back(base::StringPrintf("view=%s phase=changed visibility=%s",
+ ViewIdToString(view->id()).c_str(),
+ view->visible() ? "true" : "false"));
+ }
+
+ View* view_;
+ Changes changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewObserverTest, SetVisible) {
+ TestView v1;
+ EXPECT_TRUE(v1.visible());
+ {
+ // Change visibility from true to false and make sure we get notifications.
+ VisibilityChangeObserver observer(&v1);
+ v1.SetVisible(false);
+
+ Changes changes = observer.GetAndClearChanges();
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ("view=0,1 phase=changing visibility=true", changes[0]);
+ EXPECT_EQ("view=0,1 phase=changed visibility=false", changes[1]);
+ }
+ {
+ // Set visible to existing value and verify no notifications.
+ VisibilityChangeObserver observer(&v1);
+ v1.SetVisible(false);
+ EXPECT_TRUE(observer.GetAndClearChanges().empty());
+ }
+}
+
+} // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/types.h b/mojo/services/public/cpp/view_manager/types.h
new file mode 100644
index 0000000..d72236f
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/types.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_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 {
+
+// Used to identify views and change ids.
+typedef uint32_t Id;
+
+// Used to identify a connection as well as a connection specific view id. For
+// example, the Id for a view consists of the ConnectionSpecificId of the
+// connection and the ConnectionSpecificId of the view.
+typedef uint16_t ConnectionSpecificId;
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
diff --git a/mojo/services/public/cpp/view_manager/util.h b/mojo/services/public/cpp/view_manager/util.h
new file mode 100644
index 0000000..72c904d
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/util.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_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 {
+
+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 mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
diff --git a/mojo/services/public/cpp/view_manager/view.h b/mojo/services/public/cpp/view_manager/view.h
new file mode 100644
index 0000000..f7afa49
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view.h
@@ -0,0 +1,139 @@
+// 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 <vector>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkBitmap;
+
+namespace mojo {
+
+class BitmapUploader;
+class ServiceProviderImpl;
+class View;
+class ViewManager;
+class ViewObserver;
+
+// Views are owned by the ViewManager.
+// TODO(beng): Right now, you'll have to implement a ViewObserver to track
+// destruction and NULL any pointers you have.
+// Investigate some kind of smart pointer or weak pointer for these.
+class View {
+ public:
+ typedef std::vector<View*> Children;
+
+ static View* Create(ViewManager* view_manager);
+
+ // Destroys this view and all its children.
+ void Destroy();
+
+ ViewManager* view_manager() { return manager_; }
+
+ // Configuration.
+ Id id() const { return id_; }
+
+ // Geometric disposition.
+ const gfx::Rect& bounds() const { return bounds_; }
+ void SetBounds(const gfx::Rect& bounds);
+
+ // Visibility (also see IsDrawn()).
+ bool visible() const { return visible_; }
+ void SetVisible(bool value);
+
+ // A View is drawn if the View and all its ancestors are visible and the
+ // View is attached to the root.
+ bool IsDrawn() const;
+
+ // Observation.
+ void AddObserver(ViewObserver* observer);
+ void RemoveObserver(ViewObserver* observer);
+
+ // Tree.
+ View* parent() { return parent_; }
+ const View* parent() const { return parent_; }
+ const Children& children() const { return children_; }
+
+ void AddChild(View* child);
+ void RemoveChild(View* child);
+
+ void Reorder(View* relative, OrderDirection direction);
+ void MoveToFront();
+ void MoveToBack();
+
+ bool Contains(View* child) const;
+
+ View* GetChildById(Id id);
+
+ void SetSurfaceId(SurfaceIdPtr id);
+
+ // TODO(beng): temporary only.
+ void SetContents(const SkBitmap& contents);
+ void SetColor(SkColor color);
+
+ // Focus.
+ void SetFocus();
+
+ // Embedding.
+ void Embed(const String& url);
+ scoped_ptr<ServiceProvider> Embed(
+ const String& url,
+ scoped_ptr<ServiceProviderImpl> exported_services);
+
+ protected:
+ // This class is subclassed only by test classes that provide a public ctor.
+ View();
+ ~View();
+
+ private:
+ friend class ViewPrivate;
+ friend class ViewManagerClientImpl;
+
+ explicit View(ViewManager* manager);
+
+ void LocalDestroy();
+ void LocalAddChild(View* child);
+ void LocalRemoveChild(View* child);
+ // Returns true if the order actually changed.
+ bool LocalReorder(View* relative, OrderDirection direction);
+ void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds);
+ void LocalSetDrawn(bool drawn);
+ void CreateBitmapUploader();
+
+ ViewManager* manager_;
+ Id id_;
+ View* parent_;
+ Children children_;
+
+ ObserverList<ViewObserver> observers_;
+
+ gfx::Rect bounds_;
+
+ bool visible_;
+
+ // Drawn state is derived from the visible state and the parent's visible
+ // state. This field is only used if the view has no parent (eg it's a root).
+ bool drawn_;
+
+ // TODO(jamesr): Temporary, remove when all clients are using surfaces
+ // directly.
+ scoped_ptr<BitmapUploader> bitmap_uploader_;
+
+ DISALLOW_COPY_AND_ASSIGN(View);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager.h b/mojo/services/public/cpp/view_manager/view_manager.h
new file mode 100644
index 0000000..d153283
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager.h
@@ -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.
+
+#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"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace mojo {
+class View;
+class ViewManagerDelegate;
+class WindowManagerDelegate;
+
+// Encapsulates a connection to the view manager service.
+// A unique connection is made for every unique embed path for an app. e.g. for
+// app B embed by the following paths: A->B, A->C->B - there are two connections
+// and thus two instances of this class.
+class ViewManager {
+ public:
+ // Sets the window manager delegate. Can only be called by the app embedded at
+ // the service root view. Can only be called once.
+ virtual void SetWindowManagerDelegate(
+ WindowManagerDelegate* window_manager_delegate) = 0;
+
+ // Dispatches the supplied event to the specified View. Can be called only
+ // by the application that called SetWindowManagerDelegate().
+ virtual void DispatchEvent(View* target, EventPtr event) = 0;
+
+ // Returns the URL of the application that embedded this application.
+ virtual const std::string& GetEmbedderURL() const = 0;
+
+ // Returns all root views known to this connection.
+ virtual const std::vector<View*>& GetRoots() const = 0;
+
+ // Returns a View known to this connection.
+ virtual View* GetViewById(Id id) = 0;
+
+ protected:
+ virtual ~ViewManager() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager_client_factory.h b/mojo/services/public/cpp/view_manager/view_manager_client_factory.h
new file mode 100644
index 0000000..d2ed548
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager_client_factory.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_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CLIENT_FACTORY_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CLIENT_FACTORY_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+
+namespace mojo {
+
+class ViewManagerDelegate;
+class Shell;
+
+// Add an instance of this class to an incoming connection to allow it to
+// instantiate ViewManagerClient implementations in response to
+// ViewManagerClient requests.
+class ViewManagerClientFactory : public InterfaceFactory<ViewManagerClient> {
+ public:
+ ViewManagerClientFactory(Shell* shell, ViewManagerDelegate* delegate);
+ virtual ~ViewManagerClientFactory();
+
+ // InterfaceFactory<ViewManagerClient> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<ViewManagerClient> request) override;
+
+ private:
+ Shell* shell_;
+ ViewManagerDelegate* delegate_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CLIENT_FACTORY_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager_context.h b/mojo/services/public/cpp/view_manager/view_manager_context.h
new file mode 100644
index 0000000..0bc51a4
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager_context.h
@@ -0,0 +1,47 @@
+// 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_CONTEXT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CONTEXT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+
+namespace mojo {
+class ApplicationImpl;
+
+class ViewManagerContext {
+ public:
+ explicit ViewManagerContext(ApplicationImpl* application_impl);
+ ~ViewManagerContext();
+
+ // Embed an application @ |url| at an appropriate View.
+ // The first time this method is called in the life time of the View Manager
+ // service the "appropriate View" is defined as being the service' root View.
+ // Subsequent times, the implementation of this method is delegated to the
+ // application embedded at the service root View. This application will have a
+ // specific definition of where within its View hierarchy to embed an
+ // un-parented URL. |exported_services| encapsulates services offered by the
+ // application calling Embed() to the application being embedded. Returns
+ // an object implementing ServiceProvider encapsulating services offered by
+ // the embedded application to the embedder.
+ void Embed(const String& url);
+ scoped_ptr<ServiceProvider> Embed(
+ const String& url,
+ scoped_ptr<ServiceProviderImpl> exported_services);
+
+ private:
+ class InternalState;
+ scoped_ptr<InternalState> state_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerContext);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_CONTEXT_H_
diff --git a/mojo/services/public/cpp/view_manager/view_manager_delegate.h b/mojo/services/public/cpp/view_manager/view_manager_delegate.h
new file mode 100644
index 0000000..bbb9867
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_manager_delegate.h
@@ -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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+class ServiceProviderImpl;
+class View;
+class ViewManager;
+
+// Interface implemented by an application using the view manager.
+class ViewManagerDelegate {
+ public:
+ // Called when the application implementing this interface is embedded at
+ // |root|. |view_manager| is an implementation of an object bound to a
+ // connection to the view manager service. The object is valid until
+ // OnViewManagerDisconnected() is called with the same object.
+ // This function is called for every embed, but there will be a unique
+ // instance of |view_manager| only for every unique connection. See
+ // the class description of ViewManager for some rules about when a new
+ // connection is made.
+ // |exported_services| is an object that the delegate can add services to to
+ // expose to the embedder. |imported_services| exposes the services offered by
+ // the embedder to the delegate. Note that if a different application is
+ // subsequently embedded at |root|, the pipe(s) connecting |imported_services|
+ // to the embedder and any services obtained from it are not broken and will
+ // continue to be valid.
+ virtual void OnEmbed(ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* exported_services,
+ scoped_ptr<ServiceProvider> imported_services) = 0;
+
+ // Called when a connection to the view manager service is closed.
+ // |view_manager| is not valid after this function returns.
+ virtual void OnViewManagerDisconnected(ViewManager* view_manager) = 0;
+
+ protected:
+ virtual ~ViewManagerDelegate() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
diff --git a/mojo/services/public/cpp/view_manager/view_observer.h b/mojo/services/public/cpp/view_manager/view_observer.h
new file mode 100644
index 0000000..1a2ab7e
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_observer.h
@@ -0,0 +1,81 @@
+// 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/cpp/view_manager/view.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+
+class View;
+
+// A note on -ing and -ed suffixes:
+//
+// -ing methods are called before changes are applied to the local view model.
+// -ed methods are called after changes are applied to the local view model.
+//
+// If the change originated from another connection to the view manager, it's
+// possible that the change has already been applied to the service-side model
+// prior to being called, so for example in the case of OnViewDestroying(), it's
+// possible the view has already been destroyed on the service side.
+
+class ViewObserver {
+ public:
+ struct TreeChangeParams {
+ TreeChangeParams();
+ View* target;
+ View* old_parent;
+ View* new_parent;
+ View* receiver;
+ };
+
+ virtual void OnTreeChanging(const TreeChangeParams& params) {}
+ virtual void OnTreeChanged(const TreeChangeParams& params) {}
+
+ virtual void OnViewReordering(View* view,
+ View* relative_view,
+ OrderDirection direction) {}
+ virtual void OnViewReordered(View* view,
+ View* relative_view,
+ OrderDirection direction) {}
+
+ virtual void OnViewDestroying(View* view) {}
+ virtual void OnViewDestroyed(View* view) {}
+
+ virtual void OnViewBoundsChanging(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {}
+ virtual void OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {}
+
+ virtual void OnViewFocusChanged(View* gained_focus, View* lost_focus) {}
+
+ virtual void OnViewInputEvent(View* view, const EventPtr& event) {}
+
+ virtual void OnViewVisibilityChanging(View* view) {}
+ virtual void OnViewVisibilityChanged(View* view) {}
+
+ // Sent when the drawn state changes. This is only sent for the root nodes
+ // when embedded.
+ virtual void OnViewDrawnChanging(View* view) {}
+ virtual void OnViewDrawnChanged(View* view) {}
+
+ protected:
+ virtual ~ViewObserver() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
diff --git a/mojo/services/public/cpp/view_manager/window_manager_delegate.h b/mojo/services/public/cpp/view_manager/window_manager_delegate.h
new file mode 100644
index 0000000..0ab271c
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/window_manager_delegate.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_PUBLIC_CPP_VIEW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace mojo {
+
+class View;
+
+// A WindowManagerDelegate is provided by the application embedded at the
+// service root view.
+class WindowManagerDelegate {
+ public:
+ // Create an appropriate view to embed |url|.
+ virtual void Embed(const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) {}
+
+ // Dispatch the supplied input event to the appropriate view (taking into
+ // account focus, activation, modality, etc.).
+ virtual void DispatchEvent(EventPtr event) = 0;
+
+ protected:
+ virtual ~WindowManagerDelegate() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
diff --git a/mojo/services/public/interfaces/clipboard/BUILD.gn b/mojo/services/public/interfaces/clipboard/BUILD.gn
new file mode 100644
index 0000000..65dd7d3
--- /dev/null
+++ b/mojo/services/public/interfaces/clipboard/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_clipboard_bindings
+mojom("clipboard") {
+ sources = [
+ "clipboard.mojom",
+ ]
+}
diff --git a/mojo/services/public/interfaces/clipboard/clipboard.mojom b/mojo/services/public/interfaces/clipboard/clipboard.mojom
new file mode 100644
index 0000000..eca47b9
--- /dev/null
+++ b/mojo/services/public/interfaces/clipboard/clipboard.mojom
@@ -0,0 +1,52 @@
+// 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 {
+
+// A wrapper type which is just a Key/Value pair. Workaround until we get
+// proper maps in mojom.
+struct MimeTypePair {
+ string mime_type;
+ array<uint8> data;
+};
+
+interface Clipboard {
+ enum Type {
+ COPY_PASTE = 0,
+ SELECTION = 1,
+ DRAG = 2
+ };
+
+ // Mime type constants
+ const string MIME_TYPE_TEXT = "text/plain";
+ const string MIME_TYPE_HTML = "text/html";
+ const string MIME_TYPE_URL = "text/url";
+
+ // Returns a sequence number which uniquely identifies clipboard state.
+ // Clients are able to assume that the clipboard contents are unchanged as
+ // long as this number has not changed. This number is monotonically
+ // increasing, is increased when the clipboard state changes, and is
+ // provided by Windows, Linux, and Mac.
+ GetSequenceNumber(Type clipboard_type) => (uint64 sequence);
+
+ // Returns the available mime types. (Note: the chrome interface has a
+ // |contains_filenames| parameter here, but it appears to always be set
+ // to false.)
+ GetAvailableMimeTypes(Type clipboard_types) => (array<string> types);
+
+ // Returns the data associated with a Mime type, returning NULL if that data
+ // doesn't exist. Note: because of the inherit raciness of clipboard access,
+ // this may return NULL even if you just verified that it exists with
+ // GetAvailableFormatMimeTypes(). We don't want to provide one API to return
+ // the entire clipboard state because the combined size of the clipboard can
+ // be megabytes, especially when image data is involved.
+ ReadMimeType(Type clipboard_type, string mime_type) => (array<uint8>? data);
+
+ // Writes a set of mime types to the clipboard. This will increment the
+ // sequence number. In the case of an empty or NULL list, this will just
+ // clear the clipboard.
+ WriteClipboardData(Type clipboard_type, array<MimeTypePair>? data);
+};
+
+} // module mojo
diff --git a/mojo/services/public/interfaces/content_handler/BUILD.gn b/mojo/services/public/interfaces/content_handler/BUILD.gn
new file mode 100644
index 0000000..c72cc9a
--- /dev/null
+++ b/mojo/services/public/interfaces/content_handler/BUILD.gn
@@ -0,0 +1,17 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_content_handler_bindings
+mojom("content_handler") {
+ sources = [
+ "content_handler.mojom",
+ ]
+
+ deps = [
+ "//mojo/public/interfaces/application",
+ "//mojo/services/public/interfaces/network",
+ ]
+}
diff --git a/mojo/services/public/interfaces/content_handler/content_handler.mojom b/mojo/services/public/interfaces/content_handler/content_handler.mojom
new file mode 100644
index 0000000..4383401
--- /dev/null
+++ b/mojo/services/public/interfaces/content_handler/content_handler.mojom
@@ -0,0 +1,16 @@
+// 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 "mojo/public/interfaces/application/service_provider.mojom"
+import "mojo/services/public/interfaces/network/url_loader.mojom"
+
+module mojo {
+
+interface ContentHandler {
+ OnConnect(string url,
+ URLResponse response,
+ ServiceProvider&? service_provider);
+};
+
+}
diff --git a/mojo/services/public/interfaces/geometry/BUILD.gn b/mojo/services/public/interfaces/geometry/BUILD.gn
new file mode 100644
index 0000000..3cd9bcd
--- /dev/null
+++ b/mojo/services/public/interfaces/geometry/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_geometry_bindings
+mojom("geometry") {
+ sources = [
+ "geometry.mojom",
+ ]
+}
diff --git a/mojo/services/public/interfaces/geometry/geometry.mojom b/mojo/services/public/interfaces/geometry/geometry.mojom
new file mode 100644
index 0000000..72e4246
--- /dev/null
+++ b/mojo/services/public/interfaces/geometry/geometry.mojom
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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 RectF {
+ float x;
+ float y;
+ float width;
+ float height;
+};
+
+struct Transform {
+ // Row major order.
+ array<float, 16>? matrix;
+};
+
+}
diff --git a/mojo/services/public/interfaces/gpu/BUILD.gn b/mojo/services/public/interfaces/gpu/BUILD.gn
new file mode 100644
index 0000000..1e56abc
--- /dev/null
+++ b/mojo/services/public/interfaces/gpu/BUILD.gn
@@ -0,0 +1,17 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_gpu_bindings
+mojom("gpu") {
+ sources = [
+ "gpu.mojom",
+ ]
+
+ deps = [
+ "//mojo/services/gles2:interfaces",
+ "//mojo/services/public/interfaces/geometry",
+ ]
+}
diff --git a/mojo/services/public/interfaces/gpu/gpu.mojom b/mojo/services/public/interfaces/gpu/gpu.mojom
new file mode 100644
index 0000000..881c6be
--- /dev/null
+++ b/mojo/services/public/interfaces/gpu/gpu.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 "mojo/services/gles2/command_buffer.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+
+module mojo {
+
+interface Gpu {
+ CreateOnscreenGLES2Context(uint64 native_viewport_id, Size? size, CommandBuffer&? gles2_client);
+ CreateOffscreenGLES2Context(CommandBuffer&? gles2_client);
+};
+
+}
diff --git a/mojo/services/public/interfaces/input_events/BUILD.gn b/mojo/services/public/interfaces/input_events/BUILD.gn
new file mode 100644
index 0000000..6c179c6
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/BUILD.gn
@@ -0,0 +1,18 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_input_events_bindings
+mojom("input_events") {
+ sources = [
+ "input_events.mojom",
+ "input_event_constants.mojom",
+ "input_key_codes.mojom",
+ ]
+
+ deps = [
+ "//mojo/services/public/interfaces/geometry",
+ ]
+}
diff --git a/mojo/services/public/interfaces/input_events/input_event_constants.mojom b/mojo/services/public/interfaces/input_events/input_event_constants.mojom
new file mode 100644
index 0000000..0de69aa
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/input_event_constants.mojom
@@ -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.
+
+module mojo {
+
+// This mirrors ui::EventType
+enum EventType {
+ UNKNOWN ,
+ MOUSE_PRESSED,
+ MOUSE_DRAGGED,
+ MOUSE_RELEASED,
+ MOUSE_MOVED,
+ MOUSE_ENTERED,
+ MOUSE_EXITED,
+ KEY_PRESSED,
+ KEY_RELEASED,
+ MOUSEWHEEL,
+ MOUSE_CAPTURE_CHANGED,
+ TOUCH_RELEASED,
+ TOUCH_PRESSED,
+ TOUCH_MOVED,
+ TOUCH_CANCELLED,
+ DROP_TARGET_EVENT,
+ TRANSLATED_KEY_PRESS,
+ TRANSLATED_KEY_RELEASE,
+ GESTURE_SCROLL_BEGIN,
+ GESTURE_SCROLL_END,
+ GESTURE_SCROLL_UPDATE,
+ GESTURE_TAP,
+ GESTURE_TAP_DOWN,
+ GESTURE_TAP_CANCEL,
+ GESTURE_TAP_UNCONFIRMED,
+ GESTURE_DOUBLE_TAP,
+ GESTURE_BEGIN,
+ GESTURE_END,
+ GESTURE_TWO_FINGER_TAP,
+ GESTURE_PINCH_BEGIN,
+ GESTURE_PINCH_END,
+ GESTURE_PINCH_UPDATE,
+ GESTURE_LONG_PRESS,
+ GESTURE_LONG_TAP,
+ GESTURE_SWIPE,
+ GESTURE_SHOW_PRESS,
+ GESTURE_WIN8_EDGE_SWIPE,
+ SCROLL,
+ SCROLL_FLING_START,
+ SCROLL_FLING_CANCEL,
+ CANCEL_MODE,
+ UMA_DATA
+};
+
+// This mirrors ui::EventFlags
+// TODO(morrita): Use shift operator once it is available.
+enum EventFlags {
+ NONE = 0,
+ CAPS_LOCK_DOWN = 1,
+ SHIFT_DOWN = 2,
+ CONTROL_DOWN = 4,
+ ALT_DOWN = 8,
+ LEFT_MOUSE_BUTTON = 16,
+ MIDDLE_MOUSE_BUTTON = 32,
+ RIGHT_MOUSE_BUTTON = 64,
+ COMMAND_DOWN = 128,
+ EXTENDED = 256,
+ IS_SYNTHESIZED = 512,
+ ALTGR_DOWN = 1024,
+ MOD3_DOWN = 2048
+};
+
+enum MouseEventFlags {
+ IS_DOUBLE_CLICK = 65536,
+ IS_TRIPLE_CLICK = 131072,
+ IS_NON_CLIENT = 262144,
+
+ // TODO(erg): Move accessibility flags and maybe synthetic touch events here.
+};
+
+} // module mojo
diff --git a/mojo/services/public/interfaces/input_events/input_events.mojom b/mojo/services/public/interfaces/input_events/input_events.mojom
new file mode 100644
index 0000000..4067f68
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/input_events.mojom
@@ -0,0 +1,74 @@
+// 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 "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/input_events/input_event_constants.mojom"
+import "mojo/services/public/interfaces/input_events/input_key_codes.mojom"
+
+module mojo {
+
+struct LocationData {
+ Point? in_view_location;
+ Point? screen_location;
+};
+
+struct KeyData {
+ // The chromium event key code; these values are from the ui/ KeyCode enum,
+ // which has the fun property of being neither consistently the Windows key
+ // code, nor the X11 keycodes. (This value is consistent across platforms
+ // for basic ASCII characters; it will differ for modifiers. We don't define
+ // this as a mojo enum because mojom doesn't appear to have a platform
+ // dependent preprocessor yet.)
+ //
+ // TODO(erg): Remove this, and declare Win32 keycodes correct by fiat. We can
+ // not do this until we remove ui::Event usage from within mojo.
+ int32 key_code;
+
+ // Whether this is a character event, and the character value if it is. Note
+ // that this is different than |text|, which holds a value even when there
+ // isn't actually a character to insert. (For example, |text| will be set and
+ // have a value on backspace, and |character| won't.)
+ bool is_char;
+ uint16 character;
+
+ // The Win32 key code. Because of the web, this is the closest thing that we
+ // have to a cross platform key state.
+ KeyboardCode windows_key_code;
+
+ // The platform specific key code.
+ //
+ // TODO(erg): This exists only for NPAPI support, pepper USB keyboard support
+ // and IME on android support. Theoretically, we should be able to remove this
+ // in the medium to long term.
+ int32 native_key_code;
+
+ // The text generated by this keystroke. Corresponds to
+ // blink::WebKeyboardEvent::text.
+ uint16 text;
+
+ // Like |text|, but unmodified by concurrently held modifier keys (except
+ // shift). Corresponds to blink::WebKeyboardEvent::unmodifiedText.
+ uint16 unmodified_text;
+};
+
+struct TouchData {
+ int32 pointer_id;
+};
+
+struct MouseWheelData {
+ int32 x_offset;
+ int32 y_offset;
+};
+
+struct Event {
+ EventType action;
+ EventFlags flags;
+ int64 time_stamp;
+ LocationData? location_data;
+ KeyData? key_data;
+ TouchData? touch_data;
+ MouseWheelData? wheel_data;
+};
+
+}
diff --git a/mojo/services/public/interfaces/input_events/input_key_codes.mojom b/mojo/services/public/interfaces/input_events/input_key_codes.mojom
new file mode 100644
index 0000000..bd955aa
--- /dev/null
+++ b/mojo/services/public/interfaces/input_events/input_key_codes.mojom
@@ -0,0 +1,188 @@
+// 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 {
+
+// Cross platform keyboard codes.
+//
+// Because the web has standardized on Win32 keyboard codes, so does mojo.
+enum KeyboardCode {
+ BACK = 0x08,
+ TAB = 0x09,
+ CLEAR = 0x0C,
+ RETURN = 0x0D,
+ SHIFT = 0x10,
+ CONTROL = 0x11,
+ MENU = 0x12, // a.k.a. ALT
+ PAUSE = 0x13,
+ CAPITAL = 0x14,
+ KANA = 0x15,
+ HANGUL = 0x15,
+ JUNJA = 0x17,
+ FINAL = 0x18,
+ HANJA = 0x19,
+ KANJI = 0x19,
+ ESCAPE = 0x1B,
+ CONVERT = 0x1C,
+ NONCONVERT = 0x1D,
+ ACCEPT = 0x1E,
+ MODECHANGE = 0x1F,
+ SPACE = 0x20,
+ PRIOR = 0x21,
+ NEXT = 0x22,
+ END = 0x23,
+ HOME = 0x24,
+ LEFT = 0x25,
+ UP = 0x26,
+ RIGHT = 0x27,
+ DOWN = 0x28,
+ SELECT = 0x29,
+ PRINT = 0x2A,
+ EXECUTE = 0x2B,
+ SNAPSHOT = 0x2C,
+ INSERT = 0x2D,
+ DELETE = 0x2E,
+ HELP = 0x2F,
+ NUM_0 = 0x30,
+ NUM_1 = 0x31,
+ NUM_2 = 0x32,
+ NUM_3 = 0x33,
+ NUM_4 = 0x34,
+ NUM_5 = 0x35,
+ NUM_6 = 0x36,
+ NUM_7 = 0x37,
+ NUM_8 = 0x38,
+ NUM_9 = 0x39,
+ A = 0x41,
+ B = 0x42,
+ C = 0x43,
+ D = 0x44,
+ E = 0x45,
+ F = 0x46,
+ G = 0x47,
+ H = 0x48,
+ I = 0x49,
+ J = 0x4A,
+ K = 0x4B,
+ L = 0x4C,
+ M = 0x4D,
+ N = 0x4E,
+ O = 0x4F,
+ P = 0x50,
+ Q = 0x51,
+ R = 0x52,
+ S = 0x53,
+ T = 0x54,
+ U = 0x55,
+ V = 0x56,
+ W = 0x57,
+ X = 0x58,
+ Y = 0x59,
+ Z = 0x5A,
+ LWIN = 0x5B,
+ COMMAND = 0x5B, // Provide the Mac name for convenience.
+ RWIN = 0x5C,
+ APPS = 0x5D,
+ SLEEP = 0x5F,
+ NUMPAD0 = 0x60,
+ NUMPAD1 = 0x61,
+ NUMPAD2 = 0x62,
+ NUMPAD3 = 0x63,
+ NUMPAD4 = 0x64,
+ NUMPAD5 = 0x65,
+ NUMPAD6 = 0x66,
+ NUMPAD7 = 0x67,
+ NUMPAD8 = 0x68,
+ NUMPAD9 = 0x69,
+ MULTIPLY = 0x6A,
+ ADD = 0x6B,
+ SEPARATOR = 0x6C,
+ SUBTRACT = 0x6D,
+ DECIMAL = 0x6E,
+ DIVIDE = 0x6F,
+ F1 = 0x70,
+ F2 = 0x71,
+ F3 = 0x72,
+ F4 = 0x73,
+ F5 = 0x74,
+ F6 = 0x75,
+ F7 = 0x76,
+ F8 = 0x77,
+ F9 = 0x78,
+ F10 = 0x79,
+ F11 = 0x7A,
+ F12 = 0x7B,
+ F13 = 0x7C,
+ F14 = 0x7D,
+ F15 = 0x7E,
+ F16 = 0x7F,
+ F17 = 0x80,
+ F18 = 0x81,
+ F19 = 0x82,
+ F20 = 0x83,
+ F21 = 0x84,
+ F22 = 0x85,
+ F23 = 0x86,
+ F24 = 0x87,
+ NUMLOCK = 0x90,
+ SCROLL = 0x91,
+ LSHIFT = 0xA0,
+ RSHIFT = 0xA1,
+ LCONTROL = 0xA2,
+ RCONTROL = 0xA3,
+ LMENU = 0xA4,
+ RMENU = 0xA5,
+ BROWSER_BACK = 0xA6,
+ BROWSER_FORWARD = 0xA7,
+ BROWSER_REFRESH = 0xA8,
+ BROWSER_STOP = 0xA9,
+ BROWSER_SEARCH = 0xAA,
+ BROWSER_FAVORITES = 0xAB,
+ BROWSER_HOME = 0xAC,
+ VOLUME_MUTE = 0xAD,
+ VOLUME_DOWN = 0xAE,
+ VOLUME_UP = 0xAF,
+ MEDIA_NEXT_TRACK = 0xB0,
+ MEDIA_PREV_TRACK = 0xB1,
+ MEDIA_STOP = 0xB2,
+ MEDIA_PLAY_PAUSE = 0xB3,
+ MEDIA_LAUNCH_MAIL = 0xB4,
+ MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
+ MEDIA_LAUNCH_APP1 = 0xB6,
+ MEDIA_LAUNCH_APP2 = 0xB7,
+
+ OEM_1 = 0xBA,
+ OEM_PLUS = 0xBB,
+ OEM_COMMA = 0xBC,
+ OEM_MINUS = 0xBD,
+ OEM_PERIOD = 0xBE,
+ OEM_2 = 0xBF,
+ OEM_3 = 0xC0,
+ OEM_4 = 0xDB,
+ OEM_5 = 0xDC,
+ OEM_6 = 0xDD,
+ OEM_7 = 0xDE,
+ OEM_8 = 0xDF,
+ OEM_102 = 0xE2,
+ PROCESSKEY = 0xE5,
+ PACKET = 0xE7,
+ DBE_SBCSCHAR = 0xF3,
+ DBE_DBCSCHAR = 0xF4,
+ ATTN = 0xF6,
+ CRSEL = 0xF7,
+ EXSEL = 0xF8,
+ EREOF = 0xF9,
+ PLAY = 0xFA,
+ ZOOM = 0xFB,
+ NONAME = 0xFC,
+ PA1 = 0xFD,
+ OEM_CLEAR = 0xFE,
+ UNKNOWN = 0,
+
+ // Windows does not have a specific key code for AltGr. We use the unused
+ // VK_OEM_AX to represent AltGr, matching the behaviour of Firefox on Linux.
+ ALTGR = 0xE1,
+};
+
+} // module mojo
diff --git a/mojo/services/public/interfaces/native_viewport/BUILD.gn b/mojo/services/public/interfaces/native_viewport/BUILD.gn
new file mode 100644
index 0000000..015fc89
--- /dev/null
+++ b/mojo/services/public/interfaces/native_viewport/BUILD.gn
@@ -0,0 +1,19 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_native_viewport_bindings
+mojom("native_viewport") {
+ sources = [
+ "native_viewport.mojom",
+ ]
+
+ public_deps = [
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/gpu",
+ "//mojo/services/public/interfaces/input_events",
+ "//mojo/services/public/interfaces/surfaces:surface_id",
+ ]
+}
diff --git a/mojo/services/public/interfaces/native_viewport/native_viewport.mojom b/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
new file mode 100644
index 0000000..6f05b72
--- /dev/null
+++ b/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
@@ -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.
+
+import "mojo/services/gles2/command_buffer.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/input_events/input_events.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+
+module mojo {
+
+[Client=NativeViewportClient]
+interface NativeViewport {
+ // TODO(sky): having a create function is awkward. Should there be a factory
+ // to create the NativeViewport that takes the size?
+ Create(Size size) => (uint64 native_viewport_id);
+ Show();
+ Hide();
+ Close();
+ SetSize(Size size);
+ SubmittedFrame(SurfaceId surface_id);
+};
+
+interface NativeViewportClient {
+ // OnSizeChanged() is sent at least once after the callback from Create() is
+ // called.
+ OnSizeChanged(Size size);
+ OnDestroyed();
+ OnEvent(Event event) => ();
+};
+
+// Connect to this interface before any other connections are made to configure
+// the NativeViewport service.
+interface NativeViewportConfig {
+ // Call UseTestConfig() and block on the reply. This will ensure that the
+ // correct global initialization is done before subsequent connections.
+ UseTestConfig() => ();
+
+ // Call UseHeadlessConfig() and block on the reply. This will ensure that
+ // the native viewport is later created in headless mode.
+ UseHeadlessConfig() => ();
+};
+
+}
diff --git a/mojo/services/public/interfaces/navigation/BUILD.gn b/mojo/services/public/interfaces/navigation/BUILD.gn
new file mode 100644
index 0000000..73e0578
--- /dev/null
+++ b/mojo/services/public/interfaces/navigation/BUILD.gn
@@ -0,0 +1,16 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_navigation_bindings
+mojom("navigation") {
+ sources = [
+ "navigation.mojom",
+ ]
+
+ deps = [
+ "//mojo/services/public/interfaces/network",
+ ]
+}
diff --git a/mojo/services/public/interfaces/navigation/navigation.mojom b/mojo/services/public/interfaces/navigation/navigation.mojom
new file mode 100644
index 0000000..6a9e94f
--- /dev/null
+++ b/mojo/services/public/interfaces/navigation/navigation.mojom
@@ -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.
+
+import "mojo/services/public/interfaces/network/url_loader.mojom"
+
+module mojo {
+
+// Expresses a preference for where a navigation will be performed.
+enum Target {
+ // No preference
+ DEFAULT,
+
+ // In the same ViewManager node that the navigation was initiated
+ SOURCE_NODE,
+
+ // In a new ViewManager node
+ NEW_NODE
+};
+
+// Embedders that support navigation of implement this interface.
+interface NavigatorHost {
+ RequestNavigate(Target target, URLRequest request);
+
+ // Applications call this to inform hosts of navigations they performed
+ // locally. For example, pushState() navigations in an HTML application.
+ DidNavigateLocally(string url);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/BUILD.gn b/mojo/services/public/interfaces/network/BUILD.gn
new file mode 100644
index 0000000..e55c6aa
--- /dev/null
+++ b/mojo/services/public/interfaces/network/BUILD.gn
@@ -0,0 +1,21 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_network_bindings
+mojom("network") {
+ sources = [
+ "cookie_store.mojom",
+ "net_address.mojom",
+ "network_error.mojom",
+ "network_service.mojom",
+ "tcp_bound_socket.mojom",
+ "tcp_client_socket.mojom",
+ "tcp_server_socket.mojom",
+ "udp_socket.mojom",
+ "url_loader.mojom",
+ "web_socket.mojom",
+ ]
+}
diff --git a/mojo/services/public/interfaces/network/cookie_store.mojom b/mojo/services/public/interfaces/network/cookie_store.mojom
new file mode 100644
index 0000000..76b374a
--- /dev/null
+++ b/mojo/services/public/interfaces/network/cookie_store.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 {
+
+interface CookieStore {
+ Get(string? url) => (string? cookies);
+ Set(string? url, string? cookie) => (bool success);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/net_address.mojom b/mojo/services/public/interfaces/network/net_address.mojom
new file mode 100644
index 0000000..337f251
--- /dev/null
+++ b/mojo/services/public/interfaces/network/net_address.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.
+
+module mojo {
+
+enum NetAddressFamily {
+ UNSPECIFIED,
+ IPV4,
+ IPV6
+};
+
+// All members are expressed in network byte order, i.e., the most significant
+// byte is stored in the smallest address. For example, if we store 127.0.0.1 in
+// |addr|, 127 should be at index 0.
+struct NetAddressIPv4 {
+ uint16 port;
+ array<uint8, 4> addr;
+};
+
+// All members are expressed in network byte order.
+struct NetAddressIPv6 {
+ uint16 port;
+ array<uint8, 16> addr;
+};
+
+struct NetAddress {
+ NetAddressFamily family = UNSPECIFIED;
+ // At most one of the following fields is non-NULL depending on the value of
+ // |family|.
+ NetAddressIPv4? ipv4;
+ NetAddressIPv6? ipv6;
+};
+
+}
+
diff --git a/mojo/services/public/interfaces/network/network_error.mojom b/mojo/services/public/interfaces/network/network_error.mojom
new file mode 100644
index 0000000..4af9935
--- /dev/null
+++ b/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/mojo/services/public/interfaces/network/network_service.mojom b/mojo/services/public/interfaces/network/network_service.mojom
new file mode 100644
index 0000000..7884144
--- /dev/null
+++ b/mojo/services/public/interfaces/network/network_service.mojom
@@ -0,0 +1,55 @@
+// 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 "mojo/services/public/interfaces/network/cookie_store.mojom"
+import "mojo/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+import "mojo/services/public/interfaces/network/tcp_bound_socket.mojom"
+import "mojo/services/public/interfaces/network/tcp_client_socket.mojom"
+import "mojo/services/public/interfaces/network/url_loader.mojom"
+import "mojo/services/public/interfaces/network/web_socket.mojom"
+
+module mojo {
+
+// TODO Darin suggfests that this should probably be two classes. One for
+// high-level origin-build requests like WebSockets and HTTP, and the other for
+// non-origin-bound low-level stuff like DNS, UDP, and TCP.
+interface NetworkService {
+ CreateURLLoader(URLLoader&? loader);
+
+ GetCookieStore(CookieStore&? cookie_store);
+
+ CreateWebSocket(WebSocket& socket);
+
+ // Creates a TCP socket bound to a given local address. This bound socket
+ // can be used for creating a client or server socket on that local address.
+ //
+ // If you want to create a client socket to connect to a server and are in
+ // the common case where you don't care about the local address it's bound
+ // to, use CreateTCPClientSocket.
+ //
+ // The local address can specify 0 for the port to specify that the OS should
+ // pick an available port for the given address, or it can pass 0 for the
+ // address and port for the OS to pick both the local address and port. In
+ // all success cases, the resulting local address will be passed to the
+ // callback as bound_to.
+ CreateTCPBoundSocket(NetAddress local_address,
+ TCPBoundSocket& bound_socket)
+ => (NetworkError result, NetAddress? bound_to);
+
+ // Creates a client socket connected to the given remote address. A local
+ // address and port will be allocated for the connection and passed to the
+ // callback on success.
+ //
+ // If you want control over the local address and port, instead use
+ // CreateTCPBoundSocket.
+ CreateTCPClientSocket(NetAddress remote_address,
+ handle<data_pipe_consumer> send_stream,
+ handle<data_pipe_producer> receive_stream,
+ TCPClientSocket& client_socket)
+ => (NetworkError result,
+ NetAddress? local_address);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/tcp_bound_socket.mojom b/mojo/services/public/interfaces/network/tcp_bound_socket.mojom
new file mode 100644
index 0000000..0772846
--- /dev/null
+++ b/mojo/services/public/interfaces/network/tcp_bound_socket.mojom
@@ -0,0 +1,39 @@
+// 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 "mojo/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+import "mojo/services/public/interfaces/network/tcp_client_socket.mojom"
+import "mojo/services/public/interfaces/network/tcp_server_socket.mojom"
+
+module mojo {
+
+// Represents a TCP socket that is bound to a local address and port, but
+// is not yet in a listening or connected state.
+//
+// A bound socket can be used to create a server socket listening on the
+// local address, or it can be used to create a client socket by connecting to
+// a remote host.
+interface TCPBoundSocket {
+ // Puts the socket into server mode, awaiting incoming connections.
+ //
+ // Once this function is called, neither StartListening nor Connect can be
+ // used on this socket again.
+ StartListening(TCPServerSocket& server);
+
+ // Puts this socket into client mode by connecting to a remote host. If you
+ // do not care about the local address or port, you can call
+ // NetworkService.CreateTCPClientSocket to connect directly and skip the
+ // "bound" state.
+ //
+ // Once this function is called, neither StartListening nor Connect can be
+ // used on this socket again.
+ Connect(NetAddress remote_address,
+ handle<data_pipe_consumer> send_stream,
+ handle<data_pipe_producer> receive_stream,
+ TCPClientSocket& client_socket)
+ => (NetworkError result);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/tcp_client_socket.mojom b/mojo/services/public/interfaces/network/tcp_client_socket.mojom
new file mode 100644
index 0000000..2dd3544
--- /dev/null
+++ b/mojo/services/public/interfaces/network/tcp_client_socket.mojom
@@ -0,0 +1,16 @@
+// 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 {
+
+// Represents a TCP socket connected to a report system.
+//
+// Reading and writing over the connection are done via the data pipe supplied
+// by the caller when creating the socket.
+interface TCPClientSocket {
+ // TODO(brettw) here we put options and whatnot for controlling the
+ // connection.
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/tcp_server_socket.mojom b/mojo/services/public/interfaces/network/tcp_server_socket.mojom
new file mode 100644
index 0000000..4b2e596
--- /dev/null
+++ b/mojo/services/public/interfaces/network/tcp_server_socket.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.
+
+import "mojo/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+import "mojo/services/public/interfaces/network/tcp_client_socket.mojom"
+
+module mojo {
+
+// Represents a TCP server socket listening for incoming requests.
+[Client=TCPServerSocketClient]
+interface TCPServerSocket {
+ // Accepts an incoming connection request and hooks up a TCPClientSocket for
+ // connecting with the remote host. This function is called in reponse to
+ // OnConnectionAvailable().
+ //
+ // On success, the address of the remote host will be provided.
+ AcceptConnection(handle<data_pipe_consumer> send_stream,
+ handle<data_pipe_producer> receive_stream,
+ TCPClientSocket& client_socket)
+ => (NetworkError result, NetAddress? remote_address);
+};
+
+interface TCPServerSocketClient {
+ // Notifies the client that an incoming connection is available.
+ //
+ // The client should call AcceptConnection() to accept the request.
+ OnConnectionAvailable();
+
+ // TODO(brettw) probably need some error reporting function here.
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/udp_socket.mojom b/mojo/services/public/interfaces/network/udp_socket.mojom
new file mode 100644
index 0000000..b5b848d
--- /dev/null
+++ b/mojo/services/public/interfaces/network/udp_socket.mojom
@@ -0,0 +1,74 @@
+// 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 "mojo/services/public/interfaces/network/net_address.mojom"
+import "mojo/services/public/interfaces/network/network_error.mojom"
+
+module mojo {
+
+// UDPSocket and UDPSocketClient represent a UDP socket and its client. The
+// typical flow of using the interfaces is:
+// - Acquire a UDPSocket interface pointer and set a UDPSocketClient instance.
+// - (optional) Set options which are allowed prior to Bind().
+// - Bind the socket.
+// - (optional) Set options which are allowed after Bind().
+// - Send / request to receive packets. Received packets will be delivered to
+// UDPSocketClient.OnReceived().
+[Client=UDPSocketClient]
+interface UDPSocket {
+ // Allows the socket to share the local address to which it will be bound with
+ // other processes. Should be called before Bind().
+ // (This is equivalent to SO_REUSEADDR of the POSIX socket API.)
+ AllowAddressReuse() => (NetworkError result);
+
+ // Binds the socket to the given address.
+ // |bound_addr| is non-NULL on success. It might not be the same as |addr|.
+ // For example, if port 0 is used in |addr|, a random port is picked and
+ // returned in |bound_addr|.
+ Bind(NetAddress addr) => (NetworkError result, NetAddress? bound_addr);
+
+ // Sets the send buffer size (in bytes) for the socket. The socket must be
+ // bound.
+ //
+ // Note: This is only treated as a hint. Even if it succeeds, the service
+ // doesn't guarantee it will conform to the size.
+ SetSendBufferSize(uint32 size) => (NetworkError result);
+
+ // Sets the receive buffer size (in bytes) for the socket. The socket must be
+ // bound.
+ //
+ // Note: This is only treated as a hint. Even if it succeeds, the service
+ // doesn't guarantee it will conform to the size.
+ SetReceiveBufferSize(uint32 size) => (NetworkError result);
+
+ // Notifies that the client is ready to accept |number| of packets.
+ // Correspondingly, OnReceived() of the UDPSocketClient interface will be
+ // called |number| times (errors also count), unless the connection is closed
+ // before that. The socket must be bound.
+ //
+ // It is allowed to call this method again before the previous request is
+ // completely satisfied. For example:
+ // service->ReceiveMorePackets(3);
+ // ...
+ // // OnReceived() is called.
+ // // OnReceived() is called.
+ // ...
+ // service->ReceiveMorePackets(3);
+ // // The client expects 4 more calls to OnReceived().
+ ReceiveMorePackets(uint32 number);
+
+ // Sends data to the specified destination. The socket must be bound.
+ // The method doesn't report the result of the operation.
+ SendToAndForget(NetAddress addr, array<uint8> data);
+
+ // Sends data to the specified destination. The socket must be bound.
+ SendTo(NetAddress addr, array<uint8> data) => (NetworkError result);
+};
+
+interface UDPSocketClient {
+ // |addr| and |data| are non-NULL on success.
+ OnReceived(NetworkError result, NetAddress? addr, array<uint8>? data);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/url_loader.mojom b/mojo/services/public/interfaces/network/url_loader.mojom
new file mode 100644
index 0000000..61cdc8f
--- /dev/null
+++ b/mojo/services/public/interfaces/network/url_loader.mojom
@@ -0,0 +1,106 @@
+// 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 "mojo/services/public/interfaces/network/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.
+ array<string?>? headers;
+
+ // The payload for the request body, represented as a concatenation of data
+ // streams. For HTTP requests, the method must be set to "POST" or "PUT".
+ array<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;
+
+ // The buffer size of the data pipe returned in URLResponse's |body| member.
+ // A value of 0 indicates that the default buffer size should be used. This
+ // value is just a suggestion. The URLLoader may choose to ignore this value.
+ uint32 response_body_buffer_size = 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 {
+ // If the response resulted in a network level error, this field will be set.
+ NetworkError? error;
+
+ // The response body stream. Read from this data pipe to receive the bytes of
+ // response body.
+ handle<data_pipe_consumer>? body;
+
+ // 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.
+ array<string?>? headers;
+
+ // The MIME type of the response body.
+ string? mime_type;
+
+ // The character set of the response body.
+ string? charset;
+
+ // These fields are set to non-NULL if this response corresponds to a
+ // redirect. Call the |FollowRedirect| method on the URLLoader instance to
+ // follow this redirect.
+ string? redirect_method;
+ string? redirect_url;
+};
+
+struct URLLoaderStatus {
+ // If the loader has failed due to a network level error, this field will be
+ // set.
+ NetworkError? error;
+
+ // Set to true if the URLLoader is still working. Set to false once an error
+ // is encountered or the response body is completely copied to the response
+ // body stream.
+ bool is_loading;
+
+ // TODO(darin): Add further details about the stages of loading (e.g.,
+ // "resolving host") that happen prior to receiving bytes.
+};
+
+interface URLLoader {
+ // Loads the given |request|, asynchronously producing |response|. Consult
+ // |response| to determine if the request resulted in an error, was
+ // redirected, or has a response body to be consumed.
+ Start(URLRequest? request) => (URLResponse? response);
+
+ // If the request passed to |Start| had |auto_follow_redirects| set to false,
+ // then upon receiving an URLResponse with a non-NULL |redirect_url| field,
+ // |FollowRedirect| may be called to load the URL indicated by the redirect.
+ FollowRedirect() => (URLResponse? response);
+
+ // Query status about the URLLoader.
+ QueryStatus() => (URLLoaderStatus? status);
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/web_socket.mojom b/mojo/services/public/interfaces/network/web_socket.mojom
new file mode 100644
index 0000000..d96fb26
--- /dev/null
+++ b/mojo/services/public/interfaces/network/web_socket.mojom
@@ -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.
+
+import "mojo/services/public/interfaces/network/network_error.mojom"
+
+module mojo {
+
+interface WebSocket {
+ enum MessageType {
+ CONTINUATION,
+ TEXT,
+ BINARY
+ };
+ const uint16 kAbnormalCloseCode = 1006; // stolen from websocket_bridge
+
+ // Initiates a WebSocket connection to the given url. |send_stream| is a data
+ // pipe which should remain open for the lifetime of the WebSocket. Data
+ // to send over the WebSocket should be written to the producer end of the
+ // |send_stream|.
+ Connect(string url,
+ array<string> protocols,
+ string origin,
+ handle<data_pipe_consumer> send_stream,
+ WebSocketClient client);
+
+ // Called after writing |num_bytes| worth of data to the WebSocket's
+ // |send_stream|.
+ Send(bool fin, MessageType type, uint32 num_bytes);
+
+ FlowControl(int64 quota);
+
+ Close(uint16 code, string reason);
+};
+
+interface WebSocketClient {
+ // Called in response to a WebSocket.Connect call to indicate success or
+ // failure. |receive_stream| is a data pipe which where incoming data from
+ // the server is written.
+ DidConnect(bool fail,
+ string selected_subprotocol,
+ string extensions,
+ handle<data_pipe_consumer> receive_stream);
+
+ // Called when there is |num_bytes| worth of incoming data available on the
+ // |receive_stream|.
+ DidReceiveData(bool fin, WebSocket.MessageType type, uint32 num_bytes);
+
+ DidReceiveFlowControl(int64 quota);
+
+ DidFail(string message);
+
+ DidClose(bool was_clean, uint16 code, string reason);
+
+ // Blink has 3 extra methods that we don't implement, because they are used
+ // for the inspector:
+ // didStartOpeningHandshake
+ // didFinishOpeningHandshake
+ // didStartClosingHandshake
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/BUILD.gn b/mojo/services/public/interfaces/surfaces/BUILD.gn
new file mode 100644
index 0000000..cd00cd5
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/BUILD.gn
@@ -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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_bindings
+mojom("surfaces") {
+ sources = [
+ "quads.mojom",
+ "surfaces.mojom",
+ "surfaces_service.mojom",
+ ]
+
+ deps = [
+ ":surface_id",
+ "//mojo/services/gles2:interfaces",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/native_viewport",
+ ]
+}
+
+
+mojom("surface_id") {
+ sources = [
+ "surface_id.mojom",
+ ]
+}
diff --git a/mojo/services/public/interfaces/surfaces/quads.mojom b/mojo/services/public/interfaces/surfaces/quads.mojom
new file mode 100644
index 0000000..4f51be4
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/quads.mojom
@@ -0,0 +1,210 @@
+// 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 "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+
+module mojo {
+
+struct Color {
+ uint32 rgba;
+};
+
+// TODO(jamesr): Populate subtype fields.
+struct CheckerboardQuadState {};
+
+struct DebugBorderQuadState {};
+
+struct IoSurfaceContentQuadState {};
+
+struct RenderPassId {
+ int32 layer_id;
+ int32 index;
+};
+
+struct RenderPassQuadState {
+ RenderPassId render_pass_id;
+
+ // If nonzero, resource id of mask to use when drawing this pass.
+ uint32 mask_resource_id;
+ RectF mask_uv_rect;
+
+ // Post-processing filters, applied to the pixels in the render pass' texture.
+ // TODO(jamesr): Support
+ // FilterOperations filters;
+
+ // The scale from layer space of the root layer of the render pass to
+ // the render pass physical pixels. This scale is applied to the filter
+ // parameters for pixel-moving filters. This scale should include
+ // content-to-target-space scale, and device pixel ratio.
+ PointF filters_scale;
+
+ // Post-processing filters, applied to the pixels showing through the
+ // background of the render pass, from behind it.
+ // TODO(jamesr): Support
+ // FilterOperations background_filters;
+};
+
+struct SolidColorQuadState {
+ Color color;
+ bool force_anti_aliasing_off;
+};
+
+struct SurfaceQuadState {
+ SurfaceId surface;
+};
+
+struct TextureQuadState {
+ uint32 resource_id;
+ bool premultiplied_alpha;
+ PointF uv_top_left;
+ PointF uv_bottom_right;
+ Color background_color;
+ array<float, 4> vertex_opacity;
+ bool flipped;
+};
+
+struct TileQuadState {
+ RectF tex_coord_rect;
+ Size texture_size;
+ bool swizzle_contents;
+ uint32 resource_id;
+};
+
+struct StreamVideoQuadState {};
+
+enum YUVColorSpace {
+ REC_601, // SDTV standard with restricted "studio swing" color range.
+ REC_601_JPEG, // Full color range [0, 255] variant of the above.
+};
+
+struct YUVVideoQuadState {
+ RectF tex_coord_rect;
+ uint32 y_plane_resource_id;
+ uint32 u_plane_resource_id;
+ uint32 v_plane_resource_id;
+ uint32 a_plane_resource_id;
+ YUVColorSpace color_space;
+};
+
+enum Material {
+ CHECKERBOARD = 1,
+ DEBUG_BORDER,
+ IO_SURFACE_CONTENT,
+ PICTURE_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.
+ Rect rect;
+
+ // This specifies the region of the quad that is opaque. This rect lives in
+ // content space.
+ 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.
+ 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;
+ TileQuadState? tile_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.
+ Transform content_to_target_transform;
+
+ // This size lives in the content space for the quad's originating layer.
+ Size content_bounds;
+
+ // This rect lives in the content space for the quad's originating layer.
+ Rect visible_content_rect;
+
+ // This rect lives in the target content space.
+ Rect clip_rect;
+
+ bool is_clipped;
+ float opacity;
+ SkXfermode blend_mode;
+ int32 sorting_context_id;
+};
+
+struct Pass {
+ int32 id;
+ Rect output_rect;
+ Rect damage_rect;
+ Transform transform_to_root_target;
+ bool has_transparent_background;
+ array<Quad> quads;
+ array<SharedQuadState> shared_quad_states;
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/surface_id.mojom b/mojo/services/public/interfaces/surfaces/surface_id.mojom
new file mode 100644
index 0000000..5a7686d
--- /dev/null
+++ b/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 {
+
+struct SurfaceId {
+ uint64 id;
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/surfaces.mojom b/mojo/services/public/interfaces/surfaces/surfaces.mojom
new file mode 100644
index 0000000..1c5b0b9
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/surfaces.mojom
@@ -0,0 +1,73 @@
+// 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 "mojo/services/gles2/command_buffer.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/surfaces/quads.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+
+module mojo {
+
+enum ResourceFormat {
+ RGBA_8888,
+ RGBA_4444,
+ BGRA_8888,
+ ALPHA_8,
+ LUMINANCE_8,
+ RGB_565,
+ ETC1,
+};
+
+struct Mailbox {
+ array<int8, 64> name;
+};
+
+struct MailboxHolder {
+ Mailbox mailbox;
+ uint32 texture_target;
+ uint32 sync_point;
+};
+
+struct TransferableResource {
+ uint32 id;
+ ResourceFormat format;
+ uint32 filter;
+ Size size;
+ MailboxHolder mailbox_holder;
+ bool is_repeated;
+ bool is_software;
+};
+
+struct ReturnedResource {
+ uint32 id;
+ uint32 sync_point;
+ int32 count;
+ bool lost;
+};
+
+struct Frame {
+ array<TransferableResource> resources;
+ array<Pass> passes;
+};
+
+interface SurfaceClient {
+ ReturnResources(array<ReturnedResource> resources);
+};
+
+[Client=SurfaceClient]
+interface Surface {
+ // The id is created by the client and must be unique and contain the
+ // connection's namespace in the upper 32 bits.
+ CreateSurface(SurfaceId id, Size size);
+
+ // The client can only submit frames to surfaces created with this connection.
+ SubmitFrame(SurfaceId id, Frame frame);
+ DestroySurface(SurfaceId id);
+
+ CreateGLES2BoundSurface(CommandBuffer gles2_client,
+ SurfaceId id,
+ Size size);
+};
+
+}
diff --git a/mojo/services/public/interfaces/surfaces/surfaces_service.mojom b/mojo/services/public/interfaces/surfaces/surfaces_service.mojom
new file mode 100644
index 0000000..76faeec
--- /dev/null
+++ b/mojo/services/public/interfaces/surfaces/surfaces_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 "mojo/services/public/interfaces/surfaces/surfaces.mojom"
+
+module mojo {
+
+// Use this interface to request connections to the surfaces service. Each
+// connection is associated with an id namespace
+interface SurfacesService {
+ CreateSurfaceConnection() => (Surface surface, uint32 id_namespace);
+};
+
+}
diff --git a/mojo/services/public/interfaces/view_manager/BUILD.gn b/mojo/services/public/interfaces/view_manager/BUILD.gn
new file mode 100644
index 0000000..0bc0920
--- /dev/null
+++ b/mojo/services/public/interfaces/view_manager/BUILD.gn
@@ -0,0 +1,20 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_bindings
+mojom("view_manager") {
+ sources = [
+ "view_manager.mojom",
+ "view_manager_constants.mojom",
+ ]
+
+ deps = [
+ "//mojo/public/interfaces/application",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/input_events",
+ "//mojo/services/public/interfaces/surfaces:surface_id",
+ ]
+}
diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom
new file mode 100644
index 0000000..094f5d3
--- /dev/null
+++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -0,0 +1,214 @@
+// 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 "mojo/public/interfaces/application/service_provider.mojom"
+import "mojo/services/public/interfaces/geometry/geometry.mojom"
+import "mojo/services/public/interfaces/input_events/input_events.mojom"
+import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
+import "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom"
+
+module mojo {
+
+struct ViewData {
+ uint32 parent_id;
+ uint32 view_id;
+ mojo.Rect bounds;
+ // True if this view is visible. The view may not be drawn on screen (see
+ // drawn for specifics).
+ bool visible;
+ // True if this view is drawn on screen. A view is drawn if attached to the
+ // root and all ancestors (including this view) are visible.
+ bool drawn;
+};
+
+enum ErrorCode {
+ NONE,
+ VALUE_IN_USE,
+ ILLEGAL_ARGUMENT,
+};
+
+// ViewManagerInitService is used to grant an application a connection to the
+// ViewManager by embedding it at an approriate View.
+interface ViewManagerInitService {
+ // Embed the application @ |url| at an appropriate View.
+ // The first time this method is called in the lifetime of a View Manager
+ // application instance, the "appropriate View" is defined as being the
+ // service root View.
+ // Subsequent times, implementation of this method is delegated to the
+ // application embedded at the service root View. This application is
+ // typically referred to as the "window manager", and will have a specific
+ // definition of where within its View hierarchy to embed an unparented URL.
+ // See ViewManagerService below for more details about |service_provider|.
+ Embed(string url, ServiceProvider? service_provider) => (bool success);
+};
+
+// 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 view is identified with a connection id of 0, and value of 1.
+[Client=ViewManagerClient]
+interface ViewManagerService {
+ // 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.
+ // Errors:
+ // ERROR_CODE_VALUE_IN_USE: a view already exists with the specified id.
+ // ERROR_CODE_ILLEGAL_ARGUMENT: The connection part of |view_id| does not
+ // match the connection id of the client.
+ CreateView(uint32 view_id) => (ErrorCode error_code);
+
+ // Deletes a view. This does not recurse. No hierarchy change notifications
+ // are sent as a result of this. Only the connection that created the view can
+ // delete it.
+ DeleteView(uint32 view_id) => (bool success);
+
+ // Sets the specified bounds of the specified view.
+ SetViewBounds(uint32 view_id, mojo.Rect bounds) => (bool success);
+
+ // Sets the visibility of the specified view to |visible|. Connections are
+ // allowed to change the visibility of any view they have created, as well as
+ // any of their roots.
+ SetViewVisibility(uint32 view_id, bool visible) => (bool success);
+
+ // Reparents a view.
+ // This fails for any of the following reasons:
+ // . |parent| or |child| does not identify a valid view.
+ // . |child| is an ancestor of |parent|.
+ // . |child| is already a child of |parent|.
+ //
+ // This may result in a connection getting OnViewDeleted(). See
+ // RemoveViewFromParent for details.
+ AddView(uint32 parent, uint32 child) => (bool success);
+
+ // Removes a view from its current parent. This fails if the view is not
+ // valid or the view already has no parent.
+ //
+ // Removing a view from a parent may result in OnViewDeleted() being sent to
+ // other connections. For example, connection A has views 1 and 2, with 2 a
+ // child of 1. Connection B has a root 1. If 2 is removed from 1 then B gets
+ // OnViewDeleted(). This is done as view 2 is effectively no longer visible to
+ // connection B.
+ RemoveViewFromParent(uint32 view_id) => (bool success);
+
+ // Reorders a view in its parent, relative to |relative_view_id| according to
+ // |direction|.
+ // Only the connection that created the view's parent can reorder its
+ // children.
+ ReorderView(uint32 view_id,
+ uint32 relative_view_id,
+ OrderDirection direction) => (bool success);
+
+ // Returns the views comprising the tree starting at |view_id|. |view_id| is
+ // the first result in the return value, unless |view_id| is invalid, in which
+ // case an empty vector is returned. The views are visited using a depth first
+ // search (pre-order).
+ GetViewTree(uint32 view_id) => (array<ViewData> views);
+
+ // Shows the surface in the specified view.
+ SetViewSurfaceId(uint32 view_id, SurfaceId surface_id) => (bool success);
+
+ // Embeds the app for |url| in the specified view. More specifically this
+ // creates a new connection to the specified url, expecting to get a
+ // ViewManagerClient and configures it with the root view |view|. Fails
+ // if |view| was not created by this connection.
+ //
+ // A view may only be a root of one connection at a time. Subsequent calls to
+ // Embed() for the same view result in the view being removed from the
+ // current connection. The current connection is told this by way of
+ // OnViewDeleted().
+ //
+ // When a connection embeds an app the connection no longer has priviledges
+ // to access or see any of the children of the view. If the view had existing
+ // children the children are removed. The one exception is the root
+ // connection.
+ //
+ // If |view_id| is 0, the View Manager delegates determination of what view to
+ // embed |url| at to the app embedded at the service root view (i.e. the
+ // window manager).
+ //
+ // |service_provider| encapsulates services offered by the embedder to the
+ // embedded app alongside this Embed() call. It also provides a means for
+ // the embedder to connect to services symmetrically exposed by the embedded
+ // app. Note that if a different app is subsequently embedded at |view_id|
+ // the |service_provider|'s connection to its client in the embedded app and
+ // any services it provided are not broken and continue to be valid.
+ Embed(string url,
+ uint32 view_id,
+ ServiceProvider? service_provider) => (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 views are not sent to the connection that originated the
+// change. For example, if connection 1 changes the bounds of a view by calling
+// SetBounds(), connection 1 does not receive OnViewBoundsChanged().
+[Client=ViewManagerService]
+interface ViewManagerClient {
+ // Invoked when the client application has been embedded at |root|.
+ // See Embed() on ViewManagerService for more details.
+ OnEmbed(uint16 connection_id,
+ string embedder_url,
+ ViewData root,
+ ServiceProvider&? service_provider);
+
+ // Invoked when a view's bounds have changed.
+ OnViewBoundsChanged(uint32 view,
+ 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 view. For example, if the old_parent is NULL, 0 is
+ // supplied.
+ // |views| contains any views that are that the client has not been told
+ // about. This is not sent for hierarchy changes of views not known to this
+ // client or not attached to the tree.
+ OnViewHierarchyChanged(uint32 view,
+ uint32 new_parent,
+ uint32 old_parent,
+ array<ViewData> views);
+
+ // Invoked when the order of views within a parent changes.
+ OnViewReordered(uint32 view_id,
+ uint32 relative_view_id,
+ OrderDirection direction);
+
+ // Invoked when a view is deleted.
+ OnViewDeleted(uint32 view);
+
+ // Invoked when the visibility of the specified view changes.
+ OnViewVisibilityChanged(uint32 view, bool visible);
+
+ // Invoked when a change to the visibility of |view| or one if it's ancestors
+ // is done such that the drawn state changes. This is only invoked for the
+ // top most view of a particular connection. For example, if you have the
+ // hierarchy: A -> B1 -> B2 (B2 is a child of B1 and B1 a child of A), B1/B2
+ // are from connection 2 and A from connection 1 with all views visible and
+ // drawn and the visiblity of A changes to false, then connection 2 is told
+ // the drawn state of B1 has changed (to false), but is not told anything
+ // about B2 as it's drawn state can be calculated from that of B1.
+ //
+ // NOTE: This is not invoked if OnViewVisibilityChanged() is invoked.
+ OnViewDrawnStateChanged(uint32 view, bool drawn);
+
+ // Invoked when an event is targeted at the specified view.
+ OnViewInputEvent(uint32 view, mojo.Event event) => ();
+
+ // TODO(sky): The following methods represent an interface between the view
+ // manager and the application embedded at the service root view
+ // (i.e. the window manager). These methods are not called on
+ // any other clients. They should be moved to a separate interface
+ // once support for derived FIFOs is landed.
+
+ // Requests the window manager create a "top level" view embedding |url|.
+ Embed(string url, ServiceProvider&? service_provider);
+
+ // Requests the view manager dispatch the event.
+ DispatchOnViewInputEvent(mojo.Event event);
+};
+
+}
diff --git a/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom b/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom
new file mode 100644
index 0000000..5d13805
--- /dev/null
+++ b/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 {
+
+enum OrderDirection {
+ ABOVE = 1,
+ BELOW,
+};
+
+}
diff --git a/mojo/services/public/interfaces/window_manager/BUILD.gn b/mojo/services/public/interfaces/window_manager/BUILD.gn
new file mode 100644
index 0000000..ee50bc5
--- /dev/null
+++ b/mojo/services/public/interfaces/window_manager/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager_bindings
+mojom("window_manager") {
+ sources = [
+ "window_manager.mojom",
+ ]
+}
diff --git a/mojo/services/public/interfaces/window_manager/window_manager.mojom b/mojo/services/public/interfaces/window_manager/window_manager.mojom
new file mode 100644
index 0000000..41deb44
--- /dev/null
+++ b/mojo/services/public/interfaces/window_manager/window_manager.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.
+
+module mojo {
+
+[Client=WindowManagerClient]
+interface WindowManagerService {
+ SetCapture(uint32 view_id) => (bool success);
+ FocusWindow(uint32 view_id) => (bool success);
+ ActivateWindow(uint32 view_id) => (bool success);
+};
+
+[Client=WindowManagerService]
+interface WindowManagerClient {
+ // Called when the window manager is ready to use (in the event a client
+ // connects to it before it has been initialized).
+ OnWindowManagerReady();
+
+ // TODO(beng): how is the WM supposed to know if a view is known to a client
+ // or not?
+ OnCaptureChanged(uint32 old_capture_view_id, uint32 new_capture_view_id);
+
+ OnFocusChanged(uint32 old_focused_node_id, uint32 new_focused_node_id);
+ OnActiveWindowChanged(uint32 old_focused_window, uint32 new_focused_window);
+};
+
+}
diff --git a/mojo/services/surfaces/BUILD.gn b/mojo/services/surfaces/BUILD.gn
new file mode 100644
index 0000000..8c43ca5
--- /dev/null
+++ b/mojo/services/surfaces/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_services.gypi:mojo_surfaces_service
+shared_library("surfaces") {
+ output_name = "mojo_surfaces_service"
+
+ deps = [
+ "//base",
+ "//cc",
+ "//cc/surfaces",
+ "//ui/gfx/geometry",
+ "//mojo/application",
+ "//mojo/cc",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/gles2:for_shared_library",
+ "//mojo/services/gles2:interfaces",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/services/public/cpp/surfaces",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/surfaces",
+ ]
+
+ sources = [
+ "surfaces_impl.cc",
+ "surfaces_impl.h",
+ "surfaces_service_application.cc",
+ "surfaces_service_application.h",
+ "surfaces_service_impl.cc",
+ "surfaces_service_impl.h",
+ ]
+}
diff --git a/mojo/services/surfaces/DEPS b/mojo/services/surfaces/DEPS
new file mode 100644
index 0000000..3c07200
--- /dev/null
+++ b/mojo/services/surfaces/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+cc",
+ "-cc/blink",
+ "+mojo/application",
+ "+mojo/cc",
+ "+mojo/services/gles2",
+ "+mojo/services/public",
+]
diff --git a/mojo/services/surfaces/surfaces_impl.cc b/mojo/services/surfaces/surfaces_impl.cc
new file mode 100644
index 0000000..9d79a7a
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_impl.cc
@@ -0,0 +1,114 @@
+// 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/surfaces/surfaces_impl.h"
+
+#include "cc/output/compositor_frame.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/surfaces/display.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/cc/context_provider_mojo.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+
+namespace mojo {
+
+SurfacesImpl::SurfacesImpl(cc::SurfaceManager* manager,
+ uint32_t id_namespace,
+ Client* client)
+ : manager_(manager),
+ factory_(manager, this),
+ id_namespace_(id_namespace),
+ client_(client) {
+}
+
+SurfacesImpl::~SurfacesImpl() {
+}
+
+void SurfacesImpl::CreateSurface(SurfaceIdPtr id, mojo::SizePtr size) {
+ cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+ if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+ // Bad message, do something bad to the caller?
+ NOTREACHED();
+ return;
+ }
+ factory_.Create(id.To<cc::SurfaceId>(), size.To<gfx::Size>());
+}
+
+void SurfacesImpl::SubmitFrame(SurfaceIdPtr id, FramePtr frame_ptr) {
+ cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+ if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+ // Bad message, do something bad to the caller?
+ LOG(FATAL) << "Received frame for id " << cc_id.id << " namespace "
+ << cc::SurfaceIdAllocator::NamespaceForId(cc_id)
+ << " should be namespace " << id_namespace_;
+ return;
+ }
+ factory_.SubmitFrame(id.To<cc::SurfaceId>(),
+ frame_ptr.To<scoped_ptr<cc::CompositorFrame> >(),
+ base::Closure());
+ client_->FrameSubmitted();
+}
+
+void SurfacesImpl::DestroySurface(SurfaceIdPtr id) {
+ cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+ if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+ // Bad message, do something bad to the caller?
+ NOTREACHED();
+ return;
+ }
+ factory_.Destroy(id.To<cc::SurfaceId>());
+}
+
+void SurfacesImpl::CreateGLES2BoundSurface(CommandBufferPtr gles2_client,
+ SurfaceIdPtr id,
+ mojo::SizePtr size) {
+ command_buffer_handle_ = gles2_client.PassMessagePipe();
+
+ cc::SurfaceId cc_id = id.To<cc::SurfaceId>();
+ if (cc::SurfaceIdAllocator::NamespaceForId(cc_id) != id_namespace_) {
+ // Bad message, do something bad to the caller?
+ LOG(FATAL) << "Received request for id " << cc_id.id << " namespace "
+ << cc::SurfaceIdAllocator::NamespaceForId(cc_id)
+ << " should be namespace " << id_namespace_;
+ return;
+ }
+ if (!display_) {
+ display_.reset(new cc::Display(this, manager_, NULL));
+ client_->SetDisplay(display_.get());
+ display_->Initialize(make_scoped_ptr(new cc::OutputSurface(
+ new ContextProviderMojo(command_buffer_handle_.Pass()))));
+ }
+ factory_.Create(cc_id, size.To<gfx::Size>());
+ display_->Resize(cc_id, size.To<gfx::Size>());
+}
+
+void SurfacesImpl::ReturnResources(const cc::ReturnedResourceArray& resources) {
+ Array<ReturnedResourcePtr> ret(resources.size());
+ for (size_t i = 0; i < resources.size(); ++i) {
+ ret[i] = ReturnedResource::From(resources[i]);
+ }
+ client()->ReturnResources(ret.Pass());
+}
+
+void SurfacesImpl::DisplayDamaged() {
+}
+
+void SurfacesImpl::DidSwapBuffers() {
+}
+
+void SurfacesImpl::DidSwapBuffersComplete() {
+}
+
+void SurfacesImpl::CommitVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) {
+}
+
+void SurfacesImpl::OutputSurfaceLost() {
+}
+
+void SurfacesImpl::SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) {
+}
+
+} // namespace mojo
diff --git a/mojo/services/surfaces/surfaces_impl.h b/mojo/services/surfaces/surfaces_impl.h
new file mode 100644
index 0000000..fb6ce53
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_impl.h
@@ -0,0 +1,76 @@
+// 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_SURFACES_SURFACES_IMPL_H_
+#define MOJO_SERVICES_SURFACES_SURFACES_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "cc/surfaces/display_client.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+
+namespace cc {
+class Display;
+}
+
+namespace mojo {
+class ApplicationManager;
+
+class SurfaceNativeViewportClient;
+
+class SurfacesImpl : public InterfaceImpl<Surface>,
+ public cc::SurfaceFactoryClient,
+ public cc::DisplayClient {
+ public:
+ class Client {
+ public:
+ virtual void FrameSubmitted() = 0;
+ virtual void SetDisplay(cc::Display*) = 0;
+ };
+
+ SurfacesImpl(cc::SurfaceManager* manager,
+ uint32_t id_namespace,
+ Client* client);
+ virtual ~SurfacesImpl();
+
+ // Surface implementation.
+ virtual void CreateSurface(SurfaceIdPtr id, mojo::SizePtr size) OVERRIDE;
+ virtual void SubmitFrame(SurfaceIdPtr id, FramePtr frame) OVERRIDE;
+ virtual void DestroySurface(SurfaceIdPtr id) OVERRIDE;
+ virtual void CreateGLES2BoundSurface(CommandBufferPtr gles2_client,
+ SurfaceIdPtr id,
+ mojo::SizePtr size) OVERRIDE;
+
+ // SurfaceFactoryClient implementation.
+ virtual void ReturnResources(
+ const cc::ReturnedResourceArray& resources) OVERRIDE;
+
+ // DisplayClient implementation.
+ virtual void DisplayDamaged() OVERRIDE;
+ virtual void DidSwapBuffers() OVERRIDE;
+ virtual void DidSwapBuffersComplete() OVERRIDE;
+ virtual void CommitVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) OVERRIDE;
+ virtual void OutputSurfaceLost() OVERRIDE;
+ virtual void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) OVERRIDE;
+
+ cc::SurfaceFactory* factory() { return &factory_; }
+
+ private:
+ cc::SurfaceManager* manager_;
+ cc::SurfaceFactory factory_;
+ uint32_t id_namespace_;
+ Client* client_;
+ scoped_ptr<cc::Display> display_;
+ ScopedMessagePipeHandle command_buffer_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfacesImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_SURFACES_SURFACES_IMPL_H_
diff --git a/mojo/services/surfaces/surfaces_service_application.cc b/mojo/services/surfaces/surfaces_service_application.cc
new file mode 100644
index 0000000..8677be4
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_application.cc
@@ -0,0 +1,53 @@
+// 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/surfaces/surfaces_service_application.h"
+
+#include "cc/surfaces/display.h"
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/services/surfaces/surfaces_service_impl.h"
+
+namespace mojo {
+
+SurfacesServiceApplication::SurfacesServiceApplication()
+ : next_id_namespace_(1u), display_(NULL), draw_timer_(false, false) {
+}
+
+SurfacesServiceApplication::~SurfacesServiceApplication() {
+}
+
+bool SurfacesServiceApplication::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ connection->AddService(this);
+ return true;
+}
+
+void SurfacesServiceApplication::Create(
+ ApplicationConnection* connection,
+ InterfaceRequest<SurfacesService> request) {
+ BindToRequest(new SurfacesServiceImpl(&manager_, &next_id_namespace_, this),
+ &request);
+}
+
+void SurfacesServiceApplication::FrameSubmitted() {
+ if (!draw_timer_.IsRunning() && display_) {
+ draw_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(17),
+ base::Bind(base::IgnoreResult(&cc::Display::Draw),
+ base::Unretained(display_)));
+ }
+}
+
+void SurfacesServiceApplication::SetDisplay(cc::Display* display) {
+ display_ = display;
+}
+
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new mojo::SurfacesServiceApplication);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/surfaces/surfaces_service_application.h b/mojo/services/surfaces/surfaces_service_application.h
new file mode 100644
index 0000000..27fd621
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_application.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_SURFACES_SURFACES_SERVICE_APPLICATION_H_
+#define MOJO_SERVICES_SURFACES_SURFACES_SERVICE_APPLICATION_H_
+
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "cc/surfaces/surface_manager.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "mojo/services/surfaces/surfaces_impl.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+class SurfacesServiceApplication : public ApplicationDelegate,
+ public InterfaceFactory<SurfacesService>,
+ public SurfacesImpl::Client {
+ public:
+ SurfacesServiceApplication();
+ virtual ~SurfacesServiceApplication();
+
+ // ApplicationDelegate implementation.
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) OVERRIDE;
+
+ // InterfaceFactory<SurfacsServicee> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<SurfacesService> request) OVERRIDE;
+
+ // SurfacesImpl::Client implementation.
+ virtual void FrameSubmitted() OVERRIDE;
+ virtual void SetDisplay(cc::Display*) OVERRIDE;
+
+ private:
+ cc::SurfaceManager manager_;
+ uint32_t next_id_namespace_;
+ cc::Display* display_;
+ // TODO(jamesr): Integrate with real scheduler.
+ base::Timer draw_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfacesServiceApplication);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_SURFACES_SURFACES_SERVICE_APPLICATION_H_
diff --git a/mojo/services/surfaces/surfaces_service_impl.cc b/mojo/services/surfaces/surfaces_service_impl.cc
new file mode 100644
index 0000000..919f8a5
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_impl.cc
@@ -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.
+
+#include "mojo/services/surfaces/surfaces_service_impl.h"
+
+namespace mojo {
+
+SurfacesServiceImpl::SurfacesServiceImpl(cc::SurfaceManager* manager,
+ uint32_t* next_id_namespace,
+ SurfacesImpl::Client* client)
+ : manager_(manager),
+ next_id_namespace_(next_id_namespace),
+ client_(client) {
+}
+SurfacesServiceImpl::~SurfacesServiceImpl() {
+}
+
+void SurfacesServiceImpl::CreateSurfaceConnection(
+ const Callback<void(SurfacePtr, uint32_t)>& callback) {
+ uint32_t id_namespace = (*next_id_namespace_)++;
+ SurfacePtr surface;
+ BindToProxy(new SurfacesImpl(manager_, id_namespace, client_), &surface);
+ callback.Run(surface.Pass(), id_namespace);
+}
+
+} // namespace mojo
diff --git a/mojo/services/surfaces/surfaces_service_impl.h b/mojo/services/surfaces/surfaces_service_impl.h
new file mode 100644
index 0000000..21dd185
--- /dev/null
+++ b/mojo/services/surfaces/surfaces_service_impl.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_SURFACES_SURFACES_SERVICE_IMPL_H_
+#define MOJO_SERVICES_SURFACES_SURFACES_SERVICE_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "mojo/services/surfaces/surfaces_impl.h"
+
+namespace cc {
+class SurfaceManager;
+}
+
+namespace mojo {
+
+class SurfacesServiceImpl : public InterfaceImpl<SurfacesService> {
+ public:
+ // The instances pointed to by |manager|, |next_id_namespace| and |client| are
+ // owned by the caller and must outlive the SurfacesServiceImpl instance.
+ SurfacesServiceImpl(cc::SurfaceManager* manager,
+ uint32_t* next_id_namespace,
+ SurfacesImpl::Client* client);
+ virtual ~SurfacesServiceImpl();
+
+ // InterfaceImpl<SurfacesService> implementation.
+ virtual void CreateSurfaceConnection(const mojo::Callback<
+ void(mojo::SurfacePtr, uint32_t)>& callback) OVERRIDE;
+
+ private:
+ cc::SurfaceManager* manager_;
+ uint32_t* next_id_namespace_;
+ SurfacesImpl::Client* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfacesServiceImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_SURFACES_SURFACES_SERVICE_IMPL_H_
diff --git a/mojo/services/test_service/BUILD.gn b/mojo/services/test_service/BUILD.gn
new file mode 100644
index 0000000..3c50ae6
--- /dev/null
+++ b/mojo/services/test_service/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_test_service_bindings
+mojom("bindings") {
+ sources = [
+ "test_request_tracker.mojom",
+ "test_service.mojom",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_test_app
+shared_library("mojo_test_app") {
+ deps = [
+ ":bindings",
+ "//base",
+ "//base:i18n",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/application",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/utility",
+ ]
+
+ sources = [
+ "test_request_tracker_client_impl.cc",
+ "test_request_tracker_client_impl.h",
+ "test_service_application.cc",
+ "test_service_application.h",
+ "test_service_impl.cc",
+ "test_service_impl.h",
+ "test_time_service_impl.cc",
+ "test_time_service_impl.h",
+ ]
+}
+
+# GYP version: //mojo/mojo_services.gypi:mojo_test_request_tracker_app
+shared_library("mojo_test_request_tracker_app") {
+ deps = [
+ ":bindings",
+ "//base",
+ "//base:i18n",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/application",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/utility",
+ ]
+
+ sources = [
+ "test_request_tracker_client_impl.cc",
+ "test_request_tracker_client_impl.h",
+ "test_request_tracker_application.cc",
+ "test_request_tracker_application.h",
+ "test_time_service_impl.cc",
+ "test_time_service_impl.h",
+ "test_request_tracker_impl.cc",
+ "test_request_tracker_impl.h",
+ ]
+}
diff --git a/mojo/services/test_service/test_request_tracker.mojom b/mojo/services/test_service/test_request_tracker.mojom
new file mode 100644
index 0000000..a750031
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker.mojom
@@ -0,0 +1,43 @@
+// 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 {
+
+// Various counters that services can periodically send to a
+// TestTrackedRequestService for recording.
+struct ServiceStats {
+ uint64 num_new_requests;
+ double health;
+};
+
+// A per-service summary of all the ServiceStats the
+// TestTrackedRequestService has observed.
+struct ServiceReport {
+ string? service_name;
+ uint64 total_requests;
+ double mean_health;
+};
+
+// A simple interface to obtain a "report" from all services that have
+// opted to connect themselves to for request tracking.
+interface TestTrackedRequestService {
+ GetReport() => (array<ServiceReport?>? report);
+};
+
+// TestRequestTracker records ServiceStats for an individual service
+// connection for aggregation in a TestTrackedRequestService.
+[Client=TestRequestTrackerClient]
+interface TestRequestTracker {
+ // Upload a ServiceStats for tracking.
+ RecordStats(uint64 client_id, ServiceStats? stats);
+};
+
+// The client-side contract for uploading ServiceStats to TestRequestTracker.
+interface TestRequestTrackerClient {
+ const uint64 kInvalidId = 0;
+ // Handshake to tell the client its global identifier and get the name.
+ SetIdAndReturnName(uint64 id) => (string? service_name);
+};
+
+} // module mojo.test
diff --git a/mojo/services/test_service/test_request_tracker_application.cc b/mojo/services/test_service/test_request_tracker_application.cc
new file mode 100644
index 0000000..b533377
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_application.cc
@@ -0,0 +1,48 @@
+// 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_request_tracker_application.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestRequestTrackerApplication::TestRequestTrackerApplication()
+ : test_tracked_request_factory_(&context_),
+ test_request_tracker_factory_(&context_) {
+}
+
+TestRequestTrackerApplication::~TestRequestTrackerApplication() {
+}
+
+bool TestRequestTrackerApplication::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ // Every instance of the service and recorder shares the context.
+ // Note, this app is single-threaded, so this is thread safe.
+ connection->AddService(&test_tracked_request_factory_);
+ connection->AddService(&test_request_tracker_factory_);
+ connection->AddService(this);
+ return true;
+}
+
+void TestRequestTrackerApplication::Create(
+ ApplicationConnection* connection,
+ InterfaceRequest<TestTimeService> request) {
+ BindToRequest(new TestTimeServiceImpl(connection), &request);
+}
+
+} // namespace test
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunner runner(
+ new mojo::test::TestRequestTrackerApplication);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/test_service/test_request_tracker_application.h b/mojo/services/test_service/test_request_tracker_application.h
new file mode 100644
index 0000000..b519a91
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_application.h
@@ -0,0 +1,48 @@
+// 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_REQUEST_TRACKER_APPLICATION_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_APPLICATION_H_
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_request_tracker_impl.h"
+
+namespace mojo {
+namespace test {
+class TestTimeService;
+
+// Embeds TestRequestTracker mojo services into an application.
+class TestRequestTrackerApplication : public ApplicationDelegate,
+ public InterfaceFactory<TestTimeService> {
+ public:
+ TestRequestTrackerApplication();
+ virtual ~TestRequestTrackerApplication();
+
+ // ApplicationDelegate methods:
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override;
+
+ // InterfaceFactory<TestTimeService> methods:
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestTimeService> request) override;
+
+ private:
+ TrackingContext context_;
+ typedef InterfaceFactoryImplWithContext<TestTrackedRequestServiceImpl,
+ TrackingContext>
+ TestTrackedRequestFactory;
+ TestTrackedRequestFactory test_tracked_request_factory_;
+ typedef InterfaceFactoryImplWithContext<TestRequestTrackerImpl,
+ TrackingContext>
+ TestRequestTrackerFactory;
+ TestRequestTrackerFactory test_request_tracker_factory_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestRequestTrackerApplication);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_APPLICATION_H_
diff --git a/mojo/services/test_service/test_request_tracker_client_impl.cc b/mojo/services/test_service/test_request_tracker_client_impl.cc
new file mode 100644
index 0000000..c9e66c3
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_client_impl.cc
@@ -0,0 +1,54 @@
+// 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_request_tracker_client_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestRequestTrackerClientImpl::TestRequestTrackerClientImpl(
+ TestRequestTrackerPtr tracker,
+ const std::string& service_name,
+ const mojo::Callback<void()>& callback)
+ : id_(0),
+ requests_since_upload_(0),
+ service_name_(service_name),
+ tracker_(tracker.Pass()),
+ tracking_connected_callback_(callback) {
+ tracker_.set_client(this);
+}
+
+TestRequestTrackerClientImpl::~TestRequestTrackerClientImpl() {
+}
+
+void TestRequestTrackerClientImpl::RecordNewRequest() {
+ requests_since_upload_++;
+ if (id_ == kInvalidId)
+ return;
+ SendStats();
+}
+
+void TestRequestTrackerClientImpl::SendStats() {
+ ServiceStatsPtr stats(ServiceStats::New());
+ stats->num_new_requests = requests_since_upload_;
+ stats->health = 0.7;
+ tracker_->RecordStats(id_, stats.Pass());
+ requests_since_upload_ = 0;
+}
+
+void TestRequestTrackerClientImpl::SetIdAndReturnName(
+ uint64_t id,
+ const mojo::Callback<void(mojo::String)>& callback) {
+ assert(id != kInvalidId);
+ assert(id_ == kInvalidId);
+ id_ = id;
+ callback.Run(service_name_);
+ tracking_connected_callback_.Run();
+ if (requests_since_upload_ == 0)
+ return;
+ SendStats();
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/services/test_service/test_request_tracker_client_impl.h b/mojo/services/test_service/test_request_tracker_client_impl.h
new file mode 100644
index 0000000..91340cb
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_client_impl.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_TEST_SERVICE_TEST_REQUEST_TRACKER_CLIENT_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_CLIENT_IMPL_H_
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+
+namespace mojo {
+namespace test {
+
+class TestRequestTrackerClientImpl : public TestRequestTrackerClient {
+ public:
+ TestRequestTrackerClientImpl(
+ TestRequestTrackerPtr tracker,
+ const std::string& service_name,
+ const mojo::Callback<void()>& tracking_connected_callback);
+ virtual ~TestRequestTrackerClientImpl();
+
+ // Call whenever an event happens that you want to be recorded.
+ void RecordNewRequest();
+
+ // TestRequestTrackerClient impl.
+ virtual void SetIdAndReturnName(
+ uint64_t id,
+ const mojo::Callback<void(mojo::String)>& callback) override;
+
+ private:
+ void SendStats();
+ uint64_t id_;
+ uint64_t requests_since_upload_;
+ const std::string service_name_;
+ TestRequestTrackerPtr tracker_;
+ mojo::Callback<void()> tracking_connected_callback_;
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_CLIENT_IMPL_H_
diff --git a/mojo/services/test_service/test_request_tracker_impl.cc b/mojo/services/test_service/test_request_tracker_impl.cc
new file mode 100644
index 0000000..9af6266
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_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 "base/bind.h"
+#include "mojo/services/test_service/test_request_tracker_impl.h"
+
+namespace mojo {
+namespace test {
+
+ TrackingContext::TrackingContext() : next_id(1) {}
+ TrackingContext::~TrackingContext() {}
+
+ TestRequestTrackerImpl::TestRequestTrackerImpl(TrackingContext* context)
+ : context_(context), weak_factory_(this) {
+ }
+
+ TestRequestTrackerImpl::~TestRequestTrackerImpl() {
+ }
+
+void TestRequestTrackerImpl::RecordStats(
+ uint64_t client_id,
+ ServiceStatsPtr stats) {
+ assert(context_->ids_to_names.find(client_id) !=
+ context_->ids_to_names.end());
+ context_->records[client_id].push_back(*stats);
+}
+
+void TestRequestTrackerImpl::OnConnectionEstablished() {
+ uint64_t id = context_->next_id++;
+ client()->SetIdAndReturnName(id,
+ base::Bind(&TestRequestTrackerImpl::UploaderNameCallback,
+ weak_factory_.GetWeakPtr(),
+ id));
+}
+
+void TestRequestTrackerImpl::UploaderNameCallback(
+ uint64_t id, const mojo::String& name) {
+ DCHECK(context_->ids_to_names.find(id) == context_->ids_to_names.end());
+ context_->ids_to_names[id] = name;
+}
+
+TestTrackedRequestServiceImpl::TestTrackedRequestServiceImpl(
+ TrackingContext* context)
+ : context_(context) {
+}
+
+TestTrackedRequestServiceImpl::~TestTrackedRequestServiceImpl() {
+}
+
+void TestTrackedRequestServiceImpl::GetReport(
+ const mojo::Callback<void(mojo::Array<ServiceReportPtr>)>& callback) {
+ mojo::Array<ServiceReportPtr> reports;
+ for (AllRecordsMap::const_iterator it1 = context_->records.begin();
+ it1 != context_->records.end(); ++it1) {
+ ServiceReportPtr report(ServiceReport::New());
+ report->service_name = context_->ids_to_names[it1->first];
+ double mean_health_numerator = 0;
+ size_t num_samples = it1->second.size();
+ if (num_samples == 0)
+ continue;
+
+ for (std::vector<ServiceStats>::const_iterator it2 = it1->second.begin();
+ it2 != it1->second.end(); ++it2) {
+ report->total_requests += it2->num_new_requests;
+ mean_health_numerator += it2->health;
+ }
+ report->mean_health = mean_health_numerator / num_samples;
+ reports.push_back(report.Pass());
+ }
+ callback.Run(reports.Pass());
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/services/test_service/test_request_tracker_impl.h b/mojo/services/test_service/test_request_tracker_impl.h
new file mode 100644
index 0000000..736cc70
--- /dev/null
+++ b/mojo/services/test_service/test_request_tracker_impl.h
@@ -0,0 +1,65 @@
+// 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_REQUEST_TRACKER_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+
+namespace mojo {
+class ApplicationConnection;
+namespace test {
+
+typedef std::map<uint64_t, std::vector<ServiceStats> > AllRecordsMap;
+
+// Shared state between all instances of TestRequestTrackerImpl
+// and the master TrackedRequestService.
+struct TrackingContext {
+ TrackingContext();
+ ~TrackingContext();
+ AllRecordsMap records;
+ std::map<uint64_t, std::string> ids_to_names;
+ uint64_t next_id;
+};
+
+class TestRequestTrackerImpl : public InterfaceImpl<TestRequestTracker> {
+ public:
+ explicit TestRequestTrackerImpl(TrackingContext* context);
+ virtual ~TestRequestTrackerImpl();
+
+ // TestRequestTracker.
+ virtual void RecordStats(uint64_t client_id, ServiceStatsPtr stats) override;
+
+ // InterfaceImpl override.
+ virtual void OnConnectionEstablished() override;
+
+ private:
+ void UploaderNameCallback(uint64_t id, const mojo::String& name);
+ TrackingContext* context_;
+ base::WeakPtrFactory<TestRequestTrackerImpl> weak_factory_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestRequestTrackerImpl);
+};
+
+class TestTrackedRequestServiceImpl
+ : public InterfaceImpl<TestTrackedRequestService> {
+ public:
+ explicit TestTrackedRequestServiceImpl(TrackingContext* context);
+ virtual ~TestTrackedRequestServiceImpl();
+
+ // |TestTrackedRequestService| implementation.
+ virtual void GetReport(
+ const mojo::Callback<void(mojo::Array<ServiceReportPtr>)>& callback)
+ override;
+
+ private:
+ TrackingContext* context_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestTrackedRequestServiceImpl);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_REQUEST_TRACKER_IMPL_H_
diff --git a/mojo/services/test_service/test_service.mojom b/mojo/services/test_service/test_service.mojom
new file mode 100644
index 0000000..bda3b68
--- /dev/null
+++ b/mojo/services/test_service/test_service.mojom
@@ -0,0 +1,21 @@
+// 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 TestService {
+ Ping() => ();
+ // Connect to a TestTimeService at |app_url| and ferry the data back
+ // in |response|.
+ ConnectToAppAndGetTime(string? app_url) => (int64 time_usec);
+ StartTrackingRequests() => ();
+};
+
+interface TestTimeService {
+ // Provides a constant time value.
+ GetPartyTime() => (int64 time_usec);
+ StartTrackingRequests() => ();
+};
+
+} // module mojo.test
diff --git a/mojo/services/test_service/test_service_application.cc b/mojo/services/test_service/test_service_application.cc
new file mode 100644
index 0000000..f08e183
--- /dev/null
+++ b/mojo/services/test_service/test_service_application.cc
@@ -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.
+
+#include "mojo/services/test_service/test_service_application.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/services/test_service/test_service_impl.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestServiceApplication::TestServiceApplication() : ref_count_(0) {
+}
+
+TestServiceApplication::~TestServiceApplication() {
+}
+
+bool TestServiceApplication::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ connection->AddService<TestService>(this);
+ connection->AddService<TestTimeService>(this);
+ return true;
+}
+
+void TestServiceApplication::Create(ApplicationConnection* connection,
+ InterfaceRequest<TestService> request) {
+ BindToRequest(new TestServiceImpl(connection, this), &request);
+}
+
+void TestServiceApplication::Create(ApplicationConnection* connection,
+ InterfaceRequest<TestTimeService> request) {
+ BindToRequest(new TestTimeServiceImpl(connection), &request);
+}
+
+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
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunner runner(new mojo::test::TestServiceApplication);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/test_service/test_service_application.h b/mojo/services/test_service/test_service_application.h
new file mode 100644
index 0000000..47743a1
--- /dev/null
+++ b/mojo/services/test_service/test_service_application.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_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+namespace test {
+class TestService;
+class TestTimeService;
+
+class TestServiceApplication : public ApplicationDelegate,
+ public InterfaceFactory<TestService>,
+ public InterfaceFactory<TestTimeService> {
+ public:
+ TestServiceApplication();
+ virtual ~TestServiceApplication();
+
+ // ApplicationDelegate implementation.
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override;
+
+ // InterfaceFactory<TestService> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestService> request) override;
+
+ // InterfaceFactory<TestTimeService> implementation.
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestTimeService> request) 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/mojo/services/test_service/test_service_impl.cc b/mojo/services/test_service/test_service_impl.cc
new file mode 100644
index 0000000..9e589a5
--- /dev/null
+++ b/mojo/services/test_service/test_service_impl.cc
@@ -0,0 +1,67 @@
+// 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 "base/bind.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/test_service/test_request_tracker_client_impl.h"
+#include "mojo/services/test_service/test_service_application.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestServiceImpl::TestServiceImpl(ApplicationConnection* connection,
+ TestServiceApplication* application)
+ : application_(application), connection_(connection) {
+}
+
+TestServiceImpl::~TestServiceImpl() {
+}
+
+void TestServiceImpl::OnConnectionEstablished() {
+ application_->AddRef();
+}
+
+void TestServiceImpl::OnConnectionError() {
+ application_->ReleaseRef();
+}
+
+void TestServiceImpl::Ping(const mojo::Callback<void()>& callback) {
+ if (tracking_)
+ tracking_->RecordNewRequest();
+ callback.Run();
+}
+
+void SendTimeResponse(
+ const mojo::Callback<void(int64_t)>& requestor_callback,
+ int64_t time_usec) {
+ requestor_callback.Run(time_usec);
+}
+
+void TestServiceImpl::ConnectToAppAndGetTime(
+ const mojo::String& app_url,
+ const mojo::Callback<void(int64_t)>& callback) {
+ connection_->ConnectToService(app_url, &time_service_);
+ if (tracking_) {
+ tracking_->RecordNewRequest();
+ time_service_->StartTrackingRequests(mojo::Callback<void()>());
+ }
+ time_service_->GetPartyTime(base::Bind(&SendTimeResponse, callback));
+}
+
+void TestServiceImpl::StartTrackingRequests(
+ const mojo::Callback<void()>& callback) {
+ TestRequestTrackerPtr tracker;
+ connection_->ConnectToService(
+ "mojo:mojo_test_request_tracker_app", &tracker);
+ tracking_.reset(
+ new TestRequestTrackerClientImpl(tracker.Pass(), Name_, callback));
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/services/test_service/test_service_impl.h b/mojo/services/test_service/test_service_impl.h
new file mode 100644
index 0000000..379f02e
--- /dev/null
+++ b/mojo/services/test_service/test_service_impl.h
@@ -0,0 +1,47 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+
+namespace mojo {
+class ApplicationConnection;
+namespace test {
+
+class TestRequestTrackerClientImpl;
+class TestServiceApplication;
+
+class TestServiceImpl : public InterfaceImpl<TestService> {
+ public:
+ TestServiceImpl(ApplicationConnection* connection,
+ TestServiceApplication* application);
+ virtual ~TestServiceImpl();
+
+ // |TestService| methods:
+ virtual void OnConnectionEstablished() override;
+ virtual void OnConnectionError() override;
+ virtual void Ping(const mojo::Callback<void()>& callback) override;
+ virtual void ConnectToAppAndGetTime(
+ const mojo::String& app_url,
+ const mojo::Callback<void(int64_t)>& callback) override;
+ virtual void StartTrackingRequests(
+ const mojo::Callback<void()>& callback) override;
+
+ private:
+ TestServiceApplication* const application_;
+ ApplicationConnection* const connection_;
+ TestTimeServicePtr time_service_;
+ scoped_ptr<TestRequestTrackerClientImpl> tracking_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceImpl);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_
diff --git a/mojo/services/test_service/test_time_service_impl.cc b/mojo/services/test_service/test_time_service_impl.cc
new file mode 100644
index 0000000..f4adbaa
--- /dev/null
+++ b/mojo/services/test_service/test_time_service_impl.cc
@@ -0,0 +1,43 @@
+// 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/time/time.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/test_service/test_request_tracker.mojom.h"
+#include "mojo/services/test_service/test_request_tracker_client_impl.h"
+#include "mojo/services/test_service/test_time_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestTimeServiceImpl::TestTimeServiceImpl(ApplicationConnection* application)
+ : application_(application) {
+}
+
+TestTimeServiceImpl::~TestTimeServiceImpl() {
+}
+
+void TestTimeServiceImpl::StartTrackingRequests(
+ const mojo::Callback<void()>& callback) {
+ TestRequestTrackerPtr tracker;
+ application_->ConnectToService(
+ "mojo:mojo_test_request_tracker_app", &tracker);
+ tracking_.reset(new TestRequestTrackerClientImpl(
+ tracker.Pass(), Name_, callback));
+}
+
+void TestTimeServiceImpl::GetPartyTime(
+ const mojo::Callback<void(int64_t)>& callback) {
+ if (tracking_)
+ tracking_->RecordNewRequest();
+ base::Time frozen_time(base::Time::UnixEpoch()
+ + base::TimeDelta::FromDays(10957)
+ + base::TimeDelta::FromHours(7)
+ + base::TimeDelta::FromMinutes(59));
+ int64 time(frozen_time.ToInternalValue());
+ callback.Run(time);
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/services/test_service/test_time_service_impl.h b/mojo/services/test_service/test_time_service_impl.h
new file mode 100644
index 0000000..c9eaea2
--- /dev/null
+++ b/mojo/services/test_service/test_time_service_impl.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_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace test {
+
+class TestRequestTrackerClientImpl;
+
+class TestTimeServiceImpl : public InterfaceImpl<TestTimeService> {
+ public:
+ explicit TestTimeServiceImpl(ApplicationConnection* application);
+ virtual ~TestTimeServiceImpl();
+
+ // |TestTimeService| methods:
+ virtual void GetPartyTime(
+ const mojo::Callback<void(int64_t time_usec)>& callback) override;
+ virtual void StartTrackingRequests(
+ const mojo::Callback<void()>& callback) override;
+
+ private:
+ ApplicationConnection* application_;
+ scoped_ptr<TestRequestTrackerClientImpl> tracking_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestTimeServiceImpl);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_TIME_SERVICE_IMPL_H_
diff --git a/mojo/services/view_manager/BUILD.gn b/mojo/services/view_manager/BUILD.gn
new file mode 100644
index 0000000..ddb76e2
--- /dev/null
+++ b/mojo/services/view_manager/BUILD.gn
@@ -0,0 +1,96 @@
+# 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("//build/config/ui.gni")
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager
+shared_library("view_manager") {
+ output_name = "mojo_view_manager"
+
+ deps = [
+ "//base",
+ "//cc/surfaces",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/interfaces/application",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/services/public/cpp/input_events",
+ "//mojo/services/public/cpp/surfaces",
+ "//mojo/services/public/interfaces/geometry",
+ "//mojo/services/public/interfaces/input_events",
+ "//mojo/services/public/interfaces/native_viewport",
+ "//mojo/services/public/interfaces/surfaces",
+ "//mojo/services/public/interfaces/view_manager",
+ "//ui/base",
+ "//ui/events",
+ "//ui/events:events_base",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ ]
+
+ defines = [
+ "MOJO_VIEW_MANAGER_IMPLEMENTATION",
+ ]
+
+ sources = [
+ "access_policy.h",
+ "access_policy_delegate.h",
+ "connection_manager.cc",
+ "connection_manager.h",
+ "default_access_policy.cc",
+ "default_access_policy.h",
+ "display_manager.cc",
+ "display_manager.h",
+ "main.cc",
+ "server_view.cc",
+ "server_view.h",
+ "server_view_delegate.h",
+ "view_manager_export.h",
+ "view_manager_init_service_context.cc",
+ "view_manager_init_service_context.h",
+ "view_manager_init_service_impl.cc",
+ "view_manager_init_service_impl.h",
+ "view_manager_service_impl.cc",
+ "view_manager_service_impl.h",
+ "window_manager_access_policy.cc",
+ "window_manager_access_policy.h",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_view_manager_unittests
+test("mojo_view_manager_unittests") {
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//mojo/application",
+ "//mojo/application_manager",
+ "//mojo/edk/system",
+ "//mojo/environment:chromium",
+ "//mojo/public/cpp/bindings",
+ "//mojo/services/public/cpp/geometry",
+ "//mojo/services/public/cpp/input_events",
+ "//mojo/services/public/cpp/view_manager",
+ "//mojo/services/public/cpp/view_manager/lib:run_unittests",
+ "//mojo/shell:test_support",
+ "//testing/gtest",
+ "//ui/gfx/geometry",
+ ]
+
+ if (use_x11) {
+ deps += ["//ui/gfx/x"]
+ }
+
+ if (is_component_build) {
+ deps += ["//ui/gl"]
+ }
+
+ sources = [
+ "test_change_tracker.cc",
+ "test_change_tracker.h",
+ "view_manager_unittest.cc",
+ ]
+}
diff --git a/mojo/services/view_manager/DEPS b/mojo/services/view_manager/DEPS
new file mode 100644
index 0000000..d91b8be
--- /dev/null
+++ b/mojo/services/view_manager/DEPS
@@ -0,0 +1,23 @@
+include_rules = [
+ "+cc",
+ "-cc/blink",
+ "+gpu/command_buffer/service/mailbox_manager.h",
+ "+mojo/application",
+ "+mojo/cc",
+ "+mojo/geometry",
+ "+mojo/services",
+ "+third_party/skia",
+ "+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/application_manager/application_manager.h",
+ ],
+}
diff --git a/mojo/services/view_manager/access_policy.h b/mojo/services/view_manager/access_policy.h
new file mode 100644
index 0000000..cac5579
--- /dev/null
+++ b/mojo/services/view_manager/access_policy.h
@@ -0,0 +1,53 @@
+// 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_ACCESS_POLICY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
+
+#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+
+namespace mojo {
+namespace service {
+
+class ServerView;
+
+// AccessPolicy is used by ViewManagerServiceImpl to determine what a connection
+// is allowed to do.
+class AccessPolicy {
+ public:
+ virtual ~AccessPolicy() {}
+
+ // Unless otherwise mentioned all arguments have been validated. That is the
+ // |view| arguments are non-null unless otherwise stated (eg CanSetView() is
+ // allowed to take a NULL view).
+ virtual bool CanRemoveViewFromParent(const ServerView* view) const = 0;
+ virtual bool CanAddView(const ServerView* parent,
+ const ServerView* child) const = 0;
+ virtual bool CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const = 0;
+ virtual bool CanDeleteView(const ServerView* view) const = 0;
+ virtual bool CanGetViewTree(const ServerView* view) const = 0;
+ // Used when building a view tree (GetViewTree()) to decide if we should
+ // descend into |view|.
+ virtual bool CanDescendIntoViewForViewTree(const ServerView* view) const = 0;
+ virtual bool CanEmbed(const ServerView* view) const = 0;
+ virtual bool CanChangeViewVisibility(const ServerView* view) const = 0;
+ virtual bool CanSetViewSurfaceId(const ServerView* view) const = 0;
+ virtual bool CanSetViewBounds(const ServerView* view) const = 0;
+
+ // Returns whether the connection should notify on a hierarchy change.
+ // |new_parent| and |old_parent| are initially set to the new and old parents
+ // but may be altered so that the client only sees a certain set of views.
+ virtual bool ShouldNotifyOnHierarchyChange(
+ const ServerView* view,
+ const ServerView** new_parent,
+ const ServerView** old_parent) const = 0;
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_H_
diff --git a/mojo/services/view_manager/access_policy_delegate.h b/mojo/services/view_manager/access_policy_delegate.h
new file mode 100644
index 0000000..12c7479
--- /dev/null
+++ b/mojo/services/view_manager/access_policy_delegate.h
@@ -0,0 +1,39 @@
+// 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_ACCESS_POLICY_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "mojo/services/view_manager/ids.h"
+
+namespace mojo {
+namespace service {
+
+class ServerView;
+
+// Delegate used by the AccessPolicy implementations to get state.
+class AccessPolicyDelegate {
+ public:
+ // Returns the ids of the roots views for this connection. That is, this is
+ // the set of views the connection was embedded at.
+ virtual const base::hash_set<Id>& GetRootsForAccessPolicy() const = 0;
+
+ // Returns true if |view| has been exposed to the client.
+ virtual bool IsViewKnownForAccessPolicy(const ServerView* view) const = 0;
+
+ // Returns true if Embed(view) has been invoked on |view|.
+ virtual bool IsViewRootOfAnotherConnectionForAccessPolicy(
+ const ServerView* view) const = 0;
+
+ protected:
+ virtual ~AccessPolicyDelegate() {}
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_ACCESS_POLICY_DELEGATE_H_
diff --git a/mojo/services/view_manager/connection_manager.cc b/mojo/services/view_manager/connection_manager.cc
new file mode 100644
index 0000000..6d9fe2a
--- /dev/null
+++ b/mojo/services/view_manager/connection_manager.cc
@@ -0,0 +1,297 @@
+// 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/connection_manager.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/view_manager/view_manager_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+ConnectionManager::ScopedChange::ScopedChange(
+ ViewManagerServiceImpl* connection,
+ ConnectionManager* connection_manager,
+ bool is_delete_view)
+ : connection_manager_(connection_manager),
+ connection_id_(connection->id()),
+ is_delete_view_(is_delete_view) {
+ connection_manager_->PrepareForChange(this);
+}
+
+ConnectionManager::ScopedChange::~ScopedChange() {
+ connection_manager_->FinishChange();
+}
+
+ConnectionManager::ConnectionManager(
+ ApplicationConnection* app_connection,
+ const Callback<void()>& native_viewport_closed_callback)
+ : app_connection_(app_connection),
+ next_connection_id_(1),
+ display_manager_(app_connection,
+ this,
+ native_viewport_closed_callback),
+ root_(new ServerView(this, RootViewId())),
+ current_change_(NULL) {
+ root_->SetBounds(gfx::Rect(800, 600));
+}
+
+ConnectionManager::~ConnectionManager() {
+ while (!connections_created_by_connect_.empty())
+ delete *(connections_created_by_connect_.begin());
+ // All the connections should have been destroyed.
+ DCHECK(connection_map_.empty());
+ root_.reset();
+}
+
+ConnectionSpecificId ConnectionManager::GetAndAdvanceNextConnectionId() {
+ const ConnectionSpecificId id = next_connection_id_++;
+ DCHECK_LT(id, next_connection_id_);
+ return id;
+}
+
+void ConnectionManager::AddConnection(ViewManagerServiceImpl* connection) {
+ DCHECK_EQ(0u, connection_map_.count(connection->id()));
+ connection_map_[connection->id()] = connection;
+}
+
+void ConnectionManager::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 ConnectionManager::EmbedRoot(
+ const std::string& url,
+ InterfaceRequest<ServiceProvider> service_provider) {
+ if (connection_map_.empty()) {
+ EmbedImpl(kInvalidConnectionId,
+ String::From(url),
+ RootViewId(),
+ service_provider.Pass());
+ return;
+ }
+ ViewManagerServiceImpl* connection = GetConnection(kWindowManagerConnection);
+ connection->client()->Embed(url, service_provider.Pass());
+}
+
+void ConnectionManager::Embed(
+ ConnectionSpecificId creator_id,
+ const String& url,
+ Id transport_view_id,
+ InterfaceRequest<ServiceProvider> service_provider) {
+ EmbedImpl(creator_id,
+ url,
+ ViewIdFromTransportId(transport_view_id),
+ service_provider.Pass())->set_delete_on_connection_error();
+}
+
+ViewManagerServiceImpl* ConnectionManager::GetConnection(
+ ConnectionSpecificId connection_id) {
+ ConnectionMap::iterator i = connection_map_.find(connection_id);
+ return i == connection_map_.end() ? NULL : i->second;
+}
+
+ServerView* ConnectionManager::GetView(const ViewId& id) {
+ if (id == root_->id())
+ return root_.get();
+ ConnectionMap::iterator i = connection_map_.find(id.connection_id);
+ return i == connection_map_.end() ? NULL : i->second->GetView(id);
+}
+
+void ConnectionManager::OnConnectionMessagedClient(ConnectionSpecificId id) {
+ if (current_change_)
+ current_change_->MarkConnectionAsMessaged(id);
+}
+
+bool ConnectionManager::DidConnectionMessageClient(
+ ConnectionSpecificId id) const {
+ return current_change_ && current_change_->DidMessageConnection(id);
+}
+
+const ViewManagerServiceImpl* ConnectionManager::GetConnectionWithRoot(
+ const ViewId& id) const {
+ for (ConnectionMap::const_iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ if (i->second->HasRoot(id))
+ return i->second;
+ }
+ return NULL;
+}
+
+void ConnectionManager::DispatchViewInputEventToWindowManager(EventPtr 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(event.Pass());
+}
+
+void ConnectionManager::ProcessViewBoundsChanged(const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ i->second->ProcessViewBoundsChanged(
+ view, old_bounds, new_bounds, IsChangeSource(i->first));
+ }
+}
+
+void ConnectionManager::ProcessWillChangeViewHierarchy(
+ const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ i->second->ProcessWillChangeViewHierarchy(
+ view, new_parent, old_parent, IsChangeSource(i->first));
+ }
+}
+
+void ConnectionManager::ProcessViewHierarchyChanged(
+ const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ i->second->ProcessViewHierarchyChanged(
+ view, new_parent, old_parent, IsChangeSource(i->first));
+ }
+}
+
+void ConnectionManager::ProcessViewReorder(const ServerView* view,
+ const ServerView* relative_view,
+ const OrderDirection direction) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ i->second->ProcessViewReorder(
+ view, relative_view, direction, IsChangeSource(i->first));
+ }
+}
+
+void ConnectionManager::ProcessViewDeleted(const ViewId& view) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ i->second->ProcessViewDeleted(view, IsChangeSource(i->first));
+ }
+}
+
+void ConnectionManager::PrepareForChange(ScopedChange* change) {
+ // Should only ever have one change in flight.
+ CHECK(!current_change_);
+ current_change_ = change;
+}
+
+void ConnectionManager::FinishChange() {
+ // PrepareForChange/FinishChange should be balanced.
+ CHECK(current_change_);
+ current_change_ = NULL;
+}
+
+ViewManagerServiceImpl* ConnectionManager::EmbedImpl(
+ const ConnectionSpecificId creator_id,
+ const String& url,
+ const ViewId& root_id,
+ InterfaceRequest<ServiceProvider> service_provider) {
+ MessagePipe pipe;
+
+ ServiceProvider* view_manager_service_provider =
+ app_connection_->ConnectToApplication(url)->GetServiceProvider();
+ view_manager_service_provider->ConnectToService(
+ ViewManagerServiceImpl::Client::Name_, pipe.handle1.Pass());
+
+ 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>(),
+ root_id,
+ service_provider.Pass());
+ WeakBindToPipe(connection, pipe.handle0.Pass());
+ connections_created_by_connect_.insert(connection);
+ OnConnectionMessagedClient(connection->id());
+ return connection;
+}
+
+void ConnectionManager::OnViewDestroyed(const ServerView* view) {
+ ProcessViewDeleted(view->id());
+}
+
+void ConnectionManager::OnWillChangeViewHierarchy(
+ const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) {
+ if (!display_manager_.in_setup())
+ ProcessWillChangeViewHierarchy(view, new_parent, old_parent);
+}
+
+void ConnectionManager::OnViewHierarchyChanged(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) {
+ if (!display_manager_.in_setup())
+ ProcessViewHierarchyChanged(view, new_parent, old_parent);
+ // TODO(beng): optimize.
+ if (old_parent) {
+ display_manager_.SchedulePaint(old_parent,
+ gfx::Rect(old_parent->bounds().size()));
+ }
+ if (new_parent) {
+ display_manager_.SchedulePaint(new_parent,
+ gfx::Rect(new_parent->bounds().size()));
+ }
+}
+
+void ConnectionManager::OnViewBoundsChanged(const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ ProcessViewBoundsChanged(view, old_bounds, new_bounds);
+ if (!view->parent())
+ return;
+
+ // TODO(sky): optimize this.
+ display_manager_.SchedulePaint(view->parent(), old_bounds);
+ display_manager_.SchedulePaint(view->parent(), new_bounds);
+}
+
+void ConnectionManager::OnViewSurfaceIdChanged(const ServerView* view) {
+ display_manager_.SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
+void ConnectionManager::OnViewReordered(const ServerView* view,
+ const ServerView* relative,
+ OrderDirection direction) {
+ display_manager_.SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
+void ConnectionManager::OnWillChangeViewVisibility(const ServerView* view) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end();
+ ++i) {
+ i->second->ProcessWillChangeViewVisibility(view, IsChangeSource(i->first));
+ }
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/connection_manager.h b/mojo/services/view_manager/connection_manager.h
new file mode 100644
index 0000000..c92fd69
--- /dev/null
+++ b/mojo/services/view_manager/connection_manager.h
@@ -0,0 +1,206 @@
+// 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_CONNECTION_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/view_manager/display_manager.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/server_view.h"
+#include "mojo/services/view_manager/server_view_delegate.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace service {
+
+class ViewManagerServiceImpl;
+
+// ConnectionManager manages the set of connections to the ViewManager (all the
+// ViewManagerServiceImpls) as well as providing the root of the hierarchy.
+class MOJO_VIEW_MANAGER_EXPORT ConnectionManager : public ServerViewDelegate {
+ public:
+ // Create when a ViewManagerServiceImpl is about to make a change. Ensures
+ // clients are notified correctly.
+ class ScopedChange {
+ public:
+ ScopedChange(ViewManagerServiceImpl* connection,
+ ConnectionManager* connection_manager,
+ bool is_delete_view);
+ ~ScopedChange();
+
+ ConnectionSpecificId connection_id() const { return connection_id_; }
+ bool is_delete_view() const { return is_delete_view_; }
+
+ // 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:
+ ConnectionManager* connection_manager_;
+ const ConnectionSpecificId connection_id_;
+ const bool is_delete_view_;
+
+ // See description of MarkConnectionAsMessaged/DidMessageConnection.
+ std::set<ConnectionSpecificId> message_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedChange);
+ };
+
+ ConnectionManager(ApplicationConnection* app_connection,
+ const Callback<void()>& native_viewport_closed_callback);
+ virtual ~ConnectionManager();
+
+ // Returns the id for the next ViewManagerServiceImpl.
+ ConnectionSpecificId GetAndAdvanceNextConnectionId();
+
+ 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,
+ InterfaceRequest<ServiceProvider> service_provider);
+
+ // See description of ViewManagerService::Embed() for details. This assumes
+ // |transport_view_id| is valid.
+ void Embed(ConnectionSpecificId creator_id,
+ const String& url,
+ Id transport_view_id,
+ InterfaceRequest<ServiceProvider> service_provider);
+
+ // Returns the connection by id.
+ ViewManagerServiceImpl* GetConnection(ConnectionSpecificId connection_id);
+
+ // Returns the View identified by |id|.
+ ServerView* GetView(const ViewId& id);
+
+ ServerView* root() { return root_.get(); }
+
+ bool IsProcessingChange() const { return current_change_ != NULL; }
+
+ bool is_processing_delete_view() const {
+ return current_change_ && current_change_->is_delete_view();
+ }
+
+ // 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;
+
+ // Returns the ViewManagerServiceImpl that has |id| as a root.
+ ViewManagerServiceImpl* GetConnectionWithRoot(const ViewId& id) {
+ return const_cast<ViewManagerServiceImpl*>(
+ const_cast<const ConnectionManager*>(this)->GetConnectionWithRoot(id));
+ }
+ const ViewManagerServiceImpl* GetConnectionWithRoot(const ViewId& id) const;
+
+ void DispatchViewInputEventToWindowManager(EventPtr event);
+
+ // These functions trivially delegate to all ViewManagerServiceImpls, which in
+ // term notify their clients.
+ void ProcessViewDestroyed(ServerView* view);
+ void ProcessViewBoundsChanged(const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds);
+ void ProcessWillChangeViewHierarchy(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent);
+ void ProcessViewHierarchyChanged(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent);
+ void ProcessViewReorder(const ServerView* view,
+ const ServerView* relative_view,
+ const OrderDirection direction);
+ void ProcessViewDeleted(const ViewId& view);
+
+ private:
+ 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 ViewId& root_id,
+ InterfaceRequest<ServiceProvider> service_provider);
+
+ // Overridden from ServerViewDelegate:
+ virtual void OnViewDestroyed(const ServerView* view) OVERRIDE;
+ virtual void OnWillChangeViewHierarchy(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) OVERRIDE;
+ virtual void OnViewHierarchyChanged(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) OVERRIDE;
+ virtual void OnViewBoundsChanged(const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE;
+ virtual void OnViewSurfaceIdChanged(const ServerView* view) OVERRIDE;
+ virtual void OnViewReordered(const ServerView* view,
+ const ServerView* relative,
+ OrderDirection direction) OVERRIDE;
+ virtual void OnWillChangeViewVisibility(const ServerView* view) OVERRIDE;
+
+ ApplicationConnection* app_connection_;
+
+ // ID to use for next ViewManagerServiceImpl.
+ ConnectionSpecificId next_connection_id_;
+
+ // Set of ViewManagerServiceImpls.
+ ConnectionMap connection_map_;
+
+ DisplayManager display_manager_;
+
+ scoped_ptr<ServerView> 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(ConnectionManager);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_CONNECTION_MANAGER_H_
diff --git a/mojo/services/view_manager/default_access_policy.cc b/mojo/services/view_manager/default_access_policy.cc
new file mode 100644
index 0000000..a2443c3
--- /dev/null
+++ b/mojo/services/view_manager/default_access_policy.cc
@@ -0,0 +1,106 @@
+// 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/default_access_policy.h"
+
+#include "mojo/services/view_manager/access_policy_delegate.h"
+#include "mojo/services/view_manager/server_view.h"
+
+namespace mojo {
+namespace service {
+
+DefaultAccessPolicy::DefaultAccessPolicy(ConnectionSpecificId connection_id,
+ AccessPolicyDelegate* delegate)
+ : connection_id_(connection_id),
+ delegate_(delegate) {
+}
+
+DefaultAccessPolicy::~DefaultAccessPolicy() {
+}
+
+bool DefaultAccessPolicy::CanRemoveViewFromParent(
+ const ServerView* view) const {
+ if (!WasCreatedByThisConnection(view))
+ return false; // Can only unparent views we created.
+
+ return IsViewInRoots(view->parent()) ||
+ WasCreatedByThisConnection(view->parent());
+}
+
+bool DefaultAccessPolicy::CanAddView(const ServerView* parent,
+ const ServerView* child) const {
+ return WasCreatedByThisConnection(child) &&
+ (IsViewInRoots(parent) ||
+ (WasCreatedByThisConnection(parent) &&
+ !delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(parent)));
+}
+
+bool DefaultAccessPolicy::CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const {
+ return WasCreatedByThisConnection(view) &&
+ WasCreatedByThisConnection(relative_view);
+}
+
+bool DefaultAccessPolicy::CanDeleteView(const ServerView* view) const {
+ return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::CanGetViewTree(const ServerView* view) const {
+ return WasCreatedByThisConnection(view) || IsViewInRoots(view);
+}
+
+bool DefaultAccessPolicy::CanDescendIntoViewForViewTree(
+ const ServerView* view) const {
+ return WasCreatedByThisConnection(view) &&
+ !delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view);
+}
+
+bool DefaultAccessPolicy::CanEmbed(const ServerView* view) const {
+ return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::CanChangeViewVisibility(
+ const ServerView* view) const {
+ return WasCreatedByThisConnection(view) || IsViewInRoots(view);
+}
+
+bool DefaultAccessPolicy::CanSetViewSurfaceId(const ServerView* view) const {
+ // Once a view embeds another app, the embedder app is no longer able to
+ // call SetViewSurfaceId() - this ability is transferred to the embedded app.
+ if (delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view))
+ return false;
+ return WasCreatedByThisConnection(view) || IsViewInRoots(view);
+}
+
+bool DefaultAccessPolicy::CanSetViewBounds(const ServerView* view) const {
+ return WasCreatedByThisConnection(view);
+}
+
+bool DefaultAccessPolicy::ShouldNotifyOnHierarchyChange(
+ const ServerView* view,
+ const ServerView** new_parent,
+ const ServerView** old_parent) const {
+ if (!WasCreatedByThisConnection(view))
+ return false;
+
+ if (*new_parent && !WasCreatedByThisConnection(*new_parent) &&
+ !IsViewInRoots(*new_parent)) {
+ *new_parent = NULL;
+ }
+
+ if (*old_parent && !WasCreatedByThisConnection(*old_parent) &&
+ !IsViewInRoots(*old_parent)) {
+ *old_parent = NULL;
+ }
+ return true;
+}
+
+bool DefaultAccessPolicy::IsViewInRoots(const ServerView* view) const {
+ return delegate_->GetRootsForAccessPolicy().count(
+ ViewIdToTransportId(view->id())) > 0;
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/default_access_policy.h b/mojo/services/view_manager/default_access_policy.h
new file mode 100644
index 0000000..197736a
--- /dev/null
+++ b/mojo/services/view_manager/default_access_policy.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_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "mojo/services/view_manager/access_policy.h"
+
+namespace mojo {
+namespace service {
+
+class AccessPolicyDelegate;
+
+// AccessPolicy for all connections, except the window manager.
+class DefaultAccessPolicy : public AccessPolicy {
+ public:
+ DefaultAccessPolicy(ConnectionSpecificId connection_id,
+ AccessPolicyDelegate* delegate);
+ virtual ~DefaultAccessPolicy();
+
+ // AccessPolicy:
+ virtual bool CanRemoveViewFromParent(const ServerView* view) const OVERRIDE;
+ virtual bool CanAddView(const ServerView* parent,
+ const ServerView* child) const OVERRIDE;
+ virtual bool CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const OVERRIDE;
+ virtual bool CanDeleteView(const ServerView* view) const OVERRIDE;
+ virtual bool CanGetViewTree(const ServerView* view) const OVERRIDE;
+ virtual bool CanDescendIntoViewForViewTree(
+ const ServerView* view) const OVERRIDE;
+ virtual bool CanEmbed(const ServerView* view) const OVERRIDE;
+ virtual bool CanChangeViewVisibility(const ServerView* view) const OVERRIDE;
+ virtual bool CanSetViewSurfaceId(const ServerView* view) const OVERRIDE;
+ virtual bool CanSetViewBounds(const ServerView* view) const OVERRIDE;
+ virtual bool ShouldNotifyOnHierarchyChange(
+ const ServerView* view,
+ const ServerView** new_parent,
+ const ServerView** old_parent) const OVERRIDE;
+
+ private:
+ bool IsViewInRoots(const ServerView* view) const;
+
+ template <typename T>
+ bool WasCreatedByThisConnection(const T* t) const {
+ return t->id().connection_id == connection_id_;
+ }
+
+ const ConnectionSpecificId connection_id_;
+ AccessPolicyDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultAccessPolicy);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_DEFAULT_ACCESS_POLICY_H_
diff --git a/mojo/services/view_manager/display_manager.cc b/mojo/services/view_manager/display_manager.cc
new file mode 100644
index 0000000..435951b
--- /dev/null
+++ b/mojo/services/view_manager/display_manager.cc
@@ -0,0 +1,172 @@
+// 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/display_manager.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
+#include "mojo/services/public/interfaces/gpu/gpu.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/quads.mojom.h"
+#include "mojo/services/view_manager/connection_manager.h"
+
+namespace mojo {
+namespace service {
+namespace {
+
+gfx::Rect ConvertRectToRoot(const ServerView* view, const gfx::Rect& bounds) {
+ gfx::Point origin(bounds.origin());
+ while (view->parent()) {
+ origin += view->bounds().OffsetFromOrigin();
+ view = view->parent();
+ if (!view->visible())
+ return gfx::Rect();
+ }
+ return gfx::Rect(origin, bounds.size());
+}
+
+void DrawViewTree(Pass* pass, const ServerView* view, gfx::Vector2d offset) {
+ if (!view->visible())
+ return;
+
+ gfx::Rect node_bounds = view->bounds() + offset;
+ std::vector<const ServerView*> children(view->GetChildren());
+ for (std::vector<const ServerView*>::reverse_iterator it = children.rbegin();
+ it != children.rend();
+ ++it) {
+ DrawViewTree(pass, *it, offset + view->bounds().OffsetFromOrigin());
+ }
+
+ cc::SurfaceId node_id = view->surface_id();
+
+ SurfaceQuadStatePtr surface_quad_state = SurfaceQuadState::New();
+ surface_quad_state->surface = SurfaceId::From(node_id);
+
+ gfx::Transform node_transform;
+ node_transform.Translate(node_bounds.x(), node_bounds.y());
+
+ QuadPtr surface_quad = Quad::New();
+ surface_quad->material = Material::MATERIAL_SURFACE_CONTENT;
+ surface_quad->rect = Rect::From(node_bounds);
+ surface_quad->opaque_rect = Rect::From(node_bounds);
+ surface_quad->visible_rect = Rect::From(node_bounds);
+ surface_quad->needs_blending = true;
+ surface_quad->shared_quad_state_index =
+ base::saturated_cast<int32_t>(pass->shared_quad_states.size());
+ surface_quad->surface_quad_state = surface_quad_state.Pass();
+
+ SharedQuadStatePtr sqs = CreateDefaultSQS(node_bounds.size());
+ sqs->content_to_target_transform = Transform::From(node_transform);
+
+ pass->quads.push_back(surface_quad.Pass());
+ pass->shared_quad_states.push_back(sqs.Pass());
+}
+
+} // namespace
+
+DisplayManager::DisplayManager(
+ ApplicationConnection* app_connection,
+ ConnectionManager* connection_manager,
+ const Callback<void()>& native_viewport_closed_callback)
+ : connection_manager_(connection_manager),
+ in_setup_(false),
+ size_(800, 600),
+ draw_timer_(false, false),
+ weak_factory_(this) {
+ app_connection->ConnectToService("mojo:mojo_native_viewport_service",
+ &native_viewport_);
+ native_viewport_.set_client(this);
+ native_viewport_->Create(
+ Size::From(size_),
+ base::Bind(&DisplayManager::OnCreatedNativeViewport,
+ weak_factory_.GetWeakPtr()));
+ native_viewport_->Show();
+ app_connection->ConnectToService("mojo:mojo_surfaces_service",
+ &surfaces_service_);
+ surfaces_service_->CreateSurfaceConnection(base::Bind(
+ &DisplayManager::OnSurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
+}
+
+DisplayManager::~DisplayManager() {
+}
+
+void DisplayManager::SchedulePaint(const ServerView* view,
+ const gfx::Rect& bounds) {
+ if (!view->visible())
+ return;
+ gfx::Rect root_relative_rect = ConvertRectToRoot(view, bounds);
+ if (root_relative_rect.IsEmpty())
+ return;
+ dirty_rect_.Union(root_relative_rect);
+ if (!draw_timer_.IsRunning()) {
+ draw_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta(),
+ base::Bind(&DisplayManager::Draw, base::Unretained(this)));
+ }
+}
+
+void DisplayManager::OnCreatedNativeViewport(uint64_t native_viewport_id) {
+}
+
+void DisplayManager::OnSurfaceConnectionCreated(SurfacePtr surface,
+ uint32_t id_namespace) {
+ surface_ = surface.Pass();
+ surface_.set_client(this);
+ surface_id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
+ Draw();
+}
+
+void DisplayManager::Draw() {
+ if (!surface_)
+ return;
+ if (surface_id_.is_null()) {
+ surface_id_ = surface_id_allocator_->GenerateId();
+ surface_->CreateSurface(SurfaceId::From(surface_id_), Size::From(size_));
+ }
+
+ PassPtr pass = CreateDefaultPass(1, gfx::Rect(size_));
+ pass->damage_rect = Rect::From(dirty_rect_);
+
+ DrawViewTree(pass.get(), connection_manager_->root(), gfx::Vector2d());
+
+ FramePtr frame = Frame::New();
+ frame->passes.push_back(pass.Pass());
+ frame->resources.resize(0u);
+ surface_->SubmitFrame(SurfaceId::From(surface_id_), frame.Pass());
+
+ native_viewport_->SubmittedFrame(SurfaceId::From(surface_id_));
+
+ dirty_rect_ = gfx::Rect();
+}
+
+void DisplayManager::OnDestroyed() {
+ native_viewport_closed_callback_.Run();
+}
+
+void DisplayManager::OnSizeChanged(SizePtr size) {
+ size_ = size.To<gfx::Size>();
+ connection_manager_->root()->SetBounds(gfx::Rect(size_));
+ if (surface_id_.is_null())
+ return;
+ surface_->DestroySurface(SurfaceId::From(surface_id_));
+ surface_id_ = cc::SurfaceId();
+ SchedulePaint(connection_manager_->root(), gfx::Rect(size_));
+}
+
+void DisplayManager::OnEvent(EventPtr event,
+ const mojo::Callback<void()>& callback) {
+ connection_manager_->DispatchViewInputEventToWindowManager(event.Pass());
+ callback.Run();
+}
+
+void DisplayManager::ReturnResources(Array<ReturnedResourcePtr> resources) {
+ DCHECK_EQ(0u, resources.size());
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/display_manager.h b/mojo/services/view_manager/display_manager.h
new file mode 100644
index 0000000..21009f3
--- /dev/null
+++ b/mojo/services/view_manager/display_manager.h
@@ -0,0 +1,88 @@
+// 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_DISPLAY_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
+#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+class SurfaceIdAllocator;
+}
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace service {
+
+class ConnectionManager;
+class ServerView;
+
+// DisplayManager binds the root node to an actual display.
+class MOJO_VIEW_MANAGER_EXPORT DisplayManager
+ : NON_EXPORTED_BASE(public NativeViewportClient),
+ NON_EXPORTED_BASE(public SurfaceClient) {
+ public:
+ DisplayManager(ApplicationConnection* app_connection,
+ ConnectionManager* connection_manager,
+ const Callback<void()>& native_viewport_closed_callback);
+ virtual ~DisplayManager();
+
+ // Schedules a paint for the specified region of the specified view.
+ void SchedulePaint(const ServerView* view, const gfx::Rect& bounds);
+
+ // See description above field for details.
+ bool in_setup() const { return in_setup_; }
+
+ private:
+ void OnCreatedNativeViewport(uint64_t native_viewport_id);
+ void OnSurfaceConnectionCreated(SurfacePtr surface, uint32_t id_namespace);
+ void Draw();
+
+ // NativeViewportClient implementation.
+ virtual void OnDestroyed() OVERRIDE;
+ virtual void OnSizeChanged(SizePtr size) OVERRIDE;
+ virtual void OnEvent(EventPtr event,
+ const mojo::Callback<void()>& callback) OVERRIDE;
+
+ // SurfaceClient implementation.
+ virtual void ReturnResources(Array<ReturnedResourcePtr> resources) OVERRIDE;
+
+ ConnectionManager* connection_manager_;
+
+ // Returns true if adding the root view's window to |window_tree_host_|.
+ bool in_setup_;
+
+ gfx::Size size_;
+ gfx::Rect dirty_rect_;
+ base::Timer draw_timer_;
+
+ SurfacesServicePtr surfaces_service_;
+ SurfacePtr surface_;
+ scoped_ptr<cc::SurfaceIdAllocator> surface_id_allocator_;
+ cc::SurfaceId surface_id_;
+ NativeViewportPtr native_viewport_;
+ Callback<void()> native_viewport_closed_callback_;
+ base::WeakPtrFactory<DisplayManager> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayManager);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_DISPLAY_MANAGER_H_
diff --git a/mojo/services/view_manager/ids.h b/mojo/services/view_manager/ids.h
new file mode 100644
index 0000000..0d1c486
--- /dev/null
+++ b/mojo/services/view_manager/ids.h
@@ -0,0 +1,63 @@
+// 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"
+
+namespace mojo {
+namespace service {
+
+// Connection id is used to indicate no connection. That is, no
+// ViewManagerServiceImpl ever gets this id.
+const ConnectionSpecificId kInvalidConnectionId = 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 view ids.
+struct 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;
+};
+
+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 ViewId RootViewId() {
+ return ViewId(kInvalidConnectionId, 1);
+}
+
+// Returns a ViewId that is reserved to indicate no view. That is, no view will
+// ever be created with this id.
+inline ViewId InvalidViewId() {
+ return ViewId(kInvalidConnectionId, 0);
+}
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_IDS_H_
diff --git a/mojo/services/view_manager/main.cc b/mojo/services/view_manager/main.cc
new file mode 100644
index 0000000..86a7ba7
--- /dev/null
+++ b/mojo/services/view_manager/main.cc
@@ -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.
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/services/view_manager/view_manager_init_service_context.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+class ViewManagerApp : public ApplicationDelegate,
+ public InterfaceFactory<ViewManagerInitService> {
+ public:
+ ViewManagerApp() {}
+ virtual ~ViewManagerApp() {}
+
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) OVERRIDE {
+ context_.ConfigureIncomingConnection(connection);
+ // TODO(sky): this needs some sort of authentication as well as making sure
+ // we only ever have one active at a time.
+ connection->AddService(this);
+ return true;
+ }
+
+ virtual void Create(
+ ApplicationConnection* connection,
+ InterfaceRequest<ViewManagerInitService> request) OVERRIDE {
+ BindToRequest(new ViewManagerInitServiceImpl(connection, &context_),
+ &request);
+ }
+
+ private:
+ ViewManagerInitServiceContext context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerApp);
+};
+
+} // namespace service
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new mojo::service::ViewManagerApp);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/view_manager/server_view.cc b/mojo/services/view_manager/server_view.cc
new file mode 100644
index 0000000..8f5a559
--- /dev/null
+++ b/mojo/services/view_manager/server_view.cc
@@ -0,0 +1,144 @@
+// 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/server_view.h"
+
+#include "mojo/services/view_manager/server_view_delegate.h"
+
+namespace mojo {
+namespace service {
+
+ServerView::ServerView(ServerViewDelegate* delegate, const ViewId& id)
+ : delegate_(delegate), id_(id), parent_(NULL), visible_(true) {
+ DCHECK(delegate); // Must provide a delegate.
+}
+
+ServerView::~ServerView() {
+ while (!children_.empty())
+ children_.front()->parent()->Remove(children_.front());
+
+ if (parent_)
+ parent_->Remove(this);
+
+ delegate_->OnViewDestroyed(this);
+}
+
+void ServerView::Add(ServerView* child) {
+ // We assume validation checks happened already.
+ DCHECK(child);
+ DCHECK(child != this);
+ DCHECK(!child->Contains(this));
+ if (child->parent() == this) {
+ if (children_.size() == 1)
+ return; // Already in the right position.
+ Reorder(child, children_.back(), ORDER_DIRECTION_ABOVE);
+ return;
+ }
+
+ const ServerView* old_parent = child->parent();
+ child->delegate_->OnWillChangeViewHierarchy(child, this, old_parent);
+ if (child->parent())
+ child->parent()->RemoveImpl(child);
+
+ child->parent_ = this;
+ children_.push_back(child);
+ child->delegate_->OnViewHierarchyChanged(child, this, old_parent);
+}
+
+void ServerView::Remove(ServerView* child) {
+ // We assume validation checks happened else where.
+ DCHECK(child);
+ DCHECK(child != this);
+ DCHECK(child->parent() == this);
+
+ child->delegate_->OnWillChangeViewHierarchy(child, NULL, this);
+ RemoveImpl(child);
+ child->delegate_->OnViewHierarchyChanged(child, NULL, this);
+}
+
+void ServerView::Reorder(ServerView* child,
+ ServerView* relative,
+ OrderDirection direction) {
+ // We assume validation checks happened else where.
+ DCHECK(child);
+ DCHECK(child->parent() == this);
+ DCHECK_GT(children_.size(), 1u);
+ children_.erase(std::find(children_.begin(), children_.end(), child));
+ Views::iterator i = std::find(children_.begin(), children_.end(), relative);
+ if (direction == ORDER_DIRECTION_ABOVE) {
+ DCHECK(i != children_.end());
+ children_.insert(++i, child);
+ } else if (direction == ORDER_DIRECTION_BELOW) {
+ DCHECK(i != children_.end());
+ children_.insert(i, child);
+ }
+ delegate_->OnViewReordered(this, relative, direction);
+}
+
+void ServerView::SetBounds(const gfx::Rect& bounds) {
+ if (bounds_ == bounds)
+ return;
+
+ const gfx::Rect old_bounds = bounds_;
+ bounds_ = bounds;
+ delegate_->OnViewBoundsChanged(this, old_bounds, bounds);
+}
+
+const ServerView* ServerView::GetRoot() const {
+ const ServerView* view = this;
+ while (view && view->parent())
+ view = view->parent();
+ return view;
+}
+
+std::vector<const ServerView*> ServerView::GetChildren() const {
+ std::vector<const ServerView*> children;
+ children.reserve(children_.size());
+ for (size_t i = 0; i < children_.size(); ++i)
+ children.push_back(children_[i]);
+ return children;
+}
+
+std::vector<ServerView*> ServerView::GetChildren() {
+ // TODO(sky): rename to children() and fix return type.
+ return children_;
+}
+
+bool ServerView::Contains(const ServerView* view) const {
+ for (const ServerView* parent = view; parent; parent = parent->parent_) {
+ if (parent == this)
+ return true;
+ }
+ return false;
+}
+
+void ServerView::SetVisible(bool value) {
+ if (visible_ == value)
+ return;
+
+ delegate_->OnWillChangeViewVisibility(this);
+ visible_ = value;
+}
+
+bool ServerView::IsDrawn(const ServerView* root) const {
+ if (!root->visible_)
+ return false;
+ const ServerView* view = this;
+ while (view && view != root && view->visible_)
+ view = view->parent_;
+ return view == root;
+}
+
+void ServerView::SetSurfaceId(cc::SurfaceId surface_id) {
+ surface_id_ = surface_id;
+ delegate_->OnViewSurfaceIdChanged(this);
+}
+
+void ServerView::RemoveImpl(ServerView* view) {
+ view->parent_ = NULL;
+ children_.erase(std::find(children_.begin(), children_.end(), view));
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/server_view.h b/mojo/services/view_manager/server_view.h
new file mode 100644
index 0000000..af7b37d
--- /dev/null
+++ b/mojo/services/view_manager/server_view.h
@@ -0,0 +1,91 @@
+// 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_SERVER_VIEW_H_
+#define MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
+
+#include <vector>
+
+#include "base/logging.h"
+#include "cc/surfaces/surface_id.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/gfx/geometry/rect.h"
+
+namespace mojo {
+namespace service {
+
+class ServerViewDelegate;
+
+// Server side representation of a view. Delegate is informed of interesting
+// events.
+//
+// It is assumed that all functions that mutate the tree have validated the
+// mutation is possible before hand. For example, Reorder() assumes the supplied
+// view is a child and not already in position.
+class MOJO_VIEW_MANAGER_EXPORT ServerView {
+ public:
+ ServerView(ServerViewDelegate* delegate, const ViewId& id);
+ virtual ~ServerView();
+
+ const ViewId& id() const { return id_; }
+
+ void Add(ServerView* child);
+ void Remove(ServerView* child);
+ void Reorder(ServerView* child,
+ ServerView* relative,
+ OrderDirection direction);
+
+ const gfx::Rect& bounds() const { return bounds_; }
+ void SetBounds(const gfx::Rect& bounds);
+
+ const ServerView* parent() const { return parent_; }
+ ServerView* parent() { return parent_; }
+
+ const ServerView* GetRoot() const;
+ ServerView* GetRoot() {
+ return const_cast<ServerView*>(
+ const_cast<const ServerView*>(this)->GetRoot());
+ }
+
+ std::vector<const ServerView*> GetChildren() const;
+ std::vector<ServerView*> GetChildren();
+
+ // Returns true if this contains |view| or is |view|.
+ bool Contains(const ServerView* view) const;
+
+ // Returns true if the window is visible. This does not consider visibility
+ // of any ancestors.
+ bool visible() const { return visible_; }
+ void SetVisible(bool value);
+
+ // Returns true if this view is attached to |root| and all ancestors are
+ // visible.
+ bool IsDrawn(const ServerView* root) const;
+
+ void SetSurfaceId(cc::SurfaceId surface_id);
+ const cc::SurfaceId surface_id() const { return surface_id_; }
+
+ private:
+ typedef std::vector<ServerView*> Views;
+
+ // Implementation of removing a view. Doesn't send any notification.
+ void RemoveImpl(ServerView* view);
+
+ ServerViewDelegate* delegate_;
+ const ViewId id_;
+ ServerView* parent_;
+ Views children_;
+ bool visible_;
+ gfx::Rect bounds_;
+ cc::SurfaceId surface_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerView);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_H_
diff --git a/mojo/services/view_manager/server_view_delegate.h b/mojo/services/view_manager/server_view_delegate.h
new file mode 100644
index 0000000..24e5f8f
--- /dev/null
+++ b/mojo/services/view_manager/server_view_delegate.h
@@ -0,0 +1,52 @@
+// 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_SERVER_VIEW_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
+
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+namespace service {
+
+class ServerView;
+
+class MOJO_VIEW_MANAGER_EXPORT ServerViewDelegate {
+ public:
+ // Invoked at the end of the View's destructor (after it has been removed from
+ // the hierarchy).
+ virtual void OnViewDestroyed(const ServerView* view) = 0;
+
+ virtual void OnWillChangeViewHierarchy(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) = 0;
+
+ virtual void OnViewHierarchyChanged(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent) = 0;
+
+ virtual void OnViewBoundsChanged(const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) = 0;
+
+ virtual void OnViewSurfaceIdChanged(const ServerView* view) = 0;
+
+ virtual void OnViewReordered(const ServerView* view,
+ const ServerView* relative,
+ OrderDirection direction) = 0;
+
+ virtual void OnWillChangeViewVisibility(const ServerView* view) = 0;
+
+ protected:
+ virtual ~ServerViewDelegate() {}
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_SERVER_VIEW_DELEGATE_H_
diff --git a/mojo/services/view_manager/test_change_tracker.cc b/mojo/services/view_manager/test_change_tracker.cc
new file mode 100644
index 0000000..bf95ac5
--- /dev/null
+++ b/mojo/services/view_manager/test_change_tracker.cc
@@ -0,0 +1,244 @@
+// 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 service {
+
+std::string ViewIdToString(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_DIRECTION_ABOVE ? "above" : "below";
+}
+
+std::string ChangeToDescription1(const Change& change) {
+ switch (change.type) {
+ case CHANGE_TYPE_EMBED:
+ return base::StringPrintf("OnEmbed creator=%s",
+ change.creator_url.data());
+
+ case CHANGE_TYPE_NODE_BOUNDS_CHANGED:
+ return base::StringPrintf(
+ "BoundsChanged view=%s old_bounds=%s new_bounds=%s",
+ ViewIdToString(change.view_id).c_str(),
+ RectToString(change.bounds).c_str(),
+ RectToString(change.bounds2).c_str());
+
+ case CHANGE_TYPE_NODE_HIERARCHY_CHANGED:
+ return base::StringPrintf(
+ "HierarchyChanged view=%s new_parent=%s old_parent=%s",
+ ViewIdToString(change.view_id).c_str(),
+ ViewIdToString(change.view_id2).c_str(),
+ ViewIdToString(change.view_id3).c_str());
+
+ case CHANGE_TYPE_NODE_REORDERED:
+ return base::StringPrintf("Reordered view=%s relative=%s direction=%s",
+ ViewIdToString(change.view_id).c_str(),
+ ViewIdToString(change.view_id2).c_str(),
+ DirectionToString(change.direction).c_str());
+
+ case CHANGE_TYPE_NODE_DELETED:
+ return base::StringPrintf("ViewDeleted view=%s",
+ ViewIdToString(change.view_id).c_str());
+
+ case CHANGE_TYPE_NODE_VISIBILITY_CHANGED:
+ return base::StringPrintf("VisibilityChanged view=%s visible=%s",
+ ViewIdToString(change.view_id).c_str(),
+ change.bool_value ? "true" : "false");
+
+ case CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED:
+ return base::StringPrintf("DrawnStateChanged view=%s drawn=%s",
+ ViewIdToString(change.view_id).c_str(),
+ change.bool_value ? "true" : "false");
+
+ case CHANGE_TYPE_INPUT_EVENT:
+ return base::StringPrintf("InputEvent view=%s event_action=%d",
+ ViewIdToString(change.view_id).c_str(),
+ change.event_action);
+
+ case CHANGE_TYPE_DELEGATE_EMBED:
+ return base::StringPrintf("DelegateEmbed url=%s",
+ change.embed_url.data());
+ }
+ 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 ChangeViewDescription(const std::vector<Change>& changes) {
+ if (changes.size() != 1)
+ return std::string();
+ std::vector<std::string> view_strings(changes[0].views.size());
+ for (size_t i = 0; i < changes[0].views.size(); ++i)
+ view_strings[i] = "[" + changes[0].views[i].ToString() + "]";
+ return JoinString(view_strings, ',');
+}
+
+TestView ViewDataToTestView(const ViewDataPtr& data) {
+ TestView view;
+ view.parent_id = data->parent_id;
+ view.view_id = data->view_id;
+ view.visible = data->visible;
+ view.drawn = data->drawn;
+ return view;
+}
+
+void ViewDatasToTestViews(const Array<ViewDataPtr>& data,
+ std::vector<TestView>* test_views) {
+ for (size_t i = 0; i < data.size(); ++i)
+ test_views->push_back(ViewDataToTestView(data[i]));
+}
+
+Change::Change()
+ : type(CHANGE_TYPE_EMBED),
+ connection_id(0),
+ view_id(0),
+ view_id2(0),
+ view_id3(0),
+ event_action(0),
+ direction(ORDER_DIRECTION_ABOVE),
+ bool_value(false) {
+}
+
+Change::~Change() {
+}
+
+TestChangeTracker::TestChangeTracker()
+ : delegate_(NULL) {
+}
+
+TestChangeTracker::~TestChangeTracker() {
+}
+
+void TestChangeTracker::OnEmbed(ConnectionSpecificId connection_id,
+ const String& creator_url,
+ ViewDataPtr root) {
+ Change change;
+ change.type = CHANGE_TYPE_EMBED;
+ change.connection_id = connection_id;
+ change.creator_url = creator_url;
+ change.views.push_back(ViewDataToTestView(root));
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewBoundsChanged(Id view_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
+ change.view_id = view_id;
+ change.bounds = old_bounds.To<gfx::Rect>();
+ change.bounds2 = new_bounds.To<gfx::Rect>();
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewHierarchyChanged(Id view_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Array<ViewDataPtr> views) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED;
+ change.view_id = view_id;
+ change.view_id2 = new_parent_id;
+ change.view_id3 = old_parent_id;
+ ViewDatasToTestViews(views, &change.views);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewReordered(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_REORDERED;
+ change.view_id = view_id;
+ change.view_id2 = relative_view_id;
+ change.direction = direction;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewDeleted(Id view_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_DELETED;
+ change.view_id = view_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewVisibilityChanged(Id view_id, bool visible) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_VISIBILITY_CHANGED;
+ change.view_id = view_id;
+ change.bool_value = visible;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewDrawnStateChanged(Id view_id, bool drawn) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED;
+ change.view_id = view_id;
+ change.bool_value = drawn;
+ 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::DelegateEmbed(const String& url) {
+ Change change;
+ change.type = CHANGE_TYPE_DELEGATE_EMBED;
+ change.embed_url = url;
+ AddChange(change);
+}
+
+void TestChangeTracker::AddChange(const Change& change) {
+ changes_.push_back(change);
+ if (delegate_)
+ delegate_->OnChangeAdded();
+}
+
+std::string TestView::ToString() const {
+ return base::StringPrintf("view=%s parent=%s",
+ ViewIdToString(view_id).c_str(),
+ ViewIdToString(parent_id).c_str());
+}
+
+std::string TestView::ToString2() const {
+ return base::StringPrintf("view=%s parent=%s visible=%s drawn=%s",
+ ViewIdToString(view_id).c_str(),
+ ViewIdToString(parent_id).c_str(),
+ visible ? "true" : "false",
+ drawn ? "true" : "false");
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/test_change_tracker.h b/mojo/services/view_manager/test_change_tracker.h
new file mode 100644
index 0000000..791c8ad
--- /dev/null
+++ b/mojo/services/view_manager/test_change_tracker.h
@@ -0,0 +1,133 @@
+// 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 service {
+
+enum ChangeType {
+ CHANGE_TYPE_EMBED,
+ // TODO(sky): NODE->VIEW.
+ CHANGE_TYPE_NODE_BOUNDS_CHANGED,
+ CHANGE_TYPE_NODE_HIERARCHY_CHANGED,
+ CHANGE_TYPE_NODE_REORDERED,
+ CHANGE_TYPE_NODE_VISIBILITY_CHANGED,
+ CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED,
+ CHANGE_TYPE_NODE_DELETED,
+ CHANGE_TYPE_INPUT_EVENT,
+ CHANGE_TYPE_DELEGATE_EMBED,
+};
+
+// TODO(sky): consider nuking and converting directly to ViewData.
+struct TestView {
+ // Returns a string description of this.
+ std::string ToString() const;
+
+ // Returns a string description that includes visible and drawn.
+ std::string ToString2() const;
+
+ Id parent_id;
+ Id view_id;
+ bool visible;
+ bool drawn;
+};
+
+// Tracks a call to ViewManagerClient. See the individual functions for the
+// fields that are used.
+struct Change {
+ Change();
+ ~Change();
+
+ ChangeType type;
+ ConnectionSpecificId connection_id;
+ std::vector<TestView> views;
+ Id view_id;
+ Id view_id2;
+ Id view_id3;
+ gfx::Rect bounds;
+ gfx::Rect bounds2;
+ int32 event_action;
+ String creator_url;
+ String embed_url;
+ OrderDirection direction;
+ bool bool_value;
+};
+
+// Converts Changes to string descriptions.
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes);
+
+// Returns a string description of |changes[0].views|. Returns an empty string
+// if change.size() != 1.
+std::string ChangeViewDescription(const std::vector<Change>& changes);
+
+// Converts ViewDatas to TestViews.
+void ViewDatasToTestViews(const Array<ViewDataPtr>& data,
+ std::vector<TestView>* test_views);
+
+// 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 OnEmbed(ConnectionSpecificId connection_id,
+ const String& creator_url,
+ ViewDataPtr root);
+ void OnViewBoundsChanged(Id view_id, RectPtr old_bounds, RectPtr new_bounds);
+ void OnViewHierarchyChanged(Id view_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Array<ViewDataPtr> views);
+ void OnViewReordered(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction);
+ void OnViewDeleted(Id view_id);
+ void OnViewVisibilityChanged(Id view_id, bool visible);
+ void OnViewDrawnStateChanged(Id view_id, bool drawn);
+ void OnViewInputEvent(Id view_id, EventPtr event);
+ void DelegateEmbed(const String& url);
+
+ private:
+ void AddChange(const Change& change);
+
+ Delegate* delegate_;
+ std::vector<Change> changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestChangeTracker);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
diff --git a/mojo/services/view_manager/view_manager_export.h b/mojo/services/view_manager/view_manager_export.h
new file mode 100644
index 0000000..29b0e62
--- /dev/null
+++ b/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/mojo/services/view_manager/view_manager_init_service_context.cc b/mojo/services/view_manager/view_manager_init_service_context.cc
new file mode 100644
index 0000000..79a7067
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_context.cc
@@ -0,0 +1,78 @@
+// 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_context.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "mojo/services/view_manager/connection_manager.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+ViewManagerInitServiceContext::ConnectParams::ConnectParams() {}
+
+ViewManagerInitServiceContext::ConnectParams::~ConnectParams() {}
+
+ViewManagerInitServiceContext::ViewManagerInitServiceContext()
+ : deleting_connection_(false) {
+}
+ViewManagerInitServiceContext::~ViewManagerInitServiceContext() {}
+
+void ViewManagerInitServiceContext::AddConnection(
+ ViewManagerInitServiceImpl* connection) {
+ DCHECK(std::find(connections_.begin(), connections_.end(), connection) ==
+ connections_.end());
+ connections_.push_back(connection);
+}
+
+void ViewManagerInitServiceContext::RemoveConnection(
+ ViewManagerInitServiceImpl* connection) {
+ if (!deleting_connection_) {
+ Connections::iterator it =
+ std::find(connections_.begin(), connections_.end(), connection);
+ DCHECK(it != connections_.end());
+ connections_.erase(it);
+ }
+
+ // This object is owned by an object that outlives the current thread's
+ // message loop, so we need to destroy the ConnectionManager earlier, as it
+ // may attempt to post tasks during its destruction.
+ if (connections_.empty())
+ connection_manager_.reset();
+}
+
+void ViewManagerInitServiceContext::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ if (!connection_manager_.get()) {
+ connection_manager_.reset(new ConnectionManager(
+ connection,
+ base::Bind(&ViewManagerInitServiceContext::OnNativeViewportDeleted,
+ base::Unretained(this))));
+ }
+}
+
+void ViewManagerInitServiceContext::Embed(
+ const String& url,
+ ServiceProviderPtr service_provider,
+ const Callback<void(bool)>& callback) {
+ connection_manager_->EmbedRoot(url, Get(&service_provider));
+ callback.Run(true);
+}
+
+void ViewManagerInitServiceContext::OnNativeViewportDeleted() {
+ // Prevent the connection from modifying the connection list during manual
+ // teardown.
+ base::AutoReset<bool> deleting_connection(&deleting_connection_, true);
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ delete *it;
+ }
+ connections_.clear();
+ connection_manager_.reset();
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_init_service_context.h b/mojo/services/view_manager/view_manager_init_service_context.h
new file mode 100644
index 0000000..5019c68
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_context.h
@@ -0,0 +1,64 @@
+// 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_CONTEXT_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_CONTEXT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+namespace service {
+
+class ConnectionManager;
+class ViewManagerInitServiceImpl;
+
+// State shared between all ViewManagerInitService impls.
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceContext {
+ public:
+ ViewManagerInitServiceContext();
+ virtual ~ViewManagerInitServiceContext();
+
+ void AddConnection(ViewManagerInitServiceImpl* connection);
+ void RemoveConnection(ViewManagerInitServiceImpl* connection);
+
+ void ConfigureIncomingConnection(ApplicationConnection* connection);
+
+ void Embed(const String& url,
+ ServiceProviderPtr service_provider,
+ const Callback<void(bool)>& callback);
+
+ ConnectionManager* connection_manager() { return connection_manager_.get(); }
+
+ private:
+ typedef std::vector<ViewManagerInitServiceImpl*> Connections;
+
+ struct ConnectParams {
+ ConnectParams();
+ ~ConnectParams();
+
+ std::string url;
+ InterfaceRequest<ServiceProvider> service_provider;
+ Callback<void(bool)> callback;
+ };
+
+ void OnNativeViewportDeleted();
+
+ scoped_ptr<ConnectionManager> connection_manager_;
+ Connections connections_;
+
+ bool deleting_connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceContext);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_CONTEXT_H_
diff --git a/mojo/services/view_manager/view_manager_init_service_impl.cc b/mojo/services/view_manager/view_manager_init_service_impl.cc
new file mode 100644
index 0000000..ab30718
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_impl.cc
@@ -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.
+
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+#include "base/bind.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_init_service_context.h"
+#include "mojo/services/view_manager/view_manager_service_impl.h"
+
+namespace mojo {
+namespace service {
+
+ViewManagerInitServiceImpl::ViewManagerInitServiceImpl(
+ ApplicationConnection* connection,
+ ViewManagerInitServiceContext* context)
+ : context_(context) {
+ context_->AddConnection(this);
+}
+
+ViewManagerInitServiceImpl::~ViewManagerInitServiceImpl() {
+ context_->RemoveConnection(this);
+}
+
+void ViewManagerInitServiceImpl::Embed(
+ const String& url,
+ ServiceProviderPtr service_provider,
+ const Callback<void(bool)>& callback) {
+ context_->Embed(url, service_provider.Pass(), callback);
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_init_service_impl.h b/mojo/services/view_manager/view_manager_init_service_impl.h
new file mode 100644
index 0000000..49cc628
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_init_service_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_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/connection_manager.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+
+namespace service {
+
+class ViewManagerInitServiceContext;
+
+#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:
+ ViewManagerInitServiceImpl(ApplicationConnection* connection,
+ ViewManagerInitServiceContext* context);
+ virtual ~ViewManagerInitServiceImpl();
+
+ private:
+ // ViewManagerInitService overrides:
+ virtual void Embed(const String& url,
+ ServiceProviderPtr service_provider,
+ const Callback<void(bool)>& callback) OVERRIDE;
+
+ ViewManagerInitServiceContext* context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceImpl);
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
diff --git a/mojo/services/view_manager/view_manager_service_impl.cc b/mojo/services/view_manager/view_manager_service_impl.cc
new file mode 100644
index 0000000..ad0df79
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_service_impl.cc
@@ -0,0 +1,564 @@
+// 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 "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/cpp/surfaces/surfaces_type_converters.h"
+#include "mojo/services/view_manager/connection_manager.h"
+#include "mojo/services/view_manager/default_access_policy.h"
+#include "mojo/services/view_manager/server_view.h"
+#include "mojo/services/view_manager/window_manager_access_policy.h"
+
+namespace mojo {
+namespace service {
+
+ViewManagerServiceImpl::ViewManagerServiceImpl(
+ ConnectionManager* connection_manager,
+ ConnectionSpecificId creator_id,
+ const std::string& creator_url,
+ const std::string& url,
+ const ViewId& root_id,
+ InterfaceRequest<ServiceProvider> service_provider)
+ : connection_manager_(connection_manager),
+ id_(connection_manager_->GetAndAdvanceNextConnectionId()),
+ url_(url),
+ creator_id_(creator_id),
+ creator_url_(creator_url),
+ delete_on_connection_error_(false),
+ service_provider_(service_provider.Pass()) {
+ CHECK(GetView(root_id));
+ roots_.insert(ViewIdToTransportId(root_id));
+ if (root_id == RootViewId())
+ access_policy_.reset(new WindowManagerAccessPolicy(id_, this));
+ else
+ access_policy_.reset(new DefaultAccessPolicy(id_, this));
+}
+
+ViewManagerServiceImpl::~ViewManagerServiceImpl() {
+ // Delete any views we created.
+ if (!view_map_.empty()) {
+ ConnectionManager::ScopedChange change(this, connection_manager_, true);
+ while (!view_map_.empty())
+ delete view_map_.begin()->second;
+ }
+
+ connection_manager_->RemoveConnection(this);
+}
+
+const ServerView* 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 connection_manager_->GetView(id);
+}
+
+bool ViewManagerServiceImpl::HasRoot(const ViewId& id) const {
+ return roots_.find(ViewIdToTransportId(id)) != roots_.end();
+}
+
+void ViewManagerServiceImpl::OnViewManagerServiceImplDestroyed(
+ ConnectionSpecificId id) {
+ if (creator_id_ == id)
+ creator_id_ = kInvalidConnectionId;
+}
+
+void ViewManagerServiceImpl::ProcessViewBoundsChanged(
+ const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ bool originated_change) {
+ if (originated_change || !IsViewKnown(view))
+ return;
+ client()->OnViewBoundsChanged(ViewIdToTransportId(view->id()),
+ Rect::From(old_bounds),
+ Rect::From(new_bounds));
+}
+
+void ViewManagerServiceImpl::ProcessWillChangeViewHierarchy(
+ const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ const bool old_drawn = view->IsDrawn(connection_manager_->root());
+ const bool new_drawn = view->visible() && new_parent &&
+ new_parent->IsDrawn(connection_manager_->root());
+ if (old_drawn == new_drawn)
+ return;
+
+ NotifyDrawnStateChanged(view, new_drawn);
+}
+
+void ViewManagerServiceImpl::ProcessViewHierarchyChanged(
+ const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent,
+ bool originated_change) {
+ if (originated_change && !IsViewKnown(view) && new_parent &&
+ IsViewKnown(new_parent)) {
+ std::vector<const ServerView*> unused;
+ GetUnknownViewsFrom(view, &unused);
+ }
+ if (originated_change || connection_manager_->is_processing_delete_view() ||
+ connection_manager_->DidConnectionMessageClient(id_)) {
+ return;
+ }
+
+ if (!access_policy_->ShouldNotifyOnHierarchyChange(
+ view, &new_parent, &old_parent)) {
+ return;
+ }
+ // Inform the client of any new views and update the set of views we know
+ // about.
+ std::vector<const ServerView*> to_send;
+ if (!IsViewKnown(view))
+ GetUnknownViewsFrom(view, &to_send);
+ const ViewId new_parent_id(new_parent ? new_parent->id() : ViewId());
+ const ViewId old_parent_id(old_parent ? old_parent->id() : ViewId());
+ client()->OnViewHierarchyChanged(ViewIdToTransportId(view->id()),
+ ViewIdToTransportId(new_parent_id),
+ ViewIdToTransportId(old_parent_id),
+ ViewsToViewDatas(to_send));
+ connection_manager_->OnConnectionMessagedClient(id_);
+}
+
+void ViewManagerServiceImpl::ProcessViewReorder(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction,
+ bool originated_change) {
+ if (originated_change || !IsViewKnown(view) || !IsViewKnown(relative_view))
+ return;
+
+ client()->OnViewReordered(ViewIdToTransportId(view->id()),
+ ViewIdToTransportId(relative_view->id()),
+ direction);
+}
+
+void ViewManagerServiceImpl::ProcessViewDeleted(const ViewId& view,
+ bool originated_change) {
+ view_map_.erase(view.view_id);
+
+ const bool in_known = known_views_.erase(ViewIdToTransportId(view)) > 0;
+ roots_.erase(ViewIdToTransportId(view));
+
+ if (originated_change)
+ return;
+
+ if (in_known) {
+ client()->OnViewDeleted(ViewIdToTransportId(view));
+ connection_manager_->OnConnectionMessagedClient(id_);
+ }
+}
+
+void ViewManagerServiceImpl::ProcessWillChangeViewVisibility(
+ const ServerView* view,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ if (IsViewKnown(view)) {
+ client()->OnViewVisibilityChanged(ViewIdToTransportId(view->id()),
+ !view->visible());
+ return;
+ }
+
+ bool view_target_drawn_state;
+ if (view->visible()) {
+ // View is being hidden, won't be drawn.
+ view_target_drawn_state = false;
+ } else {
+ // View is being shown. View will be drawn if its parent is drawn.
+ view_target_drawn_state =
+ view->parent() && view->parent()->IsDrawn(connection_manager_->root());
+ }
+
+ NotifyDrawnStateChanged(view, view_target_drawn_state);
+}
+
+void ViewManagerServiceImpl::OnConnectionError() {
+ if (delete_on_connection_error_)
+ delete this;
+}
+
+bool ViewManagerServiceImpl::IsViewKnown(const ServerView* view) const {
+ return known_views_.count(ViewIdToTransportId(view->id())) > 0;
+}
+
+bool ViewManagerServiceImpl::CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const {
+ if (!view || !relative_view)
+ return false;
+
+ if (!view->parent() || view->parent() != relative_view->parent())
+ return false;
+
+ if (!access_policy_->CanReorderView(view, relative_view, direction))
+ return false;
+
+ std::vector<const ServerView*> children = view->parent()->GetChildren();
+ const size_t child_i =
+ std::find(children.begin(), children.end(), view) - children.begin();
+ const size_t target_i =
+ std::find(children.begin(), children.end(), relative_view) -
+ children.begin();
+ if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
+ (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ViewManagerServiceImpl::DeleteViewImpl(ViewManagerServiceImpl* source,
+ ServerView* view) {
+ DCHECK(view);
+ DCHECK_EQ(view->id().connection_id, id_);
+ ConnectionManager::ScopedChange change(source, connection_manager_, true);
+ delete view;
+ return true;
+}
+
+void ViewManagerServiceImpl::GetUnknownViewsFrom(
+ const ServerView* view,
+ std::vector<const ServerView*>* views) {
+ if (IsViewKnown(view) || !access_policy_->CanGetViewTree(view))
+ return;
+ views->push_back(view);
+ known_views_.insert(ViewIdToTransportId(view->id()));
+ if (!access_policy_->CanDescendIntoViewForViewTree(view))
+ return;
+ std::vector<const ServerView*> children(view->GetChildren());
+ for (size_t i = 0 ; i < children.size(); ++i)
+ GetUnknownViewsFrom(children[i], views);
+}
+
+void ViewManagerServiceImpl::RemoveFromKnown(
+ const ServerView* view,
+ std::vector<ServerView*>* local_views) {
+ if (view->id().connection_id == id_) {
+ if (local_views)
+ local_views->push_back(GetView(view->id()));
+ return;
+ }
+ known_views_.erase(ViewIdToTransportId(view->id()));
+ std::vector<const ServerView*> children = view->GetChildren();
+ for (size_t i = 0; i < children.size(); ++i)
+ RemoveFromKnown(children[i], local_views);
+}
+
+void ViewManagerServiceImpl::RemoveRoot(const ViewId& view_id) {
+ const Id transport_view_id(ViewIdToTransportId(view_id));
+ CHECK(roots_.count(transport_view_id) > 0);
+
+ roots_.erase(transport_view_id);
+
+ // No need to do anything if we created the view.
+ if (view_id.connection_id == id_)
+ return;
+
+ client()->OnViewDeleted(transport_view_id);
+ connection_manager_->OnConnectionMessagedClient(id_);
+
+ // This connection no longer knows about the view. Unparent any views that
+ // were parented to views in the root.
+ std::vector<ServerView*> local_views;
+ RemoveFromKnown(GetView(view_id), &local_views);
+ for (size_t i = 0; i < local_views.size(); ++i)
+ local_views[i]->parent()->Remove(local_views[i]);
+}
+
+void ViewManagerServiceImpl::RemoveChildrenAsPartOfEmbed(
+ const ViewId& view_id) {
+ ServerView* view = GetView(view_id);
+ CHECK(view);
+ CHECK(view->id().connection_id == view_id.connection_id);
+ std::vector<ServerView*> children = view->GetChildren();
+ for (size_t i = 0; i < children.size(); ++i)
+ view->Remove(children[i]);
+}
+
+Array<ViewDataPtr> ViewManagerServiceImpl::ViewsToViewDatas(
+ const std::vector<const ServerView*>& views) {
+ Array<ViewDataPtr> array(views.size());
+ for (size_t i = 0; i < views.size(); ++i)
+ array[i] = ViewToViewData(views[i]).Pass();
+ return array.Pass();
+}
+
+ViewDataPtr ViewManagerServiceImpl::ViewToViewData(const ServerView* view) {
+ DCHECK(IsViewKnown(view));
+ const ServerView* parent = view->parent();
+ // 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 && !IsViewKnown(parent))
+ parent = NULL;
+ ViewDataPtr view_data(ViewData::New());
+ view_data->parent_id = ViewIdToTransportId(parent ? parent->id() : ViewId());
+ view_data->view_id = ViewIdToTransportId(view->id());
+ view_data->bounds = Rect::From(view->bounds());
+ view_data->visible = view->visible();
+ view_data->drawn = view->IsDrawn(connection_manager_->root());
+ return view_data.Pass();
+}
+
+void ViewManagerServiceImpl::GetViewTreeImpl(
+ const ServerView* view,
+ std::vector<const ServerView*>* views) const {
+ DCHECK(view);
+
+ if (!access_policy_->CanGetViewTree(view))
+ return;
+
+ views->push_back(view);
+
+ if (!access_policy_->CanDescendIntoViewForViewTree(view))
+ return;
+
+ std::vector<const ServerView*> children(view->GetChildren());
+ for (size_t i = 0 ; i < children.size(); ++i)
+ GetViewTreeImpl(children[i], views);
+}
+
+void ViewManagerServiceImpl::NotifyDrawnStateChanged(const ServerView* view,
+ bool new_drawn_value) {
+ // Even though we don't know about view, it may be an ancestor of one of our
+ // roots, in which case the change may effect our roots drawn state.
+ for (ViewIdSet::iterator i = roots_.begin(); i != roots_.end(); ++i) {
+ const ServerView* root = GetView(ViewIdFromTransportId(*i));
+ DCHECK(root);
+ if (view->Contains(root) &&
+ (new_drawn_value != root->IsDrawn(connection_manager_->root()))) {
+ client()->OnViewDrawnStateChanged(ViewIdToTransportId(root->id()),
+ new_drawn_value);
+ }
+ }
+}
+
+void ViewManagerServiceImpl::CreateView(
+ Id transport_view_id,
+ const Callback<void(ErrorCode)>& callback) {
+ const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+ ErrorCode error_code = ERROR_CODE_NONE;
+ if (view_id.connection_id != id_) {
+ error_code = ERROR_CODE_ILLEGAL_ARGUMENT;
+ } else if (view_map_.find(view_id.view_id) != view_map_.end()) {
+ error_code = ERROR_CODE_VALUE_IN_USE;
+ } else {
+ view_map_[view_id.view_id] = new ServerView(connection_manager_, view_id);
+ known_views_.insert(transport_view_id);
+ }
+ callback.Run(error_code);
+}
+
+void ViewManagerServiceImpl::DeleteView(
+ Id transport_view_id,
+ const Callback<void(bool)>& callback) {
+ ServerView* view = GetView(ViewIdFromTransportId(transport_view_id));
+ bool success = false;
+ if (view && access_policy_->CanDeleteView(view)) {
+ ViewManagerServiceImpl* connection =
+ connection_manager_->GetConnection(view->id().connection_id);
+ success = connection && connection->DeleteViewImpl(this, view);
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::AddView(
+ Id parent_id,
+ Id child_id,
+ const Callback<void(bool)>& callback) {
+ bool success = false;
+ ServerView* parent = GetView(ViewIdFromTransportId(parent_id));
+ ServerView* child = GetView(ViewIdFromTransportId(child_id));
+ if (parent && child && child->parent() != parent &&
+ !child->Contains(parent) && access_policy_->CanAddView(parent, child)) {
+ success = true;
+ ConnectionManager::ScopedChange change(this, connection_manager_, false);
+ parent->Add(child);
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::RemoveViewFromParent(
+ Id view_id,
+ const Callback<void(bool)>& callback) {
+ bool success = false;
+ ServerView* view = GetView(ViewIdFromTransportId(view_id));
+ if (view && view->parent() && access_policy_->CanRemoveViewFromParent(view)) {
+ success = true;
+ ConnectionManager::ScopedChange change(this, connection_manager_, false);
+ view->parent()->Remove(view);
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::ReorderView(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction,
+ const Callback<void(bool)>& callback) {
+ bool success = false;
+ ServerView* view = GetView(ViewIdFromTransportId(view_id));
+ ServerView* relative_view = GetView(ViewIdFromTransportId(relative_view_id));
+ if (CanReorderView(view, relative_view, direction)) {
+ success = true;
+ ConnectionManager::ScopedChange change(this, connection_manager_, false);
+ view->parent()->Reorder(view, relative_view, direction);
+ connection_manager_->ProcessViewReorder(view, relative_view, direction);
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::GetViewTree(
+ Id view_id,
+ const Callback<void(Array<ViewDataPtr>)>& callback) {
+ ServerView* view = GetView(ViewIdFromTransportId(view_id));
+ std::vector<const ServerView*> views;
+ if (view) {
+ GetViewTreeImpl(view, &views);
+ // TODO(sky): this should map in views that weren't none.
+ }
+ callback.Run(ViewsToViewDatas(views));
+}
+
+void ViewManagerServiceImpl::SetViewSurfaceId(
+ Id view_id,
+ SurfaceIdPtr surface_id,
+ const Callback<void(bool)>& callback) {
+ // TODO(sky): add coverage of not being able to set for random node.
+ ServerView* view = GetView(ViewIdFromTransportId(view_id));
+ if (!view || !access_policy_->CanSetViewSurfaceId(view)) {
+ callback.Run(false);
+ return;
+ }
+ view->SetSurfaceId(surface_id.To<cc::SurfaceId>());
+ callback.Run(true);
+}
+
+void ViewManagerServiceImpl::SetViewBounds(
+ Id view_id,
+ RectPtr bounds,
+ const Callback<void(bool)>& callback) {
+ ServerView* view = GetView(ViewIdFromTransportId(view_id));
+ const bool success = view && access_policy_->CanSetViewBounds(view);
+ if (success) {
+ ConnectionManager::ScopedChange change(this, connection_manager_, false);
+ view->SetBounds(bounds.To<gfx::Rect>());
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::SetViewVisibility(
+ Id transport_view_id,
+ bool visible,
+ const Callback<void(bool)>& callback) {
+ ServerView* view = GetView(ViewIdFromTransportId(transport_view_id));
+ if (!view || view->visible() == visible ||
+ !access_policy_->CanChangeViewVisibility(view)) {
+ callback.Run(false);
+ return;
+ }
+ {
+ ConnectionManager::ScopedChange change(this, connection_manager_, false);
+ view->SetVisible(visible);
+ }
+ callback.Run(true);
+}
+
+void ViewManagerServiceImpl::Embed(
+ const String& url,
+ Id transport_view_id,
+ ServiceProviderPtr service_provider,
+ const Callback<void(bool)>& callback) {
+ InterfaceRequest<ServiceProvider> spir;
+ spir.Bind(service_provider.PassMessagePipe());
+
+ if (ViewIdFromTransportId(transport_view_id) == InvalidViewId()) {
+ connection_manager_->EmbedRoot(url, spir.Pass());
+ callback.Run(true);
+ return;
+ }
+ const ServerView* view = GetView(ViewIdFromTransportId(transport_view_id));
+ if (!view || !access_policy_->CanEmbed(view)) {
+ callback.Run(false);
+ return;
+ }
+
+ // Only allow a node to be the root for one connection.
+ const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+ ViewManagerServiceImpl* existing_owner =
+ connection_manager_->GetConnectionWithRoot(view_id);
+
+ ConnectionManager::ScopedChange change(this, connection_manager_, true);
+ RemoveChildrenAsPartOfEmbed(view_id);
+ if (existing_owner) {
+ // Never message the originating connection.
+ connection_manager_->OnConnectionMessagedClient(id_);
+ existing_owner->RemoveRoot(view_id);
+ }
+ connection_manager_->Embed(id_, url, transport_view_id, spir.Pass());
+ callback.Run(true);
+}
+
+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));
+
+ // If another app is embedded at this view, we forward the input event to the
+ // embedded app, rather than the app that created the view.
+ ViewManagerServiceImpl* connection =
+ connection_manager_->GetConnectionWithRoot(view_id);
+ if (!connection)
+ connection = connection_manager_->GetConnection(view_id.connection_id);
+ if (connection) {
+ connection->client()->OnViewInputEvent(
+ transport_view_id,
+ event.Pass(),
+ base::Bind(&base::DoNothing));
+ }
+}
+
+void ViewManagerServiceImpl::OnConnectionEstablished() {
+ connection_manager_->AddConnection(this);
+
+ std::vector<const ServerView*> to_send;
+ for (ViewIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i)
+ GetUnknownViewsFrom(GetView(ViewIdFromTransportId(*i)), &to_send);
+
+ client()->OnEmbed(id_,
+ creator_url_,
+ ViewToViewData(to_send.front()),
+ service_provider_.Pass());
+}
+
+const base::hash_set<Id>&
+ViewManagerServiceImpl::GetRootsForAccessPolicy() const {
+ return roots_;
+}
+
+bool ViewManagerServiceImpl::IsViewKnownForAccessPolicy(
+ const ServerView* view) const {
+ return IsViewKnown(view);
+}
+
+bool ViewManagerServiceImpl::IsViewRootOfAnotherConnectionForAccessPolicy(
+ const ServerView* view) const {
+ ViewManagerServiceImpl* connection =
+ connection_manager_->GetConnectionWithRoot(view->id());
+ return connection && connection != this;
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_service_impl.h b/mojo/services/view_manager/view_manager_service_impl.h
new file mode 100644
index 0000000..0295119
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_service_impl.h
@@ -0,0 +1,240 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/access_policy_delegate.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+namespace service {
+
+class AccessPolicy;
+class ConnectionManager;
+class ServerView;
+
+#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 AccessPolicyDelegate {
+ public:
+ ViewManagerServiceImpl(ConnectionManager* connection_manager,
+ ConnectionSpecificId creator_id,
+ const std::string& creator_url,
+ const std::string& url,
+ const ViewId& root_id,
+ InterfaceRequest<ServiceProvider> service_provider);
+ 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 View with the specified id.
+ ServerView* GetView(const ViewId& id) {
+ return const_cast<ServerView*>(
+ const_cast<const ViewManagerServiceImpl*>(this)->GetView(id));
+ }
+ const ServerView* GetView(const ViewId& id) const;
+
+ // Returns true if this has |id| as a root.
+ bool HasRoot(const ViewId& id) const;
+
+ // 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 ProcessViewBoundsChanged(const ServerView* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ bool originated_change);
+ void ProcessWillChangeViewHierarchy(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent,
+ bool originated_change);
+ void ProcessViewHierarchyChanged(const ServerView* view,
+ const ServerView* new_parent,
+ const ServerView* old_parent,
+ bool originated_change);
+ void ProcessViewReorder(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction,
+ bool originated_change);
+ void ProcessViewDeleted(const ViewId& view, bool originated_change);
+ void ProcessWillChangeViewVisibility(const ServerView* view,
+ bool originated_change);
+
+ // TODO(sky): move this to private section (currently can't because of
+ // bindings).
+ // InterfaceImp overrides:
+ virtual void OnConnectionError() override;
+
+ private:
+ typedef std::map<ConnectionSpecificId, ServerView*> ViewMap;
+ typedef base::hash_set<Id> ViewIdSet;
+
+ bool IsViewKnown(const ServerView* view) const;
+
+ // These functions return true if the corresponding mojom function is allowed
+ // for this connection.
+ bool CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const;
+
+ // Deletes a view owned by this connection. Returns true on success. |source|
+ // is the connection that originated the change.
+ bool DeleteViewImpl(ViewManagerServiceImpl* source, ServerView* view);
+
+ // If |view| is known (in |known_views_|) does nothing. Otherwise adds |view|
+ // to |views|, marks |view| as known and recurses.
+ void GetUnknownViewsFrom(const ServerView* view,
+ std::vector<const ServerView*>* views);
+
+ // Removes |view| and all its descendants from |known_views_|. This does not
+ // recurse through views that were created by this connection. All views owned
+ // by this connection are added to |local_views|.
+ void RemoveFromKnown(const ServerView* view,
+ std::vector<ServerView*>* local_views);
+
+ // Removes |view_id| from the set of roots this connection knows about.
+ void RemoveRoot(const ViewId& view_id);
+
+ void RemoveChildrenAsPartOfEmbed(const ViewId& view_id);
+
+ // Converts View(s) to ViewData(s) for transport. This assumes all the views
+ // are valid for the client. The parent of views the client is not allowed to
+ // see are set to NULL (in the returned ViewData(s)).
+ Array<ViewDataPtr> ViewsToViewDatas(
+ const std::vector<const ServerView*>& views);
+ ViewDataPtr ViewToViewData(const ServerView* view);
+
+ // Implementation of GetViewTree(). Adds |view| to |views| and recurses if
+ // CanDescendIntoViewForViewTree() returns true.
+ void GetViewTreeImpl(const ServerView* view,
+ std::vector<const ServerView*>* views) const;
+
+ // Notify the client if the drawn state of any of the roots changes.
+ // |view| is the view that is changing to the drawn state |new_drawn_value|.
+ void NotifyDrawnStateChanged(const ServerView* view, bool new_drawn_value);
+
+ // ViewManagerService:
+ virtual void CreateView(Id transport_view_id,
+ const Callback<void(ErrorCode)>& callback) OVERRIDE;
+ virtual void DeleteView(Id transport_view_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void AddView(Id parent_id,
+ Id child_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void RemoveViewFromParent(
+ Id view_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void ReorderView(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void GetViewTree(
+ Id view_id,
+ const Callback<void(Array<ViewDataPtr>)>& callback) OVERRIDE;
+ virtual void SetViewSurfaceId(Id view_id,
+ SurfaceIdPtr surface_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void SetViewBounds(Id view_id,
+ RectPtr bounds,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void SetViewVisibility(Id view_id,
+ bool visible,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void Embed(const String& url,
+ Id view_id,
+ ServiceProviderPtr service_provider,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void DispatchOnViewInputEvent(Id view_id, EventPtr event) OVERRIDE;
+
+ // InterfaceImpl:
+ virtual void OnConnectionEstablished() override;
+
+ // AccessPolicyDelegate:
+ virtual const base::hash_set<Id>& GetRootsForAccessPolicy() const OVERRIDE;
+ virtual bool IsViewKnownForAccessPolicy(
+ const ServerView* view) const OVERRIDE;
+ virtual bool IsViewRootOfAnotherConnectionForAccessPolicy(
+ const ServerView* view) const OVERRIDE;
+
+ ConnectionManager* connection_manager_;
+
+ // Id of this connection as assigned by ConnectionManager.
+ 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_;
+
+ scoped_ptr<AccessPolicy> access_policy_;
+
+ // The views and views created by this connection. This connection owns these
+ // objects.
+ ViewMap view_map_;
+
+ // The set of views that has been communicated to the client.
+ ViewIdSet known_views_;
+
+ // Set of root views from other connections. More specifically any time
+ // Embed() is invoked the id of the view is added to this set for the child
+ // connection. The connection Embed() was invoked on (the parent) doesn't
+ // directly track which connections are attached to which of its views. That
+ // information can be found by looking through the |roots_| of all
+ // connections.
+ ViewIdSet roots_;
+
+ // See description above setter.
+ bool delete_on_connection_error_;
+
+ InterfaceRequest<ServiceProvider> service_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceImpl);
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
diff --git a/mojo/services/view_manager/view_manager_unittest.cc b/mojo/services/view_manager/view_manager_unittest.cc
new file mode 100644
index 0000000..cc972d4
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_unittest.cc
@@ -0,0 +1,1434 @@
+// 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/application_manager/application_manager.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.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"
+
+#if defined(OS_WIN)
+#include "ui/gfx/win/window_impl.h"
+#endif
+
+namespace mojo {
+namespace service {
+
+namespace {
+
+const char kTestServiceURL[] = "mojo:test_url";
+const char kTestServiceURL2[] = "mojo:test_url2";
+
+// 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),
+ main_loop_(NULL),
+ view_manager_(NULL),
+ quit_count_(0),
+ router_(NULL) {
+ SetInstance(this);
+ }
+
+ virtual ~ViewManagerProxy() {
+ }
+
+ // Returns true if in an initial state. If this returns false it means the
+ // last test didn't clean up properly, or most likely didn't invoke
+ // WaitForInstance() when it needed to.
+ static bool IsInInitialState() { return instance_ == NULL; }
+
+ // 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();
+ }
+
+ void ClearChanges() {
+ changes_.clear();
+ tracker_->changes()->clear();
+ }
+
+ void CopyChangesFromTracker() {
+ std::vector<Change> changes;
+ tracker_->changes()->swap(changes);
+ changes_.swap(changes);
+ }
+
+ // The following functions are cover methods for ViewManagerService. They
+ // block until the result is received.
+ bool CreateView(Id view_id) {
+ changes_.clear();
+ ErrorCode result = ERROR_CODE_NONE;
+ view_manager_->CreateView(
+ view_id,
+ base::Bind(&ViewManagerProxy::GotResultWithErrorCode,
+ base::Unretained(this),
+ &result));
+ RunMainLoop();
+ return result == ERROR_CODE_NONE;
+ }
+ ErrorCode CreateViewWithErrorCode(Id view_id) {
+ changes_.clear();
+ ErrorCode result = ERROR_CODE_NONE;
+ view_manager_->CreateView(
+ view_id,
+ base::Bind(&ViewManagerProxy::GotResultWithErrorCode,
+ base::Unretained(this),
+ &result));
+ RunMainLoop();
+ return result;
+ }
+ bool AddView(Id parent, Id child) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->AddView(parent, child,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool RemoveViewFromParent(Id view_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->RemoveViewFromParent(
+ view_id,
+ base::Bind(
+ &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool ReorderView(Id view_id, Id relative_view_id, OrderDirection direction) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->ReorderView(
+ view_id,
+ relative_view_id,
+ direction,
+ base::Bind(
+ &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ void GetViewTree(Id view_id, std::vector<TestView>* views) {
+ changes_.clear();
+ view_manager_->GetViewTree(
+ view_id,
+ base::Bind(
+ &ViewManagerProxy::GotViewTree, base::Unretained(this), views));
+ RunMainLoop();
+ }
+ bool Embed(const Id view_id, const char* url) {
+ changes_.clear();
+ base::AutoReset<bool> auto_reset(&in_embed_, true);
+ bool result = false;
+ ServiceProviderPtr services;
+ view_manager_->Embed(
+ url,
+ view_id,
+ services.Pass(),
+ 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 SetViewBounds(Id view_id, const gfx::Rect& bounds) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->SetViewBounds(
+ view_id,
+ Rect::From(bounds),
+ base::Bind(
+ &ViewManagerProxy::GotResult, base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool SetViewVisibility(Id view_id, bool visible) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->SetViewVisibility(
+ view_id,
+ visible,
+ 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();
+ }
+
+ 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 GotResultWithErrorCode(ErrorCode* error_code_cache,
+ ErrorCode error_code) {
+ *error_code_cache = error_code;
+ DCHECK(main_run_loop_);
+ main_run_loop_->Quit();
+ }
+
+ void GotViewTree(std::vector<TestView>* views, Array<ViewDataPtr> results) {
+ ViewDatasToTestViews(results, views);
+ 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_);
+ }
+
+ // InterfaceImpl:
+ virtual void OnConnectionEstablished() OVERRIDE {
+ connection_.set_router(internal_state()->router());
+ connection_.set_view_manager(client());
+ }
+
+ // ViewManagerClient:
+ virtual void OnEmbed(
+ ConnectionSpecificId connection_id,
+ const String& creator_url,
+ ViewDataPtr root,
+ InterfaceRequest<ServiceProvider> services) OVERRIDE {
+ tracker_.OnEmbed(connection_id, creator_url, root.Pass());
+ }
+ virtual void OnViewBoundsChanged(Id view_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) OVERRIDE {
+ tracker_.OnViewBoundsChanged(view_id, old_bounds.Pass(), new_bounds.Pass());
+ }
+ virtual void OnViewHierarchyChanged(Id view,
+ Id new_parent,
+ Id old_parent,
+ Array<ViewDataPtr> views) OVERRIDE {
+ tracker_.OnViewHierarchyChanged(view, new_parent, old_parent, views.Pass());
+ }
+ virtual void OnViewReordered(Id view_id,
+ Id relative_view_id,
+ OrderDirection direction) OVERRIDE {
+ tracker_.OnViewReordered(view_id, relative_view_id, direction);
+ }
+ virtual void OnViewDeleted(Id view) OVERRIDE { tracker_.OnViewDeleted(view); }
+ virtual void OnViewVisibilityChanged(uint32_t view, bool visible) OVERRIDE {
+ tracker_.OnViewVisibilityChanged(view, visible);
+ }
+ virtual void OnViewDrawnStateChanged(uint32_t view, bool drawn) OVERRIDE {
+ tracker_.OnViewDrawnStateChanged(view, drawn);
+ }
+ virtual void OnViewInputEvent(Id view_id,
+ EventPtr event,
+ const Callback<void()>& callback) OVERRIDE {
+ tracker_.OnViewInputEvent(view_id, event.Pass());
+ }
+ virtual void Embed(
+ const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) OVERRIDE {
+ tracker_.DelegateEmbed(url);
+ }
+ virtual void DispatchOnViewInputEvent(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 EmbedApplicationLoader : public ApplicationLoader,
+ ApplicationDelegate,
+ public InterfaceFactory<ViewManagerClient> {
+ public:
+ EmbedApplicationLoader() {}
+ virtual ~EmbedApplicationLoader() {}
+
+ // ApplicationLoader implementation:
+ virtual void Load(ApplicationManager* manager,
+ const GURL& url,
+ scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
+ ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+ if (!shell_handle.is_valid())
+ return;
+ scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
+ shell_handle.Pass()));
+ apps_.push_back(app.release());
+ }
+ virtual void OnApplicationError(ApplicationManager* manager,
+ const GURL& url) OVERRIDE {}
+
+ // ApplicationDelegate implementation:
+ virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
+ OVERRIDE {
+ connection->AddService(this);
+ return true;
+ }
+
+ // InterfaceFactory<ViewManagerClient> implementation:
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<ViewManagerClient> request) OVERRIDE {
+ BindToRequest(new TestViewManagerClientConnection, &request);
+ }
+
+ private:
+ ScopedVector<ApplicationImpl> apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbedApplicationLoader);
+};
+
+// 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 Embed(). |result| is the result of the
+// Embed() call and |run_loop| the nested RunLoop.
+void EmbedCallback(bool* result_cache, base::RunLoop* run_loop, bool result) {
+ *result_cache = result;
+ run_loop->Quit();
+}
+
+// Embed from an application that does not yet have a view manager connection.
+// Blocks until result is determined.
+bool InitEmbed(ViewManagerInitService* view_manager_init,
+ const std::string& url,
+ size_t number_of_calls) {
+ bool result = false;
+ base::RunLoop run_loop;
+ for (size_t i = 0; i < number_of_calls; ++i) {
+ ServiceProviderPtr sp;
+ view_manager_init->Embed(url, sp.Pass(),
+ base::Bind(&EmbedCallback, &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),
+ connection3_(NULL) {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(ViewManagerProxy::IsInInitialState());
+ test_helper_.Init();
+
+#if defined(OS_WIN)
+ // As we unload the wndproc of window classes we need to be sure to
+ // unregister them.
+ gfx::WindowImpl::UnregisterClassesAtExit();
+#endif
+
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(new EmbedApplicationLoader()),
+ GURL(kTestServiceURL));
+
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(new EmbedApplicationLoader()),
+ GURL(kTestServiceURL2));
+
+ test_helper_.application_manager()->ConnectToService(
+ GURL("mojo:mojo_view_manager"), &view_manager_init_);
+ ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 1));
+
+ connection_ = ViewManagerProxy::WaitForInstance();
+ ASSERT_TRUE(connection_ != NULL);
+ connection_->DoRunLoopUntilChangesCount(1);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (connection3_)
+ connection3_->Destroy();
+ if (connection2_)
+ connection2_->Destroy();
+ if (connection_)
+ connection_->Destroy();
+ }
+
+ protected:
+ void EstablishSecondConnectionWithRoot(Id root_id) {
+ ASSERT_TRUE(connection_->Embed(root_id, kTestServiceURL));
+ 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_view) {
+ if (create_initial_view)
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishSecondConnectionWithRoot(BuildViewId(1, 1)));
+ const std::vector<Change>& changes(connection2_->changes());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("OnEmbed creator=mojo:test_url",
+ ChangesToDescription1(changes)[0]);
+ if (create_initial_view)
+ EXPECT_EQ("[view=1,1 parent=null]", ChangeViewDescription(changes));
+ }
+
+ void EstablishThirdConnection(ViewManagerProxy* owner, Id root_id) {
+ ASSERT_TRUE(connection3_ == NULL);
+ ASSERT_TRUE(owner->Embed(root_id, kTestServiceURL2));
+ connection3_ = ViewManagerProxy::WaitForInstance();
+ ASSERT_TRUE(connection3_ != NULL);
+ connection3_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection3_->changes().size());
+ EXPECT_EQ("OnEmbed creator=mojo:test_url",
+ ChangesToDescription1(connection3_->changes())[0]);
+ }
+
+ void DestroySecondConnection() {
+ connection2_->Destroy();
+ connection2_ = NULL;
+ }
+
+ base::ShadowingAtExitManager at_exit_;
+ shell::ShellTestHelper test_helper_;
+
+ ViewManagerInitServicePtr view_manager_init_;
+
+ // NOTE: this connection is the root. As such, it has special permissions.
+ ViewManagerProxy* connection_;
+ ViewManagerProxy* connection2_;
+ ViewManagerProxy* connection3_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+TEST_F(ViewManagerTest, SecondEmbedRoot_InitService) {
+ ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 1));
+ connection_->DoRunLoopUntilChangesCount(1);
+ EXPECT_EQ(kTestServiceURL, connection_->changes()[0].embed_url);
+}
+
+TEST_F(ViewManagerTest, SecondEmbedRoot_Service) {
+ ASSERT_TRUE(connection_->Embed(BuildViewId(0, 0), kTestServiceURL));
+ connection_->DoRunLoopUntilChangesCount(1);
+ EXPECT_EQ(kTestServiceURL, connection_->changes()[0].embed_url);
+}
+
+TEST_F(ViewManagerTest, MultipleEmbedRootsBeforeWTHReady) {
+ ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 2));
+ connection_->DoRunLoopUntilChangesCount(2);
+ EXPECT_EQ(kTestServiceURL, connection_->changes()[0].embed_url);
+ EXPECT_EQ(kTestServiceURL, connection_->changes()[1].embed_url);
+}
+
+// Verifies client gets a valid id.
+// http://crbug.com/396492
+TEST_F(ViewManagerTest, DISABLED_ValidId) {
+ // TODO(beng): this should really have the URL of the application that
+ // connected to ViewManagerInit.
+ EXPECT_EQ("OnEmbed 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);
+}
+
+// Verifies two clients/connections get different ids.
+TEST_F(ViewManagerTest, TwoClientsGetDifferentConnectionIds) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ EXPECT_EQ("OnEmbed creator=mojo:test_url",
+ ChangesToDescription1(connection2_->changes())[0]);
+
+ // It isn't strictly 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);
+}
+
+// Verifies when Embed() is invoked any child views are removed.
+TEST_F(ViewManagerTest, ViewsRemovedWhenEmbedding) {
+ // Two views 1 and 2. 2 is parented to 1.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+ EXPECT_EQ("[view=1,1 parent=null]",
+ ChangeViewDescription(connection2_->changes()));
+
+ // Embed() removed view 2.
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(1, 2), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=1,2 parent=null", views[0].ToString());
+ }
+
+ // |connection2_| should not see view 2.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+ }
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(1, 2), &views);
+ EXPECT_TRUE(views.empty());
+ }
+
+ // Views 3 and 4 in connection 2.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 4)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(2, 3), BuildViewId(2, 4)));
+
+ // Connection 3 rooted at 2.
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishThirdConnection(connection2_, BuildViewId(2, 3)));
+
+ // View 4 should no longer have a parent.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(2, 3), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=2,3 parent=null", views[0].ToString());
+
+ views.clear();
+ connection2_->GetViewTree(BuildViewId(2, 4), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=2,4 parent=null", views[0].ToString());
+ }
+
+ // And view 4 should not be visible to connection 3.
+ {
+ std::vector<TestView> views;
+ connection3_->GetViewTree(BuildViewId(2, 3), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=2,3 parent=null", views[0].ToString());
+ }
+}
+
+// Verifies once Embed() has been invoked the parent connection can't see any
+// children.
+TEST_F(ViewManagerTest, CantAccessChildrenOfEmbeddedView) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishThirdConnection(connection2_, BuildViewId(2, 2)));
+
+ ASSERT_TRUE(connection3_->CreateView(BuildViewId(3, 3)));
+ ASSERT_TRUE(connection3_->AddView(BuildViewId(2, 2), BuildViewId(3, 3)));
+
+ // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
+ // different connection.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(2, 2), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=2,2 parent=1,1", views[0].ToString());
+ }
+
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(3, 3), &views);
+ EXPECT_TRUE(views.empty());
+ }
+
+ // Connection 2 shouldn't be able to get view 3 at all.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(3, 3), &views);
+ EXPECT_TRUE(views.empty());
+ }
+
+ // Connection 1 should be able to see it all (its the root).
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(3u, views.size());
+ EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+ EXPECT_EQ("view=2,2 parent=1,1", views[1].ToString());
+ EXPECT_EQ("view=3,3 parent=2,2", views[2].ToString());
+ }
+}
+
+// Verifies once Embed() has been invoked the parent can't mutate the children.
+TEST_F(ViewManagerTest, CantModifyChildrenOfEmbeddedView) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishThirdConnection(connection2_, BuildViewId(2, 2)));
+
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+ // Connection 2 shouldn't be able to add anything to the view anymore.
+ ASSERT_FALSE(connection2_->AddView(BuildViewId(2, 2), BuildViewId(2, 3)));
+
+ // Create view 3 in connection 3 and add it to view 3.
+ ASSERT_TRUE(connection3_->CreateView(BuildViewId(3, 3)));
+ ASSERT_TRUE(connection3_->AddView(BuildViewId(2, 2), BuildViewId(3, 3)));
+
+ // Connection 2 shouldn't be able to remove view 3.
+ ASSERT_FALSE(connection2_->RemoveViewFromParent(BuildViewId(3, 3)));
+}
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerTest, CreateView) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ // Can't create a view with the same id.
+ ASSERT_EQ(ERROR_CODE_VALUE_IN_USE,
+ connection_->CreateViewWithErrorCode(BuildViewId(1, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ // Can't create a view with a bogus connection id.
+ EXPECT_EQ(ERROR_CODE_ILLEGAL_ARGUMENT,
+ connection_->CreateViewWithErrorCode(BuildViewId(2, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+}
+
+// Verifies AddView fails when view is already in position.
+TEST_F(ViewManagerTest, AddViewWithNoChange) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 2.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 3)));
+
+ // Try again, this should fail.
+ EXPECT_FALSE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 3)));
+}
+
+// Verifies AddView fails when view is already in position.
+TEST_F(ViewManagerTest, AddAncestorFails) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 2.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 3)));
+
+ // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
+ EXPECT_FALSE(connection_->AddView(BuildViewId(1, 3), BuildViewId(1, 2)));
+}
+
+// Verifies adding to root sends right notifications.
+TEST_F(ViewManagerTest, AddToRoot) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 21)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 21.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 21), BuildViewId(1, 3)));
+
+ // Make 21 a child of 1.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 21)));
+
+ // Connection 2 should not be told anything (because the view is from a
+ // different connection). Create a view to ensure we got a response from
+ // the server.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 100)));
+ connection2_->CopyChangesFromTracker();
+ EXPECT_TRUE(connection2_->changes().empty());
+}
+
+// Verifies HierarchyChanged is correctly sent for various adds/removes.
+TEST_F(ViewManagerTest, ViewHierarchyChangedViews) {
+ // 1,2->1,11.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 2), BuildViewId(1, 11)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // 1,1->1,2->1,11
+ {
+ // Client 2 should not get anything (1,2 is from another connection).
+ connection2_->ClearChanges();
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 100)));
+ connection2_->CopyChangesFromTracker();
+ EXPECT_TRUE(connection2_->changes().empty());
+ }
+
+ // 0,1->1,1->1,2->1,11.
+ {
+ // Client 2 is now connected to the root, so it should have gotten a drawn
+ // notification.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,1 drawn=true",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+
+ // 1,1->1,2->1,11.
+ {
+ // Client 2 is no longer connected to the root, should get drawn state
+ // changed.
+ ASSERT_TRUE(connection_->RemoveViewFromParent(BuildViewId(1, 1)));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,1 drawn=false",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+
+ // 1,1->1,2->1,11->1,111.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 111)));
+ {
+ connection2_->ClearChanges();
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 11), BuildViewId(1, 111)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 103)));
+ connection2_->CopyChangesFromTracker();
+ EXPECT_TRUE(connection2_->changes().empty());
+ }
+
+ // 0,1->1,1->1,2->1,11->1,111
+ {
+ connection2_->ClearChanges();
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,1 drawn=true",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+}
+
+TEST_F(ViewManagerTest, ViewHierarchyChangedAddingKnownToUnknown) {
+ // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
+ // parent).
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 21)));
+
+ // Set up the hierarchy.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 11)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(2, 2), BuildViewId(2, 21)));
+
+ // Remove 11, should result in a hierarchy change for the root.
+ {
+ connection_->ClearChanges();
+ ASSERT_TRUE(connection2_->RemoveViewFromParent(BuildViewId(2, 11)));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("HierarchyChanged view=2,11 new_parent=null old_parent=1,1",
+ changes[0]);
+ }
+
+ // Add 2 to 1.
+ {
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ(
+ "[view=2,2 parent=1,1],"
+ "[view=2,21 parent=2,2]",
+ ChangeViewDescription(connection_->changes()));
+ }
+}
+
+TEST_F(ViewManagerTest, ReorderView) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ Id view1_id = BuildViewId(2, 1);
+ Id view2_id = BuildViewId(2, 2);
+ Id view3_id = BuildViewId(2, 3);
+ Id view4_id = BuildViewId(1, 4); // Peer to 1,1
+ Id view5_id = BuildViewId(1, 5); // Peer to 1,1
+ Id view6_id = BuildViewId(2, 6); // Child of 1,2.
+ Id view7_id = BuildViewId(2, 7); // Unparented.
+ Id view8_id = BuildViewId(2, 8); // Unparented.
+ ASSERT_TRUE(connection2_->CreateView(view1_id));
+ ASSERT_TRUE(connection2_->CreateView(view2_id));
+ ASSERT_TRUE(connection2_->CreateView(view3_id));
+ ASSERT_TRUE(connection_->CreateView(view4_id));
+ ASSERT_TRUE(connection_->CreateView(view5_id));
+ ASSERT_TRUE(connection2_->CreateView(view6_id));
+ ASSERT_TRUE(connection2_->CreateView(view7_id));
+ ASSERT_TRUE(connection2_->CreateView(view8_id));
+ ASSERT_TRUE(connection2_->AddView(view1_id, view2_id));
+ ASSERT_TRUE(connection2_->AddView(view2_id, view6_id));
+ ASSERT_TRUE(connection2_->AddView(view1_id, view3_id));
+ ASSERT_TRUE(
+ connection_->AddView(ViewIdToTransportId(RootViewId()), view4_id));
+ ASSERT_TRUE(
+ connection_->AddView(ViewIdToTransportId(RootViewId()), view5_id));
+
+ ASSERT_TRUE(
+ connection_->AddView(ViewIdToTransportId(RootViewId()), view1_id));
+
+ {
+ ASSERT_TRUE(
+ connection2_->ReorderView(view2_id, view3_id, ORDER_DIRECTION_ABOVE));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("Reordered view=2,2 relative=2,3 direction=above", changes[0]);
+ }
+
+ {
+ ASSERT_TRUE(
+ connection2_->ReorderView(view2_id, view3_id, ORDER_DIRECTION_BELOW));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("Reordered view=2,2 relative=2,3 direction=below", changes[0]);
+ }
+
+ // view2 is already below view3.
+ EXPECT_FALSE(
+ connection2_->ReorderView(view2_id, view3_id, ORDER_DIRECTION_BELOW));
+
+ // view4 & 5 are unknown to connection2_.
+ EXPECT_FALSE(
+ connection2_->ReorderView(view4_id, view5_id, ORDER_DIRECTION_ABOVE));
+
+ // view6 & view3 have different parents.
+ EXPECT_FALSE(
+ connection_->ReorderView(view3_id, view6_id, ORDER_DIRECTION_ABOVE));
+
+ // Non-existent view-ids
+ EXPECT_FALSE(connection_->ReorderView(
+ BuildViewId(1, 27), BuildViewId(1, 28), ORDER_DIRECTION_ABOVE));
+
+ // view7 & view8 are un-parented.
+ EXPECT_FALSE(
+ connection_->ReorderView(view7_id, view8_id, ORDER_DIRECTION_ABOVE));
+}
+
+// Verifies DeleteView works.
+TEST_F(ViewManagerTest, DeleteView) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+
+ // Make 2 a child of 1.
+ {
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ }
+
+ // Delete 2.
+ {
+ ASSERT_TRUE(connection2_->DeleteView(BuildViewId(2, 2)));
+ EXPECT_TRUE(connection2_->changes().empty());
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ViewDeleted view=2,2", changes[0]);
+ }
+}
+
+// Verifies DeleteView isn't allowed from a separate connection.
+TEST_F(ViewManagerTest, DeleteViewFromAnotherConnectionDisallowed) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ EXPECT_FALSE(connection2_->DeleteView(BuildViewId(1, 1)));
+}
+
+// Verifies if a view was deleted and then reused that other clients are
+// properly notified.
+TEST_F(ViewManagerTest, ReuseDeletedViewId) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+
+ // Add 2 to 1.
+ {
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ("[view=2,2 parent=1,1]",
+ ChangeViewDescription(connection_->changes()));
+ }
+
+ // Delete 2.
+ {
+ ASSERT_TRUE(connection2_->DeleteView(BuildViewId(2, 2)));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ViewDeleted view=2,2", changes[0]);
+ }
+
+ // Create 2 again, and add it back to 1. Should get the same notification.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+ {
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ EXPECT_EQ("HierarchyChanged view=2,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ("[view=2,2 parent=1,1]",
+ ChangeViewDescription(connection_->changes()));
+ }
+}
+
+// Assertions for GetViewTree.
+TEST_F(ViewManagerTest, GetViewTree) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Create 11 in first connection and make it a child of 1.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 11)));
+
+ // Create two views in second connection, 2 and 3, both children of 1.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 2)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 2)));
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 1), BuildViewId(2, 3)));
+
+ // Verifies GetViewTree() on the root. The root connection sees all.
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(0, 1), &views);
+ ASSERT_EQ(5u, views.size());
+ EXPECT_EQ("view=0,1 parent=null", views[0].ToString());
+ EXPECT_EQ("view=1,1 parent=0,1", views[1].ToString());
+ EXPECT_EQ("view=1,11 parent=1,1", views[2].ToString());
+ EXPECT_EQ("view=2,2 parent=1,1", views[3].ToString());
+ EXPECT_EQ("view=2,3 parent=1,1", views[4].ToString());
+ }
+
+ // Verifies GetViewTree() on the view 1,1. This does not include any children
+ // as they are not from this connection.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+ }
+
+ // Connection 2 shouldn't be able to get the root tree.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(0, 1), &views);
+ ASSERT_EQ(0u, views.size());
+ }
+}
+
+TEST_F(ViewManagerTest, SetViewBounds) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ ASSERT_TRUE(
+ connection_->SetViewBounds(BuildViewId(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 view=1,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100",
+ changes[0]);
+
+ // Should not be possible to change the bounds of a view created by another
+ // connection.
+ ASSERT_FALSE(
+ connection2_->SetViewBounds(BuildViewId(1, 1), gfx::Rect(0, 0, 0, 0)));
+}
+
+// Verify AddView fails when trying to manipulate views in other roots.
+TEST_F(ViewManagerTest, CantMoveViewsFromOtherRoot) {
+ // Create 1 and 2 in the first connection.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(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_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+
+ // Try to reparent 1 to the root. A connection is not allowed to reparent its
+ // roots.
+ ASSERT_FALSE(connection2_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+}
+
+// Verify RemoveViewFromParent fails for views that are descendants of the
+// roots.
+TEST_F(ViewManagerTest, CantRemoveViewsInOtherRoots) {
+ // Create 1 and 2 in the first connection and parent both to the root.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 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 view 2 or 1 from its parent.
+ ASSERT_FALSE(connection2_->RemoveViewFromParent(BuildViewId(1, 2)));
+ ASSERT_FALSE(connection2_->RemoveViewFromParent(BuildViewId(1, 1)));
+
+ // Create views 10 and 11 in 2.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 10)));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11)));
+
+ // Parent 11 to 10.
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(2, 10), BuildViewId(2, 11)));
+ // Remove 11 from 10.
+ ASSERT_TRUE(connection2_->RemoveViewFromParent(BuildViewId(2, 11)));
+
+ // Verify nothing was actually removed.
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(0, 1), &views);
+ ASSERT_EQ(3u, views.size());
+ EXPECT_EQ("view=0,1 parent=null", views[0].ToString());
+ EXPECT_EQ("view=1,1 parent=0,1", views[1].ToString());
+ EXPECT_EQ("view=1,2 parent=0,1", views[2].ToString());
+ }
+}
+
+// Verify GetViewTree fails for views that are not descendants of the roots.
+TEST_F(ViewManagerTest, CantGetViewTreeOfOtherRoots) {
+ // Create 1 and 2 in the first connection and parent both to the root.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 2)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ std::vector<TestView> views;
+
+ // Should get nothing for the root.
+ connection2_->GetViewTree(BuildViewId(0, 1), &views);
+ ASSERT_TRUE(views.empty());
+
+ // Should get nothing for view 2.
+ connection2_->GetViewTree(BuildViewId(1, 2), &views);
+ ASSERT_TRUE(views.empty());
+
+ // Should get view 1 if asked for.
+ connection2_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+}
+
+TEST_F(ViewManagerTest, OnViewInput) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Dispatch an event to the view and verify its received.
+ {
+ EventPtr event(Event::New());
+ event->action = static_cast<EventType>(1);
+ connection_->view_manager()->DispatchOnViewInputEvent(BuildViewId(1, 1),
+ event.Pass());
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("InputEvent view=1,1 event_action=1", changes[0]);
+ }
+}
+
+TEST_F(ViewManagerTest, EmbedWithSameViewId) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishThirdConnection(connection_, BuildViewId(1, 1)));
+
+ // Connection2 should have been told the view was deleted.
+ {
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ViewDeleted view=1,1", changes[0]);
+ }
+
+ // Connection2 has no root. Verify it can't see view 1,1 anymore.
+ {
+ std::vector<TestView> views;
+ connection2_->GetViewTree(BuildViewId(1, 1), &views);
+ EXPECT_TRUE(views.empty());
+ }
+}
+
+TEST_F(ViewManagerTest, EmbedWithSameViewId2) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishThirdConnection(connection_, BuildViewId(1, 1)));
+
+ // Connection2 should have been told the view was deleted.
+ connection2_->DoRunLoopUntilChangesCount(1);
+ connection2_->ClearChanges();
+
+ // Create a view in the third connection and parent it to the root.
+ ASSERT_TRUE(connection3_->CreateView(BuildViewId(3, 1)));
+ ASSERT_TRUE(connection3_->AddView(BuildViewId(1, 1), BuildViewId(3, 1)));
+
+ // Connection 1 should have been told about the add (it owns the view).
+ {
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("HierarchyChanged view=3,1 new_parent=1,1 old_parent=null",
+ changes[0]);
+ }
+
+ // Embed 1,1 again.
+ {
+ // We should get a new connection for the new embedding.
+ ASSERT_TRUE(connection_->Embed(BuildViewId(1, 1), kTestServiceURL));
+ ViewManagerProxy* connection4 = ViewManagerProxy::WaitForInstance();
+ connection4->DoRunLoopUntilChangesCount(1);
+ const std::vector<Change>& changes(connection4->changes());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("OnEmbed creator=mojo:test_url",
+ ChangesToDescription1(changes)[0]);
+ EXPECT_EQ("[view=1,1 parent=null]", ChangeViewDescription(changes));
+
+ // And 3 should get a delete.
+ connection3_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection3_->changes().size());
+ EXPECT_EQ("ViewDeleted view=1,1",
+ ChangesToDescription1(connection3_->changes())[0]);
+ }
+
+ // Connection3_ has no root. Verify it can't see view 1,1 anymore.
+ {
+ std::vector<TestView> views;
+ connection3_->GetViewTree(BuildViewId(1, 1), &views);
+ EXPECT_TRUE(views.empty());
+ }
+
+ // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
+ // connection3_ can no longer see 1,1.
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=1,1 parent=null", views[0].ToString());
+ }
+
+ // Verify connection3_ can still see the view it created 3,1.
+ {
+ std::vector<TestView> views;
+ connection3_->GetViewTree(BuildViewId(3, 1), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=3,1 parent=null", views[0].ToString());
+ }
+}
+
+// Assertions for SetViewVisibility.
+TEST_F(ViewManagerTest, SetViewVisibility) {
+ // Create 1 and 2 in the first connection and parent both to the root.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(0, 1), &views);
+ ASSERT_EQ(2u, views.size());
+ EXPECT_EQ("view=0,1 parent=null visible=true drawn=true",
+ views[0].ToString2());
+ EXPECT_EQ("view=1,1 parent=0,1 visible=true drawn=true",
+ views[1].ToString2());
+ }
+
+ // Hide 1.
+ ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 1), false));
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(1u, views.size());
+ EXPECT_EQ("view=1,1 parent=0,1 visible=false drawn=false",
+ views[0].ToString2());
+ }
+
+ // Attach 2 to 1.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(2u, views.size());
+ EXPECT_EQ("view=1,1 parent=0,1 visible=false drawn=false",
+ views[0].ToString2());
+ EXPECT_EQ("view=1,2 parent=1,1 visible=true drawn=false",
+ views[1].ToString2());
+ }
+
+ // Show 1.
+ ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 1), true));
+ {
+ std::vector<TestView> views;
+ connection_->GetViewTree(BuildViewId(1, 1), &views);
+ ASSERT_EQ(2u, views.size());
+ EXPECT_EQ("view=1,1 parent=0,1 visible=true drawn=true",
+ views[0].ToString2());
+ EXPECT_EQ("view=1,2 parent=1,1 visible=true drawn=true",
+ views[1].ToString2());
+ }
+}
+
+// Assertions for SetViewVisibility sending notifications.
+TEST_F(ViewManagerTest, SetViewVisibilityNotifications) {
+ // Create 1,1 and 1,2, 1,2 and child of 1,1 and 1,1 a child of the root.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 2)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ ASSERT_TRUE(connection_->AddView(BuildViewId(1, 1), BuildViewId(1, 2)));
+
+ // Establish the second connection at 1,2.
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishSecondConnectionWithRoot(BuildViewId(1, 2)));
+
+ // Add 2,3 as a child of 1,2.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 3)));
+ connection_->ClearChanges();
+ ASSERT_TRUE(connection2_->AddView(BuildViewId(1, 2), BuildViewId(2, 3)));
+ connection_->DoRunLoopUntilChangesCount(1);
+
+ // Hide 1,2 from connection 1. Connection 2 should see this.
+ ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 2), false));
+ {
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("VisibilityChanged view=1,2 visible=false",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+
+ // Show 1,2 from connection 2, connection 1 should be notified.
+ ASSERT_TRUE(connection2_->SetViewVisibility(BuildViewId(1, 2), true));
+ {
+ connection_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection_->changes().size());
+ EXPECT_EQ("VisibilityChanged view=1,2 visible=true",
+ ChangesToDescription1(connection_->changes())[0]);
+ }
+
+ // Hide 1,1, connection 2 should be told the draw state changed.
+ ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 1), false));
+ {
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,2 drawn=false",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+
+ // Show 1,1 from connection 1. Connection 2 should see this.
+ ASSERT_TRUE(connection_->SetViewVisibility(BuildViewId(1, 1), true));
+ {
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,2 drawn=true",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+
+ // Change visibility of 2,3, connection 1 should see this.
+ connection_->ClearChanges();
+ ASSERT_TRUE(connection2_->SetViewVisibility(BuildViewId(2, 3), false));
+ {
+ connection_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection_->changes().size());
+ EXPECT_EQ("VisibilityChanged view=2,3 visible=false",
+ ChangesToDescription1(connection_->changes())[0]);
+ }
+
+ // Remove 1,1 from the root, connection 2 should see drawn state changed.
+ ASSERT_TRUE(connection_->RemoveViewFromParent(BuildViewId(1, 1)));
+ {
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,2 drawn=false",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+
+ // Add 1,1 back to the root, connection 2 should see drawn state changed.
+ ASSERT_TRUE(connection_->AddView(BuildViewId(0, 1), BuildViewId(1, 1)));
+ {
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ EXPECT_EQ("DrawnStateChanged view=1,2 drawn=true",
+ ChangesToDescription1(connection2_->changes())[0]);
+ }
+}
+
+// TODO(sky): add coverage of test that destroys connections and ensures other
+// connections get deletion notification.
+
+// TODO(sky): need to better track changes to initial connection. For example,
+// that SetBounsdViews/AddView and the like don't result in messages to the
+// originating connection.
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/window_manager_access_policy.cc b/mojo/services/view_manager/window_manager_access_policy.cc
new file mode 100644
index 0000000..f781534
--- /dev/null
+++ b/mojo/services/view_manager/window_manager_access_policy.cc
@@ -0,0 +1,92 @@
+// 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/window_manager_access_policy.h"
+
+#include "mojo/services/view_manager/access_policy_delegate.h"
+#include "mojo/services/view_manager/server_view.h"
+
+namespace mojo {
+namespace service {
+
+// TODO(sky): document why this differs from default for each case. Maybe want
+// to subclass DefaultAccessPolicy.
+
+WindowManagerAccessPolicy::WindowManagerAccessPolicy(
+ ConnectionSpecificId connection_id,
+ AccessPolicyDelegate* delegate)
+ : connection_id_(connection_id),
+ delegate_(delegate) {
+}
+
+WindowManagerAccessPolicy::~WindowManagerAccessPolicy() {
+}
+
+bool WindowManagerAccessPolicy::CanRemoveViewFromParent(
+ const ServerView* view) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanAddView(const ServerView* parent,
+ const ServerView* child) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanDeleteView(const ServerView* view) const {
+ return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanGetViewTree(const ServerView* view) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanDescendIntoViewForViewTree(
+ const ServerView* view) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanEmbed(const ServerView* view) const {
+ return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanChangeViewVisibility(
+ const ServerView* view) const {
+ return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::CanSetViewSurfaceId(
+ const ServerView* view) const {
+ if (delegate_->IsViewRootOfAnotherConnectionForAccessPolicy(view))
+ return false;
+ return view->id().connection_id == connection_id_ ||
+ (delegate_->GetRootsForAccessPolicy().count(
+ ViewIdToTransportId(view->id())) > 0);
+}
+
+bool WindowManagerAccessPolicy::CanSetViewBounds(const ServerView* view) const {
+ return view->id().connection_id == connection_id_;
+}
+
+bool WindowManagerAccessPolicy::ShouldNotifyOnHierarchyChange(
+ const ServerView* view,
+ const ServerView** new_parent,
+ const ServerView** old_parent) const {
+ // Notify if we've already told the window manager about the view, or if we've
+ // already told the window manager about the parent. The later handles the
+ // case of a view that wasn't parented to the root getting added to the root.
+ return IsViewKnown(view) || (*new_parent && IsViewKnown(*new_parent));
+}
+
+bool WindowManagerAccessPolicy::IsViewKnown(const ServerView* view) const {
+ return delegate_->IsViewKnownForAccessPolicy(view);
+}
+
+} // namespace service
+} // namespace mojo
diff --git a/mojo/services/view_manager/window_manager_access_policy.h b/mojo/services/view_manager/window_manager_access_policy.h
new file mode 100644
index 0000000..5730a18
--- /dev/null
+++ b/mojo/services/view_manager/window_manager_access_policy.h
@@ -0,0 +1,54 @@
+// 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_WINDOW_MANAGER_ACCESS_POLICY_H_
+#define MOJO_SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
+
+#include "base/basictypes.h"
+#include "mojo/services/view_manager/access_policy.h"
+
+namespace mojo {
+namespace service {
+
+class AccessPolicyDelegate;
+
+class WindowManagerAccessPolicy : public AccessPolicy {
+ public:
+ WindowManagerAccessPolicy(ConnectionSpecificId connection_id,
+ AccessPolicyDelegate* delegate);
+ virtual ~WindowManagerAccessPolicy();
+
+ // AccessPolicy:
+ virtual bool CanRemoveViewFromParent(const ServerView* view) const OVERRIDE;
+ virtual bool CanAddView(const ServerView* parent,
+ const ServerView* child) const OVERRIDE;
+ virtual bool CanReorderView(const ServerView* view,
+ const ServerView* relative_view,
+ OrderDirection direction) const OVERRIDE;
+ virtual bool CanDeleteView(const ServerView* view) const OVERRIDE;
+ virtual bool CanGetViewTree(const ServerView* view) const OVERRIDE;
+ virtual bool CanDescendIntoViewForViewTree(
+ const ServerView* view) const OVERRIDE;
+ virtual bool CanEmbed(const ServerView* view) const OVERRIDE;
+ virtual bool CanChangeViewVisibility(const ServerView* view) const OVERRIDE;
+ virtual bool CanSetViewSurfaceId(const ServerView* view) const OVERRIDE;
+ virtual bool CanSetViewBounds(const ServerView* view) const OVERRIDE;
+ virtual bool ShouldNotifyOnHierarchyChange(
+ const ServerView* view,
+ const ServerView** new_parent,
+ const ServerView** old_parent) const OVERRIDE;
+
+ private:
+ bool IsViewKnown(const ServerView* view) const;
+
+ const ConnectionSpecificId connection_id_;
+ AccessPolicyDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerAccessPolicy);
+};
+
+} // namespace service
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_WINDOW_MANAGER_ACCESS_POLICY_H_
diff --git a/mojo/services/window_manager/BUILD.gn b/mojo/services/window_manager/BUILD.gn
new file mode 100644
index 0000000..b439775
--- /dev/null
+++ b/mojo/services/window_manager/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+
+if (use_aura) {
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager
+shared_library("window_manager") {
+ output_name = "mojo_core_window_manager"
+
+ sources = [ "main.cc" ]
+
+ public_deps = [
+ ":lib",
+ ]
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/services/public/cpp/view_manager",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager_lib
+source_set("lib") {
+ sources = [
+ "window_manager_app.cc",
+ "window_manager_app.h",
+ "window_manager_service_impl.cc",
+ "window_manager_service_impl.h",
+ ]
+
+ public_deps = [
+ "//mojo/aura",
+ ]
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/cpp/bindings",
+ "//mojo/services/public/cpp/input_events",
+ "//mojo/services/public/cpp/view_manager",
+ "//mojo/services/public/interfaces/window_manager",
+ "//ui/aura",
+ "//ui/base",
+ "//ui/events",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/wm",
+ ]
+}
+
+# GYP version: mojo/mojo_services.gypi:mojo_core_window_manager_unittests
+test("mojo_core_window_manager_unittests") {
+ sources = [
+ "window_manager_api_unittest.cc",
+ "window_manager_unittests.cc",
+ ]
+
+ deps = [
+ "//base/test:test_support",
+ "//mojo/application_manager",
+ "//mojo/edk/system",
+ "//mojo/environment:chromium",
+ "//mojo/services/public/cpp/view_manager",
+ "//mojo/services/public/interfaces/view_manager",
+ "//mojo/services/public/interfaces/window_manager",
+ "//mojo/shell:test_support",
+ "//testing/gtest",
+ "//ui/gl",
+ ]
+ if (use_x11) {
+ deps += [ "//ui/gfx/x" ]
+ }
+}
+
+} # use_aura
diff --git a/mojo/services/window_manager/DEPS b/mojo/services/window_manager/DEPS
new file mode 100644
index 0000000..3421f3b
--- /dev/null
+++ b/mojo/services/window_manager/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+mojo/aura",
+ "+mojo/application",
+ "+mojo/application_manager",
+ "+mojo/services/native_viewport",
+ "+mojo/services/public",
+ "+ui/aura",
+ "+ui/base",
+ "+ui/events",
+ "+ui/gfx",
+ "+ui/gl",
+ "+ui/wm",
+]
diff --git a/mojo/services/window_manager/main.cc b/mojo/services/window_manager/main.cc
new file mode 100644
index 0000000..ea0edb1
--- /dev/null
+++ b/mojo/services/window_manager/main.cc
@@ -0,0 +1,77 @@
+// 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/memory/scoped_ptr.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+
+// ApplicationDelegate implementation file for WindowManager users (e.g.
+// core window manager tests) that do not want to provide their own
+// ApplicationDelegate::Create().
+
+namespace mojo {
+
+class DefaultWindowManager : public ApplicationDelegate,
+ public ViewManagerDelegate,
+ public WindowManagerDelegate {
+ public:
+ DefaultWindowManager()
+ : window_manager_app_(new WindowManagerApp(this, this)),
+ view_manager_(NULL),
+ root_(NULL) {}
+ virtual ~DefaultWindowManager() {}
+
+ private:
+ // Overridden from ApplicationDelegate:
+ virtual void Initialize(ApplicationImpl* impl) override {
+ window_manager_app_->Initialize(impl);
+ }
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override {
+ window_manager_app_->ConfigureIncomingConnection(connection);
+ return true;
+ }
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnEmbed(ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* exported_services,
+ scoped_ptr<ServiceProvider> imported_services) override {
+ view_manager_ = view_manager;
+ root_ = root;
+ view_manager_->SetWindowManagerDelegate(this);
+ }
+ virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+ // Overridden from WindowManagerDelegate:
+ virtual void Embed(
+ const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) override {
+ View* view = View::Create(view_manager_);
+ root_->AddChild(view);
+ view->Embed(url, scoped_ptr<mojo::ServiceProviderImpl>(
+ new mojo::ServiceProviderImpl).Pass());
+ }
+ virtual void DispatchEvent(EventPtr event) override {}
+
+ scoped_ptr<WindowManagerApp> window_manager_app_;
+
+ ViewManager* view_manager_;
+ View* root_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(DefaultWindowManager);
+};
+
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new mojo::DefaultWindowManager);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/window_manager/window_manager_api_unittest.cc b/mojo/services/window_manager/window_manager_api_unittest.cc
new file mode 100644
index 0000000..49bd98e
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_api_unittest.cc
@@ -0,0 +1,278 @@
+// 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/memory/scoped_vector.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kTestServiceURL[] = "mojo:test_url";
+
+void EmptyResultCallback(bool result) {}
+
+// Callback from Embed(). |result| is the result of the Embed() call and
+// |run_loop| the nested RunLoop.
+void ResultCallback(bool* result_cache, base::RunLoop* run_loop, bool result) {
+ *result_cache = result;
+ run_loop->Quit();
+}
+
+// Responsible for establishing the initial ViewManagerService connection.
+// Blocks until result is determined.
+bool InitEmbed(ViewManagerInitService* view_manager_init,
+ const std::string& url) {
+ bool result = false;
+ base::RunLoop run_loop;
+ ServiceProviderPtr sp;
+ BindToProxy(new ServiceProviderImpl, &sp);
+ view_manager_init->Embed(url, sp.Pass(),
+ base::Bind(&ResultCallback, &result, &run_loop));
+ run_loop.Run();
+ return result;
+}
+
+class TestWindowManagerClient : public WindowManagerClient {
+ public:
+ typedef base::Callback<void(Id, Id)>
+ TwoNodeCallback;
+
+ explicit TestWindowManagerClient(base::RunLoop* run_loop)
+ : run_loop_(run_loop) {}
+ virtual ~TestWindowManagerClient() {}
+
+ void set_focus_changed_callback(const TwoNodeCallback& callback) {
+ focus_changed_callback_ = callback;
+ }
+ void set_active_window_changed_callback(const TwoNodeCallback& callback) {
+ active_window_changed_callback_ = callback;
+ }
+
+ private:
+ // Overridden from WindowManagerClient:
+ virtual void OnWindowManagerReady() override { run_loop_->Quit(); }
+ virtual void OnCaptureChanged(Id old_capture_node_id,
+ Id new_capture_node_id) override {}
+ virtual void OnFocusChanged(Id old_focused_node_id,
+ Id new_focused_node_id) override {
+ if (!focus_changed_callback_.is_null())
+ focus_changed_callback_.Run(old_focused_node_id, new_focused_node_id);
+ }
+ virtual void OnActiveWindowChanged(Id old_active_window,
+ Id new_active_window) override {
+ if (!active_window_changed_callback_.is_null())
+ active_window_changed_callback_.Run(old_active_window, new_active_window);
+ }
+
+ base::RunLoop* run_loop_;
+ TwoNodeCallback focus_changed_callback_;
+ TwoNodeCallback active_window_changed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowManagerClient);
+};
+
+class TestApplicationLoader : public ApplicationLoader,
+ public ApplicationDelegate,
+ public ViewManagerDelegate {
+ public:
+ typedef base::Callback<void(View*)> RootAddedCallback;
+
+ explicit TestApplicationLoader(const RootAddedCallback& root_added_callback)
+ : root_added_callback_(root_added_callback) {}
+ virtual ~TestApplicationLoader() {}
+
+ private:
+ // Overridden from ApplicationLoader:
+ virtual void Load(ApplicationManager* application_manager,
+ const GURL& url,
+ scoped_refptr<LoadCallbacks> callbacks) override {
+ ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
+ if (!shell_handle.is_valid())
+ return;
+ scoped_ptr<ApplicationImpl> app(
+ new ApplicationImpl(this, shell_handle.Pass()));
+ apps_.push_back(app.release());
+ }
+ virtual void OnApplicationError(ApplicationManager* application_manager,
+ const GURL& url) override {}
+
+ // Overridden from ApplicationDelegate:
+ virtual void Initialize(ApplicationImpl* app) override {
+ view_manager_client_factory_.reset(
+ new ViewManagerClientFactory(app->shell(), this));
+ }
+
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override {
+ connection->AddService(view_manager_client_factory_.get());
+ return true;
+ }
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnEmbed(ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* exported_services,
+ scoped_ptr<ServiceProvider> imported_services) override {
+ root_added_callback_.Run(root);
+ }
+ virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+ RootAddedCallback root_added_callback_;
+
+ ScopedVector<ApplicationImpl> apps_;
+ scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+} // namespace
+
+class WindowManagerApiTest : public testing::Test {
+ public:
+ WindowManagerApiTest() {}
+ virtual ~WindowManagerApiTest() {}
+
+ protected:
+ typedef std::pair<Id, Id> TwoIds;
+
+ Id WaitForEmbed() {
+ Id id;
+ base::RunLoop run_loop;
+ root_added_callback_ = base::Bind(&WindowManagerApiTest::OnEmbed,
+ base::Unretained(this), &id, &run_loop);
+ run_loop.Run();
+ return id;
+ }
+
+ TwoIds WaitForFocusChange() {
+ TwoIds old_and_new;
+ base::RunLoop run_loop;
+ window_manager_client()->set_focus_changed_callback(
+ base::Bind(&WindowManagerApiTest::OnFocusChanged,
+ base::Unretained(this), &old_and_new, &run_loop));
+ run_loop.Run();
+ return old_and_new;
+ }
+
+ TwoIds WaitForActiveWindowChange() {
+ TwoIds old_and_new;
+ base::RunLoop run_loop;
+ window_manager_client()->set_active_window_changed_callback(
+ base::Bind(&WindowManagerApiTest::OnActiveWindowChanged,
+ base::Unretained(this), &old_and_new, &run_loop));
+ run_loop.Run();
+ return old_and_new;
+ }
+
+ Id OpenWindow() {
+ return OpenWindowWithURL(kTestServiceURL);
+ }
+
+ Id OpenWindowWithURL(const std::string& url) {
+ InitEmbed(view_manager_init_.get(), url);
+ return WaitForEmbed();
+ }
+
+ TestWindowManagerClient* window_manager_client() {
+ return window_manager_client_.get();
+ }
+
+ WindowManagerServicePtr window_manager_;
+
+ private:
+ // Overridden from testing::Test:
+ virtual void SetUp() override {
+ test_helper_.Init();
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(new TestApplicationLoader(base::Bind(
+ &WindowManagerApiTest::OnRootAdded, base::Unretained(this)))),
+ GURL(kTestServiceURL));
+ test_helper_.application_manager()->ConnectToService(
+ GURL("mojo:mojo_view_manager"), &view_manager_init_);
+ ASSERT_TRUE(InitEmbed(view_manager_init_.get(),
+ "mojo:mojo_core_window_manager"));
+ ConnectToWindowManager();
+ }
+ virtual void TearDown() override {}
+
+ void ConnectToWindowManager() {
+ test_helper_.application_manager()->ConnectToService(
+ GURL("mojo:mojo_core_window_manager"), &window_manager_);
+ base::RunLoop connect_loop;
+ window_manager_client_.reset(new TestWindowManagerClient(&connect_loop));
+ window_manager_.set_client(window_manager_client());
+ connect_loop.Run();
+ }
+
+ void OnRootAdded(View* root) {
+ if (!root_added_callback_.is_null())
+ root_added_callback_.Run(root);
+ }
+
+ void OnEmbed(Id* root_id,
+ base::RunLoop* loop,
+ View* root) {
+ *root_id = root->id();
+ loop->Quit();
+ }
+
+ void OnFocusChanged(TwoIds* old_and_new,
+ base::RunLoop* run_loop,
+ Id old_focused_node_id,
+ Id new_focused_node_id) {
+ DCHECK(old_and_new);
+ old_and_new->first = old_focused_node_id;
+ old_and_new->second = new_focused_node_id;
+ run_loop->Quit();
+ }
+
+ void OnActiveWindowChanged(TwoIds* old_and_new,
+ base::RunLoop* run_loop,
+ Id old_focused_node_id,
+ Id new_focused_node_id) {
+ DCHECK(old_and_new);
+ old_and_new->first = old_focused_node_id;
+ old_and_new->second = new_focused_node_id;
+ run_loop->Quit();
+ }
+
+ shell::ShellTestHelper test_helper_;
+ ViewManagerInitServicePtr view_manager_init_;
+ scoped_ptr<TestWindowManagerClient> window_manager_client_;
+ TestApplicationLoader::RootAddedCallback root_added_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerApiTest);
+};
+
+TEST_F(WindowManagerApiTest, FocusAndActivateWindow) {
+ Id first_window = OpenWindow();
+ window_manager_->FocusWindow(first_window,
+ base::Bind(&EmptyResultCallback));
+ TwoIds ids = WaitForFocusChange();
+ EXPECT_TRUE(ids.first == 0);
+ EXPECT_EQ(ids.second, first_window);
+
+ Id second_window = OpenWindow();
+ window_manager_->ActivateWindow(second_window,
+ base::Bind(&EmptyResultCallback));
+ ids = WaitForActiveWindowChange();
+ EXPECT_EQ(ids.first, first_window);
+ EXPECT_EQ(ids.second, second_window);
+}
+
+} // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_app.cc b/mojo/services/window_manager/window_manager_app.cc
new file mode 100644
index 0000000..a22acba
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app.cc
@@ -0,0 +1,342 @@
+// 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/window_manager/window_manager_app.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/aura/aura_init.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/hit_test.h"
+#include "ui/wm/core/capture_controller.h"
+#include "ui/wm/core/focus_controller.h"
+#include "ui/wm/public/activation_client.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(mojo::View*);
+
+namespace mojo {
+
+// The aura::Windows we use to track Views don't render, so we don't actually
+// need to supply a fully functional WindowDelegate. We do need to provide _a_
+// delegate however, otherwise the event dispatcher won't dispatch events to
+// these windows. (The aura WindowTargeter won't allow a delegate-less window
+// to be the target of an event, since the window delegate is considered the
+// "target handler").
+class DummyDelegate : public aura::WindowDelegate {
+ public:
+ DummyDelegate() {}
+ virtual ~DummyDelegate() {}
+
+ private:
+ // WindowDelegate overrides:
+ virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(); }
+ virtual gfx::Size GetMaximumSize() const OVERRIDE { return gfx::Size(); }
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {}
+ virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
+ return gfx::kNullCursor;
+ }
+ virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE {
+ return HTCAPTION;
+ }
+ virtual bool ShouldDescendIntoChildForEventHandling(
+ aura::Window* child,
+ const gfx::Point& location) OVERRIDE { return true; }
+ virtual bool CanFocus() OVERRIDE { return true; }
+ 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 { return false; }
+ virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {}
+
+ DISALLOW_COPY_AND_ASSIGN(DummyDelegate);
+};
+
+namespace {
+
+DEFINE_WINDOW_PROPERTY_KEY(View*, kViewKey, NULL);
+
+Id GetIdForWindow(aura::Window* window) {
+ return window ? WindowManagerApp::GetViewForWindow(window)->id() : 0;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, public:
+
+WindowManagerApp::WindowManagerApp(
+ ViewManagerDelegate* view_manager_delegate,
+ WindowManagerDelegate* window_manager_delegate)
+ : window_manager_service_factory_(this),
+ wrapped_view_manager_delegate_(view_manager_delegate),
+ wrapped_window_manager_delegate_(window_manager_delegate),
+ view_manager_(NULL),
+ root_(NULL),
+ dummy_delegate_(new DummyDelegate) {
+}
+
+WindowManagerApp::~WindowManagerApp() {}
+
+// static
+View* WindowManagerApp::GetViewForWindow(aura::Window* window) {
+ return window->GetProperty(kViewKey);
+}
+
+aura::Window* WindowManagerApp::GetWindowForViewId(Id view) {
+ ViewIdToWindowMap::const_iterator it = view_id_to_window_map_.find(view);
+ return it != view_id_to_window_map_.end() ? it->second : NULL;
+}
+
+void WindowManagerApp::AddConnection(WindowManagerServiceImpl* connection) {
+ DCHECK(connections_.find(connection) == connections_.end());
+ connections_.insert(connection);
+}
+
+void WindowManagerApp::RemoveConnection(WindowManagerServiceImpl* connection) {
+ DCHECK(connections_.find(connection) != connections_.end());
+ connections_.erase(connection);
+}
+
+void WindowManagerApp::SetCapture(Id view) {
+ capture_client_->capture_client()->SetCapture(GetWindowForViewId(view));
+ // TODO(beng): notify connected clients that capture has changed, probably
+ // by implementing some capture-client observer.
+}
+
+void WindowManagerApp::FocusWindow(Id view) {
+ aura::Window* window = GetWindowForViewId(view);
+ DCHECK(window);
+ focus_client_->FocusWindow(window);
+}
+
+void WindowManagerApp::ActivateWindow(Id view) {
+ aura::Window* window = GetWindowForViewId(view);
+ DCHECK(window);
+ activation_client_->ActivateWindow(window);
+}
+
+bool WindowManagerApp::IsReady() const {
+ return view_manager_ && root_;
+}
+
+void WindowManagerApp::InitFocus(wm::FocusRules* rules) {
+ wm::FocusController* focus_controller = new wm::FocusController(rules);
+ activation_client_ = focus_controller;
+ focus_client_.reset(focus_controller);
+ aura::client::SetFocusClient(window_tree_host_->window(), focus_controller);
+ aura::client::SetActivationClient(window_tree_host_->window(),
+ focus_controller);
+
+ focus_client_->AddObserver(this);
+ activation_client_->AddObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ApplicationDelegate implementation:
+
+void WindowManagerApp::Initialize(ApplicationImpl* impl) {
+ aura_init_.reset(new AuraInit);
+ view_manager_client_factory_.reset(
+ new ViewManagerClientFactory(impl->shell(), this));
+}
+
+bool WindowManagerApp::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ connection->AddService(&window_manager_service_factory_);
+ connection->AddService(view_manager_client_factory_.get());
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ViewManagerDelegate implementation:
+
+void WindowManagerApp::OnEmbed(ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* exported_services,
+ scoped_ptr<ServiceProvider> imported_services) {
+ DCHECK(!view_manager_ && !root_);
+ view_manager_ = view_manager;
+ view_manager_->SetWindowManagerDelegate(this);
+ root_ = root;
+
+ window_tree_host_.reset(new WindowTreeHostMojo(root_, this));
+ window_tree_host_->window()->SetBounds(root->bounds());
+ window_tree_host_->window()->Show();
+
+ RegisterSubtree(root_, window_tree_host_->window());
+
+ capture_client_.reset(
+ new wm::ScopedCaptureClient(window_tree_host_->window()));
+
+ if (wrapped_view_manager_delegate_) {
+ wrapped_view_manager_delegate_->OnEmbed(
+ view_manager, root, exported_services, imported_services.Pass());
+ }
+
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->NotifyReady();
+ }
+}
+
+void WindowManagerApp::OnViewManagerDisconnected(
+ ViewManager* view_manager) {
+ DCHECK_EQ(view_manager_, view_manager);
+ if (wrapped_view_manager_delegate_)
+ wrapped_view_manager_delegate_->OnViewManagerDisconnected(view_manager);
+ view_manager_ = NULL;
+ base::MessageLoop::current()->Quit();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, WindowManagerDelegate implementation:
+
+void WindowManagerApp::Embed(
+ const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) {
+ if (wrapped_window_manager_delegate_)
+ wrapped_window_manager_delegate_->Embed(url, service_provider.Pass());
+}
+
+void WindowManagerApp::DispatchEvent(EventPtr event) {
+ scoped_ptr<ui::Event> ui_event = event.To<scoped_ptr<ui::Event> >();
+ if (ui_event)
+ window_tree_host_->SendEventToProcessor(ui_event.get());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ViewObserver implementation:
+
+void WindowManagerApp::OnTreeChanged(
+ const ViewObserver::TreeChangeParams& params) {
+ if (params.receiver != root_)
+ return;
+ DCHECK(params.old_parent || params.new_parent);
+ if (!params.target)
+ return;
+
+ if (params.new_parent) {
+ if (view_id_to_window_map_.find(params.target->id()) ==
+ view_id_to_window_map_.end()) {
+ ViewIdToWindowMap::const_iterator it =
+ view_id_to_window_map_.find(params.new_parent->id());
+ DCHECK(it != view_id_to_window_map_.end());
+ RegisterSubtree(params.target, it->second);
+ }
+ } else if (params.old_parent) {
+ UnregisterSubtree(params.target);
+ }
+}
+
+void WindowManagerApp::OnViewDestroyed(View* view) {
+ if (view != root_)
+ return;
+ aura::Window* window = GetWindowForViewId(view->id());
+ window->RemovePreTargetHandler(this);
+ root_ = NULL;
+ STLDeleteValues(&view_id_to_window_map_);
+ if (focus_client_.get())
+ focus_client_->RemoveObserver(this);
+ if (activation_client_)
+ activation_client_->RemoveObserver(this);
+ window_tree_host_.reset();
+}
+
+void WindowManagerApp::OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ aura::Window* window = GetWindowForViewId(view->id());
+ window->SetBounds(new_bounds);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, WindowTreeHostMojoDelegate implementation:
+
+void WindowManagerApp::CompositorContentsChanged(const SkBitmap& bitmap) {
+ // We draw nothing.
+ NOTREACHED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ui::EventHandler implementation:
+
+void WindowManagerApp::OnEvent(ui::Event* event) {
+ aura::Window* window = static_cast<aura::Window*>(event->target());
+ view_manager_->DispatchEvent(GetViewForWindow(window), Event::From(*event));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, aura::client::FocusChangeObserver implementation:
+
+void WindowManagerApp::OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) {
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->NotifyViewFocused(GetIdForWindow(gained_focus),
+ GetIdForWindow(lost_focus));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, aura::client::ActivationChangeObserver implementation:
+
+void WindowManagerApp::OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) {
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->NotifyWindowActivated(GetIdForWindow(gained_active),
+ GetIdForWindow(lost_active));
+ }
+ if (gained_active) {
+ View* view = GetViewForWindow(gained_active);
+ view->MoveToFront();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, private:
+
+void WindowManagerApp::RegisterSubtree(View* view, aura::Window* parent) {
+ view->AddObserver(this);
+ DCHECK(view_id_to_window_map_.find(view->id()) ==
+ view_id_to_window_map_.end());
+ aura::Window* window = new aura::Window(dummy_delegate_.get());
+ window->set_id(view->id());
+ window->SetProperty(kViewKey, view);
+ // All events pass through the root during dispatch, so we only need a handler
+ // installed there.
+ if (view == root_)
+ window->AddPreTargetHandler(this);
+ parent->AddChild(window);
+ window->SetBounds(view->bounds());
+ window->Show();
+ view_id_to_window_map_[view->id()] = window;
+ View::Children::const_iterator it = view->children().begin();
+ for (; it != view->children().end(); ++it)
+ RegisterSubtree(*it, window);
+}
+
+void WindowManagerApp::UnregisterSubtree(View* view) {
+ view->RemoveObserver(this);
+ ViewIdToWindowMap::iterator it = view_id_to_window_map_.find(view->id());
+ DCHECK(it != view_id_to_window_map_.end());
+ scoped_ptr<aura::Window> window(it->second);
+ view_id_to_window_map_.erase(it);
+ View::Children::const_iterator child = view->children().begin();
+ for (; child != view->children().end(); ++child)
+ UnregisterSubtree(*child);
+}
+
+} // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_app.h b/mojo/services/window_manager/window_manager_app.h
new file mode 100644
index 0000000..01221d6
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app.h
@@ -0,0 +1,171 @@
+// 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_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+#define MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
+#include "mojo/services/window_manager/window_manager_service_impl.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/public/activation_change_observer.h"
+
+namespace aura {
+namespace client {
+class ActivationClient;
+class FocusClient;
+}
+class Window;
+}
+
+namespace wm {
+class FocusRules;
+class ScopedCaptureClient;
+}
+
+namespace mojo {
+
+class AuraInit;
+class DummyDelegate;
+class WindowManagerServiceImpl;
+class WindowTreeHostMojo;
+
+// Implements core window manager functionality that could conceivably be shared
+// across multiple window managers implementing superficially different user
+// experiences. Establishes communication with the view manager.
+// A window manager wishing to use this core should create and own an instance
+// of this object. They may implement the associated ViewManager/WindowManager
+// delegate interfaces exposed by the view manager, this object provides the
+// canonical implementation of said interfaces but will call out to the wrapped
+// instances.
+// This object maintains an aura::WindowTreeHost containing a hierarchy of
+// aura::Windows. Window manager functionality (e.g. focus, activation,
+// modality, etc.) are implemented using aura core window manager components.
+class WindowManagerApp
+ : public ApplicationDelegate,
+ public ViewManagerDelegate,
+ public WindowManagerDelegate,
+ public ViewObserver,
+ public WindowTreeHostMojoDelegate,
+ public ui::EventHandler,
+ public aura::client::FocusChangeObserver,
+ public aura::client::ActivationChangeObserver {
+ public:
+ WindowManagerApp(ViewManagerDelegate* view_manager_delegate,
+ WindowManagerDelegate* window_manager_delegate);
+ virtual ~WindowManagerApp();
+
+ static View* GetViewForWindow(aura::Window* window);
+ aura::Window* GetWindowForViewId(Id view);
+
+ // Register/deregister new connections to the window manager service.
+ void AddConnection(WindowManagerServiceImpl* connection);
+ void RemoveConnection(WindowManagerServiceImpl* connection);
+
+ // These are canonical implementations of the window manager API methods.
+ void SetCapture(Id view);
+ void FocusWindow(Id view);
+ void ActivateWindow(Id view);
+
+ bool IsReady() const;
+
+ // A client of this object will use this accessor to gain access to the
+ // aura::Window hierarchy and attach event handlers.
+ aura::WindowTreeHost* host() { return window_tree_host_.get(); }
+
+ void InitFocus(wm::FocusRules* rules);
+
+ // Overridden from ApplicationDelegate:
+ virtual void Initialize(ApplicationImpl* impl) override;
+ virtual bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override;
+
+ private:
+ typedef std::set<WindowManagerServiceImpl*> Connections;
+ typedef std::map<Id, aura::Window*> ViewIdToWindowMap;
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnEmbed(ViewManager* view_manager,
+ View* root,
+ ServiceProviderImpl* exported_services,
+ scoped_ptr<ServiceProvider> imported_services) override;
+ virtual void OnViewManagerDisconnected(ViewManager* view_manager) override;
+
+ // Overridden from WindowManagerDelegate:
+ virtual void Embed(
+ const String& url,
+ InterfaceRequest<ServiceProvider> service_provider) OVERRIDE;
+ virtual void DispatchEvent(EventPtr event) OVERRIDE;
+
+ // Overridden from ViewObserver:
+ virtual void OnTreeChanged(
+ const ViewObserver::TreeChangeParams& params) override;
+ virtual void OnViewDestroyed(View* view) override;
+ virtual void OnViewBoundsChanged(View* view,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override;
+
+ // Overridden from WindowTreeHostMojoDelegate:
+ virtual void CompositorContentsChanged(const SkBitmap& bitmap) override;
+
+ // Overridden from ui::EventHandler:
+ virtual void OnEvent(ui::Event* event) override;
+
+ // Overridden from aura::client::FocusChangeObserver:
+ virtual void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) override;
+
+ // Overridden from aura::client::ActivationChangeObserver:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) override;
+
+ // Creates an aura::Window for every view in the hierarchy beneath |view|,
+ // and adds to the registry so that it can be retrieved later via
+ // GetWindowForViewId().
+ // TODO(beng): perhaps View should have a property bag.
+ void RegisterSubtree(View* view, aura::Window* parent);
+ // Deletes the aura::Windows associated with the hierarchy beneath |id|,
+ // and removes from the registry.
+ void UnregisterSubtree(View* view);
+
+ InterfaceFactoryImplWithContext<WindowManagerServiceImpl, WindowManagerApp>
+ window_manager_service_factory_;
+
+ ViewManagerDelegate* wrapped_view_manager_delegate_;
+ WindowManagerDelegate* wrapped_window_manager_delegate_;
+
+ ViewManager* view_manager_;
+ scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+ View* root_;
+
+ scoped_ptr<AuraInit> aura_init_;
+ scoped_ptr<WindowTreeHostMojo> window_tree_host_;
+
+ scoped_ptr<wm::ScopedCaptureClient> capture_client_;
+ scoped_ptr<aura::client::FocusClient> focus_client_;
+ aura::client::ActivationClient* activation_client_;
+
+ Connections connections_;
+ ViewIdToWindowMap view_id_to_window_map_;
+
+ scoped_ptr<DummyDelegate> dummy_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerApp);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
diff --git a/mojo/services/window_manager/window_manager_service_impl.cc b/mojo/services/window_manager/window_manager_service_impl.cc
new file mode 100644
index 0000000..aa1be19
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_service_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/window_manager/window_manager_service_impl.h"
+
+#include "mojo/services/window_manager/window_manager_app.h"
+
+namespace mojo {
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerServiceImpl, public:
+
+WindowManagerServiceImpl::WindowManagerServiceImpl(
+ WindowManagerApp* window_manager)
+ : window_manager_(window_manager) {
+ window_manager_->AddConnection(this);
+}
+
+WindowManagerServiceImpl::~WindowManagerServiceImpl() {
+ window_manager_->RemoveConnection(this);
+}
+
+void WindowManagerServiceImpl::NotifyReady() {
+ client()->OnWindowManagerReady();
+}
+
+void WindowManagerServiceImpl::NotifyViewFocused(Id new_focused_id,
+ Id old_focused_id) {
+ client()->OnFocusChanged(old_focused_id, new_focused_id);
+}
+
+void WindowManagerServiceImpl::NotifyWindowActivated(Id new_active_id,
+ Id old_active_id) {
+ client()->OnActiveWindowChanged(old_active_id, new_active_id);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerServiceImpl, WindowManager implementation:
+
+void WindowManagerServiceImpl::SetCapture(
+ Id view,
+ const Callback<void(bool)>& callback) {
+ bool success = window_manager_->IsReady();
+ if (success)
+ window_manager_->SetCapture(view);
+ callback.Run(success);
+}
+
+void WindowManagerServiceImpl::FocusWindow(
+ Id view,
+ const Callback<void(bool)>& callback) {
+ bool success = window_manager_->IsReady();
+ if (success)
+ window_manager_->FocusWindow(view);
+ callback.Run(success);
+}
+
+void WindowManagerServiceImpl::ActivateWindow(
+ Id view,
+ const Callback<void(bool)>& callback) {
+ bool success = window_manager_->IsReady();
+ if (success)
+ window_manager_->ActivateWindow(view);
+ callback.Run(success);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerServiceImpl, InterfaceImpl overrides:
+
+void WindowManagerServiceImpl::OnConnectionEstablished() {
+ // If the connection was established prior to the window manager being
+ // embedded by the view manager, |window_manager_|'s ViewManagerDelegate
+ // impl will call NotifyReady() when it is.
+ if (window_manager_->IsReady())
+ NotifyReady();
+}
+
+} // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_service_impl.h b/mojo/services/window_manager/window_manager_service_impl.h
new file mode 100644
index 0000000..2d662e4
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_service_impl.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_WINDOW_MANAGER_WINDOW_MANAGER_SERVICE_IMPL_H_
+#define MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_SERVICE_IMPL_H_
+
+#include "base/basictypes.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
+
+namespace mojo {
+
+class WindowManagerApp;
+
+class WindowManagerServiceImpl : public InterfaceImpl<WindowManagerService> {
+ public:
+ explicit WindowManagerServiceImpl(WindowManagerApp* manager);
+ virtual ~WindowManagerServiceImpl();
+
+ void NotifyReady();
+ void NotifyViewFocused(Id new_focused_id, Id old_focused_id);
+ void NotifyWindowActivated(Id new_active_id, Id old_active_id);
+
+ private:
+ // Overridden from WindowManagerService:
+ virtual void SetCapture(Id view,
+ const Callback<void(bool)>& callback) override;
+ virtual void FocusWindow(Id view,
+ const Callback<void(bool)>& callback) override;
+ virtual void ActivateWindow(Id view,
+ const Callback<void(bool)>& callback) override;
+
+ // Overridden from InterfaceImpl:
+ virtual void OnConnectionEstablished() override;
+
+ WindowManagerApp* window_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerServiceImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_SERVICE_IMPL_H_
diff --git a/mojo/services/window_manager/window_manager_unittests.cc b/mojo/services/window_manager/window_manager_unittests.cc
new file mode 100644
index 0000000..2ddceb8
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_unittests.cc
@@ -0,0 +1,43 @@
+// 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 "base/test/test_suite.h"
+#include "ui/gl/gl_surface.h"
+
+#if defined(USE_X11)
+#include "ui/gfx/x/x11_connection.h"
+#endif
+
+namespace mojo {
+
+class WindowManagerTestSuite : public base::TestSuite {
+ public:
+ WindowManagerTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+ virtual ~WindowManagerTestSuite() {}
+
+ protected:
+ virtual void Initialize() OVERRIDE {
+#if defined(USE_X11)
+ // Each test ends up creating a new thread for the native viewport service.
+ // In other words we'll use X on different threads, so tell it that.
+ gfx::InitializeThreadedX11();
+#endif
+ base::TestSuite::Initialize();
+ gfx::GLSurface::InitializeOneOffForTests();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerTestSuite);
+};
+
+} // namespace mojo
+
+int main(int argc, char** argv) {
+ mojo::WindowManagerTestSuite test_suite(argc, argv);
+
+ return base::LaunchUnitTests(
+ argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite)));
+}