diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-16 00:29:22 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-16 00:29:22 +0000 |
commit | fbea02332ae95f590c8a84019b582fa35e788a7c (patch) | |
tree | eeff857a083663f3d5556fcff304b5fd05e32eb2 | |
parent | 0018067c7afbdf516d1845b35a3a245d96910f66 (diff) | |
download | chromium_src-fbea02332ae95f590c8a84019b582fa35e788a7c.zip chromium_src-fbea02332ae95f590c8a84019b582fa35e788a7c.tar.gz chromium_src-fbea02332ae95f590c8a84019b582fa35e788a7c.tar.bz2 |
Linux: print page to file rather than using shared memory to send it to the browser.
BUG=9847
adapted from patch by <minyu.huang [at] gmail>
Review URL: http://codereview.chromium.org/203062
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26308 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/file_util.h | 4 | ||||
-rw-r--r-- | base/file_util_posix.cc | 25 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter.cc | 29 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter.h | 10 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter_gtk.cc | 63 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 10 | ||||
-rw-r--r-- | chrome/common/temp_scaffolding_stubs.cc | 48 | ||||
-rw-r--r-- | chrome/common/temp_scaffolding_stubs.h | 4 | ||||
-rw-r--r-- | chrome/renderer/print_web_view_helper_linux.cc | 96 | ||||
-rw-r--r-- | printing/pdf_ps_metafile_linux.cc | 22 | ||||
-rw-r--r-- | printing/pdf_ps_metafile_linux.h | 10 | ||||
-rw-r--r-- | printing/pdf_ps_metafile_linux_unittest.cc | 17 |
12 files changed, 174 insertions, 164 deletions
diff --git a/base/file_util.h b/base/file_util.h index ba8738c..6474f048 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -367,6 +367,10 @@ int ReadFile(const std::wstring& filename, char* data, int size); int WriteFile(const FilePath& filename, const char* data, int size); // Deprecated temporary compatibility function. int WriteFile(const std::wstring& filename, const char* data, int size); +#if defined(OS_POSIX) +// Append the data to |fd|. Does not close |fd| when done. +int WriteFileDescriptor(const int fd, const char* data, int size); +#endif // Gets the current working directory for the process. bool GetCurrentDirectory(FilePath* path); diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index bd18a83..7c212c0 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -513,20 +513,23 @@ int WriteFile(const FilePath& filename, const char* data, int size) { if (fd < 0) return -1; - // Allow for partial writes + int rv = WriteFileDescriptor(fd, data, size); + HANDLE_EINTR(close(fd)); + return rv; +} + +int WriteFileDescriptor(const int fd, const char* data, int size) { + // Allow for partial writes. ssize_t bytes_written_total = 0; - do { - ssize_t bytes_written_partial = - HANDLE_EINTR(write(fd, data + bytes_written_total, - size - bytes_written_total)); - if (bytes_written_partial < 0) { - HANDLE_EINTR(close(fd)); + for (ssize_t bytes_written_partial = 0; bytes_written_total < size; + bytes_written_total += bytes_written_partial) { + bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + size - bytes_written_total)); + if (bytes_written_partial < 0) return -1; - } - bytes_written_total += bytes_written_partial; - } while (bytes_written_total < size); + } - HANDLE_EINTR(close(fd)); return bytes_written_total; } diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 095131f..4a5de3a 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -339,8 +339,10 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_DuplicateSection, OnDuplicateSection) #endif #if defined(OS_LINUX) - IPC_MESSAGE_HANDLER(ViewHostMsg_AllocateShareMemory, - OnAllocateShareMemory) + IPC_MESSAGE_HANDLER(ViewHostMsg_AllocateTempFileForPrinting, + OnAllocateTempFileForPrinting) + IPC_MESSAGE_HANDLER(ViewHostMsg_TempFileForPrintingWritten, + OnTempFileForPrintingWritten) #endif IPC_MESSAGE_HANDLER(ViewHostMsg_ResourceTypeStats, OnResourceTypeStats) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ResolveProxy, OnResolveProxy) @@ -706,29 +708,6 @@ void ResourceMessageFilter::OnDuplicateSection( } #endif -#if defined(OS_LINUX) -void ResourceMessageFilter::OnAllocateShareMemory( - size_t buffer_size, - base::SharedMemoryHandle* browser_handle) { - // We don't want to allocate a super big chunk of memory. - // 32MB should be large enough for printing on Linux. - if (buffer_size > 32 * 1024 * 1024) { - *browser_handle = base::SharedMemory::NULLHandle(); - NOTREACHED() << "Buffer too large: " << buffer_size; - return; - } - base::SharedMemory shared_buf; - shared_buf.Create(L"", false, false, buffer_size); - if (shared_buf.Map(buffer_size)) { - shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle); - } else { - *browser_handle = base::SharedMemory::NULLHandle(); - NOTREACHED() << "Cannot map buffer"; - return; - } -} -#endif - void ResourceMessageFilter::OnResourceTypeStats( const WebCache::ResourceTypeStats& stats) { HISTOGRAM_COUNTS("WebCoreCache.ImagesSizeKB", diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 897e373..dceaa3d 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -52,6 +52,9 @@ struct WebScreenInfo; } struct ViewHostMsg_ScriptedPrint_Params; +#if defined(OS_LINUX) +struct ViewHostMsg_DidPrintPage_Params; +#endif // This class filters out incoming IPC messages for network requests and // processes them on the IPC thread. As a result, network requests are not @@ -204,10 +207,11 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, #endif #if defined(OS_LINUX) - // Used to ask the browser allocate a block of shared memory for the renderer + // Used to ask the browser allocate a temporary file for the renderer // to fill in resulting PDF in renderer. - void OnAllocateShareMemory(size_t buffer_size, - base::SharedMemoryHandle* browser_handle); + void OnAllocateTempFileForPrinting(base::FileDescriptor* temp_file_fd, + int* fd_in_browser); + void OnTempFileForPrintingWritten(int fd_in_browser); #endif void OnResourceTypeStats(const WebKit::WebCache::ResourceTypeStats& stats); diff --git a/chrome/browser/renderer_host/resource_message_filter_gtk.cc b/chrome/browser/renderer_host/resource_message_filter_gtk.cc index 1dd964d..8533cac 100644 --- a/chrome/browser/renderer_host/resource_message_filter_gtk.cc +++ b/chrome/browser/renderer_host/resource_message_filter_gtk.cc @@ -4,11 +4,20 @@ #include "chrome/browser/renderer_host/resource_message_filter.h" +#include <fcntl.h> +#include <map> + +#include "app/l10n_util.h" #include "base/clipboard.h" +#include "base/file_util.h" #include "base/gfx/gtk_native_view_id_manager.h" +#include "base/path_service.h" +#include "base/singleton.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/render_messages.h" #include "chrome/common/x11_util.h" +#include "grit/generated_resources.h" #include "webkit/api/public/WebScreenInfo.h" #include "webkit/api/public/x11/WebScreenInfoFactory.h" @@ -16,6 +25,16 @@ using WebKit::WebScreenInfo; using WebKit::WebScreenInfoFactory; +namespace { + +typedef std::map<int, FilePath> FdMap; + +struct PrintingFileDescriptorMap { + FdMap map; +}; + +} // namespace + // We get null window_ids passed into the two functions below; please see // http://crbug.com/9060 for more details. @@ -210,3 +229,47 @@ void ResourceMessageFilter::OnClipboardReadHTML(Clipboard::Buffer buffer, this, &ResourceMessageFilter::DoOnClipboardReadHTML, buffer, reply_msg)); } + +// Called on the IO thread. +void ResourceMessageFilter::OnAllocateTempFileForPrinting( + base::FileDescriptor* temp_file_fd, int* fd_in_browser) { + temp_file_fd->fd = *fd_in_browser = -1; + + FilePath path; + if (!file_util::CreateTemporaryFile(&path)) + return; + + int fd = open(path.value().c_str(), O_WRONLY); + if (fd < 0) + return; + + // We need to remember the FilePath of the temporary file because we need + // it when we want to rename/move it, and more importantly, to print it + // when we print by using gtk_print_job_set_source_file(). + FdMap* map = &Singleton<PrintingFileDescriptorMap>::get()->map; + FdMap::iterator it = map->find(fd); + if (it != map->end()) { + NOTREACHED() << "The file descriptor is in use. fd=" << fd; + return; + } + + (*map)[fd] = path; + temp_file_fd->fd = *fd_in_browser = fd; + temp_file_fd->auto_close = true; +} + +// Called on the IO thread. +void ResourceMessageFilter::OnTempFileForPrintingWritten(int fd_in_browser) { + FdMap* map = &Singleton<PrintingFileDescriptorMap>::get()->map; + FdMap::iterator it = map->find(fd_in_browser); + if (it == map->end()) { + NOTREACHED() << "Got a file descriptor that we didn't pass to the " + "renderer: " << fd_in_browser; + return; + } + + // TODO(estade): print it. + + // Erase the entry in the map. + map->erase(it); +} diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 806c0e5..2916c17 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1396,11 +1396,13 @@ IPC_BEGIN_MESSAGES(ViewHost) #endif #if defined(OS_LINUX) - // Asks the browser create a block of shared memory for the renderer to fill + // Asks the browser create a temporary file for the renderer to fill // in resulting NativeMetafile in printing. - IPC_SYNC_MESSAGE_ROUTED1_1(ViewHostMsg_AllocateShareMemory, - size_t /* buffer size */, - base::SharedMemoryHandle /* browser handle */) + IPC_SYNC_MESSAGE_CONTROL0_2(ViewHostMsg_AllocateTempFileForPrinting, + base::FileDescriptor /* temp file fd */, + int /* fd in browser*/) + IPC_MESSAGE_CONTROL1(ViewHostMsg_TempFileForPrintingWritten, + int /* fd in browser */) #endif // Provide the browser process with information about the WebCore resource diff --git a/chrome/common/temp_scaffolding_stubs.cc b/chrome/common/temp_scaffolding_stubs.cc index 9b1ed6d..76d2d60 100644 --- a/chrome/common/temp_scaffolding_stubs.cc +++ b/chrome/common/temp_scaffolding_stubs.cc @@ -13,12 +13,8 @@ #include "chrome/browser/rlz/rlz.h" #if defined(OS_LINUX) -#include "base/path_service.h" #include "chrome/browser/dock_info.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/common/chrome_paths.h" #include "chrome/common/render_messages.h" -#include "printing/native_metafile.h" #endif #if defined(OS_MACOSX) @@ -326,47 +322,3 @@ void BookmarkManager::Show(Profile* profile) { #endif -//------------------------------------------------------------------------------ - -#if defined(OS_LINUX) -// TODO(myhuang): This is a quick hack for testing purpose. We should implement -// PrintViewManager and other related classes later on Linux. -namespace printing { - -void PrintViewManager::DidPrintPage( - const ViewHostMsg_DidPrintPage_Params& params) { - base::SharedMemory shared_buf(params.metafile_data_handle, true); - if (!shared_buf.Map(params.data_size)) { - NOTREACHED() << "couldn't map"; - owner_.Stop(); - return; - } - - // The only format we can use now is PDF since Cairo needs to create a - // temporary file for a PostScript surface. We do not allow disk I/O in the - // renderer. - scoped_ptr<NativeMetafile> metafile( - new NativeMetafile(printing::NativeMetafile::PDF)); - - if (!metafile->Init(shared_buf.memory(), params.data_size)) { - NOTREACHED() << "Invalid metafile header"; - shared_buf.Unmap(); - owner_.Stop(); - return; - } - - // Save the PDF file to default download location. - // NOTE: Existing file will be overwritten. - FilePath default_save_path; - if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &default_save_path)) { - NOTREACHED(); - } - FilePath save_filename = - default_save_path.Append(FilePath("chromium_printing_test.pdf")); - metafile->SaveTo(save_filename); - shared_buf.Unmap(); -} - -} // namespace printing - -#endif diff --git a/chrome/common/temp_scaffolding_stubs.h b/chrome/common/temp_scaffolding_stubs.h index 7b01f31..6f186ff 100644 --- a/chrome/common/temp_scaffolding_stubs.h +++ b/chrome/common/temp_scaffolding_stubs.h @@ -67,13 +67,9 @@ class PrintViewManager : public RenderViewHostDelegate::Printing { NOTIMPLEMENTED(); } -#if defined(OS_LINUX) - virtual void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params); -#else virtual void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params) { NOTIMPLEMENTED(); } -#endif private: TabContents& owner_; diff --git a/chrome/renderer/print_web_view_helper_linux.cc b/chrome/renderer/print_web_view_helper_linux.cc index 211eb6f..b8e0ca2 100644 --- a/chrome/renderer/print_web_view_helper_linux.cc +++ b/chrome/renderer/print_web_view_helper_linux.cc @@ -4,6 +4,7 @@ #include "chrome/renderer/print_web_view_helper.h" +#include "base/file_descriptor_posix.h" #include "base/logging.h" #include "chrome/common/render_messages.h" #include "printing/native_metafile.h" @@ -53,64 +54,49 @@ void PrintWebViewHelper::PrintPages(const ViewMsg_PrintPages_Params& params, // TODO(myhuang): Send ViewHostMsg_DidGetPrintedPagesCount. - if (page_count) { - // We only can use PDF in the renderer because Cairo needs to create a - // temporary file for a PostScript surface. - printing::NativeMetafile metafile(printing::NativeMetafile::PDF); - metafile.Init(); - - ViewMsg_PrintPage_Params print_page_params; - print_page_params.params = params.params; - const gfx::Size& canvas_size = prep_frame_view.GetPrintCanvasSize(); - if (params.pages.empty()) { - for (int i = 0; i < page_count; ++i) { - print_page_params.page_number = i; - PrintPage(print_page_params, canvas_size, frame, &metafile); - } - } else { - for (size_t i = 0; i < params.pages.size(); ++i) { - print_page_params.page_number = params.pages[i]; - PrintPage(print_page_params, canvas_size, frame, &metafile); - } - } + if (page_count == 0) + return; - metafile.Close(); - - // Get the size of the resulting metafile. - unsigned int buf_size = metafile.GetDataSize(); - DCHECK_GT(buf_size, 0u); - - ViewHostMsg_DidPrintPage_Params did_page_params; - - // Ask the browser create the shared memory for us. - if (Send(new ViewHostMsg_AllocateShareMemory( - routing_id(), - buf_size, - &did_page_params.metafile_data_handle))) { - if (did_page_params.metafile_data_handle.fd > -1) { - base::SharedMemory shared_buf(did_page_params.metafile_data_handle, - false); - if (shared_buf.Map(buf_size)) { - if (metafile.GetData(shared_buf.memory(), buf_size)) { - // FIXME(myhuang): This is for testing purpose at this moment. - // We use this message to pass the resulting PDF to the browser, - // and the browser will save this PDF on the disk. - did_page_params.data_size = buf_size; - Send(new ViewHostMsg_DidPrintPage(routing_id(), did_page_params)); - } else { - NOTREACHED() << "GetData() failed"; - } - shared_buf.Unmap(); - } else { - NOTREACHED() << "Buffer mapping failed"; - } - } else { - NOTREACHED() << "Buffer allocation failed"; - } - } else { - NOTREACHED() << "Buffer allocation failed"; + // We only can use PDF in the renderer because Cairo needs to create a + // temporary file for a PostScript surface. + printing::NativeMetafile metafile(printing::NativeMetafile::PDF); + metafile.Init(); + + ViewMsg_PrintPage_Params print_page_params; + print_page_params.params = params.params; + const gfx::Size& canvas_size = prep_frame_view.GetPrintCanvasSize(); + if (params.pages.empty()) { + for (int i = 0; i < page_count; ++i) { + print_page_params.page_number = i; + PrintPage(print_page_params, canvas_size, frame, &metafile); + } + } else { + for (size_t i = 0; i < params.pages.size(); ++i) { + print_page_params.page_number = params.pages[i]; + PrintPage(print_page_params, canvas_size, frame, &metafile); } } + + metafile.Close(); + + // Get the size of the resulting metafile. + unsigned int buf_size = metafile.GetDataSize(); + DCHECK_GT(buf_size, 0u); + + base::FileDescriptor fd; + int fd_in_browser = -1; + + // Ask the browser to open a file for us. + if (!Send(new ViewHostMsg_AllocateTempFileForPrinting(&fd, + &fd_in_browser))) { + return; + } + + if (!metafile.SaveTo(fd)) + return; + + // Tell the browser we've finished writing the file. + Send(new ViewHostMsg_TempFileForPrintingWritten(fd_in_browser)); } void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params, diff --git a/printing/pdf_ps_metafile_linux.cc b/printing/pdf_ps_metafile_linux.cc index d55795c..d5fe5fb 100644 --- a/printing/pdf_ps_metafile_linux.cc +++ b/printing/pdf_ps_metafile_linux.cc @@ -16,6 +16,8 @@ #include <map> +#include "base/eintr_wrapper.h" +#include "base/file_descriptor_posix.h" #include "base/file_util.h" #include "base/logging.h" #include "base/singleton.h" @@ -471,7 +473,7 @@ bool PdfPsMetafile::GetData(void* dst_buffer, size_t dst_buffer_size) const { return true; } -bool PdfPsMetafile::SaveTo(const FilePath& filename) const { +bool PdfPsMetafile::SaveTo(const base::FileDescriptor& fd) const { // We need to check at least these two members to ensure that either Init() // has been called to initialize |all_pages_|, or metafile has been closed. // Passing these two checks also implies that surface_, page_surface_, and @@ -479,15 +481,21 @@ bool PdfPsMetafile::SaveTo(const FilePath& filename) const { DCHECK(!context_); DCHECK(!all_pages_.empty()); - const unsigned int data_size = GetDataSize(); - const unsigned int bytes_written = - file_util::WriteFile(filename, all_pages_.data(), data_size); - if (bytes_written != data_size) { - DLOG(ERROR) << "Failed to save file: " << filename.value(); + if (fd.fd < 0) { + DLOG(ERROR) << "Invalid file descriptor!"; return false; } - return true; + bool success = true; + if (file_util::WriteFileDescriptor(fd.fd, all_pages_.data(), + GetDataSize()) < 0) { + DLOG(ERROR) << "Failed to save file with fd " << fd.fd; + success = false; + } + + if (fd.auto_close) + HANDLE_EINTR(close(fd.fd)); + return success; } void PdfPsMetafile::CleanUpAll() { diff --git a/printing/pdf_ps_metafile_linux.h b/printing/pdf_ps_metafile_linux.h index 04b0ca1e..faf6ab7 100644 --- a/printing/pdf_ps_metafile_linux.h +++ b/printing/pdf_ps_metafile_linux.h @@ -12,6 +12,10 @@ typedef struct _cairo_surface cairo_surface_t; typedef struct _cairo cairo_t; +namespace base { +class FileDescriptor; +} + class FilePath; namespace printing { @@ -77,10 +81,10 @@ class PdfPsMetafile { // Returns true only when success. bool GetData(void* dst_buffer, size_t dst_buffer_size) const; - // Saves PDF/PS contents stored in buffer |all_pages_| into |filename| on - // the disk. + // Saves PDF/PS contents stored in buffer |all_pages_| into the file + // associated with |fd|. // This function should ONLY be called after PDF/PS file is closed. - bool SaveTo(const FilePath& filename) const; + bool SaveTo(const base::FileDescriptor& fd) const; private: // Cleans up all resources. diff --git a/printing/pdf_ps_metafile_linux_unittest.cc b/printing/pdf_ps_metafile_linux_unittest.cc index 7339559..e852b37 100644 --- a/printing/pdf_ps_metafile_linux_unittest.cc +++ b/printing/pdf_ps_metafile_linux_unittest.cc @@ -4,15 +4,24 @@ #include "printing/pdf_ps_metafile_linux.h" +#include <fcntl.h> #include <string> #include <vector> +#include "base/file_descriptor_posix.h" #include "base/file_util.h" #include "testing/gtest/include/gtest/gtest.h" typedef struct _cairo cairo_t; -TEST(PdfTest, ThreePages) { +class PdfPsTest : public testing::Test { + protected: + base::FileDescriptor DevNullFD() { + return base::FileDescriptor(open("/dev/null", O_WRONLY), true); + } +}; + +TEST_F(PdfPsTest, Pdf) { // Tests in-renderer constructor. printing::PdfPsMetafile pdf(printing::PdfPsMetafile::PDF); EXPECT_TRUE(pdf.Init()); @@ -53,10 +62,10 @@ TEST(PdfTest, ThreePages) { EXPECT_EQ(header.find("%PDF", 0), 0u); // Tests if we can save data. - EXPECT_TRUE(pdf.SaveTo(FilePath("/dev/null"))); + EXPECT_TRUE(pdf.SaveTo(DevNullFD())); } -TEST(PsTest, TwoPages) { +TEST_F(PdfPsTest, Ps) { // Tests in-renderer constructor. printing::PdfPsMetafile ps(printing::PdfPsMetafile::PS); EXPECT_TRUE(ps.Init()); @@ -97,5 +106,5 @@ TEST(PsTest, TwoPages) { EXPECT_EQ(header.find("%!PS", 0), 0u); // Tests if we can save data. - EXPECT_TRUE(ps.SaveTo(FilePath("/dev/null"))); + EXPECT_TRUE(ps.SaveTo(DevNullFD())); } |