summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/resources/media/media_engagement.js
blob: 5491928b2229c606fd9b6ed482de97f4f6f5f9b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

// Allow a function to be provided by tests, which will be called when
// the page has been populated with media engagement details.
const pageIsPopulatedResolver = new PromiseResolver();
function whenPageIsPopulatedForTest() {
  return pageIsPopulatedResolver.promise;
}

(function() {

let uiHandler = null;
let info = null;
let engagementTableBody = null;
let sortReverse = true;
let sortKey = 'totalScore';
let configTableBody = null;
let showNoPlaybacks = false;

/**
 * Creates a single row in the engagement table.
 * @param {!MediaEngagementScoreDetails} rowInfo The info to create the row.
 * @return {!HTMLElement}
 */
function createRow(rowInfo) {
  const template = $('datarow');
  const td = template.content.querySelectorAll('td');

  td[0].textContent = rowInfo.origin.scheme + '://' + rowInfo.origin.host;
  if (rowInfo.origin.scheme == 'http' && rowInfo.origin.port != '80') {
    td[0].textContent += ':' + rowInfo.origin.port;
  } else if (rowInfo.origin.scheme == 'https' && rowInfo.origin.port != '443') {
    td[0].textContent += ':' + rowInfo.origin.port;
  }

  td[1].textContent = rowInfo.visits;
  td[2].textContent = rowInfo.mediaPlaybacks;
  td[3].textContent = rowInfo.audioContextPlaybacks;
  td[4].textContent = rowInfo.mediaElementPlaybacks;
  td[5].textContent = rowInfo.audiblePlaybacks;
  td[6].textContent = rowInfo.significantPlaybacks;
  td[7].textContent = rowInfo.lastMediaPlaybackTime ?
      new Date(rowInfo.lastMediaPlaybackTime).toISOString() :
      '';
  td[8].textContent = rowInfo.isHigh ? 'Yes' : 'No';
  td[9].textContent = rowInfo.highScoreChanges;
  td[10].textContent = rowInfo.totalScore ? rowInfo.totalScore.toFixed(2) : '0';
  td[11].getElementsByClassName('engagement-bar')[0].style.width =
      (rowInfo.totalScore * 50) + 'px';
  return document.importNode(template.content, true);
}

/**
 * Remove all rows from the engagement table.
 */
function clearTable() {
  engagementTableBody.innerHTML = '';
}

/**
 * Sort the engagement info based on |sortKey| and |sortReverse|.
 */
function sortInfo() {
  info.sort((a, b) => {
    return (sortReverse ? -1 : 1) * compareTableItem(sortKey, a, b);
  });
}

/**
 * Compares two MediaEngagementScoreDetails objects based on |sortKey|.
 * @param {string} sortKey The name of the property to sort by.
 * @param {number|url.mojom.Origin} The first object to compare.
 * @param {number|url.mojom.Origin} The second object to compare.
 * @return {number} A negative number if |a| should be ordered before
 *     |b|, a positive number otherwise.
 */
function compareTableItem(sortKey, a, b) {
  const val1 = a[sortKey];
  const val2 = b[sortKey];

  // Compare the hosts of the origin ignoring schemes.
  if (sortKey == 'origin') {
    return val1.host > val2.host ? 1 : -1;
  }

  if (sortKey == 'visits' || sortKey == 'mediaPlaybacks' ||
      sortKey == 'lastMediaPlaybackTime' || sortKey == 'totalScore' ||
      sortKey == 'audiblePlaybacks' || sortKey == 'significantPlaybacks' ||
      sortKey == 'highScoreChanges' || sortKey == 'mediaElementPlaybacks' ||
      sortKey == 'audioContextPlaybacks' || sortKey == 'isHigh') {
    return val1 - val2;
  }

  assertNotReached('Unsupported sort key: ' + sortKey);
  return 0;
}

/**
 * Creates a single row in the config table.
 * @param {string} name The name of the config setting.
 * @param {string} value The value of the config setting.
 * @return {!HTMLElement}
 */
function createConfigRow(name, value) {
  const template = $('configrow');
  const td = template.content.querySelectorAll('td');
  td[0].textContent = name;
  td[1].textContent = value;
  return document.importNode(template.content, true);
}

/**
 * Regenerates the config table.
 * @param {!MediaEngagementConfig} config The config of the MEI service.
 */

function renderConfigTable(config) {
  configTableBody.innerHTML = '';

  configTableBody.appendChild(
      createConfigRow('Min Sessions', config.scoreMinVisits));
  configTableBody.appendChild(
      createConfigRow('Lower Threshold', config.highScoreLowerThreshold));
  configTableBody.appendChild(
      createConfigRow('Upper Threshold', config.highScoreUpperThreshold));

  configTableBody.appendChild(createConfigRow(
      'Record MEI data', formatFeatureFlag(config.featureRecordData)));
  configTableBody.appendChild(createConfigRow(
      'Bypass autoplay based on MEI',
      formatFeatureFlag(config.featureBypassAutoplay)));
  configTableBody.appendChild(createConfigRow(
      'Preload MEI data', formatFeatureFlag(config.featurePreloadData)));
  configTableBody.appendChild(createConfigRow(
      'MEI for HTTPS only', formatFeatureFlag(config.featureHttpsOnly)));
  configTableBody.appendChild(createConfigRow(
      'Autoplay disable settings',
      formatFeatureFlag(config.featureAutoplayDisableSettings)));
  configTableBody.appendChild(createConfigRow(
      'Autoplay whitelist settings',
      formatFeatureFlag(config.featureAutoplayWhitelistSettings)));
  configTableBody.appendChild(createConfigRow(
      'Unified autoplay (preference)',
      formatFeatureFlag(config.prefDisableUnifiedAutoplay)));
  configTableBody.appendChild(createConfigRow(
      'Custom autoplay policy',
      formatFeatureFlag(config.hasCustomAutoplayPolicy)));
  configTableBody.appendChild(
      createConfigRow('Autoplay Policy', config.autoplayPolicy));
  configTableBody.appendChild(createConfigRow(
      'Preload version',
      config.preloadVersion ? config.preloadVersion : 'Not Available'));
}

/**
 * Converts a boolean into a string value.
 * @param {bool} value The value of the config setting.
 * @return {string}
 */
function formatFeatureFlag(value) {
  return value ? 'Enabled' : 'Disabled';
}

/**
 * Regenerates the engagement table from |info|.
 */
function renderTable() {
  clearTable();
  sortInfo();
  info.filter(rowInfo => (showNoPlaybacks || rowInfo.mediaPlaybacks > 0))
      .forEach(rowInfo => engagementTableBody.appendChild(createRow(rowInfo)));
}

/**
 * Retrieve media engagement info and render the engagement table.
 */
function updateEngagementTable() {
  // Populate engagement table.
  uiHandler.getMediaEngagementScoreDetails().then(response => {
    info = response.info;
    renderTable();
    pageIsPopulatedResolver.resolve();
  });

  // Populate config settings.
  uiHandler.getMediaEngagementConfig().then(response => {
    renderConfigTable(response.config);
  });
}

document.addEventListener('DOMContentLoaded', function() {
  uiHandler = media.mojom.MediaEngagementScoreDetailsProvider.getProxy();
  updateEngagementTable();

  engagementTableBody = $('engagement-table-body');
  configTableBody = $('config-table-body');

  // Set table header sort handlers.
  const engagementTableHeader = $('engagement-table-header');
  const headers = engagementTableHeader.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');
      }
      renderTable();
    });
  }

  // Add handler to 'copy all to clipboard' button
  const copyAllToClipboardButton = $('copy-all-to-clipboard');
  copyAllToClipboardButton.addEventListener('click', (e) => {
    // Make sure nothing is selected
    window.getSelection().removeAllRanges();

    document.execCommand('selectAll');
    document.execCommand('copy');

    // And deselect everything at the end.
    window.getSelection().removeAllRanges();
  });

  // Add handler to 'show no playbacks' checkbox
  const showNoPlaybacksCheckbox = $('show-no-playbacks');
  showNoPlaybacksCheckbox.addEventListener('change', (e) => {
    showNoPlaybacks = e.target.checked;
    renderTable();
  });
});
})();