blob: 942c5d3a661ee79d4e52ea5f0c938e871c91b2bc [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
Paul Lewiscf2ef222019-11-22 14:55:35 +00005const MaxLengthForLinks = 40;
6
Patrick Hulcea087f622018-05-18 00:37:53 +00007/**
8 * @override
9 */
Connor Clark2bc3be22020-02-14 14:34:19 -080010export class LighthouseReportRenderer extends ReportRenderer {
Patrick Hulcea087f622018-05-18 00:37:53 +000011 /**
Paul Irish8f1e33d2018-05-31 02:29:50 +000012 * @param {!Element} el Parent element to render the report into.
13 * @param {!ReportRenderer.RunnerResultArtifacts=} artifacts
Patrick Hulcea087f622018-05-18 00:37:53 +000014 */
Paul Irish8f1e33d2018-05-31 02:29:50 +000015 static addViewTraceButton(el, artifacts) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000016 if (!artifacts || !artifacts.traces || !artifacts.traces.defaultPass) {
Paul Irish8f1e33d2018-05-31 02:29:50 +000017 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000018 }
Patrick Hulcea087f622018-05-18 00:37:53 +000019
cjamcl@google.com21d2d222019-08-09 01:58:17 +000020 const container = el.querySelector('.lh-audit-group');
21 const columnsEl = container.querySelector('.lh-columns');
22 // There will be no columns if just the PWA category.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000023 if (!columnsEl) {
cjamcl@google.com21d2d222019-08-09 01:58:17 +000024 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000025 }
cjamcl@google.com21d2d222019-08-09 01:58:17 +000026
Paul Irish8f1e33d2018-05-31 02:29:50 +000027 const defaultPassTrace = artifacts.traces.defaultPass;
28 const timelineButton = UI.createTextButton(Common.UIString('View Trace'), onViewTraceClick, 'view-trace');
cjamcl@google.com21d2d222019-08-09 01:58:17 +000029 container.insertBefore(timelineButton, columnsEl.nextSibling);
Paul Irish8f1e33d2018-05-31 02:29:50 +000030
31 async function onViewTraceClick() {
Connor Clark2bc3be22020-02-14 14:34:19 -080032 Host.userMetrics.actionTaken(Host.UserMetrics.Action.LighthouseViewTrace);
Paul Lewis0a7c6b62020-01-23 16:16:22 +000033 await self.UI.inspectorView.showPanel('timeline');
Paul Irish8f1e33d2018-05-31 02:29:50 +000034 Timeline.TimelinePanel.instance().loadFromEvents(defaultPassTrace.traceEvents);
35 }
Patrick Hulcea087f622018-05-18 00:37:53 +000036 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000037
38 /**
39 * @param {!Element} el
40 */
41 static async linkifyNodeDetails(el) {
Paul Lewis4ae5f4f2020-01-23 10:19:33 +000042 const mainTarget = self.SDK.targetManager.mainTarget();
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000043 const domModel = mainTarget.model(SDK.DOMModel);
44
45 for (const origElement of el.getElementsByClassName('lh-node')) {
46 /** @type {!DetailsRenderer.NodeDetailsJSON} */
47 const detailsItem = origElement.dataset;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000048 if (!detailsItem.path) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000049 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000050 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000051
52 const nodeId = await domModel.pushNodeByPathToFrontend(detailsItem.path);
53
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000054 if (!nodeId) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000055 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000056 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000057 const node = domModel.nodeForId(nodeId);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000058 if (!node) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000059 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000060 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000061
Jeff Fisher3f5f19c2019-08-28 19:10:02 +000062 const element = await Common.Linkifier.linkify(node, {tooltip: detailsItem.snippet});
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000063 origElement.title = '';
64 origElement.textContent = '';
65 origElement.appendChild(element);
66 }
67 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000068
69 /**
70 * @param {!Element} el
71 */
Connor Clark0403a422019-11-18 18:03:18 -080072 static async linkifySourceLocationDetails(el) {
73 for (const origElement of el.getElementsByClassName('lh-source-location')) {
74 /** @type {!DetailsRenderer.SourceLocationDetailsJSON} */
75 const detailsItem = origElement.dataset;
76 if (!detailsItem.sourceUrl || !detailsItem.sourceLine || !detailsItem.sourceColumn) {
77 continue;
78 }
79 const url = detailsItem.sourceUrl;
80 const line = Number(detailsItem.sourceLine);
81 const column = Number(detailsItem.sourceColumn);
Paul Lewiscf2ef222019-11-22 14:55:35 +000082 const element =
83 await Components.Linkifier.linkifyURL(url, {lineNumber: line, column, maxLength: MaxLengthForLinks});
Connor Clark0403a422019-11-18 18:03:18 -080084 origElement.title = '';
85 origElement.textContent = '';
86 origElement.appendChild(element);
87 }
88 }
89
90 /**
91 * @param {!Element} el
92 */
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000093 static handleDarkMode(el) {
Paul Lewis93d8e2c2020-01-24 16:34:55 +000094 if (self.UI.themeSupport.themeName() === 'dark') {
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000095 el.classList.add('dark');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000096 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000097 }
Paul Lewiscf2ef222019-11-22 14:55:35 +000098}
cjamcl@google.comc5214af2019-06-25 20:31:21 +000099
100/**
101 * @override
102 */
Connor Clark2bc3be22020-02-14 14:34:19 -0800103export class LighthouseReportUIFeatures extends ReportUIFeatures {
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000104 /**
Connor Clark99508362019-08-20 19:52:23 +0000105 * @param {!DOM} dom
106 */
107 constructor(dom) {
108 super(dom);
109 this._beforePrint = null;
110 this._afterPrint = null;
111 }
112
113 /**
114 * @param {?function()} beforePrint
115 */
116 setBeforePrint(beforePrint) {
117 this._beforePrint = beforePrint;
118 }
119
120 /**
121 * @param {?function()} afterPrint
122 */
123 setAfterPrint(afterPrint) {
124 this._afterPrint = afterPrint;
125 }
126
127 /**
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000128 * Returns the html that recreates this report.
129 * @return {string}
130 * @protected
131 */
132 getReportHtml() {
133 this.resetUIState();
134 return Lighthouse.ReportGenerator.generateReportHtml(this.json);
135 }
136
137 /**
138 * Downloads a file (blob) using the system dialog prompt.
139 * @param {!Blob|!File} blob The file to save.
140 */
141 async _saveFile(blob) {
142 const domain = new Common.ParsedURL(this.json.finalUrl).domain();
143 const sanitizedDomain = domain.replace(/[^a-z0-9.-]+/gi, '_');
144 const timestamp = new Date(this.json.fetchTime).toISO8601Compact();
145 const ext = blob.type.match('json') ? '.json' : '.html';
146 const basename = `${sanitizedDomain}-${timestamp}${ext}`;
147 const text = await blob.text();
Paul Lewisdff48e42020-01-24 11:46:48 +0000148 self.Workspace.fileManager.save(basename, text, true /* forceSaveAs */);
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000149 }
150
151 async _print() {
152 const document = this.getDocument();
153 const clonedReport = document.querySelector('.lh-root').cloneNode(true /* deep */);
154 const printWindow = window.open('', '_blank', 'channelmode=1,status=1,resizable=1');
155 const style = printWindow.document.createElement('style');
Connor Clark933525c2020-02-27 15:27:39 -0800156 style.textContent = Root.Runtime.cachedResources['third_party/lighthouse/report-assets/report.css'];
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000157 printWindow.document.head.appendChild(style);
158 printWindow.document.body.replaceWith(clonedReport);
159 // Linkified nodes are shadow elements, which aren't exposed via `cloneNode`.
Connor Clark2bc3be22020-02-14 14:34:19 -0800160 await LighthouseReportRenderer.linkifyNodeDetails(clonedReport);
Connor Clark99508362019-08-20 19:52:23 +0000161
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000162 if (this._beforePrint) {
Connor Clark99508362019-08-20 19:52:23 +0000163 this._beforePrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000164 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000165 printWindow.focus();
166 printWindow.print();
167 printWindow.close();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000168 if (this._afterPrint) {
Connor Clark99508362019-08-20 19:52:23 +0000169 this._afterPrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000170 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000171 }
172
173 /**
174 * @suppress {visibility}
175 * @return {!Document}
176 */
177 getDocument() {
178 return this._document;
179 }
180
181 /**
182 * @suppress {visibility}
183 */
184 resetUIState() {
185 this._resetUIState();
186 }
Paul Lewiscf2ef222019-11-22 14:55:35 +0000187}