1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174 |
- // Copyright 2020 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 "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/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 "third_party/abseil-cpp/absl/types/optional.h"
- namespace chromeos {
- namespace {
- // Defines the containing directory of all metadata in the serving root.
- const char kMetadataParentDirectory[] = "metadata_v3";
- // Defines the number of shards of sharded metadata.
- constexpr int kNumShards = 20;
- // Defines the magic maximal number for USB vendor IDs and product IDs
- // (restricted to 16 bits).
- constexpr int kSixteenBitsMaximum = 0xffff;
- // Convenience struct containing parsed metadata of type T.
- template <typename T>
- struct ParsedMetadataWithTimestamp {
- base::Time time_of_parse;
- T value;
- };
- // Tracks the progress of a single call to
- // PpdMetadataManager::FindAllEmmsAvailableInIndex().
- 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;
- // The effective-make-and-model string currently being sought in the
- // forward index search tracked by this struct.
- base::StringPiece CurrentEmm() const {
- DCHECK_LT(current_index_, emms_.size());
- return emms_[current_index_];
- }
- // Returns whether the CurrentEmm() is the last one in |this|
- // that needs searching.
- 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_++;
- }
- // Called when the PpdMetadataManager has searched all appropriate
- // forward index metadata for all |emms_|.
- void PostCallback() {
- DCHECK(CurrentEmmIsLast());
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb_), cb_arg_));
- }
- // Called when the PpdMetadataManager successfully maps the
- // CurrentEmm() to a ParsedIndexValues struct.
- void AddDataFromForwardIndexForCurrentEmm(const ParsedIndexValues& value) {
- cb_arg_.insert_or_assign(CurrentEmm(), value);
- }
- base::Time MaxAge() const { return max_age_; }
- private:
- // List of all effective-make-and-model strings that caller gave to
- // PpdMetadataManager::FindAllEmmsAvailableInIndex().
- std::vector<std::string> emms_;
- // Index into |emms| that marks the effective-make-and-model string
- // currently being searched.
- size_t current_index_;
- // Freshness requirement for forward indices that this search reads.
- base::Time max_age_;
- // Callback that caller gave to
- // PpdMetadataManager::FindAllEmmsAvailableInIndex().
- PpdMetadataManager::FindAllEmmsAvailableInIndexCallback cb_;
- // Accrues data to pass to |cb|.
- base::flat_map<std::string, ParsedIndexValues> cb_arg_;
- };
- // Enqueues calls to PpdMetadataManager::FindAllEmmsAvailableInIndex().
- 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();
- }
- // Progresses the frontmost search context, advancing it to its
- // next effective-make-and-model string to find in forward index
- // metadata.
- //
- // If the frontmost search context has no more
- // effective-make-and-model strings to search, then
- // 1. its callback is posted from here and
- // 2. it is popped off the |contexts| queue.
- void AdvanceToNextEmm() {
- DCHECK(!IsIdle());
- if (CurrentContext().CurrentEmmIsLast()) {
- CurrentContext().PostCallback();
- contexts_.pop();
- } else {
- CurrentContext().AdvanceToNextEmm();
- }
- }
- private:
- base::queue<ForwardIndexSearchContext> contexts_;
- };
- // Maps parsed metadata by name to parsed contents.
- //
- // Implementation note: the keys (metadata names) used here are
- // basenames attached to their containing directory - e.g.
- // * "metadata_v3/index-00.json"
- // * "metadata_v3/locales.json"
- // This is done to match up with the PrinterConfigCache class and
- // with the folder layout of the Chrome OS Printing serving root.
- template <typename T>
- using CachedParsedMetadataMap =
- base::flat_map<std::string, ParsedMetadataWithTimestamp<T>>;
- // Returns whether |map| has a value for |key| fresher than
- // |expiration|.
- 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;
- }
- // Calculates the shard number of |key| inside sharded metadata.
- int IndexShard(base::StringPiece key) {
- unsigned int hash = 5381;
- for (char c : key) {
- hash = hash * 33 + c;
- }
- return hash % kNumShards;
- }
- // Helper class used by PpdMetadataManagerImpl::SetMetadataLocale().
- // Sifts through the list of locales advertised by the Chrome OS
- // Printing serving root and selects the best match for a
- // particular browser locale.
- //
- // This class must not outlive any data it is fed.
- // This class is neither copyable nor movable.
- 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;
- // Finds and returns the best-fit metadata locale from |locales|.
- // Returns the empty string if no best candidate was found.
- 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:
- // Returns whether or not |locale| appears to be a parent of our
- // |browser_locale_|. For example, "en-GB" is a parent of "en-GB-foo."
- 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);
- }
- // Updates our |best_distant_relative_locale_| to |locale| if we find
- // that it's a better match.
- //
- // The best distant relative locale is the one that
- // * has the longest piecewise match with |browser_locale_| but
- // * has the shortest piecewise length.
- // So given a |browser_locale_| "es," the better distant relative
- // locale between "es-GB" and "es-GB-foo" is "es-GB."
- 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;
- }
- }
- // Reads |locale| and updates our members as necessary.
- // For example, |locale| could reveal support for the "en" locale.
- 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()) {
- // We need only track distant relative locales if we don't have a
- // |best_parent_locale_|, which is always a better choice.
- AnalyzeCandidateAsDistantRelative(locale);
- }
- }
- // Analyzes all candidate locales in |locales|, updating our
- // private members with best-fit locale(s).
- void AnalyzeCandidates(base::span<const std::string> locales) {
- for (base::StringPiece locale : locales) {
- // The serving root indicates direct support for our browser
- // locale; there's no need to analyze anything else, since this
- // is definitely the best match we're going to get.
- 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_;
- // See IsParentOfBrowserLocale().
- std::string best_parent_locale_;
- // See AnalyzeCandidateAsDistantRelative().
- std::string best_distant_relative_locale_;
- size_t best_distant_relative_locale_piecewise_match_length_;
- size_t best_distant_relative_locale_piecewise_length_;
- // Denotes whether or not the Chrome OS Printing serving root serves
- // metadata for the "en" locale - our final fallback.
- bool is_english_available_;
- };
- // Represents the basename and containing directory of a piece of PPD
- // metadata. Does not own any strings given to its setter methods and
- // must not outlive them.
- class PpdMetadataPathSpecifier {
- public:
- enum class Type {
- kLocales,
- kManufacturers, // locale-sensitive
- kPrinters, // locale-sensitive
- kForwardIndex, // sharded
- kReverseIndex, // locale-sensitive; sharded
- kUsbIndex,
- kUsbVendorIds,
- };
- explicit PpdMetadataPathSpecifier(Type type)
- : type_(type),
- printers_basename_(nullptr),
- metadata_locale_(nullptr),
- shard_(0),
- usb_vendor_id_(0) {}
- ~PpdMetadataPathSpecifier() = default;
- // PpdMetadataPathSpecifier is neither copyable nor movable.
- 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", kMetadataParentDirectory);
- case Type::kManufacturers:
- DCHECK(metadata_locale_);
- DCHECK(!base::StringPiece(metadata_locale_).empty());
- return base::StringPrintf("%s/manufacturers-%s.json",
- kMetadataParentDirectory, metadata_locale_);
- case Type::kPrinters:
- DCHECK(printers_basename_);
- DCHECK(!base::StringPiece(printers_basename_).empty());
- return base::StringPrintf("%s/%s", kMetadataParentDirectory,
- printers_basename_);
- case Type::kForwardIndex:
- DCHECK(shard_ >= 0 && shard_ < kNumShards);
- return base::StringPrintf("%s/index-%02d.json",
- kMetadataParentDirectory, 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",
- kMetadataParentDirectory, metadata_locale_,
- shard_);
- case Type::kUsbIndex:
- DCHECK(usb_vendor_id_ >= 0 && usb_vendor_id_ <= kSixteenBitsMaximum);
- return base::StringPrintf("%s/usb-%04x.json", kMetadataParentDirectory,
- usb_vendor_id_);
- case Type::kUsbVendorIds:
- return base::StringPrintf("%s/usb_vendor_ids.json",
- kMetadataParentDirectory);
- }
- // This function cannot fail except by maintainer error.
- NOTREACHED();
- return std::string();
- }
- // Private const char* members are const char* for compatibility with
- // base::StringPrintf().
- private:
- Type type_;
- // Populated only when |type_| == kPrinters.
- // Contains the basename of the target printers metadata file.
- const char* printers_basename_;
- // Populated only when |type_| is locale-sensitive and != kPrinters.
- // Contains the metadata locale for which we intend to fetch metadata.
- const char* metadata_locale_;
- // Populated only when |type_| is sharded.
- int shard_;
- // Populated only when |type_| == kUsbIndex.
- int usb_vendor_id_;
- };
- // Note: generally, each Get*() method is segmented into three parts:
- // 1. check if query can be answered immediately,
- // 2. fetch appropriate metadata if it can't [defer to On*Fetched()],
- // and (time passes)
- // 3. answer query with appropriate metadata [call On*Available()].
- class PpdMetadataManagerImpl : public PpdMetadataManager {
- public:
- PpdMetadataManagerImpl(base::StringPiece browser_locale,
- base::Clock* clock,
- std::unique_ptr<PrinterConfigCache> config_cache)
- : browser_locale_(browser_locale),
- 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_);
- // Per header comment: if a best-fit metadata locale is already set,
- // we don't refresh it; we just immediately declare success.
- //
- // Side effect: classes composing |this| can call
- // SetLocaleForTesting() before composition and get this cop-out
- // for free.
- if (!metadata_locale_.empty()) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), true));
- return;
- }
- PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kLocales);
- const std::string metadata_name = path.AsString();
- PrinterConfigCache::FetchCallback fetch_cb =
- base::BindOnce(&PpdMetadataManagerImpl::OnLocalesFetched,
- weak_factory_.GetWeakPtr(), std::move(cb));
- // We call Fetch() with a default-constructed TimeDelta(): "give
- // me the freshest possible locales metadata."
- 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);
- 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::SequencedTaskRunnerHandle::Get()->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 we are the prime movers, then we need to set the forward
- // index search in motion.
- if (queue_was_idle) {
- ContinueSearchingForwardIndices();
- }
- // If we're not the prime movers, then a search is already ongoing
- // and we need not provide extra impetus.
- }
- void FindDeviceInUsbIndex(int vendor_id,
- int product_id,
- base::TimeDelta age,
- FindDeviceInUsbIndexCallback cb) override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Fails the |cb| immediately if the |vendor_id| or |product_id| are
- // obviously out of range.
- if (vendor_id < 0 || vendor_id > kSixteenBitsMaximum || product_id < 0 ||
- product_id > kSixteenBitsMaximum) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), std::string()));
- return;
- }
- PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kUsbIndex);
- 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);
- 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);
- 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);
- }
- // This method should read much the same as OnManufacturersFetched().
- bool SetManufacturersForTesting(
- base::StringPiece manufacturers_json) override {
- DCHECK(!metadata_locale_.empty());
- const auto parsed = ParseManufacturers(manufacturers_json);
- if (!parsed.has_value()) {
- return false;
- }
- // We need to name the manufacturers metadata manually to store it.
- PpdMetadataPathSpecifier path(
- PpdMetadataPathSpecifier::Type::kManufacturers);
- 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:
- // Denotes the status of an ongoing forward index search - see
- // FindAllEmmsAvailableInIndex().
- enum class ForwardIndexSearchStatus {
- // We called |config_cache_|::Fetch(). We provided a bound
- // callback that will resume the forward index search for us when
- // the fetch completes.
- kWillResumeOnFetchCompletion,
- // We did not call |config_cache_|::Fetch(), so |this| still has
- // control of the progression of the forward index search.
- kCanContinue,
- };
- // Called by OnLocalesFetched().
- // Continues a prior call to GetLocale().
- //
- // Attempts to set |metadata_locale_| given the advertised
- // |locales_list|. Returns true if successful and false if not.
- bool SetMetadataLocale(const std::vector<std::string>& locales_list) {
- // This class helps track all the locales that _could_ be good fits
- // given our |browser_locale_| but which are not exact matches.
- MetadataLocaleFinder locale_finder(browser_locale_);
- metadata_locale_ = std::string(locale_finder.BestCandidate(locales_list));
- return !metadata_locale_.empty();
- }
- // Called back by |config_cache_|.Fetch().
- // Continues a prior call to GetLocale().
- //
- // On successful |result|, parses and sets the |metadata_locale_|.
- // Calls |cb| with the |result|.
- void OnLocalesFetched(GetLocaleCallback cb,
- const PrinterConfigCache::FetchResult& result) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!result.succeeded) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), false));
- return;
- }
- const auto parsed = ParseLocales(result.contents);
- if (!parsed.has_value()) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), false));
- return;
- }
- // SetMetadataLocale() _can_ fail, but that would be an
- // extraordinarily bad thing - i.e. that the Chrome OS Printing
- // serving root is itself in an invalid state.
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(cb), SetMetadataLocale(parsed.value())));
- }
- // Called by one of
- // * GetManufacturers() or
- // * OnManufacturersFetched().
- // Continues a prior call to GetManufacturers().
- //
- // Invokes |cb| with success, providing it with a list of
- // manufacturers.
- 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::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(cb), PpdProvider::CallbackResultCode::SUCCESS,
- manufacturers_for_cb));
- }
- // Called by |config_cache_|.Fetch().
- // Continues a prior call to GetManufacturers().
- //
- // Parses and updates our cached map of manufacturers if |result|
- // indicates a successful fetch. Calls |cb| accordingly.
- void OnManufacturersFetched(PpdProvider::ResolveManufacturersCallback cb,
- const PrinterConfigCache::FetchResult& result) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!result.succeeded) {
- base::SequencedTaskRunnerHandle::Get()->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::SequencedTaskRunnerHandle::Get()->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));
- }
- // Called by GetPrinters().
- // Returns the known name for the Printers metadata named by
- // |manufacturer|.
- absl::optional<std::string> GetPrintersMetadataName(
- base::StringPiece manufacturer) {
- PpdMetadataPathSpecifier manufacturers_path(
- PpdMetadataPathSpecifier::Type::kManufacturers);
- manufacturers_path.SetMetadataLocale(metadata_locale_.c_str());
- const std::string manufacturers_metadata_name =
- manufacturers_path.AsString();
- if (!cached_manufacturers_.contains(manufacturers_metadata_name)) {
- // This is likely a bug: we don't have the expected manufacturers
- // metadata.
- return absl::nullopt;
- }
- const ParsedMetadataWithTimestamp<ParsedManufacturers>& manufacturers =
- cached_manufacturers_.at(manufacturers_metadata_name);
- if (!manufacturers.value.contains(manufacturer)) {
- // This is likely a bug: we don't know about this manufacturer.
- return absl::nullopt;
- }
- PpdMetadataPathSpecifier printers_path(
- PpdMetadataPathSpecifier::Type::kPrinters);
- printers_path.SetPrintersBasename(
- manufacturers.value.at(manufacturer).c_str());
- return printers_path.AsString();
- }
- // Called by one of
- // * GetPrinters() or
- // * OnPrintersFetched().
- // Continues a prior call to GetPrinters().
- //
- // Invokes |cb| with success, providing it a map of printers.
- void OnPrintersAvailable(base::StringPiece metadata_name,
- GetPrintersCallback cb) {
- const auto& parsed_printers = cached_printers_.at(metadata_name);
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), true, parsed_printers.value));
- }
- // Called by |config_cache_|.Fetch().
- // Continues a prior call to GetPrinters().
- //
- // Parses and updates our cached map of printers if |result| indicates
- // a successful fetch. Calls |cb| accordingly.
- void OnPrintersFetched(GetPrintersCallback cb,
- const PrinterConfigCache::FetchResult& result) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!result.succeeded) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), false, ParsedPrinters{}));
- return;
- }
- const auto parsed = ParsePrinters(result.contents);
- if (!parsed.has_value()) {
- base::SequencedTaskRunnerHandle::Get()->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));
- }
- // Called when one unit of sufficiently fresh forward index metadata
- // is available. Seeks out the current effective-make-and-model string
- // in said metadata.
- void FindEmmInForwardIndex(base::StringPiece metadata_name) {
- // Caller must have verified that this index is already present (and
- // sufficiently fresh) before entering this method.
- 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();
- }
- // Called by |config_cache_|.Fetch().
- // Continues a prior call to FindAllEmmsAvailableInForwardIndex().
- //
- // Parses and updates our cached map of forward indices if |result|
- // indicates a successful fetch. Continues the action that
- // necessitated fetching the present forward index.
- void OnForwardIndexFetched(const PrinterConfigCache::FetchResult& result) {
- if (!result.succeeded) {
- // We failed to fetch the forward index containing the current
- // effective-make-and-model string. There's nothing we can do but
- // carry on, e.g. by moving to deal with the next emm.
- forward_index_search_queue_.AdvanceToNextEmm();
- ContinueSearchingForwardIndices();
- return;
- }
- const auto parsed = ParseForwardIndex(result.contents);
- if (!parsed.has_value()) {
- // Same drill as fetch failure above.
- forward_index_search_queue_.AdvanceToNextEmm();
- ContinueSearchingForwardIndices();
- return;
- }
- ParsedMetadataWithTimestamp<ParsedIndex> value = {clock_->Now(),
- parsed.value()};
- cached_forward_indices_.insert_or_assign(result.key, value);
- ContinueSearchingForwardIndices();
- }
- // Works on searching the forward index for the current
- // effective-make-and-model string in the frontmost entry in the
- // forward index search queue.
- //
- // One invocation of this method ultimately processes exactly one
- // effective-make-and-model string: either we find it in some forward
- // index metadata or we don't.
- ForwardIndexSearchStatus SearchForwardIndicesForOneEmm() {
- const ForwardIndexSearchContext& context =
- forward_index_search_queue_.CurrentContext();
- PpdMetadataPathSpecifier path(
- PpdMetadataPathSpecifier::Type::kForwardIndex);
- path.SetShard(IndexShard(context.CurrentEmm()));
- const std::string forward_index_name = path.AsString();
- if (MapHasValueFresherThan(cached_forward_indices_, forward_index_name,
- context.MaxAge())) {
- // We have the appropriate forward index metadata and it's fresh
- // enough to make a determination: is the current
- // effective-make-and-model string present in this metadata?
- FindEmmInForwardIndex(forward_index_name);
- return ForwardIndexSearchStatus::kCanContinue;
- }
- // We don't have the appropriate forward index metadata. We need to
- // get it before we can determine if the current
- // effective-make-and-model string is present in it.
- //
- // PrinterConfigCache::Fetch() accepts a TimeDelta expressing the
- // maximum permissible age of the cached response; to simulate the
- // original TimeDelta that caller gave to
- // FindAllEmmsAvailableInIndex(), we find the delta between Now()
- // and the absolute time ceiling recorded in the
- // ForwardIndexSearchContext.
- 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;
- }
- // Continues working on the forward index search queue.
- void ContinueSearchingForwardIndices() {
- while (!forward_index_search_queue_.IsIdle()) {
- ForwardIndexSearchStatus status = SearchForwardIndicesForOneEmm();
- // If we invoked |config_cache_|::Fetch(), then control has passed
- // out of this class for now. It will resume from
- // OnForwardIndexFetched().
- if (status == ForwardIndexSearchStatus::kWillResumeOnFetchCompletion) {
- break;
- }
- }
- }
- // Called by one of
- // * FindDeviceInUsbIndex() or
- // * OnUsbIndexFetched().
- // Searches the now-available USB index metadata with |metadata_name|
- // for a device with given |product_id|, calling |cb| appropriately.
- 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::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(cb), std::move(effective_make_and_model)));
- }
- // Called by |config_cache_|.Fetch().
- // Continues a prior call to FindDeviceInUsbIndex().
- //
- // Parses and updates our cached map of USB index metadata if |result|
- // indicates a successful fetch.
- void OnUsbIndexFetched(std::string metadata_name,
- int product_id,
- FindDeviceInUsbIndexCallback cb,
- const PrinterConfigCache::FetchResult& fetch_result) {
- if (!fetch_result.succeeded) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), std::string()));
- return;
- }
- absl::optional<ParsedUsbIndex> parsed =
- ParseUsbIndex(fetch_result.contents);
- if (!parsed.has_value()) {
- base::SequencedTaskRunnerHandle::Get()->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));
- }
- // Called by one of
- // * GetUsbManufacturerName() or
- // * OnUsbVendorIdMapFetched().
- //
- // Searches the available USB vendor ID map (named by |metadata_name|)
- // for |vendor_id| and invokes |cb| accordingly.
- 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::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(cb), manufacturer_name));
- }
- // Called by |config_cache_|->Fetch().
- // Continues a prior call to GetUsbManufacturerName.
- //
- // Parses and updates our cached map of USB vendor IDs if |result|
- // indicates a successful fetch.
- //
- // If we're haggling over bits, it is wasteful to have a map that
- // only ever has at most one key-value pair. We willfully accept this
- // inefficiency to maintain consistency with other metadata
- // operations.
- void OnUsbVendorIdMapFetched(
- int vendor_id,
- GetUsbManufacturerNameCallback cb,
- const PrinterConfigCache::FetchResult& fetch_result) {
- if (!fetch_result.succeeded) {
- base::SequencedTaskRunnerHandle::Get()->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::SequencedTaskRunnerHandle::Get()->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));
- }
- // Called by one of
- // * SplitMakeAndModel() or
- // * OnReverseIndexFetched().
- // Continues a prior call to SplitMakeAndModel().
- //
- // Looks for |effective_make_and_model| in the reverse index named by
- // |metadata_name|, and tries to invoke |cb| with the split make and
- // model.
- 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);
- // We expect this reverse index shard to contain the decomposition
- // for |effective_make_and_model|.
- if (!parsed_reverse_index.value.contains(effective_make_and_model)) {
- base::SequencedTaskRunnerHandle::Get()->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::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(cb), PpdProvider::CallbackResultCode::SUCCESS,
- leaf.manufacturer, leaf.model));
- }
- // Called by |config_cache_|.Fetch().
- // Continues a prior call to SplitMakeAndModel().
- //
- // Parses and updates our cached map of reverse indices if |result|
- // indicates a successful fetch. Calls |cb| accordingly.
- 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::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(cb),
- PpdProvider::CallbackResultCode::SERVER_ERROR, "",
- ""));
- return;
- }
- const auto parsed = ParseReverseIndex(result.contents);
- if (!parsed.has_value()) {
- base::SequencedTaskRunnerHandle::Get()->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_;
- raw_ptr<const base::Clock> clock_;
- // The closest match to |browser_locale_| for which the serving root
- // claims to serve metadata.
- 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_;
- // Processing queue for FindAllEmmsAvailableInIndex().
- ForwardIndexSearchQueue forward_index_search_queue_;
- SEQUENCE_CHECKER(sequence_checker_);
- // Dispenses weak pointers to the |config_cache_|. This is necessary
- // because |this| could be deleted while the |config_cache_| is
- // processing something off-sequence.
- base::WeakPtrFactory<PpdMetadataManagerImpl> weak_factory_;
- };
- } // namespace
- // static
- std::unique_ptr<PpdMetadataManager> PpdMetadataManager::Create(
- base::StringPiece browser_locale,
- base::Clock* clock,
- std::unique_ptr<PrinterConfigCache> config_cache) {
- return std::make_unique<PpdMetadataManagerImpl>(browser_locale, clock,
- std::move(config_cache));
- }
- } // namespace chromeos
|