blob: abd9655e1c8cf728ab266710a70aa6dbf231595c [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2019 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.
-->
<link rel="import" href="/tracing/base/base64.html">
<link rel="import" href="/tracing/extras/chrome/speed_index_utils.html">
<link rel="import" href="/tracing/metrics/system_health/breakdown_tree_helpers.html">
<script src="/jpeg-js-decoder.js"></script>
<script>
'use strict';
tr.exportTo('tr.metrics.sh', function() {
const timeDurationInMs_smallerIsBetter =
tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;
const SpeedIndex = tr.e.chrome.SpeedIndex;
const LOADING_METRIC_BOUNDARIES = tr.v.HistogramBinBoundaries
.createLinear(0, 1e3, 20) // 50ms step to 1s
.addLinearBins(3e3, 20) // 100ms step to 3s
.addExponentialBins(20e3, 20);
const SUMMARY_OPTIONS = {
avg: true,
count: false,
max: true,
min: true,
std: true,
sum: false,
};
/**
* Adds a speed index sample.
*
* @param {tr.model.helpers.ChromeBrowserrHelper} browserHelper
* @param {number[]} samples
* @param {tr.model.ThreadSlice} navigationStart
*/
function addSpeedIndexScreenshotsBasedSample(samples, navigationStart,
loadDuration, browserHelper) {
const screenshotObjects = browserHelper.process.objects
.getAllInstancesNamed('Screenshot');
if (!screenshotObjects) return;
for (let i = 0; i < screenshotObjects.length; i++) {
const snapshots = screenshotObjects[i].snapshots;
const timestampedColorHistograms = [];
snapshots.map(snapshot => {
if (snapshot.ts >= navigationStart.start &&
snapshot.ts < navigationStart.start + loadDuration) {
timestampedColorHistograms.push({colorHistogram:
SpeedIndex.createColorHistogram(getPixelData(snapshot.args)),
ts: snapshot.ts});
}
});
samples.push(
{value: SpeedIndex.calculateSpeedIndex(timestampedColorHistograms) -
navigationStart.start});
}
}
/**
* Extract RGBA pixel values from a base64 JPEG.
*
* @param {string} base64JpegImage - base64 jpeg image
* @return {number[]} - a flat array of rgba values for each pixel
*/
function getPixelData(base64JpegImage) {
const binaryString = atob(base64JpegImage);
const bytes = new DataView(new ArrayBuffer(base64JpegImage.length));
tr.b.Base64.DecodeToTypedArray(base64JpegImage, bytes);
const rawImageData = jpegDecode(bytes.buffer, {useTArray: true});
return rawImageData.data;
}
function collectSpeedIndexSamplesFromLoadExpectations(model, chromeHelper) {
const speedIndexScreenshotsBasedSamples = [];
for (const expectation of model.userModel.expectations) {
if (!(expectation instanceof tr.model.um.LoadExpectation)) continue;
if (tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)) {
continue;
}
const rendererHelper = chromeHelper.rendererHelpers[
expectation.renderProcess.pid];
addSpeedIndexScreenshotsBasedSample(speedIndexScreenshotsBasedSamples,
expectation.navigationStart, expectation.duration,
chromeHelper.browserHelper);
}
return speedIndexScreenshotsBasedSamples;
}
/** This metric is experminetal and should not be used. **/
function screenshotsBasedSpeedIndexMetric(histograms, model) {
const speedIndexScreenshotsBasedHistogram = histograms.createHistogram(
'speedIndexScreenshotsBased', timeDurationInMs_smallerIsBetter, [], {
binBoundaries: LOADING_METRIC_BOUNDARIES,
description: 'The average time at which visible parts of the' +
' page are displayed.',
summaryOptions: SUMMARY_OPTIONS,
});
const chromeHelper = model.getOrCreateHelper(
tr.model.helpers.ChromeModelHelper);
const samples =
collectSpeedIndexSamplesFromLoadExpectations(model, chromeHelper);
for (const sample of samples) {
speedIndexScreenshotsBasedHistogram.addSample(sample.value);
}
}
tr.metrics.MetricRegistry.register(screenshotsBasedSpeedIndexMetric);
return {
screenshotsBasedSpeedIndexMetric
};
});
</script>