diff options
25 files changed, 627 insertions, 175 deletions
diff --git a/chrome/browser/printing/print_preview_data_service.cc b/chrome/browser/printing/print_preview_data_service.cc index 2199e73..53ba36f 100644 --- a/chrome/browser/printing/print_preview_data_service.cc +++ b/chrome/browser/printing/print_preview_data_service.cc @@ -4,7 +4,67 @@ #include "chrome/browser/printing/print_preview_data_service.h" +#include "base/memory/ref_counted_memory.h" #include "base/memory/singleton.h" +#include "printing/print_job_constants.h" + +// PrintPreviewDataStore stores data for preview workflow and preview printing +// workflow. +// +// NOTE: +// This class stores a list of PDFs. The list |index| is zero-based and can +// be |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to represent complete preview +// document. The PDF stored at |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| is +// optimized with font subsetting, compression, etc. PDF's stored at all other +// indices are unoptimized. +// +// PrintPreviewDataStore owns the data and is responsible for freeing it when +// either: +// a) There is a new data. +// b) When PrintPreviewDataStore is destroyed. +// +class PrintPreviewDataStore : public base::RefCounted<PrintPreviewDataStore> { + public: + PrintPreviewDataStore() {} + + // Get the preview page for the specified |index|. + void GetPreviewDataForIndex(int index, scoped_refptr<RefCountedBytes>* data) { + if (index != printing::COMPLETE_PREVIEW_DOCUMENT_INDEX && + index < printing::FIRST_PAGE_INDEX) { + return; + } + + PreviewPageDataMap::iterator it = page_data_map_.find(index); + if (it != page_data_map_.end()) + *data = it->second.get(); + } + + // Set/Update the preview data entry for the specified |index|. + void SetPreviewDataForIndex(int index, const RefCountedBytes* data) { + if (index != printing::COMPLETE_PREVIEW_DOCUMENT_INDEX && + index < printing::FIRST_PAGE_INDEX) { + return; + } + + page_data_map_[index] = const_cast<RefCountedBytes*>(data); + } + + private: + friend class base::RefCounted<PrintPreviewDataStore>; + + // 1:1 relationship between page index and its associated preview data. + // Key: Page index is zero-based and can be + // |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to represent complete preview + // document. + // Value: Preview data. + typedef std::map<int, scoped_refptr<RefCountedBytes> > PreviewPageDataMap; + + ~PrintPreviewDataStore() {} + + PreviewPageDataMap page_data_map_; + + DISALLOW_COPY_AND_ASSIGN(PrintPreviewDataStore); +}; // static PrintPreviewDataService* PrintPreviewDataService::GetInstance() { @@ -19,21 +79,29 @@ PrintPreviewDataService::~PrintPreviewDataService() { void PrintPreviewDataService::GetDataEntry( const std::string& preview_ui_addr_str, + int index, scoped_refptr<RefCountedBytes>* data_bytes) { - PreviewDataSourceMap::iterator it = data_src_map_.find(preview_ui_addr_str); - if (it != data_src_map_.end()) - *data_bytes = it->second.get(); + *data_bytes = NULL; + PreviewDataStoreMap::iterator it = data_store_map_.find(preview_ui_addr_str); + if (it != data_store_map_.end()) + it->second->GetPreviewDataForIndex(index, data_bytes); } void PrintPreviewDataService::SetDataEntry( - const std::string& preview_ui_addr_str, const RefCountedBytes* data_bytes) { - RemoveEntry(preview_ui_addr_str); - data_src_map_[preview_ui_addr_str] = const_cast<RefCountedBytes*>(data_bytes); + const std::string& preview_ui_addr_str, + int index, + const RefCountedBytes* data_bytes) { + PreviewDataStoreMap::iterator it = data_store_map_.find(preview_ui_addr_str); + if (it == data_store_map_.end()) + data_store_map_[preview_ui_addr_str] = new PrintPreviewDataStore(); + + data_store_map_[preview_ui_addr_str]->SetPreviewDataForIndex(index, + data_bytes); } void PrintPreviewDataService::RemoveEntry( const std::string& preview_ui_addr_str) { - PreviewDataSourceMap::iterator it = data_src_map_.find(preview_ui_addr_str); - if (it != data_src_map_.end()) - data_src_map_.erase(it); + PreviewDataStoreMap::iterator it = data_store_map_.find(preview_ui_addr_str); + if (it != data_store_map_.end()) + data_store_map_.erase(it); } diff --git a/chrome/browser/printing/print_preview_data_service.h b/chrome/browser/printing/print_preview_data_service.h index 63618ac..2e94393 100644 --- a/chrome/browser/printing/print_preview_data_service.h +++ b/chrome/browser/printing/print_preview_data_service.h @@ -10,27 +10,31 @@ #include <string> #include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_memory.h" template<typename T> struct DefaultSingletonTraits; -// PrintPreviewDataService manages data for chrome://print requests. -// -// PrintPreviewDataService owns the data and is responsible for freeing it when -// either: -// a) There is a new data. -// b) When PrintPreviewDataService is destroyed. -// +class PrintPreviewDataStore; +class RefCountedBytes; + +// PrintPreviewDataService manages data stores for chrome://print requests. +// It owns the data store object and is responsible for freeing it. class PrintPreviewDataService { public: static PrintPreviewDataService* GetInstance(); - // Get the data entry from PreviewDataSourceMap. - void GetDataEntry(const std::string& preview_ui_addr_str, + // Get the data entry from PrintPreviewDataStore. |index| is zero-based or + // |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to represent complete preview + // data. Use |index| to retrieve a specific preview page data. |data| is set + // to NULL if the requested page is not yet available. + void GetDataEntry(const std::string& preview_ui_addr_str, int index, scoped_refptr<RefCountedBytes>* data); - // Set/Update the data entry in PreviewDataSourceMap. - void SetDataEntry(const std::string& preview_ui_addr_str, + // Set/Update the data entry in PrintPreviewDataStore. |index| is zero-based + // or |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to represent complete + // preview data. Use |index| to set/update a specific preview page data. + // NOTE: PrintPreviewDataStore owns the data. Do not refcount |data| before + // calling this function. It will be refcounted in PrintPreviewDataStore. + void SetDataEntry(const std::string& preview_ui_addr_str, int index, const RefCountedBytes* data); // Remove the corresponding PrintPreviewUI entry from the map. @@ -39,16 +43,16 @@ class PrintPreviewDataService { private: friend struct DefaultSingletonTraits<PrintPreviewDataService>; - // 1:1 relationship between PrintPreviewUI and preview data. + // 1:1 relationship between PrintPreviewUI and data store object. // Key: Print preview UI address string. - // Value: Preview data. - typedef std::map<std::string, scoped_refptr<RefCountedBytes> > - PreviewDataSourceMap; + // Value: Print preview data store object. + typedef std::map<std::string, scoped_refptr<PrintPreviewDataStore> > + PreviewDataStoreMap; PrintPreviewDataService(); virtual ~PrintPreviewDataService(); - PreviewDataSourceMap data_src_map_; + PreviewDataStoreMap data_store_map_; DISALLOW_COPY_AND_ASSIGN(PrintPreviewDataService); }; diff --git a/chrome/browser/printing/print_preview_message_handler.cc b/chrome/browser/printing/print_preview_message_handler.cc index 75aa54c..d2b1181 100644 --- a/chrome/browser/printing/print_preview_message_handler.cc +++ b/chrome/browser/printing/print_preview_message_handler.cc @@ -7,6 +7,8 @@ #include <vector> #include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "base/shared_memory.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/printing/background_printing_manager.h" #include "chrome/browser/printing/print_job_manager.h" @@ -21,6 +23,7 @@ #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/common/content_restriction.h" +#include "printing/print_job_constants.h" namespace { @@ -37,6 +40,20 @@ void StopWorker(int document_cookie) { } } +RefCountedBytes* GetDataFromHandle(base::SharedMemoryHandle handle, + uint32 data_size) { + scoped_ptr<base::SharedMemory> shared_buf( + new base::SharedMemory(handle, true)); + if (!shared_buf->Map(data_size)) { + NOTREACHED(); + return NULL; + } + char* preview_data = static_cast<char*>(shared_buf->memory()); + std::vector<unsigned char> data(data_size); + memcpy(&data[0], preview_data, data_size); + return RefCountedBytes::TakeVector(&data); +} + } // namespace namespace printing { @@ -64,7 +81,8 @@ void PrintPreviewMessageHandler::OnRequestPrintPreview() { } void PrintPreviewMessageHandler::OnDidGetPreviewPageCount(int document_cookie, - int page_count) { + int page_count, + bool is_modifiable) { if (page_count <= 0) return; TabContents* print_preview_tab = GetPrintPreviewTab(); @@ -73,10 +91,12 @@ void PrintPreviewMessageHandler::OnDidGetPreviewPageCount(int document_cookie, PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(print_preview_tab->web_ui()); - print_preview_ui->OnDidGetPreviewPageCount(document_cookie, page_count); + print_preview_ui->OnDidGetPreviewPageCount( + document_cookie, page_count, is_modifiable); } -void PrintPreviewMessageHandler::OnDidPreviewPage(int page_number) { +void PrintPreviewMessageHandler::OnDidPreviewPage( + const PrintHostMsg_DidPreviewPage_Params& params) { RenderViewHost* rvh = tab_contents()->render_view_host(); TabContents* print_preview_tab = GetPrintPreviewTab(); if (!(print_preview_tab && print_preview_tab->web_ui())) { @@ -96,10 +116,25 @@ void PrintPreviewMessageHandler::OnDidPreviewPage(int page_number) { return; } - // Continue - if (page_number >= 0) + int requested_preview_page_index = INVALID_PAGE_INDEX; + int page_number = params.page_number; + + if (page_number == FIRST_PAGE_INDEX) + print_preview_ui->ClearAllPreviewData(); + + if (page_number >= FIRST_PAGE_INDEX && params.data_size) { + RefCountedBytes* data_bytes = + GetDataFromHandle(params.metafile_data_handle, params.data_size); + DCHECK(data_bytes); + + print_preview_ui->SetPrintPreviewDataForIndex(page_number, data_bytes); print_preview_ui->OnDidPreviewPage(page_number); - rvh->Send(new PrintMsg_ContinuePreview(rvh->routing_id())); + // TODO(kmadhusu): Query |PrintPreviewUI| and update + // |requested_preview_page_index| accordingly. + } + + rvh->Send(new PrintMsg_ContinuePreview(rvh->routing_id(), + requested_preview_page_index)); } void PrintPreviewMessageHandler::OnPagesReadyForPreview( @@ -121,41 +156,28 @@ void PrintPreviewMessageHandler::OnPagesReadyForPreview( if (params.reuse_existing_data) { // Need to match normal rendering where we are expected to send this. print_preview_ui->OnDidGetPreviewPageCount(params.document_cookie, - params.expected_pages_count); - - print_preview_ui->OnPreviewDataIsAvailable( - params.expected_pages_count, - wrapper->print_view_manager()->RenderSourceName(), - params.modifiable, - params.preview_request_id); - return; - } + params.expected_pages_count, + params.modifiable); - base::SharedMemory* shared_buf = - new base::SharedMemory(params.metafile_data_handle, true); - if (!shared_buf->Map(params.data_size)) { - NOTREACHED(); - delete shared_buf; + print_preview_ui->OnReusePreviewData(params.preview_request_id); return; } wrapper->print_view_manager()->OverrideTitle(tab_contents()); - const unsigned char* preview_data = - static_cast<unsigned char*>(shared_buf->memory()); - uint32 preview_data_size = params.data_size; - // TODO(joth): This seems like a good match for using RefCountedStaticMemory // to avoid the memory copy, but the SetPrintPreviewData call chain below // needs updating to accept the RefCountedMemory* base class. - scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); - html_bytes->data().assign(preview_data, preview_data + preview_data_size); + RefCountedBytes* data_bytes = + GetDataFromHandle(params.metafile_data_handle, params.data_size); + if (!data_bytes) + return; - print_preview_ui->SetPrintPreviewData(html_bytes.get()); + print_preview_ui->SetPrintPreviewDataForIndex(COMPLETE_PREVIEW_DOCUMENT_INDEX, + data_bytes); print_preview_ui->OnPreviewDataIsAvailable( params.expected_pages_count, wrapper->print_view_manager()->RenderSourceName(), - params.modifiable, params.preview_request_id); } diff --git a/chrome/browser/printing/print_preview_message_handler.h b/chrome/browser/printing/print_preview_message_handler.h index 9d049d0..ff56b01 100644 --- a/chrome/browser/printing/print_preview_message_handler.h +++ b/chrome/browser/printing/print_preview_message_handler.h @@ -9,6 +9,7 @@ #include "content/browser/tab_contents/tab_contents_observer.h" struct PrintHostMsg_DidPreviewDocument_Params; +struct PrintHostMsg_DidPreviewPage_Params; namespace printing { @@ -30,9 +31,11 @@ class PrintPreviewMessageHandler : public TabContentsObserver { // Message handlers. void OnRequestPrintPreview(); - void OnDidGetPreviewPageCount(int document_cookie, int page_count); + void OnDidGetPreviewPageCount(int document_cookie, + int page_count, + bool is_modifiable); // |page_number| is 0-based. - void OnDidPreviewPage(int page_number); + void OnDidPreviewPage(const PrintHostMsg_DidPreviewPage_Params& params); void OnPagesReadyForPreview( const PrintHostMsg_DidPreviewDocument_Params& params); void OnPrintPreviewFailed(int document_cookie); diff --git a/chrome/browser/resources/print_preview/layout_settings.js b/chrome/browser/resources/print_preview/layout_settings.js index 2f74381..0932e21 100644 --- a/chrome/browser/resources/print_preview/layout_settings.js +++ b/chrome/browser/resources/print_preview/layout_settings.js @@ -66,7 +66,8 @@ cr.define('print_preview', function() { * @private */ onPrinterCapabilitiesUpdated_: function(e) { - this.fadeInOut(e.printerCapabilities.disableLandscapeOption); + if (e.printerCapabilities.disableLandscapeOption) + this.fadeInOut_(e.printerCapabilities.disableLandscapeOption); }, /** @@ -93,8 +94,9 @@ cr.define('print_preview', function() { /** * @param {boolean} fadeOut True if |this.layoutOption_| should be faded * out, false if it should be faded in. + * @private */ - fadeInOut: function(fadeOut) { + fadeInOut_: function(fadeOut) { fadeOut ? fadeOutElement(this.layoutOption_) : fadeInElement(this.layoutOption_); } diff --git a/chrome/browser/resources/print_preview/page_settings.js b/chrome/browser/resources/print_preview/page_settings.js index 5d8656b..48210c6 100644 --- a/chrome/browser/resources/print_preview/page_settings.js +++ b/chrome/browser/resources/print_preview/page_settings.js @@ -20,7 +20,8 @@ cr.define('print_preview', function() { // whenever needed. this.timerId_; - // Contains the previously selected pages. It is used in + // Contains the previously selected pages (pages requested by last + // preview request). It is used in // |this.onSelectedPagesMayHaveChanged_()| to make sure that a new preview // is not requested more often than necessary. this.previouslySelectedPages_ = []; @@ -108,6 +109,16 @@ cr.define('print_preview', function() { }, /** + * Returns the previously selected pages in ascending order without any + * duplicates. + * + * @return {Array} + */ + get previouslySelectedPages() { + return this.previouslySelectedPages_; + }, + + /** * Returns an array of objects describing the selected page ranges. See * documentation of pageSetToPageRanges() for more details. * @return {Array} diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js index 17bdbcf..4f2aa94 100644 --- a/chrome/browser/resources/print_preview/print_preview.js +++ b/chrome/browser/resources/print_preview/print_preview.js @@ -71,6 +71,7 @@ var addedCloudPrinters = {}; // The maximum number of cloud printers to allow in the dropdown. const maxCloudPrinters = 10; +var isPipeliningSupported = false; /** * Window onload handler, sets up the page and starts print preview by getting @@ -79,6 +80,8 @@ const maxCloudPrinters = 10; function onLoad() { cr.enablePlatformSpecificCSSRules(); + isPipeliningSupported = !cr.isWindows; + if (!checkCompatiblePluginExists()) { disableInputElementsInSidebar(); displayErrorMessageWithButton(localStrings.getString('noPlugin'), @@ -365,6 +368,16 @@ function hasRequestedPreview() { } /** + * Checks if |previewResponseId| matches |lastPreviewRequestId|. Used to ignore + * obsolete preview data responses. + * @param {number} previewResponseId The id to check. + * @return {boolean} True if previewResponseId reffers to the expected response. + */ +function isExpectedPreviewResponse(previewResponseId) { + return lastPreviewRequestID == previewResponseId; +} + +/** * Returns the name of the selected printer or the empty string if no * printer is selected. * @return {string} The name of the currently selected printer. @@ -790,6 +803,10 @@ function printPreviewFailed() { * Called when the PDF plugin loads its document. */ function onPDFLoad() { + if (previewModifiable && isPipeliningSupported) { + setPluginPreviewPageCount(); + cr.dispatchSimpleEvent(document, 'updateSummary'); + } $('pdf-viewer').fitToHeight(); setColor($('color').checked); hideLoadingAnimation(); @@ -797,13 +814,41 @@ function onPDFLoad() { cr.dispatchSimpleEvent(document, 'PDFLoaded'); } +function setPluginPreviewPageCount() { + $('pdf-viewer').printPreviewPageCount( + pageSettings.previouslySelectedPages.length); +} + /** * Update the page count and check the page range. * Called from PrintPreviewUI::OnDidGetPreviewPageCount(). * @param {number} pageCount The number of pages. + * @param {boolean} isModifiable Indicates whether the previewed document can be + * modified. */ -function onDidGetPreviewPageCount(pageCount) { +function onDidGetPreviewPageCount(pageCount, isModifiable) { pageSettings.updateState(pageCount); + previewModifiable = isModifiable; +} + +/** + * Called when no pipelining previewed pages. + */ +function reloadPreviewPages(previewUid, previewResponseId) { + if (!isExpectedPreviewResponse(previewResponseId)) + return; + hasPendingPreviewRequest = false; + + if (checkIfSettingsChangedAndRegeneratePreview()) + return; + cr.dispatchSimpleEvent(document, 'updateSummary'); + cr.dispatchSimpleEvent(document, 'updatePrintButton'); + addEventListeners(); + hideLoadingAnimation(); + var pageSet = pageSettings.previouslySelectedPages; + for (var i = 0; i < pageSet.length; i++) + $('pdf-viewer').loadPreviewPage(getPageSrcURL(previewUid, pageSet[i]-1), i); + // TODO(dpapad): handle pending print file requests. } /** @@ -812,10 +857,20 @@ function onDidGetPreviewPageCount(pageCount) { * Called from PrintPreviewUI::OnDidPreviewPage(). * @param {number} pageNumber The page number, 0-based. */ -function onDidPreviewPage(pageNumber) { +function onDidPreviewPage(pageNumber, previewUid) { + // Refactor + if (!previewModifiable || !isPipeliningSupported) + return; + + var pageIndex = pageSettings.previouslySelectedPages.indexOf(pageNumber + 1); + if (checkIfSettingsChangedAndRegeneratePreview()) return; - // TODO(thestig) Make use of |pageNumber| for pipelined preview generation. + if (pageIndex == 0) + createPDFPlugin(previewUid); + + $('pdf-viewer').loadPreviewPage( + getPageSrcURL(previewUid, pageNumber), pageIndex); } /** @@ -825,24 +880,26 @@ function onDidPreviewPage(pageNumber) { * @param {string} jobTitle The print job title. * @param {boolean} modifiable If the preview is modifiable. * @param {string} previewUid Preview unique identifier. - * @param {number} previewRequestId The preview request id that resulted in this - * response. + * @param {number} previewResponseId The preview request id that resulted in + * this response. */ function updatePrintPreview(jobTitle, - modifiable, previewUid, - previewRequestId) { - if (lastPreviewRequestID != previewRequestId) + previewResponseId) { + if (!isExpectedPreviewResponse(previewResponseId)) return; hasPendingPreviewRequest = false; if (checkIfSettingsChangedAndRegeneratePreview()) return; - previewModifiable = modifiable; document.title = localStrings.getStringF('printPreviewTitleFormat', jobTitle); - createPDFPlugin(previewUid); + if (!previewModifiable || !isPipeliningSupported) { + // If the preview is not modifiable the plugin has not been created yet. + createPDFPlugin(previewUid); + } + cr.dispatchSimpleEvent(document, 'updateSummary'); cr.dispatchSimpleEvent(document, 'updatePrintButton'); addEventListeners(); @@ -883,17 +940,19 @@ function createPDFPlugin(previewUid) { // Need to call this before the reload(), where the plugin resets its // internal page count. pdfViewer.goToPage('0'); - pdfViewer.reload(); pdfViewer.grayscale(!isColor()); return; } + // Get the complete preview document. + var dataIndex = previewModifiable && isPipeliningSupported ? '0' : '-1'; + pdfViewer = document.createElement('embed'); pdfViewer.setAttribute('id', 'pdf-viewer'); pdfViewer.setAttribute('type', 'application/x-google-chrome-print-preview-pdf'); - pdfViewer.setAttribute('src', 'chrome://print/' + previewUid + '/print.pdf'); + pdfViewer.setAttribute('src', getPageSrcURL(previewUid, dataIndex)); pdfViewer.setAttribute('aria-live', 'polite'); pdfViewer.setAttribute('aria-atomic', 'true'); $('mainview').appendChild(pdfViewer); @@ -907,9 +966,11 @@ function createPDFPlugin(previewUid) { */ function checkCompatiblePluginExists() { var dummyPlugin = $('dummy-viewer') - return (dummyPlugin.onload && - dummyPlugin.goToPage && - dummyPlugin.removePrintButton); + return !!(dummyPlugin.onload && + dummyPlugin.goToPage && + dummyPlugin.removePrintButton && + dummyPlugin.loadPreviewPage && + dummyPlugin.printPreviewPageCount); } window.addEventListener('DOMContentLoaded', onLoad); diff --git a/chrome/browser/resources/print_preview/print_preview_utils.js b/chrome/browser/resources/print_preview/print_preview_utils.js index 26a3760..68e5224 100644 --- a/chrome/browser/resources/print_preview/print_preview_utils.js +++ b/chrome/browser/resources/print_preview/print_preview_utils.js @@ -179,3 +179,13 @@ function pageSetToPageRanges(pageSet) { } return pageRanges; } + +/** + * Constructs a url for getting a specific page. + * @param {string} id The id of the preview data. + * @param {number} pageNumber The number of the desired page. + * @return {string} The constructed URL. + */ +function getPageSrcURL(id, pageNumber) { + return 'chrome://print/' + id + '/' + pageNumber + '/print.pdf'; +} diff --git a/chrome/browser/ui/webui/print_preview_data_source.cc b/chrome/browser/ui/webui/print_preview_data_source.cc index 04399b2..5cda03d 100644 --- a/chrome/browser/ui/webui/print_preview_data_source.cc +++ b/chrome/browser/ui/webui/print_preview_data_source.cc @@ -7,7 +7,9 @@ #include <algorithm> #include "base/message_loop.h" +#include "base/string_number_conversions.h" #include "base/string_piece.h" +#include "base/string_split.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" @@ -140,13 +142,18 @@ PrintPreviewDataSource::~PrintPreviewDataSource() { void PrintPreviewDataSource::StartDataRequest(const std::string& path, bool is_incognito, int request_id) { - scoped_refptr<RefCountedBytes> data(new RefCountedBytes); + scoped_refptr<RefCountedBytes> data; bool preview_data_requested = EndsWith(path, "/print.pdf", true); if (preview_data_requested) { - size_t index = path.rfind("/print.pdf"); - PrintPreviewDataService::GetInstance()->GetDataEntry(path.substr(0, index), - &data); + std::vector<std::string> url_substr; + base::SplitString(path, '/', &url_substr); + int page_index = 0; + if (url_substr.size() == 3 && base::StringToInt(url_substr[1], + &page_index)) { + PrintPreviewDataService::GetInstance()->GetDataEntry(url_substr[0], + page_index, &data); + } } if (path.empty()) { @@ -163,11 +170,11 @@ void PrintPreviewDataSource::StartDataRequest(const std::string& path, SendResponse(request_id, base::RefCountedString::TakeString(&full_html)); return; - } else if (preview_data_requested && data->front()) { + } else if (preview_data_requested && data.get()) { // Print Preview data. SendResponse(request_id, data); return; - } else { + } else { // Invalid request. scoped_refptr<RefCountedBytes> empty_bytes(new RefCountedBytes); SendResponse(request_id, empty_bytes); @@ -176,7 +183,5 @@ void PrintPreviewDataSource::StartDataRequest(const std::string& path, } std::string PrintPreviewDataSource::GetMimeType(const std::string& path) const { - if (path.empty()) - return "text/html"; // Print Preview Index Page. - return "application/pdf"; // Print Preview data + return path.empty() ? "text/html" : "application/pdf"; } diff --git a/chrome/browser/ui/webui/print_preview_data_source.h b/chrome/browser/ui/webui/print_preview_data_source.h index a6b02a1..7f82d00 100644 --- a/chrome/browser/ui/webui/print_preview_data_source.h +++ b/chrome/browser/ui/webui/print_preview_data_source.h @@ -14,13 +14,16 @@ // PrintPreviewDataSource serves data for chrome://print requests. // // The format for requesting data is as follows: -// chrome://print/<PrintPreviewUIAddrStr>/print.pdf +// chrome://print/<PrintPreviewUIAddrStr>/<PageIndex>/print.pdf // // Parameters (< > required): // <PrintPreviewUIAddrStr> = Print preview UI identifier. +// <PageIndex> = Page index is zero-based or +// |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to represent +// a print ready PDF. // // Example: -// chrome://print/0xab0123ef/print.pdf +// chrome://print/0xab0123ef/10/print.pdf class PrintPreviewDataSource : public ChromeURLDataManager::DataSource { public: diff --git a/chrome/browser/ui/webui/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview_handler.cc index 0096840..22be5a7 100644 --- a/chrome/browser/ui/webui/print_preview_handler.cc +++ b/chrome/browser/ui/webui/print_preview_handler.cc @@ -713,8 +713,9 @@ void PrintPreviewHandler::SendCloudPrintJob(const DictionaryValue& settings, std::string print_ticket) { scoped_refptr<RefCountedBytes> data; PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui_); - print_preview_ui->GetPrintPreviewData(&data); - CHECK(data->front()); + print_preview_ui->GetPrintPreviewDataForIndex( + printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data); + CHECK(data.get()); DCHECK_GT(data->size(), 0U); TabContentsWrapper* wrapper = @@ -846,9 +847,10 @@ void PrintPreviewHandler::OnNavigation() { void PrintPreviewHandler::FileSelected(const FilePath& path, int index, void* params) { PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui_); - scoped_refptr<RefCountedBytes> data(new RefCountedBytes()); - print_preview_ui->GetPrintPreviewData(&data); - if (!data->front()) { + scoped_refptr<RefCountedBytes> data; + print_preview_ui->GetPrintPreviewDataForIndex( + printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data); + if (!data.get()) { NOTREACHED(); return; } diff --git a/chrome/browser/ui/webui/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview_ui.cc index 6fe75d5..bb3c037 100644 --- a/chrome/browser/ui/webui/print_preview_ui.cc +++ b/chrome/browser/ui/webui/print_preview_ui.cc @@ -37,12 +37,19 @@ PrintPreviewUI::~PrintPreviewUI() { print_preview_data_service()->RemoveEntry(preview_ui_addr_str_); } -void PrintPreviewUI::GetPrintPreviewData(scoped_refptr<RefCountedBytes>* data) { - print_preview_data_service()->GetDataEntry(preview_ui_addr_str_, data); +void PrintPreviewUI::GetPrintPreviewDataForIndex( + int index, + scoped_refptr<RefCountedBytes>* data) { + print_preview_data_service()->GetDataEntry(preview_ui_addr_str_, index, data); } -void PrintPreviewUI::SetPrintPreviewData(const RefCountedBytes* data) { - print_preview_data_service()->SetDataEntry(preview_ui_addr_str_, data); +void PrintPreviewUI::SetPrintPreviewDataForIndex(int index, + const RefCountedBytes* data) { + print_preview_data_service()->SetDataEntry(preview_ui_addr_str_, index, data); +} + +void PrintPreviewUI::ClearAllPreviewData() { + print_preview_data_service()->RemoveEntry(preview_ui_addr_str_); } void PrintPreviewUI::OnInitiatorTabClosed( @@ -56,22 +63,33 @@ void PrintPreviewUI::OnPrintPreviewRequest() { } void PrintPreviewUI::OnDidGetPreviewPageCount(int document_cookie, - int page_count) { + int page_count, + bool is_modifiable) { DCHECK_GT(page_count, 0); document_cookie_ = document_cookie; FundamentalValue count(page_count); - CallJavascriptFunction("onDidGetPreviewPageCount", count); + FundamentalValue modifiable(is_modifiable); + CallJavascriptFunction("onDidGetPreviewPageCount", count, modifiable); } void PrintPreviewUI::OnDidPreviewPage(int page_number) { DCHECK_GE(page_number, 0); FundamentalValue number(page_number); - CallJavascriptFunction("onDidPreviewPage", number); + StringValue ui_identifier(preview_ui_addr_str_); + CallJavascriptFunction("onDidPreviewPage", number, ui_identifier); +} + +void PrintPreviewUI::OnReusePreviewData(int preview_request_id) { + DecrementRequestCount(); + + StringValue ui_identifier(preview_ui_addr_str_); + FundamentalValue ui_preview_request_id(preview_request_id); + CallJavascriptFunction("reloadPreviewPages", ui_identifier, + ui_preview_request_id); } void PrintPreviewUI::OnPreviewDataIsAvailable(int expected_pages_count, const string16& job_title, - bool modifiable, int preview_request_id) { VLOG(1) << "Print preview request finished with " << expected_pages_count << " pages"; @@ -84,13 +102,11 @@ void PrintPreviewUI::OnPreviewDataIsAvailable(int expected_pages_count, expected_pages_count); initial_preview_start_time_ = base::TimeTicks(); } - FundamentalValue pages_count(expected_pages_count); StringValue title(job_title); - FundamentalValue is_preview_modifiable(modifiable); StringValue ui_identifier(preview_ui_addr_str_); FundamentalValue ui_preview_request_id(preview_request_id); - CallJavascriptFunction("updatePrintPreview", title, is_preview_modifiable, - ui_identifier, ui_preview_request_id); + CallJavascriptFunction("updatePrintPreview", title, ui_identifier, + ui_preview_request_id); } void PrintPreviewUI::OnNavigation() { diff --git a/chrome/browser/ui/webui/print_preview_ui.h b/chrome/browser/ui/webui/print_preview_ui.h index 13b4aff..192611b 100644 --- a/chrome/browser/ui/webui/print_preview_ui.h +++ b/chrome/browser/ui/webui/print_preview_ui.h @@ -22,13 +22,19 @@ class PrintPreviewUI : public ChromeWebUI { explicit PrintPreviewUI(TabContents* contents); virtual ~PrintPreviewUI(); - // Gets the print preview |data|. The data is valid as long as the - // PrintPreviewDataService is valid and SetPrintPreviewData() does not get - // called. - void GetPrintPreviewData(scoped_refptr<RefCountedBytes>* data); + // Gets the print preview |data|. |index| is zero-based, and can be + // |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to get the entire preview + // document. + void GetPrintPreviewDataForIndex(int index, + scoped_refptr<RefCountedBytes>* data); - // Sets the print preview |data|. - void SetPrintPreviewData(const RefCountedBytes* data); + // Sets the print preview |data|. |index| is zero-based, and can be + // |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to set the entire preview + // document. + void SetPrintPreviewDataForIndex(int index, const RefCountedBytes* data); + + // Clear the existing print preview data. + void ClearAllPreviewData(); // Notify the Web UI that there is a print preview request. // There should be a matching call to OnPreviewDataIsAvailable() or @@ -36,7 +42,11 @@ class PrintPreviewUI : public ChromeWebUI { void OnPrintPreviewRequest(); // Notify the Web UI that the print preview will have |page_count| pages. - void OnDidGetPreviewPageCount(int document_cookie_, int page_count); + // |is_modifiable| indicates if the preview can be rerendered with different + // print settings. + void OnDidGetPreviewPageCount(int document_cookie_, + int page_count, + bool is_modifiable); // Notify the Web UI that the 0-based page |page_number| has been rendered. void OnDidPreviewPage(int page_number); @@ -44,14 +54,13 @@ class PrintPreviewUI : public ChromeWebUI { // Notify the Web UI renderer that preview data is available. // |expected_pages_count| specifies the total number of pages. // |job_title| is the title of the page being previewed. - // |modifiable| indicates if the preview can be rerendered with different - // print settings. // |preview_request_id| indicates wich request resulted in this response. void OnPreviewDataIsAvailable(int expected_pages_count, const string16& job_title, - bool modifiable, int preview_request_id); + void OnReusePreviewData(int preview_request_id); + // Notify the Web UI that a navigation has occurred in this tab. This is the // last chance to communicate with the source tab before the assocation is // erased. diff --git a/chrome/browser/ui/webui/print_preview_ui_unittest.cc b/chrome/browser/ui/webui/print_preview_ui_unittest.cc index 65613d3..53a09c1 100644 --- a/chrome/browser/ui/webui/print_preview_ui_unittest.cc +++ b/chrome/browser/ui/webui/print_preview_ui_unittest.cc @@ -11,6 +11,7 @@ #include "chrome/test/browser_with_test_window_test.h" #include "chrome/test/testing_profile.h" #include "content/browser/tab_contents/tab_contents.h" +#include "printing/print_job_constants.h" namespace { @@ -47,20 +48,99 @@ TEST_F(PrintPreviewUITest, PrintPreviewData) { reinterpret_cast<PrintPreviewUI*>(preview_tab->web_ui()); ASSERT_TRUE(preview_ui != NULL); - scoped_refptr<RefCountedBytes> data(new RefCountedBytes); - preview_ui->GetPrintPreviewData(&data); - EXPECT_EQ(NULL, data->front()); - EXPECT_EQ(0U, data->size()); + scoped_refptr<RefCountedBytes> data; + preview_ui->GetPrintPreviewDataForIndex( + printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, + &data); + EXPECT_EQ(NULL, data.get()); std::vector<unsigned char> preview_data(blob1, blob1 + sizeof(blob1)); scoped_refptr<RefCountedBytes> dummy_data(new RefCountedBytes(preview_data)); - preview_ui->SetPrintPreviewData(dummy_data.get()); - preview_ui->GetPrintPreviewData(&data); + preview_ui->SetPrintPreviewDataForIndex( + printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, + dummy_data.get()); + preview_ui->GetPrintPreviewDataForIndex( + printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, + &data); EXPECT_EQ(dummy_data->size(), data->size()); EXPECT_EQ(dummy_data.get(), data.get()); // This should not cause any memory leaks. dummy_data = new RefCountedBytes(); - preview_ui->SetPrintPreviewData(dummy_data); + preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, + dummy_data); + + // Clear the preview data. + preview_ui->ClearAllPreviewData(); + + preview_ui->GetPrintPreviewDataForIndex( + printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, + &data); + EXPECT_EQ(NULL, data.get()); +} + +// Set and get the individual draft pages. +TEST_F(PrintPreviewUITest, PrintPreviewDraftPages) { +#if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS) + CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnablePrintPreview); +#endif + ASSERT_TRUE(browser()); + BrowserList::SetLastActive(browser()); + ASSERT_TRUE(BrowserList::GetLastActive()); + + browser()->NewTab(); + TabContents* initiator_tab = browser()->GetSelectedTabContents(); + ASSERT_TRUE(initiator_tab); + + scoped_refptr<printing::PrintPreviewTabController> + controller(new printing::PrintPreviewTabController()); + ASSERT_TRUE(controller); + + TabContents* preview_tab = controller->GetOrCreatePreviewTab(initiator_tab); + + EXPECT_NE(initiator_tab, preview_tab); + EXPECT_EQ(2, browser()->tab_count()); + + PrintPreviewUI* preview_ui = + reinterpret_cast<PrintPreviewUI*>(preview_tab->web_ui()); + ASSERT_TRUE(preview_ui != NULL); + + scoped_refptr<RefCountedBytes> data; + preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, &data); + EXPECT_EQ(NULL, data.get()); + + std::vector<unsigned char> preview_data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> dummy_data(new RefCountedBytes(preview_data)); + + preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, + dummy_data.get()); + preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, &data); + EXPECT_EQ(dummy_data->size(), data->size()); + EXPECT_EQ(dummy_data.get(), data.get()); + + // Set and get the third page data. + preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 2, + dummy_data.get()); + preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 2, + &data); + EXPECT_EQ(dummy_data->size(), data->size()); + EXPECT_EQ(dummy_data.get(), data.get()); + + // Get the second page data. + preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 1, + &data); + EXPECT_EQ(NULL, data.get()); + + preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 1, + dummy_data.get()); + preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 1, + &data); + EXPECT_EQ(dummy_data->size(), data->size()); + EXPECT_EQ(dummy_data.get(), data.get()); + + // Clear the preview data. + preview_ui->ClearAllPreviewData(); + preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, &data); + EXPECT_EQ(NULL, data.get()); } diff --git a/chrome/common/print_messages.h b/chrome/common/print_messages.h index 8773bae..ee5bf44 100644 --- a/chrome/common/print_messages.h +++ b/chrome/common/print_messages.h @@ -60,6 +60,11 @@ IPC_STRUCT_BEGIN(PrintMsg_PrintPage_Params) // The page number is the indicator of the square that should be rendered // according to the layout specified in PrintMsg_Print_Params. IPC_STRUCT_MEMBER(int, page_number) + + // The page number in the resulting document. If the user is only printing + // page 2, |page_number| above will 1, but |page_slot| will be 0, as it's the + // first page in the final document. + IPC_STRUCT_MEMBER(int, page_slot) IPC_STRUCT_END() IPC_STRUCT_BEGIN(PrintMsg_PrintPages_Params) @@ -96,6 +101,19 @@ IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewDocument_Params) IPC_STRUCT_MEMBER(int, preview_request_id) IPC_STRUCT_END() +// Parameters to describe a rendered preview page. +IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewPage_Params) + // A shared memory handle to metafile data for a draft document of the page. + IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) + + // Size of metafile data. + IPC_STRUCT_MEMBER(uint32, data_size) + + // |page_number| is zero-based and can be |printing::INVALID_PAGE_INDEX| if it + // is just a check. + IPC_STRUCT_MEMBER(int, page_number) +IPC_STRUCT_END() + // Parameters to describe a rendered page. IPC_STRUCT_BEGIN(PrintHostMsg_DidPrintPage_Params) // A shared memory handle to the EMF data. This data can be quite large so a @@ -174,7 +192,11 @@ IPC_MESSAGE_ROUTED0(PrintMsg_PrintForSystemDialog) IPC_MESSAGE_ROUTED0(PrintMsg_ResetScriptedPrintCount) // Tells a renderer to continue generating the print preview. -IPC_MESSAGE_ROUTED0(PrintMsg_ContinuePreview) +// Use |requested_preview_page_index| to request a specific preview page data. +// |requested_preview_page_index| is 1-based or |printing::INVALID_PAGE_INDEX| +// to render the next page. +IPC_MESSAGE_ROUTED1(PrintMsg_ContinuePreview, + int /* requested_preview_page_index */) // Tells a renderer to abort the print preview and reset all state. IPC_MESSAGE_ROUTED0(PrintMsg_AbortPreview) @@ -242,15 +264,14 @@ IPC_MESSAGE_CONTROL1(PrintHostMsg_TempFileForPrintingWritten, IPC_MESSAGE_ROUTED0(PrintHostMsg_RequestPrintPreview) // Notify the browser the number of pages in the print preview document. -IPC_MESSAGE_ROUTED2(PrintHostMsg_DidGetPreviewPageCount, +IPC_MESSAGE_ROUTED3(PrintHostMsg_DidGetPreviewPageCount, int /* document cookie */, - int /* page count */) + int /* page count */, + bool /* is modifiable */) -// Notify the browser a print preview page has been rendered. Give the browser -// a chance to cancel the print preview as needed. Page number is zero-based, -// and can be -1 if it is just a check. +// Notify the browser a print preview page has been rendered. IPC_MESSAGE_ROUTED1(PrintHostMsg_DidPreviewPage, - int /* page number */) + PrintHostMsg_DidPreviewPage_Params /* params */) // Sends back to the browser the complete rendered document for print preview // that was requested by a PrintMsg_PrintPreview message. The memory handle in diff --git a/chrome/renderer/mock_render_thread.cc b/chrome/renderer/mock_render_thread.cc index ab0654d..aac7156 100644 --- a/chrome/renderer/mock_render_thread.cc +++ b/chrome/renderer/mock_render_thread.cc @@ -212,12 +212,14 @@ void MockRenderThread::OnDidPrintPage( } void MockRenderThread::OnDidGetPreviewPageCount(int document_cookie, - int number_pages) { + int number_pages, + bool is_modifiable) { print_preview_pages_remaining_ = number_pages; } -void MockRenderThread::OnDidPreviewPage(int page_number) { - if (page_number < 0) +void MockRenderThread::OnDidPreviewPage( + const PrintHostMsg_DidPreviewPage_Params& params) { + if (params.page_number < printing::FIRST_PAGE_INDEX) return; print_preview_pages_remaining_--; } diff --git a/chrome/renderer/mock_render_thread.h b/chrome/renderer/mock_render_thread.h index 1d2036b..5eb7ad3 100644 --- a/chrome/renderer/mock_render_thread.h +++ b/chrome/renderer/mock_render_thread.h @@ -20,6 +20,7 @@ class MessageReplyDeserializer; struct PrintMsg_Print_Params; struct PrintMsg_PrintPages_Params; +struct PrintHostMsg_DidPreviewPage_Params; struct PrintHostMsg_ScriptedPrint_Params; // This class is very simple mock of RenderThread. It simulates an IPC channel @@ -124,8 +125,9 @@ class MockRenderThread : public RenderThreadBase { void OnDidGetPrintedPagesCount(int cookie, int number_pages); void OnDidPrintPage(const PrintHostMsg_DidPrintPage_Params& params); - void OnDidGetPreviewPageCount(int document_cookie, int number_pages); - void OnDidPreviewPage(int page_number); + void OnDidGetPreviewPageCount(int document_cookie, int number_pages, + bool is_modifiable); + void OnDidPreviewPage(const PrintHostMsg_DidPreviewPage_Params& params); // For print preview, PrintWebViewHelper will update settings. void OnUpdatePrintSettings(int document_cookie, diff --git a/chrome/renderer/print_web_view_helper.cc b/chrome/renderer/print_web_view_helper.cc index 8430356..028fe2d 100644 --- a/chrome/renderer/print_web_view_helper.cc +++ b/chrome/renderer/print_web_view_helper.cc @@ -337,6 +337,8 @@ void PrintWebViewHelper::OnPrintPreview(const DictionaryValue& settings) { Send(new PrintHostMsg_PagesReadyForPreview(routing_id(), preview_params)); return; } + // Always clear |old_print_pages_params_| before rendering the pages. + old_print_pages_params_.reset(); // PDF printer device supports alpha blending. print_pages_params_->params.supports_alpha_blend = true; @@ -350,20 +352,29 @@ bool PrintWebViewHelper::CreatePreviewDocument() { if (!print_preview_context_.CreatePreviewDocument(&print_params, pages)) return false; int page_count = print_preview_context_.total_page_count(); + bool is_modifiable = print_preview_context_.IsModifiable(); int document_cookie = print_pages_params_->params.document_cookie; Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), document_cookie, - page_count)); - PreviewPageRendered(-1); + page_count, is_modifiable)); + PreviewPageRendered(printing::INVALID_PAGE_INDEX, NULL); return true; } -void PrintWebViewHelper::OnContinuePreview() { +void PrintWebViewHelper::OnContinuePreview(int requested_preview_page_index) { // Spurious message. We already finished/cancelled/aborted the print preview. if (!print_preview_context_.IsBusy()) return; + int page_number; +#if defined(USE_SKIA) + if (requested_preview_page_index >= printing::FIRST_PAGE_INDEX) { + page_number = requested_preview_page_index; + } else +#endif + { + page_number = print_preview_context_.GetNextPageNumber(); + } - int page_number = print_preview_context_.GetNextPageNumber(); - if (page_number >= 0) { + if (page_number >= printing::FIRST_PAGE_INDEX) { // Continue generating the print preview. RenderPreviewPage(page_number); return; @@ -576,6 +587,7 @@ bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params& params, if (params.pages.empty()) { for (int i = 0; i < page_count; ++i) { page_params.page_number = i; + page_params.page_slot = i; PrintPageInternal(page_params, canvas_size, frame); } } else { @@ -583,6 +595,7 @@ bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params& params, if (params.pages[i] >= page_count) break; page_params.page_number = params.pages[i]; + page_params.page_slot = i; PrintPageInternal(page_params, canvas_size, frame); } } @@ -901,8 +914,33 @@ void PrintWebViewHelper::RequestPrintPreview() { Send(new PrintHostMsg_RequestPrintPreview(routing_id())); } -void PrintWebViewHelper::PreviewPageRendered(int page_number) { - Send(new PrintHostMsg_DidPreviewPage(routing_id(), page_number)); +void PrintWebViewHelper::PreviewPageRendered(int page_number, + printing::Metafile* metafile) { +#if !defined(OS_WIN) + if ((page_number == printing::INVALID_PAGE_INDEX && metafile) || + (page_number >= printing::FIRST_PAGE_INDEX && !metafile && + print_preview_context_.IsModifiable())) { + NOTREACHED(); + DidFinishPrinting(FAIL_PREVIEW); + return; + } +#endif + + uint32 buf_size = 0; + PrintHostMsg_DidPreviewPage_Params preview_page_params; + // Get the size of the resulting metafile. + if (metafile) { + buf_size = metafile->GetDataSize(); + DCHECK_GT(buf_size, 0u); + if (!CopyMetafileDataToSharedMem( + metafile, &(preview_page_params.metafile_data_handle))) { + DidFinishPrinting(FAIL_PREVIEW); + return; + } + } + preview_page_params.data_size = buf_size; + preview_page_params.page_number = page_number; + Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params)); } PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext() @@ -966,15 +1004,20 @@ bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument( current_page_number_ = 0; if (pages.empty()) { actual_page_count_ = total_page_count_; - rendered_pages_ = std::vector<bool>(total_page_count_, false); + rendered_pages_ = std::vector<PreviewPageInfo>(total_page_count_, + std::make_pair(false, -1)); } else { actual_page_count_ = pages.size(); - rendered_pages_ = std::vector<bool>(total_page_count_, true); + rendered_pages_ = std::vector<PreviewPageInfo>(total_page_count_, + std::make_pair(true, -1)); for (int i = 0; i < actual_page_count_; ++i) { int page_number = pages[i]; - if (page_number < 0 || page_number >= total_page_count_) + if (page_number < printing::FIRST_PAGE_INDEX || + page_number >= total_page_count_) { return false; - rendered_pages_[page_number] = false; + } + rendered_pages_[page_number].first = false; + rendered_pages_[page_number].second = i; } } @@ -1029,14 +1072,15 @@ void PrintWebViewHelper::PrintPreviewContext::Abort() { int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() { DCHECK_EQ(RENDERING, state_); - while (current_page_number_ < total_page_count_ && - rendered_pages_[current_page_number_]) { - ++current_page_number_; + for (int i = 0; i < total_page_count_; i++) { + if (!rendered_pages_[current_page_number_].first) + break; + current_page_number_ = (current_page_number_ + 1) % total_page_count_; } - if (current_page_number_ == total_page_count_) - return -1; - rendered_pages_[current_page_number_] = true; - return current_page_number_++; + if (rendered_pages_[current_page_number_].first) + return printing::INVALID_PAGE_INDEX; + rendered_pages_[current_page_number_].first = true; + return current_page_number_; } bool PrintWebViewHelper::PrintPreviewContext::IsReadyToRender() const { @@ -1048,12 +1092,21 @@ bool PrintWebViewHelper::PrintPreviewContext::IsBusy() const { } bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() const { + // TODO(vandebo) I think this should only return false if the content is a + // PDF, just because we are printing a particular node does not mean it's + // a PDF (right?), we should check the mime type of the node. if (node()) return false; std::string mime(frame()->dataSource()->response().mimeType().utf8()); return mime != "application/pdf"; } +int PrintWebViewHelper::PrintPreviewContext::GetPageSlotForPage( + int page_number) const { + int page_slot = rendered_pages_[page_number].second; + return page_slot == -1 ? page_number : page_slot; +} + WebKit::WebFrame* PrintWebViewHelper::PrintPreviewContext::frame() const { return frame_; } diff --git a/chrome/renderer/print_web_view_helper.h b/chrome/renderer/print_web_view_helper.h index fa555fe..14fc465 100644 --- a/chrome/renderer/print_web_view_helper.h +++ b/chrome/renderer/print_web_view_helper.h @@ -6,6 +6,7 @@ #define CHROME_RENDERER_PRINT_WEB_VIEW_HELPER_H_ #pragma once +#include <utility> #include <vector> #include "base/memory/scoped_ptr.h" @@ -134,8 +135,10 @@ class PrintWebViewHelper : public RenderViewObserver, // Initialize the print preview document. bool CreatePreviewDocument(); - // Continue generating the print preview. - void OnContinuePreview(); + // Continue generating the print preview. |requested_preview_page_index| + // specifies the browser requested preview page index. It is 1-based or + // |printing::INVALID_PAGE_INDEX| to continue with next page. + void OnContinuePreview(int requested_preview_page_index); // Renders a print preview page. |page_number| is 0-based. void RenderPreviewPage(int page_number); // Finalize the print preview document. @@ -225,11 +228,12 @@ class PrintWebViewHelper : public RenderViewObserver, // Platform specific helper function for rendering page(s) to |metafile|. #if defined(OS_WIN) void RenderPage(const PrintMsg_Print_Params& params, float* scale_factor, - int page_number, bool is_preview, WebKit::WebFrame* frame, + int page_number, int page_slot, bool is_preview, + WebKit::WebFrame* frame, scoped_ptr<printing::Metafile>* metafile); #elif defined(OS_MACOSX) void RenderPage(const gfx::Size& page_size, const gfx::Rect& content_area, - const float& scale_factor, int page_number, + const float& scale_factor, int page_number, int page_slot, WebKit::WebFrame* frame, printing::Metafile* metafile); #elif defined(OS_POSIX) bool RenderPages(const PrintMsg_PrintPages_Params& params, @@ -281,8 +285,9 @@ class PrintWebViewHelper : public RenderViewObserver, void RequestPrintPreview(); // Notify the browser a print preview page has been rendered. - // |page_number| is 0-based. - void PreviewPageRendered(int page_number); + // |page_number| is 0-based or |printing::INVALID_PAGE_INDEX| to check + // for pending preview requests. + void PreviewPageRendered(int page_number, printing::Metafile* metafile); WebKit::WebView* print_web_view_; @@ -336,6 +341,11 @@ class PrintWebViewHelper : public RenderViewObserver, bool IsBusy() const; bool IsModifiable() const; + // Return the page slot in the final document for |page_number|. i.e. if + // the user selected just page 3, the page slot would be 0, since it is + // the first page in the resulting document. + int GetPageSlotForPage(int page_number) const; + // Getters WebKit::WebFrame* frame() const; WebKit::WebNode* node() const; @@ -372,8 +382,10 @@ class PrintWebViewHelper : public RenderViewObserver, // The current page to render. int current_page_number_; - // Array to keep track of which pages have been printed. - std::vector<bool> rendered_pages_; + // |rendered_pages_| tracks which pages need to be printed as well as + // the page slot it should be printed in. See GetPageSlotForPage. + typedef std::pair<bool, int> PreviewPageInfo; + std::vector<PreviewPageInfo> rendered_pages_; base::TimeDelta document_render_time_; base::TimeTicks begin_time_; diff --git a/chrome/renderer/print_web_view_helper_browsertest.cc b/chrome/renderer/print_web_view_helper_browsertest.cc index 86aec37..06246d4 100644 --- a/chrome/renderer/print_web_view_helper_browsertest.cc +++ b/chrome/renderer/print_web_view_helper_browsertest.cc @@ -354,8 +354,10 @@ TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreview) { // Need to finish simulating print preview. // Generate the page and finalize it. - PrintWebViewHelper::Get(view_)->OnContinuePreview(); - PrintWebViewHelper::Get(view_)->OnContinuePreview(); + PrintWebViewHelper::Get(view_)->OnContinuePreview( + printing::INVALID_PAGE_INDEX); + PrintWebViewHelper::Get(view_)->OnContinuePreview( + printing::INVALID_PAGE_INDEX); EXPECT_EQ(0, render_thread_.print_preview_pages_remaining()); VerifyPrintPreviewFailed(false); diff --git a/chrome/renderer/print_web_view_helper_linux.cc b/chrome/renderer/print_web_view_helper_linux.cc index 6620693..27667d6 100644 --- a/chrome/renderer/print_web_view_helper_linux.cc +++ b/chrome/renderer/print_web_view_helper_linux.cc @@ -29,15 +29,23 @@ void PrintWebViewHelper::RenderPreviewPage(int page_number) { PrintMsg_PrintPage_Params page_params; page_params.params = print_preview_context_.print_params(); page_params.page_number = page_number; + page_params.page_slot = + print_preview_context_.GetPageSlotForPage(page_number); base::TimeTicks begin_time = base::TimeTicks::Now(); PrintPageInternal(page_params, print_preview_context_.GetPrintCanvasSize(), print_preview_context_.frame(), print_preview_context_.metafile()); + print_preview_context_.RenderedPreviewPage( base::TimeTicks::Now() - begin_time); - PreviewPageRendered(page_number); + printing::Metafile* page_metafile = NULL; + if (print_preview_context_.IsModifiable()) { + page_metafile = reinterpret_cast<printing::PreviewMetafile*>( + print_preview_context_.metafile())->GetMetafileForCurrentPage(); + } + PreviewPageRendered(page_number, page_metafile); } bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params& params, @@ -154,11 +162,13 @@ bool PrintWebViewHelper::RenderPages(const PrintMsg_PrintPages_Params& params, if (params.pages.empty()) { for (int i = 0; i < *page_count; ++i) { page_params.page_number = i; + page_params.page_slot = i; PrintPageInternal(page_params, canvas_size, frame, metafile); } } else { for (size_t i = 0; i < params.pages.size(); ++i) { page_params.page_number = params.pages[i]; + page_params.page_slot = i; PrintPageInternal(page_params, canvas_size, frame, metafile); } } @@ -192,7 +202,7 @@ void PrintWebViewHelper::PrintPageInternal( page_layout_in_points.content_height); SkDevice* device = metafile->StartPageForVectorCanvas( - params.page_number, page_size, content_area, 1.0f); + params.page_slot, page_size, content_area, 1.0f); if (!device) return; diff --git a/chrome/renderer/print_web_view_helper_mac.mm b/chrome/renderer/print_web_view_helper_mac.mm index f5ded39..3ceeb5d 100644 --- a/chrome/renderer/print_web_view_helper_mac.mm +++ b/chrome/renderer/print_web_view_helper_mac.mm @@ -32,11 +32,12 @@ void PrintWebViewHelper::PrintPageInternal( float scale_factor = frame->getPrintPageShrink(params.page_number); int page_number = params.page_number; + int page_slot = params.page_slot; // Render page for printing. gfx::Rect content_area(params.params.printable_size); RenderPage(params.params.printable_size, content_area, scale_factor, - page_number, frame, &metafile); + page_number, page_slot, frame, &metafile); metafile.FinishDocument(); PrintHostMsg_DidPrintPage_Params page_params; @@ -66,23 +67,60 @@ void PrintWebViewHelper::RenderPreviewPage(int page_number) { printParams.printable_size.width(), printParams.printable_size.height()); + printing::Metafile* initial_render_metafile = + print_preview_context_.metafile(); +#if !defined(USE_SKIA) + if (print_preview_context_.IsModifiable()) { + initial_render_metafile = new printing::PreviewMetafile(); + if (!initial_render_metafile->Init()) { + DidFinishPrinting(FAIL_PREVIEW); + return; + } + } +#endif + base::TimeTicks begin_time = base::TimeTicks::Now(); RenderPage(printParams.page_size, content_area, scale_factor, page_number, - print_preview_context_.frame(), print_preview_context_.metafile()); + print_preview_context_.GetPageSlotForPage(page_number), + print_preview_context_.frame(), initial_render_metafile); print_preview_context_.RenderedPreviewPage( base::TimeTicks::Now() - begin_time); - PreviewPageRendered(page_number); + + printing::Metafile* draft_metafile = NULL; + if (print_preview_context_.IsModifiable()) { +#if defined(USE_SKIA) + draft_metafile = reinterpret_cast<printing::PreviewMetafile*>( + print_preview_context_.metafile())->GetMetafileForCurrentPage(); +#else + draft_metafile = initial_render_metafile; + draft_metafile->FinishDocument(); + + // With CG, we rendered into a new metafile so we could get it as a draft + // document. Now we need to add it to complete document. + draft_metafile->RenderPage(1, + print_preview_context_.metafile()->context(), + CGRectMake(content_area.x(), content_area.y(), + content_area.width(), + content_area.height()), + false /*shrunk_to_fit*/, + false /*stretch_to_fit*/, + true /*center_horizontally*/, + true /*center_vertically*/); +#endif + } + + PreviewPageRendered(page_number, draft_metafile); } void PrintWebViewHelper::RenderPage( const gfx::Size& page_size, const gfx::Rect& content_area, - const float& scale_factor, int page_number, WebFrame* frame, + const float& scale_factor, int page_number, int page_slot, WebFrame* frame, printing::Metafile* metafile) { { #if defined(USE_SKIA) SkDevice* device = metafile->StartPageForVectorCanvas( - page_number, page_size, content_area, scale_factor); + page_slot, page_size, content_area, scale_factor); if (!device) return; diff --git a/chrome/renderer/print_web_view_helper_win.cc b/chrome/renderer/print_web_view_helper_win.cc index d627fff..7f74acf 100644 --- a/chrome/renderer/print_web_view_helper_win.cc +++ b/chrome/renderer/print_web_view_helper_win.cc @@ -84,13 +84,14 @@ void PrintWebViewHelper::PrintPageInternal( skia::InitializeDC(metafile->context()); int page_number = params.page_number; + int page_slot = params.page_slot; // Calculate the dpi adjustment. float scale_factor = static_cast<float>(params.params.desired_dpi / params.params.dpi); // Render page for printing. - RenderPage(params.params, &scale_factor, page_number, false, frame, + RenderPage(params.params, &scale_factor, page_number, page_slot, false, frame, &metafile); // Close the device context to retrieve the compiled metafile. @@ -128,23 +129,28 @@ void PrintWebViewHelper::RenderPreviewPage(int page_number) { // Calculate the dpi adjustment. float scale_factor = static_cast<float>(print_params.desired_dpi / print_params.dpi); - // Needed for RenderPage() below. - // Not taking ownership with intent to reset(). + + // |metafile| is needed for RenderPage() below. |metafile| will not take the + // ownership of |print_preview_context_| metafile. scoped_ptr<Metafile> metafile(print_preview_context_.metafile()); base::TimeTicks begin_time = base::TimeTicks::Now(); - RenderPage(print_params, &scale_factor, page_number, true, + RenderPage(print_params, &scale_factor, page_number, + print_preview_context_.GetPageSlotForPage(page_number), true, print_preview_context_.frame(), &metafile); - // Release since |print_preview_context_| is the real owner. - metafile.release(); + print_preview_context_.RenderedPreviewPage( base::TimeTicks::Now() - begin_time); - PreviewPageRendered(page_number); + + // Release since |print_preview_context_| is the real owner. + metafile.release(); + PreviewPageRendered(page_number, NULL); } void PrintWebViewHelper::RenderPage( const PrintMsg_Print_Params& params, float* scale_factor, int page_number, - bool is_preview, WebFrame* frame, scoped_ptr<Metafile>* metafile) { + int page_slot, bool is_preview, WebFrame* frame, + scoped_ptr<Metafile>* metafile) { PageSizeMargins page_layout_in_points; GetPageSizeAndMarginsInPoints(frame, page_number, params, &page_layout_in_points); @@ -173,7 +179,7 @@ void PrintWebViewHelper::RenderPage( static_cast<int>(page_layout_in_points.content_width), static_cast<int>(page_layout_in_points.content_height)); SkDevice* device = (*metafile)->StartPageForVectorCanvas( - page_number, page_size, content_area, + page_slot, page_size, content_area, frame->getPrintPageShrink(page_number)); DCHECK(device); // The printPage method may take a reference to the canvas we pass down, so it diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc index f0e5e77..0c50b44 100644 --- a/printing/print_job_constants.cc +++ b/printing/print_job_constants.cc @@ -45,4 +45,10 @@ const char kSettingPrinterName[] = "printerName"; // Print to PDF option: true if selected, false if not. const char kSettingPrintToPDF[] = "printToPDF"; +// Indices used to represent first page, invalid page and complete +// preview document. +const int FIRST_PAGE_INDEX = 0; +const int COMPLETE_PREVIEW_DOCUMENT_INDEX = -1; +const int INVALID_PAGE_INDEX = -2; + } // namespace printing diff --git a/printing/print_job_constants.h b/printing/print_job_constants.h index e877a01..8c6103b 100644 --- a/printing/print_job_constants.h +++ b/printing/print_job_constants.h @@ -21,6 +21,10 @@ extern const char kSettingPageRangeTo[]; extern const char kSettingPrinterName[]; extern const char kSettingPrintToPDF[]; +extern const int FIRST_PAGE_INDEX; +extern const int COMPLETE_PREVIEW_DOCUMENT_INDEX; +extern const int INVALID_PAGE_INDEX; + // Print job duplex mode values. enum DuplexMode { SIMPLEX, |