blob: fb429993ff3e264afa3a00e30e05640708cc31e5 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "headless/lib/browser/headless_print_manager.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "components/printing/browser/print_manager_utils.h"
#include "components/printing/common/print_messages.h"
#include "content/public/browser/render_view_host.h"
#include "printing/print_job_constants.h"
#include "printing/units.h"
namespace headless {
struct HeadlessPrintManager::FrameDispatchHelper {
HeadlessPrintManager* manager;
content::RenderFrameHost* render_frame_host;
bool Send(IPC::Message* msg) { return render_frame_host->Send(msg); }
void OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
manager->OnGetDefaultPrintSettings(reply_msg);
}
void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& scripted_params,
IPC::Message* reply_msg) {
manager->OnScriptedPrint(scripted_params, reply_msg);
}
};
HeadlessPrintSettings::HeadlessPrintSettings()
: prefer_css_page_size(false),
landscape(false),
display_header_footer(false),
should_print_backgrounds(false),
scale(1),
ignore_invalid_page_ranges(false) {}
HeadlessPrintManager::HeadlessPrintManager(content::WebContents* web_contents)
: PrintManager(web_contents) {
Reset();
}
HeadlessPrintManager::~HeadlessPrintManager() = default;
// static
std::string HeadlessPrintManager::PrintResultToString(PrintResult result) {
switch (result) {
case PRINT_SUCCESS:
return std::string(); // no error message
case PRINTING_FAILED:
return "Printing failed";
case INVALID_PRINTER_SETTINGS:
return "Show invalid printer settings error";
case INVALID_MEMORY_HANDLE:
return "Invalid memory handle";
case METAFILE_MAP_ERROR:
return "Map to shared memory error";
case METAFILE_INVALID_HEADER:
return "Invalid metafile header";
case METAFILE_GET_DATA_ERROR:
return "Get data from metafile error";
case SIMULTANEOUS_PRINT_ACTIVE:
return "The previous printing job hasn't finished";
case PAGE_RANGE_SYNTAX_ERROR:
return "Page range syntax error";
case PAGE_COUNT_EXCEEDED:
return "Page range exceeds page count";
default:
NOTREACHED();
return "Unknown PrintResult";
}
}
// static
HeadlessPrintManager::PageRangeStatus
HeadlessPrintManager::PageRangeTextToPages(base::StringPiece page_range_text,
bool ignore_invalid_page_ranges,
int pages_count,
std::vector<int>* pages) {
printing::PageRanges page_ranges;
for (const auto& range_string :
base::SplitStringPiece(page_range_text, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
printing::PageRange range;
if (range_string.find("-") == base::StringPiece::npos) {
if (!base::StringToInt(range_string, &range.from))
return SYNTAX_ERROR;
range.to = range.from;
} else if (range_string == "-") {
range.from = 1;
range.to = pages_count;
} else if (range_string.starts_with("-")) {
range.from = 1;
if (!base::StringToInt(range_string.substr(1), &range.to))
return SYNTAX_ERROR;
} else if (range_string.ends_with("-")) {
range.to = pages_count;
if (!base::StringToInt(range_string.substr(0, range_string.length() - 1),
&range.from))
return SYNTAX_ERROR;
} else {
auto tokens = base::SplitStringPiece(
range_string, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (tokens.size() != 2 || !base::StringToInt(tokens[0], &range.from) ||
!base::StringToInt(tokens[1], &range.to))
return SYNTAX_ERROR;
}
if (range.from < 1 || range.from > range.to) {
if (!ignore_invalid_page_ranges)
return SYNTAX_ERROR;
continue;
}
if (range.from > pages_count) {
if (!ignore_invalid_page_ranges)
return LIMIT_ERROR;
continue;
}
if (range.to > pages_count)
range.to = pages_count;
// Page numbers are 1-based in the dictionary.
// Page numbers are 0-based for the print settings.
range.from--;
range.to--;
page_ranges.push_back(range);
}
*pages = printing::PageRange::GetPages(page_ranges);
return PRINT_NO_ERROR;
}
void HeadlessPrintManager::GetPDFContents(content::RenderFrameHost* rfh,
const HeadlessPrintSettings& settings,
GetPDFCallback callback) {
DCHECK(callback);
if (callback_) {
std::move(callback).Run(SIMULTANEOUS_PRINT_ACTIVE,
base::MakeRefCounted<base::RefCountedString>());
return;
}
printing_rfh_ = rfh;
callback_ = std::move(callback);
print_params_ = GetPrintParamsFromSettings(settings);
page_ranges_text_ = settings.page_ranges;
ignore_invalid_page_ranges_ = settings.ignore_invalid_page_ranges;
rfh->Send(new PrintMsg_PrintPages(rfh->GetRoutingID()));
}
std::unique_ptr<PrintMsg_PrintPages_Params>
HeadlessPrintManager::GetPrintParamsFromSettings(
const HeadlessPrintSettings& settings) {
printing::PrintSettings print_settings;
print_settings.set_dpi(printing::kPointsPerInch);
print_settings.set_should_print_backgrounds(
settings.should_print_backgrounds);
print_settings.set_scale_factor(settings.scale);
print_settings.SetOrientation(settings.landscape);
print_settings.set_display_header_footer(settings.display_header_footer);
if (print_settings.display_header_footer()) {
url::Replacements<char> url_sanitizer;
url_sanitizer.ClearUsername();
url_sanitizer.ClearPassword();
std::string url = printing_rfh_->GetLastCommittedURL()
.ReplaceComponents(url_sanitizer)
.spec();
print_settings.set_url(base::UTF8ToUTF16(url));
}
print_settings.set_margin_type(printing::CUSTOM_MARGINS);
print_settings.SetCustomMargins(settings.margins_in_points);
gfx::Rect printable_area_device_units(settings.paper_size_in_points);
print_settings.SetPrinterPrintableArea(settings.paper_size_in_points,
printable_area_device_units, true);
auto print_params = std::make_unique<PrintMsg_PrintPages_Params>();
printing::RenderParamsFromPrintSettings(print_settings,
&print_params->params);
print_params->params.document_cookie = printing::PrintSettings::NewCookie();
print_params->params.header_template =
base::UTF8ToUTF16(settings.header_template);
print_params->params.footer_template =
base::UTF8ToUTF16(settings.footer_template);
print_params->params.prefer_css_page_size = settings.prefer_css_page_size;
return print_params;
}
bool HeadlessPrintManager::OnMessageReceived(
const IPC::Message& message,
content::RenderFrameHost* render_frame_host) {
if (!printing_rfh_ &&
(message.type() == PrintHostMsg_GetDefaultPrintSettings::ID ||
message.type() == PrintHostMsg_ScriptedPrint::ID)) {
std::string type;
switch (message.type()) {
case PrintHostMsg_GetDefaultPrintSettings::ID:
type = "GetDefaultPrintSettings";
break;
case PrintHostMsg_ScriptedPrint::ID:
type = "ScriptedPrint";
break;
default:
type = "Unknown";
break;
}
DLOG(ERROR)
<< "Unexpected message received before GetPDFContents is called: "
<< type;
// TODO: consider propagating the error back to the caller, rather than
// effectively dropping the request.
render_frame_host->Send(IPC::SyncMessage::GenerateReply(&message));
return true;
}
FrameDispatchHelper helper = {this, render_frame_host};
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(HeadlessPrintManager, message)
IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError,
OnShowInvalidPrinterSettingsError)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintDocument, OnDidPrintDocument)
IPC_MESSAGE_FORWARD_DELAY_REPLY(
PrintHostMsg_GetDefaultPrintSettings, &helper,
FrameDispatchHelper::OnGetDefaultPrintSettings)
IPC_MESSAGE_FORWARD_DELAY_REPLY(PrintHostMsg_ScriptedPrint, &helper,
FrameDispatchHelper::OnScriptedPrint)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled || PrintManager::OnMessageReceived(message, render_frame_host);
}
void HeadlessPrintManager::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg,
print_params_->params);
printing_rfh_->Send(reply_msg);
}
void HeadlessPrintManager::OnScriptedPrint(
const PrintHostMsg_ScriptedPrint_Params& params,
IPC::Message* reply_msg) {
PageRangeStatus status =
PageRangeTextToPages(page_ranges_text_, ignore_invalid_page_ranges_,
params.expected_pages_count, &print_params_->pages);
switch (status) {
case SYNTAX_ERROR:
printing_rfh_->Send(reply_msg);
ReleaseJob(PAGE_RANGE_SYNTAX_ERROR);
return;
case LIMIT_ERROR:
printing_rfh_->Send(reply_msg);
ReleaseJob(PAGE_COUNT_EXCEEDED);
return;
case PRINT_NO_ERROR:
PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, *print_params_);
printing_rfh_->Send(reply_msg);
return;
default:
NOTREACHED();
return;
}
}
void HeadlessPrintManager::OnShowInvalidPrinterSettingsError() {
ReleaseJob(INVALID_PRINTER_SETTINGS);
}
void HeadlessPrintManager::OnPrintingFailed(int cookie) {
ReleaseJob(PRINTING_FAILED);
}
void HeadlessPrintManager::OnDidPrintDocument(
const PrintHostMsg_DidPrintDocument_Params& params) {
auto& content = params.content;
if (!content.metafile_data_region.IsValid()) {
ReleaseJob(INVALID_MEMORY_HANDLE);
return;
}
base::ReadOnlySharedMemoryMapping map = content.metafile_data_region.Map();
if (!map.IsValid()) {
ReleaseJob(METAFILE_MAP_ERROR);
return;
}
data_ = std::string(static_cast<const char*>(map.memory()), map.size());
ReleaseJob(PRINT_SUCCESS);
}
void HeadlessPrintManager::Reset() {
printing_rfh_ = nullptr;
callback_.Reset();
print_params_.reset();
page_ranges_text_.clear();
ignore_invalid_page_ranges_ = false;
data_.clear();
}
void HeadlessPrintManager::ReleaseJob(PrintResult result) {
if (!callback_) {
DLOG(ERROR) << "ReleaseJob is called when callback_ is null. Check whether "
"ReleaseJob is called more than once.";
return;
}
if (result == PRINT_SUCCESS) {
std::move(callback_).Run(result,
base::RefCountedString::TakeString(&data_));
} else {
std::move(callback_).Run(result,
base::MakeRefCounted<base::RefCountedString>());
}
printing_rfh_->Send(new PrintMsg_PrintingDone(printing_rfh_->GetRoutingID(),
result == PRINT_SUCCESS));
Reset();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(HeadlessPrintManager)
} // namespace headless