#include "chromeos/printing/ppd_metadata_manager.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/printing/ppd_metadata_parser.h"
#include "chromeos/printing/ppd_provider.h"
#include "chromeos/printing/printer_config_cache.h"
#include "ppd_metadata_manager.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace chromeos {
namespace {
constexpr int kNumShards = 20;
constexpr int kSixteenBitsMaximum = 0xffff;
template <typename T>
struct ParsedMetadataWithTimestamp {
base::Time time_of_parse;
T value;
};
class ForwardIndexSearchContext {
public:
ForwardIndexSearchContext(
const std::vector<std::string>& emms,
base::Time max_age,
PpdMetadataManager::FindAllEmmsAvailableInIndexCallback cb)
: emms_(emms), current_index_(), max_age_(max_age), cb_(std::move(cb)) {}
~ForwardIndexSearchContext() = default;
ForwardIndexSearchContext(const ForwardIndexSearchContext&) = delete;
ForwardIndexSearchContext& operator=(const ForwardIndexSearchContext&) =
delete;
ForwardIndexSearchContext(ForwardIndexSearchContext&&) = default;
base::StringPiece CurrentEmm() const {
DCHECK_LT(current_index_, emms_.size());
return emms_[current_index_];
}
bool CurrentEmmIsLast() const {
DCHECK_LT(current_index_, emms_.size());
return current_index_ + 1 == emms_.size();
}
void AdvanceToNextEmm() {
DCHECK_LT(current_index_, emms_.size());
current_index_++;
}
void PostCallback() {
DCHECK(CurrentEmmIsLast());
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb_), cb_arg_));
}
void AddDataFromForwardIndexForCurrentEmm(const ParsedIndexValues& value) {
cb_arg_.insert_or_assign(CurrentEmm(), value);
}
base::Time MaxAge() const { return max_age_; }
private:
std::vector<std::string> emms_;
size_t current_index_;
base::Time max_age_;
PpdMetadataManager::FindAllEmmsAvailableInIndexCallback cb_;
base::flat_map<std::string, ParsedIndexValues> cb_arg_;
};
class ForwardIndexSearchQueue {
public:
ForwardIndexSearchQueue() = default;
~ForwardIndexSearchQueue() = default;
ForwardIndexSearchQueue(const ForwardIndexSearchQueue&) = delete;
ForwardIndexSearchQueue& operator=(const ForwardIndexSearchQueue&) = delete;
void Enqueue(ForwardIndexSearchContext context) {
contexts_.push(std::move(context));
}
bool IsIdle() const { return contexts_.empty(); }
ForwardIndexSearchContext& CurrentContext() {
DCHECK(!IsIdle());
return contexts_.front();
}
void AdvanceToNextEmm() {
DCHECK(!IsIdle());
if (CurrentContext().CurrentEmmIsLast()) {
CurrentContext().PostCallback();
contexts_.pop();
} else {
CurrentContext().AdvanceToNextEmm();
}
}
private:
base::queue<ForwardIndexSearchContext> contexts_;
};
template <typename T>
using CachedParsedMetadataMap =
base::flat_map<std::string, ParsedMetadataWithTimestamp<T>>;
template <typename T>
bool MapHasValueFresherThan(const CachedParsedMetadataMap<T>& metadata_map,
base::StringPiece key,
base::Time expiration) {
if (!metadata_map.contains(key)) {
return false;
}
const auto& value = metadata_map.at(key);
return value.time_of_parse > expiration;
}
int IndexShard(base::StringPiece key) {
unsigned int hash = 5381;
for (char c : key) {
hash = hash * 33 + c;
}
return hash % kNumShards;
}
class MetadataLocaleFinder {
public:
explicit MetadataLocaleFinder(const std::string& browser_locale)
: browser_locale_(browser_locale),
browser_locale_pieces_(base::SplitStringPiece(browser_locale,
"-",
base::KEEP_WHITESPACE,
base::SPLIT_WANT_ALL)),
is_english_available_(false) {}
~MetadataLocaleFinder() = default;
MetadataLocaleFinder(const MetadataLocaleFinder&) = delete;
MetadataLocaleFinder& operator=(const MetadataLocaleFinder&) = delete;
base::StringPiece BestCandidate(base::span<const std::string> locales) {
AnalyzeCandidates(locales);
if (!best_parent_locale_.empty()) {
return best_parent_locale_;
} else if (!best_distant_relative_locale_.empty()) {
return best_distant_relative_locale_;
} else if (is_english_available_) {
return "en";
}
return base::StringPiece();
}
private:
bool IsParentOfBrowserLocale(base::StringPiece locale) const {
const std::string locale_with_trailing_hyphen = base::StrCat({locale, "-"});
return base::StartsWith(browser_locale_, locale_with_trailing_hyphen);
}
void AnalyzeCandidateAsDistantRelative(base::StringPiece locale) {
const std::vector<base::StringPiece> locale_pieces = base::SplitStringPiece(
locale, "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
const size_t locale_piecewise_length = locale_pieces.size();
const size_t iter_limit =
std::min(browser_locale_pieces_.size(), locale_piecewise_length);
size_t locale_piecewise_match_length = 0;
for (; locale_piecewise_match_length < iter_limit;
locale_piecewise_match_length++) {
if (locale_pieces[locale_piecewise_match_length] !=
browser_locale_pieces_[locale_piecewise_match_length]) {
break;
}
}
if (locale_piecewise_match_length == 0) {
return;
} else if (locale_piecewise_match_length >
best_distant_relative_locale_piecewise_match_length_ ||
(locale_piecewise_match_length ==
best_distant_relative_locale_piecewise_match_length_ &&
locale_piecewise_length <
best_distant_relative_locale_piecewise_length_)) {
best_distant_relative_locale_ = std::string(locale);
best_distant_relative_locale_piecewise_match_length_ =
locale_piecewise_match_length;
best_distant_relative_locale_piecewise_length_ = locale_piecewise_length;
}
}
void AnalyzeCandidate(base::StringPiece locale) {
if (locale == "en") {
is_english_available_ = true;
}
if (IsParentOfBrowserLocale(locale) &&
locale.size() > best_parent_locale_.size()) {
best_parent_locale_ = std::string(locale);
} else if (best_parent_locale_.empty()) {
AnalyzeCandidateAsDistantRelative(locale);
}
}
void AnalyzeCandidates(base::span<const std::string> locales) {
for (base::StringPiece locale : locales) {
if (locale == browser_locale_) {
best_parent_locale_ = std::string(browser_locale_);
return;
}
AnalyzeCandidate(locale);
}
}
const base::StringPiece browser_locale_;
const std::vector<base::StringPiece> browser_locale_pieces_;
std::string best_parent_locale_;
std::string best_distant_relative_locale_;
size_t best_distant_relative_locale_piecewise_match_length_;
size_t best_distant_relative_locale_piecewise_length_;
bool is_english_available_;
};
class PpdMetadataPathSpecifier {
public:
enum class Type {
kLocales,
kManufacturers,
kPrinters,
kForwardIndex,
kReverseIndex,
kUsbIndex,
kUsbVendorIds,
};
PpdMetadataPathSpecifier(Type type, PpdIndexChannel channel)
: type_(type),
channel_(channel),
printers_basename_(nullptr),
metadata_locale_(nullptr),
shard_(0),
usb_vendor_id_(0) {}
~PpdMetadataPathSpecifier() = default;
PpdMetadataPathSpecifier(const PpdMetadataPathSpecifier&) = delete;
PpdMetadataPathSpecifier& operator=(const PpdMetadataPathSpecifier&) = delete;
void SetPrintersBasename(const char* const basename) {
DCHECK_EQ(type_, Type::kPrinters);
printers_basename_ = basename;
}
void SetMetadataLocale(const char* const locale) {
DCHECK(type_ == Type::kManufacturers || type_ == Type::kReverseIndex);
metadata_locale_ = locale;
}
void SetUsbVendorId(const int vendor_id) {
DCHECK_EQ(type_, Type::kUsbIndex);
usb_vendor_id_ = vendor_id;
}
void SetShard(const int shard) {
DCHECK(type_ == Type::kForwardIndex || type_ == Type::kReverseIndex);
shard_ = shard;
}
std::string AsString() const {
switch (type_) {
case Type::kLocales:
return base::StringPrintf("%s/locales.json", MetadataParentDirectory());
case Type::kManufacturers:
DCHECK(metadata_locale_);
DCHECK(!base::StringPiece(metadata_locale_).empty());
return base::StringPrintf("%s/manufacturers-%s.json",
MetadataParentDirectory(), metadata_locale_);
case Type::kPrinters:
DCHECK(printers_basename_);
DCHECK(!base::StringPiece(printers_basename_).empty());
return base::StringPrintf("%s/%s", MetadataParentDirectory(),
printers_basename_);
case Type::kForwardIndex:
DCHECK(shard_ >= 0 && shard_ < kNumShards);
return base::StringPrintf("%s/index-%02d.json",
MetadataParentDirectory(), shard_);
case Type::kReverseIndex:
DCHECK(metadata_locale_);
DCHECK(!base::StringPiece(metadata_locale_).empty());
DCHECK(shard_ >= 0 && shard_ < kNumShards);
return base::StringPrintf("%s/reverse_index-%s-%02d.json",
MetadataParentDirectory(), metadata_locale_,
shard_);
case Type::kUsbIndex:
DCHECK(usb_vendor_id_ >= 0 && usb_vendor_id_ <= kSixteenBitsMaximum);
return base::StringPrintf("%s/usb-%04x.json", MetadataParentDirectory(),
usb_vendor_id_);
case Type::kUsbVendorIds:
return base::StringPrintf("%s/usb_vendor_ids.json",
MetadataParentDirectory());
}
NOTREACHED();
return std::string();
}
private:
const char* MetadataParentDirectory() const {
switch (channel_) {
case PpdIndexChannel::kLocalhost:
[[fallthrough]];
case PpdIndexChannel::kProduction:
return "metadata_v3";
case PpdIndexChannel::kStaging:
return "metadata_v3_staging";
case PpdIndexChannel::kDev:
return "metadata_v3_dev";
}
}
Type type_;
const PpdIndexChannel channel_;
const char* printers_basename_;
const char* metadata_locale_;
int shard_;
int usb_vendor_id_;
};
class PpdMetadataManagerImpl : public PpdMetadataManager {
public:
PpdMetadataManagerImpl(base::StringPiece browser_locale,
PpdIndexChannel channel,
base::Clock* clock,
std::unique_ptr<PrinterConfigCache> config_cache)
: browser_locale_(browser_locale),
channel_(channel),
clock_(clock),
config_cache_(std::move(config_cache)),
weak_factory_(this) {}
~PpdMetadataManagerImpl() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void GetLocale(GetLocaleCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!metadata_locale_.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), true));
return;
}
PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kLocales,
channel_);
const std::string metadata_name = path.AsString();
PrinterConfigCache::FetchCallback fetch_cb =
base::BindOnce(&PpdMetadataManagerImpl::OnLocalesFetched,
weak_factory_.GetWeakPtr(), std::move(cb));
config_cache_->Fetch(metadata_name, base::TimeDelta(), std::move(fetch_cb));
}
void GetManufacturers(base::TimeDelta age,
PpdProvider::ResolveManufacturersCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!metadata_locale_.empty());
PpdMetadataPathSpecifier path(
PpdMetadataPathSpecifier::Type::kManufacturers, channel_);
path.SetMetadataLocale(metadata_locale_.c_str());
const std::string metadata_name = path.AsString();
if (MapHasValueFresherThan(cached_manufacturers_, metadata_name,
clock_->Now() - age)) {
OnManufacturersAvailable(metadata_name, std::move(cb));
return;
}
PrinterConfigCache::FetchCallback fetch_cb =
base::BindOnce(&PpdMetadataManagerImpl::OnManufacturersFetched,
weak_factory_.GetWeakPtr(), std::move(cb));
config_cache_->Fetch(metadata_name, age, std::move(fetch_cb));
}
void GetPrinters(base::StringPiece manufacturer,
base::TimeDelta age,
GetPrintersCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!metadata_locale_.empty());
const auto metadata_name = GetPrintersMetadataName(manufacturer);
if (!metadata_name.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), false, ParsedPrinters{}));
return;
}
if (MapHasValueFresherThan(cached_printers_, metadata_name.value(),
clock_->Now() - age)) {
OnPrintersAvailable(metadata_name.value(), std::move(cb));
return;
}
PrinterConfigCache::FetchCallback fetch_cb =
base::BindOnce(&PpdMetadataManagerImpl::OnPrintersFetched,
weak_factory_.GetWeakPtr(), std::move(cb));
config_cache_->Fetch(metadata_name.value(), age, std::move(fetch_cb));
}
void FindAllEmmsAvailableInIndex(
const std::vector<std::string>& emms,
base::TimeDelta age,
FindAllEmmsAvailableInIndexCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ForwardIndexSearchContext context(emms, clock_->Now() - age, std::move(cb));
bool queue_was_idle = forward_index_search_queue_.IsIdle();
forward_index_search_queue_.Enqueue(std::move(context));
if (queue_was_idle) {
ContinueSearchingForwardIndices();
}
}
void FindDeviceInUsbIndex(int vendor_id,
int product_id,
base::TimeDelta age,
FindDeviceInUsbIndexCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (vendor_id < 0 || vendor_id > kSixteenBitsMaximum || product_id < 0 ||
product_id > kSixteenBitsMaximum) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), std::string()));
return;
}
PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kUsbIndex,
channel_);
path.SetUsbVendorId(vendor_id);
const std::string metadata_name = path.AsString();
if (MapHasValueFresherThan(cached_usb_indices_, metadata_name,
clock_->Now() - age)) {
OnUsbIndexAvailable(metadata_name, product_id, std::move(cb));
return;
}
auto callback = base::BindOnce(&PpdMetadataManagerImpl::OnUsbIndexFetched,
weak_factory_.GetWeakPtr(), metadata_name,
product_id, std::move(cb));
config_cache_->Fetch(metadata_name, age, std::move(callback));
}
void GetUsbManufacturerName(int vendor_id,
base::TimeDelta age,
GetUsbManufacturerNameCallback cb) override {
PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kUsbVendorIds,
channel_);
const std::string metadata_name = path.AsString();
if (MapHasValueFresherThan(cached_usb_vendor_id_map_, metadata_name,
clock_->Now() - age)) {
OnUsbVendorIdMapAvailable(metadata_name, vendor_id, std::move(cb));
return;
}
auto fetch_cb =
base::BindOnce(&PpdMetadataManagerImpl::OnUsbVendorIdMapFetched,
weak_factory_.GetWeakPtr(), vendor_id, std::move(cb));
config_cache_->Fetch(metadata_name, age, std::move(fetch_cb));
}
void SplitMakeAndModel(base::StringPiece effective_make_and_model,
base::TimeDelta age,
PpdProvider::ReverseLookupCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!metadata_locale_.empty());
PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kReverseIndex,
channel_);
path.SetMetadataLocale(metadata_locale_.c_str());
path.SetShard(IndexShard(effective_make_and_model));
const std::string metadata_name = path.AsString();
if (MapHasValueFresherThan(cached_reverse_indices_, metadata_name,
clock_->Now() - age)) {
OnReverseIndexAvailable(metadata_name, effective_make_and_model,
std::move(cb));
return;
}
PrinterConfigCache::FetchCallback fetch_cb =
base::BindOnce(&PpdMetadataManagerImpl::OnReverseIndexFetched,
weak_factory_.GetWeakPtr(),
std::string(effective_make_and_model), std::move(cb));
config_cache_->Fetch(metadata_name, age, std::move(fetch_cb));
}
PrinterConfigCache* GetPrinterConfigCacheForTesting() const override {
return config_cache_.get();
}
void SetLocaleForTesting(base::StringPiece locale) override {
metadata_locale_ = std::string(locale);
}
bool SetManufacturersForTesting(
base::StringPiece manufacturers_json) override {
DCHECK(!metadata_locale_.empty());
const auto parsed = ParseManufacturers(manufacturers_json);
if (!parsed.has_value()) {
return false;
}
PpdMetadataPathSpecifier path(
PpdMetadataPathSpecifier::Type::kManufacturers, channel_);
path.SetMetadataLocale(metadata_locale_.c_str());
const std::string manufacturers_name = path.AsString();
ParsedMetadataWithTimestamp<ParsedManufacturers> value = {clock_->Now(),
parsed.value()};
cached_manufacturers_.insert_or_assign(manufacturers_name, value);
return true;
}
base::StringPiece ExposeMetadataLocaleForTesting() const override {
return metadata_locale_;
}
private:
enum class ForwardIndexSearchStatus {
kWillResumeOnFetchCompletion,
kCanContinue,
};
bool SetMetadataLocale(const std::vector<std::string>& locales_list) {
MetadataLocaleFinder locale_finder(browser_locale_);
metadata_locale_ = std::string(locale_finder.BestCandidate(locales_list));
return !metadata_locale_.empty();
}
void OnLocalesFetched(GetLocaleCallback cb,
const PrinterConfigCache::FetchResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!result.succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), false));
return;
}
const auto parsed = ParseLocales(result.contents);
if (!parsed.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), false));
return;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), SetMetadataLocale(parsed.value())));
}
void OnManufacturersAvailable(base::StringPiece metadata_name,
PpdProvider::ResolveManufacturersCallback cb) {
const auto& parsed_manufacturers = cached_manufacturers_.at(metadata_name);
std::vector<std::string> manufacturers_for_cb;
for (const auto& iter : parsed_manufacturers.value) {
manufacturers_for_cb.push_back(iter.first);
}
std::sort(manufacturers_for_cb.begin(), manufacturers_for_cb.end());
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), PpdProvider::CallbackResultCode::SUCCESS,
manufacturers_for_cb));
}
void OnManufacturersFetched(PpdProvider::ResolveManufacturersCallback cb,
const PrinterConfigCache::FetchResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!result.succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb),
PpdProvider::CallbackResultCode::SERVER_ERROR,
std::vector<std::string>{}));
return;
}
const auto parsed = ParseManufacturers(result.contents);
if (!parsed.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb),
PpdProvider::CallbackResultCode::INTERNAL_ERROR,
std::vector<std::string>{}));
return;
}
ParsedMetadataWithTimestamp<ParsedManufacturers> value = {clock_->Now(),
parsed.value()};
cached_manufacturers_.insert_or_assign(result.key, value);
OnManufacturersAvailable(result.key, std::move(cb));
}
absl::optional<std::string> GetPrintersMetadataName(
base::StringPiece manufacturer) {
PpdMetadataPathSpecifier manufacturers_path(
PpdMetadataPathSpecifier::Type::kManufacturers, channel_);
manufacturers_path.SetMetadataLocale(metadata_locale_.c_str());
const std::string manufacturers_metadata_name =
manufacturers_path.AsString();
if (!cached_manufacturers_.contains(manufacturers_metadata_name)) {
return absl::nullopt;
}
const ParsedMetadataWithTimestamp<ParsedManufacturers>& manufacturers =
cached_manufacturers_.at(manufacturers_metadata_name);
if (!manufacturers.value.contains(manufacturer)) {
return absl::nullopt;
}
PpdMetadataPathSpecifier printers_path(
PpdMetadataPathSpecifier::Type::kPrinters, channel_);
printers_path.SetPrintersBasename(
manufacturers.value.at(manufacturer).c_str());
return printers_path.AsString();
}
void OnPrintersAvailable(base::StringPiece metadata_name,
GetPrintersCallback cb) {
const auto& parsed_printers = cached_printers_.at(metadata_name);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), true, parsed_printers.value));
}
void OnPrintersFetched(GetPrintersCallback cb,
const PrinterConfigCache::FetchResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!result.succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), false, ParsedPrinters{}));
return;
}
const auto parsed = ParsePrinters(result.contents);
if (!parsed.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), false, ParsedPrinters{}));
return;
}
ParsedMetadataWithTimestamp<ParsedPrinters> value = {clock_->Now(),
parsed.value()};
cached_printers_.insert_or_assign(result.key, value);
OnPrintersAvailable(result.key, std::move(cb));
}
void FindEmmInForwardIndex(base::StringPiece metadata_name) {
DCHECK(cached_forward_indices_.contains(metadata_name));
ForwardIndexSearchContext& context =
forward_index_search_queue_.CurrentContext();
const ParsedIndex& index = cached_forward_indices_.at(metadata_name).value;
const auto& iter = index.find(context.CurrentEmm());
if (iter != index.end()) {
context.AddDataFromForwardIndexForCurrentEmm(iter->second);
}
forward_index_search_queue_.AdvanceToNextEmm();
}
void OnForwardIndexFetched(const PrinterConfigCache::FetchResult& result) {
if (!result.succeeded) {
forward_index_search_queue_.AdvanceToNextEmm();
ContinueSearchingForwardIndices();
return;
}
const auto parsed = ParseForwardIndex(result.contents);
if (!parsed.has_value()) {
forward_index_search_queue_.AdvanceToNextEmm();
ContinueSearchingForwardIndices();
return;
}
ParsedMetadataWithTimestamp<ParsedIndex> value = {clock_->Now(),
parsed.value()};
cached_forward_indices_.insert_or_assign(result.key, value);
ContinueSearchingForwardIndices();
}
ForwardIndexSearchStatus SearchForwardIndicesForOneEmm() {
const ForwardIndexSearchContext& context =
forward_index_search_queue_.CurrentContext();
PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kForwardIndex,
channel_);
path.SetShard(IndexShard(context.CurrentEmm()));
const std::string forward_index_name = path.AsString();
if (MapHasValueFresherThan(cached_forward_indices_, forward_index_name,
context.MaxAge())) {
FindEmmInForwardIndex(forward_index_name);
return ForwardIndexSearchStatus::kCanContinue;
}
auto callback =
base::BindOnce(&PpdMetadataManagerImpl::OnForwardIndexFetched,
weak_factory_.GetWeakPtr());
config_cache_->Fetch(forward_index_name, clock_->Now() - context.MaxAge(),
std::move(callback));
return ForwardIndexSearchStatus::kWillResumeOnFetchCompletion;
}
void ContinueSearchingForwardIndices() {
while (!forward_index_search_queue_.IsIdle()) {
ForwardIndexSearchStatus status = SearchForwardIndicesForOneEmm();
if (status == ForwardIndexSearchStatus::kWillResumeOnFetchCompletion) {
break;
}
}
}
void OnUsbIndexAvailable(base::StringPiece metadata_name,
int product_id,
FindDeviceInUsbIndexCallback cb) {
DCHECK(cached_usb_indices_.contains(metadata_name));
const ParsedUsbIndex& usb_index =
cached_usb_indices_.at(metadata_name).value;
const auto& iter = usb_index.find(product_id);
std::string effective_make_and_model;
if (iter != usb_index.end()) {
effective_make_and_model = iter->second;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), std::move(effective_make_and_model)));
}
void OnUsbIndexFetched(std::string metadata_name,
int product_id,
FindDeviceInUsbIndexCallback cb,
const PrinterConfigCache::FetchResult& fetch_result) {
if (!fetch_result.succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), std::string()));
return;
}
absl::optional<ParsedUsbIndex> parsed =
ParseUsbIndex(fetch_result.contents);
if (!parsed.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), std::string()));
return;
}
DCHECK(fetch_result.key == metadata_name);
ParsedMetadataWithTimestamp<ParsedUsbIndex> value = {clock_->Now(),
parsed.value()};
cached_usb_indices_.insert_or_assign(fetch_result.key, value);
OnUsbIndexAvailable(fetch_result.key, product_id, std::move(cb));
}
void OnUsbVendorIdMapAvailable(base::StringPiece metadata_name,
int vendor_id,
GetUsbManufacturerNameCallback cb) {
DCHECK(cached_usb_vendor_id_map_.contains(metadata_name));
ParsedUsbVendorIdMap usb_vendor_id_map =
cached_usb_vendor_id_map_.at(metadata_name).value;
std::string manufacturer_name;
const auto& iter = usb_vendor_id_map.find(vendor_id);
if (iter != usb_vendor_id_map.end()) {
manufacturer_name = iter->second;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), manufacturer_name));
}
void OnUsbVendorIdMapFetched(
int vendor_id,
GetUsbManufacturerNameCallback cb,
const PrinterConfigCache::FetchResult& fetch_result) {
if (!fetch_result.succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), std::string()));
return;
}
const absl::optional<ParsedUsbVendorIdMap> parsed =
ParseUsbVendorIdMap(fetch_result.contents);
if (!parsed.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), std::string()));
return;
}
ParsedMetadataWithTimestamp<ParsedUsbVendorIdMap> value = {clock_->Now(),
parsed.value()};
cached_usb_vendor_id_map_.insert_or_assign(fetch_result.key, value);
OnUsbVendorIdMapAvailable(fetch_result.key, vendor_id, std::move(cb));
}
void OnReverseIndexAvailable(base::StringPiece metadata_name,
base::StringPiece effective_make_and_model,
PpdProvider::ReverseLookupCallback cb) {
const auto& parsed_reverse_index =
cached_reverse_indices_.at(metadata_name);
if (!parsed_reverse_index.value.contains(effective_make_and_model)) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb),
PpdProvider::CallbackResultCode::NOT_FOUND, "", ""));
return;
}
const ReverseIndexLeaf& leaf =
parsed_reverse_index.value.at(effective_make_and_model);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), PpdProvider::CallbackResultCode::SUCCESS,
leaf.manufacturer, leaf.model));
}
void OnReverseIndexFetched(std::string effective_make_and_model,
PpdProvider::ReverseLookupCallback cb,
const PrinterConfigCache::FetchResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!result.succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb),
PpdProvider::CallbackResultCode::SERVER_ERROR, "",
""));
return;
}
const auto parsed = ParseReverseIndex(result.contents);
if (!parsed.has_value()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb),
PpdProvider::CallbackResultCode::INTERNAL_ERROR, "",
""));
return;
}
ParsedMetadataWithTimestamp<ParsedReverseIndex> value = {clock_->Now(),
parsed.value()};
cached_reverse_indices_.insert_or_assign(result.key, value);
OnReverseIndexAvailable(result.key, effective_make_and_model,
std::move(cb));
}
const std::string browser_locale_;
const PpdIndexChannel channel_;
raw_ptr<const base::Clock> clock_;
std::string metadata_locale_;
std::unique_ptr<PrinterConfigCache> config_cache_;
CachedParsedMetadataMap<ParsedManufacturers> cached_manufacturers_;
CachedParsedMetadataMap<ParsedPrinters> cached_printers_;
CachedParsedMetadataMap<ParsedIndex> cached_forward_indices_;
CachedParsedMetadataMap<ParsedUsbIndex> cached_usb_indices_;
CachedParsedMetadataMap<ParsedUsbVendorIdMap> cached_usb_vendor_id_map_;
CachedParsedMetadataMap<ParsedReverseIndex> cached_reverse_indices_;
ForwardIndexSearchQueue forward_index_search_queue_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<PpdMetadataManagerImpl> weak_factory_;
};
}
std::unique_ptr<PpdMetadataManager> PpdMetadataManager::Create(
base::StringPiece browser_locale,
PpdIndexChannel channel,
base::Clock* clock,
std::unique_ptr<PrinterConfigCache> config_cache) {
return std::make_unique<PpdMetadataManagerImpl>(
browser_locale, channel, clock, std::move(config_cache));
}
}