1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
10 #include "base/callback.h"
11 #include "base/files/file.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/location.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/ref_counted_memory.h"
17 #include "base/task_runner_util.h"
18 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
19 #include "components/cloud_devices/common/cloud_device_description.h"
20 #include "components/cloud_devices/common/printer_description.h"
21 #include "device/core/device_client.h"
22 #include "device/usb/usb_device.h"
23 #include "device/usb/usb_service.h"
24 #include "extensions/browser/api/device_permissions_manager.h"
25 #include "extensions/browser/api/printer_provider/printer_provider_api.h"
26 #include "extensions/browser/api/printer_provider/printer_provider_api_factory.h"
27 #include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
30 #include "extensions/common/permissions/permissions_data.h"
31 #include "extensions/common/permissions/usb_device_permission.h"
32 #include "extensions/common/permissions/usb_device_permission_data.h"
33 #include "extensions/common/value_builder.h"
34 #include "printing/pdf_render_settings.h"
35 #include "printing/pwg_raster_settings.h"
37 using device::UsbDevice
;
38 using extensions::DevicePermissionsManager
;
39 using extensions::DictionaryBuilder
;
40 using extensions::Extension
;
41 using extensions::ExtensionRegistry
;
42 using extensions::ListBuilder
;
43 using extensions::UsbPrinterManifestData
;
44 using local_discovery::PWGRasterConverter
;
48 const char kContentTypePdf
[] = "application/pdf";
49 const char kContentTypePWGRaster
[] = "image/pwg-raster";
50 const char kContentTypeAll
[] = "*/*";
52 const char kInvalidDataPrintError
[] = "INVALID_DATA";
53 const char kInvalidTicketPrintError
[] = "INVALID_TICKET";
55 // Updates |job| with raster file path, size and last modification time.
56 // Returns the updated print job.
57 scoped_ptr
<extensions::PrinterProviderPrintJob
> UpdateJobFileInfoOnWorkerThread(
58 const base::FilePath
& raster_path
,
59 scoped_ptr
<extensions::PrinterProviderPrintJob
> job
) {
60 if (base::GetFileInfo(raster_path
, &job
->file_info
))
61 job
->document_path
= raster_path
;
65 // Callback to PWG raster conversion.
66 // Posts a task to update print job with info about file containing converted
67 // PWG raster data. The task is posted to |slow_task_runner|.
68 void UpdateJobFileInfo(
69 scoped_ptr
<extensions::PrinterProviderPrintJob
> job
,
70 const scoped_refptr
<base::TaskRunner
>& slow_task_runner
,
71 const ExtensionPrinterHandler::PrintJobCallback
& callback
,
73 const base::FilePath
& pwg_file_path
) {
75 callback
.Run(job
.Pass());
79 base::PostTaskAndReplyWithResult(
80 slow_task_runner
.get(), FROM_HERE
,
81 base::Bind(&UpdateJobFileInfoOnWorkerThread
, pwg_file_path
,
86 bool HasUsbPrinterProviderPermissions(const Extension
* extension
) {
87 return extension
->permissions_data() &&
88 extension
->permissions_data()->HasAPIPermission(
89 extensions::APIPermission::kPrinterProvider
) &&
90 extension
->permissions_data()->HasAPIPermission(
91 extensions::APIPermission::kUsb
);
96 ExtensionPrinterHandler::ExtensionPrinterHandler(
97 content::BrowserContext
* browser_context
,
98 const scoped_refptr
<base::TaskRunner
>& slow_task_runner
)
99 : browser_context_(browser_context
),
100 slow_task_runner_(slow_task_runner
),
101 weak_ptr_factory_(this) {
104 ExtensionPrinterHandler::~ExtensionPrinterHandler() {
107 void ExtensionPrinterHandler::Reset() {
108 // TODO(tbarzic): Keep track of pending request ids issued by |this| and
109 // cancel them from here.
110 pending_enumeration_count_
= 0;
111 weak_ptr_factory_
.InvalidateWeakPtrs();
114 void ExtensionPrinterHandler::StartGetPrinters(
115 const PrinterHandler::GetPrintersCallback
& callback
) {
116 // Assume that there can only be one printer enumeration occuring at once.
117 DCHECK_EQ(pending_enumeration_count_
, 0);
118 pending_enumeration_count_
= 1;
120 bool extension_supports_usb_printers
= false;
121 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context_
);
122 for (const auto& extension
: registry
->enabled_extensions()) {
123 if (UsbPrinterManifestData::Get(extension
.get()) &&
124 HasUsbPrinterProviderPermissions(extension
.get())) {
125 extension_supports_usb_printers
= true;
130 if (extension_supports_usb_printers
) {
131 device::UsbService
* service
= device::DeviceClient::Get()->GetUsbService();
132 pending_enumeration_count_
++;
134 base::Bind(&ExtensionPrinterHandler::OnUsbDevicesEnumerated
,
135 weak_ptr_factory_
.GetWeakPtr(), callback
));
138 extensions::PrinterProviderAPIFactory::GetInstance()
139 ->GetForBrowserContext(browser_context_
)
140 ->DispatchGetPrintersRequested(
141 base::Bind(&ExtensionPrinterHandler::WrapGetPrintersCallback
,
142 weak_ptr_factory_
.GetWeakPtr(), callback
));
145 void ExtensionPrinterHandler::StartGetCapability(
146 const std::string
& destination_id
,
147 const PrinterHandler::GetCapabilityCallback
& callback
) {
148 extensions::PrinterProviderAPIFactory::GetInstance()
149 ->GetForBrowserContext(browser_context_
)
150 ->DispatchGetCapabilityRequested(
152 base::Bind(&ExtensionPrinterHandler::WrapGetCapabilityCallback
,
153 weak_ptr_factory_
.GetWeakPtr(), callback
, destination_id
));
156 void ExtensionPrinterHandler::StartPrint(
157 const std::string
& destination_id
,
158 const std::string
& capability
,
159 const base::string16
& job_title
,
160 const std::string
& ticket_json
,
161 const gfx::Size
& page_size
,
162 const scoped_refptr
<base::RefCountedMemory
>& print_data
,
163 const PrinterHandler::PrintCallback
& callback
) {
164 scoped_ptr
<extensions::PrinterProviderPrintJob
> print_job(
165 new extensions::PrinterProviderPrintJob());
166 print_job
->printer_id
= destination_id
;
167 print_job
->job_title
= job_title
;
168 print_job
->ticket_json
= ticket_json
;
170 cloud_devices::CloudDeviceDescription printer_description
;
171 printer_description
.InitFromString(capability
);
173 cloud_devices::printer::ContentTypesCapability content_types
;
174 content_types
.LoadFrom(printer_description
);
176 const bool kUsePdf
= content_types
.Contains(kContentTypePdf
) ||
177 content_types
.Contains(kContentTypeAll
);
180 // TODO(tbarzic): Consider writing larger PDF to disk and provide the data
181 // the same way as it's done with PWG raster.
182 print_job
->content_type
= kContentTypePdf
;
183 print_job
->document_bytes
= print_data
;
184 DispatchPrintJob(callback
, print_job
.Pass());
188 cloud_devices::CloudDeviceDescription ticket
;
189 if (!ticket
.InitFromString(ticket_json
)) {
190 WrapPrintCallback(callback
, false, kInvalidTicketPrintError
);
194 print_job
->content_type
= kContentTypePWGRaster
;
195 ConvertToPWGRaster(print_data
, printer_description
, ticket
, page_size
,
197 base::Bind(&ExtensionPrinterHandler::DispatchPrintJob
,
198 weak_ptr_factory_
.GetWeakPtr(), callback
));
201 void ExtensionPrinterHandler::SetPwgRasterConverterForTesting(
202 scoped_ptr
<local_discovery::PWGRasterConverter
> pwg_raster_converter
) {
203 pwg_raster_converter_
= pwg_raster_converter
.Pass();
206 void ExtensionPrinterHandler::ConvertToPWGRaster(
207 const scoped_refptr
<base::RefCountedMemory
>& data
,
208 const cloud_devices::CloudDeviceDescription
& printer_description
,
209 const cloud_devices::CloudDeviceDescription
& ticket
,
210 const gfx::Size
& page_size
,
211 scoped_ptr
<extensions::PrinterProviderPrintJob
> job
,
212 const ExtensionPrinterHandler::PrintJobCallback
& callback
) {
213 if (!pwg_raster_converter_
) {
214 pwg_raster_converter_
= PWGRasterConverter::CreateDefault();
216 pwg_raster_converter_
->Start(
218 PWGRasterConverter::GetConversionSettings(printer_description
, page_size
),
219 PWGRasterConverter::GetBitmapSettings(printer_description
, ticket
),
220 base::Bind(&UpdateJobFileInfo
, base::Passed(&job
), slow_task_runner_
,
224 void ExtensionPrinterHandler::DispatchPrintJob(
225 const PrinterHandler::PrintCallback
& callback
,
226 scoped_ptr
<extensions::PrinterProviderPrintJob
> print_job
) {
227 if (print_job
->document_path
.empty() && !print_job
->document_bytes
) {
228 WrapPrintCallback(callback
, false, kInvalidDataPrintError
);
232 extensions::PrinterProviderAPIFactory::GetInstance()
233 ->GetForBrowserContext(browser_context_
)
234 ->DispatchPrintRequested(
235 *print_job
, base::Bind(&ExtensionPrinterHandler::WrapPrintCallback
,
236 weak_ptr_factory_
.GetWeakPtr(), callback
));
239 void ExtensionPrinterHandler::WrapGetPrintersCallback(
240 const PrinterHandler::GetPrintersCallback
& callback
,
241 const base::ListValue
& printers
,
243 DCHECK_GT(pending_enumeration_count_
, 0);
245 pending_enumeration_count_
--;
247 callback
.Run(printers
, pending_enumeration_count_
== 0);
250 void ExtensionPrinterHandler::WrapGetCapabilityCallback(
251 const PrinterHandler::GetCapabilityCallback
& callback
,
252 const std::string
& destination_id
,
253 const base::DictionaryValue
& capability
) {
254 callback
.Run(destination_id
, capability
);
257 void ExtensionPrinterHandler::WrapPrintCallback(
258 const PrinterHandler::PrintCallback
& callback
,
260 const std::string
& status
) {
261 callback
.Run(success
, status
);
264 void ExtensionPrinterHandler::OnUsbDevicesEnumerated(
265 const PrinterHandler::GetPrintersCallback
& callback
,
266 const std::vector
<scoped_refptr
<UsbDevice
>>& devices
) {
267 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context_
);
268 DevicePermissionsManager
* permissions_manager
=
269 DevicePermissionsManager::Get(browser_context_
);
271 ListBuilder printer_list
;
273 for (const auto& extension
: registry
->enabled_extensions()) {
274 const UsbPrinterManifestData
* manifest_data
=
275 UsbPrinterManifestData::Get(extension
.get());
276 if (!manifest_data
|| !HasUsbPrinterProviderPermissions(extension
.get()))
279 const extensions::DevicePermissions
* device_permissions
=
280 permissions_manager
->GetForExtension(extension
->id());
281 for (const auto& device
: devices
) {
282 if (manifest_data
->SupportsDevice(device
)) {
283 extensions::UsbDevicePermission::CheckParam
param(
284 device
->vendor_id(), device
->product_id(),
285 extensions::UsbDevicePermissionData::UNSPECIFIED_INTERFACE
);
286 if (device_permissions
->FindUsbDeviceEntry(device
) ||
287 extension
->permissions_data()->CheckAPIPermissionWithParam(
288 extensions::APIPermission::kUsbDevice
, ¶m
)) {
289 // Skip devices the extension already has permission to access.
295 .Set("id", base::StringPrintf("provisional-usb:%s:%u",
296 extension
->id().c_str(),
297 device
->unique_id()))
299 DevicePermissionsManager::GetPermissionMessage(
300 device
->vendor_id(), device
->product_id(),
301 device
->manufacturer_string(),
302 device
->product_string(), base::string16(), false))
303 .Set("extensionId", extension
->id())
304 .Set("extensionName", extension
->name())
305 .Set("provisional", true));
310 DCHECK_GT(pending_enumeration_count_
, 0);
311 pending_enumeration_count_
--;
312 callback
.Run(*printer_list
.Build().get(), pending_enumeration_count_
== 0);