blob: 1f89642714c3e27b75eb4d07c6232543b5216720 [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
5/**
6 * @override
7 */
cjamcl@google.comaa1532c2019-05-31 03:01:24 +00008Audits.ReportRenderer = class extends ReportRenderer {
Patrick Hulcea087f622018-05-18 00:37:53 +00009 /**
Paul Irish8f1e33d2018-05-31 02:29:50 +000010 * @param {!Element} el Parent element to render the report into.
11 * @param {!ReportRenderer.RunnerResultArtifacts=} artifacts
Patrick Hulcea087f622018-05-18 00:37:53 +000012 */
Paul Irish8f1e33d2018-05-31 02:29:50 +000013 static addViewTraceButton(el, artifacts) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000014 if (!artifacts || !artifacts.traces || !artifacts.traces.defaultPass) {
Paul Irish8f1e33d2018-05-31 02:29:50 +000015 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000016 }
Patrick Hulcea087f622018-05-18 00:37:53 +000017
cjamcl@google.com21d2d222019-08-09 01:58:17 +000018 const container = el.querySelector('.lh-audit-group');
19 const columnsEl = container.querySelector('.lh-columns');
20 // There will be no columns if just the PWA category.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000021 if (!columnsEl) {
cjamcl@google.com21d2d222019-08-09 01:58:17 +000022 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000023 }
cjamcl@google.com21d2d222019-08-09 01:58:17 +000024
Paul Irish8f1e33d2018-05-31 02:29:50 +000025 const defaultPassTrace = artifacts.traces.defaultPass;
26 const timelineButton = UI.createTextButton(Common.UIString('View Trace'), onViewTraceClick, 'view-trace');
cjamcl@google.com21d2d222019-08-09 01:58:17 +000027 container.insertBefore(timelineButton, columnsEl.nextSibling);
Paul Irish8f1e33d2018-05-31 02:29:50 +000028
29 async function onViewTraceClick() {
cjamcl@google.comaa1532c2019-05-31 03:01:24 +000030 Host.userMetrics.actionTaken(Host.UserMetrics.Action.AuditsViewTrace);
Paul Irish8f1e33d2018-05-31 02:29:50 +000031 await UI.inspectorView.showPanel('timeline');
32 Timeline.TimelinePanel.instance().loadFromEvents(defaultPassTrace.traceEvents);
33 }
Patrick Hulcea087f622018-05-18 00:37:53 +000034 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000035
36 /**
37 * @param {!Element} el
38 */
39 static async linkifyNodeDetails(el) {
40 const mainTarget = SDK.targetManager.mainTarget();
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000041 const domModel = mainTarget.model(SDK.DOMModel);
42
43 for (const origElement of el.getElementsByClassName('lh-node')) {
44 /** @type {!DetailsRenderer.NodeDetailsJSON} */
45 const detailsItem = origElement.dataset;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000046 if (!detailsItem.path) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000047 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000048 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000049
50 const nodeId = await domModel.pushNodeByPathToFrontend(detailsItem.path);
51
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000052 if (!nodeId) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000053 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000054 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000055 const node = domModel.nodeForId(nodeId);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000056 if (!node) {
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000057 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000058 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000059
Jeff Fisher3f5f19c2019-08-28 19:10:02 +000060 const element = await Common.Linkifier.linkify(node, {tooltip: detailsItem.snippet});
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000061 origElement.title = '';
62 origElement.textContent = '';
63 origElement.appendChild(element);
64 }
65 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000066
67 /**
68 * @param {!Element} el
69 */
Connor Clark0403a422019-11-18 18:03:18 -080070 static async linkifySourceLocationDetails(el) {
71 for (const origElement of el.getElementsByClassName('lh-source-location')) {
72 /** @type {!DetailsRenderer.SourceLocationDetailsJSON} */
73 const detailsItem = origElement.dataset;
74 if (!detailsItem.sourceUrl || !detailsItem.sourceLine || !detailsItem.sourceColumn) {
75 continue;
76 }
77 const url = detailsItem.sourceUrl;
78 const line = Number(detailsItem.sourceLine);
79 const column = Number(detailsItem.sourceColumn);
80 const element = await Components.Linkifier.linkifyURL(
81 url, {lineNumber: line, column, maxLength: Audits.ReportRenderer.MaxLengthForLinks});
82 origElement.title = '';
83 origElement.textContent = '';
84 origElement.appendChild(element);
85 }
86 }
87
88 /**
89 * @param {!Element} el
90 */
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000091 static handleDarkMode(el) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000092 if (UI.themeSupport.themeName() === 'dark') {
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000093 el.classList.add('dark');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000094 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000095 }
Patrick Hulcea087f622018-05-18 00:37:53 +000096};
cjamcl@google.comc5214af2019-06-25 20:31:21 +000097
98/**
99 * @override
100 */
101Audits.ReportUIFeatures = class extends ReportUIFeatures {
102 /**
Connor Clark99508362019-08-20 19:52:23 +0000103 * @param {!DOM} dom
104 */
105 constructor(dom) {
106 super(dom);
107 this._beforePrint = null;
108 this._afterPrint = null;
109 }
110
111 /**
112 * @param {?function()} beforePrint
113 */
114 setBeforePrint(beforePrint) {
115 this._beforePrint = beforePrint;
116 }
117
118 /**
119 * @param {?function()} afterPrint
120 */
121 setAfterPrint(afterPrint) {
122 this._afterPrint = afterPrint;
123 }
124
125 /**
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000126 * Returns the html that recreates this report.
127 * @return {string}
128 * @protected
129 */
130 getReportHtml() {
131 this.resetUIState();
132 return Lighthouse.ReportGenerator.generateReportHtml(this.json);
133 }
134
135 /**
136 * Downloads a file (blob) using the system dialog prompt.
137 * @param {!Blob|!File} blob The file to save.
138 */
139 async _saveFile(blob) {
140 const domain = new Common.ParsedURL(this.json.finalUrl).domain();
141 const sanitizedDomain = domain.replace(/[^a-z0-9.-]+/gi, '_');
142 const timestamp = new Date(this.json.fetchTime).toISO8601Compact();
143 const ext = blob.type.match('json') ? '.json' : '.html';
144 const basename = `${sanitizedDomain}-${timestamp}${ext}`;
145 const text = await blob.text();
146 Workspace.fileManager.save(basename, text, true /* forceSaveAs */);
147 }
148
149 async _print() {
150 const document = this.getDocument();
151 const clonedReport = document.querySelector('.lh-root').cloneNode(true /* deep */);
152 const printWindow = window.open('', '_blank', 'channelmode=1,status=1,resizable=1');
153 const style = printWindow.document.createElement('style');
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000154 style.textContent = Root.Runtime.cachedResources['audits/lighthouse/report.css'];
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000155 printWindow.document.head.appendChild(style);
156 printWindow.document.body.replaceWith(clonedReport);
157 // Linkified nodes are shadow elements, which aren't exposed via `cloneNode`.
158 await Audits.ReportRenderer.linkifyNodeDetails(clonedReport);
Connor Clark99508362019-08-20 19:52:23 +0000159
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000160 if (this._beforePrint) {
Connor Clark99508362019-08-20 19:52:23 +0000161 this._beforePrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000162 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000163 printWindow.focus();
164 printWindow.print();
165 printWindow.close();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000166 if (this._afterPrint) {
Connor Clark99508362019-08-20 19:52:23 +0000167 this._afterPrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000168 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000169 }
170
171 /**
172 * @suppress {visibility}
173 * @return {!Document}
174 */
175 getDocument() {
176 return this._document;
177 }
178
179 /**
180 * @suppress {visibility}
181 */
182 resetUIState() {
183 this._resetUIState();
184 }
185};
Connor Clark0403a422019-11-18 18:03:18 -0800186
187/**
188 * @const
189 * @type {number}
190 */
191Audits.ReportRenderer.MaxLengthForLinks = 40;