blob: daa33aec3650d05911d2f7824e97378676f21479 [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 */
70 static handleDarkMode(el) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000071 if (UI.themeSupport.themeName() === 'dark') {
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000072 el.classList.add('dark');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000073 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000074 }
Patrick Hulcea087f622018-05-18 00:37:53 +000075};
cjamcl@google.comc5214af2019-06-25 20:31:21 +000076
77/**
78 * @override
79 */
80Audits.ReportUIFeatures = class extends ReportUIFeatures {
81 /**
Connor Clark99508362019-08-20 19:52:23 +000082 * @param {!DOM} dom
83 */
84 constructor(dom) {
85 super(dom);
86 this._beforePrint = null;
87 this._afterPrint = null;
88 }
89
90 /**
91 * @param {?function()} beforePrint
92 */
93 setBeforePrint(beforePrint) {
94 this._beforePrint = beforePrint;
95 }
96
97 /**
98 * @param {?function()} afterPrint
99 */
100 setAfterPrint(afterPrint) {
101 this._afterPrint = afterPrint;
102 }
103
104 /**
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000105 * Returns the html that recreates this report.
106 * @return {string}
107 * @protected
108 */
109 getReportHtml() {
110 this.resetUIState();
111 return Lighthouse.ReportGenerator.generateReportHtml(this.json);
112 }
113
114 /**
115 * Downloads a file (blob) using the system dialog prompt.
116 * @param {!Blob|!File} blob The file to save.
117 */
118 async _saveFile(blob) {
119 const domain = new Common.ParsedURL(this.json.finalUrl).domain();
120 const sanitizedDomain = domain.replace(/[^a-z0-9.-]+/gi, '_');
121 const timestamp = new Date(this.json.fetchTime).toISO8601Compact();
122 const ext = blob.type.match('json') ? '.json' : '.html';
123 const basename = `${sanitizedDomain}-${timestamp}${ext}`;
124 const text = await blob.text();
125 Workspace.fileManager.save(basename, text, true /* forceSaveAs */);
126 }
127
128 async _print() {
129 const document = this.getDocument();
130 const clonedReport = document.querySelector('.lh-root').cloneNode(true /* deep */);
131 const printWindow = window.open('', '_blank', 'channelmode=1,status=1,resizable=1');
132 const style = printWindow.document.createElement('style');
133 style.textContent = Runtime.cachedResources['audits/lighthouse/report.css'];
134 printWindow.document.head.appendChild(style);
135 printWindow.document.body.replaceWith(clonedReport);
136 // Linkified nodes are shadow elements, which aren't exposed via `cloneNode`.
137 await Audits.ReportRenderer.linkifyNodeDetails(clonedReport);
Connor Clark99508362019-08-20 19:52:23 +0000138
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000139 if (this._beforePrint) {
Connor Clark99508362019-08-20 19:52:23 +0000140 this._beforePrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000141 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000142 printWindow.focus();
143 printWindow.print();
144 printWindow.close();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000145 if (this._afterPrint) {
Connor Clark99508362019-08-20 19:52:23 +0000146 this._afterPrint();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000147 }
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000148 }
149
150 /**
151 * @suppress {visibility}
152 * @return {!Document}
153 */
154 getDocument() {
155 return this._document;
156 }
157
158 /**
159 * @suppress {visibility}
160 */
161 resetUIState() {
162 this._resetUIState();
163 }
164};