blob: 3bc2ca097723bf52da90dfebe15645126d9a7c2c [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/print_preview_handler.h"
#include <string>
#include "base/base64.h"
#if !defined(OS_CHROMEOS)
#include "base/command_line.h"
#endif
#include "base/i18n/file_util_icu.h"
#include "base/json/json_reader.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/printing/background_printing_manager.h"
#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
#include "chrome/browser/printing/printer_manager_dialog.h"
#include "chrome/browser/printing/print_preview_tab_controller.h"
#include "chrome/browser/sessions/restore_tab_helper.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/webui/cloud_print_signin_dialog.h"
#include "chrome/browser/ui/webui/print_preview_ui.h"
#include "chrome/common/chrome_paths.h"
#if !defined(OS_CHROMEOS)
#include "chrome/common/chrome_switches.h"
#endif
#include "chrome/common/print_messages.h"
#include "content/browser/browser_thread.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "printing/backend/print_backend.h"
#include "printing/metafile.h"
#include "printing/metafile_impl.h"
#include "printing/page_range.h"
#include "printing/print_job_constants.h"
#if defined(USE_CUPS)
#include <cups/cups.h>
#include <cups/ppd.h>
#include "base/file_util.h"
#endif
namespace {
const char kDisableColorOption[] = "disableColorOption";
const char kSetColorAsDefault[] = "setColorAsDefault";
const char kSetDuplexAsDefault[] = "setDuplexAsDefault";
#if defined(USE_CUPS)
const char kColorDevice[] = "ColorDevice";
const char kDuplex[] = "Duplex";
const char kDuplexNone[] = "None";
#elif defined(OS_WIN)
const char kPskColor[] = "psk:Color";
const char kPskDuplexFeature[] = "psk:JobDuplexAllDocumentsContiguously";
const char kPskTwoSided[] = "psk:TwoSided";
#endif
enum UserActionBuckets {
PRINT_TO_PRINTER,
PRINT_TO_PDF,
CANCEL,
FALLBACK_TO_ADVANCED_SETTINGS_DIALOG,
PREVIEW_FAILED,
PREVIEW_STARTED,
INITIATOR_TAB_CRASHED,
USERACTION_BUCKET_BOUNDARY
};
enum PrintSettingsBuckets {
LANDSCAPE,
PORTRAIT,
COLOR,
BLACK_AND_WHITE,
COLLATE,
SIMPLEX,
DUPLEX,
PRINT_SETTINGS_BUCKET_BOUNDARY
};
void ReportUserActionHistogram(enum UserActionBuckets event) {
UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event,
USERACTION_BUCKET_BOUNDARY);
}
void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) {
UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting,
PRINT_SETTINGS_BUCKET_BOUNDARY);
}
// Get the print job settings dictionary from |args|. The caller takes
// ownership of the returned DictionaryValue. Returns NULL on failure.
DictionaryValue* GetSettingsDictionary(const ListValue* args) {
std::string json_str;
if (!args->GetString(0, &json_str)) {
NOTREACHED() << "Could not read JSON argument";
return NULL;
}
if (json_str.empty()) {
NOTREACHED() << "Empty print job settings";
return NULL;
}
scoped_ptr<DictionaryValue> settings(static_cast<DictionaryValue*>(
base::JSONReader::Read(json_str, false)));
if (!settings.get() || !settings->IsType(Value::TYPE_DICTIONARY)) {
NOTREACHED() << "Print job settings must be a dictionary.";
return NULL;
}
if (settings->empty()) {
NOTREACHED() << "Print job settings dictionary is empty";
return NULL;
}
return settings.release();
}
int GetPageCountFromSettingsDictionary(const DictionaryValue& settings) {
int count = 0;
ListValue* page_range_array;
if (settings.GetList(printing::kSettingPageRange, &page_range_array)) {
for (size_t index = 0; index < page_range_array->GetSize(); ++index) {
DictionaryValue* dict;
if (!page_range_array->GetDictionary(index, &dict))
continue;
printing::PageRange range;
if (!dict->GetInteger(printing::kSettingPageRangeFrom, &range.from) ||
!dict->GetInteger(printing::kSettingPageRangeTo, &range.to)) {
continue;
}
count += (range.to - range.from) + 1;
}
}
return count;
}
// Track the popularity of print settings and report the stats.
void ReportPrintSettingsStats(const DictionaryValue& settings) {
bool landscape;
if (settings.GetBoolean(printing::kSettingLandscape, &landscape))
ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT);
bool collate;
if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate)
ReportPrintSettingHistogram(COLLATE);
int duplex_mode;
if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode))
ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX);
bool is_color;
if (settings.GetBoolean(printing::kSettingColor, &is_color))
ReportPrintSettingHistogram(is_color ? COLOR : BLACK_AND_WHITE);
}
printing::BackgroundPrintingManager* GetBackgroundPrintingManager() {
return g_browser_process->background_printing_manager();
}
} // namespace
class PrintSystemTaskProxy
: public base::RefCountedThreadSafe<PrintSystemTaskProxy,
BrowserThread::DeleteOnUIThread> {
public:
PrintSystemTaskProxy(const base::WeakPtr<PrintPreviewHandler>& handler,
printing::PrintBackend* print_backend,
bool has_logged_printers_count)
: handler_(handler),
print_backend_(print_backend),
has_logged_printers_count_(has_logged_printers_count) {
}
void GetDefaultPrinter() {
VLOG(1) << "Get default printer start";
StringValue* default_printer = NULL;
if (PrintPreviewHandler::last_used_printer_name_ == NULL) {
default_printer = new StringValue(
print_backend_->GetDefaultPrinterName());
} else {
default_printer = new StringValue(
*PrintPreviewHandler::last_used_printer_name_);
}
std::string default_printer_string;
default_printer->GetAsString(&default_printer_string);
VLOG(1) << "Get default printer finished, found: "
<< default_printer_string;
StringValue* cloud_print_data = NULL;
if (PrintPreviewHandler::last_used_printer_cloud_print_data_ != NULL) {
cloud_print_data = new StringValue(
*PrintPreviewHandler::last_used_printer_cloud_print_data_);
} else {
cloud_print_data = new StringValue("");
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this,
&PrintSystemTaskProxy::SendDefaultPrinter,
default_printer,
cloud_print_data));
}
void SendDefaultPrinter(const StringValue* default_printer,
const StringValue* cloud_print_data) {
if (handler_)
handler_->SendDefaultPrinter(*default_printer, *cloud_print_data);
delete default_printer;
}
void EnumeratePrinters() {
VLOG(1) << "Enumerate printers start";
ListValue* printers = new ListValue;
printing::PrinterList printer_list;
print_backend_->EnumeratePrinters(&printer_list);
if (!has_logged_printers_count_) {
// Record the total number of printers.
UMA_HISTOGRAM_COUNTS("PrintPreview.NumberOfPrinters",
printer_list.size());
}
int i = 0;
for (printing::PrinterList::iterator iter = printer_list.begin();
iter != printer_list.end(); ++iter, ++i) {
DictionaryValue* printer_info = new DictionaryValue;
std::string printerName;
#if defined(OS_MACOSX)
// On Mac, |iter->printer_description| specifies the printer name and
// |iter->printer_name| specifies the device name / printer queue name.
printerName = iter->printer_description;
#else
printerName = iter->printer_name;
#endif
printer_info->SetString(printing::kSettingPrinterName, printerName);
printer_info->SetString(printing::kSettingDeviceName, iter->printer_name);
printers->Append(printer_info);
}
VLOG(1) << "Enumerate printers finished, found " << i << " printers";
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this,
&PrintSystemTaskProxy::SetupPrinterList,
printers));
}
void SetupPrinterList(ListValue* printers) {
if (handler_) {
handler_->SetupPrinterList(*printers);
}
delete printers;
}
void GetPrinterCapabilities(const std::string& printer_name) {
VLOG(1) << "Get printer capabilities start for " << printer_name;
printing::PrinterCapsAndDefaults printer_info;
bool supports_color = true;
bool set_duplex_as_default = false;
if (!print_backend_->GetPrinterCapsAndDefaults(printer_name,
&printer_info)) {
return;
}
#if defined(USE_CUPS)
FilePath ppd_file_path;
if (!file_util::CreateTemporaryFile(&ppd_file_path))
return;
int data_size = printer_info.printer_capabilities.length();
if (data_size != file_util::WriteFile(
ppd_file_path,
printer_info.printer_capabilities.data(),
data_size)) {
file_util::Delete(ppd_file_path, false);
return;
}
ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str());
if (ppd) {
ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL);
if (attr && attr->value)
supports_color = ppd->color_device;
ppd_choice_t* ch = ppdFindMarkedChoice(ppd, kDuplex);
if (ch == NULL) {
ppd_option_t* option = ppdFindOption(ppd, kDuplex);
if (option != NULL)
ch = ppdFindChoice(option, option->defchoice);
}
if (ch != NULL && strcmp(ch->choice, kDuplexNone) != 0)
set_duplex_as_default = true;
ppdClose(ppd);
}
file_util::Delete(ppd_file_path, false);
#elif defined(OS_WIN)
// According to XPS 1.0 spec, only color printers have psk:Color.
// Therefore we don't need to parse the whole XML file, we just need to
// search the string. The spec can be found at:
// http://msdn.microsoft.com/en-us/windows/hardware/gg463431.
supports_color = (printer_info.printer_capabilities.find(kPskColor) !=
std::string::npos);
set_duplex_as_default =
(printer_info.printer_defaults.find(kPskDuplexFeature) !=
std::string::npos) &&
(printer_info.printer_defaults.find(kPskTwoSided) !=
std::string::npos);
#else
NOTIMPLEMENTED();
#endif
DictionaryValue settings_info;
settings_info.SetBoolean(kDisableColorOption, !supports_color);
if (!supports_color) {
settings_info.SetBoolean(kSetColorAsDefault, false);
} else {
settings_info.SetBoolean(kSetColorAsDefault,
PrintPreviewHandler::last_used_color_setting_);
}
settings_info.SetBoolean(kSetDuplexAsDefault, set_duplex_as_default);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this,
&PrintSystemTaskProxy::SendPrinterCapabilities,
settings_info.DeepCopy()));
}
void SendPrinterCapabilities(DictionaryValue* settings_info) {
if (handler_)
handler_->SendPrinterCapabilities(*settings_info);
delete settings_info;
}
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
friend class DeleteTask<PrintSystemTaskProxy>;
~PrintSystemTaskProxy() {}
base::WeakPtr<PrintPreviewHandler> handler_;
scoped_refptr<printing::PrintBackend> print_backend_;
bool has_logged_printers_count_;
DISALLOW_COPY_AND_ASSIGN(PrintSystemTaskProxy);
};
// A Task implementation that stores a PDF file on disk.
class PrintToPdfTask : public Task {
public:
// Takes ownership of |metafile|.
PrintToPdfTask(printing::Metafile* metafile, const FilePath& path)
: metafile_(metafile), path_(path) {
DCHECK(metafile);
}
~PrintToPdfTask() {
// This has to get deleted on the same thread it was created.
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
new DeleteTask<printing::Metafile>(metafile_.release()));
}
// Task implementation
virtual void Run() {
metafile_->SaveTo(path_);
}
private:
// The metafile holding the PDF data.
scoped_ptr<printing::Metafile> metafile_;
// The absolute path where the file will be saved.
FilePath path_;
};
// static
FilePath* PrintPreviewHandler::last_saved_path_ = NULL;
std::string* PrintPreviewHandler::last_used_printer_cloud_print_data_ = NULL;
std::string* PrintPreviewHandler::last_used_printer_name_ = NULL;
bool PrintPreviewHandler::last_used_color_setting_ = false;
PrintPreviewHandler::PrintPreviewHandler()
: print_backend_(printing::PrintBackend::CreateInstance(NULL)),
regenerate_preview_request_count_(0),
manage_printers_dialog_request_count_(0),
reported_failed_preview_(false),
has_logged_printers_count_(false) {
ReportUserActionHistogram(PREVIEW_STARTED);
}
PrintPreviewHandler::~PrintPreviewHandler() {
if (select_file_dialog_.get())
select_file_dialog_->ListenerDestroyed();
}
void PrintPreviewHandler::RegisterMessages() {
web_ui_->RegisterMessageCallback("getDefaultPrinter",
NewCallback(this, &PrintPreviewHandler::HandleGetDefaultPrinter));
web_ui_->RegisterMessageCallback("getPrinters",
NewCallback(this, &PrintPreviewHandler::HandleGetPrinters));
web_ui_->RegisterMessageCallback("getPreview",
NewCallback(this, &PrintPreviewHandler::HandleGetPreview));
web_ui_->RegisterMessageCallback("print",
NewCallback(this, &PrintPreviewHandler::HandlePrint));
web_ui_->RegisterMessageCallback("getPrinterCapabilities",
NewCallback(this, &PrintPreviewHandler::HandleGetPrinterCapabilities));
web_ui_->RegisterMessageCallback("showSystemDialog",
NewCallback(this, &PrintPreviewHandler::HandleShowSystemDialog));
web_ui_->RegisterMessageCallback("morePrinters",
NewCallback(this, &PrintPreviewHandler::HandleShowSystemDialog));
web_ui_->RegisterMessageCallback("signIn",
NewCallback(this, &PrintPreviewHandler::HandleSignin));
web_ui_->RegisterMessageCallback("addCloudPrinter",
NewCallback(this, &PrintPreviewHandler::HandleManageCloudPrint));
web_ui_->RegisterMessageCallback("manageCloudPrinters",
NewCallback(this, &PrintPreviewHandler::HandleManageCloudPrint));
web_ui_->RegisterMessageCallback("manageLocalPrinters",
NewCallback(this, &PrintPreviewHandler::HandleManagePrinters));
web_ui_->RegisterMessageCallback("reloadCrashedInitiatorTab",
NewCallback(this, &PrintPreviewHandler::HandleReloadCrashedInitiatorTab));
web_ui_->RegisterMessageCallback("closePrintPreviewTab",
NewCallback(this, &PrintPreviewHandler::HandleClosePreviewTab));
web_ui_->RegisterMessageCallback("hidePreview",
NewCallback(this, &PrintPreviewHandler::HandleHidePreview));
web_ui_->RegisterMessageCallback("cancelPendingPrintRequest",
NewCallback(this, &PrintPreviewHandler::HandleCancelPendingPrintRequest));
web_ui_->RegisterMessageCallback("saveLastPrinter",
NewCallback(this, &PrintPreviewHandler::HandleSaveLastPrinter));
}
TabContents* PrintPreviewHandler::preview_tab() {
return web_ui_->tab_contents();
}
void PrintPreviewHandler::HandleGetDefaultPrinter(const ListValue*) {
scoped_refptr<PrintSystemTaskProxy> task =
new PrintSystemTaskProxy(AsWeakPtr(),
print_backend_.get(),
has_logged_printers_count_);
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(task.get(),
&PrintSystemTaskProxy::GetDefaultPrinter));
}
void PrintPreviewHandler::HandleGetPrinters(const ListValue*) {
scoped_refptr<PrintSystemTaskProxy> task =
new PrintSystemTaskProxy(AsWeakPtr(),
print_backend_.get(),
has_logged_printers_count_);
has_logged_printers_count_ = true;
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(task.get(),
&PrintSystemTaskProxy::EnumeratePrinters));
}
void PrintPreviewHandler::HandleGetPreview(const ListValue* args) {
scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args));
if (!settings.get())
return;
int request_id = -1;
if (!settings->GetInteger(printing::kPreviewRequestID, &request_id))
return;
PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui_);
print_preview_ui->OnPrintPreviewRequest(request_id);
// Add an additional key in order to identify |print_preview_ui| later on
// when calling PrintPreviewUI::GetCurrentPrintPreviewStatus() on the IO
// thread.
settings->SetString(printing::kPreviewUIAddr,
print_preview_ui->GetPrintPreviewUIAddress());
// Increment request count.
++regenerate_preview_request_count_;
TabContents* initiator_tab = GetInitiatorTab();
if (!initiator_tab) {
if (!reported_failed_preview_) {
ReportUserActionHistogram(PREVIEW_FAILED);
reported_failed_preview_ = true;
}
print_preview_ui->OnPrintPreviewFailed();
return;
}
// Retrieve the page title and url and send it to the renderer process if
// headers and footers are to be displayed.
bool display_header_footer = false;
if (!settings->GetBoolean(printing::kSettingHeaderFooterEnabled,
&display_header_footer)) {
NOTREACHED();
}
if (display_header_footer) {
settings->SetString(printing::kSettingHeaderFooterTitle,
initiator_tab->GetTitle());
std::string url;
NavigationEntry* entry = initiator_tab->controller().GetActiveEntry();
if (entry)
url = entry->virtual_url().spec();
settings->SetString(printing::kSettingHeaderFooterURL, url);
}
VLOG(1) << "Print preview request start";
RenderViewHost* rvh = initiator_tab->render_view_host();
rvh->Send(new PrintMsg_PrintPreview(rvh->routing_id(), *settings));
}
void PrintPreviewHandler::HandlePrint(const ListValue* args) {
ReportStats();
// Record the number of times the user requests to regenerate preview data
// before printing.
UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint",
regenerate_preview_request_count_);
TabContents* initiator_tab = GetInitiatorTab();
if (initiator_tab) {
RenderViewHost* rvh = initiator_tab->render_view_host();
rvh->Send(new PrintMsg_ResetScriptedPrintCount(rvh->routing_id()));
}
scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args));
if (!settings.get())
return;
// Storing last used color setting.
settings->GetBoolean("color", &last_used_color_setting_);
bool print_to_pdf = false;
settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf);
settings->SetBoolean(printing::kSettingHeaderFooterEnabled, false);
TabContentsWrapper* preview_tab_wrapper =
TabContentsWrapper::GetCurrentWrapperForContents(preview_tab());
bool print_to_cloud = settings->HasKey(printing::kSettingCloudPrintId);
if (print_to_cloud) {
std::string print_ticket;
args->GetString(1, &print_ticket);
SendCloudPrintJob(*settings, print_ticket);
} else if (print_to_pdf) {
ReportUserActionHistogram(PRINT_TO_PDF);
UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF",
GetPageCountFromSettingsDictionary(*settings));
// Pre-populating select file dialog with print job title.
string16 print_job_title_utf16 =
preview_tab_wrapper->print_view_manager()->RenderSourceName();
#if defined(OS_WIN)
FilePath::StringType print_job_title(print_job_title_utf16);
#elif defined(OS_POSIX)
FilePath::StringType print_job_title = UTF16ToUTF8(print_job_title_utf16);
#endif
file_util::ReplaceIllegalCharactersInPath(&print_job_title, '_');
FilePath default_filename(print_job_title);
default_filename =
default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf"));
SelectFile(default_filename);
} else {
ClearInitiatorTabDetails();
ReportPrintSettingsStats(*settings);
ReportUserActionHistogram(PRINT_TO_PRINTER);
UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter",
GetPageCountFromSettingsDictionary(*settings));
HidePreviewTab();
// The PDF being printed contains only the pages that the user selected,
// so ignore the page range and print all pages.
settings->Remove(printing::kSettingPageRange, NULL);
RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host();
rvh->Send(new PrintMsg_PrintForPrintPreview(rvh->routing_id(), *settings));
}
}
void PrintPreviewHandler::HandleHidePreview(const ListValue*) {
HidePreviewTab();
}
void PrintPreviewHandler::HandleCancelPendingPrintRequest(const ListValue*) {
TabContentsWrapper* wrapper = NULL;
TabContents* initiator_tab = GetInitiatorTab();
if (initiator_tab) {
wrapper = TabContentsWrapper::GetCurrentWrapperForContents(initiator_tab);
ClearInitiatorTabDetails();
} else {
// Initiator tab does not exists. Get the wrapper contents of current tab.
Browser* browser = BrowserList::GetLastActive();
if (browser)
wrapper = browser->GetSelectedTabContentsWrapper();
}
if (wrapper)
wrapper->print_view_manager()->PreviewPrintingRequestCancelled();
delete TabContentsWrapper::GetCurrentWrapperForContents(preview_tab());
}
void PrintPreviewHandler::HandleSaveLastPrinter(const ListValue* args) {
std::string data_to_save;
if (args->GetString(0, &data_to_save) && !data_to_save.empty()) {
if (last_used_printer_name_ == NULL)
last_used_printer_name_ = new std::string();
*last_used_printer_name_ = data_to_save;
}
if (args->GetString(1, &data_to_save) && !data_to_save.empty()) {
if (last_used_printer_cloud_print_data_ == NULL)
last_used_printer_cloud_print_data_ = new std::string();
*last_used_printer_cloud_print_data_ = data_to_save;
}
}
void PrintPreviewHandler::HandleGetPrinterCapabilities(const ListValue* args) {
std::string printer_name;
bool ret = args->GetString(0, &printer_name);
if (!ret || printer_name.empty())
return;
scoped_refptr<PrintSystemTaskProxy> task =
new PrintSystemTaskProxy(AsWeakPtr(),
print_backend_.get(),
has_logged_printers_count_);
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(task.get(),
&PrintSystemTaskProxy::GetPrinterCapabilities,
printer_name));
}
void PrintPreviewHandler::HandleSignin(const ListValue*) {
cloud_print_signin_dialog::CreateCloudPrintSigninDialog(preview_tab());
}
void PrintPreviewHandler::HandleManageCloudPrint(const ListValue*) {
Browser* browser = BrowserList::GetLastActive();
browser->OpenURL(CloudPrintURL(browser->profile()).
GetCloudPrintServiceManageURL(),
GURL(),
NEW_FOREGROUND_TAB,
PageTransition::LINK);
}
void PrintPreviewHandler::HandleShowSystemDialog(const ListValue*) {
ReportStats();
ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG);
TabContents* initiator_tab = GetInitiatorTab();
if (!initiator_tab)
return;
TabContentsWrapper* wrapper =
TabContentsWrapper::GetCurrentWrapperForContents(initiator_tab);
printing::PrintViewManager* manager = wrapper->print_view_manager();
manager->set_observer(this);
manager->PrintForSystemDialogNow();
}
void PrintPreviewHandler::HandleManagePrinters(const ListValue*) {
++manage_printers_dialog_request_count_;
printing::PrinterManagerDialog::ShowPrinterManagerDialog();
}
void PrintPreviewHandler::HandleReloadCrashedInitiatorTab(const ListValue*) {
ReportStats();
ReportUserActionHistogram(INITIATOR_TAB_CRASHED);
TabContents* initiator_tab = GetInitiatorTab();
if (!initiator_tab)
return;
initiator_tab->OpenURL(
initiator_tab->GetURL(), GURL(), CURRENT_TAB, PageTransition::RELOAD);
ActivateInitiatorTabAndClosePreviewTab();
}
void PrintPreviewHandler::HandleClosePreviewTab(const ListValue*) {
ReportStats();
ReportUserActionHistogram(CANCEL);
// Record the number of times the user requests to regenerate preview data
// before cancelling.
UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforeCancel",
regenerate_preview_request_count_);
ActivateInitiatorTabAndClosePreviewTab();
}
void PrintPreviewHandler::ReportStats() {
UMA_HISTOGRAM_COUNTS("PrintPreview.ManagePrinters",
manage_printers_dialog_request_count_);
}
void PrintPreviewHandler::ActivateInitiatorTabAndClosePreviewTab() {
TabContents* initiator_tab = GetInitiatorTab();
if (initiator_tab)
static_cast<RenderViewHostDelegate*>(initiator_tab)->Activate();
ClosePrintPreviewTab();
}
void PrintPreviewHandler::SendPrinterCapabilities(
const DictionaryValue& settings_info) {
VLOG(1) << "Get printer capabilities finished";
web_ui_->CallJavascriptFunction("updateWithPrinterCapabilities",
settings_info);
}
void PrintPreviewHandler::SendDefaultPrinter(
const StringValue& default_printer,
const StringValue& cloud_print_data) {
web_ui_->CallJavascriptFunction("setDefaultPrinter",
default_printer,
cloud_print_data);
}
void PrintPreviewHandler::SetupPrinterList(const ListValue& printers) {
SendCloudPrintEnabled();
web_ui_->CallJavascriptFunction("setPrinters", printers,
*(Value::CreateBooleanValue(true)));
}
void PrintPreviewHandler::SendCloudPrintEnabled() {
#if defined(OS_CHROMEOS)
bool enable_cloud_print_integration = true;
#else
bool enable_cloud_print_integration =
CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableCloudPrint);
#endif
GURL gcp_url(CloudPrintURL(BrowserList::GetLastActive()->profile()).
GetCloudPrintServiceURL());
base::FundamentalValue enable(enable_cloud_print_integration);
base::StringValue gcp_url_value(gcp_url.spec());
web_ui_->CallJavascriptFunction("setUseCloudPrint", enable, gcp_url_value);
}
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->GetPrintPreviewDataForIndex(
printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data);
CHECK(data.get());
DCHECK_GT(data->size(), 0U);
TabContentsWrapper* wrapper =
TabContentsWrapper::GetCurrentWrapperForContents(preview_tab());
string16 print_job_title_utf16 =
wrapper->print_view_manager()->RenderSourceName();
std::string print_job_title = UTF16ToUTF8(print_job_title_utf16);
std::string printer_id;
settings.GetString("cloudPrintID", &printer_id);
// BASE64 encode the job data.
std::string raw_data(reinterpret_cast<const char*>(data->front()),
data->size());
std::string base64_data;
if (!base::Base64Encode(raw_data, &base64_data)) {
NOTREACHED() << "Base64 encoding PDF data.";
}
const char boundary[] = "----CloudPrintFormBoundaryjc9wuprokl8i";
const char prolog[] = "--%s\r\n"
"Content-Disposition: form-data; name=\"capabilities\"\r\n\r\n%s\r\n"
"--%s\r\n"
"Content-Disposition: form-data; name=\"contentType\"\r\n\r\ndataUrl\r\n"
"--%s\r\n"
"Content-Disposition: form-data; name=\"title\"\r\n\r\n%s\r\n"
"--%s\r\n"
"Content-Disposition: form-data; name=\"printerid\"\r\n\r\n%s\r\n"
"--%s\r\n"
"Content-Disposition: form-data; name=\"content\"\r\n\r\n"
"data:application/pdf;base64,%s\r\n"
"--%s\r\n";
// TODO([email protected]) This implies a large copy operation.
// Profile this and optimize if necessary.
std::string final_data;
base::SStringPrintf(&final_data,
prolog,
boundary,
print_ticket.c_str(),
boundary,
boundary,
print_job_title.c_str(),
boundary,
printer_id.c_str(),
boundary,
base64_data.c_str(),
boundary);
StringValue data_value(final_data);
web_ui_->CallJavascriptFunction("printToCloud",
data_value);
}
TabContents* PrintPreviewHandler::GetInitiatorTab() {
printing::PrintPreviewTabController* tab_controller =
printing::PrintPreviewTabController::GetInstance();
if (!tab_controller)
return NULL;
return tab_controller->GetInitiatorTab(preview_tab());
}
void PrintPreviewHandler::ClosePrintPreviewTab() {
TabContentsWrapper* tab =
TabContentsWrapper::GetCurrentWrapperForContents(preview_tab());
if (!tab)
return;
Browser* preview_tab_browser = BrowserList::FindBrowserWithID(
tab->restore_tab_helper()->window_id().id());
if (!preview_tab_browser)
return;
TabStripModel* tabstrip = preview_tab_browser->tabstrip_model();
// Keep print preview tab out of the recently closed tab list, because
// re-opening that page will just display a non-functional print preview page.
tabstrip->CloseTabContentsAt(tabstrip->GetIndexOfController(
&preview_tab()->controller()), TabStripModel::CLOSE_NONE);
}
void PrintPreviewHandler::OnPrintDialogShown() {
static_cast<RenderViewHostDelegate*>(GetInitiatorTab())->Activate();
ClosePrintPreviewTab();
}
void PrintPreviewHandler::SelectFile(const FilePath& default_filename) {
SelectFileDialog::FileTypeInfo file_type_info;
file_type_info.extensions.resize(1);
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
// Initializing last_saved_path_ if it is not already initialized.
if (!last_saved_path_) {
last_saved_path_ = new FilePath();
// Allowing IO operation temporarily. It is ok to do so here because
// the select file dialog performs IO anyway in order to display the
// folders and also it is modal.
base::ThreadRestrictions::ScopedAllowIO allow_io;
PathService::Get(chrome::DIR_USER_DOCUMENTS, last_saved_path_);
}
if (!select_file_dialog_.get())
select_file_dialog_ = SelectFileDialog::Create(this);
select_file_dialog_->SelectFile(
SelectFileDialog::SELECT_SAVEAS_FILE,
string16(),
last_saved_path_->Append(default_filename),
&file_type_info,
0,
FILE_PATH_LITERAL(""),
preview_tab(),
platform_util::GetTopLevel(preview_tab()->GetNativeView()),
NULL);
}
void PrintPreviewHandler::OnTabDestroyed() {
TabContents* initiator_tab = GetInitiatorTab();
if (!initiator_tab)
return;
TabContentsWrapper* wrapper =
TabContentsWrapper::GetCurrentWrapperForContents(initiator_tab);
wrapper->print_view_manager()->set_observer(NULL);
}
void PrintPreviewHandler::FileSelected(const FilePath& path,
int index, void* params) {
PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui_);
scoped_refptr<RefCountedBytes> data;
print_preview_ui->GetPrintPreviewDataForIndex(
printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data);
if (!data.get()) {
NOTREACHED();
return;
}
printing::PreviewMetafile* metafile = new printing::PreviewMetafile;
metafile->InitFromData(static_cast<const void*>(data->front()), data->size());
// Updating last_saved_path_ to the newly selected folder.
*last_saved_path_ = path.DirName();
PrintToPdfTask* task = new PrintToPdfTask(metafile, path);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task);
ActivateInitiatorTabAndClosePreviewTab();
}
void PrintPreviewHandler::FileSelectionCanceled(void* params) {
PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui_);
print_preview_ui->OnFileSelectionCancelled();
}
void PrintPreviewHandler::HidePreviewTab() {
TabContentsWrapper* preview_tab_wrapper =
TabContentsWrapper::GetCurrentWrapperForContents(preview_tab());
if (GetBackgroundPrintingManager()->HasTabContents(preview_tab_wrapper))
return;
GetBackgroundPrintingManager()->OwnTabContents(preview_tab_wrapper);
}
void PrintPreviewHandler::ClearInitiatorTabDetails() {
TabContents* initiator_tab = GetInitiatorTab();
if (!initiator_tab)
return;
// We no longer require the initiator tab details. Remove those details
// associated with the preview tab to allow the initiator tab to create
// another preview tab.
printing::PrintPreviewTabController* tab_controller =
printing::PrintPreviewTabController::GetInstance();
if (tab_controller)
tab_controller->EraseInitiatorTabInfo(preview_tab());
}