Commit e14c1b39 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Media History] Add playbacks tab

Make data table generic so we can reuse it and
then adds a playbacks tab.

BUG=1024353

Change-Id: I19acb277bb874576e39951d8dda567c2c7e29484
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2048288Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740823}
parent bf3aeeb4
...@@ -86,4 +86,38 @@ bool MediaHistoryPlaybackTable::SavePlayback( ...@@ -86,4 +86,38 @@ bool MediaHistoryPlaybackTable::SavePlayback(
return true; return true;
} }
std::vector<mojom::MediaHistoryPlaybackRowPtr>
MediaHistoryPlaybackTable::GetPlaybackRows() {
std::vector<mojom::MediaHistoryPlaybackRowPtr> playbacks;
if (!CanAccessDatabase())
return playbacks;
sql::Statement statement(DB()->GetUniqueStatement(
base::StringPrintf(
"SELECT url, watch_time_s, has_audio, has_video, last_updated_time_s "
"FROM %s",
kTableName)
.c_str()));
while (statement.Step()) {
mojom::MediaHistoryPlaybackRowPtr playback(
mojom::MediaHistoryPlaybackRow::New());
playback->url = GURL(statement.ColumnString(0));
playback->watchtime =
base::TimeDelta::FromSeconds(statement.ColumnInt64(1));
playback->has_audio = statement.ColumnBool(2);
playback->has_video = statement.ColumnBool(3);
playback->last_updated_time =
base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromSeconds(statement.ColumnInt64(4)))
.ToJsTime();
playbacks.push_back(std::move(playback));
}
DCHECK(statement.Succeeded());
return playbacks;
}
} // namespace media_history } // namespace media_history
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#ifndef CHROME_BROWSER_MEDIA_HISTORY_MEDIA_HISTORY_PLAYBACK_TABLE_H_ #ifndef CHROME_BROWSER_MEDIA_HISTORY_MEDIA_HISTORY_PLAYBACK_TABLE_H_
#define CHROME_BROWSER_MEDIA_HISTORY_MEDIA_HISTORY_PLAYBACK_TABLE_H_ #define CHROME_BROWSER_MEDIA_HISTORY_MEDIA_HISTORY_PLAYBACK_TABLE_H_
#include <vector>
#include "chrome/browser/media/history/media_history_store.mojom.h"
#include "chrome/browser/media/history/media_history_table_base.h" #include "chrome/browser/media/history/media_history_table_base.h"
#include "sql/init_status.h" #include "sql/init_status.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -46,6 +49,9 @@ class MediaHistoryPlaybackTable : public MediaHistoryTableBase { ...@@ -46,6 +49,9 @@ class MediaHistoryPlaybackTable : public MediaHistoryTableBase {
// Returns a flag indicating whether the playback was created successfully. // Returns a flag indicating whether the playback was created successfully.
bool SavePlayback(const content::MediaPlayerWatchTime& watch_time); bool SavePlayback(const content::MediaPlayerWatchTime& watch_time);
// Returns the playback rows in the database.
std::vector<mojom::MediaHistoryPlaybackRowPtr> GetPlaybackRows();
DISALLOW_COPY_AND_ASSIGN(MediaHistoryPlaybackTable); DISALLOW_COPY_AND_ASSIGN(MediaHistoryPlaybackTable);
}; };
......
...@@ -65,6 +65,9 @@ class MediaHistoryStoreInternal ...@@ -65,6 +65,9 @@ class MediaHistoryStoreInternal
std::vector<mojom::MediaHistoryOriginRowPtr> GetOriginRowsForDebug(); std::vector<mojom::MediaHistoryOriginRowPtr> GetOriginRowsForDebug();
std::vector<mojom::MediaHistoryPlaybackRowPtr>
GetMediaHistoryPlaybackRowsForDebug();
void SavePlaybackSession( void SavePlaybackSession(
const GURL& url, const GURL& url,
const media_session::MediaMetadata& metadata, const media_session::MediaMetadata& metadata,
...@@ -262,6 +265,15 @@ MediaHistoryStoreInternal::GetOriginRowsForDebug() { ...@@ -262,6 +265,15 @@ MediaHistoryStoreInternal::GetOriginRowsForDebug() {
return origins; return origins;
} }
std::vector<mojom::MediaHistoryPlaybackRowPtr>
MediaHistoryStoreInternal::GetMediaHistoryPlaybackRowsForDebug() {
DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
if (!initialization_successful_)
return std::vector<mojom::MediaHistoryPlaybackRowPtr>();
return playback_table_->GetPlaybackRows();
}
int MediaHistoryStoreInternal::GetTableRowCount(const std::string& table_name) { int MediaHistoryStoreInternal::GetTableRowCount(const std::string& table_name) {
DCHECK(db_task_runner_->RunsTasksInCurrentSequence()); DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
if (!initialization_successful_) if (!initialization_successful_)
...@@ -384,6 +396,16 @@ void MediaHistoryStore::GetOriginRowsForDebug( ...@@ -384,6 +396,16 @@ void MediaHistoryStore::GetOriginRowsForDebug(
std::move(callback)); std::move(callback));
} }
void MediaHistoryStore::GetMediaHistoryPlaybackRowsForDebug(
base::OnceCallback<void(std::vector<mojom::MediaHistoryPlaybackRowPtr>)>
callback) {
base::PostTaskAndReplyWithResult(
db_->db_task_runner_.get(), FROM_HERE,
base::BindOnce(
&MediaHistoryStoreInternal::GetMediaHistoryPlaybackRowsForDebug, db_),
std::move(callback));
}
void MediaHistoryStore::SavePlaybackSession( void MediaHistoryStore::SavePlaybackSession(
const GURL& url, const GURL& url,
const media_session::MediaMetadata& metadata, const media_session::MediaMetadata& metadata,
......
...@@ -64,6 +64,12 @@ class MediaHistoryStore { ...@@ -64,6 +64,12 @@ class MediaHistoryStore {
base::OnceCallback<void(std::vector<mojom::MediaHistoryOriginRowPtr>)> base::OnceCallback<void(std::vector<mojom::MediaHistoryOriginRowPtr>)>
callback); callback);
// Returns all the rows in the playback table. This is only used for
// debugging because it loads all rows in the table.
void GetMediaHistoryPlaybackRowsForDebug(
base::OnceCallback<void(std::vector<mojom::MediaHistoryPlaybackRowPtr>)>
callback);
// Gets the playback sessions from the media history store. The results will // Gets the playback sessions from the media history store. The results will
// be ordered by most recent first and be limited to the first |num_sessions|. // be ordered by most recent first and be limited to the first |num_sessions|.
// For each session it calls |filter| and if that returns |true| then that // For each session it calls |filter| and if that returns |true| then that
......
...@@ -6,6 +6,7 @@ module media_history.mojom; ...@@ -6,6 +6,7 @@ module media_history.mojom;
import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/time.mojom";
import "url/mojom/origin.mojom"; import "url/mojom/origin.mojom";
import "url/mojom/url.mojom";
struct MediaHistoryStats { struct MediaHistoryStats {
// The row count of the different tables. The key is the table name and the // The row count of the different tables. The key is the table name and the
...@@ -26,12 +27,30 @@ struct MediaHistoryOriginRow { ...@@ -26,12 +27,30 @@ struct MediaHistoryOriginRow {
double last_updated_time; double last_updated_time;
}; };
struct MediaHistoryPlaybackRow {
// The top frame URL of the page that had the playback.
url.mojom.Url url;
// Whether the playback had audio/video tracks.
bool has_audio;
bool has_video;
// The watchtime for the playback.
mojo_base.mojom.TimeDelta watchtime;
// The last updated time of the row in JS time.
double last_updated_time;
};
// MediaHistoryStore allows the Media History WebUI to access data from the // MediaHistoryStore allows the Media History WebUI to access data from the
// Media History database for diagnostic purposes. // Media History database for diagnostic purposes.
interface MediaHistoryStore { interface MediaHistoryStore {
// Gets stats about the Media History database. // Gets stats about the Media History database.
GetMediaHistoryStats() => (MediaHistoryStats stats); GetMediaHistoryStats() => (MediaHistoryStats stats);
// Returns the rows from the origin table. // Returns the origin rows from the origins table.
GetMediaHistoryOriginRows() => (array<MediaHistoryOriginRow> rows); GetMediaHistoryOriginRows() => (array<MediaHistoryOriginRow> rows);
// Returns the playback from the playbacks table.
GetMediaHistoryPlaybackRows() => (array<MediaHistoryPlaybackRow> rows);
}; };
...@@ -91,6 +91,21 @@ class MediaHistoryStoreUnitTest : public testing::Test { ...@@ -91,6 +91,21 @@ class MediaHistoryStoreUnitTest : public testing::Test {
return out; return out;
} }
std::vector<mojom::MediaHistoryPlaybackRowPtr> GetPlaybackRowsSync() {
base::RunLoop run_loop;
std::vector<mojom::MediaHistoryPlaybackRowPtr> out;
GetMediaHistoryStore()->GetMediaHistoryPlaybackRowsForDebug(
base::BindLambdaForTesting(
[&](std::vector<mojom::MediaHistoryPlaybackRowPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
MediaHistoryStore* GetMediaHistoryStore() { MediaHistoryStore* GetMediaHistoryStore() {
return media_history_store_.get(); return media_history_store_.get();
} }
...@@ -114,14 +129,16 @@ TEST_F(MediaHistoryStoreUnitTest, CreateDatabaseTables) { ...@@ -114,14 +129,16 @@ TEST_F(MediaHistoryStoreUnitTest, CreateDatabaseTables) {
} }
TEST_F(MediaHistoryStoreUnitTest, SavePlayback) { TEST_F(MediaHistoryStoreUnitTest, SavePlayback) {
const auto now_before =
(base::Time::Now() - base::TimeDelta::FromMinutes(1)).ToJsTime();
// Create a media player watch time and save it to the playbacks table. // Create a media player watch time and save it to the playbacks table.
GURL url("http://google.com/test"); GURL url("http://google.com/test");
content::MediaPlayerWatchTime watch_time(url, url.GetOrigin(), content::MediaPlayerWatchTime watch_time(url, url.GetOrigin(),
base::TimeDelta::FromSeconds(60), base::TimeDelta::FromSeconds(60),
base::TimeDelta(), true, false); base::TimeDelta(), true, false);
GetMediaHistoryStore()->SavePlayback(watch_time); GetMediaHistoryStore()->SavePlayback(watch_time);
int64_t now_in_seconds_before = const auto now_after_a = base::Time::Now().ToJsTime();
base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds();
// Save the watch time a second time. // Save the watch time a second time.
GetMediaHistoryStore()->SavePlayback(watch_time); GetMediaHistoryStore()->SavePlayback(watch_time);
...@@ -129,49 +146,33 @@ TEST_F(MediaHistoryStoreUnitTest, SavePlayback) { ...@@ -129,49 +146,33 @@ TEST_F(MediaHistoryStoreUnitTest, SavePlayback) {
// Wait until the playbacks have finished saving. // Wait until the playbacks have finished saving.
content::RunAllTasksUntilIdle(); content::RunAllTasksUntilIdle();
int64_t now_in_seconds_after = const auto now_after_b = base::Time::Now().ToJsTime();
base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds();
// Verify that the playback table contains the expected number of items
sql::Statement select_from_playback_statement(GetDB().GetUniqueStatement(
"SELECT id, url, origin_id, watch_time_s, has_video, has_audio, "
"last_updated_time_s FROM playback"));
ASSERT_TRUE(select_from_playback_statement.is_valid());
int playback_row_count = 0;
while (select_from_playback_statement.Step()) {
++playback_row_count;
EXPECT_EQ(playback_row_count, select_from_playback_statement.ColumnInt(0));
EXPECT_EQ("http://google.com/test",
select_from_playback_statement.ColumnString(1));
EXPECT_EQ(1, select_from_playback_statement.ColumnInt(2));
EXPECT_EQ(60, select_from_playback_statement.ColumnInt(3));
EXPECT_EQ(1, select_from_playback_statement.ColumnInt(4));
EXPECT_EQ(0, select_from_playback_statement.ColumnInt(5));
EXPECT_LE(now_in_seconds_before,
select_from_playback_statement.ColumnInt64(6));
EXPECT_GE(now_in_seconds_after,
select_from_playback_statement.ColumnInt64(6));
}
EXPECT_EQ(2, playback_row_count); // Verify that the playback table contains the expected number of items.
std::vector<mojom::MediaHistoryPlaybackRowPtr> playbacks =
// Verify that the origin table contains the expected number of items GetPlaybackRowsSync();
sql::Statement select_from_origin_statement(GetDB().GetUniqueStatement( EXPECT_EQ(2u, playbacks.size());
"SELECT id, origin, last_updated_time_s FROM origin"));
ASSERT_TRUE(select_from_origin_statement.is_valid()); EXPECT_EQ("http://google.com/test", playbacks[0]->url.spec());
int origin_row_count = 0; EXPECT_FALSE(playbacks[0]->has_audio);
while (select_from_origin_statement.Step()) { EXPECT_TRUE(playbacks[0]->has_video);
++origin_row_count; EXPECT_EQ(base::TimeDelta::FromSeconds(60), playbacks[0]->watchtime);
EXPECT_EQ(1, select_from_origin_statement.ColumnInt(0)); EXPECT_LE(now_before, playbacks[0]->last_updated_time);
EXPECT_EQ("http://google.com/", EXPECT_GE(now_after_a, playbacks[0]->last_updated_time);
select_from_origin_statement.ColumnString(1));
EXPECT_LE(now_in_seconds_before,
select_from_origin_statement.ColumnInt64(2));
EXPECT_GE(now_in_seconds_after,
select_from_origin_statement.ColumnInt64(2));
}
EXPECT_EQ(1, origin_row_count); EXPECT_EQ("http://google.com/test", playbacks[1]->url.spec());
EXPECT_FALSE(playbacks[1]->has_audio);
EXPECT_TRUE(playbacks[1]->has_video);
EXPECT_EQ(base::TimeDelta::FromSeconds(60), playbacks[1]->watchtime);
EXPECT_LE(now_before, playbacks[1]->last_updated_time);
EXPECT_GE(now_after_b, playbacks[1]->last_updated_time);
// Verify that the origin table contains the expected number of items.
std::vector<mojom::MediaHistoryOriginRowPtr> origins = GetOriginRowsSync();
EXPECT_EQ(1u, origins.size());
EXPECT_EQ("http://google.com", origins[0]->origin.Serialize());
EXPECT_LE(now_before, origins[0]->last_updated_time);
EXPECT_GE(now_after_b, origins[0]->last_updated_time);
} }
TEST_F(MediaHistoryStoreUnitTest, GetStats) { TEST_F(MediaHistoryStoreUnitTest, GetStats) {
......
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
<tabs> <tabs>
<tab id="stats">Stats</tab> <tab id="stats">Stats</tab>
<tab id="origins">Origins</tab> <tab id="origins">Origins</tab>
<tab id="playbacks">Playbacks</tab>
</tabs> </tabs>
<tabpanels> <tabpanels>
<tabpanel> <tabpanel>
...@@ -124,24 +125,51 @@ ...@@ -124,24 +125,51 @@
<tabpanel> <tabpanel>
<h1>Origins</h1> <h1>Origins</h1>
<button class="copy-all-to-clipboard">Copy all to clipboard</button> <button class="copy-all-to-clipboard">Copy all to clipboard</button>
<table> <table id="origins-table">
<thead> <thead>
<tr id="data-table-header"> <tr>
<th sort-key="origin"> <th sort-key="origin">
Origin Origin
</th> </th>
<th sort-key="lastUpdatedTime" sort-reverse> <th sort-key="lastUpdatedTime">
Last Updated Last Updated
</th> </th>
<th sort-key="cachedAudioVideoWatchtime" class="sort-column" sort-reverse> <th sort-key="cachedAudioVideoWatchtime" class="sort-column" sort-reverse>
Audio + Video Watchtime (secs, cached) Audio + Video Watchtime (secs, cached)
</th> </th>
<th sort-key="actualAudioVideoWatchtime" sort-reverse> <th sort-key="actualAudioVideoWatchtime">
Audio + Video Watchtime (secs, actual) Audio + Video Watchtime (secs, actual)
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody id="data-table-body"> <tbody>
</tbody>
</table>
</tabpanel>
<tabpanel>
<h1>Playbacks</h1>
<button class="copy-all-to-clipboard">Copy all to clipboard</button>
<table id="playbacks-table">
<thead>
<tr>
<th sort-key="url">
URL
</th>
<th sort-key="lastUpdatedTime" class="sort-column" sort-reverse>
Last Updated
</th>
<th sort-key="hasAudio">
Has Audio
</th>
<th sort-key="hasVideo">
Has Video
</th>
<th sort-key="watchtime">
Watchtime (secs)
</th>
</tr>
</thead>
<tbody>
</tbody> </tbody>
</table> </table>
</tabpanel> </tabpanel>
......
...@@ -13,12 +13,10 @@ function whenPageIsPopulatedForTest() { ...@@ -13,12 +13,10 @@ function whenPageIsPopulatedForTest() {
(function() { (function() {
let data = null;
let dataTableBody = null;
let sortReverse = true;
let sortKey = 'cachedAudioVideoWatchtime';
let store = null; let store = null;
let statsTableBody = null; let statsTableBody = null;
let originsTable = null;
let playbacksTable = null;
/** /**
* Creates a single row in the stats table. * Creates a single row in the stats table.
...@@ -34,13 +32,6 @@ function createStatsRow(name, count) { ...@@ -34,13 +32,6 @@ function createStatsRow(name, count) {
return document.importNode(template.content, true); return document.importNode(template.content, true);
} }
/**
* Remove all rows from the data table.
*/
function clearDataTable() {
dataTableBody.innerHTML = '';
}
/** /**
* Compares two MediaHistoryOriginRow objects based on |sortKey|. * Compares two MediaHistoryOriginRow objects based on |sortKey|.
* @param {string} sortKey The name of the property to sort by. * @param {string} sortKey The name of the property to sort by.
...@@ -60,9 +51,14 @@ function compareTableItem(sortKey, a, b) { ...@@ -60,9 +51,14 @@ function compareTableItem(sortKey, a, b) {
return val1.host > val2.host ? 1 : -1; return val1.host > val2.host ? 1 : -1;
} }
// Compare the url property.
if (sortKey == 'url') {
return val1.url > val2.url ? 1 : -1;
}
// Compare mojo_base.mojom.TimeDelta microseconds value. // Compare mojo_base.mojom.TimeDelta microseconds value.
if (sortKey == 'cachedAudioVideoWatchtime' || if (sortKey == 'cachedAudioVideoWatchtime' ||
sortKey == 'actualAudioVideoWatchtime') { sortKey == 'actualAudioVideoWatchtime' || sortKey == 'watchtime') {
return val1.microseconds - val2.microseconds; return val1.microseconds - val2.microseconds;
} }
...@@ -75,53 +71,120 @@ function compareTableItem(sortKey, a, b) { ...@@ -75,53 +71,120 @@ function compareTableItem(sortKey, a, b) {
} }
/** /**
* Sort the data based on |sortKey| and |sortReverse|. * Formats a field to be displayed in the data table.
* @param {?object} data
* @param {string} key
* @returns {?string}
*/ */
function sortData() { function formatField(data, key) {
data.sort((a, b) => { if (data === undefined || data === null) {
return (sortReverse ? -1 : 1) * compareTableItem(sortKey, a, b); return;
}); }
}
/** if (key == 'origin') {
* Regenerates the data table from |data|. let origin = data.scheme + '://' + data.host;
*/ if (data.scheme == 'http' && data.port != '80') {
function renderDataTable() { origin += ':' + data.port;
clearDataTable(); } else if (data.scheme == 'https' && data.port != '443') {
sortData(); origin += ':' + data.port;
data.forEach(rowInfo => dataTableBody.appendChild(createDataRow(rowInfo))); }
}
return origin;
} else if (key == 'lastUpdatedTime') {
return data ? new Date(data).toISOString() : '';
} else if (
key == 'cachedAudioVideoWatchtime' ||
key == 'actualAudioVideoWatchtime' || key == 'watchtime') {
const secs = (data.microseconds / 1000000);
return secs.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
} else if (key == 'url') {
return data.url;
} else if (key == 'hasAudio' || key == 'hasVideo') {
return data ? 'Yes' : 'No';
}
/** return data;
* @param {mojo_base.mojom.TimeDelta} The time delta to format.
* @return {string} The time in seconds with commas added.
*/
function formatWatchtime(time) {
const secs = (time.microseconds / 1000000);
return secs.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
} }
/** class DataTable {
* Creates a single row in the data table. /**
* @param {!MediaHistoryOriginRow} data The data to create the row. * @param {!HTMLTableElement} table
* @return {!HTMLElement} */
*/ constructor(table) {
function createDataRow(data) { /** @private {!HTMLTableElement} */
const template = $('data-row'); this.table_ = table;
const td = template.content.querySelectorAll('td');
/** @private {Object[]} */
this.data_ = [];
// Set table header sort handlers.
const headers = this.table_.querySelectorAll('th[sort-key]');
headers.forEach((header) => {
header.addEventListener('click', this.handleSortClick.bind(this));
}, this);
}
handleSortClick(e) {
const isCurrentSortColumn = e.target.classList.contains('sort-column');
// If we are not the sort column then we should become the sort column.
if (!isCurrentSortColumn) {
const oldSortColumn = document.querySelector('.sort-column');
oldSortColumn.classList.remove('sort-column');
e.target.classList.add('sort-column');
}
// If we are the current sort column then we should toggle the reverse
// attribute to sort in reverse.
if (isCurrentSortColumn && e.target.hasAttribute('sort-reverse')) {
e.target.removeAttribute('sort-reverse');
} else {
e.target.setAttribute('sort-reverse', '');
}
td[0].textContent = data.origin.scheme + '://' + data.origin.host; this.render();
if (data.origin.scheme == 'http' && data.origin.port != '80') {
td[0].textContent += ':' + data.origin.port;
} else if (data.origin.scheme == 'https' && data.origin.port != '443') {
td[0].textContent += ':' + data.origin.port;
} }
td[1].textContent = render() {
data.lastUpdatedTime ? new Date(data.lastUpdatedTime).toISOString() : ''; // Find the body of the table and clear it.
td[2].textContent = formatWatchtime(data.cachedAudioVideoWatchtime); const body = this.table_.querySelectorAll('tbody')[0];
td[3].textContent = formatWatchtime(data.actualAudioVideoWatchtime); body.innerHTML = '';
return document.importNode(template.content, true);
// Get the sort key from the columns to determine which data should be in
// which column.
const headerCells = Array.from(this.table_.querySelectorAll('thead th'));
const sortKeys = headerCells.map((e) => e.getAttribute('sort-key'));
const currentSortCol = this.table_.querySelectorAll('.sort-column')[0];
const currentSortKey = currentSortCol.getAttribute('sort-key');
const currentSortReverse = currentSortCol.hasAttribute('sort-reverse');
// Sort the data based on the current sort key.
this.data_.sort((a, b) => {
return (currentSortReverse ? -1 : 1) *
compareTableItem(currentSortKey, a, b);
});
// Generate the table rows.
this.data_.forEach((dataRow) => {
const tr = document.createElement('tr');
body.appendChild(tr);
sortKeys.forEach((key) => {
const td = document.createElement('td');
td.textContent = formatField(dataRow[key], key);
tr.appendChild(td);
});
});
}
/**
* @param {object[]} data The data to update
*/
setData(data) {
this.data_ = data;
this.render();
}
} }
/** /**
...@@ -148,8 +211,11 @@ function showTab(name) { ...@@ -148,8 +211,11 @@ function showTab(name) {
}); });
case 'origins': case 'origins':
return store.getMediaHistoryOriginRows().then(response => { return store.getMediaHistoryOriginRows().then(response => {
data = response.rows; originsTable.setData(response.rows);
renderDataTable(); });
case 'playbacks':
return store.getMediaHistoryPlaybackRows().then(response => {
playbacksTable.setData(response.rows);
}); });
} }
...@@ -160,9 +226,11 @@ function showTab(name) { ...@@ -160,9 +226,11 @@ function showTab(name) {
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
store = mediaHistory.mojom.MediaHistoryStore.getRemote(); store = mediaHistory.mojom.MediaHistoryStore.getRemote();
dataTableBody = $('data-table-body');
statsTableBody = $('stats-table-body'); statsTableBody = $('stats-table-body');
originsTable = new DataTable($('origins-table'));
playbacksTable = new DataTable($('playbacks-table'));
cr.ui.decorate('tabbox', cr.ui.TabBox); cr.ui.decorate('tabbox', cr.ui.TabBox);
// Allow tabs to be navigated to by fragment. The fragment with be of the // Allow tabs to be navigated to by fragment. The fragment with be of the
...@@ -187,30 +255,6 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -187,30 +255,6 @@ document.addEventListener('DOMContentLoaded', function() {
window.location.hash = 'tab-' + selectedTab.id; window.location.hash = 'tab-' + selectedTab.id;
}, true); }, true);
// Set table header sort handlers.
const dataTableHeader = $('data-table-header');
const headers = dataTableHeader.children;
for (let i = 0; i < headers.length; i++) {
headers[i].addEventListener('click', (e) => {
const newSortKey = e.target.getAttribute('sort-key');
if (sortKey == newSortKey) {
sortReverse = !sortReverse;
} else {
sortKey = newSortKey;
sortReverse = false;
}
const oldSortColumn = document.querySelector('.sort-column');
oldSortColumn.classList.remove('sort-column');
e.target.classList.add('sort-column');
if (sortReverse) {
e.target.setAttribute('sort-reverse', '');
} else {
e.target.removeAttribute('sort-reverse');
}
renderDataTable();
});
}
// Add handler to 'copy all to clipboard' button. // Add handler to 'copy all to clipboard' button.
const copyAllToClipboardButtons = const copyAllToClipboardButtons =
document.querySelectorAll('.copy-all-to-clipboard'); document.querySelectorAll('.copy-all-to-clipboard');
......
...@@ -52,6 +52,12 @@ void MediaHistoryUI::GetMediaHistoryOriginRows( ...@@ -52,6 +52,12 @@ void MediaHistoryUI::GetMediaHistoryOriginRows(
return GetMediaHistoryStore()->GetOriginRowsForDebug(std::move(callback)); return GetMediaHistoryStore()->GetOriginRowsForDebug(std::move(callback));
} }
void MediaHistoryUI::GetMediaHistoryPlaybackRows(
GetMediaHistoryPlaybackRowsCallback callback) {
return GetMediaHistoryStore()->GetMediaHistoryPlaybackRowsForDebug(
std::move(callback));
}
media_history::MediaHistoryStore* MediaHistoryUI::GetMediaHistoryStore() { media_history::MediaHistoryStore* MediaHistoryUI::GetMediaHistoryStore() {
Profile* profile = Profile::FromWebUI(web_ui()); Profile* profile = Profile::FromWebUI(web_ui());
DCHECK(profile); DCHECK(profile);
......
...@@ -32,6 +32,8 @@ class MediaHistoryUI : public ui::MojoWebUIController, ...@@ -32,6 +32,8 @@ class MediaHistoryUI : public ui::MojoWebUIController,
void GetMediaHistoryStats(GetMediaHistoryStatsCallback callback) override; void GetMediaHistoryStats(GetMediaHistoryStatsCallback callback) override;
void GetMediaHistoryOriginRows( void GetMediaHistoryOriginRows(
GetMediaHistoryOriginRowsCallback callback) override; GetMediaHistoryOriginRowsCallback callback) override;
void GetMediaHistoryPlaybackRows(
GetMediaHistoryPlaybackRowsCallback callback) override;
private: private:
media_history::MediaHistoryStore* GetMediaHistoryStore(); media_history::MediaHistoryStore* GetMediaHistoryStore();
......
...@@ -86,7 +86,7 @@ TEST_F('MediaHistoryOriginsWebUIBrowserTest', 'MAYBE_All', function() { ...@@ -86,7 +86,7 @@ TEST_F('MediaHistoryOriginsWebUIBrowserTest', 'MAYBE_All', function() {
test('check data table is loaded', () => { test('check data table is loaded', () => {
let dataHeaderRows = let dataHeaderRows =
Array.from(document.getElementById('data-table-header').children); Array.from(document.querySelector('#origins-table thead tr').children);
assertDeepEquals( assertDeepEquals(
[ [
...@@ -98,3 +98,33 @@ TEST_F('MediaHistoryOriginsWebUIBrowserTest', 'MAYBE_All', function() { ...@@ -98,3 +98,33 @@ TEST_F('MediaHistoryOriginsWebUIBrowserTest', 'MAYBE_All', function() {
mocha.run(); mocha.run();
}); });
/**
* Tests for the playbacks tab.
* @extends {MediaHistoryWebUIBrowserTest}
*/
function MediaHistoryPlaybacksWebUIBrowserTest() {}
MediaHistoryPlaybacksWebUIBrowserTest.prototype = {
__proto__: MediaHistoryWebUIBrowserTest.prototype,
/** @override */
browsePreload: 'chrome://media-history#tab-playbacks',
};
TEST_F('MediaHistoryPlaybacksWebUIBrowserTest', 'MAYBE_All', function() {
suiteSetup(function() {
return whenPageIsPopulatedForTest();
});
test('check data table is loaded', () => {
let dataHeaderRows = Array.from(
document.querySelector('#playbacks-table thead tr').children);
assertDeepEquals(
['URL', 'Last Updated', 'Has Audio', 'Has Video', 'Watchtime (secs)'],
dataHeaderRows.map(x => x.textContent.trim()));
});
mocha.run();
});
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