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 "components/ntp_snippets/remote/remote_suggestions_fetcher.h" | 5 #include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" |
| 6 | 6 |
| 7 #include <cstdlib> | 7 #include <cstdlib> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 } | 129 } |
| 130 return true; | 130 return true; |
| 131 } | 131 } |
| 132 | 132 |
| 133 // Creates suggestions from dictionary values in |list| and adds them to | 133 // Creates suggestions from dictionary values in |list| and adds them to |
| 134 // |suggestions|. Returns true on success, false if anything went wrong. | 134 // |suggestions|. Returns true on success, false if anything went wrong. |
| 135 // |remote_category_id| is only used if |content_suggestions_api| is true. | 135 // |remote_category_id| is only used if |content_suggestions_api| is true. |
| 136 bool AddSuggestionsFromListValue(bool content_suggestions_api, | 136 bool AddSuggestionsFromListValue(bool content_suggestions_api, |
| 137 int remote_category_id, | 137 int remote_category_id, |
| 138 const base::ListValue& list, | 138 const base::ListValue& list, |
| 139 RemoteSuggestion::PtrVector* suggestions) { | 139 RemoteSuggestion::PtrVector* suggestions, |
| 140 const base::Time& fetch_time) { | |
| 140 for (const auto& value : list) { | 141 for (const auto& value : list) { |
| 141 const base::DictionaryValue* dict = nullptr; | 142 const base::DictionaryValue* dict = nullptr; |
| 142 if (!value->GetAsDictionary(&dict)) { | 143 if (!value->GetAsDictionary(&dict)) { |
| 143 return false; | 144 return false; |
| 144 } | 145 } |
| 145 | 146 |
| 146 std::unique_ptr<RemoteSuggestion> suggestion; | 147 std::unique_ptr<RemoteSuggestion> suggestion; |
| 147 if (content_suggestions_api) { | 148 if (content_suggestions_api) { |
| 148 suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary( | 149 suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary( |
| 149 *dict, remote_category_id); | 150 *dict, remote_category_id, fetch_time); |
| 150 } else { | 151 } else { |
| 151 suggestion = RemoteSuggestion::CreateFromChromeReaderDictionary(*dict); | 152 suggestion = |
| 153 RemoteSuggestion::CreateFromChromeReaderDictionary(*dict, fetch_time); | |
| 152 } | 154 } |
| 153 if (!suggestion) { | 155 if (!suggestion) { |
| 154 return false; | 156 return false; |
| 155 } | 157 } |
| 156 | 158 |
| 157 suggestions->push_back(std::move(suggestion)); | 159 suggestions->push_back(std::move(suggestion)); |
| 158 } | 160 } |
| 159 return true; | 161 return true; |
| 160 } | 162 } |
| 161 | 163 |
| (...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 StartTokenRequest(); | 437 StartTokenRequest(); |
| 436 } | 438 } |
| 437 | 439 |
| 438 void RemoteSuggestionsFetcher::JsonRequestDone( | 440 void RemoteSuggestionsFetcher::JsonRequestDone( |
| 439 std::unique_ptr<JsonRequest> request, | 441 std::unique_ptr<JsonRequest> request, |
| 440 SnippetsAvailableCallback callback, | 442 SnippetsAvailableCallback callback, |
| 441 std::unique_ptr<base::Value> result, | 443 std::unique_ptr<base::Value> result, |
| 442 FetchResult status_code, | 444 FetchResult status_code, |
| 443 const std::string& error_details) { | 445 const std::string& error_details) { |
| 444 DCHECK(request); | 446 DCHECK(request); |
| 447 // Record the time when request for fetching remote content snippets finished. | |
| 448 const base::Time fetch_time = base::Time::Now(); | |
|
Marc Treib
2017/02/10 16:16:56
This should use tick_clock_ (so it's injectable in
markusheintz_
2017/02/13 10:11:04
Done.
| |
| 449 | |
| 445 last_fetch_json_ = request->GetResponseString(); | 450 last_fetch_json_ = request->GetResponseString(); |
| 446 | 451 |
| 447 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", | 452 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", |
| 448 request->GetFetchDuration()); | 453 request->GetFetchDuration()); |
| 449 | 454 |
| 450 if (!result) { | 455 if (!result) { |
| 451 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, | 456 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, |
| 452 error_details); | 457 error_details); |
| 453 return; | 458 return; |
| 454 } | 459 } |
| 455 FetchedCategoriesVector categories; | 460 FetchedCategoriesVector categories; |
| 456 if (!JsonToSnippets(*result, &categories)) { | 461 |
| 462 if (!JsonToSnippets(*result, &categories, fetch_time)) { | |
| 457 LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_; | 463 LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_; |
| 458 FetchFinished(OptionalFetchedCategories(), std::move(callback), | 464 FetchFinished(OptionalFetchedCategories(), std::move(callback), |
| 459 FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string()); | 465 FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string()); |
| 460 return; | 466 return; |
| 461 } | 467 } |
| 462 // Filter out unwanted categories if necessary. | 468 // Filter out unwanted categories if necessary. |
| 463 // TODO(fhorschig): As soon as the server supports filtering by category, | 469 // TODO(fhorschig): As soon as the server supports filtering by category, |
| 464 // adjust the request instead of over-fetching and filtering here. | 470 // adjust the request instead of over-fetching and filtering here. |
| 465 FilterCategories(&categories, request->exclusive_category()); | 471 FilterCategories(&categories, request->exclusive_category()); |
| 466 | 472 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 482 static_cast<int>(FetchResult::RESULT_MAX)); | 488 static_cast<int>(FetchResult::RESULT_MAX)); |
| 483 | 489 |
| 484 DVLOG(1) << "Fetch finished: " << last_status_; | 490 DVLOG(1) << "Fetch finished: " << last_status_; |
| 485 | 491 |
| 486 std::move(callback).Run(FetchResultToStatus(fetch_result), | 492 std::move(callback).Run(FetchResultToStatus(fetch_result), |
| 487 std::move(categories)); | 493 std::move(categories)); |
| 488 } | 494 } |
| 489 | 495 |
| 490 bool RemoteSuggestionsFetcher::JsonToSnippets( | 496 bool RemoteSuggestionsFetcher::JsonToSnippets( |
| 491 const base::Value& parsed, | 497 const base::Value& parsed, |
| 492 FetchedCategoriesVector* categories) { | 498 FetchedCategoriesVector* categories, |
| 499 const base::Time& fetch_time) { | |
| 493 const base::DictionaryValue* top_dict = nullptr; | 500 const base::DictionaryValue* top_dict = nullptr; |
| 494 if (!parsed.GetAsDictionary(&top_dict)) { | 501 if (!parsed.GetAsDictionary(&top_dict)) { |
| 495 return false; | 502 return false; |
| 496 } | 503 } |
| 497 | 504 |
| 498 switch (fetch_api_) { | 505 switch (fetch_api_) { |
| 499 case FetchAPI::CHROME_READER_API: { | 506 case FetchAPI::CHROME_READER_API: { |
| 500 const int kUnusedRemoteCategoryId = -1; | 507 const int kUnusedRemoteCategoryId = -1; |
| 501 categories->push_back(FetchedCategory( | 508 categories->push_back(FetchedCategory( |
| 502 Category::FromKnownCategory(KnownCategories::ARTICLES), | 509 Category::FromKnownCategory(KnownCategories::ARTICLES), |
| 503 BuildArticleCategoryInfo(base::nullopt))); | 510 BuildArticleCategoryInfo(base::nullopt))); |
| 504 | 511 |
| 505 const base::ListValue* recos = nullptr; | 512 const base::ListValue* recos = nullptr; |
| 506 return top_dict->GetList("recos", &recos) && | 513 return top_dict->GetList("recos", &recos) && |
| 507 AddSuggestionsFromListValue(/*content_suggestions_api=*/false, | 514 AddSuggestionsFromListValue( |
| 508 kUnusedRemoteCategoryId, *recos, | 515 /*content_suggestions_api=*/false, kUnusedRemoteCategoryId, |
| 509 &categories->back().suggestions); | 516 *recos, &categories->back().suggestions, fetch_time); |
| 510 } | 517 } |
| 511 | 518 |
| 512 case FetchAPI::CHROME_CONTENT_SUGGESTIONS_API: { | 519 case FetchAPI::CHROME_CONTENT_SUGGESTIONS_API: { |
| 513 const base::ListValue* categories_value = nullptr; | 520 const base::ListValue* categories_value = nullptr; |
| 514 if (!top_dict->GetList("categories", &categories_value)) { | 521 if (!top_dict->GetList("categories", &categories_value)) { |
| 515 return false; | 522 return false; |
| 516 } | 523 } |
| 517 | 524 |
| 518 for (const auto& v : *categories_value) { | 525 for (const auto& v : *categories_value) { |
| 519 std::string utf8_title; | 526 std::string utf8_title; |
| 520 int remote_category_id = -1; | 527 int remote_category_id = -1; |
| 521 const base::DictionaryValue* category_value = nullptr; | 528 const base::DictionaryValue* category_value = nullptr; |
| 522 if (!(v->GetAsDictionary(&category_value) && | 529 if (!(v->GetAsDictionary(&category_value) && |
| 523 category_value->GetString("localizedTitle", &utf8_title) && | 530 category_value->GetString("localizedTitle", &utf8_title) && |
| 524 category_value->GetInteger("id", &remote_category_id) && | 531 category_value->GetInteger("id", &remote_category_id) && |
| 525 (remote_category_id > 0))) { | 532 (remote_category_id > 0))) { |
| 526 return false; | 533 return false; |
| 527 } | 534 } |
| 528 | 535 |
| 529 RemoteSuggestion::PtrVector suggestions; | 536 RemoteSuggestion::PtrVector suggestions; |
| 530 const base::ListValue* suggestions_list = nullptr; | 537 const base::ListValue* suggestions_list = nullptr; |
| 531 // Absence of a list of suggestions is treated as an empty list, which | 538 // Absence of a list of suggestions is treated as an empty list, which |
| 532 // is permissible. | 539 // is permissible. |
| 533 if (category_value->GetList("suggestions", &suggestions_list)) { | 540 if (category_value->GetList("suggestions", &suggestions_list)) { |
| 534 if (!AddSuggestionsFromListValue( | 541 if (!AddSuggestionsFromListValue( |
| 535 /*content_suggestions_api=*/true, remote_category_id, | 542 /*content_suggestions_api=*/true, remote_category_id, |
| 536 *suggestions_list, &suggestions)) { | 543 *suggestions_list, &suggestions, fetch_time)) { |
| 537 return false; | 544 return false; |
| 538 } | 545 } |
| 539 } | 546 } |
| 540 Category category = Category::FromRemoteCategory(remote_category_id); | 547 Category category = Category::FromRemoteCategory(remote_category_id); |
| 541 if (category.IsKnownCategory(KnownCategories::ARTICLES)) { | 548 if (category.IsKnownCategory(KnownCategories::ARTICLES)) { |
| 542 categories->push_back(FetchedCategory( | 549 categories->push_back(FetchedCategory( |
| 543 category, | 550 category, |
| 544 BuildArticleCategoryInfo(base::UTF8ToUTF16(utf8_title)))); | 551 BuildArticleCategoryInfo(base::UTF8ToUTF16(utf8_title)))); |
| 545 } else { | 552 } else { |
| 546 // TODO(tschumann): Right now, the backend does not yet populate this | 553 // TODO(tschumann): Right now, the backend does not yet populate this |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 571 interactive_request); | 578 interactive_request); |
| 572 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: | 579 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: |
| 573 return request_throttler_active_suggestions_consumer_ | 580 return request_throttler_active_suggestions_consumer_ |
| 574 .DemandQuotaForRequest(interactive_request); | 581 .DemandQuotaForRequest(interactive_request); |
| 575 } | 582 } |
| 576 NOTREACHED(); | 583 NOTREACHED(); |
| 577 return false; | 584 return false; |
| 578 } | 585 } |
| 579 | 586 |
| 580 } // namespace ntp_snippets | 587 } // namespace ntp_snippets |
| OLD | NEW |