From dcddff9bc36b2b6e9f0e5bd137759bcc98b0f8fc Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 18 May 2021 14:03:46 -0700 Subject: [PATCH 1/5] wip --- .../html/renderer/report-ui-features.js | 51 ++++++++++++++++--- lighthouse-treemap/app/src/main.js | 11 +++- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index e7423b85a1ef..4934d202e058 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -158,7 +158,8 @@ class ReportUIFeatures { this.addButton({ text: Util.i18n.strings.viewTreemapLabel, icon: 'treemap', - onClick: () => ReportUIFeatures.openTreemap(this.json), + onClick: () => ReportUIFeatures.openTreemap( + this.json, this._dom.isDevTools() ? 'url' : 'postMessage'), }); } @@ -548,20 +549,39 @@ class ReportUIFeatures { /** * Opens a new tab to the treemap app and sends the JSON results using postMessage. * @param {LH.Result} json + * @param {'postMessage'|'url'} method + * @protected */ - static openTreemap(json) { + static openTreemap(json, method = 'postMessage') { const treemapData = json.audits['script-treemap-data'].details; if (!treemapData) { throw new Error('no script treemap data found'); } - const windowName = `treemap-${json.requestedUrl}`; - /** @type {LH.Treemap.Options} */ - const treemapOptions = { - lhr: json, + // Only send the minimum lhr needed for the treemap app. + /** @type {RecursivePartial} */ + const treemapOptionsTrimmed = { + lhr: { + requestedUrl: json.requestedUrl, + finalUrl: json.finalUrl, + audits: { + 'script-treemap-data': json.audits['script-treemap-data'], + }, + configSettings: { + locale: json.configSettings.locale, + }, + }, }; + const treemapOptions = /** @type {LH.Treemap.Options} */ (treemapOptionsTrimmed); const url = getAppsOrigin() + '/treemap/'; - ReportUIFeatures.openTabAndSendData(treemapOptions, url, windowName); + const windowName = `treemap-${json.requestedUrl}`; + + ReportUIFeatures.openTabWithUrlData(treemapOptions, url, windowName); + // if (method === 'postMessage') { + // ReportUIFeatures.openTabAndSendData(treemapOptions, url, windowName); + // } else { + // ReportUIFeatures.openTabWithUrlData(treemapOptions, url, windowName); + // } } /** @@ -591,6 +611,23 @@ class ReportUIFeatures { const popup = window.open(url, windowName); } + /** + * Opens a new tab to an external page and sends data via base64 encoded url params. + * @param {{lhr: LH.Result} | LH.Treemap.Options} data + * @param {string} url_ + * @param {string} windowName + * @protected + */ + static openTabWithUrlData(data, url_, windowName) { + const url = new URL(url_); + for (const [key, value] of Object.entries(data)) { + url.searchParams.set(key, btoa(JSON.stringify(value))); + } + + // The popup's window.name is keyed by version+url+fetchTime, so we reuse/select tabs correctly + window.open(url.toString(), windowName); + } + /** * Expands all audit `
`. * Ideally, a print stylesheet could take care of this, but CSS has no way to diff --git a/lighthouse-treemap/app/src/main.js b/lighthouse-treemap/app/src/main.js index a9ec0a51a685..063502fac12f 100644 --- a/lighthouse-treemap/app/src/main.js +++ b/lighthouse-treemap/app/src/main.js @@ -735,12 +735,21 @@ function showError(message) { } async function main() { + const params = new URLSearchParams(window.location.search); + if (window.__treemapOptions) { // Prefer the hardcoded options from a saved HTML file above all. init(window.__treemapOptions); - } else if (new URLSearchParams(window.location.search).has('debug')) { + } else if (params.has('debug')) { const response = await fetch('debug.json'); init(await response.json()); + } else if (params.has('lhr')) { + const base64Lhr = params.get('lhr') || ''; + const lhr = JSON.parse(atob(base64Lhr)); + const options = { + lhr, + }; + init(options); } else { window.addEventListener('message', e => { if (e.source !== self.opener) return; From 5d94d3e71cdd76931b1974ca31b5962c5068f858 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 18 May 2021 14:39:20 -0700 Subject: [PATCH 2/5] update --- .../report/html/renderer/report-ui-features.js | 15 ++++++++------- lighthouse-treemap/app/src/main.js | 15 ++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 4934d202e058..d2dfd25335b5 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -576,12 +576,11 @@ class ReportUIFeatures { const url = getAppsOrigin() + '/treemap/'; const windowName = `treemap-${json.requestedUrl}`; - ReportUIFeatures.openTabWithUrlData(treemapOptions, url, windowName); - // if (method === 'postMessage') { - // ReportUIFeatures.openTabAndSendData(treemapOptions, url, windowName); - // } else { - // ReportUIFeatures.openTabWithUrlData(treemapOptions, url, windowName); - // } + if (method === 'postMessage') { + ReportUIFeatures.openTabAndSendData(treemapOptions, url, windowName); + } else { + ReportUIFeatures.openTabWithUrlData(treemapOptions, url, windowName); + } } /** @@ -620,9 +619,11 @@ class ReportUIFeatures { */ static openTabWithUrlData(data, url_, windowName) { const url = new URL(url_); + const params = new URLSearchParams(); for (const [key, value] of Object.entries(data)) { - url.searchParams.set(key, btoa(JSON.stringify(value))); + params.set(key, btoa(JSON.stringify(value))); } + url.hash = params.toString(); // The popup's window.name is keyed by version+url+fetchTime, so we reuse/select tabs correctly window.open(url.toString(), windowName); diff --git a/lighthouse-treemap/app/src/main.js b/lighthouse-treemap/app/src/main.js index 063502fac12f..006911f73593 100644 --- a/lighthouse-treemap/app/src/main.js +++ b/lighthouse-treemap/app/src/main.js @@ -735,17 +735,22 @@ function showError(message) { } async function main() { - const params = new URLSearchParams(window.location.search); + /** @type {Record} */ + let params = {}; + if (Object.fromEntries) { + const queryParams = new URLSearchParams(window.location.search); + const hashParams = new URLSearchParams(location.hash.replace('#', '?')); + params = Object.fromEntries([...queryParams.entries(), ...hashParams.entries()]); + } if (window.__treemapOptions) { // Prefer the hardcoded options from a saved HTML file above all. init(window.__treemapOptions); - } else if (params.has('debug')) { + } else if ('debug' in params) { const response = await fetch('debug.json'); init(await response.json()); - } else if (params.has('lhr')) { - const base64Lhr = params.get('lhr') || ''; - const lhr = JSON.parse(atob(base64Lhr)); + } else if (params.lhr) { + const lhr = JSON.parse(atob(params.lhr)); const options = { lhr, }; From 193536f7e4736e136fdc5cfff563a45249421ae3 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 18 May 2021 18:17:12 -0700 Subject: [PATCH 3/5] pr --- .../html/renderer/report-ui-features.js | 23 +++++++++++------- lighthouse-treemap/app/src/main.js | 24 +++++++++++++++---- types/treemap.d.ts | 11 ++++++++- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index d2dfd25335b5..6dc6cf95a429 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -558,9 +558,8 @@ class ReportUIFeatures { throw new Error('no script treemap data found'); } - // Only send the minimum lhr needed for the treemap app. - /** @type {RecursivePartial} */ - const treemapOptionsTrimmed = { + /** @type {LH.Treemap.Options} */ + const treemapOptions = { lhr: { requestedUrl: json.requestedUrl, finalUrl: json.finalUrl, @@ -572,7 +571,6 @@ class ReportUIFeatures { }, }, }; - const treemapOptions = /** @type {LH.Treemap.Options} */ (treemapOptionsTrimmed); const url = getAppsOrigin() + '/treemap/'; const windowName = `treemap-${json.requestedUrl}`; @@ -619,14 +617,21 @@ class ReportUIFeatures { */ static openTabWithUrlData(data, url_, windowName) { const url = new URL(url_); - const params = new URLSearchParams(); - for (const [key, value] of Object.entries(data)) { - params.set(key, btoa(JSON.stringify(value))); - } - url.hash = params.toString(); + url.hash = toBinary(JSON.stringify(data)); // The popup's window.name is keyed by version+url+fetchTime, so we reuse/select tabs correctly window.open(url.toString(), windowName); + + /** + * @param {string} string + */ + function toBinary(string) { + const codeUnits = new Uint16Array(string.length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = string.charCodeAt(i); + } + return btoa(String.fromCharCode(...new Uint8Array(codeUnits.buffer))); + } } /** diff --git a/lighthouse-treemap/app/src/main.js b/lighthouse-treemap/app/src/main.js index 006911f73593..28329a2bc63b 100644 --- a/lighthouse-treemap/app/src/main.js +++ b/lighthouse-treemap/app/src/main.js @@ -734,13 +734,28 @@ function showError(message) { document.body.textContent = message; } +/** + * @param {string} encoded + */ +function fromBinary(encoded) { + const binary = atob(encoded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return String.fromCharCode(...new Uint16Array(bytes.buffer)); +} + async function main() { - /** @type {Record} */ + /** @type {Record} */ let params = {}; if (Object.fromEntries) { const queryParams = new URLSearchParams(window.location.search); - const hashParams = new URLSearchParams(location.hash.replace('#', '?')); - params = Object.fromEntries([...queryParams.entries(), ...hashParams.entries()]); + const hashParams = location.hash ? JSON.parse(fromBinary(location.hash.substr(1))) : {}; + params = { + ...Object.fromEntries(queryParams.entries()), + ...hashParams, + }; } if (window.__treemapOptions) { @@ -750,9 +765,8 @@ async function main() { const response = await fetch('debug.json'); init(await response.json()); } else if (params.lhr) { - const lhr = JSON.parse(atob(params.lhr)); const options = { - lhr, + lhr: params.lhr, }; init(options); } else { diff --git a/types/treemap.d.ts b/types/treemap.d.ts index e1ec262512a0..09880bd13652 100644 --- a/types/treemap.d.ts +++ b/types/treemap.d.ts @@ -7,7 +7,16 @@ declare global { module LH.Treemap { interface Options { - lhr: LH.Result; + lhr: { + requestedUrl: string; + finalUrl: string; + audits: { + 'script-treemap-data': LH.Audit.Result; + }; + configSettings: { + locale: LH.Locale; + } + } } type NodePath = string[]; From 21d7f864ff406a41974ec4f5715deadc7e952f8c Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Wed, 19 May 2021 12:18:57 -0700 Subject: [PATCH 4/5] update --- lighthouse-core/report/html/renderer/report-ui-features.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 6dc6cf95a429..9debb07f5844 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -153,8 +153,7 @@ class ReportUIFeatures { const showTreemapApp = this.json.audits['script-treemap-data'] && this.json.audits['script-treemap-data'].details; - // TODO: need window.opener to work in DevTools. - if (showTreemapApp && !this._dom.isDevTools()) { + if (showTreemapApp) { this.addButton({ text: Util.i18n.strings.viewTreemapLabel, icon: 'treemap', From 3f1e77eafa8ac7cc6b8621f1ae355e68d6d02da0 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Wed, 19 May 2021 13:12:24 -0700 Subject: [PATCH 5/5] report: add buttons to .lh-buttons flex container --- .../report/html/renderer/report-ui-features.js | 8 +++++--- lighthouse-core/report/html/report-styles.css | 12 ++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 9debb07f5844..4559298db1e4 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -188,6 +188,9 @@ class ReportUIFeatures { // Not supported without metrics group. if (!metricsEl) return; + let buttonsEl = metricsEl.querySelector('.lh-buttons'); + if (!buttonsEl) buttonsEl = this._dom.createChildOf(metricsEl, 'div', 'lh-buttons'); + const classes = [ 'lh-button', ]; @@ -195,10 +198,9 @@ class ReportUIFeatures { classes.push('report-icon'); classes.push(`report-icon--${opts.icon}`); } - const buttonEl = this._dom.createChildOf(metricsEl, 'button', classes.join(' ')); - buttonEl.addEventListener('click', opts.onClick); + const buttonEl = this._dom.createChildOf(buttonsEl, 'button', classes.join(' ')); buttonEl.textContent = opts.text; - metricsEl.append(buttonEl); + buttonEl.addEventListener('click', opts.onClick); return buttonEl; } diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css index b14342a83222..2fbc048fa4d6 100644 --- a/lighthouse-core/report/html/report-styles.css +++ b/lighthouse-core/report/html/report-styles.css @@ -399,12 +399,8 @@ display: flex; align-items: center; padding: 10px 12px; - opacity: 0.7; cursor: pointer; } -.report-icon:hover { - opacity: 1; -} .report-icon[disabled] { opacity: 0.3; pointer-events: none; @@ -416,6 +412,10 @@ background-repeat: no-repeat; width: var(--report-icon-size); height: var(--report-icon-size); + opacity: 0.7; +} +.report-icon:hover::before { + opacity: 1; } .dark .report-icon::before { filter: invert(1); @@ -439,6 +439,10 @@ background-image: url('data:image/svg+xml;utf8,'); } +.lh-buttons { + display: flex; + flex-wrap: wrap; +} .lh-button { margin: 10px; height: 30px;