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

Side by Side Diff: components/doodle/doodle_fetcher.cc

Issue 2707293002: [Doodle] Split DoodleFetcher into interface + Impl (Closed)
Patch Set: review Created 3 years, 9 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
« no previous file with comments | « components/doodle/doodle_fetcher.h ('k') | components/doodle/doodle_fetcher_impl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/doodle/doodle_fetcher.h"
6
7 #include <utility>
8
9 #include "base/strings/string_number_conversions.h"
10 #include "base/time/clock.h"
11 #include "base/time/default_clock.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "components/data_use_measurement/core/data_use_user_data.h"
15 #include "components/google/core/browser/google_url_tracker.h"
16 #include "components/google/core/browser/google_util.h"
17 #include "net/base/load_flags.h"
18 #include "net/http/http_status_code.h"
19 #include "net/url_request/url_fetcher.h"
20
21 using net::URLFetcher;
22
23 namespace doodle {
24
25 namespace {
26
27 const double kMaxTimeToLiveMS = 30.0 * 24 * 60 * 60 * 1000; // 30 days
28
29 const char kDoodleConfigPath[] = "/async/ddljson";
30
31 std::string StripSafetyPreamble(const std::string& json) {
32 // The response may start with )]}'. Ignore this.
33 const char kResponsePreamble[] = ")]}'";
34
35 base::StringPiece json_sp(json);
36 if (json_sp.starts_with(kResponsePreamble)) {
37 json_sp.remove_prefix(strlen(kResponsePreamble));
38 }
39
40 return json_sp.as_string();
41 }
42
43 DoodleType ParseDoodleType(const base::DictionaryValue& ddljson) {
44 std::string type_str;
45 ddljson.GetString("doodle_type", &type_str);
46 if (type_str == "SIMPLE") {
47 return DoodleType::SIMPLE;
48 }
49 if (type_str == "RANDOM") {
50 return DoodleType::RANDOM;
51 }
52 if (type_str == "VIDEO") {
53 return DoodleType::VIDEO;
54 }
55 if (type_str == "INTERACTIVE") {
56 return DoodleType::INTERACTIVE;
57 }
58 if (type_str == "INLINE_INTERACTIVE") {
59 return DoodleType::INLINE_INTERACTIVE;
60 }
61 if (type_str == "SLIDESHOW") {
62 return DoodleType::SLIDESHOW;
63 }
64 return DoodleType::UNKNOWN;
65 }
66
67 } // namespace
68
69 DoodleImage::DoodleImage()
70 : height(0), width(0), is_animated_gif(false), is_cta(false) {}
71 DoodleImage::~DoodleImage() = default;
72
73 DoodleConfig::DoodleConfig() : doodle_type(DoodleType::UNKNOWN) {}
74 DoodleConfig::DoodleConfig(const DoodleConfig& config) = default;
75 DoodleConfig::~DoodleConfig() = default;
76
77 DoodleFetcher::DoodleFetcher(
78 scoped_refptr<net::URLRequestContextGetter> download_context,
79 GoogleURLTracker* google_url_tracker,
80 const ParseJSONCallback& json_parsing_callback)
81 : download_context_(download_context),
82 json_parsing_callback_(json_parsing_callback),
83 google_url_tracker_(google_url_tracker),
84 clock_(new base::DefaultClock()),
85 weak_ptr_factory_(this) {
86 DCHECK(google_url_tracker_);
87 }
88
89 DoodleFetcher::~DoodleFetcher() = default;
90
91 void DoodleFetcher::FetchDoodle(FinishedCallback callback) {
92 if (IsFetchInProgress()) {
93 callbacks_.push_back(std::move(callback));
94 return; // The callback will be called for the existing request's results.
95 }
96 DCHECK(!fetcher_.get());
97 callbacks_.push_back(std::move(callback));
98 fetcher_ = URLFetcher::Create(GetGoogleBaseUrl().Resolve(kDoodleConfigPath),
99 URLFetcher::GET, this);
100 fetcher_->SetRequestContext(download_context_.get());
101 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
102 net::LOAD_DO_NOT_SAVE_COOKIES |
103 net::LOAD_DO_NOT_SEND_AUTH_DATA);
104 fetcher_->SetAutomaticallyRetryOnNetworkChanges(1);
105 data_use_measurement::DataUseUserData::AttachToFetcher(
106 fetcher_.get(), data_use_measurement::DataUseUserData::DOODLE);
107 fetcher_->Start();
108 }
109
110 void DoodleFetcher::OnURLFetchComplete(const URLFetcher* source) {
111 DCHECK_EQ(fetcher_.get(), source);
112 std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_);
113
114 std::string json_string;
115 if (!(source->GetStatus().is_success() &&
116 source->GetResponseCode() == net::HTTP_OK &&
117 source->GetResponseAsString(&json_string))) {
118 RespondToAllCallbacks(DoodleState::DOWNLOAD_ERROR, base::nullopt);
119 return;
120 }
121
122 json_parsing_callback_.Run(
123 StripSafetyPreamble(std::move(json_string)),
124 base::Bind(&DoodleFetcher::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
125 base::Bind(&DoodleFetcher::OnJsonParseFailed,
126 weak_ptr_factory_.GetWeakPtr()));
127 }
128
129 void DoodleFetcher::OnJsonParsed(std::unique_ptr<base::Value> json) {
130 std::unique_ptr<base::DictionaryValue> config =
131 base::DictionaryValue::From(std::move(json));
132 if (!config.get()) {
133 DLOG(WARNING) << "Doodle JSON is not valid";
134 RespondToAllCallbacks(DoodleState::PARSING_ERROR, base::nullopt);
135 return;
136 }
137
138 const base::DictionaryValue* ddljson = nullptr;
139 if (!config->GetDictionary("ddljson", &ddljson)) {
140 DLOG(WARNING) << "Doodle JSON reponse did not contain 'ddljson' key.";
141 RespondToAllCallbacks(DoodleState::PARSING_ERROR, base::nullopt);
142 return;
143 }
144
145 base::Optional<DoodleConfig> doodle = ParseDoodle(*ddljson);
146 if (!doodle.has_value()) {
147 RespondToAllCallbacks(DoodleState::NO_DOODLE, base::nullopt);
148 return;
149 }
150
151 RespondToAllCallbacks(DoodleState::AVAILABLE, std::move(doodle));
152 }
153
154 void DoodleFetcher::OnJsonParseFailed(const std::string& error_message) {
155 DLOG(WARNING) << "JSON parsing failed: " << error_message;
156 RespondToAllCallbacks(DoodleState::PARSING_ERROR, base::nullopt);
157 }
158
159 base::Optional<DoodleConfig> DoodleFetcher::ParseDoodle(
160 const base::DictionaryValue& ddljson) const {
161 DoodleConfig doodle;
162 if (!ParseImage(ddljson, "large_image", &doodle.large_image)) {
163 return base::nullopt;
164 }
165 ParseImage(ddljson, "transparent_large_image",
166 &doodle.transparent_large_image);
167 ParseImage(ddljson, "large_cta_image", &doodle.large_cta_image);
168 ParseBaseInformation(ddljson, &doodle);
169 return doodle;
170 }
171
172 bool DoodleFetcher::ParseImage(const base::DictionaryValue& image_parent,
173 const std::string& image_name,
174 DoodleImage* image) const {
175 DCHECK(image);
176 const base::DictionaryValue* image_dict = nullptr;
177 if (!image_parent.GetDictionary(image_name, &image_dict)) {
178 return false;
179 }
180 image->url = ParseRelativeUrl(*image_dict, "url");
181 if (!image->url.is_valid()) {
182 DLOG(WARNING) << "Image URL for \"" << image_name << "\" is invalid.";
183 return false;
184 }
185 image_dict->GetInteger("height", &image->height);
186 image_dict->GetInteger("width", &image->width);
187 image_dict->GetBoolean("is_animated_gif", &image->is_animated_gif);
188 image_dict->GetBoolean("is_cta", &image->is_cta);
189 return true;
190 }
191
192 void DoodleFetcher::ParseBaseInformation(const base::DictionaryValue& ddljson,
193 DoodleConfig* config) const {
194 config->search_url = ParseRelativeUrl(ddljson, "search_url");
195 config->target_url = ParseRelativeUrl(ddljson, "target_url");
196 config->fullpage_interactive_url =
197 ParseRelativeUrl(ddljson, "fullpage_interactive_url");
198
199 config->doodle_type = ParseDoodleType(ddljson);
200 ddljson.GetString("alt_text", &config->alt_text);
201 ddljson.GetString("interactive_html", &config->interactive_html);
202
203 // The JSON doesn't guarantee the number to fit into an int.
204 double ttl = 0; // Expires immediately if the parameter is missing.
205 if (!ddljson.GetDouble("time_to_live_ms", &ttl) || ttl < 0) {
206 DLOG(WARNING) << "No valid Doodle image TTL present in ddljson!";
207 ttl = 0;
208 }
209 if (ttl > kMaxTimeToLiveMS) {
210 ttl = kMaxTimeToLiveMS;
211 DLOG(WARNING) << "Clamping Doodle image TTL to 30 days!";
212 }
213 config->expiry_date = clock_->Now() + base::TimeDelta::FromMillisecondsD(ttl);
214 }
215
216 GURL DoodleFetcher::ParseRelativeUrl(const base::DictionaryValue& dict_value,
217 const std::string& key) const {
218 std::string str_url;
219 dict_value.GetString(key, &str_url);
220 if (str_url.empty()) {
221 return GURL();
222 }
223 return GetGoogleBaseUrl().Resolve(str_url);
224 }
225
226 void DoodleFetcher::RespondToAllCallbacks(
227 DoodleState state,
228 const base::Optional<DoodleConfig>& config) {
229 for (auto& callback : callbacks_) {
230 std::move(callback).Run(state, config);
231 }
232 callbacks_.clear();
233 }
234
235 GURL DoodleFetcher::GetGoogleBaseUrl() const {
236 GURL cmd_line_url = google_util::CommandLineGoogleBaseURL();
237 if (cmd_line_url.is_valid()) {
238 return cmd_line_url;
239 }
240 return google_url_tracker_->google_url();
241 }
242
243 } // namespace doodle
OLDNEW
« no previous file with comments | « components/doodle/doodle_fetcher.h ('k') | components/doodle/doodle_fetcher_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698