Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/printing/ppd_provider.h" | 5 #include "chromeos/printing/ppd_provider.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 #include <deque> | |
| 7 #include <unordered_map> | 9 #include <unordered_map> |
| 8 #include <utility> | 10 #include <utility> |
| 11 #include <vector> | |
| 9 | 12 |
| 10 #include "base/base64.h" | 13 #include "base/base64.h" |
| 11 #include "base/bind_helpers.h" | 14 #include "base/bind_helpers.h" |
| 12 #include "base/files/file_util.h" | 15 #include "base/files/file_util.h" |
| 13 #include "base/json/json_parser.h" | 16 #include "base/json/json_parser.h" |
| 14 #include "base/memory/ptr_util.h" | 17 #include "base/memory/ptr_util.h" |
| 15 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/strings/string_split.h" | |
| 16 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
| 17 #include "base/strings/stringprintf.h" | 21 #include "base/strings/stringprintf.h" |
| 18 #include "base/synchronization/lock.h" | 22 #include "base/synchronization/lock.h" |
| 19 #include "base/task_runner_util.h" | 23 #include "base/task_runner_util.h" |
| 20 #include "base/threading/sequenced_task_runner_handle.h" | 24 #include "base/threading/sequenced_task_runner_handle.h" |
| 21 #include "base/time/time.h" | 25 #include "base/time/time.h" |
| 22 #include "base/values.h" | 26 #include "base/values.h" |
| 23 #include "chromeos/printing/ppd_cache.h" | 27 #include "chromeos/printing/ppd_cache.h" |
| 24 #include "net/base/load_flags.h" | 28 #include "net/base/load_flags.h" |
| 25 #include "net/http/http_status_code.h" | 29 #include "net/http/http_status_code.h" |
| 26 #include "net/url_request/url_fetcher.h" | 30 #include "net/url_request/url_fetcher.h" |
| 27 #include "net/url_request/url_fetcher_delegate.h" | 31 #include "net/url_request/url_fetcher_delegate.h" |
| 28 #include "net/url_request/url_request_context_getter.h" | 32 #include "net/url_request/url_request_context_getter.h" |
| 29 #include "url/gurl.h" | 33 #include "url/gurl.h" |
| 30 | 34 |
| 31 namespace chromeos { | 35 namespace chromeos { |
| 32 namespace printing { | 36 namespace printing { |
| 33 namespace { | 37 namespace { |
| 34 | 38 |
| 35 // Expected fields from the quirks server. | 39 // Returns false if there are obvious errors in the reference that will prevent |
| 36 const char kJSONPPDKey[] = "compressedPpd"; | 40 // resolution. |
| 37 const char kJSONLastUpdatedKey[] = "lastUpdatedTime"; | 41 bool PpdReferenceIsWellFormed(const Printer::PpdReference& reference) { |
| 38 const char kJSONTopListKey[] = "manufacturers"; | 42 int filled_fields = 0; |
| 39 const char kJSONManufacturer[] = "manufacturer"; | 43 if (!reference.user_supplied_ppd_url.empty()) { |
| 40 const char kJSONModelList[] = "models"; | 44 ++filled_fields; |
| 41 | 45 if (!GURL(reference.user_supplied_ppd_url).is_valid()) { |
|
skau
2017/01/27 01:37:01
I'm not sure we can depend on this being a well fo
Carlson
2017/01/27 18:48:43
I don't think I understand your concern here. Mos
skau
2017/01/27 19:16:10
I stand corrected. I didn't realize file:// was i
Carlson
2017/01/27 22:56:22
Yeah, this was a big problem, thanks for pointing
| |
| 42 class PpdProviderImpl; | 46 return false; |
| 43 | 47 } |
| 44 // PpdProvider handles two types of URL fetches, and so uses these delegates | 48 } |
| 45 // to route OnURLFetchComplete to the appropriate handler. | 49 if (!reference.effective_make_and_model.empty()) { |
| 46 class ResolveURLFetcherDelegate : public net::URLFetcherDelegate { | 50 ++filled_fields; |
| 51 } | |
| 52 // Should have exactly one non-empty field. | |
| 53 return filled_fields == 1; | |
| 54 } | |
| 55 | |
| 56 std::string PpdReferenceToCacheKey(const Printer::PpdReference& reference) { | |
| 57 DCHECK(PpdReferenceIsWellFormed(reference)); | |
| 58 // The key prefixes here are arbitrary, but ensure we can't have an (unhashed) | |
| 59 // collision between keys generated from different PpdReference fields. | |
| 60 if (!reference.effective_make_and_model.empty()) { | |
| 61 return std::string("em:") + reference.effective_make_and_model; | |
| 62 } else { | |
| 63 return std::string("up:") + reference.user_supplied_ppd_url; | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 struct ManufacturerMetadata { | |
| 68 // Key used to look up the printer list on the server. This is initially | |
| 69 // populated. | |
| 70 std::string reference; | |
| 71 | |
| 72 // Map from localized printer name to canonical-make-and-model string for | |
| 73 // the given printer. Populated on demand. | |
| 74 std::unique_ptr<std::unordered_map<std::string, std::string>> printers; | |
| 75 }; | |
| 76 | |
| 77 // A queued request to download printer information for a manufacturer. | |
| 78 struct PrinterResolutionQueueEntry { | |
| 79 // Localized manufacturer name | |
| 80 std::string manufacturer; | |
| 81 | |
| 82 // URL we are going to pull from. | |
| 83 GURL url; | |
| 84 | |
| 85 // User callback on completion. | |
| 86 PpdProvider::ResolvePrintersCallback cb; | |
| 87 }; | |
| 88 | |
| 89 class PpdProviderImpl : public PpdProvider, public net::URLFetcherDelegate { | |
| 47 public: | 90 public: |
| 48 explicit ResolveURLFetcherDelegate(PpdProviderImpl* parent) | 91 // What kind of thing is the fetcher currently fetching? We use this to |
| 49 : parent_(parent) {} | 92 // determine what to do when the fetcher returns a result. |
| 50 | 93 enum FetcherTarget { |
| 51 void OnURLFetchComplete(const net::URLFetcher* source) override; | 94 FT_LOCALES, // Locales metadata. |
| 52 | 95 FT_MANUFACTURERS, // List of manufacturers metadata. |
| 53 // Link back to parent. Not owned. | 96 FT_PRINTERS, // List of printers from a manufacturer. |
| 54 PpdProviderImpl* const parent_; | 97 FT_PPD_INDEX, // Master ppd index. |
| 55 }; | 98 FT_PPD // A Ppd file. |
| 56 | 99 }; |
| 57 class QueryAvailableURLFetcherDelegate : public net::URLFetcherDelegate { | 100 |
| 58 public: | |
| 59 explicit QueryAvailableURLFetcherDelegate(PpdProviderImpl* parent) | |
| 60 : parent_(parent) {} | |
| 61 | |
| 62 void OnURLFetchComplete(const net::URLFetcher* source) override; | |
| 63 | |
| 64 // Link back to parent. Not owned. | |
| 65 PpdProviderImpl* const parent_; | |
| 66 }; | |
| 67 | |
| 68 // Data involved in an active Resolve() URL fetch. | |
| 69 struct ResolveFetchData { | |
| 70 // The fetcher doing the fetch. | |
| 71 std::unique_ptr<net::URLFetcher> fetcher; | |
| 72 | |
| 73 // The reference being resolved. | |
| 74 Printer::PpdReference ppd_reference; | |
| 75 | |
| 76 // Callback to invoke on completion. | |
| 77 PpdProvider::ResolveCallback done_callback; | |
| 78 }; | |
| 79 | |
| 80 // Data involved in an active QueryAvailable() URL fetch. | |
| 81 struct QueryAvailableFetchData { | |
| 82 // The fetcher doing the fetch. | |
| 83 std::unique_ptr<net::URLFetcher> fetcher; | |
| 84 | |
| 85 // Callback to invoke on completion. | |
| 86 PpdProvider::QueryAvailableCallback done_callback; | |
| 87 }; | |
| 88 | |
| 89 class PpdProviderImpl : public PpdProvider { | |
| 90 public: | |
| 91 PpdProviderImpl( | 101 PpdProviderImpl( |
| 92 const std::string& api_key, | 102 const std::string& browser_locale, |
| 93 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | 103 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 94 scoped_refptr<base::SequencedTaskRunner> io_task_runner, | 104 scoped_refptr<PpdCache> ppd_cache, |
| 95 std::unique_ptr<PpdCache> cache, | |
| 96 const PpdProvider::Options& options) | 105 const PpdProvider::Options& options) |
| 97 : api_key_(api_key), | 106 : browser_locale_(browser_locale), |
| 98 url_context_getter_(url_context_getter), | 107 url_context_getter_(url_context_getter), |
| 99 io_task_runner_(io_task_runner), | 108 ppd_cache_(ppd_cache), |
| 100 cache_(std::move(cache)), | |
| 101 options_(options), | 109 options_(options), |
| 102 resolve_delegate_(this), | |
| 103 query_available_delegate_(this), | |
| 104 weak_factory_(this) { | 110 weak_factory_(this) { |
| 105 CHECK_GT(options_.max_ppd_contents_size_, 0U); | 111 CHECK_GT(options_.max_ppd_contents_size_, 0U); |
| 106 } | 112 } |
| 107 ~PpdProviderImpl() override {} | 113 |
| 108 | 114 // Resolving manufacturers requires a couple of steps, because of |
| 109 void Resolve(const Printer::PpdReference& ppd_reference, | 115 // localization. First we have to figure out what locale to use, which |
| 110 const PpdProvider::ResolveCallback& cb) override { | 116 // involves grabbing a list of available locales from the server. Once we |
| 111 CHECK(!cb.is_null()); | 117 // have decided on a locale, we go out and fetch the manufacturers map in that |
| 118 // localization. | |
| 119 // | |
| 120 // This means when a request comes in, we either queue it and start background | |
| 121 // fetches if necessary, or we satisfy it immediately from memory. | |
| 122 void ResolveManufacturers(const ResolveManufacturersCallback& cb) override { | |
| 112 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | 123 CHECK(base::SequencedTaskRunnerHandle::IsSet()) |
| 113 << "Resolve must be called from a SequencedTaskRunner context"; | 124 << "ResolveManufacturers must be called from a SequencedTaskRunner" |
| 114 auto cache_result = base::MakeUnique<base::Optional<base::FilePath>>(); | 125 "context"; |
| 115 auto* raw_cache_result_ptr = cache_result.get(); | 126 if (cached_metadata_.get() != nullptr) { |
| 116 bool post_result = io_task_runner_->PostTaskAndReply( | 127 // We already have this in memory. |
| 117 FROM_HERE, base::Bind(&PpdProviderImpl::ResolveDoCacheLookup, | 128 base::SequencedTaskRunnerHandle::Get()->PostTask( |
| 118 weak_factory_.GetWeakPtr(), ppd_reference, | 129 FROM_HERE, |
| 119 raw_cache_result_ptr), | 130 base::Bind(cb, PpdProvider::SUCCESS, GetManufacturerList())); |
| 120 base::Bind(&PpdProviderImpl::ResolveCacheLookupDone, | 131 return; |
| 121 weak_factory_.GetWeakPtr(), ppd_reference, cb, | 132 } |
| 122 base::Passed(&cache_result))); | 133 manufacturers_resolution_queue_.push_back(cb); |
| 123 DCHECK(post_result); | 134 MaybeStartFetch(); |
| 124 } | 135 } |
| 125 | 136 |
| 126 void QueryAvailable(const QueryAvailableCallback& cb) override { | 137 // If there is work outstanding that requires a URL fetch to complete, start |
| 127 CHECK(!cb.is_null()); | 138 // going on it. |
| 128 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | 139 void MaybeStartFetch() { |
| 129 << "QueryAvailable() must be called from a SequencedTaskRunner context"; | 140 if (fetcher_.get() != nullptr) { |
| 130 auto cache_result = | 141 // Something is already in flight. We'll call this again when that |
| 131 base::MakeUnique<base::Optional<PpdProvider::AvailablePrintersMap>>(); | 142 // completes. |
| 132 auto* raw_cache_result_ptr = cache_result.get(); | 143 return; |
| 133 bool post_result = io_task_runner_->PostTaskAndReply( | 144 } |
| 134 FROM_HERE, base::Bind(&PpdProviderImpl::QueryAvailableDoCacheLookup, | 145 |
| 135 weak_factory_.GetWeakPtr(), raw_cache_result_ptr), | 146 if (!manufacturers_resolution_queue_.empty()) { |
| 136 base::Bind(&PpdProviderImpl::QueryAvailableCacheLookupDone, | 147 StartFetch(GetLocalesURL(), FT_LOCALES); |
| 137 weak_factory_.GetWeakPtr(), cb, | 148 return; |
| 138 base::Passed(&cache_result))); | 149 } |
| 139 DCHECK(post_result); | 150 if (!printers_resolution_queue_.empty()) { |
| 140 } | 151 StartFetch(printers_resolution_queue_.front().url, FT_PRINTERS); |
| 141 | 152 return; |
| 142 bool CachePpd(const Printer::PpdReference& ppd_reference, | 153 } |
| 143 const base::FilePath& ppd_path) override { | 154 while (!ppd_resolution_queue_.empty()) { |
| 144 std::string buf; | 155 const auto& next = ppd_resolution_queue_.front(); |
| 145 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf, | 156 if (!next.first.user_supplied_ppd_url.empty()) { |
| 146 options_.max_ppd_contents_size_)) { | 157 DCHECK(next.first.effective_make_and_model.empty()); |
| 158 GURL url(next.first.user_supplied_ppd_url); | |
| 159 DCHECK(url.is_valid()); | |
| 160 StartFetch(url, FT_PPD); | |
| 161 return; | |
| 162 } | |
| 163 DCHECK(!next.first.effective_make_and_model.empty()); | |
| 164 if (cached_ppd_index_.get() == nullptr) { | |
| 165 // Have to have the ppd index before we can resolve by ppd server | |
| 166 // key. | |
| 167 StartFetch(GetPpdIndexURL(), FT_PPD_INDEX); | |
| 168 return; | |
| 169 } | |
| 170 // Get the URL from the ppd index and start the fetch. | |
| 171 auto it = cached_ppd_index_->find(next.first.effective_make_and_model); | |
| 172 if (it != cached_ppd_index_->end()) { | |
| 173 StartFetch(GetPpdURL(it->second), FT_PPD); | |
| 174 return; | |
| 175 } | |
| 176 // This ppd reference isn't in the index. That's not good. Fail | |
| 177 // out the current resolution and go try to start the next | |
| 178 // thing if there is one. | |
| 179 LOG(ERROR) << "PPD " << next.first.effective_make_and_model | |
| 180 << " not found in server index"; | |
| 181 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 182 FROM_HERE, | |
| 183 base::Bind(next.second, PpdProvider::INTERNAL_ERROR, std::string())); | |
| 184 ppd_resolution_queue_.pop_front(); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 void ResolvePrinters(const std::string& manufacturer, | |
| 189 const ResolvePrintersCallback& cb) override { | |
| 190 std::unordered_map<std::string, ManufacturerMetadata>::iterator it; | |
| 191 if (cached_metadata_.get() == nullptr || | |
| 192 (it = cached_metadata_->find(manufacturer)) == | |
| 193 cached_metadata_->end()) { | |
| 194 // User error. | |
| 195 LOG(ERROR) << "Can't resolve printers for unknown manufacturer " | |
| 196 << manufacturer; | |
| 197 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 198 FROM_HERE, base::Bind(cb, PpdProvider::INTERNAL_ERROR, | |
| 199 std::vector<std::string>())); | |
| 200 return; | |
| 201 } | |
| 202 if (it->second.printers.get() != nullptr) { | |
| 203 // Satisfy from the cache. | |
| 204 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 205 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, | |
| 206 GetManufacturerPrinterList(it->second))); | |
| 207 } else { | |
| 208 // We haven't resolved this manufacturer yet. | |
| 209 PrinterResolutionQueueEntry entry; | |
| 210 entry.manufacturer = manufacturer; | |
| 211 entry.url = GetPrintersURL(it->second.reference); | |
| 212 entry.cb = cb; | |
| 213 printers_resolution_queue_.push_back(entry); | |
| 214 MaybeStartFetch(); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 bool GetPpdReference(const std::string& manufacturer, | |
| 219 const std::string& printer, | |
| 220 Printer::PpdReference* reference) const override { | |
| 221 std::unordered_map<std::string, ManufacturerMetadata>::iterator top_it; | |
| 222 if (cached_metadata_.get() == nullptr) { | |
| 147 return false; | 223 return false; |
| 148 } | 224 } |
| 149 return static_cast<bool>(cache_->Store(ppd_reference, buf)); | 225 auto it = cached_metadata_->find(manufacturer); |
| 150 } | 226 if (it == cached_metadata_->end() || it->second.printers.get() == nullptr) { |
| 151 | 227 return false; |
| 152 // Called on the same thread as Resolve() when the fetcher completes its | 228 } |
| 153 // fetch. | 229 const auto& printers_map = *it->second.printers; |
| 154 void OnResolveFetchComplete(const net::URLFetcher* source) { | 230 auto it2 = printers_map.find(printer); |
| 155 std::unique_ptr<ResolveFetchData> fetch_data = GetResolveFetchData(source); | 231 if (it2 == printers_map.end()) { |
| 156 std::string ppd_contents; | 232 return false; |
| 157 uint64_t last_updated_time; | 233 } |
| 158 if (!ExtractResolveResponseData(source, fetch_data.get(), &ppd_contents, | 234 *reference = Printer::PpdReference(); |
| 159 &last_updated_time)) { | 235 reference->effective_make_and_model = it2->second; |
| 160 fetch_data->done_callback.Run(PpdProvider::SERVER_ERROR, | 236 return true; |
| 161 base::FilePath()); | 237 } |
| 162 return; | 238 |
| 163 } | 239 void ResolvePpd(const Printer::PpdReference& reference, |
| 164 | 240 const ResolvePpdCallback& cb) override { |
| 165 auto ppd_file = cache_->Store(fetch_data->ppd_reference, ppd_contents); | 241 // Do a sanity check here, so we can assumed |reference| is well-formed in |
| 166 if (!ppd_file) { | 242 // the rest of this class. |
| 167 // Failed to store. | 243 if (!PpdReferenceIsWellFormed(reference)) { |
| 168 fetch_data->done_callback.Run(PpdProvider::INTERNAL_ERROR, | 244 base::SequencedTaskRunnerHandle::Get()->PostTask( |
| 169 base::FilePath()); | 245 FROM_HERE, base::Bind(cb, PpdProvider::INTERNAL_ERROR, "")); |
| 170 return; | 246 return; |
| 171 } | 247 } |
| 172 fetch_data->done_callback.Run(PpdProvider::SUCCESS, ppd_file.value()); | 248 // First step, check the cache. If the cache lookup fails, we'll (try to) |
| 173 } | 249 // consult the server. |
| 174 | 250 ppd_cache_->Find(PpdReferenceToCacheKey(reference), |
| 175 // Called on the same thread as QueryAvailable() when the cache lookup is | 251 base::Bind(&PpdProviderImpl::ResolvePpdCacheLookupDone, |
| 176 // done. If the cache satisfied the request, finish the Query, otherwise | 252 weak_factory_.GetWeakPtr(), reference, cb)); |
| 177 // start a URL request to satisfy the Query. This runs on the same thread as | 253 } |
| 178 // QueryAvailable() was invoked on. | 254 |
| 179 void QueryAvailableCacheLookupDone( | 255 // Our only sources of long running ops are cache fetches and network fetches. |
| 180 const PpdProvider::QueryAvailableCallback& done_callback, | 256 bool Idle() const override { |
| 181 std::unique_ptr<base::Optional<PpdProvider::AvailablePrintersMap>> | 257 return ppd_cache_->Idle() && (fetcher_.get() == nullptr); |
| 182 cache_result) { | 258 } |
| 183 if (*cache_result) { | 259 |
| 184 done_callback.Run(PpdProvider::SUCCESS, cache_result->value()); | 260 // Common handler that gets called whenever a fetch completes. |
| 185 return; | 261 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 186 } | 262 switch (fetcher_target_) { |
| 187 | 263 case FT_LOCALES: |
| 188 auto fetch_data = base::MakeUnique<QueryAvailableFetchData>(); | 264 OnLocalesFetchComplete(); |
| 189 fetch_data->done_callback = done_callback; | 265 break; |
| 190 | 266 case FT_MANUFACTURERS: |
| 191 fetch_data->fetcher = net::URLFetcher::Create(GetQuirksServerPpdListURL(), | 267 OnManufacturersFetchComplete(); |
| 192 net::URLFetcher::GET, | 268 break; |
| 193 &query_available_delegate_); | 269 case FT_PRINTERS: |
| 194 fetch_data->fetcher->SetRequestContext(url_context_getter_.get()); | 270 OnPrintersFetchComplete(); |
| 195 fetch_data->fetcher->SetLoadFlags( | 271 break; |
| 196 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | 272 case FT_PPD_INDEX: |
| 197 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | 273 OnPpdIndexFetchComplete(); |
| 198 net::LOAD_DO_NOT_SEND_AUTH_DATA); | 274 break; |
| 199 auto* fetcher = fetch_data->fetcher.get(); | 275 case FT_PPD: |
| 200 StoreQueryAvailableFetchData(std::move(fetch_data)); | 276 OnPpdFetchComplete(); |
| 201 fetcher->Start(); | 277 break; |
| 202 } | 278 default: |
| 203 | 279 LOG(DFATAL) << "Unknown fetch source"; |
| 204 void OnQueryAvailableFetchComplete(const net::URLFetcher* source) { | 280 } |
| 205 std::unique_ptr<QueryAvailableFetchData> fetch_data = | 281 MaybeStartFetch(); |
| 206 GetQueryAvailableFetchData(source); | 282 } |
| 207 | 283 |
| 284 private: | |
| 285 // Return the URL used to look up the supported locales list. | |
| 286 GURL GetLocalesURL() { | |
| 287 return GURL(options_.ppd_server_root + "/metadata/locales.json"); | |
| 288 } | |
| 289 | |
| 290 // Return the URL used to get the index of ppd server key -> ppd. | |
| 291 GURL GetPpdIndexURL() { | |
| 292 return GURL(options_.ppd_server_root + "/metadata/index.json"); | |
| 293 } | |
| 294 | |
| 295 // Return the URL to get a localized manufacturers map. | |
| 296 GURL GetManufacturersURL(const std::string& locale) { | |
| 297 return GURL(base::StringPrintf("%s/metadata/manufacturers-%s.json", | |
| 298 options_.ppd_server_root.c_str(), | |
| 299 locale.c_str())); | |
| 300 } | |
| 301 | |
| 302 // Return the URL used to get a list of printers from the manufacturer |ref|. | |
| 303 GURL GetPrintersURL(const std::string& ref) { | |
| 304 return GURL(base::StringPrintf( | |
| 305 "%s/metadata/%s", options_.ppd_server_root.c_str(), ref.c_str())); | |
| 306 } | |
| 307 | |
| 308 // Return the URL used to get a ppd with the given filename. | |
| 309 GURL GetPpdURL(const std::string& filename) { | |
| 310 return GURL(base::StringPrintf( | |
| 311 "%s/ppds/%s", options_.ppd_server_root.c_str(), filename.c_str())); | |
| 312 } | |
| 313 | |
| 314 // Create and return a fetcher that has the usual (for this class) flags set | |
| 315 // and calls back to OnURLFetchComplete in this class when it finishes. | |
| 316 void StartFetch(const GURL& url, FetcherTarget target) { | |
| 317 DCHECK_EQ(nullptr, fetcher_.get()); | |
| 318 fetcher_target_ = target; | |
| 319 | |
| 320 fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this); | |
| 321 fetcher_->SetRequestContext(url_context_getter_.get()); | |
| 322 fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | |
| 323 net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 324 net::LOAD_DO_NOT_SEND_COOKIES | | |
| 325 net::LOAD_DO_NOT_SEND_AUTH_DATA); | |
| 326 fetcher_->Start(); | |
| 327 } | |
| 328 | |
| 329 // Callback when the cache lookup for a ppd request finishes. If we hit in | |
| 330 // the cache, satisfy the resolution, otherwise kick it over to the fetcher | |
| 331 // queue to be grabbed from a server. | |
| 332 void ResolvePpdCacheLookupDone(const Printer::PpdReference& reference, | |
| 333 const ResolvePpdCallback& cb, | |
| 334 const PpdCache::FindResult& result) { | |
| 335 if (result.success) { | |
| 336 // Cache hit. | |
| 337 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 338 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, result.contents)); | |
| 339 } else { | |
| 340 // Cache miss. Queue it to be satisfied by the fetcher queue. | |
| 341 ppd_resolution_queue_.push_back({reference, cb}); | |
| 342 MaybeStartFetch(); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 // Handler for the completion of the locales fetch. This response should be a | |
| 347 // list of strings, each of which is a locale in which we can answer queries | |
| 348 // on the server. The server (should) guarantee that we get 'en' as an | |
| 349 // absolute minimum. | |
| 350 // | |
| 351 // Combine this information with the browser locale to figure out the best | |
| 352 // locale to use, and then start a fetch of the manufacturers map in that | |
| 353 // locale. | |
| 354 void OnLocalesFetchComplete() { | |
| 208 std::string contents; | 355 std::string contents; |
| 209 if (!ValidateAndGetResponseAsString(*source, &contents)) { | 356 if (!ValidateAndGetResponseAsString(&contents)) { |
| 210 // Something went wrong with the fetch. | 357 FailQueuedMetadataResolutions(PpdProvider::SERVER_ERROR); |
| 211 fetch_data->done_callback.Run(PpdProvider::SERVER_ERROR, | 358 return; |
| 212 AvailablePrintersMap()); | 359 } |
| 213 return; | 360 auto top_list = base::ListValue::From(base::JSONReader::Read(contents)); |
| 214 } | 361 |
| 215 | 362 if (top_list.get() == nullptr) { |
| 216 // The server gives us JSON in the form of a list of (manufacturer, list of | 363 // We got something malformed back. |
| 217 // models) tuples. | 364 FailQueuedMetadataResolutions(PpdProvider::INTERNAL_ERROR); |
| 218 auto top_dict = | 365 return; |
| 219 base::DictionaryValue::From(base::JSONReader::Read(contents)); | 366 } |
| 220 const base::ListValue* top_list; | 367 |
| 221 if (top_dict == nullptr || !top_dict->GetList(kJSONTopListKey, &top_list)) { | 368 // This should just be a simple list of locale strings. |
| 222 LOG(ERROR) << "Malformed response from quirks server"; | 369 std::vector<std::string> available_locales; |
| 223 fetch_data->done_callback.Run(PpdProvider::SERVER_ERROR, | 370 bool found_en = false; |
| 224 AvailablePrintersMap()); | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 auto result = base::MakeUnique<PpdProvider::AvailablePrintersMap>(); | |
| 229 for (const std::unique_ptr<base::Value>& entry : *top_list) { | 371 for (const std::unique_ptr<base::Value>& entry : *top_list) { |
| 230 base::DictionaryValue* dict; | 372 std::string tmp; |
| 231 std::string manufacturer; | 373 // Locales should have at *least* a two-character country code. 100 is an |
| 232 std::string model; | 374 // arbitrary upper bound for length to protect against extreme bogosity. |
| 233 base::ListValue* model_list; | 375 if (!entry->GetAsString(&tmp) || tmp.size() < 2 || tmp.size() > 100) { |
| 234 if (!entry->GetAsDictionary(&dict) || | 376 FailQueuedMetadataResolutions(PpdProvider::INTERNAL_ERROR); |
| 235 !dict->GetString(kJSONManufacturer, &manufacturer) || | 377 return; |
| 236 !dict->GetList(kJSONModelList, &model_list)) { | 378 } |
| 237 LOG(ERROR) << "Unexpected contents in quirks server printer list."; | 379 if (tmp == "en") { |
| 238 // Just skip this entry instead of aborting the whole thing. | 380 found_en = true; |
| 239 continue; | 381 } |
| 240 } | 382 available_locales.push_back(tmp); |
| 241 | 383 } |
| 242 std::vector<std::string>& dest = (*result)[manufacturer]; | 384 if (available_locales.empty() || !found_en) { |
| 243 for (const std::unique_ptr<base::Value>& model_value : *model_list) { | 385 // We have no locales, or we didn't get an english locale (which is our |
| 244 if (model_value->GetAsString(&model)) { | 386 // ultimate fallback) |
| 245 dest.push_back(model); | 387 FailQueuedMetadataResolutions(PpdProvider::INTERNAL_ERROR); |
| 246 } else { | 388 return; |
| 247 LOG(ERROR) << "Skipping unknown model for manufacturer " | 389 } |
| 248 << manufacturer; | 390 // Everything checks out, kick off the manufacturers list fetch in the |
| 391 // appropriate locale. | |
| 392 std::string locale_to_use = GetBestLocale(available_locales); | |
| 393 StartFetch(GetManufacturersURL(locale_to_use), FT_MANUFACTURERS); | |
| 394 } | |
| 395 | |
| 396 // Called when the |fetcher_| is expected have the results of a | |
| 397 // manufacturer map (which maps localized manufacturer names to keys for | |
| 398 // looking up printers from that manufacturer). Use this information to | |
| 399 // populate manufacturer_map_, and resolve all queued ResolveManufacturers() | |
| 400 // calls. | |
| 401 void OnManufacturersFetchComplete() { | |
| 402 DCHECK_EQ(nullptr, cached_metadata_.get()); | |
| 403 std::vector<std::pair<std::string, std::string>> contents; | |
| 404 PpdProvider::CallbackResultCode code = | |
| 405 ValidateAndParseJSONResponse(&contents); | |
| 406 if (code != PpdProvider::SUCCESS) { | |
| 407 LOG(ERROR) << "Failed manufacturer parsing"; | |
| 408 FailQueuedMetadataResolutions(code); | |
| 409 return; | |
| 410 } | |
| 411 cached_metadata_ = base::MakeUnique< | |
| 412 std::unordered_map<std::string, ManufacturerMetadata>>(); | |
| 413 | |
| 414 for (const auto& entry : contents) { | |
| 415 ManufacturerMetadata value; | |
| 416 value.reference = entry.second; | |
| 417 (*cached_metadata_)[entry.first].reference = entry.second; | |
| 418 } | |
| 419 | |
| 420 std::vector<std::string> manufacturer_list = GetManufacturerList(); | |
| 421 // Complete any queued manufacturer resolutions. | |
| 422 for (const auto& cb : manufacturers_resolution_queue_) { | |
| 423 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 424 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, manufacturer_list)); | |
| 425 } | |
| 426 manufacturers_resolution_queue_.clear(); | |
| 427 } | |
| 428 | |
| 429 // The outstanding fetch associated with the front of | |
| 430 // |printers_resolution_queue_| finished, use the response to satisfy that | |
| 431 // ResolvePrinters() call. | |
| 432 void OnPrintersFetchComplete() { | |
| 433 CHECK(cached_metadata_.get() != nullptr); | |
| 434 DCHECK(!printers_resolution_queue_.empty()); | |
| 435 std::vector<std::pair<std::string, std::string>> contents; | |
| 436 PpdProvider::CallbackResultCode code = | |
| 437 ValidateAndParseJSONResponse(&contents); | |
| 438 if (code != PpdProvider::SUCCESS) { | |
| 439 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 440 FROM_HERE, base::Bind(printers_resolution_queue_.front().cb, code, | |
| 441 std::vector<std::string>())); | |
| 442 } else { | |
| 443 // This should be a list of lists of 2-element strings, where the first | |
| 444 // element is the localized name of the printer and the second element | |
| 445 // is the canonical name of the printer. | |
| 446 const std::string& manufacturer = | |
| 447 printers_resolution_queue_.front().manufacturer; | |
| 448 auto it = cached_metadata_->find(manufacturer); | |
| 449 | |
| 450 // If we kicked of a resolution, the entry better have already been | |
|
skau
2017/01/27 01:37:01
s/of/off/
Carlson
2017/01/27 18:48:43
Done.
| |
| 451 // in the map. | |
| 452 CHECK(it != cached_metadata_->end()); | |
| 453 | |
| 454 // Create the printer map in the cache, and populate it. | |
| 455 auto& manufacturer_metadata = it->second; | |
| 456 CHECK(manufacturer_metadata.printers.get() == nullptr); | |
| 457 manufacturer_metadata.printers = | |
| 458 base::MakeUnique<std::unordered_map<std::string, std::string>>(); | |
| 459 | |
| 460 for (const auto& entry : contents) { | |
| 461 manufacturer_metadata.printers->insert({entry.first, entry.second}); | |
| 462 } | |
| 463 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 464 FROM_HERE, | |
| 465 base::Bind(printers_resolution_queue_.front().cb, | |
| 466 PpdProvider::SUCCESS, | |
| 467 GetManufacturerPrinterList(manufacturer_metadata))); | |
| 468 } | |
| 469 printers_resolution_queue_.pop_front(); | |
| 470 } | |
| 471 | |
| 472 // Called when |fetcher_| should have just received the index mapping | |
| 473 // ppd server keys to ppd filenames. Use this to populate | |
| 474 // |cached_ppd_index_|. | |
| 475 void OnPpdIndexFetchComplete() { | |
| 476 std::vector<std::pair<std::string, std::string>> contents; | |
| 477 PpdProvider::CallbackResultCode code = | |
| 478 ValidateAndParseJSONResponse(&contents); | |
| 479 if (code != PpdProvider::SUCCESS) { | |
| 480 FailQueuedServerPpdResolutions(code); | |
| 481 } else { | |
| 482 cached_ppd_index_ = | |
| 483 base::MakeUnique<std::unordered_map<std::string, std::string>>(); | |
| 484 // This should be a list of lists of 2-element strings, where the first | |
| 485 // element is the |effective_make_and_model| of the printer and the second | |
| 486 // element is the filename of the ppd in the ppds/ directory on the | |
| 487 // server.. | |
|
skau
2017/01/27 01:37:00
involuntary ellipsis?
Carlson
2017/01/27 18:48:43
Involuntary sideways colon. (Fixed)
| |
| 488 for (const auto& entry : contents) { | |
| 489 cached_ppd_index_->insert(entry); | |
| 490 } | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 // This is called when |fetcher_| should have just downloaded a ppd. If we | |
| 495 // downloaded something successfully, use it to satisfy the front of the ppd | |
| 496 // resolution queue, otherwise fail out that resolution. | |
| 497 void OnPpdFetchComplete() { | |
| 498 DCHECK(!ppd_resolution_queue_.empty()); | |
| 499 std::string contents; | |
| 500 if (!ValidateAndGetResponseAsString(&contents) || | |
| 501 contents.size() > options_.max_ppd_contents_size_) { | |
| 502 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 503 FROM_HERE, base::Bind(ppd_resolution_queue_.front().second, | |
| 504 PpdProvider::SERVER_ERROR, std::string())); | |
| 505 } else { | |
| 506 ppd_cache_->Store( | |
| 507 PpdReferenceToCacheKey(ppd_resolution_queue_.front().first), contents, | |
| 508 base::Callback<void()>()); | |
| 509 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 510 FROM_HERE, base::Bind(ppd_resolution_queue_.front().second, | |
| 511 PpdProvider::SUCCESS, contents)); | |
| 512 } | |
| 513 ppd_resolution_queue_.pop_front(); | |
| 514 } | |
| 515 | |
| 516 // Something went wrong during metadata fetches. Fail all queued metadata | |
| 517 // resolutions with the given error code. | |
| 518 void FailQueuedMetadataResolutions(PpdProvider::CallbackResultCode code) { | |
| 519 for (const auto& cb : manufacturers_resolution_queue_) { | |
| 520 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 521 FROM_HERE, base::Bind(cb, code, std::vector<std::string>())); | |
| 522 } | |
| 523 manufacturers_resolution_queue_.clear(); | |
| 524 } | |
| 525 | |
| 526 // Fail all server-based ppd resolutions inflight, because we failed to grab | |
| 527 // the necessary index data from the server. Note we leave any user-based ppd | |
| 528 // resolutions intact, as they don't depend on the data we're missing. | |
| 529 void FailQueuedServerPpdResolutions(PpdProvider::CallbackResultCode code) { | |
| 530 std::deque<std::pair<Printer::PpdReference, ResolvePpdCallback>> | |
| 531 filtered_queue; | |
| 532 | |
| 533 for (const auto& entry : ppd_resolution_queue_) { | |
| 534 if (!entry.first.user_supplied_ppd_url.empty()) { | |
| 535 filtered_queue.push_back(entry); | |
| 536 } else { | |
| 537 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 538 FROM_HERE, base::Bind(entry.second, code, std::string())); | |
| 539 } | |
| 540 } | |
| 541 ppd_resolution_queue_ = std::move(filtered_queue); | |
| 542 } | |
| 543 | |
| 544 // Given a list of possible locale strings (e.g. 'en-GB'), determine which of | |
| 545 // them we should use to best serve results for the browser locale (which was | |
| 546 // given to us at construction time). | |
| 547 std::string GetBestLocale(const std::vector<std::string>& available_locales) { | |
| 548 // First look for an exact match. If we find one, just use that. | |
| 549 for (const std::string& available : available_locales) { | |
| 550 if (available == browser_locale_) { | |
| 551 return available; | |
| 552 } | |
| 553 } | |
| 554 | |
| 555 // Next, look for an available locale that is a parent of browser_locale_. | |
| 556 // Return the most specific one. For example, if we want 'en-GB-foo' and we | |
| 557 // don't have an exact match, but we do have 'en-GB' and 'en', we will | |
| 558 // return 'en-GB' -- the most specific match which is a parent of the | |
| 559 // requested locale. | |
| 560 size_t best_len = 0; | |
| 561 size_t best_idx = -1; | |
| 562 for (size_t i = 0; i < available_locales.size(); ++i) { | |
| 563 const std::string& available = available_locales[i]; | |
| 564 if (base::StringPiece(browser_locale_).starts_with(available + "-") && | |
| 565 available.size() > best_len) { | |
| 566 best_len = available.size(); | |
| 567 best_idx = i; | |
| 568 } | |
| 569 } | |
| 570 if (best_idx != static_cast<size_t>(-1)) { | |
| 571 return available_locales[best_idx]; | |
| 572 } | |
| 573 | |
| 574 // Last chance for a match, look for the locale that matches the *most* | |
| 575 // pieces of locale_, with ties broken by being least specific. So for | |
| 576 // example, if we have 'es-GB', 'es-GB-foo' but no 'es' available, and we're | |
| 577 // requesting something for 'es', we'll get back 'es-GB' -- the least | |
| 578 // specific thing that matches some of the locale. | |
| 579 std::vector<base::StringPiece> browser_locale_pieces = | |
| 580 base::SplitStringPiece(browser_locale_, "-", base::KEEP_WHITESPACE, | |
| 581 base::SPLIT_WANT_ALL); | |
| 582 size_t best_match_size = 0; | |
| 583 size_t best_match_specificity; | |
| 584 best_idx = -1; | |
| 585 for (size_t i = 0; i < available_locales.size(); ++i) { | |
| 586 const std::string& available = available_locales[i]; | |
| 587 std::vector<base::StringPiece> available_pieces = base::SplitStringPiece( | |
| 588 available, "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); | |
| 589 size_t match_size = 0; | |
| 590 for (; match_size < available_pieces.size() && | |
| 591 match_size < browser_locale_pieces.size(); | |
| 592 ++match_size) { | |
| 593 if (available_pieces[match_size] != browser_locale_pieces[match_size]) { | |
| 594 break; | |
| 249 } | 595 } |
| 250 } | 596 } |
| 251 } | 597 if (match_size > 0 && |
| 252 fetch_data->done_callback.Run(PpdProvider::SUCCESS, *result); | 598 (best_idx == static_cast<size_t>(-1) || |
| 253 if (!result->empty()) { | 599 match_size > best_match_size || |
| 254 cache_->StoreAvailablePrinters(std::move(result)); | 600 (match_size == best_match_size && |
| 255 } else { | 601 available_pieces.size() < best_match_specificity))) { |
| 256 // An empty map means something is probably wrong; if we cache this map, | 602 best_idx = i; |
| 257 // we'll have an empty map until the cache expires. So complain and | 603 best_match_size = match_size; |
| 258 // refuse to cache. | 604 best_match_specificity = available_pieces.size(); |
| 259 LOG(ERROR) << "Available printers map is unexpectedly empty. Refusing " | 605 } |
| 260 "to cache this."; | 606 } |
| 261 } | 607 if (best_idx != static_cast<size_t>(-1)) { |
| 262 } | 608 return available_locales[best_idx]; |
| 263 | 609 } |
| 264 private: | 610 |
| 265 void StoreResolveFetchData(std::unique_ptr<ResolveFetchData> fetch_data) { | 611 // Everything else failed. Throw up our hands and default to english. |
| 266 auto raw_ptr = fetch_data->fetcher.get(); | 612 return "en"; |
| 267 base::AutoLock lock(resolve_fetches_lock_); | 613 } |
| 268 resolve_fetches_[raw_ptr] = std::move(fetch_data); | 614 |
| 269 } | 615 // If |fetcher| succeeded in its fetch, get the response in |response| and |
| 270 | 616 // return true, otherwise return false. In all cases, resets |fetcher_|. |
| 271 void StoreQueryAvailableFetchData( | 617 bool ValidateAndGetResponseAsString(std::string* contents) { |
| 272 std::unique_ptr<QueryAvailableFetchData> fetch_data) { | 618 bool ret = |
| 273 auto raw_ptr = fetch_data->fetcher.get(); | 619 ((fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS) && |
| 274 base::AutoLock lock(query_available_fetches_lock_); | 620 (fetcher_->GetResponseCode() == net::HTTP_OK) && |
| 275 query_available_fetches_[raw_ptr] = std::move(fetch_data); | 621 fetcher_->GetResponseAsString(contents)); |
| 276 } | 622 fetcher_.reset(); |
| 277 | |
| 278 // Extract the result of a resolve url fetch into |ppd_contents| and | |
| 279 // |last_updated time|. Returns true on success. | |
| 280 bool ExtractResolveResponseData(const net::URLFetcher* source, | |
| 281 const ResolveFetchData* fetch, | |
| 282 std::string* ppd_contents, | |
| 283 uint64_t* last_updated_time) { | |
| 284 std::string contents; | |
| 285 if (!ValidateAndGetResponseAsString(*source, &contents)) { | |
| 286 LOG(WARNING) << "Response not validated"; | |
| 287 return false; | |
| 288 } | |
| 289 | |
| 290 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); | |
| 291 if (dict == nullptr) { | |
| 292 LOG(WARNING) << "Response not a dictionary"; | |
| 293 return false; | |
| 294 } | |
| 295 std::string last_updated_time_string; | |
| 296 if (!dict->GetString(kJSONPPDKey, ppd_contents) || | |
| 297 !dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) || | |
| 298 !base::StringToUint64(last_updated_time_string, last_updated_time)) { | |
| 299 // Malformed response. TODO(justincarlson) - LOG something here? | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 if (ppd_contents->size() > options_.max_ppd_contents_size_) { | |
| 304 LOG(WARNING) << "PPD too large"; | |
| 305 // PPD is too big. | |
| 306 // | |
| 307 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to | |
| 308 // a user that's not from an explicitly trusted source, we should also | |
| 309 // check *uncompressed* size here to head off zip-bombs (e.g. let's | |
| 310 // compress 1GBs of zeros into a 900kb file and see what cups does when it | |
| 311 // tries to expand that...) | |
| 312 ppd_contents->clear(); | |
| 313 return false; | |
| 314 } | |
| 315 return base::Base64Decode(*ppd_contents, ppd_contents); | |
| 316 } | |
| 317 | |
| 318 // Return the ResolveFetchData associated with |source|. | |
| 319 std::unique_ptr<ResolveFetchData> GetResolveFetchData( | |
| 320 const net::URLFetcher* source) { | |
| 321 base::AutoLock l(resolve_fetches_lock_); | |
| 322 auto it = resolve_fetches_.find(source); | |
| 323 CHECK(it != resolve_fetches_.end()); | |
| 324 auto ret = std::move(it->second); | |
| 325 resolve_fetches_.erase(it); | |
| 326 return ret; | 623 return ret; |
| 327 } | 624 } |
| 328 | 625 |
| 329 // Return the QueryAvailableFetchData associated with |source|. | 626 // Many of our metadata fetches happens to be in the form of a JSON |
| 330 std::unique_ptr<QueryAvailableFetchData> GetQueryAvailableFetchData( | 627 // list-of-lists-of-2-strings. So this just attempts to parse a JSON reply to |
| 331 const net::URLFetcher* source) { | 628 // |fetcher| into the passed contents vector. A return code of SUCCESS means |
| 332 base::AutoLock lock(query_available_fetches_lock_); | 629 // the JSON was formatted as expected and we've parsed it into |contents|. On |
| 333 auto it = query_available_fetches_.find(source); | 630 // error the contents of |contents| cleared. |
| 334 CHECK(it != query_available_fetches_.end()) << "Fetch data not found!"; | 631 PpdProvider::CallbackResultCode ValidateAndParseJSONResponse( |
| 335 auto fetch_data = std::move(it->second); | 632 std::vector<std::pair<std::string, std::string>>* contents) { |
| 336 query_available_fetches_.erase(it); | 633 contents->clear(); |
| 337 return fetch_data; | 634 std::string buffer; |
| 338 } | 635 if (!ValidateAndGetResponseAsString(&buffer)) { |
| 339 | 636 return PpdProvider::SERVER_ERROR; |
| 340 // Trivial wrappers around PpdCache::Find() and | 637 } |
| 341 // PpdCache::FindAvailablePrinters(). We need these wrappers both because we | 638 auto top_list = base::ListValue::From(base::JSONReader::Read(buffer)); |
| 342 // use weak_ptrs to manage lifetime, and so we need to bind callbacks to | 639 |
| 343 // *this*, and because weak_ptr's preclude return values in posted tasks, so | 640 if (top_list.get() == nullptr) { |
| 344 // we have to use a parameter to return state. | 641 // We got something malformed back. |
| 345 void ResolveDoCacheLookup( | 642 return PpdProvider::INTERNAL_ERROR; |
| 346 const Printer::PpdReference& reference, | 643 } |
| 347 base::Optional<base::FilePath>* cache_result) const { | 644 for (const auto& entry : *top_list) { |
| 348 *cache_result = cache_->Find(reference); | 645 base::ListValue* sub_list; |
| 349 } | 646 contents->push_back({}); |
| 350 | 647 if (!entry->GetAsList(&sub_list) || sub_list->GetSize() != 2 || |
| 351 void QueryAvailableDoCacheLookup( | 648 !sub_list->GetString(0, &contents->back().first) || |
| 352 base::Optional<PpdProvider::AvailablePrintersMap>* cache_result) const { | 649 !sub_list->GetString(1, &contents->back().second)) { |
| 353 *cache_result = cache_->FindAvailablePrinters(); | 650 contents->clear(); |
| 354 } | 651 return PpdProvider::INTERNAL_ERROR; |
| 355 | 652 } |
| 356 // Callback that happens when the Resolve() cache lookup completes. If the | 653 } |
| 357 // cache satisfied the request, finish the Resolve, otherwise start a URL | 654 return PpdProvider::SUCCESS; |
| 358 // request to satisfy the request. This runs on the same thread as Resolve() | 655 } |
| 359 // was invoked on. | 656 |
| 360 void ResolveCacheLookupDone( | 657 // Create the list of manufacturers from |cached_metadata_|. Requires that |
| 361 const Printer::PpdReference& ppd_reference, | 658 // the manufacturer list has already been resolved. |
| 362 const PpdProvider::ResolveCallback& done_callback, | 659 std::vector<std::string> GetManufacturerList() const { |
| 363 std::unique_ptr<base::Optional<base::FilePath>> cache_result) { | 660 CHECK(cached_metadata_.get() != nullptr); |
| 364 if (*cache_result) { | 661 std::vector<std::string> ret; |
| 365 // Cache hit. Schedule the callback now and return. | 662 ret.reserve(cached_metadata_->size()); |
| 366 done_callback.Run(PpdProvider::SUCCESS, cache_result->value()); | 663 for (const auto& entry : *cached_metadata_) { |
| 367 return; | 664 ret.push_back(entry.first); |
| 368 } | 665 } |
| 369 | 666 // TODO(justincarlson) -- this should be a localization-aware sort. |
| 370 // We don't have a way to automatically resolve user-supplied PPDs yet. So | 667 sort(ret.begin(), ret.end()); |
| 371 // if we have one specified, and it's not cached, we fail out rather than | 668 return ret; |
| 372 // fall back to quirks-server based resolution. The reasoning here is that | 669 } |
| 373 // if the user has specified a PPD when a quirks-server one exists, it | 670 |
| 374 // probably means the quirks server one doesn't work for some reason, so we | 671 // Get the list of printers from a given manufacturer from |cached_metadata_|. |
| 375 // shouldn't silently use it. | 672 // Requires that we have already resolved this from the server. |
| 376 if (!ppd_reference.user_supplied_ppd_url.empty()) { | 673 std::vector<std::string> GetManufacturerPrinterList( |
| 377 done_callback.Run(PpdProvider::NOT_FOUND, base::FilePath()); | 674 const ManufacturerMetadata& meta) const { |
| 378 return; | 675 CHECK(meta.printers.get() != nullptr); |
| 379 } | 676 std::vector<std::string> ret; |
| 380 | 677 ret.reserve(meta.printers->size()); |
| 381 auto fetch_data = base::MakeUnique<ResolveFetchData>(); | 678 for (const auto& entry : *meta.printers) { |
| 382 fetch_data->ppd_reference = ppd_reference; | 679 ret.push_back(entry.first); |
| 383 fetch_data->done_callback = done_callback; | 680 } |
| 384 fetch_data->fetcher = | 681 // TODO(justincarlson) -- this should be a localization-aware sort. |
| 385 net::URLFetcher::Create(GetQuirksServerPpdLookupURL(ppd_reference), | 682 sort(ret.begin(), ret.end()); |
| 386 net::URLFetcher::GET, &resolve_delegate_); | 683 return ret; |
| 387 | 684 } |
| 388 fetch_data->fetcher->SetRequestContext(url_context_getter_.get()); | 685 |
| 389 fetch_data->fetcher->SetLoadFlags( | 686 // Map from (localized) manufacturer name to metadata for that manufacturer. |
| 390 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | 687 // This is populated lazily. If we don't yet have a manufacturer list, the |
| 391 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | 688 // top pointer will be null. When we create the top level map, then each |
| 392 net::LOAD_DO_NOT_SEND_AUTH_DATA); | 689 // value will only contain a reference which can be used to resolve the |
| 393 auto* fetcher = fetch_data->fetcher.get(); | 690 // printer list from that manufacturer. On demand, we use these references to |
| 394 StoreResolveFetchData(std::move(fetch_data)); | 691 // resolve the actual printer lists. |
| 395 fetcher->Start(); | 692 std::unique_ptr<std::unordered_map<std::string, ManufacturerMetadata>> |
| 396 } | 693 cached_metadata_; |
| 397 | 694 |
| 398 // Generate a url to look up a manufacturer/model from the quirks server | 695 // Cached contents of the server index, which maps a |
| 399 GURL GetQuirksServerPpdLookupURL( | 696 // PpdReference::effective_make_and_model to a url for the corresponding |
| 400 const Printer::PpdReference& ppd_reference) const { | 697 // ppd. |
| 401 return GURL(base::StringPrintf( | 698 // Null until we have fetched the index. |
| 402 "https://%s/v2/printer/manufacturers/%s/models/%s?key=%s", | 699 std::unique_ptr<std::unordered_map<std::string, std::string>> |
| 403 options_.quirks_server.c_str(), | 700 cached_ppd_index_; |
| 404 ppd_reference.effective_manufacturer.c_str(), | 701 |
| 405 ppd_reference.effective_model.c_str(), api_key_.c_str())); | 702 // Queued ResolveManufacturers() calls. We will simultaneously resolve |
| 406 } | 703 // all queued requests, so no need for a deque here. |
| 407 | 704 std::vector<ResolveManufacturersCallback> manufacturers_resolution_queue_; |
| 408 // Generate a url to ask for the full supported printer list from the quirks | 705 |
| 409 // server. | 706 // Queued ResolvePrinters() calls. |
| 410 GURL GetQuirksServerPpdListURL() const { | 707 std::deque<PrinterResolutionQueueEntry> printers_resolution_queue_; |
| 411 return GURL(base::StringPrintf("https://%s/v2/printer/list?key=%s", | 708 |
| 412 options_.quirks_server.c_str(), | 709 // Queued ResolvePpd() requests. |
| 413 api_key_.c_str())); | 710 std::deque<std::pair<Printer::PpdReference, ResolvePpdCallback>> |
| 414 } | 711 ppd_resolution_queue_; |
| 415 | 712 |
| 416 // If |fetcher| succeeded in its fetch, get the response in |response| and | 713 // If the fetcher is active, what's it fetching? |
| 417 // return true, otherwise return false. | 714 FetcherTarget fetcher_target_; |
| 418 bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher, | 715 |
| 419 std::string* contents) { | 716 // Fetcher used for all network fetches. This is explicitly reset() when |
| 420 return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && | 717 // a fetch has been processed. |
| 421 (fetcher.GetResponseCode() == net::HTTP_OK) && | 718 std::unique_ptr<net::URLFetcher> fetcher_; |
| 422 fetcher.GetResponseAsString(contents); | 719 |
| 423 } | 720 // Locale of the browser, as returned by |
| 424 | 721 // BrowserContext::GetApplicationLocale(); |
| 425 // API key for accessing quirks server. | 722 const std::string browser_locale_; |
| 426 const std::string api_key_; | |
| 427 | 723 |
| 428 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; | 724 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; |
| 429 scoped_refptr<base::SequencedTaskRunner> io_task_runner_; | 725 |
| 430 std::unique_ptr<PpdCache> cache_; | 726 // Cache of ppd files. |
| 727 scoped_refptr<PpdCache> ppd_cache_; | |
| 431 | 728 |
| 432 // Construction-time options, immutable. | 729 // Construction-time options, immutable. |
| 433 const PpdProvider::Options options_; | 730 const PpdProvider::Options options_; |
| 434 | 731 |
| 435 ResolveURLFetcherDelegate resolve_delegate_; | |
| 436 QueryAvailableURLFetcherDelegate query_available_delegate_; | |
| 437 | |
| 438 // Active resolve fetches and associated lock. | |
| 439 std::unordered_map<const net::URLFetcher*, std::unique_ptr<ResolveFetchData>> | |
| 440 resolve_fetches_; | |
| 441 base::Lock resolve_fetches_lock_; | |
| 442 | |
| 443 // Active QueryAvailable() fetches and associated lock. | |
| 444 std::unordered_map<const net::URLFetcher*, | |
| 445 std::unique_ptr<QueryAvailableFetchData>> | |
| 446 query_available_fetches_; | |
| 447 base::Lock query_available_fetches_lock_; | |
| 448 | |
| 449 base::WeakPtrFactory<PpdProviderImpl> weak_factory_; | 732 base::WeakPtrFactory<PpdProviderImpl> weak_factory_; |
| 733 | |
| 734 protected: | |
| 735 ~PpdProviderImpl() override {} | |
| 450 }; | 736 }; |
| 451 | 737 |
| 452 void ResolveURLFetcherDelegate::OnURLFetchComplete( | 738 } // namespace |
| 453 const net::URLFetcher* source) { | 739 |
| 454 parent_->OnResolveFetchComplete(source); | 740 // static |
| 741 scoped_refptr<PpdProvider> PpdProvider::Create( | |
| 742 const std::string& browser_locale, | |
| 743 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | |
| 744 scoped_refptr<PpdCache> ppd_cache, | |
| 745 const PpdProvider::Options& options) { | |
| 746 return scoped_refptr<PpdProvider>(new PpdProviderImpl( | |
| 747 browser_locale, url_context_getter, ppd_cache, options)); | |
| 455 } | 748 } |
| 456 | |
| 457 void QueryAvailableURLFetcherDelegate::OnURLFetchComplete( | |
| 458 const net::URLFetcher* source) { | |
| 459 parent_->OnQueryAvailableFetchComplete(source); | |
| 460 } | |
| 461 | |
| 462 } // namespace | |
| 463 | |
| 464 // static | |
| 465 std::unique_ptr<PpdProvider> PpdProvider::Create( | |
| 466 const std::string& api_key, | |
| 467 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | |
| 468 scoped_refptr<base::SequencedTaskRunner> io_task_runner, | |
| 469 std::unique_ptr<PpdCache> cache, | |
| 470 const PpdProvider::Options& options) { | |
| 471 return base::MakeUnique<PpdProviderImpl>( | |
| 472 api_key, url_context_getter, io_task_runner, std::move(cache), options); | |
| 473 } | |
| 474 | |
| 475 } // namespace printing | 749 } // namespace printing |
| 476 } // namespace chromeos | 750 } // namespace chromeos |
| OLD | NEW |