// Copyright 2015 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/extension_printer_handler.h" #include #include #include "base/bind.h" #include "base/callback.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/memory/ref_counted_memory.h" #include "base/strings/string_split.h" #include "base/task_runner_util.h" #include "chrome/browser/printing/pwg_raster_converter.h" #include "components/cloud_devices/common/cloud_device_description.h" #include "components/cloud_devices/common/printer_description.h" #include "device/core/device_client.h" #include "device/usb/usb_device.h" #include "device/usb/usb_service.h" #include "extensions/browser/api/device_permissions_manager.h" #include "extensions/browser/api/printer_provider/printer_provider_api.h" #include "extensions/browser/api/printer_provider/printer_provider_api_factory.h" #include "extensions/browser/api/printer_provider/printer_provider_print_job.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/permissions/usb_device_permission.h" #include "extensions/common/permissions/usb_device_permission_data.h" #include "extensions/common/value_builder.h" #include "printing/pdf_render_settings.h" #include "printing/pwg_raster_settings.h" using device::UsbDevice; using extensions::DevicePermissionsManager; using extensions::DictionaryBuilder; using extensions::Extension; using extensions::ExtensionRegistry; using extensions::ListBuilder; using extensions::UsbPrinterManifestData; using printing::PWGRasterConverter; namespace { const char kContentTypePdf[] = "application/pdf"; const char kContentTypePWGRaster[] = "image/pwg-raster"; const char kContentTypeAll[] = "*/*"; const char kInvalidDataPrintError[] = "INVALID_DATA"; const char kInvalidTicketPrintError[] = "INVALID_TICKET"; const char kProvisionalUsbLabel[] = "provisional-usb"; // Updates |job| with raster file path, size and last modification time. // Returns the updated print job. scoped_ptr UpdateJobFileInfoOnWorkerThread( const base::FilePath& raster_path, scoped_ptr job) { if (base::GetFileInfo(raster_path, &job->file_info)) job->document_path = raster_path; return job; } // Callback to PWG raster conversion. // Posts a task to update print job with info about file containing converted // PWG raster data. The task is posted to |slow_task_runner|. void UpdateJobFileInfo( scoped_ptr job, const scoped_refptr& slow_task_runner, const ExtensionPrinterHandler::PrintJobCallback& callback, bool success, const base::FilePath& pwg_file_path) { if (!success) { callback.Run(std::move(job)); return; } base::PostTaskAndReplyWithResult( slow_task_runner.get(), FROM_HERE, base::Bind(&UpdateJobFileInfoOnWorkerThread, pwg_file_path, base::Passed(&job)), callback); } bool HasUsbPrinterProviderPermissions(const Extension* extension) { return extension->permissions_data() && extension->permissions_data()->HasAPIPermission( extensions::APIPermission::kPrinterProvider) && extension->permissions_data()->HasAPIPermission( extensions::APIPermission::kUsb); } std::string GenerateProvisionalUsbPrinterId(const Extension* extension, const UsbDevice* device) { return base::StringPrintf("%s:%s:%s", kProvisionalUsbLabel, extension->id().c_str(), device->guid().c_str()); } bool ParseProvisionalUsbPrinterId(const std::string& printer_id, std::string* extension_id, std::string* device_guid) { std::vector components = base::SplitString( printer_id, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); if (components.size() != 3) return false; if (components[0] != kProvisionalUsbLabel) return false; *extension_id = components[1]; *device_guid = components[2]; return true; } } // namespace ExtensionPrinterHandler::ExtensionPrinterHandler( content::BrowserContext* browser_context, const scoped_refptr& slow_task_runner) : browser_context_(browser_context), slow_task_runner_(slow_task_runner), weak_ptr_factory_(this) { } ExtensionPrinterHandler::~ExtensionPrinterHandler() { } void ExtensionPrinterHandler::Reset() { // TODO(tbarzic): Keep track of pending request ids issued by |this| and // cancel them from here. pending_enumeration_count_ = 0; weak_ptr_factory_.InvalidateWeakPtrs(); } void ExtensionPrinterHandler::StartGetPrinters( const PrinterHandler::GetPrintersCallback& callback) { // Assume that there can only be one printer enumeration occuring at once. DCHECK_EQ(pending_enumeration_count_, 0); pending_enumeration_count_ = 1; bool extension_supports_usb_printers = false; ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); for (const auto& extension : registry->enabled_extensions()) { if (UsbPrinterManifestData::Get(extension.get()) && HasUsbPrinterProviderPermissions(extension.get())) { extension_supports_usb_printers = true; break; } } if (extension_supports_usb_printers) { device::UsbService* service = device::DeviceClient::Get()->GetUsbService(); pending_enumeration_count_++; service->GetDevices( base::Bind(&ExtensionPrinterHandler::OnUsbDevicesEnumerated, weak_ptr_factory_.GetWeakPtr(), callback)); } extensions::PrinterProviderAPIFactory::GetInstance() ->GetForBrowserContext(browser_context_) ->DispatchGetPrintersRequested( base::Bind(&ExtensionPrinterHandler::WrapGetPrintersCallback, weak_ptr_factory_.GetWeakPtr(), callback)); } void ExtensionPrinterHandler::StartGetCapability( const std::string& destination_id, const PrinterHandler::GetCapabilityCallback& callback) { extensions::PrinterProviderAPIFactory::GetInstance() ->GetForBrowserContext(browser_context_) ->DispatchGetCapabilityRequested( destination_id, base::Bind(&ExtensionPrinterHandler::WrapGetCapabilityCallback, weak_ptr_factory_.GetWeakPtr(), callback, destination_id)); } void ExtensionPrinterHandler::StartPrint( const std::string& destination_id, const std::string& capability, const base::string16& job_title, const std::string& ticket_json, const gfx::Size& page_size, const scoped_refptr& print_data, const PrinterHandler::PrintCallback& callback) { scoped_ptr print_job( new extensions::PrinterProviderPrintJob()); print_job->printer_id = destination_id; print_job->job_title = job_title; print_job->ticket_json = ticket_json; cloud_devices::CloudDeviceDescription printer_description; printer_description.InitFromString(capability); cloud_devices::printer::ContentTypesCapability content_types; content_types.LoadFrom(printer_description); const bool kUsePdf = content_types.Contains(kContentTypePdf) || content_types.Contains(kContentTypeAll); if (kUsePdf) { // TODO(tbarzic): Consider writing larger PDF to disk and provide the data // the same way as it's done with PWG raster. print_job->content_type = kContentTypePdf; print_job->document_bytes = print_data; DispatchPrintJob(callback, std::move(print_job)); return; } cloud_devices::CloudDeviceDescription ticket; if (!ticket.InitFromString(ticket_json)) { WrapPrintCallback(callback, false, kInvalidTicketPrintError); return; } print_job->content_type = kContentTypePWGRaster; ConvertToPWGRaster(print_data, printer_description, ticket, page_size, std::move(print_job), base::Bind(&ExtensionPrinterHandler::DispatchPrintJob, weak_ptr_factory_.GetWeakPtr(), callback)); } void ExtensionPrinterHandler::StartGrantPrinterAccess( const std::string& printer_id, const PrinterHandler::GetPrinterInfoCallback& callback) { std::string extension_id; std::string device_guid; if (!ParseProvisionalUsbPrinterId(printer_id, &extension_id, &device_guid)) { callback.Run(base::DictionaryValue()); return; } device::UsbService* service = device::DeviceClient::Get()->GetUsbService(); scoped_refptr device = service->GetDevice(device_guid); if (!device) { callback.Run(base::DictionaryValue()); return; } DevicePermissionsManager* permissions_manager = DevicePermissionsManager::Get(browser_context_); permissions_manager->AllowUsbDevice(extension_id, device); extensions::PrinterProviderAPIFactory::GetInstance() ->GetForBrowserContext(browser_context_) ->DispatchGetUsbPrinterInfoRequested( extension_id, device, base::Bind(&ExtensionPrinterHandler::WrapGetPrinterInfoCallback, weak_ptr_factory_.GetWeakPtr(), callback)); } void ExtensionPrinterHandler::SetPWGRasterConverterForTesting( scoped_ptr pwg_raster_converter) { pwg_raster_converter_ = std::move(pwg_raster_converter); } void ExtensionPrinterHandler::ConvertToPWGRaster( const scoped_refptr& data, const cloud_devices::CloudDeviceDescription& printer_description, const cloud_devices::CloudDeviceDescription& ticket, const gfx::Size& page_size, scoped_ptr job, const ExtensionPrinterHandler::PrintJobCallback& callback) { if (!pwg_raster_converter_) { pwg_raster_converter_ = PWGRasterConverter::CreateDefault(); } pwg_raster_converter_->Start( data.get(), PWGRasterConverter::GetConversionSettings(printer_description, page_size), PWGRasterConverter::GetBitmapSettings(printer_description, ticket), base::Bind(&UpdateJobFileInfo, base::Passed(&job), slow_task_runner_, callback)); } void ExtensionPrinterHandler::DispatchPrintJob( const PrinterHandler::PrintCallback& callback, scoped_ptr print_job) { if (print_job->document_path.empty() && !print_job->document_bytes) { WrapPrintCallback(callback, false, kInvalidDataPrintError); return; } extensions::PrinterProviderAPIFactory::GetInstance() ->GetForBrowserContext(browser_context_) ->DispatchPrintRequested( *print_job, base::Bind(&ExtensionPrinterHandler::WrapPrintCallback, weak_ptr_factory_.GetWeakPtr(), callback)); } void ExtensionPrinterHandler::WrapGetPrintersCallback( const PrinterHandler::GetPrintersCallback& callback, const base::ListValue& printers, bool done) { DCHECK_GT(pending_enumeration_count_, 0); if (done) pending_enumeration_count_--; callback.Run(printers, pending_enumeration_count_ == 0); } void ExtensionPrinterHandler::WrapGetCapabilityCallback( const PrinterHandler::GetCapabilityCallback& callback, const std::string& destination_id, const base::DictionaryValue& capability) { callback.Run(destination_id, capability); } void ExtensionPrinterHandler::WrapPrintCallback( const PrinterHandler::PrintCallback& callback, bool success, const std::string& status) { callback.Run(success, status); } void ExtensionPrinterHandler::WrapGetPrinterInfoCallback( const PrinterHandler::GetPrinterInfoCallback& callback, const base::DictionaryValue& printer_info) { callback.Run(printer_info); } void ExtensionPrinterHandler::OnUsbDevicesEnumerated( const PrinterHandler::GetPrintersCallback& callback, const std::vector>& devices) { ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); DevicePermissionsManager* permissions_manager = DevicePermissionsManager::Get(browser_context_); ListBuilder printer_list; for (const auto& extension : registry->enabled_extensions()) { const UsbPrinterManifestData* manifest_data = UsbPrinterManifestData::Get(extension.get()); if (!manifest_data || !HasUsbPrinterProviderPermissions(extension.get())) continue; const extensions::DevicePermissions* device_permissions = permissions_manager->GetForExtension(extension->id()); for (const auto& device : devices) { if (manifest_data->SupportsDevice(device)) { extensions::UsbDevicePermission::CheckParam param( device->vendor_id(), device->product_id(), extensions::UsbDevicePermissionData::UNSPECIFIED_INTERFACE); if (device_permissions->FindUsbDeviceEntry(device) || extension->permissions_data()->CheckAPIPermissionWithParam( extensions::APIPermission::kUsbDevice, ¶m)) { // Skip devices the extension already has permission to access. continue; } printer_list.Append( DictionaryBuilder() .Set("id", GenerateProvisionalUsbPrinterId(extension.get(), device.get())) .Set("name", DevicePermissionsManager::GetPermissionMessage( device->vendor_id(), device->product_id(), device->manufacturer_string(), device->product_string(), base::string16(), false)) .Set("extensionId", extension->id()) .Set("extensionName", extension->name()) .Set("provisional", true) .Build()); } } } DCHECK_GT(pending_enumeration_count_, 0); pending_enumeration_count_--; callback.Run(*printer_list.Build().get(), pending_enumeration_count_ == 0); }