blob: 0cded68079160126c92c84aca9ca513070d2de05 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/printing/printers_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/md5.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/values.h"
#include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
#include "chrome/browser/chromeos/printing/specifics_translation.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "chromeos/printing/printer_configuration.h"
#include "chromeos/printing/printer_translator.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
namespace chromeos {
namespace {
// Adds |printer| with |id| to prefs. Returns true if the printer is new,
// false for an update.
bool UpdatePrinterPref(PrintersSyncBridge* sync_bridge,
const std::string& id,
const Printer& printer) {
base::Optional<sync_pb::PrinterSpecifics> specifics =
sync_bridge->GetPrinter(id);
if (!specifics.has_value()) {
sync_bridge->AddPrinter(printing::PrinterToSpecifics(printer));
return true;
}
// Preserve fields in the proto which we don't understand.
std::unique_ptr<sync_pb::PrinterSpecifics> updated_printer =
base::MakeUnique<sync_pb::PrinterSpecifics>(*specifics);
printing::MergePrinterToSpecifics(printer, updated_printer.get());
sync_bridge->AddPrinter(std::move(updated_printer));
return false;
}
} // anonymous namespace
PrintersManager::PrintersManager(
Profile* profile,
std::unique_ptr<PrintersSyncBridge> sync_bridge)
: profile_(profile), sync_bridge_(std::move(sync_bridge)) {
pref_change_registrar_.Init(profile->GetPrefs());
pref_change_registrar_.Add(
prefs::kRecommendedNativePrinters,
base::Bind(&PrintersManager::UpdateRecommendedPrinters,
base::Unretained(this)));
UpdateRecommendedPrinters();
}
PrintersManager::~PrintersManager() {}
// static
void PrintersManager::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterListPref(prefs::kPrintingDevices,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterListPref(prefs::kRecommendedNativePrinters);
}
std::vector<std::unique_ptr<Printer>> PrintersManager::GetPrinters() const {
std::vector<std::unique_ptr<Printer>> printers;
std::vector<sync_pb::PrinterSpecifics> values =
sync_bridge_->GetAllPrinters();
for (const auto& value : values) {
printers.push_back(printing::SpecificsToPrinter(value));
}
return printers;
}
std::vector<std::unique_ptr<Printer>> PrintersManager::GetRecommendedPrinters()
const {
std::vector<std::unique_ptr<Printer>> printers;
for (const std::string& key : recommended_printer_ids_) {
const base::DictionaryValue& printer_dictionary =
*(recommended_printers_.at(key));
std::unique_ptr<Printer> printer =
printing::RecommendedPrinterToPrinter(printer_dictionary);
if (printer) {
printer->set_source(Printer::SRC_POLICY);
printers.push_back(std::move(printer));
}
}
return printers;
}
std::unique_ptr<Printer> PrintersManager::GetPrinter(
const std::string& printer_id) const {
// check for a policy printer first
const auto& policy_printers = recommended_printers_;
auto found = policy_printers.find(printer_id);
if (found != policy_printers.end())
return printing::RecommendedPrinterToPrinter(*(found->second));
base::Optional<sync_pb::PrinterSpecifics> printer =
sync_bridge_->GetPrinter(printer_id);
return printer.has_value() ? printing::SpecificsToPrinter(*printer) : nullptr;
}
void PrintersManager::RegisterPrinter(std::unique_ptr<Printer> printer) {
if (printer->id().empty()) {
printer->set_id(base::GenerateGUID());
}
DCHECK_EQ(Printer::SRC_USER_PREFS, printer->source());
bool new_printer =
UpdatePrinterPref(sync_bridge_.get(), printer->id(), *printer);
if (new_printer) {
for (Observer& obs : observers_) {
obs.OnPrinterAdded(*printer);
}
} else {
for (Observer& obs : observers_) {
obs.OnPrinterUpdated(*printer);
}
}
}
bool PrintersManager::RemovePrinter(const std::string& printer_id) {
DCHECK(!printer_id.empty());
base::Optional<sync_pb::PrinterSpecifics> printer =
sync_bridge_->GetPrinter(printer_id);
bool success = false;
if (printer.has_value()) {
std::unique_ptr<Printer> p = printing::SpecificsToPrinter(*printer);
success = sync_bridge_->RemovePrinter(p->id());
if (success) {
for (Observer& obs : observers_) {
obs.OnPrinterRemoved(*p);
}
}
} else {
LOG(WARNING) << "Could not find printer" << printer_id;
}
return success;
}
void PrintersManager::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void PrintersManager::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
PrintersSyncBridge* PrintersManager::GetSyncBridge() {
return sync_bridge_.get();
}
// This method is not thread safe and could interact poorly with readers of
// |recommended_printers_|.
void PrintersManager::UpdateRecommendedPrinters() {
const PrefService* prefs = profile_->GetPrefs();
const base::ListValue* values =
prefs->GetList(prefs::kRecommendedNativePrinters);
recommended_printer_ids_.clear();
for (const auto& value : *values) {
std::string printer_json;
if (!value->GetAsString(&printer_json)) {
NOTREACHED();
continue;
}
std::unique_ptr<base::DictionaryValue> printer_dictionary =
base::DictionaryValue::From(base::JSONReader::Read(
printer_json, base::JSON_ALLOW_TRAILING_COMMAS));
if (!printer_dictionary) {
LOG(WARNING) << "Ignoring invalid printer. Invalid JSON object: "
<< printer_json;
continue;
}
// Policy printers don't have id's but the ids only need to be locally
// unique so we'll hash the record. This will not collide with the UUIDs
// generated for user entries.
std::string id = base::MD5String(printer_json);
printer_dictionary->SetString(printing::kPrinterId, id);
recommended_printer_ids_.push_back(id);
recommended_printers_[id] = std::move(printer_dictionary);
}
}
} // namespace chromeos