diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-03-12 12:55:30 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-03-20 00:35:30 +0000 |
commit | 831d20bfb1bd677245c1fbdd5ded0fc757c44541 (patch) | |
tree | cee7be7e1339515100d0b525363682a87a91e50a /src/core/printing | |
parent | ff2cccda960027744e63392a6b6ef6ef8a4657ba (diff) |
Cleanup file locations
Move printing and network specific classes to subdirectories so we have
fewer files in the main dir.
Change-Id: I675b1b8b8fd1588061104cec181087f305b44f98
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
Diffstat (limited to 'src/core/printing')
-rw-r--r-- | src/core/printing/pdfium_document_wrapper_qt.cpp | 171 | ||||
-rw-r--r-- | src/core/printing/pdfium_document_wrapper_qt.h | 71 | ||||
-rw-r--r-- | src/core/printing/print_view_manager_base_qt.cpp | 522 | ||||
-rw-r--r-- | src/core/printing/print_view_manager_base_qt.h | 178 | ||||
-rw-r--r-- | src/core/printing/print_view_manager_qt.cpp | 376 | ||||
-rw-r--r-- | src/core/printing/print_view_manager_qt.h | 141 | ||||
-rw-r--r-- | src/core/printing/printing_message_filter_qt.cpp | 238 | ||||
-rw-r--r-- | src/core/printing/printing_message_filter_qt.h | 132 |
8 files changed, 1829 insertions, 0 deletions
diff --git a/src/core/printing/pdfium_document_wrapper_qt.cpp b/src/core/printing/pdfium_document_wrapper_qt.cpp new file mode 100644 index 000000000..ca1e8cd07 --- /dev/null +++ b/src/core/printing/pdfium_document_wrapper_qt.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "pdf/features.h" +#if BUILDFLAG(ENABLE_PDF) +#define ENABLE_PDF +#endif + +#if defined(ENABLE_PDF) +#include "pdfium_document_wrapper_qt.h" + +#include <QtCore/qhash.h> +#include <QtGui/qimage.h> +#include <QtGui/qpainter.h> + +#include "third_party/pdfium/public/fpdf_doc.h" +#include "third_party/pdfium/public/fpdfview.h" + +namespace QtWebEngineCore { +int PdfiumDocumentWrapperQt::m_libraryUsers = 0; + +class QWEBENGINE_EXPORT PdfiumPageWrapperQt { +public: + PdfiumPageWrapperQt(void *data, int pageIndex, int targetWidth, int targetHeight) + : m_pageData(FPDF_LoadPage(data, pageIndex)) + , m_width(FPDF_GetPageWidth(m_pageData)) + , m_height(FPDF_GetPageHeight(m_pageData)) + , m_index(pageIndex) + , m_image(createImage(targetWidth, targetHeight)) + { + } + + PdfiumPageWrapperQt() + : m_pageData(nullptr) + , m_width(-1) + , m_height(-1) + , m_index(-1) + , m_image(QImage()) + { + } + + virtual ~PdfiumPageWrapperQt() + { + FPDF_ClosePage(m_pageData); + } + + QImage image() + { + return m_image; + } + +private: + QImage createImage(int targetWidth, int targetHeight) + { + Q_ASSERT(m_pageData); + if (targetWidth <= 0) + targetWidth = m_width; + + if (targetHeight <= 0) + targetHeight = m_height; + + QImage image(targetWidth, targetHeight, QImage::Format_RGBA8888); + Q_ASSERT(!image.isNull()); + image.fill(0xFFFFFFFF); + + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(image.width(), image.height(), + FPDFBitmap_BGRA, + image.scanLine(0), image.bytesPerLine()); + Q_ASSERT(bitmap); + + FPDF_RenderPageBitmap(bitmap, m_pageData, + 0, 0, image.width(), image.height(), + 0, 0); + FPDFBitmap_Destroy(bitmap); + bitmap = nullptr; + + // Map BGRA to RGBA as PDFium currently does not support RGBA bitmaps directly + for (int i = 0; i < image.height(); i++) { + uchar *pixels = image.scanLine(i); + for (int j = 0; j < image.width(); j++) { + qSwap(pixels[0], pixels[2]); + pixels += 4; + } + } + return image; + } + +private: + void *m_pageData; + int m_width; + int m_height; + int m_index; + QImage m_image; +}; + + +PdfiumDocumentWrapperQt::PdfiumDocumentWrapperQt(const void *pdfData, size_t size, + const QSize& imageSize, + const char *password) + : m_imageSize(imageSize * 2.0) +{ + Q_ASSERT(pdfData); + Q_ASSERT(size); + if (m_libraryUsers++ == 0) + FPDF_InitLibrary(); + + m_documentHandle = FPDF_LoadMemDocument(pdfData, static_cast<int>(size), password); + m_pageCount = FPDF_GetPageCount(m_documentHandle); +} + +QImage PdfiumDocumentWrapperQt::pageAsQImage(size_t index) +{ + if (!m_documentHandle || !m_pageCount) { + qWarning("Failure to generate QImage from invalid or empty PDF document."); + return QImage(); + } + + if (static_cast<int>(index) >= m_pageCount) { + qWarning("Failure to generate QImage from PDF data: index out of bounds."); + return QImage(); + } + + PdfiumPageWrapperQt pageWrapper(m_documentHandle, index, + m_imageSize.width(), m_imageSize.height()); + return pageWrapper.image(); +} + +PdfiumDocumentWrapperQt::~PdfiumDocumentWrapperQt() +{ + FPDF_CloseDocument(m_documentHandle); + if (--m_libraryUsers == 0) + FPDF_DestroyLibrary(); +} + +} +#endif // defined (ENABLE_PDF) diff --git a/src/core/printing/pdfium_document_wrapper_qt.h b/src/core/printing/pdfium_document_wrapper_qt.h new file mode 100644 index 000000000..28c490ae5 --- /dev/null +++ b/src/core/printing/pdfium_document_wrapper_qt.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PDFIUM_DOCUMENT_WRAPPER_QT_H +#define PDFIUM_DOCUMENT_WRAPPER_QT_H + +#if defined(ENABLE_PDF) +#include "qtwebenginecoreglobal.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qhash.h> +#include <QtGui/qimage.h> + +namespace QtWebEngineCore { +class PdfiumPageWrapperQt; + +class QWEBENGINE_EXPORT PdfiumDocumentWrapperQt +{ +public: + PdfiumDocumentWrapperQt(const void *pdfData, size_t size, const QSize &imageSize, + const char *password = nullptr); + virtual ~PdfiumDocumentWrapperQt(); + QImage pageAsQImage(size_t index); + int pageCount() const { return m_pageCount; } + +private: + static int m_libraryUsers; + int m_pageCount; + void *m_documentHandle; + QSize m_imageSize; +}; + +} // namespace QtWebEngineCore +#endif // defined (ENABLE_PDF) +#endif // PDFIUM_DOCUMENT_WRAPPER_QT_H diff --git a/src/core/printing/print_view_manager_base_qt.cpp b/src/core/printing/print_view_manager_base_qt.cpp new file mode 100644 index 000000000..abc1edf74 --- /dev/null +++ b/src/core/printing/print_view_manager_base_qt.cpp @@ -0,0 +1,522 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This is based on chrome/browser/printing/print_view_manager_base.cc: +// Copyright 2013 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.Chromium file. + +#include "print_view_manager_qt.h" + +#include "type_conversion.h" +#include "web_engine_context.h" + +#include "base/memory/ref_counted_memory.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/timer/timer.h" +#include "base/values.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/printing/print_job.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printer_query.h" +#include "components/printing/common/print_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "printing/pdf_metafile_skia.h" +#include "printing/print_job_constants.h" +#include "printing/printed_document.h" + +namespace QtWebEngineCore { + +PrintViewManagerBaseQt::PrintViewManagerBaseQt(content::WebContents *contents) + : printing::PrintManager(contents) + , cookie_(0) + , m_isInsideInnerMessageLoop(false) +#if !defined(OS_MACOSX) + , m_isExpectingFirstPage(false) +#endif + , m_didPrintingSucceed(false) + , m_printerQueriesQueue(WebEngineContext::current()->getPrintJobManager()->queue()) +{ + // FIXME: Check if this needs to be executed async: + PrintViewManagerBaseQt::UpdatePrintingEnabled(); +} + +PrintViewManagerBaseQt::~PrintViewManagerBaseQt() +{ + ReleasePrinterQuery(); + DisconnectFromCurrentPrintJob(); +} + +void PrintViewManagerBaseQt::UpdatePrintingEnabled() +{ + bool enabled = false; +#if BUILDFLAG(ENABLE_BASIC_PRINTING) + enabled = true; +#endif + web_contents()->ForEachFrame( + base::Bind(&PrintViewManagerBaseQt::SendPrintingEnabled, + base::Unretained(this), enabled)); +} + +void PrintViewManagerBaseQt::NavigationStopped() +{ + // Cancel the current job, wait for the worker to finish. + TerminatePrintJob(true); +} + +base::string16 PrintViewManagerBaseQt::RenderSourceName() +{ + return toString16(QLatin1String("")); +} + +bool PrintViewManagerBaseQt::PrintDocument(printing::PrintedDocument *document, + const scoped_refptr<base::RefCountedBytes> &print_data, + const gfx::Size &page_size, + const gfx::Rect &content_area, + const gfx::Point &offsets) +{ + std::unique_ptr<printing::PdfMetafileSkia> metafile = + std::make_unique<printing::PdfMetafileSkia>(printing::SkiaDocumentType::PDF); + if (!metafile->InitFromData(print_data->front(), print_data->size())) { + NOTREACHED() << "Invalid metafile"; + web_contents()->Stop(); + return false; + } + + // Update the rendered document. It will send notifications to the listener. + document->SetDocument(std::move(metafile), page_size, content_area); + ShouldQuitFromInnerMessageLoop(); + return true; +} + +printing::PrintedDocument *PrintViewManagerBaseQt::GetDocument(int cookie) +{ + if (!OpportunisticallyCreatePrintJob(cookie)) + return nullptr; + + printing::PrintedDocument* document = m_printJob->document(); + if (!document || cookie != document->cookie()) { + // Out of sync. It may happen since we are completely asynchronous. Old + // spurious messages can be received if one of the processes is overloaded. + return nullptr; + } + return document; +} + +// IPC handlers +void PrintViewManagerBaseQt::OnDidPrintDocument(const PrintHostMsg_DidPrintDocument_Params ¶ms) +{ + printing::PrintedDocument *document = GetDocument(params.document_cookie); + if (!document) + return; + + if (!base::SharedMemory::IsHandleValid(params.metafile_data_handle)) { + NOTREACHED() << "invalid memory handle"; + web_contents()->Stop(); + return; + } + + std::unique_ptr<base::SharedMemory> shared_buf = + std::make_unique<base::SharedMemory>(params.metafile_data_handle, true); + if (!shared_buf->Map(params.data_size)) { + NOTREACHED() << "couldn't map"; + web_contents()->Stop(); + return; + } + scoped_refptr<base::RefCountedBytes> bytes = + base::MakeRefCounted<base::RefCountedBytes>( + reinterpret_cast<const unsigned char*>(shared_buf->memory()), params.data_size); + PrintDocument(document, bytes, params.page_size, params.content_area, params.physical_offsets); +} + +void PrintViewManagerBaseQt::OnShowInvalidPrinterSettingsError() +{ +} + +void PrintViewManagerBaseQt::DidStartLoading() +{ + UpdatePrintingEnabled(); +} + +void PrintViewManagerBaseQt::RenderFrameDeleted(content::RenderFrameHost *render_frame_host) +{ + // Terminates or cancels the print job if one was pending. + if (render_frame_host != web_contents()->GetMainFrame()) + return; + + PrintManager::PrintingRenderFrameDeleted(); + ReleasePrinterQuery(); + + if (!m_printJob.get()) + return; + + scoped_refptr<printing::PrintedDocument> document(m_printJob->document()); + if (document.get()) { + // If IsComplete() returns false, the document isn't completely rendered. + // Since our renderer is gone, there's nothing to do, cancel it. Otherwise, + // the print job may finish without problem. + TerminatePrintJob(!document->IsComplete()); + } +} + +bool PrintViewManagerBaseQt::OnMessageReceived(const IPC::Message& message, content::RenderFrameHost* render_frame_host) +{ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBaseQt, message) + IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintDocument, OnDidPrintDocument) + IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError, OnShowInvalidPrinterSettingsError); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled || PrintManager::OnMessageReceived(message, render_frame_host); +} + +void PrintViewManagerBaseQt::Observe(int type, + const content::NotificationSource& /*source*/, + const content::NotificationDetails& details) +{ + DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type); + OnNotifyPrintJobEvent(*content::Details<printing::JobEventDetails>(details).ptr()); +} + +void PrintViewManagerBaseQt::OnNotifyPrintJobEvent(const printing::JobEventDetails& event_details) +{ + switch (event_details.type()) { + case printing::JobEventDetails::FAILED: + TerminatePrintJob(true); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_RELEASED, + content::Source<content::WebContents>(web_contents()), + content::NotificationService::NoDetails()); + break; + case printing::JobEventDetails::USER_INIT_DONE: + case printing::JobEventDetails::DEFAULT_INIT_DONE: + case printing::JobEventDetails::USER_INIT_CANCELED: + NOTREACHED(); + break; + case printing::JobEventDetails::ALL_PAGES_REQUESTED: + break; + case printing::JobEventDetails::NEW_DOC: +#if defined(OS_WIN) + case printing::JobEventDetails::PAGE_DONE: +#endif + case printing::JobEventDetails::DOC_DONE: + // Don't care about the actual printing process. + break; + case printing::JobEventDetails::JOB_DONE: + // Printing is done, we don't need it anymore. + // print_job_->is_job_pending() may still be true, depending on the order + // of object registration. + m_didPrintingSucceed = true; + ReleasePrintJob(); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_RELEASED, + content::Source<content::WebContents>(web_contents()), + content::NotificationService::NoDetails()); + break; + default: + NOTREACHED(); + break; + } +} + +// Requests the RenderView to render all the missing pages for the print job. +// No-op if no print job is pending. Returns true if at least one page has +// been requested to the renderer. +bool PrintViewManagerBaseQt::RenderAllMissingPagesNow() +{ + if (!m_printJob.get() || !m_printJob->is_job_pending()) + return false; + + // We can't print if there is no renderer. + if (!web_contents() || + !web_contents()->GetRenderViewHost() || + !web_contents()->GetRenderViewHost()->IsRenderViewLive()) { + return false; + } + + // Is the document already complete? + if (m_printJob->document() && m_printJob->document()->IsComplete()) { + m_didPrintingSucceed = true; + return true; + } + + // WebContents is either dying or a second consecutive request to print + // happened before the first had time to finish. We need to render all the + // pages in an hurry if a print_job_ is still pending. No need to wait for it + // to actually spool the pages, only to have the renderer generate them. Run + // a message loop until we get our signal that the print job is satisfied. + // PrintJob will send a ALL_PAGES_REQUESTED after having received all the + // pages it needs. MessageLoop::current()->Quit() will be called as soon as + // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED + // or in DidPrintPage(). The check is done in + // ShouldQuitFromInnerMessageLoop(). + // BLOCKS until all the pages are received. (Need to enable recursive task) + if (!RunInnerMessageLoop()) { + // This function is always called from DisconnectFromCurrentPrintJob() so we + // know that the job will be stopped/canceled in any case. + return false; + } + return true; +} + +// Quits the current message loop if these conditions hold true: a document is +// loaded and is complete and waiting_for_pages_to_be_rendered_ is true. This +// function is called in DidPrintPage() or on ALL_PAGES_REQUESTED +// notification. The inner message loop is created was created by +// RenderAllMissingPagesNow(). +void PrintViewManagerBaseQt::ShouldQuitFromInnerMessageLoop() +{ + // Look at the reason. + DCHECK(m_printJob->document()); + if (m_printJob->document() && + m_printJob->document()->IsComplete() && + m_isInsideInnerMessageLoop) { + // We are in a message loop created by RenderAllMissingPagesNow. Quit from + // it. + base::MessageLoop::current()->QuitWhenIdleClosure(); + m_isInsideInnerMessageLoop = false; + } +} + +bool PrintViewManagerBaseQt::CreateNewPrintJob(printing::PrintJobWorkerOwner* job) +{ + DCHECK(!m_isInsideInnerMessageLoop); + + // Disconnect the current |m_printJob|. + DisconnectFromCurrentPrintJob(); + + // We can't print if there is no renderer. + if (!web_contents()->GetRenderViewHost() || + !web_contents()->GetRenderViewHost()->IsRenderViewLive()) { + return false; + } + + // Ask the renderer to generate the print preview, create the print preview + // view and switch to it, initialize the printer and show the print dialog. + DCHECK(!m_printJob.get()); + DCHECK(job); + if (!job) + return false; + + m_printJob = new printing::PrintJob(); + m_printJob->Initialize(job, RenderSourceName(), number_pages_); + m_registrar.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source<printing::PrintJob>(m_printJob.get())); + m_didPrintingSucceed = false; + return true; +} + +void PrintViewManagerBaseQt::DisconnectFromCurrentPrintJob() +{ + // Make sure all the necessary rendered page are done. Don't bother with the + // return value. + bool result = RenderAllMissingPagesNow(); + + // Verify that assertion. + if (m_printJob.get() && + m_printJob->document() && + !m_printJob->document()->IsComplete()) { + DCHECK(!result); + // That failed. + TerminatePrintJob(true); + } else { + // DO NOT wait for the job to finish. + ReleasePrintJob(); + } +#if !defined(OS_MACOSX) + m_isExpectingFirstPage = true; +#endif +} + +void PrintViewManagerBaseQt::TerminatePrintJob(bool cancel) +{ + if (!m_printJob.get()) + return; + + if (cancel) { + // We don't need the metafile data anymore because the printing is canceled. + m_printJob->Cancel(); + m_isInsideInnerMessageLoop = false; + } else { + DCHECK(!m_isInsideInnerMessageLoop); + DCHECK(!m_printJob->document() || m_printJob->document()->IsComplete()); + + // WebContents is either dying or navigating elsewhere. We need to render + // all the pages in an hurry if a print job is still pending. This does the + // trick since it runs a blocking message loop: + m_printJob->Stop(); + } + ReleasePrintJob(); +} + +void PrintViewManagerBaseQt::ReleasePrintJob() +{ + content::RenderFrameHost *rfh = web_contents() ? web_contents()->GetMainFrame() : nullptr; + + if (!m_printJob.get()) + return; + + if (rfh) + rfh->Send(new PrintMsg_PrintingDone(rfh->GetRoutingID(), m_didPrintingSucceed)); + + m_registrar.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source<printing::PrintJob>(m_printJob.get())); + // Don't close the worker thread. + m_printJob = nullptr; +} + + +bool PrintViewManagerBaseQt::RunInnerMessageLoop() { + // This value may actually be too low: + // + // - If we're looping because of printer settings initialization, the premise + // here is that some poor users have their print server away on a VPN over a + // slow connection. In this situation, the simple fact of opening the printer + // can be dead slow. On the other side, we don't want to die infinitely for a + // real network error. Give the printer 60 seconds to comply. + // + // - If we're looping because of renderer page generation, the renderer could + // be CPU bound, the page overly complex/large or the system just + // memory-bound. + static const int kPrinterSettingsTimeout = 60000; + base::OneShotTimer quit_timer; + base::RunLoop runLoop; + quit_timer.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds(kPrinterSettingsTimeout), + runLoop.QuitWhenIdleClosure()); + + m_isInsideInnerMessageLoop = true; + + // Need to enable recursive task. + { + m_quitClosure = runLoop.QuitClosure(); + base::MessageLoop* loop = base::MessageLoop::current(); + base::MessageLoop::ScopedNestableTaskAllower allowNested(loop); + runLoop.Run(); + } + + bool success = true; + if (m_isInsideInnerMessageLoop) { + // Ok we timed out. That's sad. + m_isInsideInnerMessageLoop = false; + success = false; + } + + return success; +} + +bool PrintViewManagerBaseQt::OpportunisticallyCreatePrintJob(int cookie) +{ + if (m_printJob.get()) + return true; + + if (!cookie) { + // Out of sync. It may happens since we are completely asynchronous. Old + // spurious message can happen if one of the processes is overloaded. + return false; + } + + // The job was initiated by a script. Time to get the corresponding worker + // thread. + scoped_refptr<printing::PrinterQuery> queued_query = m_printerQueriesQueue->PopPrinterQuery(cookie); + if (!queued_query.get()) { + NOTREACHED(); + return false; + } + + if (!CreateNewPrintJob(queued_query.get())) { + // Don't kill anything. + return false; + } + + // Settings are already loaded. Go ahead. This will set + // print_job_->is_job_pending() to true. + m_printJob->StartPrinting(); + return true; +} + +void PrintViewManagerBaseQt::ReleasePrinterQuery() +{ + if (!cookie_) + return; + + int cookie = cookie_; + cookie_ = 0; + + printing::PrintJobManager* printJobManager = WebEngineContext::current()->getPrintJobManager(); + // May be NULL in tests. + if (!printJobManager) + return; + + scoped_refptr<printing::PrinterQuery> printerQuery; + printerQuery = m_printerQueriesQueue->PopPrinterQuery(cookie); + if (!printerQuery.get()) + return; + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::BindOnce(&printing::PrinterQuery::StopWorker, printerQuery.get())); +} + +// Originally from print_preview_message_handler.cc: +void PrintViewManagerBaseQt::StopWorker(int documentCookie) { + if (documentCookie <= 0) + return; + scoped_refptr<printing::PrinterQuery> printer_query = + m_printerQueriesQueue->PopPrinterQuery(documentCookie); + if (printer_query.get()) { + content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, + base::BindOnce(&printing::PrinterQuery::StopWorker, printer_query)); + } +} + +void PrintViewManagerBaseQt::SendPrintingEnabled(bool enabled, content::RenderFrameHost* rfh) +{ + rfh->Send(new PrintMsg_SetPrintingEnabled(rfh->GetRoutingID(), enabled)); +} + +} // namespace QtWebEngineCore diff --git a/src/core/printing/print_view_manager_base_qt.h b/src/core/printing/print_view_manager_base_qt.h new file mode 100644 index 000000000..0d86fcf90 --- /dev/null +++ b/src/core/printing/print_view_manager_base_qt.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2013 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.Chromium file. + +#ifndef PRINT_VIEW_MANAGER_BASE_QT_H +#define PRINT_VIEW_MANAGER_BASE_QT_H + +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "components/prefs/pref_member.h" +#include "components/printing/browser/print_manager.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +struct PrintHostMsg_DidPrintDocument_Params; + +namespace base { +class RefCountedBytes; +} + +namespace content { +class RenderFrameHost; +class RenderViewHost; +} + +namespace printing { +class JobEventDetails; +class MetafilePlayer; +class PrintJob; +class PrintJobWorkerOwner; +class PrintQueriesQueue; +class PrintedDocument; +class PrinterQuery; +} + +namespace QtWebEngineCore { +class PrintViewManagerBaseQt : public content::NotificationObserver + , public printing::PrintManager +{ +public: + ~PrintViewManagerBaseQt() override; + + // Whether printing is enabled or not. + void UpdatePrintingEnabled(); + + virtual base::string16 RenderSourceName(); + +protected: + explicit PrintViewManagerBaseQt(content::WebContents*); + + // content::WebContentsObserver implementation. + // Cancels the print job. + void NavigationStopped() override; + + // content::WebContentsObserver implementation. + void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; + bool OnMessageReceived(const IPC::Message& message, + content::RenderFrameHost* render_frame_host) override; + + // IPC Message handlers. + void OnDidPrintDocument(const PrintHostMsg_DidPrintDocument_Params& params); + void OnShowInvalidPrinterSettingsError(); + + // Processes a NOTIFY_PRINT_JOB_EVENT notification. + void OnNotifyPrintJobEvent(const printing::JobEventDetails& event_details); + + int number_pages_; // Number of pages to print in the print job. + int cookie_; + std::unique_ptr<base::DictionaryValue> m_printSettings; + + // content::NotificationObserver implementation. + void Observe(int, + const content::NotificationSource&, + const content::NotificationDetails&) override; + void StopWorker(int document_cookie); + + // In the case of Scripted Printing, where the renderer is controlling the + // control flow, print_job_ is initialized whenever possible. No-op is + // print_job_ is initialized. + bool OpportunisticallyCreatePrintJob(int cookie); + + // Requests the RenderView to render all the missing pages for the print job. + // No-op if no print job is pending. Returns true if at least one page has + // been requested to the renderer. + bool RenderAllMissingPagesNow(); + + // Checks that synchronization is correct and a print query exists for + // |cookie|. If so, returns the document associated with the cookie. + printing::PrintedDocument* GetDocument(int cookie); + + // Starts printing a document with data given in |print_data|. |print_data| + // must successfully initialize a metafile. |document| is the printed + // document associated with the print job. Returns true if successful. + bool PrintDocument(printing::PrintedDocument *document, + const scoped_refptr<base::RefCountedBytes> &print_data, + const gfx::Size &page_size, + const gfx::Rect &content_area, + const gfx::Point &offsets); + + // Quits the current message loop if these conditions hold true: a document is + // loaded and is complete and waiting_for_pages_to_be_rendered_ is true. This + // function is called in DidPrintDocument() or on ALL_PAGES_REQUESTED + // notification. The inner message loop is created was created by + // RenderAllMissingPagesNow(). + void ShouldQuitFromInnerMessageLoop(); + + bool RunInnerMessageLoop(); + + void TerminatePrintJob(bool cancel); + void DisconnectFromCurrentPrintJob(); + + bool CreateNewPrintJob(printing::PrintJobWorkerOwner* job); + void ReleasePrintJob(); + void ReleasePrinterQuery(); + +private: + // Helper method for UpdatePrintingEnabled(). + void SendPrintingEnabled(bool enabled, content::RenderFrameHost* rfh); + + content::NotificationRegistrar m_registrar; + scoped_refptr<printing::PrintJob> m_printJob; + // Closure for quitting nested message loop. + base::Closure m_quitClosure; + + bool m_isInsideInnerMessageLoop; +#if !defined(OS_MACOSX) + bool m_isExpectingFirstPage; +#endif + bool m_didPrintingSucceed; + scoped_refptr<printing::PrintQueriesQueue> m_printerQueriesQueue; + // content::WebContentsObserver implementation. + void DidStartLoading() override; + + DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBaseQt); +}; + +} // namespace QtWebEngineCore +#endif // PRINT_VIEW_MANAGER_BASE_QT_H + diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp new file mode 100644 index 000000000..627afb01a --- /dev/null +++ b/src/core/printing/print_view_manager_qt.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2013 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.Chromium file. + +#include "print_view_manager_qt.h" + +#include "type_conversion.h" +#include "web_engine_context.h" + +#include <QtGui/qpagelayout.h> +#include <QtGui/qpagesize.h> + +#include "base/values.h" +#include "base/memory/ref_counted_memory.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printer_query.h" +#include "components/printing/common/print_messages.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/common/web_preferences.h" +#include "printing/pdf_metafile_skia.h" +#include "printing/print_job_constants.h" +#include "printing/units.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(QtWebEngineCore::PrintViewManagerQt); + +namespace { +static const qreal kMicronsToMillimeter = 1000.0f; + +static std::vector<char> +GetStdVectorFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +{ + std::unique_ptr<base::SharedMemory> shared_buf( + new base::SharedMemory(handle, true)); + + if (!shared_buf->Map(data_size)) { + return std::vector<char>(); + } + + char* data = static_cast<char*>(shared_buf->memory()); + return std::vector<char>(data, data + data_size); +} + +static scoped_refptr<base::RefCountedBytes> +GetBytesFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +{ + std::unique_ptr<base::SharedMemory> shared_buf( + new base::SharedMemory(handle, true)); + + if (!shared_buf->Map(data_size)) { + return NULL; + } + + unsigned char* data = static_cast<unsigned char*>(shared_buf->memory()); + std::vector<unsigned char> dataVector(data, data + data_size); + return base::RefCountedBytes::TakeVector(&dataVector); +} + +// Write the PDF file to disk. +static void SavePdfFile(scoped_refptr<base::RefCountedBytes> data, + const base::FilePath& path, + const QtWebEngineCore::PrintViewManagerQt::PrintToPDFFileCallback + &saveCallback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); + DCHECK_GT(data->size(), 0U); + + printing::PdfMetafileSkia metafile(printing::SkiaDocumentType::PDF); + metafile.InitFromData(static_cast<const void*>(data->front()), data->size()); + + base::File file(path, + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + bool success = file.IsValid() && metafile.SaveTo(&file); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(saveCallback, success)); +} + +static base::DictionaryValue *createPrintSettings() +{ + base::DictionaryValue *printSettings = new base::DictionaryValue(); + // TO DO: Check if we can use the request ID from Qt here somehow. + static int internalRequestId = 0; + + printSettings->SetBoolean(printing::kIsFirstRequest, internalRequestId++ == 0); + printSettings->SetInteger(printing::kPreviewRequestID, internalRequestId); + + // The following are standard settings that Chromium expects to be set. + printSettings->SetBoolean(printing::kSettingPrintToPDF, true); + printSettings->SetBoolean(printing::kSettingCloudPrintDialog, false); + printSettings->SetBoolean(printing::kSettingPrintWithPrivet, false); + printSettings->SetBoolean(printing::kSettingPrintWithExtension, false); + + printSettings->SetBoolean(printing::kSettingGenerateDraftData, false); + printSettings->SetBoolean(printing::kSettingPreviewModifiable, false); + + printSettings->SetInteger(printing::kSettingDpiHorizontal, printing::kPointsPerInch); + printSettings->SetInteger(printing::kSettingDpiVertical, printing::kPointsPerInch); + + printSettings->SetInteger(printing::kSettingDuplexMode, printing::SIMPLEX); + printSettings->SetInteger(printing::kSettingCopies, 1); + printSettings->SetBoolean(printing::kSettingCollate, false); + printSettings->SetBoolean(printing::kSettingGenerateDraftData, false); + printSettings->SetBoolean(printing::kSettingPreviewModifiable, false); + + printSettings->SetBoolean(printing::kSettingShouldPrintSelectionOnly, false); + printSettings->SetBoolean(printing::kSettingShouldPrintBackgrounds, true); + printSettings->SetBoolean(printing::kSettingHeaderFooterEnabled, false); + printSettings->SetBoolean(printing::kSettingRasterizePdf, false); + printSettings->SetInteger(printing::kSettingScaleFactor, 100); + printSettings->SetString(printing::kSettingDeviceName, ""); + printSettings->SetInteger(printing::kPreviewUIID, 12345678); + + return printSettings; +} + +static base::DictionaryValue *createPrintSettingsFromQPageLayout(const QPageLayout &pageLayout, + bool useCustomMargins) +{ + base::DictionaryValue *printSettings = createPrintSettings(); + + //Set page size attributes, chromium expects these in micrometers + QRectF pageSizeInMillimeter = pageLayout.pageSize().rect(QPageSize::Millimeter); + if (!useCustomMargins) { + // QPrinter will extend this size with its margins + QMarginsF margins = pageLayout.margins(QPageLayout::Millimeter); + pageSizeInMillimeter = pageSizeInMillimeter.marginsRemoved(margins); + } + std::unique_ptr<base::DictionaryValue> sizeDict(new base::DictionaryValue); + sizeDict->SetInteger(printing::kSettingMediaSizeWidthMicrons, pageSizeInMillimeter.width() * kMicronsToMillimeter); + sizeDict->SetInteger(printing::kSettingMediaSizeHeightMicrons, pageSizeInMillimeter.height() * kMicronsToMillimeter); + printSettings->Set(printing::kSettingMediaSize, std::move(sizeDict)); + + if (useCustomMargins) { + // Apply page margins when printing to PDF + QMargins pageMarginsInPoints = pageLayout.marginsPoints(); + std::unique_ptr<base::DictionaryValue> marginsDict(new base::DictionaryValue); + marginsDict->SetInteger(printing::kSettingMarginTop, pageMarginsInPoints.top()); + marginsDict->SetInteger(printing::kSettingMarginBottom, pageMarginsInPoints.bottom()); + marginsDict->SetInteger(printing::kSettingMarginLeft, pageMarginsInPoints.left()); + marginsDict->SetInteger(printing::kSettingMarginRight, pageMarginsInPoints.right()); + + printSettings->Set(printing::kSettingMarginsCustom, std::move(marginsDict)); + printSettings->SetInteger(printing::kSettingMarginsType, printing::CUSTOM_MARGINS); + } else { + // QPrinter will handle margins + printSettings->SetInteger(printing::kSettingMarginsType, printing::NO_MARGINS); + } + + printSettings->SetBoolean(printing::kSettingLandscape, pageLayout.orientation() == QPageLayout::Landscape); + + return printSettings; +} + +} // namespace + +namespace QtWebEngineCore { + +PrintViewManagerQt::~PrintViewManagerQt() +{ +} + +#if BUILDFLAG(ENABLE_BASIC_PRINTING) +void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayout, + bool printInColor, + const QString &filePath, + const PrintToPDFFileCallback& callback) +{ + if (callback.is_null()) + return; + + if (m_printSettings || !filePath.length()) { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(callback, false)); + return; + } + + m_pdfOutputPath = toFilePath(filePath); + m_pdfSaveCallback = callback; + if (!PrintToPDFInternal(pageLayout, printInColor)) { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(callback, false)); + resetPdfState(); + } +} + +void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, + bool printInColor, + bool useCustomMargins, + const PrintToPDFCallback& callback) +{ + if (callback.is_null()) + return; + + // If there already is a pending print in progress, don't try starting another one. + if (m_printSettings) { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(callback, std::vector<char>())); + return; + } + + m_pdfPrintCallback = callback; + if (!PrintToPDFInternal(pageLayout, printInColor, useCustomMargins)) { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(callback, std::vector<char>())); + + resetPdfState(); + } +} + +bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout, + const bool printInColor, + const bool useCustomMargins) +{ + if (!pageLayout.isValid()) + return false; + + m_printSettings.reset(createPrintSettingsFromQPageLayout(pageLayout, useCustomMargins)); + m_printSettings->SetBoolean(printing::kSettingShouldPrintBackgrounds + , web_contents()->GetRenderViewHost()->GetWebkitPreferences().should_print_backgrounds); + m_printSettings->SetInteger(printing::kSettingColor, + printInColor ? printing::COLOR : printing::GRAYSCALE); + return web_contents()->GetMainFrame()->Send( + new PrintMsg_InitiatePrintPreview(web_contents()->GetMainFrame()->GetRoutingID(), false)); +} + +#endif // BUILDFLAG(ENABLE_BASIC_PRINTING) + +// PrintedPagesSource implementation. +base::string16 PrintViewManagerQt::RenderSourceName() +{ + return toString16(QLatin1String("")); +} + +PrintViewManagerQt::PrintViewManagerQt(content::WebContents *contents) + : PrintViewManagerBaseQt(contents) +{ + +} + +// content::WebContentsObserver implementation. +bool PrintViewManagerQt::OnMessageReceived(const IPC::Message& message, content::RenderFrameHost* render_frame_host) +{ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintViewManagerQt, message) + IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog, OnDidShowPrintDialog) + IPC_MESSAGE_HANDLER(PrintHostMsg_RequestPrintPreview, OnRequestPrintPreview) + IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting, OnMetafileReadyForPrinting); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled || PrintManager::OnMessageReceived(message, render_frame_host); +} + +void PrintViewManagerQt::resetPdfState() +{ + m_pdfOutputPath.clear(); + m_pdfPrintCallback.Reset(); + m_pdfSaveCallback.Reset(); + m_printSettings.reset(); +} + +// IPC handlers + +void PrintViewManagerQt::OnRequestPrintPreview( + const PrintHostMsg_RequestPrintPreview_Params &/*params*/) +{ + auto *rfh = web_contents()->GetMainFrame(); + rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), *m_printSettings)); + rfh->Send(new PrintMsg_ClosePrintPreviewDialog(rfh->GetRoutingID())); +} + +void PrintViewManagerQt::OnMetafileReadyForPrinting( + const PrintHostMsg_DidPreviewDocument_Params& params) +{ + StopWorker(params.document_cookie); + + // Create local copies so we can reset the state and take a new pdf print job. + base::Callback<void(const std::vector<char>&)> pdf_print_callback = m_pdfPrintCallback; + base::Callback<void(bool)> pdf_save_callback = m_pdfSaveCallback; + base::FilePath pdfOutputPath = m_pdfOutputPath; + + resetPdfState(); + + if (!pdf_print_callback.is_null()) { + std::vector<char> data_vector = GetStdVectorFromHandle(params.metafile_data_handle, + params.data_size); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(pdf_print_callback, data_vector)); + } else { + scoped_refptr<base::RefCountedBytes> data_bytes + = GetBytesFromHandle(params.metafile_data_handle, params.data_size); + content::BrowserThread::PostTask(content::BrowserThread::FILE, + FROM_HERE, + base::Bind(&SavePdfFile, data_bytes, pdfOutputPath, pdf_save_callback)); + } +} + +void PrintViewManagerQt::OnDidShowPrintDialog() +{ +} + +// content::WebContentsObserver implementation. +void PrintViewManagerQt::DidStartLoading() +{ +} + +// content::WebContentsObserver implementation. +// Cancels the print job. +void PrintViewManagerQt::NavigationStopped() +{ + if (!m_pdfPrintCallback.is_null()) { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(m_pdfPrintCallback, std::vector<char>())); + } + resetPdfState(); +} + +void PrintViewManagerQt::RenderProcessGone(base::TerminationStatus status) +{ + PrintViewManagerBaseQt::RenderProcessGone(status); + if (!m_pdfPrintCallback.is_null()) { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(m_pdfPrintCallback, std::vector<char>())); + } + resetPdfState(); +} + + +} // namespace QtWebEngineCore diff --git a/src/core/printing/print_view_manager_qt.h b/src/core/printing/print_view_manager_qt.h new file mode 100644 index 000000000..654608ddd --- /dev/null +++ b/src/core/printing/print_view_manager_qt.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2013 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.Chromium file. + +#ifndef PRINT_VIEW_MANAGER_QT_H +#define PRINT_VIEW_MANAGER_QT_H + +#include "print_view_manager_base_qt.h" + +#include <QtWebEngineCore/qtwebenginecoreglobal.h> +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "components/prefs/pref_member.h" +#include "components/printing/browser/print_manager.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/web_contents_user_data.h" +#include "printing/features/features.h" + +struct PrintHostMsg_RequestPrintPreview_Params; +struct PrintHostMsg_DidPreviewDocument_Params; + +namespace content { +class RenderViewHost; +} + +namespace printing { +class JobEventDetails; +class MetafilePlayer; +class PrintJob; +class PrintJobWorkerOwner; +class PrintQueriesQueue; +} + +QT_BEGIN_NAMESPACE +class QPageLayout; +class QString; +QT_END_NAMESPACE + +namespace QtWebEngineCore { +class PrintViewManagerQt + : PrintViewManagerBaseQt + , public content::WebContentsUserData<PrintViewManagerQt> +{ +public: + ~PrintViewManagerQt() override; + typedef base::Callback<void(const std::vector<char> &result)> PrintToPDFCallback; + typedef base::Callback<void(bool success)> PrintToPDFFileCallback; +#if BUILDFLAG(ENABLE_BASIC_PRINTING) + // Method to print a page to a Pdf document with page size \a pageSize in location \a filePath. + void PrintToPDFFileWithCallback(const QPageLayout &pageLayout, + bool printInColor, + const QString &filePath, + const PrintToPDFFileCallback& callback); + void PrintToPDFWithCallback(const QPageLayout &pageLayout, + bool printInColor, + bool useCustomMargins, + const PrintToPDFCallback &callback); +#endif // ENABLE_BASIC_PRINTING + + base::string16 RenderSourceName() override; + +protected: + explicit PrintViewManagerQt(content::WebContents*); + + // content::WebContentsObserver implementation. + // Cancels the print job. + void NavigationStopped() override; + + // Terminates or cancels the print job if one was pending. + void RenderProcessGone(base::TerminationStatus status) override; + + // content::WebContentsObserver implementation. + bool OnMessageReceived(const IPC::Message& message, + content::RenderFrameHost* render_frame_host) override; + + // IPC handlers + void OnDidShowPrintDialog(); + void OnRequestPrintPreview(const PrintHostMsg_RequestPrintPreview_Params&); + void OnMetafileReadyForPrinting(const PrintHostMsg_DidPreviewDocument_Params& params); + +#if BUILDFLAG(ENABLE_BASIC_PRINTING) + bool PrintToPDFInternal(const QPageLayout &, bool printInColor, bool useCustomMargins = true); +#endif // BUILDFLAG(ENABLE_BASIC_PRINTING) + + base::FilePath m_pdfOutputPath; + PrintToPDFCallback m_pdfPrintCallback; + PrintToPDFFileCallback m_pdfSaveCallback; + +private: + friend class content::WebContentsUserData<PrintViewManagerQt>; + + void resetPdfState(); + + // content::WebContentsObserver implementation. + void DidStartLoading() override; + DISALLOW_COPY_AND_ASSIGN(PrintViewManagerQt); +}; + +} // namespace QtWebEngineCore +#endif // PRINT_VIEW_MANAGER_QT_H + diff --git a/src/core/printing/printing_message_filter_qt.cpp b/src/core/printing/printing_message_filter_qt.cpp new file mode 100644 index 000000000..c0e3498ee --- /dev/null +++ b/src/core/printing/printing_message_filter_qt.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Based on chrome/browser/printing/printing_message_filter.cc: +// Copyright (c) 2012 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.Chromium file. + +#include "printing_message_filter_qt.h" + +#include "web_engine_context.h" + +#include <string> + +#include "base/bind.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printer_query.h" +#include "components/printing/browser/print_manager_utils.h" +#include "components/printing/common/print_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/child_process_host.h" + +using content::BrowserThread; + +namespace QtWebEngineCore { + +PrintingMessageFilterQt::PrintingMessageFilterQt(int render_process_id) + : BrowserMessageFilter(PrintMsgStart), + render_process_id_(render_process_id), + queue_(WebEngineContext::current()->getPrintJobManager()->queue()) { + DCHECK(queue_.get()); +} + +PrintingMessageFilterQt::~PrintingMessageFilterQt() { +} + +void PrintingMessageFilterQt::OverrideThreadForMessage( + const IPC::Message& message, BrowserThread::ID* thread) { +} + +bool PrintingMessageFilterQt::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintingMessageFilterQt, message) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings, + OnGetDefaultPrintSettings) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings, + OnUpdatePrintSettings) + IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void PrintingMessageFilterQt::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + scoped_refptr<printing::PrinterQuery> printer_query; + + printer_query = queue_->PopPrinterQuery(0); + if (!printer_query.get()) { + printer_query = + queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id()); + } + + // Loads default settings. This is asynchronous, only the IPC message sender + // will hang until the settings are retrieved. + printer_query->GetSettings( + printing::PrinterQuery::GetSettingsAskParam::DEFAULTS, + 0, + false, + printing::DEFAULT_MARGINS, + false, + false, + base::Bind(&PrintingMessageFilterQt::OnGetDefaultPrintSettingsReply, + this, + printer_query, + reply_msg)); +} + +void PrintingMessageFilterQt::OnGetDefaultPrintSettingsReply( + scoped_refptr<printing::PrinterQuery> printer_query, + IPC::Message* reply_msg) { + PrintMsg_Print_Params params; + if (!printer_query.get() || + printer_query->last_status() != printing::PrintingContext::OK) { + params.Reset(); + } else { + RenderParamsFromPrintSettings(printer_query->settings(), ¶ms); + params.document_cookie = printer_query->cookie(); + } + PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params); + Send(reply_msg); + // If printing was enabled. + if (printer_query.get()) { + // If user hasn't cancelled. + if (printer_query->cookie() && printer_query->settings().dpi()) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } + } +} + +void PrintingMessageFilterQt::OnScriptedPrint( + const PrintHostMsg_ScriptedPrint_Params& params, + IPC::Message* reply_msg) { + scoped_refptr<printing::PrinterQuery> printer_query = + queue_->PopPrinterQuery(params.cookie); + if (!printer_query.get()) { + printer_query = + queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id()); + } + printer_query->GetSettings( + printing::PrinterQuery::GetSettingsAskParam::ASK_USER, + params.expected_pages_count, + params.has_selection, + params.margin_type, + params.is_scripted, + params.is_modifiable, + base::Bind(&PrintingMessageFilterQt::OnScriptedPrintReply, + this, + printer_query, + reply_msg)); +} + +void PrintingMessageFilterQt::OnScriptedPrintReply( + scoped_refptr<printing::PrinterQuery> printer_query, + IPC::Message* reply_msg) { + PrintMsg_PrintPages_Params params; + + if (printer_query->last_status() != printing::PrintingContext::OK || + !printer_query->settings().dpi()) { + params.Reset(); + } else { + RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); + params.params.document_cookie = printer_query->cookie(); + params.pages = printing::PageRange::GetPages(printer_query->settings().ranges()); + } + PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params); + Send(reply_msg); + if (params.params.dpi && params.params.document_cookie) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } +} + +void PrintingMessageFilterQt::OnUpdatePrintSettings( + int document_cookie, const base::DictionaryValue& job_settings, + IPC::Message* reply_msg) { + std::unique_ptr<base::DictionaryValue> new_settings(job_settings.DeepCopy()); + + scoped_refptr<printing::PrinterQuery> printer_query; + printer_query = queue_->PopPrinterQuery(document_cookie); + if (!printer_query.get()) { + printer_query = queue_->CreatePrinterQuery( + content::ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE); + } + printer_query->SetSettings( + std::move(new_settings), + base::Bind(&PrintingMessageFilterQt::OnUpdatePrintSettingsReply, this, + printer_query, reply_msg)); +} + +void PrintingMessageFilterQt::OnUpdatePrintSettingsReply( + scoped_refptr<printing::PrinterQuery> printer_query, + IPC::Message* reply_msg) { + PrintMsg_PrintPages_Params params; + if (!printer_query.get() || + printer_query->last_status() != printing::PrintingContext::OK) { + params.Reset(); + } else { + RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); + params.params.document_cookie = printer_query->cookie(); + params.pages = printing::PageRange::GetPages(printer_query->settings().ranges()); + } + + PrintHostMsg_UpdatePrintSettings::WriteReplyParams( + reply_msg, + params, + printer_query.get() && + (printer_query->last_status() == printing::PrintingContext::CANCEL)); + Send(reply_msg); + // If user hasn't cancelled. + if (printer_query.get()) { + if (printer_query->cookie() && printer_query->settings().dpi()) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } + } +} + +void PrintingMessageFilterQt::OnCheckForCancel(int32_t preview_ui_id, + int preview_request_id, + bool* cancel) { + *cancel = false; +} + +} // namespace printing diff --git a/src/core/printing/printing_message_filter_qt.h b/src/core/printing/printing_message_filter_qt.h new file mode 100644 index 000000000..ca5e29f08 --- /dev/null +++ b/src/core/printing/printing_message_filter_qt.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2012 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.Chromium file. + +#ifndef PRINTING_PRINTING_MESSAGE_FILTER_QT_H_ +#define PRINTING_PRINTING_MESSAGE_FILTER_QT_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "components/prefs/pref_member.h" +#include "content/public/browser/browser_message_filter.h" + +#if defined(OS_WIN) +#include "base/memory/shared_memory.h" +#endif + +struct PrintHostMsg_ScriptedPrint_Params; + +namespace base { +class DictionaryValue; +class FilePath; +} + +namespace content { +class WebContents; +} + +namespace printing { + +class PrintJobManager; +class PrintQueriesQueue; +class PrinterQuery; +} + +namespace QtWebEngineCore { +// This class filters out incoming printing related IPC messages for the +// renderer process on the IPC thread. +class PrintingMessageFilterQt : public content::BrowserMessageFilter { + public: + PrintingMessageFilterQt(int render_process_id); + + // content::BrowserMessageFilter methods. + void OverrideThreadForMessage(const IPC::Message& message, + content::BrowserThread::ID* thread) override; + bool OnMessageReceived(const IPC::Message& message) override; + + private: + ~PrintingMessageFilterQt() override; + + // GetPrintSettingsForRenderView must be called via PostTask and + // base::Bind. Collapse the settings-specific params into a + // struct to avoid running into issues with too many params + // to base::Bind. + struct GetPrintSettingsForRenderViewParams; + + // Get the default print setting. + void OnGetDefaultPrintSettings(IPC::Message* reply_msg); + void OnGetDefaultPrintSettingsReply(scoped_refptr<printing::PrinterQuery> printer_query, + IPC::Message* reply_msg); + + // The renderer host have to show to the user the print dialog and returns + // the selected print settings. The task is handled by the print worker + // thread and the UI thread. The reply occurs on the IO thread. + void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params, + IPC::Message* reply_msg); + void OnScriptedPrintReply(scoped_refptr<printing::PrinterQuery> printer_query, + IPC::Message* reply_msg); + + // Modify the current print settings based on |job_settings|. The task is + // handled by the print worker thread and the UI thread. The reply occurs on + // the IO thread. + void OnUpdatePrintSettings(int document_cookie, + const base::DictionaryValue& job_settings, + IPC::Message* reply_msg); + void OnUpdatePrintSettingsReply(scoped_refptr<printing::PrinterQuery> printer_query, + IPC::Message* reply_msg); + + // Check to see if print preview has been cancelled. + void OnCheckForCancel(int32_t preview_ui_id, + int preview_request_id, + bool* cancel); + + const int render_process_id_; + + scoped_refptr<printing::PrintQueriesQueue> queue_; + + DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilterQt); +}; + +} // namespace printing + +#endif // PRINTING_PRINTING_MESSAGE_FILTER_QT_H_ |