Commit 0930dd89 authored by Sam Bowen's avatar Sam Bowen Committed by Commit Bot

Add button to media feeds web UI to manually fetch feed

Bug: 1064748
Change-Id: I74627723099d1b09359cd6fa7575136a39b0d01a
Binary-Size: The binary size of the generated code was reduced as much as possible in https://crrev.com/c/2144773
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2137514
Commit-Queue: Sam Bowen <sgbowen@google.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarBecca Hughes <beccahughes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758316}
parent 0a506859
......@@ -4,6 +4,7 @@
#include "chrome/browser/media/feeds/media_feeds_converter.h"
#include <cmath>
#include <numeric>
#include <vector>
......@@ -94,15 +95,13 @@ bool IsUrl(const Property& property) {
});
}
// Checks that the property contains at least positive integer and that all
// numbers it contains are positive integers.
// Checks that the property is a positive integer.
bool IsPositiveInteger(const Property& property) {
return !property.values->long_values.empty() &&
std::accumulate(property.values->long_values.begin(),
property.values->long_values.end(), true,
[](auto& accumulation, auto& long_value) {
return accumulation && long_value > 0;
});
if (!property.values->long_values.empty())
return property.values->long_values[0] > 0;
if (!property.values->double_values.empty())
return property.values->double_values[0] > 0;
return false;
}
// Checks that the property contains at least one non-empty string and that all
......@@ -139,18 +138,12 @@ bool IsDateOrDateTime(const Property& property) {
return !property.values->date_time_values.empty();
}
// Gets a number from the property which may be stored either as a long or a
// string.
// Gets a number from the property which may be stored as a long or double.
base::Optional<uint64_t> GetNumber(const Property& property) {
if (!property.values->long_values.empty())
return property.values->long_values[0];
if (!property.values->string_values.empty()) {
uint64_t number;
bool parsed_number =
base::StringToUint64(property.values->string_values[0], &number);
if (parsed_number)
return number;
}
if (!property.values->double_values.empty())
return lround(property.values->double_values[0]);
return base::nullopt;
}
......@@ -662,15 +655,6 @@ void GetDataFeedItems(
continue;
converted_item->images = converted_images.value();
bool has_embedded_action =
item->type == schema_org::entity::kTVSeries &&
GetProperty(item.get(), schema_org::property::kEpisode);
if (!convert_property.Run(schema_org::property::kPotentialAction,
!has_embedded_action,
base::BindOnce(&GetActionAndStatus))) {
continue;
}
if (!convert_property.Run(schema_org::property::kInteractionStatistic,
false,
base::BindOnce(&GetInteractionStatistics))) {
......@@ -735,20 +719,29 @@ void GetDataFeedItems(
}
}
bool has_embedded_action =
item->type == schema_org::entity::kTVSeries && converted_item->action;
if (!convert_property.Run(schema_org::property::kPotentialAction,
!has_embedded_action,
base::BindOnce(&GetActionAndStatus))) {
continue;
}
converted_feed_items->push_back(std::move(converted_item));
}
}
// static
base::Optional<std::vector<mojom::MediaFeedItemPtr>> GetMediaFeeds(
EntityPtr entity) {
if (entity->type != "CompleteDataFeed")
const EntityPtr& entity,
std::vector<media_session::MediaImage>* logos,
std::string* display_name) {
if (entity->type != schema_org::entity::kCompleteDataFeed)
return base::nullopt;
DCHECK(logos && display_name);
auto* provider = GetProperty(entity.get(), schema_org::property::kProvider);
std::string display_name;
std::vector<media_session::MediaImage> logos;
if (!ValidateProvider(*provider, &display_name, &logos))
if (!ValidateProvider(*provider, display_name, logos))
return base::nullopt;
std::vector<mojom::MediaFeedItemPtr> media_feed_items;
......
......@@ -17,7 +17,9 @@ namespace media_feeds {
// its feed items are not, GetMediaFeeds excludes the invalid feed items from
// the returned result.
base::Optional<std::vector<mojom::MediaFeedItemPtr>> GetMediaFeeds(
schema_org::improved::mojom::EntityPtr schema_org_entity);
const schema_org::improved::mojom::EntityPtr& schema_org_entity,
std::vector<media_session::MediaImage>* logos,
std::string* display_name);
} // namespace media_feeds
......
......@@ -7,6 +7,7 @@
#include "components/schema_org/common/metadata.mojom.h"
#include "components/schema_org/extractor.h"
#include "components/schema_org/schema_org_entity_names.h"
#include "components/schema_org/validator.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
......@@ -115,7 +116,7 @@ void MediaFeedsFetcher::OnURLFetchComplete(
schema_org::improved::mojom::EntityPtr parsed_entity =
extractor_.Extract(*feed_data);
if (!parsed_entity) {
if (!schema_org::ValidateEntity(parsed_entity.get())) {
std::move(callback).Run(nullptr, Status::kInvalidFeedData);
return;
}
......
......@@ -283,4 +283,10 @@ interface MediaFeedsStore {
// Gets the items for the media feed.
GetItemsForMediaFeed(int64 feed_id) => (array<MediaFeedItem> items);
// Fetch the details of a previously-discovered media feed and store them in
// media history. The feed ID and URL both come from the media history store.
// The feed ID is the unique ID for the feed and the URL is the location where
// the feed was discovered.
FetchMediaFeed(int64 feed_id, url.mojom.Url url) => ();
};
......@@ -250,12 +250,15 @@ void MediaHistoryKeyedService::StoreMediaFeedFetchResult(
const media_feeds::mojom::FetchResult result,
const bool was_fetched_from_cache,
const std::vector<media_session::MediaImage>& logos,
const std::string& display_name) {
const std::string& display_name,
base::OnceClosure callback) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::StoreMediaFeedFetchResult,
store, feed_id, std::move(items), result,
was_fetched_from_cache, logos, display_name));
store->db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::StoreMediaFeedFetchResult, store,
feed_id, std::move(items), result,
was_fetched_from_cache, logos, display_name),
std::move(callback));
}
}
......
......@@ -102,7 +102,8 @@ class MediaHistoryKeyedService : public KeyedService,
const media_feeds::mojom::FetchResult result,
const bool was_fetched_from_cache,
const std::vector<media_session::MediaImage>& logos,
const std::string& display_name);
const std::string& display_name,
base::OnceClosure callback);
void GetURLsInTableForTest(const std::string& table,
base::OnceCallback<void(std::set<GURL>)> callback);
......
......@@ -401,11 +401,11 @@ TEST_P(MediaHistoryKeyedServiceTest, CleanUpDatabaseWhenOriginIsDeleted) {
service()->StoreMediaFeedFetchResult(
1, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), "Test");
std::vector<media_session::MediaImage>(), "Test", base::DoNothing());
service()->StoreMediaFeedFetchResult(
2, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), "test");
std::vector<media_session::MediaImage>(), "test", base::DoNothing());
// Wait until the feed data has finished saving.
WaitForDB();
......@@ -618,11 +618,11 @@ TEST_P(MediaHistoryKeyedServiceTest, CleanUpDatabaseWhenURLIsDeleted) {
service()->StoreMediaFeedFetchResult(
1, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), "Test");
std::vector<media_session::MediaImage>(), "Test", base::DoNothing());
service()->StoreMediaFeedFetchResult(
2, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), "test");
std::vector<media_session::MediaImage>(), "test", base::DoNothing());
// Wait until the feed data has finished saving.
WaitForDB();
......
......@@ -877,7 +877,7 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult) {
service()->StoreMediaFeedFetchResult(
feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false, GetExpectedLogos(),
kExpectedDisplayName);
kExpectedDisplayName, base::DoNothing());
WaitForDB();
{
......@@ -913,7 +913,8 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult) {
service()->StoreMediaFeedFetchResult(
feed_id, GetAltExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), kExpectedDisplayName);
std::vector<media_session::MediaImage>(), kExpectedDisplayName,
base::DoNothing());
WaitForDB();
base::Optional<base::Time> last_fetch_time_not_cache_hit;
......@@ -954,7 +955,8 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult) {
service()->StoreMediaFeedFetchResult(
feed_id, GetAltExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ true,
std::vector<media_session::MediaImage>(), kExpectedDisplayName);
std::vector<media_session::MediaImage>(), kExpectedDisplayName,
base::DoNothing());
WaitForDB();
{
......@@ -1004,7 +1006,8 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_WithEmpty) {
service()->StoreMediaFeedFetchResult(
feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
{
......@@ -1025,7 +1028,8 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_WithEmpty) {
feed_id, std::vector<media_feeds::mojom::MediaFeedItemPtr>(),
media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
{
......@@ -1052,14 +1056,16 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_MultipleFeeds) {
service()->StoreMediaFeedFetchResult(
feed_id_a, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
service()->StoreMediaFeedFetchResult(
feed_id_b, GetAltExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
{
......@@ -1127,7 +1133,8 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_BadType) {
service()->StoreMediaFeedFetchResult(
feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
{
......@@ -1182,7 +1189,8 @@ TEST_P(MediaHistoryStoreFeedsTest, RediscoverMediaFeed) {
service()->StoreMediaFeedFetchResult(
feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
{
......@@ -1265,7 +1273,7 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_IncreaseFailed) {
feed_id, GetExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError,
/* was_fetched_from_cache= */ false, GetExpectedLogos(),
kExpectedDisplayName);
kExpectedDisplayName, base::DoNothing());
WaitForDB();
{
......@@ -1289,7 +1297,7 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_IncreaseFailed) {
feed_id, GetExpectedItems(),
media_feeds::mojom::FetchResult::kFailedBackendError,
/* was_fetched_from_cache= */ false, GetExpectedLogos(),
kExpectedDisplayName);
kExpectedDisplayName, base::DoNothing());
WaitForDB();
{
......@@ -1312,7 +1320,7 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_IncreaseFailed) {
service()->StoreMediaFeedFetchResult(
feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false, GetExpectedLogos(),
kExpectedDisplayName);
kExpectedDisplayName, base::DoNothing());
WaitForDB();
{
......@@ -1383,7 +1391,8 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_CheckLogoMax) {
service()->StoreMediaFeedFetchResult(
feed_id, GetExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError,
/* was_fetched_from_cache= */ false, logos, kExpectedDisplayName);
/* was_fetched_from_cache= */ false, logos, kExpectedDisplayName,
base::DoNothing());
WaitForDB();
{
......@@ -1458,7 +1467,7 @@ TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_CheckImageMax) {
service()->StoreMediaFeedFetchResult(
feed_id, std::move(items), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false, GetExpectedLogos(),
kExpectedDisplayName);
kExpectedDisplayName, base::DoNothing());
WaitForDB();
{
......@@ -1496,7 +1505,7 @@ TEST_P(MediaHistoryStoreFeedsTest,
service()->StoreMediaFeedFetchResult(
feed_id, std::move(items), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false, GetExpectedLogos(),
kExpectedDisplayName);
kExpectedDisplayName, base::DoNothing());
WaitForDB();
{
......@@ -1529,14 +1538,16 @@ TEST_P(MediaHistoryStoreFeedsTest, SafeSearchCheck) {
service()->StoreMediaFeedFetchResult(
feed_id_a, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
service()->StoreMediaFeedFetchResult(
feed_id_b, GetAltExpectedItems(),
media_feeds::mojom::FetchResult::kSuccess,
/* was_fetched_from_cache= */ false,
std::vector<media_session::MediaImage>(), std::string());
std::vector<media_session::MediaImage>(), std::string(),
base::DoNothing());
WaitForDB();
std::map<int64_t, media_feeds::mojom::SafeSearchResult> found_ids;
......
......@@ -41,14 +41,20 @@ class MediaFeedsTableDelegate {
td.appendChild(a);
a.addEventListener('click', () => {
store.getItemsForMediaFeed(dataRow.id).then(response => {
feedItemsTable.setData(response.items);
showFeedContents(dataRow);
});
td.appendChild(document.createElement('br'));
// Show the feed items section.
$('current-feed').textContent = dataRow.url.url;
$('feed-content').style.display = 'block';
const fetchFeed = document.createElement('a');
fetchFeed.href = '#feed-content';
fetchFeed.textContent = 'Fetch Feed';
td.appendChild(fetchFeed);
mediaFeedItemsPageIsPopulatedResolver.resolve();
fetchFeed.addEventListener('click', () => {
store.fetchMediaFeed(dataRow.id, dataRow.url).then(response => {
updateFeedsTable();
showFeedContents(dataRow);
});
});
}
......@@ -371,6 +377,22 @@ function updateFeedsTable() {
});
}
/**
* Retrieve feed items and render the feed contents table.
* @param {Object} dataRow
*/
function showFeedContents(dataRow) {
store.getItemsForMediaFeed(dataRow.id).then(response => {
feedItemsTable.setData(response.items);
// Show the feed items section.
$('current-feed').textContent = dataRow.url.url;
$('feed-content').style.display = 'block';
mediaFeedItemsPageIsPopulatedResolver.resolve();
});
}
document.addEventListener('DOMContentLoaded', () => {
store = mediaFeeds.mojom.MediaFeedsStore.getRemote();
......
......@@ -8,6 +8,9 @@
#include "base/macros.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/media/feeds/media_feeds_converter.h"
#include "chrome/browser/media/feeds/media_feeds_fetcher.h"
#include "chrome/browser/media/feeds/media_feeds_store.mojom-shared.h"
#include "chrome/browser/media/history/media_history_keyed_service.h"
#include "chrome/browser/media/history/media_history_keyed_service_factory.h"
#include "chrome/browser/media/history/media_history_store.h"
......@@ -15,6 +18,7 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/dev_ui_browser_resources.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
......@@ -58,6 +62,46 @@ void MediaFeedsUI::GetItemsForMediaFeed(int64_t feed_id,
std::move(callback));
}
void MediaFeedsUI::OnFetchResponse(
int64_t feed_id,
FetchMediaFeedCallback callback,
const schema_org::improved::mojom::EntityPtr& response,
media_feeds::MediaFeedsFetcher::Status status) {
std::vector<media_session::MediaImage> logos;
std::string display_name;
auto feed_items = media_feeds::GetMediaFeeds(response, &logos, &display_name);
if (!feed_items.has_value())
return;
GetMediaHistoryService()->StoreMediaFeedFetchResult(
feed_id, std::move(feed_items.value()),
media_feeds::mojom::FetchResult::kSuccess, false, std::move(logos),
display_name, std::move(callback));
fetcher_.reset();
}
void MediaFeedsUI::FetchMediaFeed(int64_t feed_id,
const GURL& url,
FetchMediaFeedCallback callback) {
if (fetcher_) {
std::move(callback).Run();
return;
}
fetcher_ = std::make_unique<media_feeds::MediaFeedsFetcher>(
content::BrowserContext::GetDefaultStoragePartition(
Profile::FromWebUI(web_ui()))
->GetURLLoaderFactoryForBrowserProcess());
fetcher_->FetchFeed(
url,
// Use of unretained is safe because the callback is owned
// by fetcher_, which will not outlive this.
base::BindOnce(&MediaFeedsUI::OnFetchResponse, base::Unretained(this),
feed_id, std::move(callback)));
}
media_history::MediaHistoryKeyedService*
MediaFeedsUI::GetMediaHistoryService() {
Profile* profile = Profile::FromWebUI(web_ui());
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_MEDIA_FEEDS_UI_H_
#define CHROME_BROWSER_UI_WEBUI_MEDIA_MEDIA_FEEDS_UI_H_
#include "chrome/browser/media/feeds/media_feeds_fetcher.h"
#include "chrome/browser/media/feeds/media_feeds_store.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
......@@ -32,11 +33,20 @@ class MediaFeedsUI : public ui::MojoWebUIController,
void GetMediaFeeds(GetMediaFeedsCallback callback) override;
void GetItemsForMediaFeed(int64_t feed_id,
GetItemsForMediaFeedCallback callback) override;
void FetchMediaFeed(int64_t feed_id,
const GURL& url,
FetchMediaFeedCallback callback) override;
private:
media_history::MediaHistoryKeyedService* GetMediaHistoryService();
void OnFetchResponse(int64_t feed_id,
FetchMediaFeedCallback callback,
const schema_org::improved::mojom::EntityPtr& response,
media_feeds::MediaFeedsFetcher::Status status);
mojo::ReceiverSet<media_feeds::mojom::MediaFeedsStore> receiver_;
// Used to fetch media feeds. Null if no fetch is ongoing.
std::unique_ptr<media_feeds::MediaFeedsFetcher> fetcher_;
WEB_UI_CONTROLLER_TYPE_DECL();
};
......
......@@ -111,7 +111,7 @@ MediaFeedsWebUIBrowserTest.prototype = {
GEN('logos.push_back(logo2);');
GEN('service->StoreMediaFeedFetchResult(');
GEN(' 1, std::move(items), media_feeds::mojom::FetchResult::kSuccess,');
GEN(' false, logos, "Test Feed");');
GEN(' false, logos, "Test Feed", base::DoNothing());');
GEN('base::RunLoop run_loop;');
GEN('service->PostTaskToDBForTest(run_loop.QuitClosure());');
GEN('run_loop.Run();');
......@@ -159,7 +159,8 @@ TEST_F('MediaFeedsWebUIBrowserTest', 'All', function() {
'https://www.example.org/logo1.pnghttps://www.example.org/logo2.png',
feedsContents.childNodes[12].textContent.trim());
assertEquals(
'Show Contents', feedsContents.childNodes[13].textContent.trim());
'Show ContentsFetch Feed',
feedsContents.childNodes[13].textContent.trim());
// Click on the show contents button.
feedsContents.childNodes[13].firstChild.click();
......
......@@ -37,9 +37,8 @@ bool EntityPropertyIsValidType(const property::PropertyConfiguration& config,
// static
bool ValidateEntity(Entity* entity) {
if (!entity::IsValidEntityName(entity->type)) {
if (!entity || !entity::IsValidEntityName(entity->type))
return false;
}
// Cycle through properties and remove any that have the wrong type.
auto it = entity->properties.begin();
......
......@@ -26,6 +26,19 @@
],
"rdfs:comment": "Indicates a target EntryPoint for an Action.",
"rdfs:label": "target"
},
{
"@id": "http://schema.org/interactionType",
"@type": "rdf:Property",
"http://schema.org/domainIncludes": {
"@id": "http://schema.org/InteractionCounter"
},
"http://schema.org/rangeIncludes": [
{"@id": "http://schema.org/Action"},
{"@id": "http://schema.org/Text"}
],
"rdfs:comment": "The Action representing the type of interaction. For up votes, +1s, etc. use <a class=\"localLink\" href=\"http://schema.org/LikeAction\">LikeAction</a>. For down votes use <a class=\"localLink\" href=\"http://schema.org/DislikeAction\">DislikeAction</a>. Otherwise, use the most specific Action.",
"rdfs:label": "interactionType"
}
]
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment