Index: chrome/browser/chromeos/printer_detector/cups_printer_detector.cc |
diff --git a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc |
index 4532256dc3c09642ac36568617d8fce6372f67d8..3719c474e7dbffc25c9f6fb68d920cd25842f675 100644 |
--- a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc |
+++ b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc |
@@ -11,68 +11,61 @@ |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
#include "base/scoped_observer.h" |
#include "base/strings/utf_string_conversions.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/chromeos/printer_detector/printer_detector.h" |
+#include "chrome/browser/chromeos/printing/ppd_provider_factory.h" |
+#include "chrome/browser/chromeos/printing/printer_configurer.h" |
+#include "chrome/browser/chromeos/printing/printers_manager_factory.h" |
+#include "chrome/browser/chromeos/printing/usb_printer_util.h" |
#include "chrome/browser/chromeos/profiles/profile_helper.h" |
-#include "chrome/browser/notifications/notification.h" |
-#include "chrome/browser/notifications/notification_delegate.h" |
-#include "chrome/browser/notifications/notification_ui_manager.h" |
-#include "chrome/browser/ui/browser_navigator.h" |
-#include "chrome/browser/ui/browser_navigator_params.h" |
-#include "chrome/common/url_constants.h" |
-#include "chrome/grit/generated_resources.h" |
-#include "chrome/grit/theme_resources.h" |
-#include "components/user_manager/user.h" |
-#include "components/user_manager/user_manager.h" |
+#include "chromeos/dbus/dbus_thread_manager.h" |
+#include "chromeos/dbus/debug_daemon_client.h" |
+#include "chromeos/printing/ppd_provider.h" |
+#include "content/public/browser/browser_thread.h" |
#include "device/base/device_client.h" |
#include "device/usb/usb_device.h" |
#include "device/usb/usb_device_filter.h" |
#include "device/usb/usb_service.h" |
-#include "extensions/browser/extension_registry.h" |
-#include "extensions/browser/extension_system.h" |
-#include "extensions/common/one_shot_event.h" |
-#include "ui/base/l10n/l10n_util.h" |
-#include "ui/base/resource/resource_bundle.h" |
namespace chromeos { |
namespace { |
-const char kUSBPrinterFoundNotificationID[] = |
- "chrome://settings/cupsPrinters/printer_found"; |
+using printing::PpdProvider; |
-// Base class used for printer USB interfaces |
-// (https://www.usb.org/developers/defined_class). |
-const uint8_t kPrinterInterfaceClass = 7; |
+// Aggregates the information needed for printer setup so it's easier to pass it |
+// around. |
+struct SetUpPrinterData { |
+ // The configurer running the SetUpPrinter call. |
+ std::unique_ptr<PrinterConfigurer> configurer; |
-// USBPrinterSetupNotificationDelegate takes a pointer to the Impl class, so |
-// we have to forward declare it. |
-class CupsPrinterDetectorImpl; |
+ // The printer being set up. |
+ std::unique_ptr<Printer> printer; |
-class USBPrinterSetupNotificationDelegate : public NotificationDelegate { |
- public: |
- explicit USBPrinterSetupNotificationDelegate( |
- CupsPrinterDetectorImpl* printer_detector) |
- : printer_detector_(printer_detector) {} |
- |
- // NotificationDelegate override: |
- std::string id() const override { return kUSBPrinterFoundNotificationID; } |
- |
- // This is defined out of line because it needs the PrinterDetectorImpl |
- // full class declaration, not just the forward declaration. |
- void ButtonClick(int button_index) override; |
- |
- bool HasClickedListener() override { return true; } |
- |
- private: |
- ~USBPrinterSetupNotificationDelegate() override = default; |
+ // The usb device causing this setup flow. |
+ scoped_refptr<device::UsbDevice> device; |
- CupsPrinterDetectorImpl* printer_detector_; |
+ // True if this printer is one that the user doesn't already have a |
+ // configuration for. |
+ bool is_new; |
- DISALLOW_COPY_AND_ASSIGN(USBPrinterSetupNotificationDelegate); |
+ // True if this was a printer that was plugged in during the session, false if |
+ // it was already plugged in when the session started. |
+ bool hotplugged; |
}; |
+// Given a usb device, guesses the make and model for a driver lookup. |
+// |
+// TODO(justincarlson): Possibly go deeper and query the IEEE1284 fields |
+// for make and model if we determine those are more likely to contain |
+// what we want. |
+std::string GuessEffectiveMakeAndModel(const device::UsbDevice& device) { |
+ return base::UTF16ToUTF8(device.manufacturer_string()) + " " + |
+ base::UTF16ToUTF8(device.product_string()); |
+} |
+ |
// The PrinterDetector that drives the flow for setting up a USB printer to use |
// CUPS backend. |
class CupsPrinterDetectorImpl : public PrinterDetector, |
@@ -80,155 +73,177 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
public: |
explicit CupsPrinterDetectorImpl(Profile* profile) |
: profile_(profile), observer_(this), weak_ptr_factory_(this) { |
- extensions::ExtensionSystem::Get(profile)->ready().Post( |
- FROM_HERE, base::Bind(&CupsPrinterDetectorImpl::Initialize, |
- weak_ptr_factory_.GetWeakPtr())); |
+ device::UsbService* usb_service = |
+ device::DeviceClient::Get()->GetUsbService(); |
+ if (usb_service) { |
+ observer_.Add(usb_service); |
+ usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ } |
} |
~CupsPrinterDetectorImpl() override = default; |
- void ClickOnNotificationButton(int button_index) { |
- // Remove the old notification first. |
- const ProfileID profile_id = NotificationUIManager::GetProfileID(profile_); |
- g_browser_process->notification_ui_manager()->CancelById( |
- kUSBPrinterFoundNotificationID, profile_id); |
- |
- if (command_ == ButtonCommand::SETUP) { |
- OnSetUpUSBPrinterStarted(); |
- // TODO(skau/xdai): call the CUPS backend to set up the USB printer and |
- // then call OnSetUpPrinterDone() or OnSetUpPrinterError() depending on |
- // the setup result. |
- } else if (command_ == ButtonCommand::CANCEL_SETUP) { |
- // TODO(skau/xdai): call the CUPS backend to cancel the printer setup. |
- } else if (command_ == ButtonCommand::GET_HELP) { |
- chrome::NavigateParams params(profile_, |
- GURL(chrome::kChromeUIMdCupsSettingsURL), |
- ui::PAGE_TRANSITION_LINK); |
- params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
- params.window_action = chrome::NavigateParams::SHOW_WINDOW; |
- chrome::Navigate(¶ms); |
+ private: |
+ // Callback for initial enumeration of usb devices. |
+ void OnGetDevices( |
+ const std::vector<scoped_refptr<device::UsbDevice>>& devices) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ for (const auto& device : devices) { |
+ MaybeSetUpDevice(device, false); |
} |
} |
- private: |
- // Action that should be performed when a notification button is clicked. |
- enum class ButtonCommand { |
- SETUP, |
- CANCEL_SETUP, |
- CLOSE, |
- GET_HELP, |
- }; |
- |
- // UsbService::observer override: |
+ // UsbService::observer override. |
void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { |
- const user_manager::User* user = |
- ProfileHelper::Get()->GetUserByProfile(profile_); |
- // TODO(justincarlson) - See if it's appropriate to relax any of these |
- // constraints. |
- if (!user || !user->HasGaiaAccount() || !user_manager::UserManager::Get() || |
- user != user_manager::UserManager::Get()->GetActiveUser()) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ MaybeSetUpDevice(device, true); |
+ } |
+ |
+ // UsbService::observer override. |
+ void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ if (!UsbDeviceIsPrinter(device)) { |
return; |
} |
+ known_printers_.erase(device->guid()); |
+ // TODO(justincarlson): Update failed printers. |
+ } |
+ |
+ // Returns the existing printer using this URI, if one exists, or |
+ // null otherwise. |
+ std::unique_ptr<Printer> FindExistingPrinter(const std::string& uri) { |
+ // TODO(justincarlson): add a GetPrinterByURI to PrintersManager. |
+ // https://crbug.com/700602 |
+ auto existing_printers = |
+ PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters(); |
+ for (std::unique_ptr<Printer>& printer : existing_printers) { |
+ if (printer->uri() == uri) { |
+ // Found a match, so use the existing configuration. |
+ return std::move(printer); |
+ } |
+ } |
+ return nullptr; |
+ } |
+ |
+ // If this device is a printer and we haven't already tried to set it up, |
+ // starts the process of setting the printer up. |hotplugged| |
+ // should be true if this was plugged in during the session. |
+ void MaybeSetUpDevice(scoped_refptr<device::UsbDevice> device, |
+ bool hotplugged) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- device::UsbDeviceFilter printer_filter; |
- printer_filter.interface_class = kPrinterInterfaceClass; |
- if (!printer_filter.Matches(device)) |
+ if (!UsbDeviceIsPrinter(device) || |
+ base::ContainsKey(known_printers_, device->guid())) { |
return; |
+ } |
+ known_printers_.insert(device->guid()); |
- ShowUSBPrinterSetupNotification(device); |
- } |
+ auto data = base::MakeUnique<SetUpPrinterData>(); |
+ data->configurer = PrinterConfigurer::Create(profile_); |
+ data->device = device; |
+ data->hotplugged = hotplugged; |
- // Initializes the printer detector. |
- void Initialize() { |
- device::UsbService* usb_service = |
- device::DeviceClient::Get()->GetUsbService(); |
- if (!usb_service) |
+ data->printer = FindExistingPrinter(UsbPrinterUri(*device)); |
+ if (data->printer != nullptr) { |
+ data->is_new = false; |
+ OnPrinterResolved(std::move(data)); |
return; |
- observer_.Add(usb_service); |
- } |
+ } |
- void SetNotificationUIManagerForTesting( |
- NotificationUIManager* manager) override { |
- LOG(FATAL) << "Not implemented for CUPS"; |
+ // It's not a device we have configured previously. |
+ // |
+ // TODO(justincarlson): Add a notification that we are attempting to set up |
+ // this printer at this point. |
+ data->is_new = true; |
+ data->printer = UsbDeviceToPrinter(*device); |
+ if (data->printer == nullptr) { |
+ return; |
+ } |
+ |
+ // Look for an exact match based on USB ids. |
+ scoped_refptr<PpdProvider> ppd_provider = |
+ printing::CreateProvider(profile_); |
+ ppd_provider->ResolveUsbIds( |
+ device->vendor_id(), device->product_id(), |
+ base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone, |
+ weak_ptr_factory_.GetWeakPtr(), ppd_provider, |
+ base::Passed(std::move(data)))); |
} |
- void ShowUSBPrinterSetupNotification( |
- scoped_refptr<device::UsbDevice> device) { |
- // TODO(justincarlson) - Test this notification across a wide variety of |
- // less-than-sane printers to make sure the notification text stays as |
- // intelligible as possible. |
- base::string16 printer_name = device->manufacturer_string() + |
- base::UTF8ToUTF16(" ") + |
- device->product_string(); |
- ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
- message_center::RichNotificationData data; |
- data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_BUTTON))); |
- notification_.reset(new Notification( |
- message_center::NOTIFICATION_TYPE_SIMPLE, |
- l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_TITLE), // title |
- printer_name, // body |
- bundle.GetImageNamed(IDR_PRINTER_DETECTED_NOTIFICATION), // icon |
- message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, |
- kUSBPrinterFoundNotificationID), |
- base::string16(), // display_source |
- GURL(), kUSBPrinterFoundNotificationID, data, |
- new USBPrinterSetupNotificationDelegate(this))); |
- notification_->SetSystemPriority(); |
- command_ = ButtonCommand::SETUP; |
- |
- g_browser_process->notification_ui_manager()->Add(*notification_, profile_); |
+ void OnPrinterResolved(std::unique_ptr<SetUpPrinterData> data) { |
+ // |data| will be invalidated by the move below, so we have to latch it |
+ // before the call. |
+ SetUpPrinterData* data_ptr = data.get(); |
+ data_ptr->configurer->SetUpPrinter( |
+ *(data_ptr->printer), |
+ base::Bind(&CupsPrinterDetectorImpl::SetUpPrinterDone, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ base::Passed(std::move(data)))); |
} |
- void OnSetUpUSBPrinterStarted() { |
- notification_->set_title(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_IN_PROGRESS_TITLE)); |
- notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); |
- notification_->set_progress(-1); |
- std::vector<message_center::ButtonInfo> buttons; |
- buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_CANCEL_BUTTON))); |
- notification_->set_buttons(buttons); |
- command_ = ButtonCommand::CANCEL_SETUP; |
- g_browser_process->notification_ui_manager()->Add(*notification_, profile_); |
+ // Called when the query for a driver based on usb ids completes. |
+ // |
+ // Note |provider| is not used in this function, it's just passed along to |
+ // keep it alive during the USB resolution. |
+ void ResolveUsbIdsDone(scoped_refptr<PpdProvider> provider, |
+ std::unique_ptr<SetUpPrinterData> data, |
+ PpdProvider::CallbackResultCode result, |
+ const std::string& effective_make_and_model) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ if (result == PpdProvider::SUCCESS) { |
+ // Got something based on usb ids. Go with it. |
+ data->printer->mutable_ppd_reference()->effective_make_and_model = |
+ effective_make_and_model; |
+ } else { |
+ // Couldn't figure this printer out based on usb ids, fall back to |
+ // guessing the make/model string from what the USB system reports. |
+ // |
+ // TODO(justincarlson): Consider adding a mechanism for aggregating data |
+ // about which usb devices are in the wild but unsupported? |
+ data->printer->mutable_ppd_reference()->effective_make_and_model = |
+ GuessEffectiveMakeAndModel(*data->device); |
+ } |
+ OnPrinterResolved(std::move(data)); |
} |
- void OnSetUpUSBPrinterDone() { |
- notification_->set_title(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_SUCCESS_TITLE)); |
- notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
- std::vector<message_center::ButtonInfo> buttons; |
- buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_CLOSE_BUTTON))); |
- notification_->set_buttons(buttons); |
- command_ = ButtonCommand::CLOSE; |
- g_browser_process->notification_ui_manager()->Add(*notification_, profile_); |
+ // Called with the result of asking to have a printer configured for CUPS. If |
+ // |printer_to_register| is non-null and we successfully configured, then the |
+ // printer is registered with the printers manager. |
+ void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data, |
+ PrinterSetupResult result) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ if (result == PrinterSetupResult::SUCCESS) { |
+ if (data->is_new) { |
+ PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( |
+ std::move(data->printer)); |
+ } |
+ // TODO(justincarlson): If the device was hotplugged, pop a timed |
+ // notification that says the printer is now available for printing. |
+ } |
+ // TODO(justincarlson): If this doesn't succeed, Pop a notification that |
+ // tells the user automatic setup failed and offers to open the CUPS printer |
+ // configuration settings. |
+ // |
+ // TODO(justincarlson): If this doesn't succeed, Update the list of printers |
+ // that failed to set up. |
} |
- void OnSetUpUSBPrinterError() { |
- notification_->set_title(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_FAILED_TITLE)); |
- notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE); |
- std::vector<message_center::ButtonInfo> buttons; |
- buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16( |
- IDS_PRINTER_DETECTED_NOTIFICATION_SET_UP_GET_HELP_BUTTON))); |
- notification_->set_buttons(buttons); |
- command_ = ButtonCommand::GET_HELP; |
- g_browser_process->notification_ui_manager()->Add(*notification_, profile_); |
+ void SetNotificationUIManagerForTesting( |
+ NotificationUIManager* manager) override { |
+ LOG(FATAL) << "Not implemented for CUPS"; |
} |
- std::unique_ptr<Notification> notification_; |
- ButtonCommand command_ = ButtonCommand::SETUP; |
+ // USB GUIDs of printers we've already dealt with. There's an inherent race |
+ // between initially querying all usb devices and receiving a notification |
+ // about a new device, so this set lets us guarantee that we handle a given |
+ // printer exactly once. |
+ std::set<std::string> known_printers_; |
Profile* profile_; |
ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; |
base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; |
}; |
-void USBPrinterSetupNotificationDelegate::ButtonClick(int button_index) { |
- printer_detector_->ClickOnNotificationButton(button_index); |
-} |
- |
} // namespace |
// static |