Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "components/ntp_snippets/remote/ntp_snippet.h" | 5 #include "components/ntp_snippets/remote/ntp_snippet.h" |
| 6 | 6 |
| 7 #include "base/memory/ptr_util.h" | 7 #include "base/memory/ptr_util.h" |
| 8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "base/strings/stringprintf.h" | 9 #include "base/strings/stringprintf.h" |
| 10 #include "base/values.h" | 10 #include "base/values.h" |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 | 73 |
| 74 const int kArticlesRemoteId = 1; | 74 const int kArticlesRemoteId = 1; |
| 75 static_assert( | 75 static_assert( |
| 76 static_cast<int>(KnownCategories::ARTICLES) - | 76 static_cast<int>(KnownCategories::ARTICLES) - |
| 77 static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET) == | 77 static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET) == |
| 78 kArticlesRemoteId, | 78 kArticlesRemoteId, |
| 79 "kArticlesRemoteId has a wrong value?!"); | 79 "kArticlesRemoteId has a wrong value?!"); |
| 80 | 80 |
| 81 const int kChromeReaderDefaultExpiryTimeMins = 3 * 24 * 60; | 81 const int kChromeReaderDefaultExpiryTimeMins = 3 * 24 * 60; |
| 82 | 82 |
| 83 NTPSnippet::NTPSnippet(const std::string& id, int remote_category_id) | 83 NTPSnippet::NTPSnippet(const std::vector<std::string>& ids, |
| 84 : ids_(1, id), | 84 int remote_category_id) |
| 85 : ids_(ids), | |
| 85 score_(0), | 86 score_(0), |
| 86 is_dismissed_(false), | 87 is_dismissed_(false), |
| 87 remote_category_id_(remote_category_id) {} | 88 remote_category_id_(remote_category_id) {} |
| 88 | 89 |
| 89 NTPSnippet::~NTPSnippet() = default; | 90 NTPSnippet::~NTPSnippet() = default; |
| 90 | 91 |
| 91 // static | 92 // static |
| 92 std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromChromeReaderDictionary( | 93 std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromChromeReaderDictionary( |
| 93 const base::DictionaryValue& dict) { | 94 const base::DictionaryValue& dict) { |
| 94 const base::DictionaryValue* content = nullptr; | 95 const base::DictionaryValue* content = nullptr; |
| 95 if (!dict.GetDictionary("contentInfo", &content)) { | 96 if (!dict.GetDictionary("contentInfo", &content)) { |
| 96 return nullptr; | 97 return nullptr; |
| 97 } | 98 } |
| 98 | 99 |
| 99 // Need at least the id. | 100 // Need at least a primary id. |
| 100 std::string id; | 101 std::string id; |
|
tschumann
2016/11/25 15:55:26
nit: rename 'id' to 'primary_id'?
Marc Treib
2016/11/25 16:57:36
Done.
| |
| 101 if (!content->GetString("url", &id) || id.empty()) { | 102 if (!content->GetString("url", &id) || id.empty()) { |
| 102 return nullptr; | 103 return nullptr; |
| 103 } | 104 } |
| 104 | 105 |
| 105 std::unique_ptr<NTPSnippet> snippet(new NTPSnippet(id, kArticlesRemoteId)); | |
|
Marc Treib
2016/11/25 14:22:51
Sorry for the large diff - I had to move stuff aro
| |
| 106 | |
| 107 std::string title; | |
| 108 if (content->GetString("title", &title)) { | |
| 109 snippet->title_ = title; | |
| 110 } | |
| 111 std::string salient_image_url; | |
| 112 if (content->GetString("thumbnailUrl", &salient_image_url)) { | |
| 113 snippet->salient_image_url_ = GURL(salient_image_url); | |
| 114 } | |
| 115 std::string snippet_str; | |
| 116 if (content->GetString("snippet", &snippet_str)) { | |
| 117 snippet->snippet_ = snippet_str; | |
| 118 } | |
| 119 // The creation and expiry timestamps are uint64s which are stored as strings. | |
| 120 std::string creation_timestamp_str; | |
| 121 if (content->GetString("creationTimestampSec", &creation_timestamp_str)) { | |
| 122 snippet->publish_date_ = TimeFromJsonString(creation_timestamp_str); | |
| 123 } | |
| 124 std::string expiry_timestamp_str; | |
| 125 if (content->GetString("expiryTimestampSec", &expiry_timestamp_str)) { | |
| 126 snippet->expiry_date_ = TimeFromJsonString(expiry_timestamp_str); | |
| 127 } | |
| 128 | |
| 129 // If publish and/or expiry date are missing, fill in reasonable defaults. | |
| 130 if (snippet->publish_date_.is_null()) { | |
| 131 snippet->publish_date_ = base::Time::Now(); | |
| 132 } | |
| 133 if (snippet->expiry_date_.is_null()) { | |
| 134 snippet->expiry_date_ = | |
| 135 snippet->publish_date() + | |
| 136 base::TimeDelta::FromMinutes(kChromeReaderDefaultExpiryTimeMins); | |
| 137 } | |
| 138 | |
| 139 const base::ListValue* corpus_infos_list = nullptr; | 106 const base::ListValue* corpus_infos_list = nullptr; |
| 140 if (!content->GetList("sourceCorpusInfo", &corpus_infos_list)) { | 107 if (!content->GetList("sourceCorpusInfo", &corpus_infos_list)) { |
| 141 DLOG(WARNING) << "No sources found for article " << title; | 108 DLOG(WARNING) << "No sources found for article " << id; |
| 142 return nullptr; | 109 return nullptr; |
| 143 } | 110 } |
| 144 | 111 |
| 145 std::vector<std::string> additional_ids; | 112 std::vector<std::string> ids(1, id); |
| 146 std::vector<SnippetSource> sources; | 113 std::vector<SnippetSource> sources; |
| 147 for (const auto& value : *corpus_infos_list) { | 114 for (const auto& value : *corpus_infos_list) { |
| 148 const base::DictionaryValue* dict_value = nullptr; | 115 const base::DictionaryValue* dict_value = nullptr; |
| 149 if (!value->GetAsDictionary(&dict_value)) { | 116 if (!value->GetAsDictionary(&dict_value)) { |
| 150 DLOG(WARNING) << "Invalid source info for article " << id; | 117 DLOG(WARNING) << "Invalid source info for article " << id; |
| 151 continue; | 118 continue; |
| 152 } | 119 } |
| 153 | 120 |
| 154 std::string corpus_id_str; | 121 std::string corpus_id_str; |
| 155 GURL corpus_id; | 122 GURL corpus_id; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 179 if (dict_value->GetString("ampUrl", &_url_str)) { | 146 if (dict_value->GetString("ampUrl", &_url_str)) { |
| 180 amp_url = GURL(amp_url_str); | 147 amp_url = GURL(amp_url_str); |
| 181 DLOG_IF(WARNING, !amp_url.is_valid()) << "Invalid AMP url " | 148 DLOG_IF(WARNING, !amp_url.is_valid()) << "Invalid AMP url " |
| 182 << amp_url_str; | 149 << amp_url_str; |
| 183 } | 150 } |
| 184 sources.emplace_back(corpus_id, site_title, | 151 sources.emplace_back(corpus_id, site_title, |
| 185 amp_url.is_valid() ? amp_url : GURL()); | 152 amp_url.is_valid() ? amp_url : GURL()); |
| 186 // We use the raw string so that we can compare it against other primary | 153 // We use the raw string so that we can compare it against other primary |
| 187 // IDs. Parsing the ID as a URL might add a trailing slash (and we don't do | 154 // IDs. Parsing the ID as a URL might add a trailing slash (and we don't do |
| 188 // this for the primary ID). | 155 // this for the primary ID). |
| 189 additional_ids.push_back(corpus_id_str); | 156 ids.push_back(corpus_id_str); |
| 190 } | 157 } |
| 191 snippet->AddIDs(additional_ids); | |
| 192 | |
| 193 if (sources.empty()) { | 158 if (sources.empty()) { |
| 194 DLOG(WARNING) << "No sources found for article " << id; | 159 DLOG(WARNING) << "No sources found for article " << id; |
| 195 return nullptr; | 160 return nullptr; |
| 196 } | 161 } |
| 162 | |
| 163 std::unique_ptr<NTPSnippet> snippet(new NTPSnippet(ids, kArticlesRemoteId)); | |
| 164 | |
| 165 std::string title; | |
| 166 if (content->GetString("title", &title)) { | |
| 167 snippet->title_ = title; | |
| 168 } | |
| 169 std::string salient_image_url; | |
| 170 if (content->GetString("thumbnailUrl", &salient_image_url)) { | |
| 171 snippet->salient_image_url_ = GURL(salient_image_url); | |
| 172 } | |
| 173 std::string snippet_str; | |
| 174 if (content->GetString("snippet", &snippet_str)) { | |
| 175 snippet->snippet_ = snippet_str; | |
| 176 } | |
| 177 // The creation and expiry timestamps are uint64s which are stored as strings. | |
| 178 std::string creation_timestamp_str; | |
| 179 if (content->GetString("creationTimestampSec", &creation_timestamp_str)) { | |
| 180 snippet->publish_date_ = TimeFromJsonString(creation_timestamp_str); | |
| 181 } | |
| 182 std::string expiry_timestamp_str; | |
| 183 if (content->GetString("expiryTimestampSec", &expiry_timestamp_str)) { | |
| 184 snippet->expiry_date_ = TimeFromJsonString(expiry_timestamp_str); | |
| 185 } | |
| 186 | |
| 187 // If publish and/or expiry date are missing, fill in reasonable defaults. | |
| 188 if (snippet->publish_date_.is_null()) { | |
| 189 snippet->publish_date_ = base::Time::Now(); | |
| 190 } | |
| 191 if (snippet->expiry_date_.is_null()) { | |
| 192 snippet->expiry_date_ = | |
| 193 snippet->publish_date() + | |
| 194 base::TimeDelta::FromMinutes(kChromeReaderDefaultExpiryTimeMins); | |
| 195 } | |
| 196 | |
| 197 const SnippetSource& source = FindBestSource(sources); | 197 const SnippetSource& source = FindBestSource(sources); |
| 198 snippet->url_ = source.url; | 198 snippet->url_ = source.url; |
| 199 snippet->publisher_name_ = source.publisher_name; | 199 snippet->publisher_name_ = source.publisher_name; |
| 200 snippet->amp_url_ = source.amp_url; | 200 snippet->amp_url_ = source.amp_url; |
| 201 | 201 |
| 202 double score; | 202 double score; |
| 203 if (dict.GetDouble("score", &score)) { | 203 if (dict.GetDouble("score", &score)) { |
| 204 snippet->score_ = score; | 204 snippet->score_ = score; |
| 205 } | 205 } |
| 206 | 206 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 220 std::string id; | 220 std::string id; |
| 221 if (!value->GetAsString(&id)) { | 221 if (!value->GetAsString(&id)) { |
| 222 return nullptr; | 222 return nullptr; |
| 223 } | 223 } |
| 224 parsed_ids.push_back(id); | 224 parsed_ids.push_back(id); |
| 225 } | 225 } |
| 226 | 226 |
| 227 if (parsed_ids.empty()) { | 227 if (parsed_ids.empty()) { |
| 228 return nullptr; | 228 return nullptr; |
| 229 } | 229 } |
| 230 auto snippet = | 230 auto snippet = base::MakeUnique<NTPSnippet>(parsed_ids, remote_category_id); |
| 231 base::MakeUnique<NTPSnippet>(parsed_ids.front(), remote_category_id); | |
| 232 parsed_ids.erase(parsed_ids.begin(), parsed_ids.begin() + 1); | |
| 233 snippet->AddIDs(parsed_ids); | |
| 234 | 231 |
| 235 if (!(dict.GetString("title", &snippet->title_) && | 232 if (!(dict.GetString("title", &snippet->title_) && |
| 236 dict.GetString("snippet", &snippet->snippet_) && | 233 dict.GetString("snippet", &snippet->snippet_) && |
| 237 GetTimeValue(dict, "creationTime", &snippet->publish_date_) && | 234 GetTimeValue(dict, "creationTime", &snippet->publish_date_) && |
| 238 GetTimeValue(dict, "expirationTime", &snippet->expiry_date_) && | 235 GetTimeValue(dict, "expirationTime", &snippet->expiry_date_) && |
| 239 GetURLValue(dict, "imageUrl", &snippet->salient_image_url_) && | 236 GetURLValue(dict, "imageUrl", &snippet->salient_image_url_) && |
| 240 dict.GetString("attribution", &snippet->publisher_name_) && | 237 dict.GetString("attribution", &snippet->publisher_name_) && |
| 241 GetURLValue(dict, "fullPageUrl", &snippet->url_))) { | 238 GetURLValue(dict, "fullPageUrl", &snippet->url_))) { |
| 242 return nullptr; | 239 return nullptr; |
| 243 } | 240 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 257 const SnippetProto& proto) { | 254 const SnippetProto& proto) { |
| 258 // Need at least the id. | 255 // Need at least the id. |
| 259 if (proto.ids_size() == 0 || proto.ids(0).empty()) { | 256 if (proto.ids_size() == 0 || proto.ids(0).empty()) { |
| 260 return nullptr; | 257 return nullptr; |
| 261 } | 258 } |
| 262 | 259 |
| 263 int remote_category_id = proto.has_remote_category_id() | 260 int remote_category_id = proto.has_remote_category_id() |
| 264 ? proto.remote_category_id() | 261 ? proto.remote_category_id() |
| 265 : kArticlesRemoteId; | 262 : kArticlesRemoteId; |
| 266 | 263 |
| 267 auto snippet = base::MakeUnique<NTPSnippet>(proto.ids(0), remote_category_id); | 264 std::vector<std::string> ids(proto.ids().begin(), proto.ids().end()); |
| 268 snippet->AddIDs( | 265 auto snippet = base::MakeUnique<NTPSnippet>(ids, remote_category_id); |
| 269 std::vector<std::string>(proto.ids().begin() + 1, proto.ids().end())); | |
| 270 | 266 |
| 271 snippet->title_ = proto.title(); | 267 snippet->title_ = proto.title(); |
| 272 snippet->snippet_ = proto.snippet(); | 268 snippet->snippet_ = proto.snippet(); |
| 273 snippet->salient_image_url_ = GURL(proto.salient_image_url()); | 269 snippet->salient_image_url_ = GURL(proto.salient_image_url()); |
| 274 snippet->publish_date_ = base::Time::FromInternalValue(proto.publish_date()); | 270 snippet->publish_date_ = base::Time::FromInternalValue(proto.publish_date()); |
| 275 snippet->expiry_date_ = base::Time::FromInternalValue(proto.expiry_date()); | 271 snippet->expiry_date_ = base::Time::FromInternalValue(proto.expiry_date()); |
| 276 snippet->score_ = proto.score(); | 272 snippet->score_ = proto.score(); |
| 277 snippet->is_dismissed_ = proto.dismissed(); | 273 snippet->is_dismissed_ = proto.dismissed(); |
| 278 | 274 |
| 279 std::vector<SnippetSource> sources; | 275 std::vector<SnippetSource> sources; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 307 return snippet; | 303 return snippet; |
| 308 } | 304 } |
| 309 | 305 |
| 310 // static | 306 // static |
| 311 std::unique_ptr<NTPSnippet> NTPSnippet::CreateForTesting( | 307 std::unique_ptr<NTPSnippet> NTPSnippet::CreateForTesting( |
| 312 const std::string& id, | 308 const std::string& id, |
| 313 int remote_category_id, | 309 int remote_category_id, |
| 314 const GURL& url, | 310 const GURL& url, |
| 315 const std::string& publisher_name, | 311 const std::string& publisher_name, |
| 316 const GURL& amp_url) { | 312 const GURL& amp_url) { |
| 317 auto snippet = base::MakeUnique<NTPSnippet>(id, remote_category_id); | 313 auto snippet = base::MakeUnique<NTPSnippet>(std::vector<std::string>(1, id), |
| 314 remote_category_id); | |
| 318 snippet->url_ = url; | 315 snippet->url_ = url; |
| 319 snippet->publisher_name_ = publisher_name; | 316 snippet->publisher_name_ = publisher_name; |
| 320 snippet->amp_url_ = amp_url; | 317 snippet->amp_url_ = amp_url; |
| 321 | 318 |
| 322 return snippet; | 319 return snippet; |
| 323 } | 320 } |
| 324 | 321 |
| 325 SnippetProto NTPSnippet::ToProto() const { | 322 SnippetProto NTPSnippet::ToProto() const { |
| 326 SnippetProto result; | 323 SnippetProto result; |
| 327 for (const std::string& id : ids_) { | 324 for (const std::string& id : ids_) { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 351 if (!publisher_name_.empty()) { | 348 if (!publisher_name_.empty()) { |
| 352 source_proto->set_publisher_name(publisher_name_); | 349 source_proto->set_publisher_name(publisher_name_); |
| 353 } | 350 } |
| 354 if (amp_url_.is_valid()) { | 351 if (amp_url_.is_valid()) { |
| 355 source_proto->set_amp_url(amp_url_.spec()); | 352 source_proto->set_amp_url(amp_url_.spec()); |
| 356 } | 353 } |
| 357 | 354 |
| 358 return result; | 355 return result; |
| 359 } | 356 } |
| 360 | 357 |
| 361 void NTPSnippet::AddIDs(const std::vector<std::string>& ids) { | |
| 362 ids_.insert(ids_.end(), ids.begin(), ids.end()); | |
| 363 } | |
| 364 | |
| 365 // static | 358 // static |
| 366 base::Time NTPSnippet::TimeFromJsonString(const std::string& timestamp_str) { | 359 base::Time NTPSnippet::TimeFromJsonString(const std::string& timestamp_str) { |
| 367 int64_t timestamp; | 360 int64_t timestamp; |
| 368 if (!base::StringToInt64(timestamp_str, ×tamp)) { | 361 if (!base::StringToInt64(timestamp_str, ×tamp)) { |
| 369 // Even if there's an error in the conversion, some garbage data may still | 362 // Even if there's an error in the conversion, some garbage data may still |
| 370 // be written to the output var, so reset it. | 363 // be written to the output var, so reset it. |
| 371 DLOG(WARNING) << "Invalid json timestamp: " << timestamp_str; | 364 DLOG(WARNING) << "Invalid json timestamp: " << timestamp_str; |
| 372 timestamp = 0; | 365 timestamp = 0; |
| 373 } | 366 } |
| 374 return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(timestamp); | 367 return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(timestamp); |
| 375 } | 368 } |
| 376 | 369 |
| 377 // static | 370 // static |
| 378 std::string NTPSnippet::TimeToJsonString(const base::Time& time) { | 371 std::string NTPSnippet::TimeToJsonString(const base::Time& time) { |
| 379 return base::Int64ToString((time - base::Time::UnixEpoch()).InSeconds()); | 372 return base::Int64ToString((time - base::Time::UnixEpoch()).InSeconds()); |
| 380 } | 373 } |
| 381 | 374 |
| 382 } // namespace ntp_snippets | 375 } // namespace ntp_snippets |
| OLD | NEW |