diff options
author | erg <erg@chromium.org> | 2014-09-16 17:08:56 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-17 00:09:12 +0000 |
commit | b69fbca034e3c2181e0a12cf02baec4de526bf25 (patch) | |
tree | 8bdb2d79438facc797c47f3a6ee483039e5afb02 /mojo | |
parent | 0d9398539d5a78e8025eeb1c92f9d396c946e105 (diff) | |
download | chromium_src-b69fbca034e3c2181e0a12cf02baec4de526bf25.zip chromium_src-b69fbca034e3c2181e0a12cf02baec4de526bf25.tar.gz chromium_src-b69fbca034e3c2181e0a12cf02baec4de526bf25.tar.bz2 |
mojo: Create a basic clipboard.
This creates a basic clipboard interface and uses it from
html_viewer. This is a minimal implementation and does not actually
interact with the system clipboard.
BUG=411039
First Committed: https://crrev.com/757286d8a2c778fe4622890140c9b9d2afd21063
Review URL: https://codereview.chromium.org/562483002
Cr-Commit-Position: refs/heads/master@{#295182}
Diffstat (limited to 'mojo')
23 files changed, 916 insertions, 0 deletions
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index c5b9683..276a4b9 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn @@ -37,6 +37,7 @@ group("tests") { "//mojo/public/cpp/system/tests:mojo_public_system_unittests", "//mojo/public/cpp/utility/tests:mojo_public_utility_unittests", "//mojo/public/js/bindings/tests:mojo_js_unittests", + "//mojo/services/clipboard:mojo_clipboard_unittests", "//mojo/services/public/cpp/surfaces/tests:mojo_surfaces_lib_unittests", "//mojo/shell:mojo_shell_tests", "//mojo/system:mojo_message_pipe_perftests", diff --git a/mojo/common/common_type_converters.cc b/mojo/common/common_type_converters.cc index ffc1907..114b409 100644 --- a/mojo/common/common_type_converters.cc +++ b/mojo/common/common_type_converters.cc @@ -46,4 +46,20 @@ GURL TypeConverter<GURL, String>::Convert(const String& input) { return GURL(input.get()); } +std::string TypeConverter<std::string, Array<uint8_t> >::Convert( + const Array<uint8_t>& input) { + if (input.is_null()) + return std::string(); + + return std::string(reinterpret_cast<const char*>(&input.front()), + input.size()); +} + +Array<uint8_t> TypeConverter<Array<uint8_t>, std::string>::Convert( + const std::string& input) { + Array<uint8_t> result(input.size()); + memcpy(&result.front(), input.c_str(), input.size()); + return result.Pass(); +} + } // namespace mojo diff --git a/mojo/common/common_type_converters.h b/mojo/common/common_type_converters.h index 159325e..7b0260a 100644 --- a/mojo/common/common_type_converters.h +++ b/mojo/common/common_type_converters.h @@ -8,6 +8,7 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "mojo/common/mojo_common_export.h" +#include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/string.h" #include "mojo/public/cpp/bindings/type_converter.h" @@ -45,6 +46,20 @@ struct MOJO_COMMON_EXPORT TypeConverter<GURL, String> { static GURL Convert(const String& input); }; +// TODO(erg): In the very long term, we will want to remove conversion between +// std::strings and arrays of unsigned bytes. However, there is too much code +// across chrome which uses std::string as a bag of bytes that we probably +// don't want to roll this function at each callsite. +template <> +struct MOJO_COMMON_EXPORT TypeConverter<std::string, Array<uint8_t> > { + static std::string Convert(const Array<uint8_t>& input); +}; + +template <> +struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, std::string> { + static Array<uint8_t> Convert(const std::string& input); +}; + } // namespace mojo #endif // MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_ diff --git a/mojo/common/common_type_converters_unittest.cc b/mojo/common/common_type_converters_unittest.cc index 314f180..f97be0a 100644 --- a/mojo/common/common_type_converters_unittest.cc +++ b/mojo/common/common_type_converters_unittest.cc @@ -84,6 +84,27 @@ TEST(CommonTypeConvertersTest, URL) { ASSERT_EQ(0U, string_from_invalid.size()); } +TEST(CommonTypeConvertersTest, ArrayUint8ToStdString) { + Array<uint8_t> data(4); + data[0] = 'd'; + data[1] = 'a'; + data[2] = 't'; + data[3] = 'a'; + + EXPECT_EQ("data", data.To<std::string>()); +} + +TEST(CommonTypeConvertersTest, StdStringToArrayUint8) { + std::string input("data"); + Array<uint8_t> data = Array<uint8_t>::From(input); + + ASSERT_EQ(4ul, data.size()); + EXPECT_EQ('d', data[0]); + EXPECT_EQ('a', data[1]); + EXPECT_EQ('t', data[2]); + EXPECT_EQ('a', data[3]); +} + } // namespace test } // namespace common } // namespace mojo diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index 29a49f0..890af4e 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -29,6 +29,8 @@ 'mojo_application_manager_unittests', 'mojo_apps_js_unittests', 'mojo_base.gyp:mojo_base', + 'mojo_clipboard', + 'mojo_clipboard_unittests', 'mojo_compositor_app', 'mojo_content_handler_demo', 'mojo_echo_client', diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi index e775c00..4b68611 100644 --- a/mojo/mojo_services.gypi +++ b/mojo/mojo_services.gypi @@ -5,6 +5,59 @@ { 'targets': [ { + # GN version: //mojo/services/clipboard/ + 'target_name': 'mojo_clipboard', + 'type': 'loadable_module', + 'dependencies': [ + '../base/base.gyp:base', + 'mojo_base.gyp:mojo_common_lib', + 'mojo_base.gyp:mojo_cpp_bindings', + 'mojo_base.gyp:mojo_utility', + 'mojo_base.gyp:mojo_application_chromium', + 'mojo_clipboard_bindings', + '<(mojo_system_for_loadable_module)', + ], + 'sources': [ + 'services/clipboard/clipboard_standalone_impl.cc', + 'services/clipboard/clipboard_standalone_impl.h', + 'services/clipboard/main.cc', + ], + }, + { + # GN version: //mojo/services/public/interfaces/clipboard + 'target_name': 'mojo_clipboard_bindings', + 'type': 'static_library', + 'sources': [ + 'services/public/interfaces/clipboard/clipboard.mojom', + ], + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'dependencies': [ + 'mojo_base.gyp:mojo_cpp_bindings', + ], + 'export_dependent_settings': [ + 'mojo_base.gyp:mojo_cpp_bindings', + ], + }, + { + # GN version: //mojo/services/clipboard:mojo_clipboard_unittests + 'target_name': 'mojo_clipboard_unittests', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:base', + '../base/base.gyp:test_support_base', + '../testing/gtest.gyp:gtest', + 'mojo_application_manager', + 'mojo_base.gyp:mojo_application_chromium', + 'mojo_base.gyp:mojo_run_all_unittests', + 'mojo_base.gyp:mojo_system_impl', + 'mojo_clipboard_bindings', + 'mojo_shell_test_support', + ], + 'sources': [ + 'services/clipboard/clipboard_standalone_unittest.cc', + ], + }, + { # GN version: //mojo/services/html_viewer 'target_name': 'mojo_html_viewer', 'type': 'loadable_module', @@ -23,6 +76,7 @@ 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_cpp_bindings', 'mojo_base.gyp:mojo_utility', + 'mojo_clipboard_bindings', 'mojo_cc_support', 'mojo_content_handler_bindings', 'mojo_gpu_bindings', @@ -47,6 +101,8 @@ 'services/html_viewer/html_viewer.cc', 'services/html_viewer/html_document_view.cc', 'services/html_viewer/html_document_view.h', + 'services/html_viewer/webclipboard_impl.cc', + 'services/html_viewer/webclipboard_impl.h', 'services/html_viewer/webcookiejar_impl.cc', 'services/html_viewer/webcookiejar_impl.h', 'services/html_viewer/webmediaplayer_factory.cc', diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h index 9b5aff8..0ef6261 100644 --- a/mojo/public/cpp/bindings/array.h +++ b/mojo/public/cpp/bindings/array.h @@ -68,6 +68,9 @@ class Array { bool is_null() const { return is_null_; } + ConstRefType front() const { return vec_.front(); } + RefType front() { return vec_.front(); } + size_t size() const { return vec_.size(); } ConstRefType at(size_t offset) const { return Traits::at(&vec_, offset); } diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn index ae16a08..90988ab 100644 --- a/mojo/services/BUILD.gn +++ b/mojo/services/BUILD.gn @@ -6,10 +6,12 @@ import("//build/config/ui.gni") group("services") { deps = [ + "//mojo/services/clipboard", "//mojo/services/gles2:bindings", "//mojo/services/html_viewer", "//mojo/services/native_viewport", "//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", diff --git a/mojo/services/clipboard/BUILD.gn b/mojo/services/clipboard/BUILD.gn new file mode 100644 index 0000000..5019a1a --- /dev/null +++ b/mojo/services/clipboard/BUILD.gn @@ -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. + +# GYP version: mojo/mojo_services.gypi:mojo_clipboard +component("clipboard") { + deps = [ + "//base", + "//mojo/common", + "//mojo/environment:chromium", + "//mojo/public/c/system:for_component", + "//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/environment:chromium", + "//mojo/services/public/interfaces/clipboard:clipboard", + "//mojo/shell:test_support", + "//mojo/system", + "//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..6909c10 --- /dev/null +++ b/mojo/services/clipboard/clipboard_standalone_impl.h @@ -0,0 +1,61 @@ +// 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) + MOJO_OVERRIDE; + virtual void GetAvailableMimeTypes( + Clipboard::Type clipboard_types, + const mojo::Callback<void(mojo::Array<mojo::String>)>& callback) + MOJO_OVERRIDE; + virtual void ReadMimeType( + Clipboard::Type clipboard_type, + const mojo::String& mime_type, + const mojo::Callback<void(mojo::Array<uint8_t>)>& callback) + MOJO_OVERRIDE; + virtual void WriteClipboardData(Clipboard::Type clipboard_type, + mojo::Array<MimeTypePairPtr> data) + MOJO_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..60838f1 --- /dev/null +++ b/mojo/services/clipboard/clipboard_standalone_unittest.cc @@ -0,0 +1,185 @@ +// 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 "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..423aca0 --- /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/html_viewer/BUILD.gn b/mojo/services/html_viewer/BUILD.gn index 3e640b9..22246ab 100644 --- a/mojo/services/html_viewer/BUILD.gn +++ b/mojo/services/html_viewer/BUILD.gn @@ -18,6 +18,8 @@ shared_library("html_viewer") { "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", @@ -53,6 +55,7 @@ shared_library("html_viewer") { "//mojo/public/cpp/bindings", "//mojo/public/cpp/utility", "//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/navigation", diff --git a/mojo/services/html_viewer/blink_basic_type_converters.cc b/mojo/services/html_viewer/blink_basic_type_converters.cc index 23b9e5f..eb52652 100644 --- a/mojo/services/html_viewer/blink_basic_type_converters.cc +++ b/mojo/services/html_viewer/blink_basic_type_converters.cc @@ -21,4 +21,14 @@ 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 index cb3b803..60ddeee 100644 --- a/mojo/services/html_viewer/blink_basic_type_converters.h +++ b/mojo/services/html_viewer/blink_basic_type_converters.h @@ -25,6 +25,10 @@ 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> > { diff --git a/mojo/services/html_viewer/blink_platform_impl.cc b/mojo/services/html_viewer/blink_platform_impl.cc index 1d19b83..315a850 100644 --- a/mojo/services/html_viewer/blink_platform_impl.cc +++ b/mojo/services/html_viewer/blink_platform_impl.cc @@ -11,6 +11,7 @@ #include "base/synchronization/waitable_event.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" @@ -59,6 +60,10 @@ BlinkPlatformImpl::BlinkPlatformImpl(ApplicationImpl* app) 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() { @@ -68,6 +73,10 @@ blink::WebCookieJar* BlinkPlatformImpl::cookieJar() { return cookie_jar_.get(); } +blink::WebClipboard* BlinkPlatformImpl::clipboard() { + return clipboard_.get(); +} + blink::WebMimeRegistry* BlinkPlatformImpl::mimeRegistry() { return &mime_registry_; } diff --git a/mojo/services/html_viewer/blink_platform_impl.h b/mojo/services/html_viewer/blink_platform_impl.h index aeae757..522f98d 100644 --- a/mojo/services/html_viewer/blink_platform_impl.h +++ b/mojo/services/html_viewer/blink_platform_impl.h @@ -18,6 +18,7 @@ namespace mojo { class ApplicationImpl; +class WebClipboardImpl; class WebCookieJarImpl; class BlinkPlatformImpl : public blink::Platform { @@ -27,6 +28,7 @@ class BlinkPlatformImpl : public blink::Platform { // blink::Platform methods: virtual blink::WebCookieJar* cookieJar(); + virtual blink::WebClipboard* clipboard(); virtual blink::WebMimeRegistry* mimeRegistry(); virtual blink::WebThemeEngine* themeEngine(); virtual blink::WebString defaultLocale(); @@ -78,6 +80,7 @@ class BlinkPlatformImpl : public blink::Platform { cc_blink::WebCompositorSupportImpl compositor_support_; WebThemeEngineImpl theme_engine_; scoped_ptr<WebCookieJarImpl> cookie_jar_; + scoped_ptr<WebClipboardImpl> clipboard_; WebMimeRegistryImpl mime_registry_; blink::WebScrollbarBehavior scrollbar_behavior_; 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/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..c1e1be4 --- /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; + 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) => (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) => (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, MimeTypePair[]? data); +}; + +} // module mojo |