blob: f3f1edf2ac343b4e6bcbb4e3f1b06f41b4232045 [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) {
14 if (!artifacts || !artifacts.traces || !artifacts.traces.defaultPass)
15 return;
Patrick Hulcea087f622018-05-18 00:37:53 +000016
cjamcl@google.com21d2d222019-08-09 01:58:17 +000017 const container = el.querySelector('.lh-audit-group');
18 const columnsEl = container.querySelector('.lh-columns');
19 // There will be no columns if just the PWA category.
20 if (!columnsEl)
21 return;
22
Paul Irish8f1e33d2018-05-31 02:29:50 +000023 const defaultPassTrace = artifacts.traces.defaultPass;
24 const timelineButton = UI.createTextButton(Common.UIString('View Trace'), onViewTraceClick, 'view-trace');
cjamcl@google.com21d2d222019-08-09 01:58:17 +000025 container.insertBefore(timelineButton, columnsEl.nextSibling);
Paul Irish8f1e33d2018-05-31 02:29:50 +000026
27 async function onViewTraceClick() {
cjamcl@google.comaa1532c2019-05-31 03:01:24 +000028 Host.userMetrics.actionTaken(Host.UserMetrics.Action.AuditsViewTrace);
Paul Irish8f1e33d2018-05-31 02:29:50 +000029 await UI.inspectorView.showPanel('timeline');
30 Timeline.TimelinePanel.instance().loadFromEvents(defaultPassTrace.traceEvents);
31 }
Patrick Hulcea087f622018-05-18 00:37:53 +000032 }
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000033
34 /**
35 * @param {!Element} el
36 */
37 static async linkifyNodeDetails(el) {
38 const mainTarget = SDK.targetManager.mainTarget();
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000039 const domModel = mainTarget.model(SDK.DOMModel);
40
41 for (const origElement of el.getElementsByClassName('lh-node')) {
42 /** @type {!DetailsRenderer.NodeDetailsJSON} */
43 const detailsItem = origElement.dataset;
44 if (!detailsItem.path)
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000045 continue;
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000046
47 const nodeId = await domModel.pushNodeByPathToFrontend(detailsItem.path);
48
49 if (!nodeId)
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000050 continue;
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000051 const node = domModel.nodeForId(nodeId);
52 if (!node)
cjamcl@google.com5c46b7e2018-12-04 14:44:47 +000053 continue;
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000054
Jeff Fisher3f5f19c2019-08-28 19:10:02 +000055 const element = await Common.Linkifier.linkify(node, {tooltip: detailsItem.snippet});
cjamcl@google.comda0e4c62018-11-27 23:52:10 +000056 origElement.title = '';
57 origElement.textContent = '';
58 origElement.appendChild(element);
59 }
60 }
cjamcl@google.comf2f8c092019-05-30 22:01:56 +000061
62 /**
63 * @param {!Element} el
64 */
65 static handleDarkMode(el) {
66 if (UI.themeSupport.themeName() === 'dark')
67 el.classList.add('dark');
68 }
Patrick Hulcea087f622018-05-18 00:37:53 +000069};
cjamcl@google.comc5214af2019-06-25 20:31:21 +000070
71/**
72 * @override
73 */
74Audits.ReportUIFeatures = class extends ReportUIFeatures {
75 /**
Connor Clark99508362019-08-20 19:52:23 +000076 * @param {!DOM} dom
77 */
78 constructor(dom) {
79 super(dom);
80 this._beforePrint = null;
81 this._afterPrint = null;
82 }
83
84 /**
85 * @param {?function()} beforePrint
86 */
87 setBeforePrint(beforePrint) {
88 this._beforePrint = beforePrint;
89 }
90
91 /**
92 * @param {?function()} afterPrint
93 */
94 setAfterPrint(afterPrint) {
95 this._afterPrint = afterPrint;
96 }
97
98 /**
cjamcl@google.comc5214af2019-06-25 20:31:21 +000099 * Returns the html that recreates this report.
100 * @return {string}
101 * @protected
102 */
103 getReportHtml() {
104 this.resetUIState();
105 return Lighthouse.ReportGenerator.generateReportHtml(this.json);
106 }
107
108 /**
109 * Downloads a file (blob) using the system dialog prompt.
110 * @param {!Blob|!File} blob The file to save.
111 */
112 async _saveFile(blob) {
113 const domain = new Common.ParsedURL(this.json.finalUrl).domain();
114 const sanitizedDomain = domain.replace(/[^a-z0-9.-]+/gi, '_');
115 const timestamp = new Date(this.json.fetchTime).toISO8601Compact();
116 const ext = blob.type.match('json') ? '.json' : '.html';
117 const basename = `${sanitizedDomain}-${timestamp}${ext}`;
118 const text = await blob.text();
119 Workspace.fileManager.save(basename, text, true /* forceSaveAs */);
120 }
121
122 async _print() {
123 const document = this.getDocument();
124 const clonedReport = document.querySelector('.lh-root').cloneNode(true /* deep */);
125 const printWindow = window.open('', '_blank', 'channelmode=1,status=1,resizable=1');
126 const style = printWindow.document.createElement('style');
127 style.textContent = Runtime.cachedResources['audits/lighthouse/report.css'];
128 printWindow.document.head.appendChild(style);
129 printWindow.document.body.replaceWith(clonedReport);
130 // Linkified nodes are shadow elements, which aren't exposed via `cloneNode`.
131 await Audits.ReportRenderer.linkifyNodeDetails(clonedReport);
Connor Clark99508362019-08-20 19:52:23 +0000132
133 if (this._beforePrint)
134 this._beforePrint();
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000135 printWindow.focus();
136 printWindow.print();
137 printWindow.close();
Connor Clark99508362019-08-20 19:52:23 +0000138 if (this._afterPrint)
139 this._afterPrint();
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000140 }
141
142 /**
143 * @suppress {visibility}
144 * @return {!Document}
145 */
146 getDocument() {
147 return this._document;
148 }
149
150 /**
151 * @suppress {visibility}
152 */
153 resetUIState() {
154 this._resetUIState();
155 }
156};