Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Side by Side Diff: chromeos/printing/ppd_provider.cc

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

Powered by Google App Engine
This is Rietveld 408576698