| 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 <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
| 10 #include "base/json/json_parser.h" | 10 #include "base/json/json_parser.h" |
| 11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/sequence_checker.h" | 12 #include "base/sequence_checker.h" |
| 13 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "base/task_runner_util.h" |
| 16 #include "base/threading/sequenced_task_runner_handle.h" | 17 #include "base/threading/sequenced_task_runner_handle.h" |
| 17 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 18 #include "base/values.h" | 19 #include "base/values.h" |
| 19 #include "chromeos/printing/ppd_cache.h" | 20 #include "chromeos/printing/ppd_cache.h" |
| 20 #include "net/base/load_flags.h" | 21 #include "net/base/load_flags.h" |
| 21 #include "net/http/http_status_code.h" | 22 #include "net/http/http_status_code.h" |
| 22 #include "net/url_request/url_fetcher.h" | 23 #include "net/url_request/url_fetcher.h" |
| 23 #include "net/url_request/url_fetcher_delegate.h" | 24 #include "net/url_request/url_fetcher_delegate.h" |
| 24 #include "net/url_request/url_request_context_getter.h" | 25 #include "net/url_request/url_request_context_getter.h" |
| 25 #include "url/gurl.h" | 26 #include "url/gurl.h" |
| 26 | 27 |
| 27 namespace chromeos { | 28 namespace chromeos { |
| 28 namespace printing { | 29 namespace printing { |
| 29 namespace { | 30 namespace { |
| 30 | 31 |
| 31 // Expected fields from the quirks server. | 32 // Expected fields from the quirks server. |
| 32 const char kJSONPPDKey[] = "compressedPpd"; | 33 const char kJSONPPDKey[] = "compressedPpd"; |
| 33 const char kJSONLastUpdatedKey[] = "lastUpdatedTime"; | 34 const char kJSONLastUpdatedKey[] = "lastUpdatedTime"; |
| 34 const char kJSONTopListKey[] = "manufacturers"; | 35 const char kJSONTopListKey[] = "manufacturers"; |
| 35 const char kJSONManufacturer[] = "manufacturer"; | 36 const char kJSONManufacturer[] = "manufacturer"; |
| 36 const char kJSONModelList[] = "models"; | 37 const char kJSONModelList[] = "models"; |
| 37 | 38 |
| 38 class PpdProviderImpl; | 39 class PpdProviderImpl; |
| 39 | 40 |
| 40 // URLFetcherDelegate that just forwards the complete callback back to | 41 // URLFetcherDelegate that just invokes a callback when the fetch is complete. |
| 41 // the PpdProvider that owns the delegate. | |
| 42 class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate { | 42 class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate { |
| 43 public: | 43 public: |
| 44 explicit ForwardingURLFetcherDelegate(PpdProviderImpl* owner) | 44 explicit ForwardingURLFetcherDelegate( |
| 45 : owner_(owner) {} | 45 const base::Callback<void()>& done_callback) |
| 46 : done_callback_(done_callback) {} |
| 46 ~ForwardingURLFetcherDelegate() override {} | 47 ~ForwardingURLFetcherDelegate() override {} |
| 47 | 48 |
| 48 // URLFetcherDelegate API method. Defined below since we need the | 49 // URLFetcherDelegate API method. Defined below since we need the |
| 49 // PpdProviderImpl definition first. | 50 // PpdProviderImpl definition first. |
| 50 void OnURLFetchComplete(const net::URLFetcher* source) override; | 51 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 52 done_callback_.Run(); |
| 53 } |
| 51 | 54 |
| 52 private: | 55 private: |
| 53 // owner of this delegate. | 56 // Callback to be run on fetch complete. |
| 54 PpdProviderImpl* const owner_; | 57 base::Callback<void()> done_callback_; |
| 55 }; | 58 }; |
| 56 | 59 |
| 57 class PpdProviderImpl : public PpdProvider { | 60 class PpdProviderImpl : public PpdProvider { |
| 58 public: | 61 public: |
| 59 PpdProviderImpl( | 62 PpdProviderImpl( |
| 60 const std::string& api_key, | 63 const std::string& api_key, |
| 61 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | 64 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 65 scoped_refptr<base::SequencedTaskRunner> io_task_runner, |
| 62 std::unique_ptr<PpdCache> cache, | 66 std::unique_ptr<PpdCache> cache, |
| 63 const PpdProvider::Options& options) | 67 const PpdProvider::Options& options) |
| 64 : api_key_(api_key), | 68 : api_key_(api_key), |
| 65 forwarding_delegate_(this), | |
| 66 url_context_getter_(url_context_getter), | 69 url_context_getter_(url_context_getter), |
| 70 io_task_runner_(io_task_runner), |
| 67 cache_(std::move(cache)), | 71 cache_(std::move(cache)), |
| 68 options_(options) { | 72 options_(options), |
| 73 weak_factory_(this) { |
| 69 CHECK_GT(options_.max_ppd_contents_size_, 0U); | 74 CHECK_GT(options_.max_ppd_contents_size_, 0U); |
| 70 } | 75 } |
| 71 ~PpdProviderImpl() override {} | 76 ~PpdProviderImpl() override {} |
| 72 | 77 |
| 73 void Resolve(const Printer::PpdReference& ppd_reference, | 78 void Resolve(const Printer::PpdReference& ppd_reference, |
| 74 const PpdProvider::ResolveCallback& cb) override { | 79 const PpdProvider::ResolveCallback& cb) override { |
| 75 CHECK(!cb.is_null()); | 80 CHECK(!cb.is_null()); |
| 76 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); | 81 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); |
| 77 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | 82 CHECK(base::SequencedTaskRunnerHandle::IsSet()) |
| 78 << "Resolve must be called from a SequencedTaskRunner context"; | 83 << "Resolve must be called from a SequencedTaskRunner context"; |
| 79 CHECK(resolve_fetcher_ == nullptr) | 84 CHECK(!resolve_inflight_) |
| 80 << "Can't have concurrent PpdProvider Resolve calls"; | 85 << "Can't have concurrent PpdProvider Resolve calls"; |
| 86 resolve_inflight_ = true; |
| 87 auto cache_result = base::MakeUnique<base::Optional<base::FilePath>>(); |
| 88 bool post_result = io_task_runner_->PostTaskAndReply( |
| 89 FROM_HERE, base::Bind(&PpdProviderImpl::ResolveDoCacheLookup, |
| 90 weak_factory_.GetWeakPtr(), ppd_reference, |
| 91 cache_result.get()), |
| 92 base::Bind(&PpdProviderImpl::ResolveCacheLookupDone, |
| 93 weak_factory_.GetWeakPtr(), ppd_reference, cb, |
| 94 std::move(cache_result))); |
| 95 DCHECK(post_result); |
| 96 } |
| 81 | 97 |
| 82 base::Optional<base::FilePath> tmp = cache_->Find(ppd_reference); | 98 void QueryAvailable(const QueryAvailableCallback& cb) override { |
| 83 if (tmp) { | 99 CHECK(!cb.is_null()); |
| 100 CHECK(base::SequencedTaskRunnerHandle::IsSet()) |
| 101 << "QueryAvailable() must be called from a SequencedTaskRunner context"; |
| 102 auto cache_result = base::MakeUnique< |
| 103 base::Optional<const PpdProvider::AvailablePrintersMap*>>(); |
| 104 CHECK(!query_inflight_) |
| 105 << "Can't have concurrent PpdProvider QueryAvailable calls"; |
| 106 query_inflight_ = true; |
| 107 CHECK(io_task_runner_->PostTaskAndReply( |
| 108 FROM_HERE, base::Bind(&PpdProviderImpl::QueryAvailableDoCacheLookup, |
| 109 weak_factory_.GetWeakPtr(), cache_result.get()), |
| 110 base::Bind(&PpdProviderImpl::QueryAvailableCacheLookupDone, |
| 111 weak_factory_.GetWeakPtr(), cb, std::move(cache_result)))); |
| 112 } |
| 113 |
| 114 bool CachePpd(const Printer::PpdReference& ppd_reference, |
| 115 const base::FilePath& ppd_path) override { |
| 116 std::string buf; |
| 117 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf, |
| 118 options_.max_ppd_contents_size_)) { |
| 119 return false; |
| 120 } |
| 121 return static_cast<bool>(cache_->Store(ppd_reference, buf)); |
| 122 } |
| 123 |
| 124 private: |
| 125 // Trivial wrappers around PpdCache::Find() and |
| 126 // PpdCache::FindAvailablePrinters(). We need these wrappers both because we |
| 127 // use weak_ptrs to manage lifetime, and so we need to bind callbacks to |
| 128 // *this*, and because weak_ptr's preclude return values in posted tasks, so |
| 129 // we have to use a parameter to return state. |
| 130 void ResolveDoCacheLookup( |
| 131 const Printer::PpdReference& reference, |
| 132 base::Optional<base::FilePath>* cache_result) const { |
| 133 *cache_result = cache_->Find(reference); |
| 134 } |
| 135 |
| 136 void QueryAvailableDoCacheLookup( |
| 137 base::Optional<const PpdProvider::AvailablePrintersMap*>* cache_result) |
| 138 const { |
| 139 auto tmp = cache_->FindAvailablePrinters(); |
| 140 if (tmp != nullptr) { |
| 141 *cache_result = tmp; |
| 142 } else { |
| 143 *cache_result = base::nullopt; |
| 144 } |
| 145 } |
| 146 |
| 147 // Callback that happens when the Resolve() cache lookup completes. If the |
| 148 // cache satisfied the request, finish the Resolve, otherwise start a URL |
| 149 // request to satisfy the request. This runs on the same thread as Resolve() |
| 150 // was invoked on. |
| 151 void ResolveCacheLookupDone( |
| 152 const Printer::PpdReference& ppd_reference, |
| 153 const PpdProvider::ResolveCallback& done_callback, |
| 154 const std::unique_ptr<base::Optional<base::FilePath>>& cache_result) { |
| 155 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); |
| 156 if (*cache_result) { |
| 84 // Cache hit. Schedule the callback now and return. | 157 // Cache hit. Schedule the callback now and return. |
| 85 base::SequencedTaskRunnerHandle::Get()->PostTask( | 158 resolve_inflight_ = false; |
| 86 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, tmp.value())); | 159 done_callback.Run(PpdProvider::SUCCESS, cache_result->value()); |
| 87 return; | 160 return; |
| 88 } | 161 } |
| 89 | 162 |
| 90 // We don't have a way to automatically resolve user-supplied PPDs yet. So | 163 // We don't have a way to automatically resolve user-supplied PPDs yet. So |
| 91 // if we have one specified, and it's not cached, we fail out rather than | 164 // if we have one specified, and it's not cached, we fail out rather than |
| 92 // fall back to quirks-server based resolution. The reasoning here is that | 165 // fall back to quirks-server based resolution. The reasoning here is that |
| 93 // if the user has specified a PPD when a quirks-server one exists, it | 166 // if the user has specified a PPD when a quirks-server one exists, it |
| 94 // probably means the quirks server one doesn't work for some reason, so we | 167 // probably means the quirks server one doesn't work for some reason, so we |
| 95 // shouldn't silently use it. | 168 // shouldn't silently use it. |
| 96 if (!ppd_reference.user_supplied_ppd_url.empty()) { | 169 if (!ppd_reference.user_supplied_ppd_url.empty()) { |
| 97 base::SequencedTaskRunnerHandle::Get()->PostTask( | 170 resolve_inflight_ = false; |
| 98 FROM_HERE, base::Bind(cb, PpdProvider::NOT_FOUND, base::FilePath())); | 171 done_callback.Run(PpdProvider::NOT_FOUND, base::FilePath()); |
| 99 return; | 172 return; |
| 100 } | 173 } |
| 101 | 174 |
| 102 resolve_reference_ = ppd_reference; | 175 // Missed in the cache, so start a URLRequest to resolve the request. |
| 103 resolve_done_callback_ = cb; | 176 resolve_fetcher_delegate_ = base::MakeUnique<ForwardingURLFetcherDelegate>( |
| 177 base::Bind(&PpdProviderImpl::OnResolveFetchComplete, |
| 178 weak_factory_.GetWeakPtr(), ppd_reference, done_callback)); |
| 104 | 179 |
| 105 resolve_fetcher_ = | 180 resolve_fetcher_ = net::URLFetcher::Create( |
| 106 net::URLFetcher::Create(GetQuirksServerPpdLookupURL(ppd_reference), | 181 GetQuirksServerPpdLookupURL(ppd_reference), net::URLFetcher::GET, |
| 107 net::URLFetcher::GET, &forwarding_delegate_); | 182 resolve_fetcher_delegate_.get()); |
| 183 |
| 108 resolve_fetcher_->SetRequestContext(url_context_getter_.get()); | 184 resolve_fetcher_->SetRequestContext(url_context_getter_.get()); |
| 109 resolve_fetcher_->SetLoadFlags( | 185 resolve_fetcher_->SetLoadFlags( |
| 110 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | 186 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | |
| 111 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | 187 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | |
| 112 net::LOAD_DO_NOT_SEND_AUTH_DATA); | 188 net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| 113 resolve_fetcher_->Start(); | 189 resolve_fetcher_->Start(); |
| 114 }; | |
| 115 | |
| 116 void AbortResolve() override { | |
| 117 // UrlFetcher guarantees that when the object has been destroyed, no further | |
| 118 // callbacks will occur. | |
| 119 resolve_fetcher_.reset(); | |
| 120 } | 190 } |
| 121 | 191 |
| 122 void QueryAvailable(const QueryAvailableCallback& cb) override { | |
| 123 CHECK(!cb.is_null()); | |
| 124 CHECK(query_sequence_checker_.CalledOnValidSequence()); | |
| 125 CHECK(base::SequencedTaskRunnerHandle::IsSet()) | |
| 126 << "QueryAvailable() must be called from a SequencedTaskRunner context"; | |
| 127 CHECK(query_fetcher_ == nullptr) | |
| 128 << "Can't have concurrent PpdProvider QueryAvailable() calls"; | |
| 129 | |
| 130 const PpdProvider::AvailablePrintersMap* result = | |
| 131 cache_->FindAvailablePrinters(); | |
| 132 if (result != nullptr) { | |
| 133 // Satisfy from cache. | |
| 134 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 135 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, *result)); | |
| 136 return; | |
| 137 } | |
| 138 // Not in the cache, ask QuirksServer. | |
| 139 query_done_callback_ = cb; | |
| 140 | |
| 141 query_fetcher_ = | |
| 142 net::URLFetcher::Create(GetQuirksServerPpdListURL(), | |
| 143 net::URLFetcher::GET, &forwarding_delegate_); | |
| 144 query_fetcher_->SetRequestContext(url_context_getter_.get()); | |
| 145 query_fetcher_->SetLoadFlags( | |
| 146 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | |
| 147 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | | |
| 148 net::LOAD_DO_NOT_SEND_AUTH_DATA); | |
| 149 query_fetcher_->Start(); | |
| 150 } | |
| 151 | |
| 152 void AbortQueryAvailable() override { | |
| 153 // UrlFetcher guarantees that when the object has been destroyed, no further | |
| 154 // callbacks will occur. | |
| 155 query_fetcher_.reset(); | |
| 156 } | |
| 157 | |
| 158 bool CachePpd(const Printer::PpdReference& ppd_reference, | |
| 159 const base::FilePath& ppd_path) override { | |
| 160 std::string buf; | |
| 161 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf, | |
| 162 options_.max_ppd_contents_size_)) { | |
| 163 return false; | |
| 164 } | |
| 165 return static_cast<bool>(cache_->Store(ppd_reference, buf)); | |
| 166 } | |
| 167 | |
| 168 // Route to the proper fetch complete handler based on which fetcher caused | |
| 169 // it. | |
| 170 void OnURLFetchComplete(const net::URLFetcher* fetcher) { | |
| 171 if (fetcher == resolve_fetcher_.get()) { | |
| 172 OnResolveFetchComplete(); | |
| 173 } else if (fetcher == query_fetcher_.get()) { | |
| 174 OnQueryAvailableFetchComplete(); | |
| 175 } else { | |
| 176 NOTREACHED() << "Unknown fetcher completed."; | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 private: | |
| 181 // Called on the same thread as Resolve() when the fetcher completes its | 192 // Called on the same thread as Resolve() when the fetcher completes its |
| 182 // fetch. | 193 // fetch. |
| 183 void OnResolveFetchComplete() { | 194 void OnResolveFetchComplete( |
| 195 const Printer::PpdReference& ppd_reference, |
| 196 const PpdProvider::ResolveCallback& done_callback) { |
| 184 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); | 197 CHECK(resolve_sequence_checker_.CalledOnValidSequence()); |
| 185 // Scope the allocated |resolve_fetcher_| into this function so we clean it | 198 // Scope the allocated |resolve_fetcher_| and |resolve_fetcher_delegate_| |
| 186 // up when we're done here instead of leaving it around until the next | 199 // into this function so we clean it up when we're done here instead of |
| 187 // Resolve() call. | 200 // leaving it around until the next Resolve() call. |
| 188 auto fetcher = std::move(resolve_fetcher_); | 201 auto fetcher_delegate(std::move(resolve_fetcher_delegate_)); |
| 202 auto fetcher(std::move(resolve_fetcher_)); |
| 203 resolve_inflight_ = false; |
| 189 std::string contents; | 204 std::string contents; |
| 190 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { | 205 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { |
| 191 // Something went wrong with the fetch. | 206 // Something went wrong with the fetch. |
| 192 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 207 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 193 return; | 208 return; |
| 194 } | 209 } |
| 195 | 210 |
| 196 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); | 211 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); |
| 197 if (dict == nullptr) { | 212 if (dict == nullptr) { |
| 198 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 213 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 199 return; | 214 return; |
| 200 } | 215 } |
| 201 std::string ppd_contents; | 216 std::string ppd_contents; |
| 202 std::string last_updated_time_string; | 217 std::string last_updated_time_string; |
| 203 uint64_t last_updated_time; | 218 uint64_t last_updated_time; |
| 204 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) && | 219 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) && |
| 205 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) && | 220 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) && |
| 206 base::StringToUint64(last_updated_time_string, &last_updated_time))) { | 221 base::StringToUint64(last_updated_time_string, &last_updated_time))) { |
| 207 // Malformed response. TODO(justincarlson) - LOG something here? | 222 // Malformed response. TODO(justincarlson) - LOG something here? |
| 208 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 223 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 209 return; | 224 return; |
| 210 } | 225 } |
| 211 | 226 |
| 212 if (ppd_contents.size() > options_.max_ppd_contents_size_) { | 227 if (ppd_contents.size() > options_.max_ppd_contents_size_) { |
| 213 // PPD is too big. | 228 // PPD is too big. |
| 214 // | 229 // |
| 215 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to | 230 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to |
| 216 // a user that's not from an explicitly trusted source, we should also | 231 // a user that's not from an explicitly trusted source, we should also |
| 217 // check *uncompressed* size here to head off zip-bombs (e.g. let's | 232 // check *uncompressed* size here to head off zip-bombs (e.g. let's |
| 218 // compress 1GBs of zeros into a 900kb file and see what cups does when it | 233 // compress 1GBs of zeros into a 900kb file and see what cups does when it |
| 219 // tries to expand that...) | 234 // tries to expand that...) |
| 220 resolve_done_callback_.Run(PpdProvider::SERVER_ERROR, base::FilePath()); | 235 done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath()); |
| 221 return; | 236 return; |
| 222 } | 237 } |
| 223 | 238 |
| 224 auto ppd_file = cache_->Store(resolve_reference_, ppd_contents); | 239 auto ppd_file = cache_->Store(ppd_reference, ppd_contents); |
| 225 if (!ppd_file) { | 240 if (!ppd_file) { |
| 226 // Failed to store. | 241 // Failed to store. |
| 227 resolve_done_callback_.Run(PpdProvider::INTERNAL_ERROR, base::FilePath()); | 242 done_callback.Run(PpdProvider::INTERNAL_ERROR, base::FilePath()); |
| 228 return; | 243 return; |
| 229 } | 244 } |
| 230 resolve_done_callback_.Run(PpdProvider::SUCCESS, ppd_file.value()); | 245 done_callback.Run(PpdProvider::SUCCESS, ppd_file.value()); |
| 231 } | 246 } |
| 232 | 247 |
| 233 // Called on the same thread as QueryAvailable() when the fetcher completes | 248 // Called on the same thread as QueryAvailable() when the cache lookup is |
| 234 // its fetch. | 249 // done. If the cache satisfied the request, finish the Query, otherwise |
| 235 void OnQueryAvailableFetchComplete() { | 250 // start a URL request to satisfy the Query. This runs on the same thread as |
| 251 // QueryAvailable() was invoked on. |
| 252 void QueryAvailableCacheLookupDone( |
| 253 const PpdProvider::QueryAvailableCallback& done_callback, |
| 254 const std::unique_ptr< |
| 255 base::Optional<const PpdProvider::AvailablePrintersMap*>>& |
| 256 cache_result) { |
| 236 CHECK(query_sequence_checker_.CalledOnValidSequence()); | 257 CHECK(query_sequence_checker_.CalledOnValidSequence()); |
| 237 // Scope the object fetcher into this function so we clean it up when we're | 258 if (*cache_result) { |
| 238 // done here instead of leaving it around until the next QueryAvailable() | 259 query_inflight_ = false; |
| 239 // call. | 260 done_callback.Run(PpdProvider::SUCCESS, *cache_result->value()); |
| 240 auto fetcher = std::move(query_fetcher_); | 261 return; |
| 262 } |
| 263 // Missed in the cache, start a query. |
| 264 query_fetcher_delegate_ = base::MakeUnique<ForwardingURLFetcherDelegate>( |
| 265 base::Bind(&PpdProviderImpl::OnQueryAvailableFetchComplete, |
| 266 weak_factory_.GetWeakPtr(), done_callback)); |
| 267 |
| 268 query_fetcher_ = net::URLFetcher::Create(GetQuirksServerPpdListURL(), |
| 269 net::URLFetcher::GET, |
| 270 query_fetcher_delegate_.get()); |
| 271 query_fetcher_->SetRequestContext(url_context_getter_.get()); |
| 272 query_fetcher_->SetLoadFlags( |
| 273 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | |
| 274 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | |
| 275 net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| 276 query_fetcher_->Start(); |
| 277 } |
| 278 |
| 279 void OnQueryAvailableFetchComplete( |
| 280 const PpdProvider::QueryAvailableCallback& done_callback) { |
| 281 CHECK(query_sequence_checker_.CalledOnValidSequence()); |
| 282 // Scope the object fetcher and task_runner into this function so we clean |
| 283 // it up when we're done here instead of leaving it around until the next |
| 284 // QueryAvailable() call. |
| 285 auto fetcher_delegate(std::move(query_fetcher_delegate_)); |
| 286 auto fetcher(std::move(query_fetcher_)); |
| 287 query_inflight_ = false; |
| 241 std::string contents; | 288 std::string contents; |
| 242 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { | 289 if (!ValidateAndGetResponseAsString(*fetcher, &contents)) { |
| 243 // Something went wrong with the fetch. | 290 // Something went wrong with the fetch. |
| 244 query_done_callback_.Run(PpdProvider::SERVER_ERROR, | 291 done_callback.Run(PpdProvider::SERVER_ERROR, AvailablePrintersMap()); |
| 245 AvailablePrintersMap()); | |
| 246 return; | 292 return; |
| 247 } | 293 } |
| 248 | 294 |
| 249 // The server gives us JSON in the form of a list of (manufacturer, list of | 295 // The server gives us JSON in the form of a list of (manufacturer, list of |
| 250 // models) tuples. | 296 // models) tuples. |
| 251 auto top_dict = | 297 auto top_dict = |
| 252 base::DictionaryValue::From(base::JSONReader::Read(contents)); | 298 base::DictionaryValue::From(base::JSONReader::Read(contents)); |
| 253 const base::ListValue* top_list; | 299 const base::ListValue* top_list; |
| 254 if (top_dict == nullptr || !top_dict->GetList(kJSONTopListKey, &top_list)) { | 300 if (top_dict == nullptr || !top_dict->GetList(kJSONTopListKey, &top_list)) { |
| 255 LOG(ERROR) << "Malformed response from quirks server"; | 301 LOG(ERROR) << "Malformed response from quirks server"; |
| 256 query_done_callback_.Run(PpdProvider::SERVER_ERROR, | 302 done_callback.Run(PpdProvider::SERVER_ERROR, AvailablePrintersMap()); |
| 257 PpdProvider::AvailablePrintersMap()); | |
| 258 return; | 303 return; |
| 259 } | 304 } |
| 260 | 305 |
| 261 auto result = base::MakeUnique<PpdProvider::AvailablePrintersMap>(); | 306 auto result = base::MakeUnique<PpdProvider::AvailablePrintersMap>(); |
| 262 for (const std::unique_ptr<base::Value>& entry : *top_list) { | 307 for (const std::unique_ptr<base::Value>& entry : *top_list) { |
| 263 base::DictionaryValue* dict; | 308 base::DictionaryValue* dict; |
| 264 std::string manufacturer; | 309 std::string manufacturer; |
| 265 std::string model; | 310 std::string model; |
| 266 base::ListValue* model_list; | 311 base::ListValue* model_list; |
| 267 if (!entry->GetAsDictionary(&dict) || | 312 if (!entry->GetAsDictionary(&dict) || |
| 268 !dict->GetString(kJSONManufacturer, &manufacturer) || | 313 !dict->GetString(kJSONManufacturer, &manufacturer) || |
| 269 !dict->GetList(kJSONModelList, &model_list)) { | 314 !dict->GetList(kJSONModelList, &model_list)) { |
| 270 LOG(ERROR) << "Unexpected contents in quirks server printer list."; | 315 LOG(ERROR) << "Unexpected contents in quirks server printer list."; |
| 271 // Just skip this entry instead of aborting the whole thing. | 316 // Just skip this entry instead of aborting the whole thing. |
| 272 continue; | 317 continue; |
| 273 } | 318 } |
| 274 | 319 |
| 275 std::vector<std::string>& dest = (*result)[manufacturer]; | 320 std::vector<std::string>& dest = (*result)[manufacturer]; |
| 276 for (const std::unique_ptr<base::Value>& model_value : *model_list) { | 321 for (const std::unique_ptr<base::Value>& model_value : *model_list) { |
| 277 if (model_value->GetAsString(&model)) { | 322 if (model_value->GetAsString(&model)) { |
| 278 dest.push_back(model); | 323 dest.push_back(model); |
| 279 } else { | 324 } else { |
| 280 LOG(ERROR) << "Skipping unknown model for manufacturer " | 325 LOG(ERROR) << "Skipping unknown model for manufacturer " |
| 281 << manufacturer; | 326 << manufacturer; |
| 282 } | 327 } |
| 283 } | 328 } |
| 284 } | 329 } |
| 285 query_done_callback_.Run(PpdProvider::SUCCESS, *result); | 330 done_callback.Run(PpdProvider::SUCCESS, *result); |
| 286 if (!result->empty()) { | 331 if (!result->empty()) { |
| 287 cache_->StoreAvailablePrinters(std::move(result)); | 332 cache_->StoreAvailablePrinters(std::move(result)); |
| 288 } else { | 333 } else { |
| 289 // An empty map means something is probably wrong; if we cache this map, | 334 // An empty map means something is probably wrong; if we cache this map, |
| 290 // we'll have an empty map until the cache expires. So complain and | 335 // we'll have an empty map until the cache expires. So complain and |
| 291 // refuse to cache. | 336 // refuse to cache. |
| 292 LOG(ERROR) << "Available printers map is unexpectedly empty. Refusing " | 337 LOG(ERROR) << "Available printers map is unexpectedly empty. Refusing " |
| 293 "to cache this."; | 338 "to cache this."; |
| 294 } | 339 } |
| 295 } | 340 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 316 // return true, otherwise return false. | 361 // return true, otherwise return false. |
| 317 bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher, | 362 bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher, |
| 318 std::string* contents) { | 363 std::string* contents) { |
| 319 return ((fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && | 364 return ((fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && |
| 320 (fetcher.GetResponseCode() == net::HTTP_OK) && | 365 (fetcher.GetResponseCode() == net::HTTP_OK) && |
| 321 fetcher.GetResponseAsString(contents)); | 366 fetcher.GetResponseAsString(contents)); |
| 322 } | 367 } |
| 323 | 368 |
| 324 // State held across a Resolve() call. | 369 // State held across a Resolve() call. |
| 325 | 370 |
| 326 // Reference we're currently trying to resolve. | |
| 327 Printer::PpdReference resolve_reference_; | |
| 328 | |
| 329 // Callback to invoke on completion. | |
| 330 PpdProvider::ResolveCallback resolve_done_callback_; | |
| 331 | |
| 332 // Check that Resolve() and its callback are sequenced appropriately. | 371 // Check that Resolve() and its callback are sequenced appropriately. |
| 333 base::SequenceChecker resolve_sequence_checker_; | 372 base::SequenceChecker resolve_sequence_checker_; |
| 334 | 373 |
| 335 // Fetcher for the current call, if any. | 374 // Fetcher and associated delegate for the current Resolve() call, if a fetch |
| 375 // is in progress. These are both null if no Resolve() is in flight. |
| 336 std::unique_ptr<net::URLFetcher> resolve_fetcher_; | 376 std::unique_ptr<net::URLFetcher> resolve_fetcher_; |
| 377 std::unique_ptr<ForwardingURLFetcherDelegate> resolve_fetcher_delegate_; |
| 378 // Is there a current Resolve() inflight? Used to help fail-fast in the case |
| 379 // of inappropriate concurrent usage. |
| 380 bool resolve_inflight_ = false; |
| 337 | 381 |
| 338 // State held across a QueryAvailable() call. | 382 // State held across a QueryAvailable() call. |
| 339 | 383 |
| 340 // Callback to invoke on completion. | |
| 341 PpdProvider::QueryAvailableCallback query_done_callback_; | |
| 342 | |
| 343 // Check that QueryAvailable() and its callback are sequenced appropriately. | 384 // Check that QueryAvailable() and its callback are sequenced appropriately. |
| 344 base::SequenceChecker query_sequence_checker_; | 385 base::SequenceChecker query_sequence_checker_; |
| 345 | 386 |
| 346 // Fetcher for the current call, if any. | 387 // Fetcher and associated delegate for the current QueryAvailable() call, if a |
| 388 // fetch is in progress. These are both null if no QueryAvailable() is in |
| 389 // flight. |
| 347 std::unique_ptr<net::URLFetcher> query_fetcher_; | 390 std::unique_ptr<net::URLFetcher> query_fetcher_; |
| 391 std::unique_ptr<ForwardingURLFetcherDelegate> query_fetcher_delegate_; |
| 392 // Is there a current QueryAvailable() inflight? Used to help fail-fast in |
| 393 // the case of inappropriate concurrent usage. |
| 394 bool query_inflight_ = false; |
| 348 | 395 |
| 349 // Common state. | 396 // Common state. |
| 350 | 397 |
| 351 // API key for accessing quirks server. | 398 // API key for accessing quirks server. |
| 352 const std::string api_key_; | 399 const std::string api_key_; |
| 353 | 400 |
| 354 ForwardingURLFetcherDelegate forwarding_delegate_; | |
| 355 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; | 401 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; |
| 402 scoped_refptr<base::SequencedTaskRunner> io_task_runner_; |
| 356 std::unique_ptr<PpdCache> cache_; | 403 std::unique_ptr<PpdCache> cache_; |
| 357 | 404 |
| 358 // Construction-time options, immutable. | 405 // Construction-time options, immutable. |
| 359 const PpdProvider::Options options_; | 406 const PpdProvider::Options options_; |
| 407 |
| 408 base::WeakPtrFactory<PpdProviderImpl> weak_factory_; |
| 360 }; | 409 }; |
| 361 | 410 |
| 362 void ForwardingURLFetcherDelegate::OnURLFetchComplete( | |
| 363 const net::URLFetcher* source) { | |
| 364 owner_->OnURLFetchComplete(source); | |
| 365 } | |
| 366 | |
| 367 } // namespace | 411 } // namespace |
| 368 | 412 |
| 369 // static | 413 // static |
| 370 std::unique_ptr<PpdProvider> PpdProvider::Create( | 414 std::unique_ptr<PpdProvider> PpdProvider::Create( |
| 371 const std::string& api_key, | 415 const std::string& api_key, |
| 372 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | 416 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 417 scoped_refptr<base::SequencedTaskRunner> io_task_runner, |
| 373 std::unique_ptr<PpdCache> cache, | 418 std::unique_ptr<PpdCache> cache, |
| 374 const PpdProvider::Options& options) { | 419 const PpdProvider::Options& options) { |
| 375 return base::MakeUnique<PpdProviderImpl>(api_key, url_context_getter, | 420 return base::MakeUnique<PpdProviderImpl>( |
| 376 std::move(cache), options); | 421 api_key, url_context_getter, io_task_runner, std::move(cache), options); |
| 377 } | 422 } |
| 378 | 423 |
| 379 } // namespace printing | 424 } // namespace printing |
| 380 } // namespace chromeos | 425 } // namespace chromeos |
| OLD | NEW |