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/strings/string_split.h"
18 #include "base/task_runner_util.h"
19 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
20 #include "components/cloud_devices/common/cloud_device_description.h"
21 #include "components/cloud_devices/common/printer_description.h"
22 #include "device/core/device_client.h"
23 #include "device/usb/usb_device.h"
24 #include "device/usb/usb_service.h"
25 #include "extensions/browser/api/device_permissions_manager.h"
26 #include "extensions/browser/api/printer_provider/printer_provider_api.h"
27 #include "extensions/browser/api/printer_provider/printer_provider_api_factory.h"
28 #include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
31 #include "extensions/common/permissions/permissions_data.h"
32 #include "extensions/common/permissions/usb_device_permission.h"
33 #include "extensions/common/permissions/usb_device_permission_data.h"
34 #include "extensions/common/value_builder.h"
35 #include "printing/pdf_render_settings.h"
36 #include "printing/pwg_raster_settings.h"
38 using device::UsbDevice
;
39 using extensions::DevicePermissionsManager
;
40 using extensions::DictionaryBuilder
;
41 using extensions::Extension
;
42 using extensions::ExtensionRegistry
;
43 using extensions::ListBuilder
;
44 using extensions::UsbPrinterManifestData
;
45 using local_discovery::PWGRasterConverter
;
49 const char kContentTypePdf
[] = "application/pdf";
50 const char kContentTypePWGRaster
[] = "image/pwg-raster";
51 const char kContentTypeAll
[] = "*/*";
53 const char kInvalidDataPrintError
[] = "INVALID_DATA";
54 const char kInvalidTicketPrintError
[] = "INVALID_TICKET";
56 const char kProvisionalUsbLabel
[] = "provisional-usb";
58 // Updates |job| with raster file path, size and last modification time.
59 // Returns the updated print job.
60 scoped_ptr
<extensions::PrinterProviderPrintJob
> UpdateJobFileInfoOnWorkerThread(
61 const base::FilePath
& raster_path
,
62 scoped_ptr
<extensions::PrinterProviderPrintJob
> job
) {
63 if (base::GetFileInfo(raster_path
, &job
->file_info
))
64 job
->document_path
= raster_path
;
68 // Callback to PWG raster conversion.
69 // Posts a task to update print job with info about file containing converted
70 // PWG raster data. The task is posted to |slow_task_runner|.
71 void UpdateJobFileInfo(
72 scoped_ptr
<extensions::PrinterProviderPrintJob
> job
,
73 const scoped_refptr
<base::TaskRunner
>& slow_task_runner
,
74 const ExtensionPrinterHandler::PrintJobCallback
& callback
,
76 const base::FilePath
& pwg_file_path
) {
78 callback
.Run(job
.Pass());
82 base::PostTaskAndReplyWithResult(
83 slow_task_runner
.get(), FROM_HERE
,
84 base::Bind(&UpdateJobFileInfoOnWorkerThread
, pwg_file_path
,
89 bool HasUsbPrinterProviderPermissions(const Extension
* extension
) {
90 return extension
->permissions_data() &&
91 extension
->permissions_data()->HasAPIPermission(
92 extensions::APIPermission::kPrinterProvider
) &&
93 extension
->permissions_data()->HasAPIPermission(
94 extensions::APIPermission::kUsb
);
97 std::string
GenerateProvisionalUsbPrinterId(const Extension
* extension
,
98 const UsbDevice
* device
) {
99 return base::StringPrintf("%s:%s:%s", kProvisionalUsbLabel
,
100 extension
->id().c_str(), device
->guid().c_str());
103 bool ParseProvisionalUsbPrinterId(const std::string
& printer_id
,
104 std::string
* extension_id
,
105 std::string
* device_guid
) {
106 std::vector
<std::string
> components
= base::SplitString(
107 printer_id
, ":", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
109 if (components
.size() != 3)
112 if (components
[0] != kProvisionalUsbLabel
)
115 *extension_id
= components
[1];
116 *device_guid
= components
[2];
122 ExtensionPrinterHandler::ExtensionPrinterHandler(
123 content::BrowserContext
* browser_context
,
124 const scoped_refptr
<base::TaskRunner
>& slow_task_runner
)
125 : browser_context_(browser_context
),
126 slow_task_runner_(slow_task_runner
),
127 weak_ptr_factory_(this) {
130 ExtensionPrinterHandler::~ExtensionPrinterHandler() {
133 void ExtensionPrinterHandler::Reset() {
134 // TODO(tbarzic): Keep track of pending request ids issued by |this| and
135 // cancel them from here.
136 pending_enumeration_count_
= 0;
137 weak_ptr_factory_
.InvalidateWeakPtrs();
140 void ExtensionPrinterHandler::StartGetPrinters(
141 const PrinterHandler::GetPrintersCallback
& callback
) {
142 // Assume that there can only be one printer enumeration occuring at once.
143 DCHECK_EQ(pending_enumeration_count_
, 0);
144 pending_enumeration_count_
= 1;
146 bool extension_supports_usb_printers
= false;
147 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context_
);
148 for (const auto& extension
: registry
->enabled_extensions()) {
149 if (UsbPrinterManifestData::Get(extension
.get()) &&
150 HasUsbPrinterProviderPermissions(extension
.get())) {
151 extension_supports_usb_printers
= true;
156 if (extension_supports_usb_printers
) {
157 device::UsbService
* service
= device::DeviceClient::Get()->GetUsbService();
158 pending_enumeration_count_
++;
160 base::Bind(&ExtensionPrinterHandler::OnUsbDevicesEnumerated
,
161 weak_ptr_factory_
.GetWeakPtr(), callback
));
164 extensions::PrinterProviderAPIFactory::GetInstance()
165 ->GetForBrowserContext(browser_context_
)
166 ->DispatchGetPrintersRequested(
167 base::Bind(&ExtensionPrinterHandler::WrapGetPrintersCallback
,
168 weak_ptr_factory_
.GetWeakPtr(), callback
));
171 void ExtensionPrinterHandler::StartGetCapability(
172 const std::string
& destination_id
,
173 const PrinterHandler::GetCapabilityCallback
& callback
) {
174 extensions::PrinterProviderAPIFactory::GetInstance()
175 ->GetForBrowserContext(browser_context_
)
176 ->DispatchGetCapabilityRequested(
178 base::Bind(&ExtensionPrinterHandler::WrapGetCapabilityCallback
,
179 weak_ptr_factory_
.GetWeakPtr(), callback
, destination_id
));
182 void ExtensionPrinterHandler::StartPrint(
183 const std::string
& destination_id
,
184 const std::string
& capability
,
185 const base::string16
& job_title
,
186 const std::string
& ticket_json
,
187 const gfx::Size
& page_size
,
188 const scoped_refptr
<base::RefCountedMemory
>& print_data
,
189 const PrinterHandler::PrintCallback
& callback
) {
190 scoped_ptr
<extensions::PrinterProviderPrintJob
> print_job(
191 new extensions::PrinterProviderPrintJob());
192 print_job
->printer_id
= destination_id
;
193 print_job
->job_title
= job_title
;
194 print_job
->ticket_json
= ticket_json
;
196 cloud_devices::CloudDeviceDescription printer_description
;
197 printer_description
.InitFromString(capability
);
199 cloud_devices::printer::ContentTypesCapability content_types
;
200 content_types
.LoadFrom(printer_description
);
202 const bool kUsePdf
= content_types
.Contains(kContentTypePdf
) ||
203 content_types
.Contains(kContentTypeAll
);
206 // TODO(tbarzic): Consider writing larger PDF to disk and provide the data
207 // the same way as it's done with PWG raster.
208 print_job
->content_type
= kContentTypePdf
;
209 print_job
->document_bytes
= print_data
;
210 DispatchPrintJob(callback
, print_job
.Pass());
214 cloud_devices::CloudDeviceDescription ticket
;
215 if (!ticket
.InitFromString(ticket_json
)) {
216 WrapPrintCallback(callback
, false, kInvalidTicketPrintError
);
220 print_job
->content_type
= kContentTypePWGRaster
;
221 ConvertToPWGRaster(print_data
, printer_description
, ticket
, page_size
,
223 base::Bind(&ExtensionPrinterHandler::DispatchPrintJob
,
224 weak_ptr_factory_
.GetWeakPtr(), callback
));
227 void ExtensionPrinterHandler::StartGrantPrinterAccess(
228 const std::string
& printer_id
,
229 const PrinterHandler::GetPrinterInfoCallback
& callback
) {
230 std::string extension_id
;
231 std::string device_guid
;
232 if (!ParseProvisionalUsbPrinterId(printer_id
, &extension_id
, &device_guid
)) {
233 callback
.Run(base::DictionaryValue());
237 device::UsbService
* service
= device::DeviceClient::Get()->GetUsbService();
238 scoped_refptr
<UsbDevice
> device
= service
->GetDevice(device_guid
);
240 callback
.Run(base::DictionaryValue());
244 DevicePermissionsManager
* permissions_manager
=
245 DevicePermissionsManager::Get(browser_context_
);
246 permissions_manager
->AllowUsbDevice(extension_id
, device
);
248 extensions::PrinterProviderAPIFactory::GetInstance()
249 ->GetForBrowserContext(browser_context_
)
250 ->DispatchGetUsbPrinterInfoRequested(
251 extension_id
, device
,
252 base::Bind(&ExtensionPrinterHandler::WrapGetPrinterInfoCallback
,
253 weak_ptr_factory_
.GetWeakPtr(), callback
));
256 void ExtensionPrinterHandler::SetPwgRasterConverterForTesting(
257 scoped_ptr
<local_discovery::PWGRasterConverter
> pwg_raster_converter
) {
258 pwg_raster_converter_
= pwg_raster_converter
.Pass();
261 void ExtensionPrinterHandler::ConvertToPWGRaster(
262 const scoped_refptr
<base::RefCountedMemory
>& data
,
263 const cloud_devices::CloudDeviceDescription
& printer_description
,
264 const cloud_devices::CloudDeviceDescription
& ticket
,
265 const gfx::Size
& page_size
,
266 scoped_ptr
<extensions::PrinterProviderPrintJob
> job
,
267 const ExtensionPrinterHandler::PrintJobCallback
& callback
) {
268 if (!pwg_raster_converter_
) {
269 pwg_raster_converter_
= PWGRasterConverter::CreateDefault();
271 pwg_raster_converter_
->Start(
273 PWGRasterConverter::GetConversionSettings(printer_description
, page_size
),
274 PWGRasterConverter::GetBitmapSettings(printer_description
, ticket
),
275 base::Bind(&UpdateJobFileInfo
, base::Passed(&job
), slow_task_runner_
,
279 void ExtensionPrinterHandler::DispatchPrintJob(
280 const PrinterHandler::PrintCallback
& callback
,
281 scoped_ptr
<extensions::PrinterProviderPrintJob
> print_job
) {
282 if (print_job
->document_path
.empty() && !print_job
->document_bytes
) {
283 WrapPrintCallback(callback
, false, kInvalidDataPrintError
);
287 extensions::PrinterProviderAPIFactory::GetInstance()
288 ->GetForBrowserContext(browser_context_
)
289 ->DispatchPrintRequested(
290 *print_job
, base::Bind(&ExtensionPrinterHandler::WrapPrintCallback
,
291 weak_ptr_factory_
.GetWeakPtr(), callback
));
294 void ExtensionPrinterHandler::WrapGetPrintersCallback(
295 const PrinterHandler::GetPrintersCallback
& callback
,
296 const base::ListValue
& printers
,
298 DCHECK_GT(pending_enumeration_count_
, 0);
300 pending_enumeration_count_
--;
302 callback
.Run(printers
, pending_enumeration_count_
== 0);
305 void ExtensionPrinterHandler::WrapGetCapabilityCallback(
306 const PrinterHandler::GetCapabilityCallback
& callback
,
307 const std::string
& destination_id
,
308 const base::DictionaryValue
& capability
) {
309 callback
.Run(destination_id
, capability
);
312 void ExtensionPrinterHandler::WrapPrintCallback(
313 const PrinterHandler::PrintCallback
& callback
,
315 const std::string
& status
) {
316 callback
.Run(success
, status
);
319 void ExtensionPrinterHandler::WrapGetPrinterInfoCallback(
320 const PrinterHandler::GetPrinterInfoCallback
& callback
,
321 const base::DictionaryValue
& printer_info
) {
322 callback
.Run(printer_info
);
325 void ExtensionPrinterHandler::OnUsbDevicesEnumerated(
326 const PrinterHandler::GetPrintersCallback
& callback
,
327 const std::vector
<scoped_refptr
<UsbDevice
>>& devices
) {
328 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context_
);
329 DevicePermissionsManager
* permissions_manager
=
330 DevicePermissionsManager::Get(browser_context_
);
332 ListBuilder printer_list
;
334 for (const auto& extension
: registry
->enabled_extensions()) {
335 const UsbPrinterManifestData
* manifest_data
=
336 UsbPrinterManifestData::Get(extension
.get());
337 if (!manifest_data
|| !HasUsbPrinterProviderPermissions(extension
.get()))
340 const extensions::DevicePermissions
* device_permissions
=
341 permissions_manager
->GetForExtension(extension
->id());
342 for (const auto& device
: devices
) {
343 if (manifest_data
->SupportsDevice(device
)) {
344 extensions::UsbDevicePermission::CheckParam
param(
345 device
->vendor_id(), device
->product_id(),
346 extensions::UsbDevicePermissionData::UNSPECIFIED_INTERFACE
);
347 if (device_permissions
->FindUsbDeviceEntry(device
) ||
348 extension
->permissions_data()->CheckAPIPermissionWithParam(
349 extensions::APIPermission::kUsbDevice
, ¶m
)) {
350 // Skip devices the extension already has permission to access.
356 .Set("id", GenerateProvisionalUsbPrinterId(extension
.get(),
359 DevicePermissionsManager::GetPermissionMessage(
360 device
->vendor_id(), device
->product_id(),
361 device
->manufacturer_string(),
362 device
->product_string(), base::string16(), false))
363 .Set("extensionId", extension
->id())
364 .Set("extensionName", extension
->name())
365 .Set("provisional", true));
370 DCHECK_GT(pending_enumeration_count_
, 0);
371 pending_enumeration_count_
--;
372 callback
.Run(*printer_list
.Build().get(), pending_enumeration_count_
== 0);