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