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