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 05369a7ae9c694b9f9b8120248c46638bfd17a00..a8b1e8f106d0c9b7418344476ab2192aa19f4c9f 100644 |
--- a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc |
+++ b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc |
@@ -12,8 +12,11 @@ |
#include "base/bind_helpers.h" |
#include "base/macros.h" |
#include "base/memory/ptr_util.h" |
+#include "base/observer_list_threadsafe.h" |
#include "base/scoped_observer.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "base/synchronization/lock.h" |
+#include "base/threading/sequenced_task_runner_handle.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" |
@@ -72,18 +75,48 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
public device::UsbService::Observer { |
public: |
explicit CupsPrinterDetectorImpl(Profile* profile) |
- : profile_(profile), observer_(this), weak_ptr_factory_(this) { |
+ : profile_(profile), |
+ usb_observer_(this), |
+ observer_list_( |
+ new base::ObserverListThreadSafe<PrinterDetector::Observer>), |
+ weak_ptr_factory_(this) { |
device::UsbService* usb_service = |
device::DeviceClient::Get()->GetUsbService(); |
if (usb_service) { |
- observer_.Add(usb_service); |
+ usb_observer_.Add(usb_service); |
usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices, |
weak_ptr_factory_.GetWeakPtr())); |
} |
} |
~CupsPrinterDetectorImpl() override = default; |
+ // PrinterDetector interface function. |
+ void AddObserver(PrinterDetector::Observer* observer) override { |
+ observer_list_->AddObserver(observer); |
+ } |
+ |
+ // PrinterDetector interface function. |
+ void RemoveObserver(PrinterDetector::Observer* observer) override { |
+ observer_list_->RemoveObserver(observer); |
+ } |
+ |
+ // PrinterDetector interface function. |
+ std::vector<Printer> GetPrinters() override { |
+ base::AutoLock auto_lock(pp_lock_); |
+ return GetPrintersLocked(); |
+ } |
+ |
private: |
+ std::vector<Printer> GetPrintersLocked() { |
+ pp_lock_.AssertAcquired(); |
+ std::vector<Printer> printers; |
+ printers.reserve(present_printers_.size()); |
+ for (const auto& entry : present_printers_) { |
+ printers.push_back(*entry.second); |
+ } |
+ return printers; |
+ } |
+ |
// Callback for initial enumeration of usb devices. |
void OnGetDevices( |
const std::vector<scoped_refptr<device::UsbDevice>>& devices) { |
@@ -105,24 +138,21 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
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); |
- } |
+ base::AutoLock auto_lock(pp_lock_); |
+ if (base::ContainsKey(present_printers_, device->guid())) { |
+ present_printers_.erase(device->guid()); |
+ auto printers = GetPrintersLocked(); |
+ // We already have pp_lock_, so need to call the pre-locked version of |
+ // GetPrinters to prevent deadlock. |
+ observer_list_->Notify( |
+ FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, |
+ GetPrintersLocked()); |
+ } else { |
+ // If the device has been removed but it's not in present_printers_, it |
+ // must still be in the setup flow. |
+ deferred_printer_removals_.insert(device->guid()); |
} |
- return nullptr; |
} |
// If this device is a printer and we haven't already tried to set it up, |
@@ -132,20 +162,32 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
bool hotplugged) { |
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- if (!UsbDeviceIsPrinter(*device) || |
- base::ContainsKey(known_printers_, device->guid())) { |
+ if (!UsbDeviceIsPrinter(*device)) { |
return; |
} |
- known_printers_.insert(device->guid()); |
+ // If we got this far, we want to try to auto-configure this printer. |
auto data = base::MakeUnique<SetUpPrinterData>(); |
data->configurer = PrinterConfigurer::Create(profile_); |
data->device = device; |
data->hotplugged = hotplugged; |
- data->printer = FindExistingPrinter(UsbPrinterUri(*device)); |
- if (data->printer != nullptr) { |
+ data->printer = UsbDeviceToPrinter(*device); |
+ if (data->printer == nullptr) { |
+ // We've failed to understand this printer device, an error will already |
+ // have been logged, so just bail. |
+ return; |
+ } |
+ |
+ // If the user already has a configuration for this device, substitute that |
+ // one for the one we generated automatically and skip the parts where we |
+ // try to automagically figure out the driver. |
+ std::unique_ptr<Printer> existing_printer_configuration = |
+ PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter( |
+ data->printer->id()); |
+ if (existing_printer_configuration != nullptr) { |
data->is_new = false; |
+ data->printer = std::move(existing_printer_configuration); |
OnPrinterResolved(std::move(data)); |
return; |
} |
@@ -155,10 +197,6 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
// 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 = |
@@ -214,18 +252,30 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
if (result == PrinterSetupResult::SUCCESS) { |
if (data->is_new) { |
+ // We aren't done with data->printer yet, so we have to copy it instead |
+ // of moving it. |
+ auto printer_copy = base::MakeUnique<Printer>(*data->printer); |
PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( |
- std::move(data->printer)); |
+ std::move(printer_copy)); |
} |
// 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 |
+ // 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. |
+ |
+ if (base::ContainsKey(deferred_printer_removals_, data->device->guid())) { |
+ // The device was removed before we finished the flow, so just don't add |
+ // it to present_printers_; |
+ deferred_printer_removals_.erase(data->device->guid()); |
+ } else { |
+ base::AutoLock auto_lock(pp_lock_); |
+ present_printers_.emplace(data->device->guid(), std::move(data->printer)); |
+ observer_list_->Notify( |
+ FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, |
+ GetPrintersLocked()); |
+ } |
} |
void SetNotificationUIManagerForTesting( |
@@ -233,19 +283,33 @@ class CupsPrinterDetectorImpl : public PrinterDetector, |
LOG(FATAL) << "Not implemented for CUPS"; |
} |
- // 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_; |
+ // Map from USB GUID to Printer that we have detected as being currently |
+ // plugged in and have finished processing. Note present_printers_ may be |
+ // accessed from multiple threads, so is protected by pp_lock_. |
+ std::map<std::string, std::unique_ptr<Printer>> present_printers_; |
+ base::Lock pp_lock_; |
+ |
+ // If the usb device is removed before we've finished processing it, we'll |
+ // defer the cleanup until the setup flow finishes. This is the set of |
+ // guids which have been removed before the flow finished. |
+ std::set<std::string> deferred_printer_removals_; |
Profile* profile_; |
- ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; |
+ ScopedObserver<device::UsbService, device::UsbService::Observer> |
+ usb_observer_; |
+ scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>> |
+ observer_list_; |
base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; |
}; |
} // namespace |
+// Nop base class implementation of GetPrinters(). Because this is non-empty we |
+// have to define it out-of-line. |
+std::vector<Printer> PrinterDetector::GetPrinters() { |
+ return std::vector<Printer>(); |
+} |
+ |
// static |
std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { |
return base::MakeUnique<CupsPrinterDetectorImpl>(profile); |