diff options
-rw-r--r-- | base/process_util_linux.cc | 5 | ||||
-rw-r--r-- | chrome/app/chrome_dll_main.cc | 14 | ||||
-rw-r--r-- | chrome/app/chrome_main_uitest.cc | 5 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_sandbox_host_linux.cc | 226 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_sandbox_host_linux.h | 34 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.cc | 6 | ||||
-rw-r--r-- | chrome/browser/zygote_main_linux.cc | 57 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/common/chrome_descriptors.h | 1 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig.cpp | 237 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_control.h | 26 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_direct.cpp | 180 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_direct.h | 44 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_impl.h | 60 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_ipc.cpp | 144 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_ipc.h | 74 | ||||
-rwxr-xr-x | skia/skia.gyp | 8 |
17 files changed, 1103 insertions, 20 deletions
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index 7560257..a5337b4 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -102,6 +102,11 @@ bool LaunchApp(const std::vector<std::string>& argv, if (!ShuffleFileDescriptors(fd_shuffle)) exit(127); + // If we are using the SUID sandbox, it sets a magic environment variable + // ("SBX_D"), so we remove that variable from the environment here on the + // off chance that it's already set. + unsetenv("SBX_D"); + CloseSuperfluousFds(fd_shuffle); scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index 4759d02..c22d05c 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -425,20 +425,6 @@ int ChromeMain(int argc, const char** argv) { InitCrashReporter(); #endif -#if defined(OS_POSIX) - // Bug 11776: we mistakenly created directories world-readable. - // Fix old instances of these directories manually. - // TODO(evanm): remove this code in a month or two. - if (user_data_dir.empty()) { - FilePath fix_dir; - CHECK(PathService::Get(chrome::DIR_USER_DATA, &fix_dir)); - struct stat statbuf; - CHECK(stat(fix_dir.value().c_str(), &statbuf) == 0); - if ((statbuf.st_mode & 0077) != 0) - CHECK(chmod(fix_dir.value().c_str(), 0700) == 0); - } -#endif - bool single_process = #if defined (GOOGLE_CHROME_BUILD) // This is an unsupported and not fully tested mode, so don't enable it for diff --git a/chrome/app/chrome_main_uitest.cc b/chrome/app/chrome_main_uitest.cc index f229751..ea0bbc7 100644 --- a/chrome/app/chrome_main_uitest.cc +++ b/chrome/app/chrome_main_uitest.cc @@ -19,8 +19,9 @@ TEST_F(ChromeMainTest, AppLaunch) { EXPECT_EQ(1, UITest::GetBrowserProcessCount()); } else { #if defined(OS_LINUX) - // On Linux we'll have three processes: browser, renderer and zygote. - EXPECT_EQ(3, UITest::GetBrowserProcessCount()); + // On Linux we'll have four processes: browser, renderer, zygote and + // sandbox helper. + EXPECT_EQ(4, UITest::GetBrowserProcessCount()); #else // We should have two instances of the browser process alive - // one is the Browser and the other is the Renderer. diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.cc b/chrome/browser/renderer_host/render_sandbox_host_linux.cc new file mode 100644 index 0000000..c0ca6f6 --- /dev/null +++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2009 The Chromium 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 "chrome/browser/renderer_host/render_sandbox_host_linux.h" + +#include <stdint.h> +#include <unistd.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/poll.h> + +#include "base/eintr_wrapper.h" +#include "base/process_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/pickle.h" +#include "base/unix_domain_socket_posix.h" + +#include "SkFontHost_fontconfig_direct.h" +#include "SkFontHost_fontconfig_ipc.h" + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +// BEWARE: code in this file run across *processes* (not just threads). + +// This code runs in a child process +class SandboxIPCProcess { + public: + // lifeline_fd: this is the read end of a pipe which the browser process + // holds the other end of. If the browser process dies, it's descriptors are + // closed and we will noticed an EOF on the pipe. That's our signal to exit. + // browser_socket: the 'browser's end of the sandbox IPC socketpair. From the + // point of view of the renderer's, it's talking to the browser but this + // object actually services the requests. + SandboxIPCProcess(int lifeline_fd, int browser_socket) + : lifeline_fd_(lifeline_fd), + browser_socket_(browser_socket), + font_config_(new FontConfigDirect()) { + base::InjectiveMultimap multimap; + multimap.push_back(base::InjectionArc(0, lifeline_fd, false)); + multimap.push_back(base::InjectionArc(0, browser_socket, false)); + + base::CloseSuperfluousFds(multimap); + } + + void Run() { + struct pollfd pfds[2]; + pfds[0].fd = lifeline_fd_; + pfds[0].events = POLLIN; + pfds[1].fd = browser_socket_; + pfds[1].events = POLLIN; + + bool failed_polls = 0; + for (;;) { + const int r = HANDLE_EINTR(poll(pfds, 2, -1)); + if (r < 1) { + LOG(WARNING) << "poll errno:" << errno; + if (failed_polls++ == 3) { + LOG(FATAL) << "poll failing. Sandbox host aborting."; + return; + } + continue; + } + + failed_polls = 0; + + if (pfds[0].revents) { + // our parent died so we should too. + _exit(0); + } + + if (pfds[1].revents) { + HandleRequestFromRenderer(browser_socket_); + } + } + } + + private: + // --------------------------------------------------------------------------- + // Requests from the renderer... + + void HandleRequestFromRenderer(int fd) { + std::vector<int> fds; + static const unsigned kMaxMessageLength = 2048; + char buf[kMaxMessageLength]; + const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds); + if (len == -1) + return; + if (fds.size() == 0) + return; + + Pickle pickle(buf, len); + void* iter = NULL; + + int kind; + if (!pickle.ReadInt(&iter, &kind)) + goto error; + + if (kind == FontConfigIPC::METHOD_MATCH) { + HandleFontMatchRequest(fd, pickle, iter, fds); + } else if (kind == FontConfigIPC::METHOD_OPEN) { + HandleFontOpenRequest(fd, pickle, iter, fds); + } + + error: + for (std::vector<int>::const_iterator + i = fds.begin(); i != fds.end(); ++i) { + close(*i); + } + } + + void HandleFontMatchRequest(int fd, Pickle& pickle, void* iter, + std::vector<int>& fds) { + bool fileid_valid; + uint32_t fileid; + bool is_bold, is_italic; + std::string family; + + if (!pickle.ReadBool(&iter, &fileid_valid)) + return; + if (fileid_valid) { + if (!pickle.ReadUInt32(&iter, &fileid)) + return; + } + if (!pickle.ReadBool(&iter, &is_bold) || + !pickle.ReadBool(&iter, &is_italic) || + !pickle.ReadString(&iter, &family)) { + return; + } + + std::string result_family; + unsigned result_fileid; + + const bool r = font_config_->Match( + &result_family, &result_fileid, fileid_valid, fileid, family, is_bold, + is_italic); + + Pickle reply; + if (!r) { + reply.WriteBool(false); + } else { + reply.WriteBool(true); + reply.WriteUInt32(result_fileid); + reply.WriteString(result_family); + } + SendRendererReply(fds, reply, -1); + } + + void HandleFontOpenRequest(int fd, Pickle& pickle, void* iter, + std::vector<int>& fds) { + uint32_t fileid; + if (!pickle.ReadUInt32(&iter, &fileid)) + return; + const int result_fd = font_config_->Open(fileid); + + Pickle reply; + if (result_fd == -1) { + reply.WriteBool(false); + } else { + reply.WriteBool(true); + } + + SendRendererReply(fds, reply, result_fd); + } + + void SendRendererReply(const std::vector<int>& fds, const Pickle& reply, + int reply_fd) { + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + struct iovec iov = {const_cast<void*>(reply.data()), reply.size()}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char control_buffer[CMSG_SPACE(sizeof(int))]; + + if (reply_fd != -1) { + struct cmsghdr *cmsg; + + msg.msg_control = control_buffer; + msg.msg_controllen = sizeof(control_buffer); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(int)); + msg.msg_controllen = cmsg->cmsg_len; + } + + HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT)); + } + + // --------------------------------------------------------------------------- + + const int lifeline_fd_; + const int browser_socket_; + FontConfigDirect* const font_config_; +}; + +// ----------------------------------------------------------------------------- + +// Runs on the main thread at startup. +RenderSandboxHostLinux::RenderSandboxHostLinux() { + int fds[2]; + CHECK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0); + + renderer_socket_ = fds[0]; + const int browser_socket = fds[1]; + + int pipefds[2]; + CHECK(0 == pipe(pipefds)); + const int child_lifeline_fd = pipefds[0]; + childs_lifeline_fd_ = pipefds[1]; + + const pid_t child = fork(); + if (child == 0) { + SandboxIPCProcess handler(child_lifeline_fd, browser_socket); + handler.Run(); + _exit(0); + } +} + +RenderSandboxHostLinux::~RenderSandboxHostLinux() { + HANDLE_EINTR(close(renderer_socket_)); + HANDLE_EINTR(close(childs_lifeline_fd_)); +} diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.h b/chrome/browser/renderer_host/render_sandbox_host_linux.h new file mode 100644 index 0000000..43fa447 --- /dev/null +++ b/chrome/browser/renderer_host/render_sandbox_host_linux.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_ + +#include "base/singleton.h" +#include "base/thread.h" +#include "base/message_loop.h" + +// This is a singleton object which handles sandbox requests from the +// renderers. +class RenderSandboxHostLinux { + public: + // Get the file descriptor which renderers should be given in order to signal + // crashes to the browser. + int GetRendererSocket() const { return renderer_socket_; } + + private: + friend struct DefaultSingletonTraits<RenderSandboxHostLinux>; + // This object must be constructed on the main thread. + RenderSandboxHostLinux(); + ~RenderSandboxHostLinux(); + + int renderer_socket_; + int childs_lifeline_fd_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderSandboxHostLinux); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_ diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc index f56e6e93..1c582a3 100644 --- a/chrome/browser/zygote_host_linux.cc +++ b/chrome/browser/zygote_host_linux.cc @@ -16,6 +16,7 @@ #include "base/process_util.h" #include "base/unix_domain_socket_posix.h" +#include "chrome/browser/renderer_host/render_sandbox_host_linux.h" #include "chrome/common/chrome_switches.h" ZygoteHost::ZygoteHost() { @@ -38,6 +39,11 @@ ZygoteHost::ZygoteHost() { cmd_line.PrependWrapper(prefix); } + // Start up the sandbox host process and get the file descriptor for the + // renderers to talk to it. + const int sfd = Singleton<RenderSandboxHostLinux>()->GetRendererSocket(); + fds_to_map.push_back(std::make_pair(sfd, 4)); + base::ProcessHandle process; base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process); CHECK(process != -1) << "Failed to launch zygote process"; diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc index d450c39..6d15c0f 100644 --- a/chrome/browser/zygote_main_linux.cc +++ b/chrome/browser/zygote_main_linux.cc @@ -7,6 +7,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <sys/signal.h> +#include <sys/prctl.h> #include "base/command_line.h" #include "base/eintr_wrapper.h" @@ -19,6 +20,8 @@ #include "chrome/common/main_function_params.h" #include "chrome/common/process_watcher.h" +#include "skia/ext/SkFontHost_fontconfig_control.h" + // http://code.google.com/p/chromium/wiki/LinuxZygote // This is the object which implements the zygote. The ZygoteMain function, @@ -29,6 +32,8 @@ class Zygote { bool ProcessRequests() { // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the // browser on it. + // A SOCK_DGRAM is installed in fd 4. This is the sandbox IPC channel. + // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC // We need to accept SIGCHLD, even though our handler is a no-op because // otherwise we cannot wait on children. (According to POSIX 2001.) @@ -54,7 +59,7 @@ class Zygote { // new process and thus need to unwind back into ChromeMain. bool HandleRequestFromBrowser(int fd) { std::vector<int> fds; - static const unsigned kMaxMessageLength = 2048; + static const unsigned kMaxMessageLength = 1024; char buf[kMaxMessageLength]; const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds); if (len == -1) { @@ -135,6 +140,9 @@ class Zygote { mapping.push_back(std::make_pair(key, fds[i])); } + mapping.push_back(std::make_pair( + static_cast<uint32_t>(kSandboxIPCChannel), 4)); + child = fork(); if (!child) { @@ -159,10 +167,55 @@ class Zygote { close(*i); return false; } - // --------------------------------------------------------------------------- }; +static bool MaybeEnterChroot() { + const char* const sandbox_fd_string = getenv("SBX_D"); + if (sandbox_fd_string) { + // The SUID sandbox sets this environment variable to a file descriptor + // over which we can signal that we have completed our startup and can be + // chrooted. + + char* endptr; + const long fd_long = strtol(sandbox_fd_string, &endptr, 10); + if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) + return false; + const int fd = fd_long; + + static const char kChrootMe = 'C'; + static const char kChrootMeSuccess = 'O'; + + if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) + return false; + + char reply; + if (HANDLE_EINTR(read(fd, &reply, 1)) != 1) + return false; + if (reply != kChrootMeSuccess) + return false; + if (chdir("/") == -1) + return false; + + static const int kMagicSandboxIPCDescriptor = 4; + SkiaFontConfigUseIPCImplementation(kMagicSandboxIPCDescriptor); + + prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) + return false; + } else { + SkiaFontConfigUseDirectImplementation(); + } + + return true; +} + bool ZygoteMain(const MainFunctionParams& params) { + if (!MaybeEnterChroot()) { + LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " + << errno << ")"; + return false; + } + Zygote zygote; return zygote.ProcessRequests(); } diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 4cd57bd..bed25c9 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1229,6 +1229,8 @@ 'browser/renderer_host/download_throttling_resource_handler.h', 'browser/renderer_host/render_process_host.cc', 'browser/renderer_host/render_process_host.h', + 'browser/renderer_host/render_sandbox_host_linux.h', + 'browser/renderer_host/render_sandbox_host_linux.cc', 'browser/renderer_host/render_view_host.cc', 'browser/renderer_host/render_view_host.h', 'browser/renderer_host/render_view_host_delegate.h', diff --git a/chrome/common/chrome_descriptors.h b/chrome/common/chrome_descriptors.h index 3d6be45..c86eee5 100644 --- a/chrome/common/chrome_descriptors.h +++ b/chrome/common/chrome_descriptors.h @@ -10,6 +10,7 @@ enum { kPrimaryIPCChannel = 0, kCrashDumpSignal = 1, + kSandboxIPCChannel = 2, // http://code.google.com/p/chromium/LinuxSandboxIPC }; #endif // CHROME_COMMON_CHROME_DESCRIPTORS_H_ diff --git a/skia/ext/SkFontHost_fontconfig.cpp b/skia/ext/SkFontHost_fontconfig.cpp new file mode 100644 index 0000000..04ac5b9d --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig.cpp @@ -0,0 +1,237 @@ +/* libs/graphics/ports/SkFontHost_fontconfig.cpp +** +** Copyright 2008, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// ----------------------------------------------------------------------------- +// This file provides implementations of the font resolution members of +// SkFontHost by using the fontconfig[1] library. Fontconfig is usually found +// on Linux systems and handles configuration, parsing and caching issues +// involved with enumerating and matching fonts. +// +// [1] http://fontconfig.org +// ----------------------------------------------------------------------------- + +#include <map> +#include <string> + +#include <unistd.h> +#include <sys/stat.h> + +#include "SkFontHost.h" +#include "SkStream.h" +#include "SkFontHost_fontconfig_impl.h" +#include "SkFontHost_fontconfig_direct.h" +#include "SkFontHost_fontconfig_ipc.h" + +static FontConfigInterface* global_fc_impl = NULL; + +void SkiaFontConfigUseDirectImplementation() { + if (global_fc_impl) + delete global_fc_impl; + global_fc_impl = new FontConfigDirect; +} + +void SkiaFontConfigUseIPCImplementation(int fd) { + if (global_fc_impl) + delete global_fc_impl; + global_fc_impl = new FontConfigIPC(fd); +} + +static FontConfigInterface* GetFcImpl() { + if (!global_fc_impl) + global_fc_impl = new FontConfigDirect; + return global_fc_impl; +} + +static SkMutex global_fc_map_lock; +static std::map<uint32_t, SkTypeface *> global_fc_typefaces; + +// This is the maximum size of the font cache. +static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB + +// UniqueIds are encoded as (fileid << 8) | style + +static unsigned UniqueIdToFileId(unsigned uniqueid) +{ + return uniqueid >> 8; +} + +static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid) +{ + return static_cast<SkTypeface::Style>(uniqueid & 0xff); +} + +static unsigned FileIdAndStyleToUniqueId(unsigned fileid, + SkTypeface::Style style) +{ + SkASSERT(style & 0xff == style); + return (fileid << 8) | static_cast<int>(style); +} + +class FontConfigTypeface : public SkTypeface { +public: + FontConfigTypeface(Style style, uint32_t id) + : SkTypeface(style, id) + { } +}; + +// static +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + SkTypeface::Style style) +{ + std::string resolved_family_name; + + if (familyFace) { + // Given the fileid we can ask fontconfig for the familyname of the + // font. + const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID()); + if (!GetFcImpl()->Match( + &resolved_family_name, NULL, true /* fileid valid */, fileid, "", + -1, -1)) { + return NULL; + } + } else if (familyName) { + resolved_family_name = familyName; + } else { + return NULL; + } + + const bool bold = style & SkTypeface::kBold; + const bool italic = style & SkTypeface::kItalic; + unsigned fileid; + if (!GetFcImpl()->Match(NULL, &fileid, false, -1, /* no fileid */ + resolved_family_name, bold, italic)) { + return NULL; + } + + const unsigned id = FileIdAndStyleToUniqueId(fileid, style); + SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); + + { + SkAutoMutexAcquire ac(global_fc_map_lock); + global_fc_typefaces[id] = typeface; + } + + return typeface; +} + +// static +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromStream unimplemented"); + return NULL; +} + +// static +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); + return NULL; +} + +// static +bool SkFontHost::ValidFontID(SkFontID uniqueID) { + SkAutoMutexAcquire ac(global_fc_map_lock); + return global_fc_typefaces.find(uniqueID) != global_fc_typefaces.end(); +} + +void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return NULL; +} + +// static +uint32_t SkFontHost::NextLogicalFont(SkFontID fontID) { + // We don't handle font fallback, WebKit does. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkFileDescriptorStream : public SkStream { + public: + SkFileDescriptorStream(int fd) + : fd_(fd) { + } + + ~SkFileDescriptorStream() { + close(fd_); + } + + virtual bool rewind() { + if (lseek(fd_, 0, SEEK_SET) == -1) + return false; + return true; + } + + // SkStream implementation. + virtual size_t read(void* buffer, size_t size) { + if (!buffer && !size) { + // This is request for the length of the stream. + struct stat st; + if (fstat(fd_, &st) == -1) + return 0; + return st.st_size; + } + + if (!buffer) { + // This is a request to skip bytes. + const off_t current_position = lseek(fd_, 0, SEEK_CUR); + if (current_position == -1) + return 0; + const off_t new_position = lseek(fd_, size, SEEK_CUR); + if (new_position == -1) + return 0; + if (new_position < current_position) { + lseek(fd_, current_position, SEEK_SET); + return 0; + } + return new_position; + } + + // This is a request to read bytes. + return ::read(fd_, buffer, size); + } + + private: + const int fd_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// static +SkStream* SkFontHost::OpenStream(uint32_t id) +{ + const unsigned fileid = UniqueIdToFileId(id); + const int fd = GetFcImpl()->Open(fileid); + if (fd < 0) + return NULL; + + return SkNEW_ARGS(SkFileDescriptorStream, (fd)); +} + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > kFontCacheMemoryBudget) + return sizeAllocatedSoFar - kFontCacheMemoryBudget; + else + return 0; // nothing to do +} diff --git a/skia/ext/SkFontHost_fontconfig_control.h b/skia/ext/SkFontHost_fontconfig_control.h new file mode 100644 index 0000000..2af6c2e --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig_control.h @@ -0,0 +1,26 @@ +/* libs/graphics/ports/SkFontHost_fontconfig_control.h +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef FontConfigControl_DEFINED +#define FontConfigControl_DEFINED + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +extern void SkiaFontConfigUseDirectImplementation(); +extern void SkiaFontConfigUseIPCImplementation(int fd); + +#endif // FontConfigControl_DEFINED diff --git a/skia/ext/SkFontHost_fontconfig_direct.cpp b/skia/ext/SkFontHost_fontconfig_direct.cpp new file mode 100644 index 0000000..001fccd --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig_direct.cpp @@ -0,0 +1,180 @@ +/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost_fontconfig_direct.h" + +#include <unistd.h> +#include <fcntl.h> + +#include <fontconfig/fontconfig.h> + +FontConfigDirect::FontConfigDirect() + : next_file_id_(0) { + FcInit(); +} + +// ----------------------------------------------------------------------------- +// Normally we only return exactly the font asked for. In last-resort cases, +// the request is for one of the basic font names "Sans", "Serif" or +// "Monospace". This function tells you whether a given request is for such a +// fallback. +// ----------------------------------------------------------------------------- +static bool IsFallbackFontAllowed(const std::string& family) +{ + return family == "Sans" || + family == "Serif" || + family == "Monospace"; +} + +bool FontConfigDirect::Match(std::string* result_family, + unsigned* result_fileid, + bool fileid_valid, unsigned fileid, + const std::string& family, int is_bold, + int is_italic) { + SkAutoMutexAcquire ac(mutex_); + FcPattern* pattern = FcPatternCreate(); + + FcValue fcvalue; + if (fileid_valid) { + const std::map<unsigned, std::string>::const_iterator + i = fileid_to_filename_.find(fileid); + if (i == fileid_to_filename_.end()) { + FcPatternDestroy(pattern); + return false; + } + + fcvalue.type = FcTypeString; + fcvalue.u.s = (FcChar8*) i->second.c_str(); + FcPatternAdd(pattern, FC_FILE, fcvalue, 0); + } + if (!family.empty()) { + fcvalue.type = FcTypeString; + fcvalue.u.s = (FcChar8*) family.c_str(); + FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0); + } + if (is_bold > 0) { + fcvalue.type = FcTypeInteger; + fcvalue.u.i = is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL; + FcPatternAdd(pattern, FC_WEIGHT, fcvalue, 0); + } + if (is_italic > 0) { + fcvalue.type = FcTypeInteger; + fcvalue.u.i = is_bold ? FC_SLANT_ITALIC : FC_SLANT_ROMAN; + FcPatternAdd(pattern, FC_SLANT, fcvalue, 0); + } + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + // Font matching: + // CSS often specifies a fallback list of families: + // font-family: a, b, c, serif; + // However, fontconfig will always do its best to find *a* font when asked + // for something so we need a way to tell if the match which it has found is + // "good enough" for us. Otherwise, we can return NULL which gets piped up + // and lets WebKit know to try the next CSS family name. However, fontconfig + // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we + // wish to support that. + // + // Thus, if a specific family is requested we set @family_requested. Then we + // record two strings: the family name after config processing and the + // family name after resolving. If the two are equal, it's a good match. + // + // So consider the case where a user has mapped Arial to Helvetica in their + // config. + // requested family: "Arial" + // post_config_family: "Helvetica" + // post_match_family: "Helvetica" + // -> good match + // + // and for a missing font: + // requested family: "Monaco" + // post_config_family: "Monaco" + // post_match_family: "Times New Roman" + // -> BAD match + // + // However, we special-case fallback fonts; see IsFallbackFontAllowed(). + FcChar8* post_config_family; + FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); + + FcResult result; + FcPattern* match = FcFontMatch(0, pattern, &result); + if (!match) { + FcPatternDestroy(pattern); + return false; + } + + FcChar8* post_match_family; + FcPatternGetString(match, FC_FAMILY, 0, &post_match_family); + const bool family_names_match = + family.empty() ? + true : + strcasecmp((char *)post_config_family, (char *)post_match_family) == 0; + + FcPatternDestroy(pattern); + + if (!family_names_match && !IsFallbackFontAllowed(family)) { + FcPatternDestroy(match); + return false; + } + + FcChar8* c_filename; + if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) { + FcPatternDestroy(match); + return NULL; + } + const std::string filename((char *) c_filename); + + unsigned out_fileid; + if (fileid_valid) { + out_fileid = fileid; + } else { + const std::map<std::string, unsigned>::const_iterator + i = filename_to_fileid_.find(filename); + if (i == filename_to_fileid_.end()) { + out_fileid = next_file_id_++; + filename_to_fileid_[filename] = out_fileid; + fileid_to_filename_[out_fileid] = filename; + } else { + out_fileid = i->second; + } + } + + if (*result_fileid) + *result_fileid = out_fileid; + + FcChar8* c_family; + if (FcPatternGetString(match, FC_FAMILY, 0, &c_family)) { + FcPatternDestroy(match); + return NULL; + } + + if (result_family) + *result_family = (char *) c_family; + + return true; +} + +int FontConfigDirect::Open(unsigned fileid) { + SkAutoMutexAcquire ac(mutex_); + const std::map<unsigned, std::string>::const_iterator + i = fileid_to_filename_.find(fileid); + if (i == fileid_to_filename_.end()) + return -1; + + return open(i->second.c_str(), O_RDONLY); +} diff --git a/skia/ext/SkFontHost_fontconfig_direct.h b/skia/ext/SkFontHost_fontconfig_direct.h new file mode 100644 index 0000000..d9c934a --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig_direct.h @@ -0,0 +1,44 @@ +/* libs/graphics/ports/SkFontHost_fontconfig_direct.h +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef FontConfigDirect_DEFINED +#define FontConfigDirect_DEFINED + +#include <map> +#include <string> + +#include "SkThread.h" +#include "SkFontHost_fontconfig_impl.h" + +class FontConfigDirect : public FontConfigInterface { + public: + FontConfigDirect(); + + // FontConfigInterface implementation. Thread safe. + virtual bool Match(std::string* result_family, unsigned* result_fileid, + bool fileid_valid, unsigned fileid, + const std::string& family, int is_bold, int is_italic); + virtual int Open(unsigned fileid); + + private: + SkMutex mutex_; + std::map<unsigned, std::string> fileid_to_filename_; + std::map<std::string, unsigned> filename_to_fileid_; + unsigned next_file_id_; +}; + +#endif // FontConfigDirect_DEFINED diff --git a/skia/ext/SkFontHost_fontconfig_impl.h b/skia/ext/SkFontHost_fontconfig_impl.h new file mode 100644 index 0000000..a593891 --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig_impl.h @@ -0,0 +1,60 @@ +/* libs/graphics/ports/SkFontHost_fontconfig_impl.h +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* The SkFontHost_fontconfig code requires an implementation of an abstact + * fontconfig interface. We do this because sometimes fontconfig is not + * directly availible and this provides an ability to change the fontconfig + * implementation at run-time. + */ + +#ifndef FontConfigInterface_DEFINED +#define FontConfigInterface_DEFINED + +#include <string> + +class FontConfigInterface { + public: + virtual ~FontConfigInterface() { } + + /** Performs config match + * + * @param result_family (output) on success, the resulting family name. + * @param result_fileid (output) on success, the resulting file id. + * @param fileid_valid if true, then |fileid| is valid + * @param fileid the fileid (as returned by this function) which we are + * trying to match. + * @param family (optional) the family of the font that we are trying to + * match. + * @param is_bold (optional, set to -1 to ignore) + * @param is_italic (optional, set to -1 to ignore) + * @return true iff successful. + */ + virtual bool Match( + std::string* result_family, + unsigned* result_fileid, + bool fileid_valid, + unsigned fileid, + const std::string& family, + int is_bold, + int is_italic) = 0; + + /** Open a font file given the fileid as returned by Match + */ + virtual int Open(unsigned fileid) = 0; +}; + +#endif // FontConfigInterface_DEFINED diff --git a/skia/ext/SkFontHost_fontconfig_ipc.cpp b/skia/ext/SkFontHost_fontconfig_ipc.cpp new file mode 100644 index 0000000..1705aed --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig_ipc.cpp @@ -0,0 +1,144 @@ +/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +#include "SkFontHost_fontconfig_ipc.h" + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include "base/pickle.h" +#include "base/unix_domain_socket_posix.h" + +FontConfigIPC::FontConfigIPC(int fd) + : fd_(fd) { +} + +FontConfigIPC::~FontConfigIPC() { + close(fd_); +} + +static ssize_t SyncIPC(int fd, uint8_t* reply, unsigned reply_len, int* result_fd, + const Pickle& request) { + int fds[2]; + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == -1) + return false; + + std::vector<int> fd_vector; + fd_vector.push_back(fds[1]); + if (!base::SendMsg(fd, request.data(), request.size(), fd_vector)) { + close(fds[0]); + close(fds[1]); + return -1; + } + close(fds[1]); + + fd_vector.clear(); + const ssize_t r = base::RecvMsg(fds[0], reply, reply_len, &fd_vector); + close(fds[0]); + if (r == -1) + return -1; + + if ((fd_vector.size() > 0 && result_fd == NULL) || fd_vector.size() > 1) { + for (std::vector<int>::const_iterator + i = fd_vector.begin(); i != fd_vector.end(); ++i) { + close(*i); + } + + return -1; + } + + if (result_fd) { + if (fd_vector.size() == 0) { + *result_fd = -1; + } else { + *result_fd = fd_vector[0]; + } + } + + return r; +} + +bool FontConfigIPC::Match(std::string* result_family, + unsigned* result_fileid, + bool fileid_valid, unsigned fileid, + const std::string& family, int is_bold, + int is_italic) { + Pickle request; + request.WriteInt(METHOD_MATCH); + request.WriteBool(fileid_valid); + if (fileid_valid) + request.WriteUInt32(fileid); + request.WriteBool(is_bold); + request.WriteBool(is_italic); + request.WriteString(family); + + uint8_t reply_buf[512]; + const ssize_t r = SyncIPC(fd_, reply_buf, sizeof(reply_buf), NULL, request); + if (r == -1) + return false; + + Pickle reply(reinterpret_cast<char*>(reply_buf), r); + void* iter = NULL; + bool result; + if (!reply.ReadBool(&iter, &result)) + return false; + if (!result) + return false; + + uint32_t reply_fileid; + std::string reply_family; + if (!reply.ReadUInt32(&iter, &reply_fileid) || + !reply.ReadString(&iter, &reply_family)) { + return false; + } + + *result_fileid = reply_fileid; + if (result_family) + *result_family = reply_family; + + return true; +} + +int FontConfigIPC::Open(unsigned fileid) { + Pickle request; + request.WriteInt(METHOD_OPEN); + request.WriteUInt32(fileid); + + int result_fd = -1; + uint8_t reply_buf[256]; + const ssize_t r = SyncIPC(fd_, reply_buf, sizeof(reply_buf), &result_fd, request); + + if (r == -1) + return -1; + + Pickle reply(reinterpret_cast<char*>(reply_buf), r); + bool result; + void* iter = NULL; + if (!reply.ReadBool(&iter, &result) || + !result) { + if (result_fd) + close(result_fd); + return -1; + } + + return result_fd; +} diff --git a/skia/ext/SkFontHost_fontconfig_ipc.h b/skia/ext/SkFontHost_fontconfig_ipc.h new file mode 100644 index 0000000..5efd1ee --- /dev/null +++ b/skia/ext/SkFontHost_fontconfig_ipc.h @@ -0,0 +1,74 @@ +/* libs/graphics/ports/SkFontHost_fontconfig_ipc.h +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef FontConfigIPC_DEFINED +#define FontConfigIPC_DEFINED + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +#include <map> +#include <string> + +#include "SkFontHost_fontconfig_impl.h" + +class FontConfigIPC : public FontConfigInterface { + public: + FontConfigIPC(int fd); + ~FontConfigIPC(); + + // FontConfigInterface implementation. + virtual bool Match(std::string* result_family, unsigned* result_fileid, + bool fileid_valid, unsigned fileid, + const std::string& family, int is_bold, int is_italic); + virtual int Open(unsigned fileid); + + enum Method { + METHOD_MATCH = 0, + METHOD_OPEN = 1, + }; + + struct MatchRequest { + uint16_t method; + uint8_t fileid_valid; + uint32_t fileid; + int8_t is_bold; + int8_t is_italic; + uint8_t family_len; + // char family[0] follows. + }; + + struct MatchReply { + uint8_t result; + uint32_t result_fileid; + uint16_t filename_len; + // char filename[0] follows. + }; + + struct OpenRequest { + uint16_t method; + unsigned fileid; + }; + + struct OpenReply { + uint8_t result; + }; + + private: + const int fd_; +}; + +#endif // FontConfigIPC_DEFINED diff --git a/skia/skia.gyp b/skia/skia.gyp index 4158b2b..912e80a 100755 --- a/skia/skia.gyp +++ b/skia/skia.gyp @@ -358,7 +358,6 @@ '../third_party/skia/src/ports/SkFontHost_FreeType.cpp', #'../third_party/skia/src/ports/SkFontHost_android.cpp', #'../third_party/skia/src/ports/SkFontHost_ascender.cpp', - '../third_party/skia/src/ports/SkFontHost_fontconfig.cpp', '../third_party/skia/src/ports/SkFontHost_tables.cpp', #'../third_party/skia/src/ports/SkFontHost_gamma.cpp', '../third_party/skia/src/ports/SkFontHost_gamma_none.cpp', @@ -566,7 +565,6 @@ '../third_party/skia/src/ports/SkFontHost_FreeType.cpp', '../third_party/skia/src/ports/SkFontHost_TryeType_Tables.cpp', '../third_party/skia/src/ports/SkFontHost_gamma_none.cpp', - '../third_party/skia/src/ports/SkFontHost_fontconfig.cpp', '../third_party/skia/src/ports/SkFontHost_gamma_none.cpp', '../third_party/skia/src/ports/SkFontHost_tables.cpp', '../third_party/skia/src/core/SkTypeface.cpp', @@ -595,6 +593,12 @@ '../third_party/skia/src/ports/SkFontHost_none.cpp', 'ext/SkTypeface_fake.cpp', ], + 'sources': [ + # http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + 'ext/SkFontHost_fontconfig.cpp', + 'ext/SkFontHost_fontconfig_direct.cpp', + 'ext/SkFontHost_fontconfig_ipc.cpp', + ], 'export_dependent_settings': [ '../third_party/harfbuzz/harfbuzz.gyp:harfbuzz', '../third_party/harfbuzz/harfbuzz.gyp:harfbuzz_interface', |