blob: c4959514026c50a92e51a11fe051231c7e9abcc6 [file] [log] [blame]
Patrick Hulcea087f622018-05-18 00:37:53 +00001// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Tim van der Lippe132b4ba2020-09-02 16:00:48 +01005// @ts-nocheck
6// TODO(crbug.com/1011811): Enable TypeScript compiler checks
7
Paul Lewisdaac1062020-03-05 14:37:10 +00008import * as Common from '../common/common.js';
9import * as Components from '../components/components.js';
10import * as HostModule from '../host/host.js';
Simon Zünd2c704cd2020-06-04 11:08:35 +020011import * as Platform from '../platform/platform.js';
Tim van der Lippe5df64b22020-09-11 13:04:24 +010012import * as Root from '../root/root.js';
Paul Lewisdaac1062020-03-05 14:37:10 +000013import * as SDK from '../sdk/sdk.js';
Paul Lewisca569a52020-09-09 17:11:51 +010014import * as ThemeSupport from '../theme_support/theme_support.js';
Paul Lewisdaac1062020-03-05 14:37:10 +000015import * as Timeline from '../timeline/timeline.js';
16import * as UI from '../ui/ui.js';
Tim van der Lippe696c9262020-08-26 15:39:32 +010017import * as Workspace from '../workspace/workspace.js';
Paul Lewisdaac1062020-03-05 14:37:10 +000018
Tim van der Lippe1e10f852020-10-30 14:35:01 +000019import * as ReportRenderer from './LighthouseReporterTypes.js'; // eslint-disable-line no-unused-vars
20
Paul Lewiscf2ef222019-11-22 14:55:35 +000021const MaxLengthForLinks = 40;
22
Patrick Hulcea087f622018-05-18 00:37:53 +000023/**
24 * @override
25 */
Tim van der Lippe1e10f852020-10-30 14:35:01 +000026export class LighthouseReportRenderer extends self.ReportRenderer {
Patrick Hulcea087f622018-05-18 00:37:53 +000027 /**
Tim van der Lippe4df32c92020-11-06 12:35:05 +000028 * @param {!DOM} dom
29 */
30 constructor(dom) {
31 super(dom);
32 }
33 /**
Paul Irish8f1e33d2018-05-31 02:29:50 +000034 * @param {!Element} el Parent element to render the report into.
35 * @param {!ReportRenderer.RunnerResultArtifacts=} artifacts
Patrick Hulcea087f622018-05-18 00:37:53 +000036 */
Paul Irish8f1e33d2018-05-31 02:29:50 +000037 static addViewTraceButton(el, artifacts) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000038 if (!artifacts || !artifacts.traces || !artifacts.traces.defaultPass) {
Paul Irish8f1e33d2018-05-31 02:29:50 +000039 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000040 }
Patrick Hulcea087f622018-05-18 00:37:53 +000041
Adam Rainef46d1582020-08-17 20:24:32 +000042 const simulated = artifacts.settings.throttlingMethod === 'simulate';
cjamcl@google.com21d2d222019-08-09 01:58:17 +000043 const container = el.querySelector('.lh-audit-group');
Paul Irishbf204682020-05-13 16:11:37 -070044 const disclaimerEl = container.querySelector('.lh-metrics__disclaimer');
45 // If it was a PWA-only run, we'd have a trace but no perf category to add the button to
46 if (!disclaimerEl) {
cjamcl@google.com21d2d222019-08-09 01:58:17 +000047 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000048 }
cjamcl@google.com21d2d222019-08-09 01:58:17 +000049
Paul Irish8f1e33d2018-05-31 02:29:50 +000050 const defaultPassTrace = artifacts.traces.defaultPass;
Adam Rainef46d1582020-08-17 20:24:32 +000051 const label = simulated ? Common.UIString.UIString('View Original Trace') : Common.UIString.UIString('View Trace');
52 const timelineButton = UI.UIUtils.createTextButton(label, onViewTraceClick, 'view-trace');
53 if (simulated) {
54 timelineButton.title = Common.UIString.UIString(
55 'The performance metrics above are simulated and won\'t match the timings found in this trace. Disable simulated throttling in "Lighthouse Settings" if you want the timings to match.');
56 }
Paul Irishbf204682020-05-13 16:11:37 -070057 container.insertBefore(timelineButton, disclaimerEl.nextSibling);
Paul Irish8f1e33d2018-05-31 02:29:50 +000058
59 async function onViewTraceClick() {
Paul Lewisdaac1062020-03-05 14:37:10 +000060 HostModule.userMetrics.actionTaken(Host.UserMetrics.Action.LighthouseViewTrace);
Tim van der Lippe80d82652020-08-27 14:53:44 +010061 await UI.InspectorView.InspectorView.instance().showPanel('timeline');
Paul Lewisdaac1062020-03-05 14:37:10 +000062 Timeline.TimelinePanel.TimelinePanel.instance().loadFromEvents(defaultPassTrace.traceEvents);
Paul Irish8f1e33d2018-05-31 02:29:50 +000063 }
Patrick Hulcea087f622018-05-18 00:37:53 +000064 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000065
66 /**
67 * @param {!Element} el
68 */
69 static async linkifyNodeDetails(el) {
Paul Lewisdaac1062020-03-05 14:37:10 +000070 const mainTarget = SDK.SDKModel.TargetManager.instance().mainTarget();
71 const domModel = mainTarget.model(SDK.DOMModel.DOMModel);
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000072
73 for (const origElement of el.getElementsByClassName('lh-node')) {
Tim van der Lippe1e10f852020-10-30 14:35:01 +000074 /** @type {!ReportRenderer.NodeDetailsJSON} */
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000075 const detailsItem = origElement.dataset;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000076 if (!detailsItem.path) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000077 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000078 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000079
80 const nodeId = await domModel.pushNodeByPathToFrontend(detailsItem.path);
81
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000082 if (!nodeId) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000083 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000084 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000085 const node = domModel.nodeForId(nodeId);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000086 if (!node) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000087 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000088 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000089
Paul Lewisdaac1062020-03-05 14:37:10 +000090 const element = await Common.Linkifier.Linkifier.linkify(node, {tooltip: detailsItem.snippet});
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000091 origElement.title = '';
92 origElement.textContent = '';
93 origElement.appendChild(element);
94 }
95 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000096
97 /**
98 * @param {!Element} el
99 */
Connor Clark0403a422019-11-18 18:03:18 -0800100 static async linkifySourceLocationDetails(el) {
101 for (const origElement of el.getElementsByClassName('lh-source-location')) {
Tim van der Lippe1e10f852020-10-30 14:35:01 +0000102 /** @type {!ReportRenderer.SourceLocationDetailsJSON} */
Connor Clark0403a422019-11-18 18:03:18 -0800103 const detailsItem = origElement.dataset;
104 if (!detailsItem.sourceUrl || !detailsItem.sourceLine || !detailsItem.sourceColumn) {
105 continue;
106 }
107 const url = detailsItem.sourceUrl;
108 const line = Number(detailsItem.sourceLine);
109 const column = Number(detailsItem.sourceColumn);
Paul Lewisdaac1062020-03-05 14:37:10 +0000110 const element = await Components.Linkifier.Linkifier.linkifyURL(
111 url, {lineNumber: line, column, maxLength: MaxLengthForLinks});
Connor Clark0403a422019-11-18 18:03:18 -0800112 origElement.title = '';
113 origElement.textContent = '';
114 origElement.appendChild(element);
115 }
116 }
117
118 /**
119 * @param {!Element} el
120 */
cjamcl@google.comf2f8c092019-05-30 22:01:56 +0000121 static handleDarkMode(el) {
Paul Lewisca569a52020-09-09 17:11:51 +0100122 if (ThemeSupport.ThemeSupport.instance().themeName() === 'dark') {
cjamcl@google.comf2f8c092019-05-30 22:01:56 +0000123 el.classList.add('dark');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000124 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +0000125 }
Paul Lewiscf2ef222019-11-22 14:55:35 +0000126}
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000127
128/**
129 * @override
130 */
Tim van der Lippe1e10f852020-10-30 14:35:01 +0000131export class LighthouseReportUIFeatures extends self.ReportUIFeatures {
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000132 /**
Connor Clark99508362019-08-20 19:52:23 +0000133 * @param {!DOM} dom
134 */
135 constructor(dom) {
136 super(dom);
137 this._beforePrint = null;
138 this._afterPrint = null;
139 }
140
141 /**
Tim van der Lippe4df32c92020-11-06 12:35:05 +0000142 * @override
143 * @param {?function():void} beforePrint
Connor Clark99508362019-08-20 19:52:23 +0000144 */
145 setBeforePrint(beforePrint) {
146 this._beforePrint = beforePrint;
147 }
148
149 /**
Tim van der Lippe4df32c92020-11-06 12:35:05 +0000150 * @override
151 * @param {?function():void} afterPrint
Connor Clark99508362019-08-20 19:52:23 +0000152 */
153 setAfterPrint(afterPrint) {
154 this._afterPrint = afterPrint;
155 }
156
157 /**
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000158 * Returns the html that recreates this report.
159 * @return {string}
160 * @protected
161 */
162 getReportHtml() {
163 this.resetUIState();
164 return Lighthouse.ReportGenerator.generateReportHtml(this.json);
165 }
166
167 /**
168 * Downloads a file (blob) using the system dialog prompt.
169 * @param {!Blob|!File} blob The file to save.
170 */
171 async _saveFile(blob) {
Paul Lewisdaac1062020-03-05 14:37:10 +0000172 const domain = new Common.ParsedURL.ParsedURL(this.json.finalUrl).domain();
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000173 const sanitizedDomain = domain.replace(/[^a-z0-9.-]+/gi, '_');
Simon Zünd2c704cd2020-06-04 11:08:35 +0200174 const timestamp = Platform.DateUtilities.toISO8601Compact(new Date(this.json.fetchTime));
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000175 const ext = blob.type.match('json') ? '.json' : '.html';
176 const basename = `${sanitizedDomain}-${timestamp}${ext}`;
177 const text = await blob.text();
Tim van der Lippe696c9262020-08-26 15:39:32 +0100178 Workspace.FileManager.FileManager.instance().save(basename, text, true /* forceSaveAs */);
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000179 }
180
181 async _print() {
182 const document = this.getDocument();
183 const clonedReport = document.querySelector('.lh-root').cloneNode(true /* deep */);
184 const printWindow = window.open('', '_blank', 'channelmode=1,status=1,resizable=1');
185 const style = printWindow.document.createElement('style');
Tim van der Lippe5df64b22020-09-11 13:04:24 +0100186 style.textContent = Root.Runtime.cachedResources.get('third_party/lighthouse/report-assets/report.css');
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000187 printWindow.document.head.appendChild(style);
188 printWindow.document.body.replaceWith(clonedReport);
189 // Linkified nodes are shadow elements, which aren't exposed via `cloneNode`.
Connor Clark2bc3be22020-02-14 14:34:19 -0800190 await LighthouseReportRenderer.linkifyNodeDetails(clonedReport);
Connor Clark99508362019-08-20 19:52:23 +0000191
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000192 if (this._beforePrint) {
Connor Clark99508362019-08-20 19:52:23 +0000193 this._beforePrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000194 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000195 printWindow.focus();
196 printWindow.print();
197 printWindow.close();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000198 if (this._afterPrint) {
Connor Clark99508362019-08-20 19:52:23 +0000199 this._afterPrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000200 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000201 }
202
203 /**
204 * @suppress {visibility}
205 * @return {!Document}
206 */
207 getDocument() {
208 return this._document;
209 }
210
211 /**
212 * @suppress {visibility}
213 */
214 resetUIState() {
215 this._resetUIState();
216 }
Paul Lewiscf2ef222019-11-22 14:55:35 +0000217}