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.
4 
5 #include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/location.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/task/post_task.h"
18 #include "chrome/browser/printing/pwg_raster_converter.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
21 #include "components/cloud_devices/common/cloud_device_description.h"
22 #include "components/cloud_devices/common/printer_description.h"
23 #include "extensions/browser/api/device_permissions_manager.h"
24 #include "extensions/browser/api/printer_provider/printer_provider_api.h"
25 #include "extensions/browser/api/printer_provider/printer_provider_api_factory.h"
26 #include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
27 #include "extensions/browser/api/usb/usb_device_manager.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/print_job_constants.h"
35 #include "printing/pwg_raster_settings.h"
36 #include "services/device/public/mojom/usb_device.mojom.h"
37 
38 using extensions::DevicePermissionsManager;
39 using extensions::DictionaryBuilder;
40 using extensions::Extension;
41 using extensions::ExtensionRegistry;
42 using extensions::ListBuilder;
43 using extensions::UsbDeviceManager;
44 using extensions::UsbPrinterManifestData;
45 
46 namespace printing {
47 
48 namespace {
49 
50 const char kContentTypePdf[] = "application/pdf";
51 const char kContentTypePWGRaster[] = "image/pwg-raster";
52 const char kContentTypeAll[] = "*/*";
53 
54 const char kInvalidDataPrintError[] = "INVALID_DATA";
55 const char kInvalidTicketPrintError[] = "INVALID_TICKET";
56 
57 const char kProvisionalUsbLabel[] = "provisional-usb";
58 
59 // Updates |job| with raster data. Returns the updated print job.
UpdateJobFileInfo(std::unique_ptr<extensions::PrinterProviderPrintJob> job,ExtensionPrinterHandler::PrintJobCallback callback,base::ReadOnlySharedMemoryRegion pwg_region)60 void UpdateJobFileInfo(std::unique_ptr<extensions::PrinterProviderPrintJob> job,
61                        ExtensionPrinterHandler::PrintJobCallback callback,
62                        base::ReadOnlySharedMemoryRegion pwg_region) {
63   auto data =
64       base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(pwg_region);
65   if (data)
66     job->document_bytes = data;
67   std::move(callback).Run(std::move(job));
68 }
69 
HasUsbPrinterProviderPermissions(const Extension * extension)70 bool HasUsbPrinterProviderPermissions(const Extension* extension) {
71   return extension->permissions_data() &&
72         extension->permissions_data()->HasAPIPermission(
73             extensions::APIPermission::kPrinterProvider) &&
74         extension->permissions_data()->HasAPIPermission(
75             extensions::APIPermission::kUsb);
76 }
77 
GenerateProvisionalUsbPrinterId(const Extension * extension,const device::mojom::UsbDeviceInfo & device)78 std::string GenerateProvisionalUsbPrinterId(
79     const Extension* extension,
80     const device::mojom::UsbDeviceInfo& device) {
81   return base::StringPrintf("%s:%s:%s", kProvisionalUsbLabel,
82                             extension->id().c_str(), device.guid.c_str());
83 }
84 
ParseProvisionalUsbPrinterId(const std::string & printer_id,std::string * extension_id,std::string * device_guid)85 bool ParseProvisionalUsbPrinterId(const std::string& printer_id,
86                                   std::string* extension_id,
87                                   std::string* device_guid) {
88   std::vector<std::string> components = base::SplitString(
89       printer_id, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
90 
91   if (components.size() != 3)
92     return false;
93 
94   if (components[0] != kProvisionalUsbLabel)
95     return false;
96 
97   *extension_id = components[1];
98   *device_guid = components[2];
99   return true;
100 }
101 
GetPrinterProviderAPI(Profile * profile)102 extensions::PrinterProviderAPI* GetPrinterProviderAPI(Profile* profile) {
103   return extensions::PrinterProviderAPIFactory::GetInstance()
104       ->GetForBrowserContext(profile);
105 }
106 
107 }  // namespace
108 
ExtensionPrinterHandler(Profile * profile)109 ExtensionPrinterHandler::ExtensionPrinterHandler(Profile* profile)
110     : profile_(profile) {}
111 
~ExtensionPrinterHandler()112 ExtensionPrinterHandler::~ExtensionPrinterHandler() {
113 }
114 
Reset()115 void ExtensionPrinterHandler::Reset() {
116   // TODO(tbarzic): Keep track of pending request ids issued by |this| and
117   // cancel them from here.
118   pending_enumeration_count_ = 0;
119   weak_ptr_factory_.InvalidateWeakPtrs();
120 }
121 
StartGetPrinters(AddedPrintersCallback callback,GetPrintersDoneCallback done_callback)122 void ExtensionPrinterHandler::StartGetPrinters(
123     AddedPrintersCallback callback,
124     GetPrintersDoneCallback done_callback) {
125   // Assume that there can only be one printer enumeration occuring at once.
126   DCHECK_EQ(pending_enumeration_count_, 0);
127   pending_enumeration_count_ = 1;
128   done_callback_ = std::move(done_callback);
129 
130   bool extension_supports_usb_printers = false;
131   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
132   for (const auto& extension : registry->enabled_extensions()) {
133     if (UsbPrinterManifestData::Get(extension.get()) &&
134         HasUsbPrinterProviderPermissions(extension.get())) {
135       extension_supports_usb_printers = true;
136       break;
137     }
138   }
139 
140   if (extension_supports_usb_printers) {
141     pending_enumeration_count_++;
142     UsbDeviceManager* usb_manager = UsbDeviceManager::Get(profile_);
143     DCHECK(usb_manager);
144     usb_manager->GetDevices(
145         base::BindOnce(&ExtensionPrinterHandler::OnUsbDevicesEnumerated,
146                        weak_ptr_factory_.GetWeakPtr(), callback));
147   }
148 
149   GetPrinterProviderAPI(profile_)->DispatchGetPrintersRequested(
150       base::BindRepeating(&ExtensionPrinterHandler::WrapGetPrintersCallback,
151                           weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
152 }
153 
StartGetCapability(const std::string & destination_id,GetCapabilityCallback callback)154 void ExtensionPrinterHandler::StartGetCapability(
155     const std::string& destination_id,
156     GetCapabilityCallback callback) {
157   GetPrinterProviderAPI(profile_)->DispatchGetCapabilityRequested(
158       destination_id,
159       base::BindOnce(&ExtensionPrinterHandler::WrapGetCapabilityCallback,
160                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
161 }
162 
StartPrint(const base::string16 & job_title,base::Value settings,scoped_refptr<base::RefCountedMemory> print_data,PrintCallback callback)163 void ExtensionPrinterHandler::StartPrint(
164     const base::string16& job_title,
165     base::Value settings,
166     scoped_refptr<base::RefCountedMemory> print_data,
167     PrintCallback callback) {
168   auto print_job = std::make_unique<extensions::PrinterProviderPrintJob>();
169   print_job->job_title = job_title;
170   std::string capabilities;
171   gfx::Size page_size;
172   if (!ParseSettings(settings, &print_job->printer_id, &capabilities,
173                      &page_size, &print_job->ticket)) {
174     std::move(callback).Run(base::Value("Invalid settings"));
175     return;
176   }
177 
178   cloud_devices::CloudDeviceDescription printer_description;
179   printer_description.InitFromString(capabilities);
180 
181   cloud_devices::printer::ContentTypesCapability content_types;
182   content_types.LoadFrom(printer_description);
183 
184   const bool use_pdf = content_types.Contains(kContentTypePdf) ||
185                        content_types.Contains(kContentTypeAll);
186   if (use_pdf) {
187     print_job->content_type = kContentTypePdf;
188     print_job->document_bytes = print_data;
189     DispatchPrintJob(std::move(callback), std::move(print_job));
190     return;
191   }
192 
193   if (!cloud_devices::CloudDeviceDescription::IsValidTicket(
194           print_job->ticket)) {
195     WrapPrintCallback(std::move(callback),
196                       base::Value(kInvalidTicketPrintError));
197     return;
198   }
199 
200   print_job->content_type = kContentTypePWGRaster;
201   ConvertToPWGRaster(
202       print_data, printer_description, page_size, std::move(print_job),
203       base::BindOnce(&ExtensionPrinterHandler::DispatchPrintJob,
204                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
205 }
206 
StartGrantPrinterAccess(const std::string & printer_id,GetPrinterInfoCallback callback)207 void ExtensionPrinterHandler::StartGrantPrinterAccess(
208     const std::string& printer_id,
209     GetPrinterInfoCallback callback) {
210   std::string extension_id;
211   std::string device_guid;
212   if (!ParseProvisionalUsbPrinterId(printer_id, &extension_id, &device_guid)) {
213     std::move(callback).Run(base::DictionaryValue());
214     return;
215   }
216 
217   UsbDeviceManager* usb_manager = UsbDeviceManager::Get(profile_);
218   DCHECK(usb_manager);
219 
220   const device::mojom::UsbDeviceInfo* device =
221       usb_manager->GetDeviceInfo(device_guid);
222   if (!device) {
223     std::move(callback).Run(base::DictionaryValue());
224     return;
225   }
226 
227   DevicePermissionsManager* permissions_manager =
228       DevicePermissionsManager::Get(profile_);
229   permissions_manager->AllowUsbDevice(extension_id, *device);
230 
231   GetPrinterProviderAPI(profile_)->DispatchGetUsbPrinterInfoRequested(
232       extension_id, *device,
233       base::BindOnce(&ExtensionPrinterHandler::WrapGetPrinterInfoCallback,
234                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
235 }
236 
SetPwgRasterConverterForTesting(std::unique_ptr<PwgRasterConverter> pwg_raster_converter)237 void ExtensionPrinterHandler::SetPwgRasterConverterForTesting(
238     std::unique_ptr<PwgRasterConverter> pwg_raster_converter) {
239   pwg_raster_converter_ = std::move(pwg_raster_converter);
240 }
241 
ConvertToPWGRaster(scoped_refptr<base::RefCountedMemory> data,const cloud_devices::CloudDeviceDescription & printer_description,const gfx::Size & page_size,std::unique_ptr<extensions::PrinterProviderPrintJob> job,PrintJobCallback callback)242 void ExtensionPrinterHandler::ConvertToPWGRaster(
243     scoped_refptr<base::RefCountedMemory> data,
244     const cloud_devices::CloudDeviceDescription& printer_description,
245     const gfx::Size& page_size,
246     std::unique_ptr<extensions::PrinterProviderPrintJob> job,
247     PrintJobCallback callback) {
248   if (!pwg_raster_converter_)
249     pwg_raster_converter_ = PwgRasterConverter::CreateDefault();
250 
251   cloud_devices::CloudDeviceDescription ticket;
252   bool ok = ticket.InitFromValue(std::move(job->ticket));
253   DCHECK(ok);
254   PwgRasterSettings bitmap_settings =
255       PwgRasterConverter::GetBitmapSettings(printer_description, ticket);
256   job->ticket = std::move(ticket).ToValue();
257 
258   pwg_raster_converter_->Start(
259       data.get(),
260       PwgRasterConverter::GetConversionSettings(printer_description, page_size,
261                                                 bitmap_settings.use_color),
262       bitmap_settings,
263       base::BindOnce(&UpdateJobFileInfo, std::move(job), std::move(callback)));
264 }
265 
DispatchPrintJob(PrintCallback callback,std::unique_ptr<extensions::PrinterProviderPrintJob> print_job)266 void ExtensionPrinterHandler::DispatchPrintJob(
267     PrintCallback callback,
268     std::unique_ptr<extensions::PrinterProviderPrintJob> print_job) {
269   if (!print_job->document_bytes) {
270     WrapPrintCallback(std::move(callback), base::Value(kInvalidDataPrintError));
271     return;
272   }
273 
274   extensions::PrinterProviderAPIFactory::GetInstance()
275       ->GetForBrowserContext(profile_)
276       ->DispatchPrintRequested(
277           std::move(*print_job),
278           base::BindOnce(&ExtensionPrinterHandler::WrapPrintCallback,
279                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
280 }
281 
WrapGetPrintersCallback(AddedPrintersCallback callback,const base::ListValue & printers,bool done)282 void ExtensionPrinterHandler::WrapGetPrintersCallback(
283     AddedPrintersCallback callback,
284     const base::ListValue& printers,
285     bool done) {
286   DCHECK_GT(pending_enumeration_count_, 0);
287   if (!printers.empty())
288     callback.Run(printers);
289 
290   if (done)
291     pending_enumeration_count_--;
292   if (pending_enumeration_count_ == 0)
293     std::move(done_callback_).Run();
294 }
295 
WrapGetCapabilityCallback(GetCapabilityCallback callback,const base::DictionaryValue & capability)296 void ExtensionPrinterHandler::WrapGetCapabilityCallback(
297     GetCapabilityCallback callback,
298     const base::DictionaryValue& capability) {
299   base::Value capabilities(base::Value::Type::DICTIONARY);
300   base::Value cdd = ValidateCddForPrintPreview(capability.Clone());
301   // Leave |capabilities| empty if |cdd| is empty.
302   if (!cdd.DictEmpty())
303     capabilities.SetKey(kSettingCapabilities, std::move(cdd));
304 
305   std::move(callback).Run(std::move(capabilities));
306 }
307 
WrapPrintCallback(PrintCallback callback,const base::Value & status)308 void ExtensionPrinterHandler::WrapPrintCallback(PrintCallback callback,
309                                                 const base::Value& status) {
310   std::move(callback).Run(status);
311 }
312 
WrapGetPrinterInfoCallback(GetPrinterInfoCallback callback,const base::DictionaryValue & printer_info)313 void ExtensionPrinterHandler::WrapGetPrinterInfoCallback(
314     GetPrinterInfoCallback callback,
315     const base::DictionaryValue& printer_info) {
316   std::move(callback).Run(printer_info);
317 }
318 
OnUsbDevicesEnumerated(AddedPrintersCallback callback,std::vector<device::mojom::UsbDeviceInfoPtr> devices)319 void ExtensionPrinterHandler::OnUsbDevicesEnumerated(
320     AddedPrintersCallback callback,
321     std::vector<device::mojom::UsbDeviceInfoPtr> devices) {
322   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
323   DevicePermissionsManager* permissions_manager =
324       DevicePermissionsManager::Get(profile_);
325 
326   ListBuilder printer_list;
327 
328   for (const auto& extension : registry->enabled_extensions()) {
329     const UsbPrinterManifestData* manifest_data =
330         UsbPrinterManifestData::Get(extension.get());
331     if (!manifest_data || !HasUsbPrinterProviderPermissions(extension.get()))
332       continue;
333 
334     const extensions::DevicePermissions* device_permissions =
335         permissions_manager->GetForExtension(extension->id());
336     for (const auto& device : devices) {
337       if (manifest_data->SupportsDevice(*device)) {
338         std::unique_ptr<extensions::UsbDevicePermission::CheckParam> param =
339             extensions::UsbDevicePermission::CheckParam::ForUsbDevice(
340                 extension.get(), *device);
341         if (device_permissions->FindUsbDeviceEntry(*device) ||
342             extension->permissions_data()->CheckAPIPermissionWithParam(
343                 extensions::APIPermission::kUsbDevice, param.get())) {
344           // Skip devices the extension already has permission to access.
345           continue;
346         }
347 
348         printer_list.Append(
349             DictionaryBuilder()
350                 .Set("id",
351                      GenerateProvisionalUsbPrinterId(extension.get(), *device))
352                 .Set("name",
353                      DevicePermissionsManager::GetPermissionMessage(
354                          device->vendor_id, device->product_id,
355                          device->manufacturer_name.value_or(base::string16()),
356                          device->product_name.value_or(base::string16()),
357                          base::string16(), false))
358                 .Set("extensionId", extension->id())
359                 .Set("extensionName", extension->name())
360                 .Set("provisional", true)
361                 .Build());
362       }
363     }
364   }
365 
366   DCHECK_GT(pending_enumeration_count_, 0);
367   pending_enumeration_count_--;
368   std::unique_ptr<base::ListValue> list = printer_list.Build();
369   if (!list->empty())
370     callback.Run(*list);
371   if (pending_enumeration_count_ == 0)
372     std::move(done_callback_).Run();
373 }
374 
375 }  // namespace printing
376