diff options
-rw-r--r-- | chrome/chrome.gyp | 6 | ||||
-rw-r--r-- | chrome/chrome_common.gypi | 1 | ||||
-rw-r--r-- | chrome/common/common_param_traits.cc | 51 | ||||
-rw-r--r-- | chrome/common/common_param_traits.h | 25 | ||||
-rw-r--r-- | chrome/common/common_param_traits_unittest.cc | 72 | ||||
-rw-r--r-- | chrome/common/utility_messages_internal.h | 27 | ||||
-rw-r--r-- | chrome/service/service_child_process_host.cc | 33 | ||||
-rw-r--r-- | chrome/service/service_child_process_host.h | 29 | ||||
-rw-r--r-- | chrome/service/service_main.cc | 8 | ||||
-rw-r--r-- | chrome/service/service_utility_process_host.cc | 147 | ||||
-rw-r--r-- | chrome/service/service_utility_process_host.h | 120 | ||||
-rw-r--r-- | chrome/utility/utility_main.cc | 14 | ||||
-rw-r--r-- | chrome/utility/utility_thread.cc | 171 | ||||
-rw-r--r-- | chrome/utility/utility_thread.h | 29 |
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); }; |