| OLD | NEW |
| (Empty) |
| 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 <stdint.h> | |
| 6 | |
| 7 #include <memory> | |
| 8 #include <utility> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/bind.h" | |
| 12 #include "base/bind_helpers.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "base/metrics/histogram_macros.h" | |
| 15 #include "base/scoped_observer.h" | |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/strings/utf_string_conversions.h" | |
| 18 #include "chrome/browser/browser_process.h" | |
| 19 #include "chrome/browser/chromeos/printer_detector/printer_detector.h" | |
| 20 #include "chrome/browser/chromeos/printing/usb_printer_util.h" | |
| 21 #include "chrome/browser/chromeos/profiles/profile_helper.h" | |
| 22 #include "chrome/browser/notifications/notification.h" | |
| 23 #include "chrome/browser/notifications/notification_delegate.h" | |
| 24 #include "chrome/browser/notifications/notification_ui_manager.h" | |
| 25 #include "chrome/browser/ui/browser_navigator.h" | |
| 26 #include "chrome/common/extensions/api/webstore_widget_private.h" | |
| 27 #include "chrome/common/extensions/extension_constants.h" | |
| 28 #include "chrome/grit/generated_resources.h" | |
| 29 #include "chrome/grit/theme_resources.h" | |
| 30 #include "components/user_manager/user.h" | |
| 31 #include "components/user_manager/user_manager.h" | |
| 32 #include "device/base/device_client.h" | |
| 33 #include "device/usb/usb_device.h" | |
| 34 #include "device/usb/usb_device_filter.h" | |
| 35 #include "device/usb/usb_ids.h" | |
| 36 #include "device/usb/usb_service.h" | |
| 37 #include "extensions/browser/event_router.h" | |
| 38 #include "extensions/browser/extension_registry.h" | |
| 39 #include "extensions/browser/extension_system.h" | |
| 40 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h" | |
| 41 #include "extensions/common/extension.h" | |
| 42 #include "extensions/common/extension_set.h" | |
| 43 #include "extensions/common/one_shot_event.h" | |
| 44 #include "extensions/common/permissions/api_permission.h" | |
| 45 #include "extensions/common/permissions/permissions_data.h" | |
| 46 #include "extensions/common/permissions/usb_device_permission.h" | |
| 47 #include "ui/base/l10n/l10n_util.h" | |
| 48 #include "ui/base/resource/resource_bundle.h" | |
| 49 | |
| 50 namespace webstore_widget_private_api = | |
| 51 extensions::api::webstore_widget_private; | |
| 52 | |
| 53 namespace chromeos { | |
| 54 namespace { | |
| 55 | |
| 56 const char kPrinterProviderFoundNotificationID[] = | |
| 57 "chrome://settings/printer/printer_app_found"; | |
| 58 | |
| 59 const char kNoPrinterProviderNotificationID[] = | |
| 60 "chrome://settings/printer/no_printer_app"; | |
| 61 | |
| 62 enum PrinterServiceEvent { | |
| 63 PRINTER_ADDED, | |
| 64 DEPRECATED_PAGE_DISPLAYED, | |
| 65 NOTIFICATION_SHOWN_PRINTER_SUPPORTED, | |
| 66 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED, | |
| 67 WEBSTORE_WIDGET_APP_LAUNCHED, | |
| 68 PRINTER_SERVICE_EVENT_MAX, | |
| 69 }; | |
| 70 | |
| 71 base::string16 GetNotificationTitle(uint16_t vendor_id, uint16_t product_id) { | |
| 72 const char* vendor_name = device::UsbIds::GetVendorName(vendor_id); | |
| 73 if (vendor_name) { | |
| 74 return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE, | |
| 75 base::UTF8ToUTF16(vendor_name)); | |
| 76 } else { | |
| 77 return l10n_util::GetStringUTF16( | |
| 78 IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR); | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 std::string GetNotificationTag(const std::string& vendor_id, | |
| 83 const std::string& product_id) { | |
| 84 return vendor_id + ":" + product_id; | |
| 85 } | |
| 86 | |
| 87 // Checks if there is an enabled extension with printerProvider permission and | |
| 88 // usbDevices persmission for the USB (vendor_id, product_id) pair. | |
| 89 bool HasAppThatSupportsPrinter(Profile* profile, | |
| 90 const scoped_refptr<device::UsbDevice>& device) { | |
| 91 const extensions::ExtensionSet& enabled_extensions = | |
| 92 extensions::ExtensionRegistry::Get(profile)->enabled_extensions(); | |
| 93 for (const auto& extension : enabled_extensions) { | |
| 94 if (!extension->permissions_data() || | |
| 95 !extension->permissions_data()->HasAPIPermission( | |
| 96 extensions::APIPermission::kPrinterProvider) || | |
| 97 !extension->permissions_data()->HasAPIPermission( | |
| 98 extensions::APIPermission::kUsb)) { | |
| 99 continue; | |
| 100 } | |
| 101 | |
| 102 const extensions::UsbPrinterManifestData* manifest_data = | |
| 103 extensions::UsbPrinterManifestData::Get(extension.get()); | |
| 104 if (manifest_data && manifest_data->SupportsDevice(device)) { | |
| 105 return true; | |
| 106 } | |
| 107 | |
| 108 std::unique_ptr<extensions::UsbDevicePermission::CheckParam> param = | |
| 109 extensions::UsbDevicePermission::CheckParam::ForUsbDevice( | |
| 110 extension.get(), device.get()); | |
| 111 if (extension->permissions_data()->CheckAPIPermissionWithParam( | |
| 112 extensions::APIPermission::kUsbDevice, param.get())) { | |
| 113 return true; | |
| 114 } | |
| 115 } | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 // Delegate for notification shown when a printer provider app for the plugged | |
| 120 // in printer is found. | |
| 121 class PrinterProviderExistsNotificationDelegate : public NotificationDelegate { | |
| 122 public: | |
| 123 PrinterProviderExistsNotificationDelegate(const std::string& vendor_id, | |
| 124 const std::string& product_id) | |
| 125 : vendor_id_(vendor_id), product_id_(product_id) {} | |
| 126 | |
| 127 std::string id() const override { | |
| 128 return "system.printer.printer_provider_exists/" + | |
| 129 GetNotificationTag(vendor_id_, product_id_); | |
| 130 } | |
| 131 | |
| 132 private: | |
| 133 ~PrinterProviderExistsNotificationDelegate() override = default; | |
| 134 | |
| 135 const std::string vendor_id_; | |
| 136 const std::string product_id_; | |
| 137 | |
| 138 DISALLOW_COPY_AND_ASSIGN(PrinterProviderExistsNotificationDelegate); | |
| 139 }; | |
| 140 | |
| 141 // Delegate for notification shown when there are no printer provider apps that | |
| 142 // support the plugged in printer found. | |
| 143 // The notification is clickable, and clicking it is supposed to launch | |
| 144 // Chrome Web Store widget listing apps that can support the plugged in printer. | |
| 145 // (not implemented yet). | |
| 146 class SearchPrinterAppNotificationDelegate : public NotificationDelegate { | |
| 147 public: | |
| 148 SearchPrinterAppNotificationDelegate(content::BrowserContext* browser_context, | |
| 149 uint16_t vendor_id, | |
| 150 const std::string& vendor_id_str, | |
| 151 uint16_t product_id, | |
| 152 const std::string& product_id_str) | |
| 153 : browser_context_(browser_context), | |
| 154 vendor_id_(vendor_id), | |
| 155 vendor_id_str_(vendor_id_str), | |
| 156 product_id_(product_id), | |
| 157 product_id_str_(product_id_str) {} | |
| 158 | |
| 159 std::string id() const override { | |
| 160 return "system.printer.no_printer_provider_found/" + | |
| 161 GetNotificationTag(vendor_id_str_, product_id_str_); | |
| 162 } | |
| 163 | |
| 164 bool HasClickedListener() override { return true; } | |
| 165 | |
| 166 void Click() override { | |
| 167 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
| 168 WEBSTORE_WIDGET_APP_LAUNCHED, | |
| 169 PRINTER_SERVICE_EVENT_MAX); | |
| 170 webstore_widget_private_api::Options options; | |
| 171 options.type = webstore_widget_private_api::TYPE_PRINTER_PROVIDER; | |
| 172 options.usb_id.reset(new webstore_widget_private_api::UsbId()); | |
| 173 options.usb_id->vendor_id = vendor_id_; | |
| 174 options.usb_id->product_id = product_id_; | |
| 175 | |
| 176 extensions::EventRouter* event_router = | |
| 177 extensions::EventRouter::Get(browser_context_); | |
| 178 std::unique_ptr<extensions::Event> event(new extensions::Event( | |
| 179 extensions::events::WEBSTORE_WIDGET_PRIVATE_ON_SHOW_WIDGET, | |
| 180 webstore_widget_private_api::OnShowWidget::kEventName, | |
| 181 webstore_widget_private_api::OnShowWidget::Create(options))); | |
| 182 event_router->DispatchEventToExtension(extension_misc::kWebstoreWidgetAppId, | |
| 183 std::move(event)); | |
| 184 } | |
| 185 | |
| 186 private: | |
| 187 ~SearchPrinterAppNotificationDelegate() override = default; | |
| 188 | |
| 189 content::BrowserContext* browser_context_; | |
| 190 uint16_t vendor_id_; | |
| 191 std::string vendor_id_str_; | |
| 192 uint16_t product_id_; | |
| 193 std::string product_id_str_; | |
| 194 | |
| 195 DISALLOW_COPY_AND_ASSIGN(SearchPrinterAppNotificationDelegate); | |
| 196 }; | |
| 197 | |
| 198 // The PrinterDetector that initiates extension-based USB printer setup: | |
| 199 // | |
| 200 // * if there is a printer provider extension for a detected USB printer | |
| 201 // installed, it shows a notification informing the user the printer is ready to | |
| 202 // be used. | |
| 203 // | |
| 204 // * otherwise, it shows a notification offering the user an option to install a | |
| 205 // printer provider extension for the printer from the Chrome Web Store. | |
| 206 // | |
| 207 // TODO(justincarlson) - Remove this implementation when CUPS support is enabled | |
| 208 // by default. | |
| 209 class LegacyPrinterDetectorImpl : public PrinterDetector, | |
| 210 public device::UsbService::Observer { | |
| 211 public: | |
| 212 explicit LegacyPrinterDetectorImpl(Profile* profile) | |
| 213 : profile_(profile), | |
| 214 notification_ui_manager_(nullptr), | |
| 215 observer_(this), | |
| 216 weak_ptr_factory_(this) { | |
| 217 extensions::ExtensionSystem::Get(profile)->ready().Post( | |
| 218 FROM_HERE, base::Bind(&LegacyPrinterDetectorImpl::Initialize, | |
| 219 weak_ptr_factory_.GetWeakPtr())); | |
| 220 } | |
| 221 ~LegacyPrinterDetectorImpl() override = default; | |
| 222 | |
| 223 private: | |
| 224 // UsbService::observer override: | |
| 225 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { | |
| 226 const user_manager::User* user = | |
| 227 ProfileHelper::Get()->GetUserByProfile(profile_); | |
| 228 if (!user || !user->HasGaiaAccount() || !user_manager::UserManager::Get() || | |
| 229 user != user_manager::UserManager::Get()->GetActiveUser()) { | |
| 230 return; | |
| 231 } | |
| 232 | |
| 233 if (!UsbDeviceIsPrinter(*device)) { | |
| 234 return; | |
| 235 } | |
| 236 | |
| 237 if (notification_ui_manager_ == nullptr) { | |
| 238 notification_ui_manager_ = g_browser_process->notification_ui_manager(); | |
| 239 } | |
| 240 | |
| 241 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
| 242 PRINTER_ADDED, PRINTER_SERVICE_EVENT_MAX); | |
| 243 ShowPrinterPluggedNotification(device); | |
| 244 } | |
| 245 | |
| 246 // Initializes the printer detector. | |
| 247 void Initialize() { | |
| 248 device::UsbService* usb_service = | |
| 249 device::DeviceClient::Get()->GetUsbService(); | |
| 250 if (!usb_service) | |
| 251 return; | |
| 252 observer_.Add(usb_service); | |
| 253 } | |
| 254 | |
| 255 void SetNotificationUIManagerForTesting( | |
| 256 NotificationUIManager* manager) override { | |
| 257 notification_ui_manager_ = manager; | |
| 258 } | |
| 259 | |
| 260 // Shows a notification for a plugged in printer. | |
| 261 // If there is a printerProvider app that handles the printer's USB | |
| 262 // (vendor_id, product_id) pair, the notification informs the user that the | |
| 263 // printer is ready to be used, otherwise it offers the user to search the | |
| 264 // Chrome Web Store for an app that can handle the printer. | |
| 265 void ShowPrinterPluggedNotification( | |
| 266 const scoped_refptr<device::UsbDevice>& device) { | |
| 267 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
| 268 std::unique_ptr<Notification> notification; | |
| 269 | |
| 270 const std::string kVendorIdStr = base::IntToString(device->vendor_id()); | |
| 271 const std::string kProductIdStr = base::IntToString(device->product_id()); | |
| 272 | |
| 273 if (HasAppThatSupportsPrinter(profile_, device)) { | |
| 274 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
| 275 NOTIFICATION_SHOWN_PRINTER_SUPPORTED, | |
| 276 PRINTER_SERVICE_EVENT_MAX); | |
| 277 notification.reset(new Notification( | |
| 278 message_center::NOTIFICATION_TYPE_SIMPLE, | |
| 279 GetNotificationTitle(device->vendor_id(), device->product_id()), | |
| 280 l10n_util::GetStringUTF16( | |
| 281 IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY), | |
| 282 bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION), | |
| 283 message_center::NotifierId( | |
| 284 message_center::NotifierId::SYSTEM_COMPONENT, | |
| 285 kPrinterProviderFoundNotificationID), | |
| 286 base::string16(), GURL(kPrinterProviderFoundNotificationID), | |
| 287 GetNotificationTag(kVendorIdStr, kProductIdStr), | |
| 288 message_center::RichNotificationData(), | |
| 289 new PrinterProviderExistsNotificationDelegate(kVendorIdStr, | |
| 290 kProductIdStr))); | |
| 291 } else { | |
| 292 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
| 293 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED, | |
| 294 PRINTER_SERVICE_EVENT_MAX); | |
| 295 message_center::RichNotificationData options; | |
| 296 options.clickable = true; | |
| 297 notification.reset(new Notification( | |
| 298 message_center::NOTIFICATION_TYPE_SIMPLE, | |
| 299 GetNotificationTitle(device->vendor_id(), device->product_id()), | |
| 300 l10n_util::GetStringUTF16( | |
| 301 IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY), | |
| 302 bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION), | |
| 303 message_center::NotifierId( | |
| 304 message_center::NotifierId::SYSTEM_COMPONENT, | |
| 305 kNoPrinterProviderNotificationID), | |
| 306 base::string16(), GURL(kNoPrinterProviderNotificationID), | |
| 307 GetNotificationTag(kVendorIdStr, kProductIdStr), options, | |
| 308 new SearchPrinterAppNotificationDelegate( | |
| 309 profile_, device->vendor_id(), kVendorIdStr, device->product_id(), | |
| 310 kProductIdStr))); | |
| 311 } | |
| 312 | |
| 313 notification->SetSystemPriority(); | |
| 314 notification_ui_manager_->Add(*notification, profile_); | |
| 315 } | |
| 316 | |
| 317 std::unique_ptr<Notification> notification_; | |
| 318 | |
| 319 Profile* profile_; | |
| 320 NotificationUIManager* notification_ui_manager_; | |
| 321 ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; | |
| 322 base::WeakPtrFactory<LegacyPrinterDetectorImpl> weak_ptr_factory_; | |
| 323 }; | |
| 324 | |
| 325 } // namespace | |
| 326 | |
| 327 // static | |
| 328 std::unique_ptr<PrinterDetector> PrinterDetector::CreateLegacy( | |
| 329 Profile* profile) { | |
| 330 return base::MakeUnique<LegacyPrinterDetectorImpl>(profile); | |
| 331 } | |
| 332 | |
| 333 } // namespace chromeos | |
| OLD | NEW |