diff options
25 files changed, 866 insertions, 2 deletions
diff --git a/components/font_service/BUILD.gn b/components/font_service/BUILD.gn new file mode 100644 index 0000000..0e6f5d4 --- /dev/null +++ b/components/font_service/BUILD.gn @@ -0,0 +1,36 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//mojo/public/mojo_application.gni") + +source_set("lib") { + sources = [ + "font_service_app.cc", + "font_service_app.h", + ] + + deps = [ + "//components/font_service/public/interfaces", + "//mojo/application/public/cpp", + "//mojo/platform_handle", + "//third_party/mojo/src/mojo/public/cpp/bindings", + ] + + public_deps = [ + "//skia", + ] +} + +mojo_native_application("font_service") { + sources = [ + "main.cc", + ] + + deps = [ + ":lib", + "//base", + "//mojo/application/public/cpp", + "//third_party/mojo/src/mojo/public/cpp/bindings:callback", + ] +} diff --git a/components/font_service/DEPS b/components/font_service/DEPS new file mode 100644 index 0000000..45a4dee --- /dev/null +++ b/components/font_service/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + "+mojo/application", + "+mojo/common", + "+mojo/message_pump", + "+mojo/platform_handle", + "+skia", + "+third_party/mojo/src/mojo/public", + "+third_party/skia/include", +] diff --git a/components/font_service/font_service_app.cc b/components/font_service/font_service_app.cc new file mode 100644 index 0000000..77f03a4 --- /dev/null +++ b/components/font_service/font_service_app.cc @@ -0,0 +1,123 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/font_service/font_service_app.h" + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "mojo/application/public/cpp/application_connection.h" +#include "mojo/platform_handle/platform_handle_functions.h" + +COMPILE_ASSERT(static_cast<uint32>(SkTypeface::kNormal) == + static_cast<uint32>(font_service::TYPEFACE_STYLE_NORMAL), + typeface_flags_should_match); +COMPILE_ASSERT(static_cast<uint32>(SkTypeface::kBold) == + static_cast<uint32>(font_service::TYPEFACE_STYLE_BOLD), + typeface_flags_should_match); +COMPILE_ASSERT(static_cast<uint32>(SkTypeface::kItalic) == + static_cast<uint32>(font_service::TYPEFACE_STYLE_ITALIC), + typeface_flags_should_match); +COMPILE_ASSERT( + static_cast<uint32>(SkTypeface::kBoldItalic) == + static_cast<uint32>(font_service::TYPEFACE_STYLE_BOLD_ITALIC), + typeface_flags_should_match); + +namespace { + +mojo::ScopedHandle GetHandleForPath(const base::FilePath& path) { + if (path.empty()) + return mojo::ScopedHandle(); + + mojo::ScopedHandle to_pass; + base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) { + LOG(WARNING) << "file not valid, path=" << path.value(); + return mojo::ScopedHandle(); + } + + MojoHandle mojo_handle; + MojoResult create_result = + MojoCreatePlatformHandleWrapper(file.TakePlatformFile(), &mojo_handle); + if (create_result != MOJO_RESULT_OK) { + LOG(WARNING) << "unable to create wrapper, path=" << path.value() + << "result=" << create_result; + return mojo::ScopedHandle(); + } + + return mojo::ScopedHandle(mojo::Handle(mojo_handle)).Pass(); +} + +} // namespace + +namespace font_service { + +FontServiceApp::FontServiceApp() {} + +FontServiceApp::~FontServiceApp() {} + +void FontServiceApp::Initialize(mojo::ApplicationImpl* app) {} + +bool FontServiceApp::ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) { + connection->AddService(this); + return true; +} + +void FontServiceApp::Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<FontService> request) { + bindings_.AddBinding(this, request.Pass()); +} + +void FontServiceApp::MatchFamilyName(const mojo::String& family_name, + TypefaceStyle requested_style, + const MatchFamilyNameCallback& callback) { + SkFontConfigInterface::FontIdentity result_identity; + SkString result_family; + SkTypeface::Style result_style; + SkFontConfigInterface* fc = + SkFontConfigInterface::GetSingletonDirectInterface(); + const bool r = fc->matchFamilyName( + family_name.data(), static_cast<SkTypeface::Style>(requested_style), + &result_identity, &result_family, &result_style); + + if (!r) { + callback.Run(nullptr, "", TYPEFACE_STYLE_NORMAL); + return; + } + + // Stash away the returned path, so we can give it an ID (index) + // which will later be given to us in a request to open the file. + int index = FindOrAddPath(result_identity.fString); + + FontIdentityPtr identity(FontIdentity::New()); + identity->id = static_cast<uint32_t>(index); + identity->ttc_index = result_identity.fTTCIndex; + identity->str_representation = result_identity.fString.c_str(); + + callback.Run(identity.Pass(), result_family.c_str(), + static_cast<TypefaceStyle>(result_style)); +} + +void FontServiceApp::OpenStream(uint32_t id_number, + const OpenStreamCallback& callback) { + mojo::ScopedHandle handle; + if (id_number < static_cast<uint32_t>(paths_.count())) { + handle = + GetHandleForPath(base::FilePath(paths_[id_number]->c_str())).Pass(); + } + + callback.Run(handle.Pass()); +} + +int FontServiceApp::FindOrAddPath(const SkString& path) { + int count = paths_.count(); + for (int i = 0; i < count; ++i) { + if (path == *paths_[i]) + return i; + } + *paths_.append() = new SkString(path); + return count; +} + +} // namespace font_service diff --git a/components/font_service/font_service_app.h b/components/font_service/font_service_app.h new file mode 100644 index 0000000..9eda20a --- /dev/null +++ b/components/font_service/font_service_app.h @@ -0,0 +1,53 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_FONT_SERVICE_FONT_SERVICE_APP_H_ +#define COMPONENTS_FONT_SERVICE_FONT_SERVICE_APP_H_ + +#include "components/font_service/public/interfaces/font_service.mojom.h" +#include "mojo/application/public/cpp/application_delegate.h" +#include "mojo/application/public/cpp/interface_factory.h" +#include "mojo/common/weak_binding_set.h" +#include "skia/ext/skia_utils_base.h" + +namespace font_service { + +class FontServiceApp : public mojo::ApplicationDelegate, + public mojo::InterfaceFactory<FontService>, + public FontService { + public: + FontServiceApp(); + ~FontServiceApp() override; + + private: + // ApplicationDelegate: + void Initialize(mojo::ApplicationImpl* app) override; + bool ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) override; + + // mojo::InterfaceFactory<FontService>: + void Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<FontService> request) override; + + // FontService: + void MatchFamilyName(const mojo::String& family_name, + TypefaceStyle requested_style, + const MatchFamilyNameCallback& callback) override; + void OpenStream(uint32_t id_number, + const OpenStreamCallback& callback) override; + + int FindOrAddPath(const SkString& path); + + mojo::WeakBindingSet<FontService> bindings_; + + // We don't want to leak paths to our callers; we thus enumerate the paths of + // fonts. + SkTDArray<SkString*> paths_; + + DISALLOW_COPY_AND_ASSIGN(FontServiceApp); +}; + +} // namespace font_service + +#endif // COMPONENTS_FONT_SERVICE_FONT_SERVICE_APP_H_ diff --git a/components/font_service/main.cc b/components/font_service/main.cc new file mode 100644 index 0000000..9b234e6 --- /dev/null +++ b/components/font_service/main.cc @@ -0,0 +1,12 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/font_service/font_service_app.h" +#include "mojo/application/public/cpp/application_runner.h" +#include "third_party/mojo/src/mojo/public/c/system/main.h" + +MojoResult MojoMain(MojoHandle shell_handle) { + mojo::ApplicationRunner runner(new font_service::FontServiceApp); + return runner.Run(shell_handle); +} diff --git a/components/font_service/public/cpp/BUILD.gn b/components/font_service/public/cpp/BUILD.gn new file mode 100644 index 0000000..52359b7 --- /dev/null +++ b/components/font_service/public/cpp/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("cpp") { + sources = [ + "font_loader.cc", + "font_loader.h", + "font_service_thread.cc", + "font_service_thread.h", + "mapped_font_file.cc", + "mapped_font_file.h", + ] + + deps = [ + "../interfaces", + "//base", + "//mojo/application/public/cpp", + "//mojo/application/public/interfaces", + "//mojo/common", + "//mojo/message_pump", + "//mojo/platform_handle", + "//skia", + "//third_party/mojo/src/mojo/public/cpp/bindings", + "//third_party/mojo/src/mojo/public/cpp/system", + ] +} diff --git a/components/font_service/public/cpp/font_loader.cc b/components/font_service/public/cpp/font_loader.cc new file mode 100644 index 0000000..8b63384 --- /dev/null +++ b/components/font_service/public/cpp/font_loader.cc @@ -0,0 +1,63 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/font_service/public/cpp/font_loader.h" + +#include "components/font_service/public/cpp/font_service_thread.h" +#include "mojo/application/public/cpp/application_impl.h" + +namespace font_service { + +FontLoader::FontLoader(mojo::ApplicationImpl* application_impl) { + mojo::URLRequestPtr request(mojo::URLRequest::New()); + request->url = mojo::String::From("mojo:font_service"); + FontServicePtr font_service; + application_impl->ConnectToService(request.Pass(), &font_service); + + thread_ = new internal::FontServiceThread(font_service.Pass()); +} + +FontLoader::~FontLoader() {} + +bool FontLoader::matchFamilyName(const char family_name[], + SkTypeface::Style requested, + FontIdentity* out_font_identifier, + SkString* out_family_name, + SkTypeface::Style* out_style) { + return thread_->MatchFamilyName(family_name, requested, out_font_identifier, + out_family_name, out_style); +} + +SkStreamAsset* FontLoader::openStream(const FontIdentity& identity) { + { + base::AutoLock lock(lock_); + auto mapped_font_files_it = mapped_font_files_.find(identity.fID); + if (mapped_font_files_it != mapped_font_files_.end()) + return mapped_font_files_it->second->CreateMemoryStream(); + } + + scoped_refptr<internal::MappedFontFile> mapped_font_file = + thread_->OpenStream(identity); + if (!mapped_font_file) + return nullptr; + + // Get notified with |mapped_font_file| is destroyed. + mapped_font_file->set_observer(this); + + { + base::AutoLock lock(lock_); + auto mapped_font_files_it = + mapped_font_files_.insert(std::make_pair(mapped_font_file->font_id(), + mapped_font_file.get())) + .first; + return mapped_font_files_it->second->CreateMemoryStream(); + } +} + +void FontLoader::OnMappedFontFileDestroyed(internal::MappedFontFile* f) { + base::AutoLock lock(lock_); + mapped_font_files_.erase(f->font_id()); +} + +} // namespace font_service diff --git a/components/font_service/public/cpp/font_loader.h b/components/font_service/public/cpp/font_loader.h new file mode 100644 index 0000000..38e82e1 --- /dev/null +++ b/components/font_service/public/cpp/font_loader.h @@ -0,0 +1,67 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_FONT_SERVICE_PUBLIC_CPP_FONT_LOADER_H_ +#define COMPONENTS_FONT_SERVICE_PUBLIC_CPP_FONT_LOADER_H_ + +#include "base/containers/hash_tables.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "components/font_service/public/cpp/mapped_font_file.h" +#include "components/font_service/public/interfaces/font_service.mojom.h" +#include "third_party/skia/include/core/SkStream.h" +#include "third_party/skia/include/core/SkTypeface.h" +#include "third_party/skia/include/ports/SkFontConfigInterface.h" + +namespace mojo { +class ApplicationImpl; +} + +namespace font_service { +namespace internal { +class FontServiceThread; +} + +// FontConfig implementation for Skia which proxies to the font service to get +// out of the sandbox. This methods of this class (as imposed by blink +// requirements) may be called on any thread. (Because of this restriction, +// also see the FontServiceThread class.) +// +// This is the mojo equivalent to content/common/font_config_ipc_linux.h +class FontLoader : public SkFontConfigInterface, + public internal::MappedFontFile::Observer { + public: + explicit FontLoader(mojo::ApplicationImpl* application_impl); + ~FontLoader() override; + + // SkFontConfigInterface: + bool matchFamilyName(const char family_name[], + SkTypeface::Style requested, + FontIdentity* out_font_identifier, + SkString* out_family_name, + SkTypeface::Style* out_style) override; + SkStreamAsset* openStream(const FontIdentity& identity) override; + + private: + // internal::MappedFontFile::Observer: + void OnMappedFontFileDestroyed(internal::MappedFontFile* f) override; + + // Thread to own the mojo message pipe. Because FontLoader can be called on + // multiple threads, we create a dedicated thread to send and receive mojo + // message calls. + scoped_refptr<internal::FontServiceThread> thread_; + + // Lock preventing multiple threads from opening font file and accessing + // |mapped_font_files_| map at the same time. + base::Lock lock_; + + // Maps font identity ID to the memory-mapped file with font data. + base::hash_map<uint32_t, internal::MappedFontFile*> mapped_font_files_; + + DISALLOW_COPY_AND_ASSIGN(FontLoader); +}; + +} // namespace font_service + +#endif // COMPONENTS_FONT_SERVICE_PUBLIC_CPP_FONT_LOADER_H_ diff --git a/components/font_service/public/cpp/font_service_thread.cc b/components/font_service/public/cpp/font_service_thread.cc new file mode 100644 index 0000000..ca2ab05 --- /dev/null +++ b/components/font_service/public/cpp/font_service_thread.cc @@ -0,0 +1,158 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/font_service/public/cpp/font_service_thread.h" + +#include "base/bind.h" +#include "base/files/file.h" +#include "base/synchronization/waitable_event.h" +#include "components/font_service/public/cpp/mapped_font_file.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/platform_handle/platform_handle_functions.h" + +namespace font_service { +namespace internal { + +namespace { +const char kFontThreadName[] = "Font_Proxy_Thread"; +} // namespace + +FontServiceThread::FontServiceThread(FontServicePtr font_service) + : base::Thread(kFontThreadName), + font_service_info_(font_service.PassInterface().Pass()) { + base::Thread::Options options; + options.message_pump_factory = + base::Bind(&mojo::common::MessagePumpMojo::Create); + StartWithOptions(options); +} + +bool FontServiceThread::MatchFamilyName( + const char family_name[], + SkTypeface::Style requested_style, + SkFontConfigInterface::FontIdentity* out_font_identity, + SkString* out_family_name, + SkTypeface::Style* out_style) { + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); + + bool out_valid = false; + // This proxies to the other thread, which proxies to mojo. Only on the reply + // from mojo do we return from this. + base::WaitableEvent done_event(false, false); + task_runner()->PostTask( + FROM_HERE, + base::Bind(&FontServiceThread::MatchFamilyNameImpl, this, &done_event, + family_name, requested_style, &out_valid, out_font_identity, + out_family_name, out_style)); + done_event.Wait(); + + return out_valid; +} + +scoped_refptr<MappedFontFile> FontServiceThread::OpenStream( + const SkFontConfigInterface::FontIdentity& identity) { + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); + + base::File stream_file; + // This proxies to the other thread, which proxies to mojo. Only on the reply + // from mojo do we return from this. + base::WaitableEvent done_event(false, false); + task_runner()->PostTask(FROM_HERE, + base::Bind(&FontServiceThread::OpenStreamImpl, this, + &done_event, &stream_file, identity.fID)); + done_event.Wait(); + + if (!stream_file.IsValid()) { + NOTREACHED(); + return nullptr; + } + + // Converts the file to out internal type. + scoped_refptr<MappedFontFile> mapped_font_file = + new MappedFontFile(identity.fID); + if (!mapped_font_file->Initialize(stream_file.Pass())) + return nullptr; + + return mapped_font_file; +} + +FontServiceThread::~FontServiceThread() { + Stop(); +} + +void FontServiceThread::MatchFamilyNameImpl( + base::WaitableEvent* done_event, + const char family_name[], + SkTypeface::Style requested_style, + bool* out_valid, + SkFontConfigInterface::FontIdentity* out_font_identity, + SkString* out_family_name, + SkTypeface::Style* out_style) { + DCHECK_EQ(GetThreadId(), base::PlatformThread::CurrentId()); + + font_service_->MatchFamilyName( + mojo::String(family_name), static_cast<TypefaceStyle>(requested_style), + base::Bind(&FontServiceThread::OnMatchFamilyNameComplete, this, + done_event, out_valid, out_font_identity, out_family_name, + out_style)); +} + +void FontServiceThread::OnMatchFamilyNameComplete( + base::WaitableEvent* done_event, + bool* out_valid, + SkFontConfigInterface::FontIdentity* out_font_identity, + SkString* out_family_name, + SkTypeface::Style* out_style, + FontIdentityPtr font_identity, + mojo::String family_name, + TypefaceStyle style) { + DCHECK_EQ(GetThreadId(), base::PlatformThread::CurrentId()); + + *out_valid = font_identity; + if (font_identity) { + out_font_identity->fID = font_identity->id; + out_font_identity->fTTCIndex = font_identity->ttc_index; + out_font_identity->fString = font_identity->str_representation.data(); + // TODO(erg): fStyle isn't set. This is rather odd, however it matches the + // behaviour of the current Linux IPC version. + + *out_family_name = family_name.data(); + *out_style = static_cast<SkTypeface::Style>(style); + } + + done_event->Signal(); +} + +void FontServiceThread::OpenStreamImpl(base::WaitableEvent* done_event, + base::File* output_file, + const uint32_t id_number) { + DCHECK_EQ(GetThreadId(), base::PlatformThread::CurrentId()); + + font_service_->OpenStream( + id_number, base::Bind(&FontServiceThread::OnOpenStreamComplete, this, + done_event, output_file)); +} + +void FontServiceThread::OnOpenStreamComplete(base::WaitableEvent* done_event, + base::File* output_file, + mojo::ScopedHandle handle) { + if (handle.is_valid()) { + MojoPlatformHandle platform_handle; + CHECK(MojoExtractPlatformHandle(handle.release().value(), + &platform_handle) == MOJO_RESULT_OK); + *output_file = base::File(platform_handle).Pass(); + } + + done_event->Signal(); +} + +void FontServiceThread::Init() { + font_service_.Bind(font_service_info_.Pass()); +} + +void FontServiceThread::CleanUp() { + font_service_.reset(); +} + +} // namespace internal +} // namespace font_service diff --git a/components/font_service/public/cpp/font_service_thread.h b/components/font_service/public/cpp/font_service_thread.h new file mode 100644 index 0000000..6a47540 --- /dev/null +++ b/components/font_service/public/cpp/font_service_thread.h @@ -0,0 +1,97 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_FONT_SERVICE_PUBLIC_CPP_FONT_SERVICE_THREAD_H_ +#define COMPONENTS_FONT_SERVICE_PUBLIC_CPP_FONT_SERVICE_THREAD_H_ + +#include "base/files/file.h" +#include "base/threading/thread.h" +#include "components/font_service/public/interfaces/font_service.mojom.h" +#include "third_party/skia/include/core/SkStream.h" +#include "third_party/skia/include/core/SkTypeface.h" +#include "third_party/skia/include/ports/SkFontConfigInterface.h" + +namespace font_service { +namespace internal { + +class MappedFontFile; + +// The thread which services font requests. +// +// The SkFontConfigInterface is a global singleton which can be accessed from +// multiple threads. However, mojo pipes are bound to a single thread. Because +// of this mismatch, we create a thread which owns the mojo pipe, sends and +// receives messages. The multiple threads which call through FontLoader class +// do blocking message calls to this thread. +class FontServiceThread : public base::Thread, + public base::RefCountedThreadSafe<FontServiceThread> { + public: + explicit FontServiceThread(FontServicePtr font_service); + + // These methods are proxies which run on your thread, post a blocking task + // to the FontServiceThread, and wait on an event signaled from the callback. + bool MatchFamilyName(const char family_name[], + SkTypeface::Style requested_style, + SkFontConfigInterface::FontIdentity* out_font_identity, + SkString* out_family_name, + SkTypeface::Style* out_style); + scoped_refptr<MappedFontFile> OpenStream( + const SkFontConfigInterface::FontIdentity& identity); + + private: + friend class base::RefCountedThreadSafe<FontServiceThread>; + ~FontServiceThread() override; + + // Methods which run on the FontServiceThread. The public MatchFamilyName + // calls this method, this method calls the mojo interface, and sets up the + // callback to OnMatchFamilyNameComplete. + void MatchFamilyNameImpl( + base::WaitableEvent* done_event, + const char family_name[], + SkTypeface::Style requested_style, + bool* out_valid, + SkFontConfigInterface::FontIdentity* out_font_identity, + SkString* out_family_name, + SkTypeface::Style* out_style); + + // Called on the FontServiceThread in response to receiving a message from + // our MatchFamily mojo IPC. This writes the data returned by mojo, and then + // signals |done_event| to wake up the other thread. + void OnMatchFamilyNameComplete( + base::WaitableEvent* done_event, + bool* out_valid, + SkFontConfigInterface::FontIdentity* out_font_identity, + SkString* out_family_name, + SkTypeface::Style* out_style, + FontIdentityPtr font_identity, + mojo::String family_name, + TypefaceStyle style); + + // Implementation of OpenStream; same threading restrictions as MatchFamily. + void OpenStreamImpl(base::WaitableEvent* done_event, + base::File* output_file, + const uint32_t id_number); + void OnOpenStreamComplete(base::WaitableEvent* done_event, + base::File* output_file, + mojo::ScopedHandle handle); + + // base::Thread + void Init() override; + void CleanUp() override; + + // This member is used to safely pass data from one thread to another. It is + // set in the constructor and is consumed in Init(). + mojo::InterfacePtrInfo<FontService> font_service_info_; + + // This member is set in Init(). It takes |font_service_info_|, which is + // non-thread bound, and binds it to the newly created thread. + mojo::InterfacePtr<FontService> font_service_; + + DISALLOW_COPY_AND_ASSIGN(FontServiceThread); +}; + +} // namespace internal +} // namespace font_service + +#endif // COMPONENTS_FONT_SERVICE_PUBLIC_CPP_FONT_SERVICE_THREAD_H_ diff --git a/components/font_service/public/cpp/mapped_font_file.cc b/components/font_service/public/cpp/mapped_font_file.cc new file mode 100644 index 0000000..98deaaf --- /dev/null +++ b/components/font_service/public/cpp/mapped_font_file.cc @@ -0,0 +1,48 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/font_service/public/cpp/mapped_font_file.h" + +#include "base/files/file_util.h" +#include "base/threading/thread_restrictions.h" +#include "skia/ext/refptr.h" +#include "skia/ext/skia_utils_base.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkStream.h" + +namespace font_service { +namespace internal { + +MappedFontFile::MappedFontFile(uint32_t font_id) + : font_id_(font_id), observer_(nullptr) {} + +bool MappedFontFile::Initialize(base::File file) { + base::ThreadRestrictions::ScopedAllowIO allow_mmap; + return mapped_font_file_.Initialize(file.Pass()); +} + +SkMemoryStream* MappedFontFile::CreateMemoryStream() { + DCHECK(mapped_font_file_.IsValid()); + auto data = skia::AdoptRef( + SkData::NewWithProc(mapped_font_file_.data(), mapped_font_file_.length(), + &MappedFontFile::ReleaseProc, this)); + if (!data) + return nullptr; + AddRef(); + return new SkMemoryStream(data.get()); +} + +MappedFontFile::~MappedFontFile() { + if (observer_) + observer_->OnMappedFontFileDestroyed(this); +} + +// static +void MappedFontFile::ReleaseProc(const void* ptr, void* context) { + base::ThreadRestrictions::ScopedAllowIO allow_munmap; + static_cast<MappedFontFile*>(context)->Release(); +} + +} // namespace internal +} // namespace font_service diff --git a/components/font_service/public/cpp/mapped_font_file.h b/components/font_service/public/cpp/mapped_font_file.h new file mode 100644 index 0000000..c860fed --- /dev/null +++ b/components/font_service/public/cpp/mapped_font_file.h @@ -0,0 +1,54 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_FONT_SERVICE_PUBLIC_CPP_MAPPED_FONT_FILE_H_ +#define COMPONENTS_FONT_SERVICE_PUBLIC_CPP_MAPPED_FONT_FILE_H_ + +#include "base/files/file.h" +#include "base/files/memory_mapped_file.h" +#include "base/memory/ref_counted.h" +#include "third_party/skia/include/core/SkStream.h" + +namespace font_service { +namespace internal { + +// Owns the memory of the mmaped file that we get back from the font_service. +// +// This class is an implementation detail and shouldn't be used by consumers. +class MappedFontFile : public base::RefCountedThreadSafe<MappedFontFile> { + public: + class Observer { + public: + ~Observer() {} + + // Called when a MappedFontFile is destroyed. + virtual void OnMappedFontFileDestroyed(MappedFontFile* f) = 0; + }; + + explicit MappedFontFile(uint32_t font_id); + + uint32_t font_id() const { return font_id_; } + + void set_observer(Observer* observer) { observer_ = observer; } + + bool Initialize(base::File file); + + SkMemoryStream* CreateMemoryStream(); + + private: + friend class base::RefCountedThreadSafe<MappedFontFile>; + + ~MappedFontFile(); + + static void ReleaseProc(const void* ptr, void* context); + + uint32_t font_id_; + base::MemoryMappedFile mapped_font_file_; + Observer* observer_; +}; + +} // namespace internal +} // namespace font_service + +#endif // COMPONENTS_FONT_SERVICE_PUBLIC_CPP_MAPPED_FONT_FILE_H_ diff --git a/components/font_service/public/interfaces/BUILD.gn b/components/font_service/public/interfaces/BUILD.gn new file mode 100644 index 0000000..db1a2dd --- /dev/null +++ b/components/font_service/public/interfaces/BUILD.gn @@ -0,0 +1,11 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "font_service.mojom", + ] +} diff --git a/components/font_service/public/interfaces/font_service.mojom b/components/font_service/public/interfaces/font_service.mojom new file mode 100644 index 0000000..3d53b00 --- /dev/null +++ b/components/font_service/public/interfaces/font_service.mojom @@ -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. + +module font_service; + +enum TypefaceStyle { + NORMAL = 0, + BOLD = 0x01, + ITALIC = 0x02, + BOLD_ITALIC = 0x03 +}; + +// A reference to specific font on the font service. +struct FontIdentity { + uint32 id; + int32 ttc_index; + // TODO(erg): So the string is supposed to be a path. However, the current + // chrome code goes out of its way to send this to the renderer process, and + // it is passed to blink, even though the openStream() IPC in chrome uses the + // id number instead. Do more investigation about what we need to do to plug + // this system path leak. + string str_representation; +}; + +// Loads and resolves fonts. +// +// We still need to load fonts from within a sandboxed process. We set +// up a service to match fonts and load them, +interface FontService { + // Returns the best match for |family_name| and |style|. On error, returns a + // null |identity|. + MatchFamilyName(string family_name, TypefaceStyle style) => + (FontIdentity? identity, string family_name, TypefaceStyle style); + + // Returns a handle to the raw font specified by |id_number|. + OpenStream(uint32 id_number) => (handle font_handle); +}; diff --git a/components/html_viewer/BUILD.gn b/components/html_viewer/BUILD.gn index dcbdaf2..ab49118 100644 --- a/components/html_viewer/BUILD.gn +++ b/components/html_viewer/BUILD.gn @@ -185,6 +185,10 @@ source_set("lib") { sources += [ "html_viewer_version.rc" ] } + if (is_linux && !is_android) { + deps += [ "//components/font_service/public/cpp" ] + } + data_deps = [ "//components/clipboard", "//components/view_manager", @@ -316,4 +320,8 @@ mojo_native_application("apptests") { ":html_viewer", "//components/view_manager", ] + + if (is_linux && !is_android) { + data_deps += [ "//components/font_service" ] + } } diff --git a/components/html_viewer/DEPS b/components/html_viewer/DEPS index 8355e5b..4f2c54e 100644 --- a/components/html_viewer/DEPS +++ b/components/html_viewer/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+cc", "+components/clipboard", "+components/devtools_service/public", + "+components/font_service/public", "+components/gpu", "+components/message_port", "+components/mime_util", diff --git a/components/html_viewer/global_state.cc b/components/html_viewer/global_state.cc index ec0f570..870c4e7 100644 --- a/components/html_viewer/global_state.cc +++ b/components/html_viewer/global_state.cc @@ -22,6 +22,10 @@ #include "ui/mojo/init/ui_init.h" #include "v8/include/v8.h" +#if defined(OS_LINUX) && !defined(OS_ANDROID) +#include "components/font_service/public/cpp/font_loader.h" +#endif + namespace html_viewer { namespace { @@ -93,6 +97,10 @@ void GlobalState::InitIfNecessary(const gfx::Size& screen_size_in_pixels, return; } +#if defined(OS_LINUX) && !defined(OS_ANDROID) + SkFontConfigInterface::SetGlobal(new font_service::FontLoader(app_)); +#endif + ui_init_.reset( new ui::mojo::UIInit(screen_size_in_pixels, device_pixel_ratio)); base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_); diff --git a/components/html_viewer/html_viewer_main.cc b/components/html_viewer/html_viewer_main.cc index 9afd58d..79349639 100644 --- a/components/html_viewer/html_viewer_main.cc +++ b/components/html_viewer/html_viewer_main.cc @@ -6,6 +6,32 @@ #include "mojo/application/public/cpp/application_runner.h" #include "third_party/mojo/src/mojo/public/c/system/main.h" +// TODO(erg): Much of this will be the same between mojo applications. Maybe we +// could centralize this code? +#if defined(OS_LINUX) && !defined(OS_ANDROID) +#include "base/rand_util.h" +#include "base/sys_info.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" + +// TODO(erg): Much of this was coppied from zygote_main_linux.cc +extern "C" { +void __attribute__((visibility("default"))) MojoSandboxWarm() { + base::RandUint64(); + base::SysInfo::AmountOfPhysicalMemory(); + base::SysInfo::MaxSharedMemorySize(); + base::SysInfo::NumberOfProcessors(); + + // ICU DateFormat class (used in base/time_format.cc) needs to get the + // Olson timezone ID by accessing the zoneinfo files on disk. After + // TimeZone::createDefault is called once here, the timezone ID is + // cached and there's no more need to access the file system. + scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault()); + + // TODO(erg): Perform OpenSSL warmup; it wants access to /dev/urandom. +} +} +#endif // defined(OS_LINUX) && !defined(OS_ANDROID) + MojoResult MojoMain(MojoHandle shell_handle) { mojo::ApplicationRunner runner(new html_viewer::HTMLViewer); return runner.Run(shell_handle); diff --git a/mandoline/app/core_services_initialization.cc b/mandoline/app/core_services_initialization.cc index 304fe1e..7f1dd12 100644 --- a/mandoline/app/core_services_initialization.cc +++ b/mandoline/app/core_services_initialization.cc @@ -16,6 +16,10 @@ void InitCoreServicesForContext(mojo::runner::Context* context) { GURL("mojo:clipboard"), GURL("mojo:core_services"), "Sandboxed Core"); manager->RegisterApplicationPackageAlias(GURL("mojo:filesystem"), GURL("mojo:core_services"), "Files"); +#if defined(OS_LINUX) && !defined(OS_ANDROID) + manager->RegisterApplicationPackageAlias(GURL("mojo:font_service"), + GURL("mojo:core_services"), "Files"); +#endif #if !defined(OS_ANDROID) // On Android, these are Java apps which are loaded in the shell process. manager->RegisterApplicationPackageAlias(GURL("mojo:view_manager"), diff --git a/mandoline/services/core_services/BUILD.gn b/mandoline/services/core_services/BUILD.gn index 2a34b61..cf640e6 100644 --- a/mandoline/services/core_services/BUILD.gn +++ b/mandoline/services/core_services/BUILD.gn @@ -60,6 +60,10 @@ source_set("sources") { ] } + if (is_linux && !is_android) { + deps += [ "//components/font_service:lib" ] + } + if (use_aura) { deps += [ "//components/view_manager/public/cpp", diff --git a/mandoline/services/core_services/DEPS b/mandoline/services/core_services/DEPS index 90dcddc..ca0908aa 100644 --- a/mandoline/services/core_services/DEPS +++ b/mandoline/services/core_services/DEPS @@ -1,6 +1,7 @@ include_rules = [ "+components/clipboard", "+components/filesystem", + "+components/font_service", "+components/resource_provider", "+components/view_manager", "+mojo/application", diff --git a/mandoline/services/core_services/core_services_application_delegate.cc b/mandoline/services/core_services/core_services_application_delegate.cc index 7805164..d3ef946 100644 --- a/mandoline/services/core_services/core_services_application_delegate.cc +++ b/mandoline/services/core_services/core_services_application_delegate.cc @@ -28,6 +28,10 @@ #include "mojo/services/network/network_service_delegate.h" #endif +#if defined(OS_LINUX) && !defined(OS_ANDROID) +#include "components/font_service/font_service_app.h" +#endif + namespace core_services { // A helper class for hosting a mojo::ApplicationImpl on its own thread. @@ -130,6 +134,10 @@ void CoreServicesApplicationDelegate::StartApplication( delegate.reset(new clipboard::ClipboardApplicationDelegate); } else if (url == "mojo://filesystem/") { delegate.reset(new filesystem::FileSystemApp); +#if defined(OS_LINUX) && !defined(OS_ANDROID) + } else if (url == "mojo://font_service/") { + delegate.reset(new font_service::FontServiceApp); +#endif } else if (url == "mojo://surfaces_service/") { delegate.reset(new surfaces::SurfacesServiceApplication); } else if (url == "mojo://tracing/") { diff --git a/mojo/runner/child_process.cc b/mojo/runner/child_process.cc index eda7829..85ce4b3 100644 --- a/mojo/runner/child_process.cc +++ b/mojo/runner/child_process.cc @@ -310,7 +310,13 @@ int ChildProcessMain() { if (sandbox_warm) sandbox_warm(); + // TODO(erg,jln): Allowing access to all of /dev/shm/ makes it easy to + // spy on other shared memory using processes. This is a temporary hack + // so that we have some sandbox until we have proper shared memory + // support integrated into mojo. std::vector<BrokerFilePermission> permissions; + permissions.push_back( + BrokerFilePermission::ReadWriteCreateUnlinkRecursive("/dev/shm/")); sandbox.reset(new mandoline::LinuxSandbox(permissions)); sandbox->Warmup(); sandbox->EngageNamespaceSandbox(); diff --git a/mojo/runner/linux_sandbox.cc b/mojo/runner/linux_sandbox.cc index 06165b2..fb6af97 100644 --- a/mojo/runner/linux_sandbox.cc +++ b/mojo/runner/linux_sandbox.cc @@ -92,8 +92,8 @@ class SandboxPolicy : public sandbox::bpf_dsl::Policy { LinuxSandbox::LinuxSandbox(const std::vector<BrokerFilePermission>& permissions) : broker_(new sandbox::syscall_broker::BrokerProcess(EPERM, permissions)) { - broker_->Init( - base::Bind<bool (*)()>(&sandbox::Credentials::DropAllCapabilities)); + CHECK(broker_->Init( + base::Bind<bool (*)()>(&sandbox::Credentials::DropAllCapabilities))); policy_.reset(new SandboxPolicy(broker_.get())); } diff --git a/mojo/shell/application_manager.cc b/mojo/shell/application_manager.cc index 17de9de..bbe68cf3 100644 --- a/mojo/shell/application_manager.cc +++ b/mojo/shell/application_manager.cc @@ -383,6 +383,8 @@ void ApplicationManager::HandleFetchCallback( bool start_sandboxed = false; if (app_url == GURL("mojo://core_services/") && qualifier == "Sandboxed Core") start_sandboxed = true; + else if (app_url == GURL("mojo://html_viewer/")) + start_sandboxed = true; fetcher->AsPath(blocking_pool_, base::Bind(&ApplicationManager::RunNativeApplication, |