summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/chrome.gyp6
-rw-r--r--chrome/chrome_common.gypi1
-rw-r--r--chrome/common/common_param_traits.cc51
-rw-r--r--chrome/common/common_param_traits.h25
-rw-r--r--chrome/common/common_param_traits_unittest.cc72
-rw-r--r--chrome/common/utility_messages_internal.h27
-rw-r--r--chrome/service/service_child_process_host.cc33
-rw-r--r--chrome/service/service_child_process_host.h29
-rw-r--r--chrome/service/service_main.cc8
-rw-r--r--chrome/service/service_utility_process_host.cc147
-rw-r--r--chrome/service/service_utility_process_host.h120
-rw-r--r--chrome/utility/utility_main.cc14
-rw-r--r--chrome/utility/utility_thread.cc171
-rw-r--r--chrome/utility/utility_thread.h29
14 files changed, 732 insertions, 1 deletions
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index ebd8304..67c7d5a 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1030,12 +1030,18 @@
'common',
'common_net',
'../base/base.gyp:base',
+ '../printing/printing.gyp:printing',
+ '../skia/skia.gyp:skia',
'../third_party/libjingle/libjingle.gyp:libjingle',
],
'sources': [
+ 'service/service_child_process_host.cc',
+ 'service/service_child_process_host.h',
'service/service_main.cc',
'service/service_process.cc',
'service/service_process.h',
+ 'service/service_utility_process_host.cc',
+ 'service/service_utility_process_host.h',
'service/cloud_print/cloud_print_consts.cc',
'service/cloud_print/cloud_print_consts.h',
'service/cloud_print/cloud_print_helpers.cc',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index db1db8b..1d9c9ad 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -146,6 +146,7 @@
'../build/temp_gyp/googleurl.gyp:googleurl',
'../ipc/ipc.gyp:ipc',
'../net/net.gyp:net',
+ '../printing/printing.gyp:printing',
'../skia/skia.gyp:skia',
'../third_party/bzip2/bzip2.gyp:bzip2',
'../third_party/icu/icu.gyp:icui18n',
diff --git a/chrome/common/common_param_traits.cc b/chrome/common/common_param_traits.cc
index 70227da..54bb550 100644
--- a/chrome/common/common_param_traits.cc
+++ b/chrome/common/common_param_traits.cc
@@ -7,6 +7,9 @@
#include "chrome/common/chrome_constants.h"
#include "gfx/rect.h"
#include "googleurl/src/gurl.h"
+#include "printing/native_metafile.h"
+#include "printing/page_range.h"
+
#ifndef EXCLUDE_SKIA_DEPENDENCIES
#include "third_party/skia/include/core/SkBitmap.h"
#endif
@@ -302,4 +305,52 @@ void ParamTraits<Geoposition>::Log(const Geoposition& p, std::wstring* l) {
LogParam(p.error_code, l);
}
+void ParamTraits<printing::PageRange>::Write(Message* m, const param_type& p) {
+ WriteParam(m, p.from);
+ WriteParam(m, p.to);
+}
+
+bool ParamTraits<printing::PageRange>::Read(
+ const Message* m, void** iter, param_type* p) {
+ return ReadParam(m, iter, &p->from) &&
+ ReadParam(m, iter, &p->to);
+}
+
+void ParamTraits<printing::PageRange>::Log(
+ const param_type& p, std::wstring* l) {
+ l->append(L"(");
+ LogParam(p.to, l);
+ l->append(L", ");
+ LogParam(p.from, l);
+ l->append(L")");
+}
+
+void ParamTraits<printing::NativeMetafile>::Write(
+ Message* m, const param_type& p) {
+ std::vector<uint8> buffer;
+ uint32 size = p.GetDataSize();
+ if (size) {
+ buffer.resize(size);
+ p.GetData(&buffer.front(), size);
+ }
+ WriteParam(m, buffer);
+}
+
+bool ParamTraits<printing::NativeMetafile>::Read(
+ const Message* m, void** iter, param_type* p) {
+ std::vector<uint8> buffer;
+#if defined(OS_WIN)
+ return ReadParam(m, iter, &buffer) &&
+ p->CreateFromData(&buffer.front(), static_cast<uint32>(buffer.size()));
+#else // defined(OS_WIN)
+ return ReadParam(m, iter, &buffer) &&
+ p->Init(&buffer.front(), static_cast<uint32>(buffer.size()));
+#endif // defined(OS_WIN)
+}
+
+void ParamTraits<printing::NativeMetafile>::Log(
+ const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"<printing::NativeMetafile>"));
+}
+
} // namespace IPC
diff --git a/chrome/common/common_param_traits.h b/chrome/common/common_param_traits.h
index 4ac0272..300dd2e 100644
--- a/chrome/common/common_param_traits.h
+++ b/chrome/common/common_param_traits.h
@@ -22,6 +22,7 @@
#include "ipc/ipc_message_utils.h"
#include "net/base/upload_data.h"
#include "net/url_request/url_request_status.h"
+#include "printing/native_metafile.h"
#include "webkit/glue/password_form.h"
#include "webkit/glue/webcursor.h"
#include "webkit/glue/window_open_disposition.h"
@@ -38,6 +39,10 @@ class Rect;
class Size;
} // namespace gfx
+namespace printing {
+struct PageRange;
+} // namespace printing
+
namespace webkit_glue {
struct WebApplicationInfo;
} // namespace webkit_glue
@@ -474,6 +479,26 @@ struct ParamTraits<webkit_glue::PasswordForm> {
}
};
+template <>
+struct ParamTraits<printing::PageRange> {
+ typedef printing::PageRange param_type;
+ static void Write(Message* m, const param_type& p);
+
+ static bool Read(const Message* m, void** iter, param_type* r);
+
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+template <>
+struct ParamTraits<printing::NativeMetafile> {
+ typedef printing::NativeMetafile param_type;
+ static void Write(Message* m, const param_type& p);
+
+ static bool Read(const Message* m, void** iter, param_type* r);
+
+ static void Log(const param_type& p, std::wstring* l);
+};
+
} // namespace IPC
#endif // CHROME_COMMON_COMMON_PARAM_TRAITS_H_
diff --git a/chrome/common/common_param_traits_unittest.cc b/chrome/common/common_param_traits_unittest.cc
index 842ea95..032f471 100644
--- a/chrome/common/common_param_traits_unittest.cc
+++ b/chrome/common/common_param_traits_unittest.cc
@@ -8,12 +8,23 @@
#include "base/scoped_ptr.h"
#include "base/values.h"
#include "chrome/common/common_param_traits.h"
+#include "gfx/rect.h"
#include "googleurl/src/gurl.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_utils.h"
+#include "printing/native_metafile.h"
+#include "printing/page_range.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#ifndef NDEBUG
+namespace {
+void IgnoreAssertHandler(const std::string& str) {
+}
+} // namespace
+
+#endif // NDEBUG
+
// Tests that serialize/deserialize correctly understand each other
TEST(IPCMessageTest, Serialize) {
const char* serialize_cases[] = {
@@ -205,3 +216,64 @@ TEST(IPCMessageTest, Geoposition) {
L"<Geoposition::ErrorCode>2",
log_message.c_str());
}
+
+// Tests printing::PageRange serialization
+TEST(IPCMessageTest, PageRange) {
+ printing::PageRange input;
+ input.from = 2;
+ input.to = 45;
+ IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ IPC::ParamTraits<printing::PageRange>::Write(&msg, input);
+
+ printing::PageRange output;
+ void* iter = NULL;
+ EXPECT_TRUE(IPC::ParamTraits<printing::PageRange>::Read(
+ &msg, &iter, &output));
+ EXPECT_TRUE(input == output);
+}
+
+// Tests printing::NativeMetafile serialization.
+TEST(IPCMessageTest, Metafile) {
+ // TODO(sanjeevr): Make this test meaningful for non-Windows platforms. We
+ // need to initialize the metafile using alternate means on the other OSes.
+#if defined(OS_WIN)
+ printing::NativeMetafile metafile;
+ RECT test_rect = {0, 0, 100, 100};
+ // Create a metsfile using the screen DC as a reference.
+ metafile.CreateDc(NULL, NULL);
+ metafile.CloseDc();
+
+ IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ IPC::ParamTraits<printing::NativeMetafile>::Write(&msg, metafile);
+
+ printing::NativeMetafile output;
+ void* iter = NULL;
+ EXPECT_TRUE(IPC::ParamTraits<printing::NativeMetafile>::Read(
+ &msg, &iter, &output));
+
+ EXPECT_EQ(metafile.GetDataSize(), output.GetDataSize());
+ EXPECT_EQ(metafile.GetBounds(), output.GetBounds());
+ EXPECT_EQ(::GetDeviceCaps(metafile.hdc(), LOGPIXELSX),
+ ::GetDeviceCaps(output.hdc(), LOGPIXELSX));
+
+ // Also test the corrupt case.
+ IPC::Message bad_msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ // Write some bogus metafile data.
+ const size_t bogus_data_size = metafile.GetDataSize() * 2;
+ scoped_array<char> bogus_data(new char[bogus_data_size]);
+ memset(bogus_data.get(), 'B', bogus_data_size);
+ bad_msg.WriteData(bogus_data.get(), bogus_data_size);
+ // Make sure we don't read out the metafile!
+ printing::NativeMetafile bad_output;
+ iter = NULL;
+ // The Emf code on Windows DCHECKs if it cannot create the metafile.
+#ifndef NDEBUG
+ logging::SetLogAssertHandler(IgnoreAssertHandler);
+#endif // NDEBUG
+ EXPECT_FALSE(IPC::ParamTraits<printing::NativeMetafile>::Read(
+ &bad_msg, &iter, &bad_output));
+#else // defined(OS_WIN)
+ NOTIMPLEMENTED();
+#endif // defined(OS_WIN)
+}
+
diff --git a/chrome/common/utility_messages_internal.h b/chrome/common/utility_messages_internal.h
index 5c3f971..9231501 100644
--- a/chrome/common/utility_messages_internal.h
+++ b/chrome/common/utility_messages_internal.h
@@ -13,6 +13,10 @@
// from it via utility_messages.h.
#include "ipc/ipc_message_macros.h"
+#include "base/platform_file.h"
+#include "gfx/rect.h"
+#include "printing/native_metafile.h"
+#include "printing/page_range.h"
#include "third_party/skia/include/core/SkBitmap.h"
//------------------------------------------------------------------------------
@@ -37,6 +41,13 @@ IPC_BEGIN_MESSAGES(Utility)
// Tell the utility process to decode the given image data.
IPC_MESSAGE_CONTROL1(UtilityMsg_DecodeImage,
std::vector<unsigned char>) // encoded image contents
+
+ // Tell the utility process to render the given PDF into a metafile.
+ IPC_MESSAGE_CONTROL4(UtilityMsg_RenderPDFPagesToMetafile,
+ base::PlatformFile, // PDF file
+ gfx::Rect, // Render Area
+ int, // DPI
+ std::vector<printing::PageRange>)
IPC_END_MESSAGES(Utility)
//------------------------------------------------------------------------------
@@ -84,4 +95,20 @@ IPC_BEGIN_MESSAGES(UtilityHost)
// Reply when an error occured decoding the image.
IPC_MESSAGE_CONTROL0(UtilityHostMsg_DecodeImage_Failed)
+
+ // Reply when the utility process has succeeded in rendering the PDF.
+ IPC_MESSAGE_CONTROL2(UtilityHostMsg_RenderPDFPagesToMetafile_Succeeded,
+ printing::NativeMetafile, // Output metafile
+ int) // Highest rendered page number
+
+ // Reply when an error occured rendering the PDF.
+ IPC_MESSAGE_CONTROL0(UtilityHostMsg_RenderPDFPagesToMetafile_Failed)
+
+#if defined(OS_WIN)
+ // Request that the given font be loaded by the host so it's cached by the
+ // OS. Please see ChildProcessHost::PreCacheFont for details.
+ IPC_SYNC_MESSAGE_CONTROL1_0(UtilityHostMsg_PreCacheFont,
+ LOGFONT /* font data */)
+#endif // defined(OS_WIN)
+
IPC_END_MESSAGES(UtilityHost)
diff --git a/chrome/service/service_child_process_host.cc b/chrome/service/service_child_process_host.cc
new file mode 100644
index 0000000..cea04fe
--- /dev/null
+++ b/chrome/service/service_child_process_host.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2010 The Chromium 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/service/service_child_process_host.h"
+
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "chrome/common/result_codes.h"
+#if defined(OS_WIN)
+#include "chrome/common/sandbox_policy.h"
+#endif // defined(OS_WIN)
+
+ServiceChildProcessHost::ServiceChildProcessHost(ProcessType type)
+ : ChildProcessInfo(type, -1) {
+}
+
+ServiceChildProcessHost::~ServiceChildProcessHost() {
+ // We need to kill the child process when the host dies.
+ base::KillProcess(handle(), ResultCodes::NORMAL_EXIT, false);
+}
+
+bool ServiceChildProcessHost::Launch(CommandLine* cmd_line) {
+#if !defined(OS_WIN)
+ // TODO(sanjeevr): Implement for non-Windows OSes.
+ NOTIMPLEMENTED();
+ return false;
+#else // !defined(OS_WIN)
+ set_handle(sandbox::StartProcessWithAccess(cmd_line, FilePath()));
+ return (handle() != base::kNullProcessHandle);
+#endif // !defined(OS_WIN)
+}
+
diff --git a/chrome/service/service_child_process_host.h b/chrome/service/service_child_process_host.h
new file mode 100644
index 0000000..bfc833b
--- /dev/null
+++ b/chrome/service/service_child_process_host.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 The Chromium 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 CHROME_SERVICE_SERVICE_CHILD_PROCESS_HOST_H_
+#define CHROME_SERVICE_SERVICE_CHILD_PROCESS_HOST_H_
+
+#include "base/process.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/child_process_info.h"
+
+
+// Plugins/workers and other child processes that live on the IO thread should
+// derive from this class.
+//
+class ServiceChildProcessHost : public ChildProcessHost,
+ public ChildProcessInfo {
+ public:
+ virtual ~ServiceChildProcessHost();
+
+ protected:
+ explicit ServiceChildProcessHost(ProcessType type);
+ // Derived classes call this to launch the child process synchronously.
+ // TODO(sanjeevr): Determine whether we need to make the launch asynchronous.
+ bool Launch(CommandLine* cmd_line);
+};
+
+#endif // CHROME_SERVICE_SERVICE_CHILD_PROCESS_HOST_H_
+
diff --git a/chrome/service/service_main.cc b/chrome/service/service_main.cc
index a3fcd69..2f09489 100644
--- a/chrome/service/service_main.cc
+++ b/chrome/service/service_main.cc
@@ -9,6 +9,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/json_pref_store.h"
#include "chrome/common/main_function_params.h"
+#include "chrome/common/sandbox_policy.h"
#include "chrome/service/cloud_print/cloud_print_proxy.h"
#include "chrome/service/service_process.h"
@@ -33,6 +34,13 @@ int ServiceProcessMain(const MainFunctionParams& parameters) {
MessageLoopForUI main_message_loop;
PlatformThread::SetName("CrServiceMain");
+#if defined(OS_WIN)
+ sandbox::BrokerServices* broker_services =
+ parameters.sandbox_info_.BrokerServices();
+ if (broker_services)
+ sandbox::InitBrokerServices(broker_services);
+#endif // defined(OS_WIN)
+
ServiceProcess service_process;
service_process.Initialize();
diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc
new file mode 100644
index 0000000..e465eb2
--- /dev/null
+++ b/chrome/service/service_utility_process_host.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2010 The Chromium 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/service/service_utility_process_host.h"
+
+#include "app/app_switches.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/utility_messages.h"
+#include "gfx/rect.h"
+#include "ipc/ipc_switches.h"
+#include "printing/native_metafile.h"
+#include "printing/page_range.h"
+
+ServiceUtilityProcessHost::ServiceUtilityProcessHost(
+ Client* client, base::MessageLoopProxy* client_message_loop_proxy)
+ : ServiceChildProcessHost(ChildProcessInfo::UTILITY_PROCESS),
+ client_(client),
+ client_message_loop_proxy_(client_message_loop_proxy),
+ waiting_for_reply_(false) {
+}
+
+ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
+}
+
+bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
+ const FilePath& pdf_path,
+ const gfx::Rect& render_area,
+ int render_dpi,
+ const std::vector<printing::PageRange>& page_ranges) {
+#if !defined(OS_WIN)
+ // This is only implemented on Windows (because currently it is only needed
+ // on Windows). Will add implementations on other platforms when needed.
+ NOTIMPLEMENTED();
+ return false;
+#else // !defined(OS_WIN)
+ if (!StartProcess())
+ return false;
+
+ ScopedHandle pdf_file(
+ ::CreateFile(pdf_path.value().c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL));
+ if (pdf_file == INVALID_HANDLE_VALUE)
+ return false;
+ HANDLE pdf_file_in_utility_process = NULL;
+ ::DuplicateHandle(::GetCurrentProcess(), pdf_file, handle(),
+ &pdf_file_in_utility_process, 0, false,
+ DUPLICATE_SAME_ACCESS);
+ if (!pdf_file_in_utility_process)
+ return false;
+ waiting_for_reply_ = true;
+ return SendOnChannel(
+ new UtilityMsg_RenderPDFPagesToMetafile(pdf_file_in_utility_process,
+ render_area,
+ render_dpi,
+ page_ranges));
+#endif // !defined(OS_WIN)
+}
+
+bool ServiceUtilityProcessHost::StartProcess() {
+ // Name must be set or metrics_service will crash in any test which
+ // launches a UtilityProcessHost.
+ set_name(L"utility process");
+
+ if (!CreateChannel())
+ return false;
+
+ FilePath exe_path = GetUtilityProcessCmd();
+ if (exe_path.empty()) {
+ NOTREACHED() << "Unable to get utility process binary name.";
+ return false;
+ }
+
+ CommandLine cmd_line(exe_path);
+ cmd_line.AppendSwitchWithValue(switches::kProcessType,
+ switches::kUtilityProcess);
+ cmd_line.AppendSwitchWithValue(switches::kProcessChannelID,
+ ASCIIToWide(channel_id()));
+ cmd_line.AppendSwitch(switches::kLang);
+
+ return Launch(&cmd_line);
+}
+
+FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
+ return GetChildPath(true);
+}
+
+void ServiceUtilityProcessHost::OnChildDied() {
+ if (waiting_for_reply_) {
+ // If we are yet to receive a reply then notify the client that the
+ // child died.
+ client_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(client_.get(), &Client::OnChildDied));
+ }
+ // The base class implementation will delete |this|.
+ ServiceChildProcessHost::OnChildDied();
+}
+
+void ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
+ bool msg_is_ok = false;
+ IPC_BEGIN_MESSAGE_MAP_EX(ServiceUtilityProcessHost, message, msg_is_ok)
+#if defined(OS_WIN) // This hack is Windows-specific.
+ IPC_MESSAGE_HANDLER(UtilityHostMsg_PreCacheFont, OnPreCacheFont)
+#endif
+ IPC_MESSAGE_UNHANDLED(msg_is_ok__ = MessageForClient(message))
+ IPC_END_MESSAGE_MAP_EX()
+}
+
+bool ServiceUtilityProcessHost::MessageForClient(const IPC::Message& message) {
+ DCHECK(waiting_for_reply_);
+ bool ret = client_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(client_.get(), &Client::OnMessageReceived,
+ message));
+ waiting_for_reply_ = false;
+ return ret;
+}
+
+#if defined(OS_WIN) // This hack is Windows-specific.
+void ServiceUtilityProcessHost::OnPreCacheFont(LOGFONT font) {
+ PreCacheFont(font);
+}
+#endif // OS_WIN
+
+
+void ServiceUtilityProcessHost::Client::OnMessageReceived(
+ const IPC::Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
+#if defined(OS_WIN)
+ IPC_MESSAGE_HANDLER(UtilityHostMsg_RenderPDFPagesToMetafile_Succeeded,
+ Client::OnRenderPDFPagesToMetafileSucceeded)
+#endif // OS_WIN
+ IPC_MESSAGE_HANDLER(UtilityHostMsg_RenderPDFPagesToMetafile_Failed,
+ Client::OnRenderPDFPagesToMetafileFailed)
+ IPC_END_MESSAGE_MAP_EX()
+}
+
diff --git a/chrome/service/service_utility_process_host.h b/chrome/service/service_utility_process_host.h
new file mode 100644
index 0000000..7474c8eb
--- /dev/null
+++ b/chrome/service/service_utility_process_host.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2010 The Chromium 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 CHROME_SERVICE_SERVICE_UTILITY_PROCESS_HOST_H_
+#define CHROME_SERVICE_SERVICE_UTILITY_PROCESS_HOST_H_
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif // defined(OS_WIN)
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "ipc/ipc_channel.h"
+#include "chrome/service/service_child_process_host.h"
+#include "printing/native_metafile.h"
+
+class CommandLine;
+
+namespace base {
+class MessageLoopProxy;
+} // namespace base
+
+namespace gfx {
+class Rect;
+} // namespace gfx
+
+namespace printing {
+struct PageRange;
+} // namespace printing
+
+// Acts as the service-side host to a utility child process. A
+// utility process is a short-lived sandboxed process that is created to run
+// a specific task.
+class ServiceUtilityProcessHost : public ServiceChildProcessHost {
+ public:
+ // Consumers of ServiceUtilityProcessHost must implement this interface to
+ // get results back. All functions are called on the thread passed along
+ // to ServiceUtilityProcessHost.
+ class Client : public base::RefCountedThreadSafe<Client> {
+ public:
+ Client() {}
+
+ // Called when the child process died before a reply was receieved.
+ virtual void OnChildDied() {}
+
+ // Called when at least one page in the specified PDF has been rendered
+ // successfully into |metafile|.
+ virtual void OnRenderPDFPagesToMetafileSucceeded(
+ const printing::NativeMetafile& metafile,
+ int highest_rendered_page_number) {}
+ // Called when no page in the passed in PDF could be rendered.
+ virtual void OnRenderPDFPagesToMetafileFailed() {}
+
+ protected:
+ virtual ~Client() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<Client>;
+ friend class ServiceUtilityProcessHost;
+
+ void OnMessageReceived(const IPC::Message& message);
+
+ DISALLOW_COPY_AND_ASSIGN(Client);
+ };
+
+ ServiceUtilityProcessHost(Client* client,
+ base::MessageLoopProxy* client_message_loop_proxy);
+ virtual ~ServiceUtilityProcessHost();
+
+ // Starts a process to render the specified pages in the given PDF file into
+ // a metafile. Currently only implemented for Windows. If the PDF has fewer
+ // pages than the specified page ranges, it will render as many as available.
+ bool StartRenderPDFPagesToMetafile(
+ const FilePath& pdf_path,
+ const gfx::Rect& render_area,
+ int render_dpi,
+ const std::vector<printing::PageRange>& page_ranges);
+
+ // Since we handle a sync IPC message (UtilityHostMsg_PreCacheFont), we need
+ // an Send method.
+ bool Send(IPC::Message* message) {
+ return SendOnChannel(message);
+ }
+
+ protected:
+ // Allows this method to be overridden for tests.
+ virtual FilePath GetUtilityProcessCmd();
+
+ // Overriden from ChildProcessHost.
+ virtual bool CanShutdown() { return true; }
+ virtual void OnChildDied();
+
+ private:
+ // Starts a process. Returns true iff it succeeded.
+ bool StartProcess();
+
+ // IPC messages:
+ void OnMessageReceived(const IPC::Message& message);
+ bool MessageForClient(const IPC::Message& message);
+
+#if defined(OS_WIN) // This hack is Windows-specific.
+ void OnPreCacheFont(LOGFONT font);
+#endif // defined(OS_WIN)
+
+
+ // A pointer to our client interface, who will be informed of progress.
+ scoped_refptr<Client> client_;
+ scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_;
+ bool waiting_for_reply_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceUtilityProcessHost);
+};
+
+#endif // CHROME_SERVICE_SERVICE_UTILITY_PROCESS_HOST_H_
+
diff --git a/chrome/utility/utility_main.cc b/chrome/utility/utility_main.cc
index a568566..09d0156 100644
--- a/chrome/utility/utility_main.cc
+++ b/chrome/utility/utility_main.cc
@@ -6,10 +6,13 @@
#include "app/hi_res_timer_manager.h"
#include "app/system_monitor.h"
#include "base/command_line.h"
+#include "base/file_util.h"
#include "base/message_loop.h"
+#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/common/child_process.h"
#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/logging_chrome.h"
@@ -19,7 +22,7 @@
#if defined(OS_WIN)
#include "chrome/common/sandbox_init_wrapper.h"
#include "sandbox/src/sandbox.h"
-#endif
+#endif // defined(OS_WIN)
// Mainline routine for running as the utility process.
int UtilityMain(const MainFunctionParams& parameters) {
@@ -33,6 +36,15 @@ int UtilityMain(const MainFunctionParams& parameters) {
ChildProcess utility_process;
utility_process.set_main_thread(new UtilityThread());
#if defined(OS_WIN)
+ // Load the pdf plugin before the sandbox is turned on. This is for Windows
+ // only because we need this DLL only on Windows.
+ FilePath pdf;
+ if (PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) &&
+ file_util::PathExists(pdf)) {
+ bool rv = !!LoadLibrary(pdf.value().c_str());
+ DCHECK(rv) << "Couldn't load PDF plugin";
+ }
+
sandbox::TargetServices* target_services =
parameters.sandbox_info_.TargetServices();
if (!target_services)
diff --git a/chrome/utility/utility_thread.cc b/chrome/utility/utility_thread.cc
index 259b185..eccda40 100644
--- a/chrome/utility/utility_thread.cc
+++ b/chrome/utility/utility_thread.cc
@@ -7,15 +7,25 @@
#include <vector>
#include "base/file_util.h"
+#if defined(OS_WIN)
+#include "base/iat_patch.h"
+#endif
+#include "base/path_service.h"
#include "base/values.h"
#include "chrome/common/child_process.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_unpacker.h"
#include "chrome/common/extensions/update_manifest.h"
#include "chrome/common/utility_messages.h"
#include "chrome/common/web_resource/web_resource_unpacker.h"
+#include "gfx/rect.h"
+#include "printing/native_metafile.h"
+#include "printing/page_range.h"
+#include "printing/units.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "webkit/glue/image_decoder.h"
+
UtilityThread::UtilityThread() {
ChildProcess::current()->AddRefProcess();
}
@@ -29,6 +39,8 @@ void UtilityThread::OnControlMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(UtilityMsg_UnpackWebResource, OnUnpackWebResource)
IPC_MESSAGE_HANDLER(UtilityMsg_ParseUpdateManifest, OnParseUpdateManifest)
IPC_MESSAGE_HANDLER(UtilityMsg_DecodeImage, OnDecodeImage)
+ IPC_MESSAGE_HANDLER(UtilityMsg_RenderPDFPagesToMetafile,
+ OnRenderPDFPagesToMetafile)
IPC_END_MESSAGE_MAP()
}
@@ -84,3 +96,162 @@ void UtilityThread::OnDecodeImage(
ChildProcess::current()->ReleaseProcess();
}
+
+void UtilityThread::OnRenderPDFPagesToMetafile(
+ base::PlatformFile pdf_file,
+ const gfx::Rect& render_area,
+ int render_dpi,
+ const std::vector<printing::PageRange>& page_ranges) {
+ bool succeeded = false;
+#if defined(OS_WIN)
+ printing::NativeMetafile metafile;
+ int highest_rendered_page_number = 0;
+ succeeded = RenderPDFToWinMetafile(pdf_file, render_area, render_dpi,
+ page_ranges, &metafile,
+ &highest_rendered_page_number);
+ if (succeeded) {
+ Send(new UtilityHostMsg_RenderPDFPagesToMetafile_Succeeded(metafile,
+ highest_rendered_page_number));
+ }
+#endif // defined(OS_WIN)
+ if (!succeeded) {
+ Send(new UtilityHostMsg_RenderPDFPagesToMetafile_Failed());
+ }
+ ChildProcess::current()->ReleaseProcess();
+}
+
+#if defined(OS_WIN)
+// Exported by pdf.dll
+typedef bool (*RenderPDFPageToDCProc)(
+ const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc,
+ int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y,
+ int bounds_width, int bounds_height, bool fit_to_bounds,
+ bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds);
+
+typedef bool (*GetPDFDocInfoProc)(const unsigned char* pdf_buffer,
+ int buffer_size, int* page_count,
+ double* max_page_width);
+
+// The 2 below IAT patch functions are almost identical to the code in
+// render_process_impl.cc. This is needed to work around specific Windows APIs
+// used by the Chrome PDF plugin that will fail in the sandbox.
+static iat_patch::IATPatchFunction g_iat_patch_createdca;
+HDC WINAPI UtilityProcess_CreateDCAPatch(LPCSTR driver_name,
+ LPCSTR device_name,
+ LPCSTR output,
+ const DEVMODEA* init_data) {
+ if (driver_name &&
+ (std::string("DISPLAY") == std::string(driver_name)))
+ // CreateDC fails behind the sandbox, but not CreateCompatibleDC.
+ return CreateCompatibleDC(NULL);
+
+ NOTREACHED();
+ return CreateDCA(driver_name, device_name, output, init_data);
+}
+
+static iat_patch::IATPatchFunction g_iat_patch_get_font_data;
+DWORD WINAPI UtilityProcess_GetFontDataPatch(
+ HDC hdc, DWORD table, DWORD offset, LPVOID buffer, DWORD length) {
+ int rv = GetFontData(hdc, table, offset, buffer, length);
+ if (rv == GDI_ERROR && hdc) {
+ HFONT font = static_cast<HFONT>(GetCurrentObject(hdc, OBJ_FONT));
+
+ LOGFONT logfont;
+ if (GetObject(font, sizeof(LOGFONT), &logfont)) {
+ std::vector<char> font_data;
+ if (UtilityThread::current()->Send(
+ new UtilityHostMsg_PreCacheFont(logfont)))
+ rv = GetFontData(hdc, table, offset, buffer, length);
+ }
+ }
+ return rv;
+}
+
+bool UtilityThread::RenderPDFToWinMetafile(
+ base::PlatformFile pdf_file,
+ const gfx::Rect& render_area,
+ int render_dpi,
+ const std::vector<printing::PageRange>& page_ranges,
+ printing::NativeMetafile* metafile,
+ int* highest_rendered_page_number) {
+ *highest_rendered_page_number = -1;
+ ScopedHandle file(pdf_file);
+ FilePath pdf_module_path;
+ PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path);
+ HMODULE pdf_module = GetModuleHandle(pdf_module_path.value().c_str());
+ if (!pdf_module)
+ return false;
+
+ RenderPDFPageToDCProc render_proc =
+ reinterpret_cast<RenderPDFPageToDCProc>(
+ GetProcAddress(pdf_module, "RenderPDFPageToDC"));
+ if (!render_proc)
+ return false;
+
+ GetPDFDocInfoProc get_info_proc = reinterpret_cast<GetPDFDocInfoProc>(
+ GetProcAddress(pdf_module, "GetPDFDocInfo"));
+ if (!get_info_proc)
+ return false;
+
+ // Patch the IAT for handling specific APIs known to fail in the sandbox.
+ if (!g_iat_patch_createdca.is_patched())
+ g_iat_patch_createdca.Patch(pdf_module_path.value().c_str(),
+ "gdi32.dll", "CreateDCA",
+ UtilityProcess_CreateDCAPatch);
+
+ if (!g_iat_patch_get_font_data.is_patched())
+ g_iat_patch_get_font_data.Patch(pdf_module_path.value().c_str(),
+ "gdi32.dll", "GetFontData",
+ UtilityProcess_GetFontDataPatch);
+
+ // TODO(sanjeevr): Add a method to the PDF DLL that takes in a file handle
+ // and a page range array. That way we don't need to read the entire PDF into
+ // memory.
+ DWORD length = ::GetFileSize(file, NULL);
+ if (length == INVALID_FILE_SIZE)
+ return false;
+
+ std::vector<uint8> buffer;
+ buffer.resize(length);
+ DWORD bytes_read = 0;
+ if (!ReadFile(pdf_file, &buffer.front(), length, &bytes_read, NULL) ||
+ (bytes_read != length))
+ return false;
+
+ int total_page_count = 0;
+ if (!get_info_proc(&buffer.front(), buffer.size(), &total_page_count, NULL))
+ return false;
+
+ metafile->CreateDc(NULL, NULL);
+ // Since we created the metafile using the screen DPI (but we actually want
+ // the PDF DLL to print using the passed in render_dpi, we apply the following
+ // transformation.
+ SetGraphicsMode(metafile->hdc(), GM_ADVANCED);
+ XFORM xform = {0};
+ int screen_dpi = GetDeviceCaps(GetDC(NULL), LOGPIXELSX);
+ xform.eM11 = xform.eM22 =
+ static_cast<float>(screen_dpi) / static_cast<float>(render_dpi);
+ ModifyWorldTransform(metafile->hdc(), &xform, MWT_LEFTMULTIPLY);
+
+ bool ret = false;
+ std::vector<printing::PageRange>::const_iterator iter;
+ for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
+ for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
+ if (page_number >= total_page_count)
+ break;
+ metafile->StartPage();
+ if (render_proc(&buffer.front(), buffer.size(), page_number,
+ metafile->hdc(), render_dpi, render_dpi,
+ render_area.x(), render_area.y(), render_area.width(),
+ render_area.height(), true, false, true, true))
+ if (*highest_rendered_page_number < page_number)
+ *highest_rendered_page_number = page_number;
+ ret = true;
+ metafile->EndPage();
+ }
+ }
+ metafile->CloseDc();
+ return ret;
+}
+#endif // defined(OS_WIN)
+
diff --git a/chrome/utility/utility_thread.h b/chrome/utility/utility_thread.h
index 9b88bc2..4954cf3 100644
--- a/chrome/utility/utility_thread.h
+++ b/chrome/utility/utility_thread.h
@@ -8,11 +8,21 @@
#include <string>
#include <vector>
+#include "base/platform_file.h"
#include "chrome/common/child_thread.h"
+#include "printing/native_metafile.h"
class GURL;
class SkBitmap;
+namespace gfx {
+class Rect;
+} // namespace gfx
+
+namespace printing {
+struct PageRange;
+} // namespace printing
+
// This class represents the background thread where the utility task runs.
class UtilityThread : public ChildThread {
public:
@@ -38,6 +48,25 @@ class UtilityThread : public ChildThread {
// IPC for decoding an image.
void OnDecodeImage(const std::vector<unsigned char>& encoded_data);
+ // IPC to render a PDF into a platform metafile.
+ void OnRenderPDFPagesToMetafile(
+ base::PlatformFile pdf_file,
+ const gfx::Rect& render_area,
+ int render_dpi,
+ const std::vector<printing::PageRange>& page_ranges);
+
+#if defined(OS_WIN)
+ // Helper method for Windows.
+ // |highest_rendered_page_number| is set to -1 on failure to render any page.
+ bool RenderPDFToWinMetafile(
+ base::PlatformFile pdf_file,
+ const gfx::Rect& render_area,
+ int render_dpi,
+ const std::vector<printing::PageRange>& page_ranges,
+ printing::NativeMetafile* metafile,
+ int* highest_rendered_page_number);
+#endif // defined(OS_WIN)
+
DISALLOW_COPY_AND_ASSIGN(UtilityThread);
};