-
Notifications
You must be signed in to change notification settings - Fork 9.5k
report(third party): filter out third party urls #6351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 34 commits
28cc931
d99057e
9885704
0c7d01b
9dd3047
bc9b131
7e23d46
1dac41b
52253b3
350bf5d
787f649
6ccbc88
32f0832
745b4cf
be2627e
60c1081
7a17378
fa071b1
274bc47
7d4cb53
be6d8fb
2149757
e20dae9
bfb3b56
960139f
78403fc
f9605d4
4b117b1
8b3b814
3823b51
bf66efe
1888642
9c018f8
045eb9d
9d17141
f2d8fb5
9a3f617
a410185
2380a04
34f1790
d8c7ba0
75c6ef5
5b8e948
87d5e14
b52ab91
3ff478e
341bd5d
99b0b13
c9a1b72
2b90b66
a29cd61
a99422a
56e5551
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,20 +11,14 @@ | |
|
||
/* global self */ | ||
|
||
/** @typedef {import('url').URL} OriginalURL */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brendankenny wasn't sure how to fix this but |
||
|
||
const Util = require('../report/html/renderer/util.js'); | ||
|
||
// Type cast so tsc sees window.URL and require('url').URL as sufficiently equivalent. | ||
const URL = /** @type {!Window["URL"]} */ (typeof self !== 'undefined' && self.URL) || | ||
require('url').URL; | ||
|
||
// 25 most used tld plus one domains (aka public suffixes) from http archive. | ||
// @see https://github.com/GoogleChrome/lighthouse/pull/5065#discussion_r191926212 | ||
// The canonical list is https://publicsuffix.org/learn/ but we're only using subset to conserve bytes | ||
const listOfTlds = [ | ||
'com', 'co', 'gov', 'edu', 'ac', 'org', 'go', 'gob', 'or', 'net', 'in', 'ne', 'nic', 'gouv', | ||
'web', 'spb', 'blog', 'jus', 'kiev', 'mil', 'wi', 'qc', 'ca', 'bel', 'on', | ||
]; | ||
|
||
const allowedProtocols = [ | ||
'https:', 'http:', 'chrome:', 'chrome-extension:', | ||
]; | ||
|
@@ -106,27 +100,30 @@ class URLShim extends URL { | |
* @return {string} tld | ||
*/ | ||
static getTld(hostname) { | ||
const tlds = hostname.split('.').slice(-2); | ||
|
||
if (!listOfTlds.includes(tlds[0])) { | ||
return `.${tlds[tlds.length - 1]}`; | ||
} | ||
return Util.getTld(hostname); | ||
} | ||
|
||
return `.${tlds.join('.')}`; | ||
/** | ||
* Returns a primary domain for provided hostname (e.g. www.example.com -> example.com). | ||
* @param {string|OriginalURL} url hostname or URL object | ||
* @returns {string} | ||
*/ | ||
static getRootDomain(url) { | ||
return Util.getRootDomain(url); | ||
} | ||
|
||
/** | ||
* Check if rootDomains matches | ||
* | ||
* @param {string} urlA | ||
* @param {string} urlB | ||
* @param {string|OriginalURL} urlA | ||
* @param {string|OriginalURL} urlB | ||
*/ | ||
static rootDomainsMatch(urlA, urlB) { | ||
let urlAInfo; | ||
let urlBInfo; | ||
try { | ||
urlAInfo = new URL(urlA); | ||
urlBInfo = new URL(urlB); | ||
urlAInfo = Util.createOrReturnURL(urlA); | ||
urlBInfo = Util.createOrReturnURL(urlB); | ||
} catch (err) { | ||
return false; | ||
} | ||
|
@@ -135,14 +132,9 @@ class URLShim extends URL { | |
return false; | ||
} | ||
|
||
const tldA = URLShim.getTld(urlAInfo.hostname); | ||
const tldB = URLShim.getTld(urlBInfo.hostname); | ||
|
||
// get the string before the tld | ||
const urlARootDomain = urlAInfo.hostname.replace(new RegExp(`${tldA}$`), '') | ||
.split('.').splice(-1)[0]; | ||
const urlBRootDomain = urlBInfo.hostname.replace(new RegExp(`${tldB}$`), '') | ||
.split('.').splice(-1)[0]; | ||
const urlARootDomain = URLShim.getRootDomain(urlAInfo); | ||
const urlBRootDomain = URLShim.getRootDomain(urlBInfo); | ||
|
||
return urlARootDomain === urlBRootDomain; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,15 +16,24 @@ | |
*/ | ||
'use strict'; | ||
|
||
/* eslint-env browser */ | ||
|
||
/** | ||
* @fileoverview Adds export button, print, and other dynamic functionality to | ||
* the report. | ||
*/ | ||
|
||
/* globals self URL Blob CustomEvent getFilenamePrefix window */ | ||
/* globals self URL Blob CustomEvent getFilenamePrefix Util window */ | ||
|
||
/** @typedef {import('./dom.js')} DOM */ | ||
|
||
/** | ||
* @param {HTMLTableElement} tableEl | ||
* @return {Array<HTMLTableRowElement>} | ||
*/ | ||
const getTableRows = tableEl => /** @type {Array<HTMLTableRowElement>} */ | ||
(Array.from(tableEl.tBodies[0].children)); | ||
wardpeet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
class ReportUIFeatures { | ||
/** | ||
* @param {DOM} dom | ||
|
@@ -86,6 +95,7 @@ class ReportUIFeatures { | |
this.json = report; | ||
this._setupMediaQueryListeners(); | ||
this._setupExportButton(); | ||
this._setupThirdPartyFilter(); | ||
this._setUpCollapseDetailsAfterPrinting(); | ||
this._setupHeaderAnimation(); | ||
this._resetUIState(); | ||
|
@@ -128,6 +138,84 @@ class ReportUIFeatures { | |
dropdown.addEventListener('click', this.onExport); | ||
} | ||
|
||
_setupThirdPartyFilter() { | ||
const thirdPartyFilterAuditExclusions = ['uses-rel-preconnect']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need a comment for what this is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That? That's what I'm getting just from the name - which makes me think a comment isn't needed. @wardpeet is it more nuanced than that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how bout There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine @brendankenny wanted the why in a comment. i.e. this audit is explicitly recommending to the user only third party origins that you should be connecting to so filtering out third parties doesn't make sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brendankenny all good? |
||
|
||
// get all tables with a text url | ||
/** @type {Array<HTMLTableElement>} */ | ||
const tables = Array.from(this._document.querySelectorAll('.lh-table')); | ||
const tablesWithUrls = tables | ||
.filter(el => el.querySelector('td.lh-table-column--url')) | ||
.filter(el => !thirdPartyFilterAuditExclusions.includes( | ||
/** @type {Element} */(el.closest('.lh-audit')).id)); | ||
|
||
tablesWithUrls.forEach((tableEl, index) => { | ||
const thirdPartyRows = this._getThirdPartyRows(tableEl, this.json.finalUrl); | ||
// no 3rd parties, no checkbox! | ||
if (!thirdPartyRows.size) { | ||
return; | ||
} | ||
// create input box | ||
const filterTemplate = this._dom.cloneTemplate('#tmpl-lh-3p-filter', this._document); | ||
const filterInput = /** @type {HTMLInputElement} */ (filterTemplate.querySelector('input')); | ||
const id = `lh-3p-filter-label--${index}`; | ||
|
||
filterInput.setAttribute('id', id); | ||
filterInput.addEventListener('change', e => { | ||
// remove elements from the dom and keep track of them to readd on uncheck | ||
// why removing instead of hiding? nth-child(even) background-colors keep working | ||
if (e.target instanceof HTMLInputElement && e.target.checked) { | ||
for (const [position, row] of thirdPartyRows.entries()) { | ||
const childrenArr = getTableRows(tableEl); | ||
tableEl.tBodies[0].insertBefore(row, childrenArr[position]); | ||
} | ||
} else { | ||
for (const position of thirdPartyRows.keys()) { | ||
const row = /** @type {HTMLTableRowElement} */ (thirdPartyRows.get(position)); | ||
row.remove(); | ||
} | ||
} | ||
}); | ||
|
||
if (tableEl.parentNode) { | ||
/** @type {Element} */ (filterTemplate.querySelector('label')).setAttribute('for', id); | ||
/** @type {Element} */ (filterTemplate.querySelector('.lh-3p-filter-count')).textContent = | ||
`${thirdPartyRows.size}`; | ||
/** @type {Element} */ (filterTemplate.querySelector('.lh-3p-ui-string')).textContent = | ||
`${Util.UIStrings.thirdPartyResourcesLabel}`; | ||
// Finally, add checkbox to the DOM | ||
tableEl.parentNode.insertBefore(filterTemplate, tableEl); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* @param {HTMLTableElement} el | ||
* @param {string} finalUrl | ||
* @return {Map<number, HTMLTableRowElement>} | ||
*/ | ||
_getThirdPartyRows(el, finalUrl) { | ||
/** @type {NodeListOf<HTMLElement>} */ | ||
const urlItems = el.querySelectorAll('.lh-text__url'); | ||
const rootDomain = Util.getRootDomain(finalUrl); | ||
/** @type {Map<number, HTMLTableRowElement>} */ | ||
const thirdPartyRows = new Map(); | ||
for (const urlItem of urlItems) { | ||
const isThirdParty = !(urlItem.dataset.url || '').includes(`${rootDomain}/`); | ||
wardpeet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!isThirdParty) { | ||
continue; | ||
} | ||
|
||
const urlRowEl = urlItem.closest('tr'); | ||
if (urlRowEl) { | ||
const rowPosition = getTableRows(el).indexOf(urlRowEl); | ||
thirdPartyRows.set(rowPosition, urlRowEl); | ||
} | ||
} | ||
|
||
return thirdPartyRows; | ||
} | ||
|
||
_setupHeaderAnimation() { | ||
const scoresWrapper = this._dom.find('.lh-scores-wrapper', this._document); | ||
const computedMarginTop = window.getComputedStyle(scoresWrapper).marginTop; | ||
|
Uh oh!
There was an error while loading. Please reload this page.