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

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: Plumb through rest of changes, address skau comments. Created 3 years, 11 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"
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698