Repository: GoogleChrome/lighthouse
Pull request #9791 (2019-10-05 20:51:43)

tests(build): use firehouse smoke test runner to test bundle
This patch

1) provides an interface (`firehouse`) to run smoke tests with options providing a hook for `runLighthouse` and ways to modify the expectations. This has already been done internally to test Lightrider - this is just polishing that work and bringing it home.
2) uses `firehouse` to run a few smoke tests on a nifty patched CLI that uses a bundled `lighthouse-core`.

I have a followup patch for building a standalone `firehouse` and rolling it to DevTools, with a layout test that uses `firehouse` with properly filtered expectations. It'll be a local test at first (run only during release procedures), but eventually it'll be used by whatever process we come up with to integration test our DevTools channel.

Related:

#9501 (and https://github.com/GoogleChrome/lighthouse/pull/9501#issuecomment-518756453)

Need to land bundle tests before:

#9605 (changes to bundling script)
#9193 (removes only tests that uses bundled code)

brendankenny: ha, I was working on a similar change, though it's kind of the inverse of this, which allows unification of some stuff. Maybe we can combine powers. 

Patch:
diff --git a/.travis.yml b/.travis.yml
index 69ce7695c..be7f2b718 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,6 +38,7 @@ script:
   - yarn test-clients
   - yarn test-viewer
   - yarn test-lantern
+  - yarn test-bundle
   - yarn i18n:checks
   - yarn dogfood-lhci
 before_cache:
diff --git a/docs/releasing.md b/docs/releasing.md
index 51f42e2c9..8c89f0d58 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -60,7 +60,7 @@ and that no new PRs should be merged until you are done.
 ```sh
 # Run the tests.
 bash ./lighthouse-core/scripts/release/test.sh
-# Change into the pristine folder.
+# Change into the newly-created pristine folder.
 cd ../lighthouse-pristine
 ```
 
@@ -85,7 +85,7 @@ yarn --cwd ~/chromium/src/third_party/blink/renderer/devtools test 'http/tests/d
 git diff
 
 # Verify that the Audits panel still works. Consider the new features that have been added.
-# If anything is wrong, stop releasing, investigate, and prioritize landing the PR.
+# If anything is wrong, stop releasing, investigate, land a fix and start over.
 
 # For bonus points, add some tests covering new features. Either a new test, or an extra
 # assertion in an existing test.
@@ -105,7 +105,7 @@ Confirm Lightrider integration will work:
 
 # Verify that Lightrider works properly, and is generating reports fully. Consider the new features that have been added.
 # Note: if the changes include proto changes make sure that the API has those new fields.
-# If anything is wrong, stop releasing, investigate, and prioritize landing the PR.
+# If anything is wrong, stop releasing, investigate, land a fix and start over.
 
 # For bonus points, add some tests covering new features. Either a new test, or an extra
 # assertion in an existing test.
@@ -114,10 +114,6 @@ Confirm Lightrider integration will work:
 Now that the integrations are confirmed to work, go back to `lighthouse` folder.
 
 ```sh
-# Run the tests again.
-# Note: you can skip this if you didn't need to land any changes.
-bash ./lighthouse-core/scripts/release/test.sh
-
 # Prepare the commit, replace x.x.x with the desired version
 bash ./lighthouse-core/scripts/release/prepare-commit.sh x.x.x
 
diff --git a/lighthouse-core/audits/critical-request-chains.js b/lighthouse-core/audits/critical-request-chains.js
index ebb7ba2e2..02d8ea384 100644
--- a/lighthouse-core/audits/critical-request-chains.js
+++ b/lighthouse-core/audits/critical-request-chains.js
@@ -11,7 +11,7 @@ const ComputedChains = require('../computed/critical-request-chains.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to reduce the depth of critical network requests to enhance initial load of a page. Critical request chains are series of dependent network requests that are important for page rendering. For example, here's a 4-request-deep chain: The biglogo.jpg image is required, but is requested via the styles.css style code, which is requested by the initialize.js javascript, which is requested by the page's HTML. This is displayed in a list of audit titles that Lighthouse generates. */
-  title: 'Minimize Critical Requests Depth',
+  title: 'Avoid chaining critical requests',
   /** Description of a Lighthouse audit that tells the user *why* they should reduce the depth of critical network requests to enhance initial load of a page . This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'The Critical Request Chains below show you what resources are ' +
       'loaded with a high priority. Consider reducing ' +
diff --git a/lighthouse-core/audits/seo/link-text.js b/lighthouse-core/audits/seo/link-text.js
index ddae4abca..0eac23289 100644
--- a/lighthouse-core/audits/seo/link-text.js
+++ b/lighthouse-core/audits/seo/link-text.js
@@ -53,6 +53,16 @@ const BLOCKLIST = new Set([
   'mais informações',
   'mais',
   'veja mais',
+  // Korean
+  '여기',
+  '여기를 클릭',
+  '클릭',
+  '링크',
+  '자세히',
+  '자세히 보기',
+  '계속',
+  '이동',
+  '전체 보기',
 ]);
 const i18n = require('../../lib/i18n/i18n.js');
 
diff --git a/lighthouse-core/audits/third-party-summary.js b/lighthouse-core/audits/third-party-summary.js
index 53e7a4f3c..23793edda 100644
--- a/lighthouse-core/audits/third-party-summary.js
+++ b/lighthouse-core/audits/third-party-summary.js
@@ -15,7 +15,7 @@ const MainThreadTasks = require('../computed/main-thread-tasks.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides details about the code on a web page that the user doesn't control (referred to as "third-party code"). This descriptive title is shown to users when the amount is acceptable and no user action is required. */
-  title: 'Third-Party usage',
+  title: 'Minimize third-party usage',
   /** Title of a diagnostic audit that provides details about the code on a web page that the user doesn't control (referred to as "third-party code"). This imperative title is shown to users when there is a significant amount of page execution time caused by third-party code that should be reduced. */
   failureTitle: 'Reduce the impact of third-party code',
   /** Description of a Lighthouse audit that identifies the code on the page that the user doesn't control. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
diff --git a/lighthouse-core/gather/gatherers/image-elements.js b/lighthouse-core/gather/gatherers/image-elements.js
index 77b7e566d..f4923e7f5 100644
--- a/lighthouse-core/gather/gatherers/image-elements.js
+++ b/lighthouse-core/gather/gatherers/image-elements.js
@@ -130,6 +130,12 @@ function determineNaturalSize(url) {
 }
 
 class ImageElements extends Gatherer {
+  constructor() {
+    super();
+    /** @type {Map<string, {naturalWidth: number, naturalHeight: number}>} */
+    this._naturalSizeCache = new Map();
+  }
+
   /**
    * @param {Driver} driver
    * @param {LH.Artifacts.ImageElement} element
@@ -137,11 +143,16 @@ class ImageElements extends Gatherer {
    */
   async fetchElementWithSizeInformation(driver, element) {
     const url = JSON.stringify(element.src);
+    if (this._naturalSizeCache.has(url)) {
+      return Object.assign(element, this._naturalSizeCache.get(url));
+    }
+
     try {
       // We don't want this to take forever, 250ms should be enough for images that are cached
       driver.setNextProtocolTimeout(250);
       /** @type {{naturalWidth: number, naturalHeight: number}} */
       const size = await driver.evaluateAsync(`(${determineNaturalSize.toString()})(${url})`);
+      this._naturalSizeCache.set(url, size);
       return Object.assign(element, size);
     } catch (_) {
       // determineNaturalSize fails on invalid images, which we treat as non-visible
diff --git a/lighthouse-core/lib/asset-saver.js b/lighthouse-core/lib/asset-saver.js
index 5490bcfae..677e61156 100644
--- a/lighthouse-core/lib/asset-saver.js
+++ b/lighthouse-core/lib/asset-saver.js
@@ -91,7 +91,7 @@ function stringifyReplacer(key, value) {
 }
 
 /**
- * Save artifacts object mostly to single file located at basePath/artifacts.log.
+ * Save artifacts object mostly to single file located at basePath/artifacts.json.
  * Also save the traces & devtoolsLogs to their own files
  * @param {LH.Artifacts} artifacts
  * @param {string} basePath
diff --git a/lighthouse-core/lib/i18n/README.md b/lighthouse-core/lib/i18n/README.md
index 35c61af0a..8ec3779d9 100644
--- a/lighthouse-core/lib/i18n/README.md
+++ b/lighthouse-core/lib/i18n/README.md
@@ -1,6 +1,6 @@
 # Terminology
 
-* **CTC format**: The [Chrome extension & Chrome app i18n format](https://developer.chrome.com/extensions/i18n-messages). JSON with their specified model for declaring placeholders, examples, etc. Used as an interchange data format.
+* **CTC format**: The [Chrome extension & Chrome app i18n format](https://developer.chrome.com/extensions/i18n-messages) with some minor changes. JSON with their specified model for declaring placeholders, examples, etc. Used as an interchange data format.
 * **LHL syntax** (Lighthouse Localizable syntax): The ICU-friendly string syntax that is used to author `UIStrings` and is seen in the locale files in `i18n/locales/*.json`. Lighthouse has a custom syntax these strings combines many ICU message features along with some markdown.
 * **ICU**: ICU (International Components for Unicode) is a localization project and standard defined by the Unicode consortium. In general, we refer to "ICU" as the [ICU message formatting](http://userguide.icu-project.org/formatparse/messages) syntax.
 
@@ -195,13 +195,12 @@ CTC is a name that is distinct and identifies this as the Chrome translation for
 
 ### Parts of a CTC message
 
-(From the Chrome Docs)
-
 ```json
 {
   "name": {
     "message": "Message text, with optional placeholders.",
     "description": "Translator-aimed description of the message.",
+    "meaning": "Description given when a message is duplicated, in order to give context to the message. Lighthouse uses a copy of the description for this.",
     "placeholders": {
       "placeholder_name": {
         "content": "A string to be placed within the message.",
diff --git a/lighthouse-core/lib/i18n/locales/ca.json b/lighthouse-core/lib/i18n/locales/ca.json
index da2b75cbe..1d374dc80 100644
--- a/lighthouse-core/lib/i18n/locales/ca.json
+++ b/lighthouse-core/lib/i18n/locales/ca.json
@@ -918,7 +918,7 @@
     "message": "El {decimalProportion, number, extendedPercent} del text és llegible"
   },
   "lighthouse-core/audits/seo/font-size.js | explanationViewport": {
-    "message": "El text és il·legible perquè no hi ha cap metaetiqueta de finestra gràfica optimitzada per a pantalles de mòbil."
+    "message": "El text és il·legible perquè no hi ha cap metaetiqueta de finestra gràfica optimitzada per a pantalles de dispositius mòbils."
   },
   "lighthouse-core/audits/seo/font-size.js | explanationWithDisclaimer": {
     "message": "El {decimalProportion, number, extendedPercent} del text és massa petit (d'acord amb una mostra del {decimalProportionVisited, number, extendedPercent})."
@@ -999,7 +999,7 @@
     "message": "Si el format del fitxer robots.txt no és correcte, és possible que els rastrejadors no puguin entendre com vols que rastregin o indexin el lloc web. [Obtén més informació](https://web.dev/robots-txt)."
   },
   "lighthouse-core/audits/seo/robots-txt.js | displayValueHttpBadCode": {
-    "message": "La sol·licitud del fitxer robots.txt ha tornat l'estat HTTP: {statusCode}"
+    "message": "La sol·licitud del fitxer robots.txt ha tornat l'estat HTTP següent: {statusCode}"
   },
   "lighthouse-core/audits/seo/robots-txt.js | displayValueValidationError": {
     "message": "{itemCount,plural, =1{S'ha trobat 1 error}other{S'han trobat # errors}}"
diff --git a/lighthouse-core/lib/i18n/locales/da.json b/lighthouse-core/lib/i18n/locales/da.json
index f015c0c84..a053e6cd8 100644
--- a/lighthouse-core/lib/i18n/locales/da.json
+++ b/lighthouse-core/lib/i18n/locales/da.json
@@ -759,7 +759,7 @@
     "message": "Formindsker primært trådarbejde"
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | description": {
-    "message": "Websites bør fungere på alle de mest populære browsere, så de når ud til flest mulige brugere. [Få flere oplysninger](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
+    "message": "Websites bør fungere på alle populære browsere, så de når ud til flest mulige brugere. [Få flere oplysninger](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | title": {
     "message": "Websitet fungerer i forskellige browsere"
diff --git a/lighthouse-core/lib/i18n/locales/de.json b/lighthouse-core/lib/i18n/locales/de.json
index 51fb43eb0..2493fd35b 100644
--- a/lighthouse-core/lib/i18n/locales/de.json
+++ b/lighthouse-core/lib/i18n/locales/de.json
@@ -702,10 +702,10 @@
     "message": "Zeigt Bilder mit einem korrekten Seitenverhältnis an"
   },
   "lighthouse-core/audits/image-aspect-ratio.js | warningCompute": {
-    "message": "Ungültige Informationen zur Bildgröße {url}"
+    "message": "Ungültige Informationen zur Bildgröße: {url}"
   },
   "lighthouse-core/audits/installable-manifest.js | description": {
-    "message": "Browser können Nutzer direkt dazu auffordern, Ihre Web-App zum Startbildschirm hinzuzufügen. Das kann zu mehr Interaktionen führen. [Weitere Informationen.](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)"
+    "message": "Browser können Nutzer direkt dazu auffordern, Ihre Web-App zum Startbildschirm hinzuzufügen. Das kann zu mehr Engagement führen. [Weitere Informationen.](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)"
   },
   "lighthouse-core/audits/installable-manifest.js | failureTitle": {
     "message": "Das Manifest der Web-App erfüllt die Anforderungen an die Installierbarkeit nicht"
@@ -1071,7 +1071,7 @@
     "message": "Legt keine Designfarbe für die Adressleiste fest."
   },
   "lighthouse-core/audits/themed-omnibox.js | title": {
-    "message": "Legt die Designfarbe der Adressleiste fest."
+    "message": "Legt eine Designfarbe für die Adressleiste fest."
   },
   "lighthouse-core/audits/third-party-summary.js | columnBlockingTime": {
     "message": "Dauer der Blockierung des Hauptthreads"
diff --git a/lighthouse-core/lib/i18n/locales/en-US.json b/lighthouse-core/lib/i18n/locales/en-US.json
index 0d21d4a2e..b5380e702 100644
--- a/lighthouse-core/lib/i18n/locales/en-US.json
+++ b/lighthouse-core/lib/i18n/locales/en-US.json
@@ -459,7 +459,7 @@
     "message": "{itemCount, plural,\n    =1 {1 chain found}\n    other {# chains found}\n    }"
   },
   "lighthouse-core/audits/critical-request-chains.js | title": {
-    "message": "Minimize Critical Requests Depth"
+    "message": "Avoid chaining critical requests"
   },
   "lighthouse-core/audits/deprecations.js | columnDeprecate": {
     "message": "Deprecation / Warning"
@@ -1089,7 +1089,7 @@
     "message": "Reduce the impact of third-party code"
   },
   "lighthouse-core/audits/third-party-summary.js | title": {
-    "message": "Third-Party usage"
+    "message": "Minimize third-party usage"
   },
   "lighthouse-core/audits/time-to-first-byte.js | description": {
     "message": "Time To First Byte identifies the time at which your server sends a response. [Learn more](https://web.dev/time-to-first-byte)."
diff --git a/lighthouse-core/lib/i18n/locales/en-XL.json b/lighthouse-core/lib/i18n/locales/en-XL.json
index 0ac673eda..a87d2101f 100644
--- a/lighthouse-core/lib/i18n/locales/en-XL.json
+++ b/lighthouse-core/lib/i18n/locales/en-XL.json
@@ -459,7 +459,7 @@
     "message": "{itemCount, plural,\n    =1 {1 ĉh́âín̂ f́ôún̂d́}\n    other {# ĉh́âín̂ś f̂óûńd̂}\n    }"
   },
   "lighthouse-core/audits/critical-request-chains.js | title": {
-    "message": "M̂ín̂ím̂íẑé Ĉŕît́îćâĺ R̂éq̂úêśt̂ś D̂ép̂t́ĥ"
+    "message": "Âv́ôíd̂ ćĥáîńîńĝ ćr̂ít̂íĉál̂ ŕêq́ûéŝt́ŝ"
   },
   "lighthouse-core/audits/deprecations.js | columnDeprecate": {
     "message": "D̂ép̂ŕêćât́îón̂ / Ẃâŕn̂ín̂ǵ"
@@ -1089,7 +1089,7 @@
     "message": "R̂éd̂úĉé t̂h́ê ím̂ṕâćt̂ óf̂ t́ĥír̂d́-p̂ár̂t́ŷ ćôd́ê"
   },
   "lighthouse-core/audits/third-party-summary.js | title": {
-    "message": "T̂h́îŕd̂-Ṕâŕt̂ý ûśâǵê"
+    "message": "M̂ín̂ím̂íẑé t̂h́îŕd̂-ṕâŕt̂ý ûśâǵê"
   },
   "lighthouse-core/audits/time-to-first-byte.js | description": {
     "message": "T̂ím̂é T̂ó F̂ír̂śt̂ B́ŷt́ê íd̂én̂t́îf́îéŝ t́ĥé t̂ím̂é ât́ ŵh́îćĥ ýôúr̂ śêŕv̂ér̂ śêńd̂ś â ŕêśp̂ón̂śê. [Ĺêár̂ń m̂ór̂é](https://web.dev/time-to-first-byte)."
diff --git a/lighthouse-core/lib/i18n/locales/es-419.json b/lighthouse-core/lib/i18n/locales/es-419.json
index 74d081547..b64b49567 100644
--- a/lighthouse-core/lib/i18n/locales/es-419.json
+++ b/lighthouse-core/lib/i18n/locales/es-419.json
@@ -318,7 +318,7 @@
     "message": "Los elementos `<video>` contienen un elemento `<track>` con `[kind=\"description\"]`"
   },
   "lighthouse-core/audits/apple-touch-icon.js | description": {
-    "message": "Para que el aspecto en iOS sea perfecto cuando los usuarios agreguen una app web progresiva a la pantalla principal, define un atributo `apple-touch-icon`. El atributo debe apuntar a un archivo PNG cuadrado de 192 px (o 180 px) no transparente. [Obtén más información](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
+    "message": "Para que el aspecto en iOS sea perfecto cuando los usuarios agreguen una app web progresiva a la pantalla principal, define un atributo `apple-touch-icon`. El atributo debe apuntar a un archivo PNG cuadrado de 192 px (o 180 px) que no sea transparente. [Obtén más información](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
   },
   "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
     "message": "No proporciona un ícono `apple-touch-icon` válido"
@@ -441,16 +441,16 @@
     "message": "Publica imágenes con formatos de próxima generación"
   },
   "lighthouse-core/audits/content-width.js | description": {
-    "message": "Si el ancho del contenido de tu app no coincide con el de la vista del puerto, es posible que la app no esté optimizada para pantallas de dispositivos móviles. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport)."
+    "message": "Si el ancho del contenido de tu app no coincide con el del viewport, es posible que la app no esté optimizada para pantallas de dispositivos móviles. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport)."
   },
   "lighthouse-core/audits/content-width.js | explanation": {
-    "message": "El tamaño de la vista del puerto de {innerWidth} px no coincide con el tamaño de la ventana de {outerWidth} px."
+    "message": "El tamaño de viewport de {innerWidth} px no coincide con el tamaño de ventana de {outerWidth} px."
   },
   "lighthouse-core/audits/content-width.js | failureTitle": {
-    "message": "El contenido no tiene el tamaño correcto para la vista del puerto"
+    "message": "El contenido no tiene el tamaño correcto para el viewport"
   },
   "lighthouse-core/audits/content-width.js | title": {
-    "message": "El contenido tiene el tamaño correcto para la vista del puerto"
+    "message": "El contenido tiene el tamaño correcto para el viewport"
   },
   "lighthouse-core/audits/critical-request-chains.js | description": {
     "message": "Las cadenas de solicitudes críticas que se muestran a continuación indican qué recursos son de alta prioridad en la carga. Te recomendamos que reduzcas la longitud de las cadenas, disminuyas el tamaño de los recursos para la descarga o postergues la descarga de recursos innecesarios para mejorar la carga de la página. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains)."
@@ -666,7 +666,7 @@
     "message": "Descripción"
   },
   "lighthouse-core/audits/errors-in-console.js | description": {
-    "message": "Los errores registrados en la consola indican la existencia de problemas no resueltos. Es posible que se deban a fallos en solicitudes de red y otros problemas del navegador. [Obtén más información](https://web.dev/errors-in-console)."
+    "message": "Los errores registrados en la consola indican la existencia de problemas no resueltos. Es posible que se deban a problemas con solicitudes de red y otros relativos al navegador. [Obtén más información](https://web.dev/errors-in-console)."
   },
   "lighthouse-core/audits/errors-in-console.js | failureTitle": {
     "message": "Se registraron errores del navegador en la consola"
@@ -705,13 +705,13 @@
     "message": "La información sobre el tamaño de la imagen no es válida {url}"
   },
   "lighthouse-core/audits/installable-manifest.js | description": {
-    "message": "Los navegadores pueden solicitarles a los usuarios de forma proactiva que agreguen tu app a la pantalla principal, lo que puede conllevar un mayor compromiso. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
+    "message": "Los navegadores pueden solicitar a los usuarios proactivamente que agreguen tu app a la pantalla principal, lo que es posible que genere un mayor compromiso. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
   },
   "lighthouse-core/audits/installable-manifest.js | failureTitle": {
-    "message": "El manifiesto de la aplicación web no cumple los requisitos de instalación"
+    "message": "El manifiesto de la aplicación web no cumple los requisitos de aptitud para la instalación"
   },
   "lighthouse-core/audits/installable-manifest.js | title": {
-    "message": "El manifiesto de la aplicación web cumple los requisitos de instalación"
+    "message": "El manifiesto de la aplicación web cumple los requisitos de aptitud para la instalación"
   },
   "lighthouse-core/audits/is-on-https.js | columnInsecureURL": {
     "message": "URL no segura"
@@ -738,7 +738,7 @@
     "message": "Interactiva en una red móvil simulada a los {timeInMs, number, seconds} s"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
-    "message": "Tu página es demasiado lenta y no es interactiva en menos de 10 segundos. Para obtener información sobre cómo mejorarla, accede a la sección \"Rendimiento\" para ver las oportunidades y el diagnóstico."
+    "message": "Tu página tarda mucho en cargarse y no es interactiva en menos de 10 segundos. Para obtener información sobre cómo mejorarla, accede a la sección Rendimiento, donde podrás consultar las oportunidades disponibles y el diagnóstico correspondiente."
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "La página no se carga suficientemente rápido en las redes móviles"
@@ -759,7 +759,7 @@
     "message": "Minimiza el trabajo del hilo principal"
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | description": {
-    "message": "Para alcanzar la mayor cantidad de usuarios, los sitios deben poder funcionar en todos los navegadores principales. [Obtén más información](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
+    "message": "Para alcanzar la mayor cantidad de usuarios, los sitios tienen que funcionar en todos los navegadores principales. [Obtén más información](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | title": {
     "message": "El sitio funciona en diferentes navegadores"
@@ -771,7 +771,7 @@
     "message": "Cada página tiene una URL"
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | description": {
-    "message": "Una clave del rendimiento percibido es la agilidad de las transiciones cuando presionas diferentes teclas, incluso con conexiones de red lentas. [Obtén más información](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
+    "message": "Una clave del rendimiento percibido es la agilidad de las transiciones cuando presionas en diferentes lugares, incluso con conexiones de red lentas. [Obtén más información](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | title": {
     "message": "No parece que se bloqueen las transiciones de la página en la red"
@@ -837,7 +837,7 @@
     "message": "Latencias de backend del servidor"
   },
   "lighthouse-core/audits/offline-start-url.js | description": {
-    "message": "Un service worker permite que tu aplicación web sea confiable en condiciones de red impredecibles. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+    "message": "Usar un service worker permite que tu aplicación web sea confiable en condiciones de red impredecibles. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
   },
   "lighthouse-core/audits/offline-start-url.js | failureTitle": {
     "message": "`start_url` no responde con un código de estado HTTP 200 cuando no hay conexión"
@@ -846,7 +846,7 @@
     "message": "`start_url` responde con un código de estado HTTP 200 cuando no hay conexión"
   },
   "lighthouse-core/audits/offline-start-url.js | warningCantStart": {
-    "message": "Lighthouse no pudo leer el atributo `start_url` del manifiesto. Como resultado, se consideró que el atributo `start_url` fuera la URL del documento. Mensaje de error: \"{manifestWarning}\"."
+    "message": "Lighthouse no pudo leer el atributo `start_url` del manifiesto. Por lo tanto, se tomó como `start_url` la URL del documento. Mensaje de error: \"{manifestWarning}\"."
   },
   "lighthouse-core/audits/performance-budget.js | columnOverBudget": {
     "message": "Superior a la estimación"
@@ -861,7 +861,7 @@
     "message": "Estimación de rendimiento"
   },
   "lighthouse-core/audits/redirects-http.js | description": {
-    "message": "Si ya configuraste el protocolo HTTPS, asegúrate de redireccionar todo el tráfico de HTTP a HTTPS para que todos los usuarios aprovechen las características de protección de la Web. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)."
+    "message": "Si ya configuraste el protocolo HTTPS, asegúrate de redireccionar el tráfico de HTTP a HTTPS a fin de habilitar las características de protección de la Web para todos los usuarios. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)."
   },
   "lighthouse-core/audits/redirects-http.js | failureTitle": {
     "message": "No redirecciona el tráfico HTTP a HTTPS"
@@ -1035,28 +1035,28 @@
     "message": "El tamaño de los elementos táctiles es el adecuado"
   },
   "lighthouse-core/audits/service-worker.js | description": {
-    "message": "El service worker es la tecnología que te permite usar las funciones de las apps web progresivas, como el modo sin conexión, agregarlas a la pantalla principal y las notificaciones push. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
+    "message": "El service worker es la tecnología que permite que tu app use varias funciones de las apps web progresivas, como el modo sin conexión, el agregado a la pantalla principal y las notificaciones push. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
   },
   "lighthouse-core/audits/service-worker.js | explanationBadManifest": {
-    "message": "Un service worker controla esta página, pero no se encontró ningún atributo `start_url` porque el archivo de manifiesto no es un JSON válido"
+    "message": "Un service worker controla esta página, pero no se encontró ningún atributo `start_url` porque no se pudo analizar el archivo de manifiesto como un JSON válido"
   },
   "lighthouse-core/audits/service-worker.js | explanationBadStartUrl": {
-    "message": "Un service worker controla esta página, pero el atributo `start_url` ({startUrl}) está fuera del rango del service worker ({scopeUrl})"
+    "message": "Un service worker controla esta página, pero el atributo `start_url` ({startUrl}) está fuera del alcance del service worker ({scopeUrl})"
   },
   "lighthouse-core/audits/service-worker.js | explanationNoManifest": {
-    "message": "Un service worker controla esta página, pero no se encontró el atributo `start_url` porque no se recuperó ningún manifiesto."
+    "message": "Un service worker controla esta página, pero no se encontró el atributo `start_url` porque no se obtuvo ningún manifiesto."
   },
   "lighthouse-core/audits/service-worker.js | explanationOutOfScope": {
-    "message": "Este origen tiene al menos un service worker, pero la página ({pageUrl}) no está dentro del rango."
+    "message": "Este origen tiene al menos un service worker, pero la página ({pageUrl}) no está dentro del alcance."
   },
   "lighthouse-core/audits/service-worker.js | failureTitle": {
     "message": "No registra un service worker que controle la página y `start_url`"
   },
   "lighthouse-core/audits/service-worker.js | title": {
-    "message": "Registra a un service worker que controle la página y `start_url`"
+    "message": "Registra un service worker que controle la página y `start_url`"
   },
   "lighthouse-core/audits/splash-screen.js | description": {
-    "message": "Una pantalla de presentación personalizada asegura que la experiencia de los usuarios sea de calidad cuando ejecuten tu app desde sus pantallas principales. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
+    "message": "El uso de una pantalla de presentación con un tema asegura que los usuarios tengan una experiencia de calidad al ejecutar tu app desde sus pantallas principales. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
   },
   "lighthouse-core/audits/splash-screen.js | failureTitle": {
     "message": "No se configuró para una pantalla de presentación personalizada"
@@ -1065,13 +1065,13 @@
     "message": "Se configuró para una pantalla de presentación personalizada"
   },
   "lighthouse-core/audits/themed-omnibox.js | description": {
-    "message": "El color de la barra de direcciones del navegador puede adaptarse a tu sitio web. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/address-bar)."
+    "message": "Se puede aplicar un tema a la barra de direcciones del navegador para que combine con tu sitio web. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/address-bar)."
   },
   "lighthouse-core/audits/themed-omnibox.js | failureTitle": {
-    "message": "No establece el color de un tema para la barra de direcciones."
+    "message": "No establece un color de tema para la barra de direcciones."
   },
   "lighthouse-core/audits/themed-omnibox.js | title": {
-    "message": "Establece el color de un tema para la barra de direcciones."
+    "message": "Establece un color de tema para la barra de direcciones."
   },
   "lighthouse-core/audits/third-party-summary.js | columnBlockingTime": {
     "message": "Tiempo de bloqueo del subproceso principal"
@@ -1152,7 +1152,7 @@
     "message": "Tiene una etiqueta `<meta name=\"viewport\">` con `width` o `initial-scale`"
   },
   "lighthouse-core/audits/without-javascript.js | description": {
-    "message": "Tu app debe mostrar algún contenido cuando JavaScript esté inhabilitado, aunque solo sea un aviso para informar al usuario de que es necesario activar JavaScript para usar la app. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
+    "message": "Tu app debe mostrar algún contenido cuando JavaScript está inhabilitado, aunque solo sea un aviso para informar al usuario que se necesita JavaScript para usar la app. [Obtén más información](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
   },
   "lighthouse-core/audits/without-javascript.js | explanation": {
     "message": "El cuerpo de la página debe renderizar parte del contenido aunque sus secuencias de comandos no estén disponibles."
@@ -1275,7 +1275,7 @@
     "message": "Estas comprobaciones se centran en diferentes aspectos de las apps web progresivas. [Obtén más información](https://developers.google.com/web/progressive-web-apps/checklist)."
   },
   "lighthouse-core/config/default-config.js | pwaCategoryManualDescription": {
-    "message": "Estas comprobaciones son necesarias según el modelo de referencia [Lista de tareas para AWP](https://developers.google.com/web/progressive-web-apps/checklist), pero Lighthouse no las verifica automáticamente. Es importante que las verifiques a mano (aunque no afectan a la puntuación)."
+    "message": "Estas comprobaciones son necesarias según el modelo de referencia [Lista de tareas para AWP](https://developers.google.com/web/progressive-web-apps/checklist), pero Lighthouse no las realiza automáticamente. Es importante que las verifiques a mano (aunque no afectan a la puntuación)."
   },
   "lighthouse-core/config/default-config.js | pwaCategoryTitle": {
     "message": "App web progresiva"
diff --git a/lighthouse-core/lib/i18n/locales/es.json b/lighthouse-core/lib/i18n/locales/es.json
index aa0d90d58..4e29c3129 100644
--- a/lighthouse-core/lib/i18n/locales/es.json
+++ b/lighthouse-core/lib/i18n/locales/es.json
@@ -861,7 +861,7 @@
     "message": "Límites de rendimiento"
   },
   "lighthouse-core/audits/redirects-http.js | description": {
-    "message": "Si ya has configurado HTTPS, asegúrate de redireccionar todo el tráfico HTTP a HTTPS para habilitar funciones de seguridad web para todos los usuarios. [Más información](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)"
+    "message": "Si ya has configurado HTTPS, asegúrate de redirigir todo el tráfico HTTP a HTTPS para habilitar funciones de seguridad web para todos los usuarios. [Más información](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)"
   },
   "lighthouse-core/audits/redirects-http.js | failureTitle": {
     "message": "No redirige el tráfico HTTP a HTTPS"
diff --git a/lighthouse-core/lib/i18n/locales/fil.json b/lighthouse-core/lib/i18n/locales/fil.json
index cd2f16a2d..2fd7259da 100644
--- a/lighthouse-core/lib/i18n/locales/fil.json
+++ b/lighthouse-core/lib/i18n/locales/fil.json
@@ -318,7 +318,7 @@
     "message": "Naglalaman ng element na `<track>` na may `[kind=\"description\"]` ang mga element na `<video>`"
   },
   "lighthouse-core/audits/apple-touch-icon.js | description": {
-    "message": "Para sa magandang hitsura sa iOS kapag nagdagdag ang mga user ng progressive web app sa home screen, tumukoy ng isang `apple-touch-icon`. Dapat itong nakadirekta sa isang hindi transparent na kuwadradong 192px (o 180px) PNG. [Matuto Pa](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
+    "message": "Para sa magandang hitsura sa iOS kapag nagdagdag ang mga user ng progressive web app sa home screen, tumukoy ng `apple-touch-icon`. Dapat itong nakadirekta sa isang hindi transparent na kuwadradong 192px (o 180px) PNG. [Matuto Pa](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
   },
   "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
     "message": "Hindi nagbibigay ng valid na `apple-touch-icon`"
@@ -705,7 +705,7 @@
     "message": "Invalid ang laki ng larawan ng impormasyon {url}"
   },
   "lighthouse-core/audits/installable-manifest.js | description": {
-    "message": "Puwedeng proactive na i-prompt ng mga browser ang mga user para idagdag ang iyong app sa kanilang homescreen, na maaaring magresulta sa mas mataas na pakikipag-ugnayan. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
+    "message": "Puwedeng proactive na i-prompt ng mga browser ang mga user na idagdag ang iyong app sa kanilang homescreen, na puwedeng magresulta sa mas maraming pakikipag-ugnayan. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
   },
   "lighthouse-core/audits/installable-manifest.js | failureTitle": {
     "message": "Hindi natutugunan ng manifest ng web app ang mga kinakailangan sa pag-install"
@@ -738,7 +738,7 @@
     "message": "Interactive sa naka-simulate na mobile network sa loob ng {timeInMs, number, seconds} s"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
-    "message": "Masyadong mabagal mag-load ang iyong page at hindi interactive sa loob ng 10 segundo. Tingnan ang mga pagkakataon at diagnostic sa seksyong \"Performance\" para matutunan kung paano magpahusay."
+    "message": "Masyadong mabagal mag-load ang iyong page at hindi ito interactive sa loob ng 10 segundo. Tingnan ang mga pagkakataon at diagnostic sa seksyong \"Performance\" para malaman kung paano magpahusay."
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "Hindi sapat ang bilis ng pag-load ng page sa mga mobile network"
@@ -759,19 +759,19 @@
     "message": "Binabawasan ang gawain sa pangunahing thread"
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | description": {
-    "message": "Para maabot ang pinakamaraming bilang nguser, dapat gumana ang mga site sa bawat pangunahing browser. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
+    "message": "Para maabot ang pinakamaraming user, dapat gumana ang mga site sa bawat pangunahing browser. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | title": {
-    "message": "Gumagana ang site sa cross-browser"
+    "message": "Gumagana ang site sa iba't ibang browser"
   },
   "lighthouse-core/audits/manual/pwa-each-page-has-url.js | description": {
-    "message": "Tiyaking nali-link nang malalim ang mga indibidwal na page sa pamamagitan ng URL at natatangi ang mga URL para sa pagbabahagi sa social media. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)."
+    "message": "Tiyaking puwedeng i-deep link ang mga indibidwal na page sa pamamagitan ng URL at natatangi ang mga URL para sa pagbabahagi sa social media. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)."
   },
   "lighthouse-core/audits/manual/pwa-each-page-has-url.js | title": {
     "message": "May URL ang bawat page"
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | description": {
-    "message": "Mabilis dapat ang dating ng mga transition habang nagta-tap ka sa paligid, kahit nasa mabagal na network, isang susi sa kapansin-pansing performance. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
+    "message": "Mabilis dapat ang mga transition habang nagta-tap ka, kahit sa mabagal na network, na isang susi sa nakikitang performance. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | title": {
     "message": "Mukhang hindi nagba-block sa network ang mga transition ng page"
@@ -846,7 +846,7 @@
     "message": "Tumutugon ang `start_url` gamit ang 200 kapag offline"
   },
   "lighthouse-core/audits/offline-start-url.js | warningCantStart": {
-    "message": "Hindi mabasa ng Lighthouse ang `start_url` na galing sa manifest. Dahil dito, ipinagpapalagay ang `start_url` bilang URL ng dokumento. Mensahe ng error: '{manifestWarning}'."
+    "message": "Hindi mabasa ng Lighthouse ang `start_url` na galing sa manifest. Dahil dito, ipinagpapalagay na `start_url` ang URL ng dokumento. Mensahe ng error: '{manifestWarning}.'"
   },
   "lighthouse-core/audits/performance-budget.js | columnOverBudget": {
     "message": "Lampas sa Badyet"
@@ -861,7 +861,7 @@
     "message": "Badyet sa performance"
   },
   "lighthouse-core/audits/redirects-http.js | description": {
-    "message": "Kung na-set up mo na ang HTTPS, tiyaking iyong na-redirect sa HTTPS ang lahat ng trapiko sa HTTP para ma-enable ang mga ligtas na feature sa web para sa lahat ng user mo. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)."
+    "message": "Kung na-set up mo na ang HTTPS, tiyaking ire-redirect mo sa HTTPS ang lahat ng trapiko sa HTTP para ma-enable ang mga ligtas na feature sa web para sa lahat ng iyong user. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)."
   },
   "lighthouse-core/audits/redirects-http.js | failureTitle": {
     "message": "Hindi nire-redirect sa HTTPS ang trapiko sa HTTP"
@@ -1035,19 +1035,19 @@
     "message": "Angkop ang laki ng mga target ng pag-tap"
   },
   "lighthouse-core/audits/service-worker.js | description": {
-    "message": "Ang service worker ay ang teknolohiyang nag-e-enable sa iyong app na gumamit ng maraming feature ng Progressive Web App, gaya ng offline, pagdaragdag sa homescreen, at mga push notification. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
+    "message": "Ang service worker ay ang teknolohiyang nagbibigay-daan sa iyong app na gumamit ng maraming feature ng Progressive Web App, gaya ng offline, pagdaragdag sa homescreen, at mga push notification. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
   },
   "lighthouse-core/audits/service-worker.js | explanationBadManifest": {
-    "message": "Kinokontrol ng isang service worker ang page na ito, gayunpaman, walang nakitang `start_url` dahil hindi na-parse ang manifest bilang valid na JSON"
+    "message": "Kinokontrol ng service worker ang page na ito, gayunpaman, walang nakitang `start_url` dahil hindi na-parse ang manifest bilang valid na JSON"
   },
   "lighthouse-core/audits/service-worker.js | explanationBadStartUrl": {
-    "message": "Kinokontrol ng isang service worker ang page na ito, gayunpaman, wala ang `start_url` ({startUrl}) sa saklaw ng service worker ({scopeUrl})"
+    "message": "Kinokontrol ng service worker ang page na ito, gayunpaman, wala ang `start_url` ({startUrl}) sa saklaw ng service worker ({scopeUrl})"
   },
   "lighthouse-core/audits/service-worker.js | explanationNoManifest": {
-    "message": "Kinokontrol ng isang service worker ang page na ito, gayunpaman, walang nakitang `start_url` dahil walang nakuhang manifest."
+    "message": "Kinokontrol ng service worker ang page na ito, gayunpaman, walang nakitang `start_url` dahil walang nakuhang manifest."
   },
   "lighthouse-core/audits/service-worker.js | explanationOutOfScope": {
-    "message": "May isa o higit pang service worker ang origin na ito, gayunpaman, wala sa saklaw ang page ({pageUrl})."
+    "message": "May isa o higit pang service worker ang pinagmulang ito, gayunpaman, wala sa saklaw ang page ({pageUrl})."
   },
   "lighthouse-core/audits/service-worker.js | failureTitle": {
     "message": "Hindi nagrerehistro ng service worker na kumokontrol sa page at `start_url`"
@@ -1056,7 +1056,7 @@
     "message": "Nagrerehistro ng service worker na kumokontrol sa page at `start_url`"
   },
   "lighthouse-core/audits/splash-screen.js | description": {
-    "message": "Tinititiyak ng isang splash screen na may tema ang mataas na kalidad na karanasan kapag inilunsad ng mga user ang iyong app sa kanilang mga homescreen. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
+    "message": "Tinitiyak ng splash screen na may tema na magkakaroon ng karanasang may mataas na kalidad kapag inilunsad ng mga user ang iyong app sa kanilang mga homescreen. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
   },
   "lighthouse-core/audits/splash-screen.js | failureTitle": {
     "message": "Hindi naka-configure para sa custom na splash screen"
@@ -1083,10 +1083,10 @@
     "message": "Puwedeng lubos na makaapekto ang code ng third party sa performance ng pag-load. Limitahan ang bilang ng paulit-ulit na mga third-party na provider at subukang i-load ang code ng third party pagkatapos ng pangunahing pag-load ng iyong page. [Matuto pa](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/)."
   },
   "lighthouse-core/audits/third-party-summary.js | displayValue": {
-    "message": "Na-block ng third-party na code ang pangunahing thread sa loob ng {timeInMs, number, milliseconds} ms"
+    "message": "Na-block ng code ng third party ang pangunahing thread sa loob ng {timeInMs, number, milliseconds} ms"
   },
   "lighthouse-core/audits/third-party-summary.js | failureTitle": {
-    "message": "Bawasan ang epekto ng third-party na code"
+    "message": "Bawasan ang epekto ng code ng third party"
   },
   "lighthouse-core/audits/third-party-summary.js | title": {
     "message": "Paggamit ng Third Party"
@@ -1152,7 +1152,7 @@
     "message": "May tag na `<meta name=\"viewport\">` na may `width` o `initial-scale`"
   },
   "lighthouse-core/audits/without-javascript.js | description": {
-    "message": "Dapat magpakita ng ilang content ang iyong app kapag naka-disable ang JavaScript, kahit isang babala lang ito sa user na kinakailangan ang Javascript para magamit ang app. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
+    "message": "Dapat magpakita ng ilang content ang iyong app kapag naka-disable ang JavaScript, kahit isang babala lang sa user na kinakailangan ang Javascript para magamit ang app. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
   },
   "lighthouse-core/audits/without-javascript.js | explanation": {
     "message": "Dapat mag-render ng ilang content ang nilalaman ng page kung hindi available ang mga script nito."
@@ -1164,7 +1164,7 @@
     "message": "Naglalaman ng ilang content kapag hindi available ang JavaScript"
   },
   "lighthouse-core/audits/works-offline.js | description": {
-    "message": "Kung bumubuo ka ng Progressive Web App, pag-isipang gumamit ng isang service worker para puwedeng gumana ang iyong app offline. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+    "message": "Kung bumubuo ka ng Progressive Web App, pag-isipang gumamit ng service worker para gumana ang iyong app offline. [Matuto pa](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
   },
   "lighthouse-core/audits/works-offline.js | failureTitle": {
     "message": "Hindi tumutugon ang kasalukuyang page gamit ang 200 kapag offline"
@@ -1275,7 +1275,7 @@
     "message": "Vina-validate ng mga pagsusuring ito ang mga aspeto ng isang Progressive Web App. [Matuto pa](https://developers.google.com/web/progressive-web-apps/checklist)."
   },
   "lighthouse-core/config/default-config.js | pwaCategoryManualDescription": {
-    "message": "Kinakailangan ang mga pagsusuring ito ng baseline na [Checklist ng PWA ](https://developers.google.com/web/progressive-web-apps/checklist) pero hindi awtomatikong sinusuri ng Lighthouse. Hindi nakakaapekto ang mga ito sa iyong score pero mahalagang ma-verify mo ang mga ito nang manual."
+    "message": "Kinakailangan ang mga pagsusuring ito ng baseline na [Checklist ng PWA ](https://developers.google.com/web/progressive-web-apps/checklist) pero hindi ito awtomatikong sinusuri ng Lighthouse. Hindi nakakaapekto ang mga ito sa iyong score pero mahalagang ma-verify mo ang mga ito nang manual."
   },
   "lighthouse-core/config/default-config.js | pwaCategoryTitle": {
     "message": "Progressive Web App"
diff --git a/lighthouse-core/lib/i18n/locales/he.json b/lighthouse-core/lib/i18n/locales/he.json
index cbf8d00d0..a0c3a5892 100644
--- a/lighthouse-core/lib/i18n/locales/he.json
+++ b/lighthouse-core/lib/i18n/locales/he.json
@@ -879,7 +879,7 @@
     "message": "כדי להגדיר תקציבים עבור הכמות והגודל של משאבי הדף, יש להוסיף קובץ budget.json. [מידע נוסף](https://developers.google.com/web/tools/lighthouse/audits/budgets)."
   },
   "lighthouse-core/audits/resource-summary.js | displayValue": {
-    "message": "{requestCount,plural, =1{בקשה אחת •KB‎‎ {byteCount, number, bytes}‏}two{# בקשות • KB {byteCount, number, bytes}}many{# בקשות • KB {byteCount, number, bytes}}other{# בקשות • KB {byteCount, number, bytes}}}"
+    "message": "{requestCount,plural, =1{בקשה אחת • ‎{byteCount, number, bytes} KB‏}two{# בקשות • ‎KB {byteCount, number, bytes}}many{# בקשות • ‎KB {byteCount, number, bytes}}other{# בקשות • ‎KB {byteCount, number, bytes}}}"
   },
   "lighthouse-core/audits/resource-summary.js | title": {
     "message": "יש לצמצם ככל האפשר את מספר הבקשות ואת גודל ההעברות"
diff --git a/lighthouse-core/lib/i18n/locales/hi.json b/lighthouse-core/lib/i18n/locales/hi.json
index f17c56626..99d90a2f7 100644
--- a/lighthouse-core/lib/i18n/locales/hi.json
+++ b/lighthouse-core/lib/i18n/locales/hi.json
@@ -318,7 +318,7 @@
     "message": "`<video>` एलिमेंट में `[kind=\"description\"]` वाला एक `<track>` एलिमेंट है"
   },
   "lighthouse-core/audits/apple-touch-icon.js | description": {
-    "message": "उपयोगकर्ता जब होम स्क्रीन पर प्रगतिशील वेब ऐप जोड़ें, तो iOS पर वह अच्छे से नज़र आए, इसके लिए `apple-touch-icon` तय करें. यह ज़रूरी है कि यह आइकॉन किसी ऐसे 192px (या 180px) के चौकोर PNG की तरफ़ इशारा करे जिसके आर-पार न देखा जा सके. [ज़्यादा जानें](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
+    "message": "उपयोगकर्ता जब होम स्क्रीन पर प्रगतिशील वेब ऐप्लिकेशन जोड़ें, तो iOS पर वह अच्छे से नज़र आए, इसके लिए `apple-touch-icon` तय करें. यह ज़रूरी है कि यह आइकॉन किसी ऐसे 192px (या 180px) के वर्गाकार PNG की तरफ़ इशारा करे जिसके आर-पार न देखा जा सके. [ज़्यादा जानें](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
   },
   "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
     "message": "एक सही `apple-touch-icon` नहीं उपलब्ध कराता"
@@ -441,7 +441,7 @@
     "message": "इमेज अगली जेनरेशन के फ़ॉर्मेट में ऑफ़र करें"
   },
   "lighthouse-core/audits/content-width.js | description": {
-    "message": "आपके ऐप्लिकेशन की सामग्री की चौड़ाई व्यूपोर्ट की चौड़ाई से मेल नहीं खाती है, तो आपका ऐप्लिकेशन मोबाइल स्क्रीन पर ऑप्टिमाइज़ नहीं किया जा सकेगा. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport)."
+    "message": "अगर आपके ऐप्लिकेशन की सामग्री की चौड़ाई व्यूपोर्ट की चौड़ाई से मेल नहीं खाती है, तो आपका ऐप्लिकेशन मोबाइल स्क्रीन पर ऑप्टिमाइज़ नहीं किया जा सकेगा. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport)."
   },
   "lighthouse-core/audits/content-width.js | explanation": {
     "message": "{innerWidth}px के व्यूपोर्ट का आकार {outerWidth}px की विंडो के आकार से मेल नहीं खाता है."
@@ -705,7 +705,7 @@
     "message": "इमेज के आकार की जानकारी गलत है {url}"
   },
   "lighthouse-core/audits/installable-manifest.js | description": {
-    "message": "ब्राउज़र अपनी ओर से, उपयोगकर्ताओं को आपके ऐप्लिकेशन को उनकी होमस्क्रीन पर जोड़ने के लिए कह सकते हैं. इससे उपयोगकर्ताओं के साथ बेहतरी से जुड़ने में मदद मिलती है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
+    "message": "ब्राउज़र अपनी ओर से उपयोगकर्ताओं को आपके ऐप्लिकेशन को उनकी होमस्क्रीन पर जोड़ने का सुझाव दे सकते हैं. इससे उपयोगकर्ताओं के साथ बेहतर तरीके से जुड़ने में मदद मिलती है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
   },
   "lighthouse-core/audits/installable-manifest.js | failureTitle": {
     "message": "वेब ऐप्लिकेशन का मेनिफ़ेस्ट, इंस्टॉल करने की ज़रूरतों को पूरा नहीं करता है"
@@ -738,7 +738,7 @@
     "message": "{timeInMs, number, seconds} से. में सिम्युलेट किए गए मोबाइल नेटवर्क पर इंटरैक्शन शुरू हुआ"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
-    "message": "आपका पेज बहुत धीरे लोड होता है और 10 सेकंड में इंटरैक्टिव (पेज लोड होने के दौरान उपयोगकर्ता के इनपुट जैसे कि क्लिक, टैप, और बटन दबाने का जवाब देने में लगने वाला समय) नहीं होता है. सुधार करने के तरीके जानने के लिए \"प्रदर्शन\" सेक्शन में अवसरों पर नज़र डालें और गड़बड़ी की जानकारी लें."
+    "message": "आपका पेज बहुत धीरे लोड होता है और 10 सेकंड में इंटरैक्टिव नहीं होता है. सुधार करने के तरीके जानने के लिए \"परफ़ॉर्मेंस\" सेक्शन में अवसरों पर नज़र डालें और गड़बड़ी की जानकारी लें."
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "मोबाइल नेटवर्क पर पेज लोड होने की गति ज़रूरत के मुताबिक तेज़ नहीं है"
@@ -762,19 +762,19 @@
     "message": "ज़्यादा से ज़्यादा उपयोगकर्ताओं तक पहुंचने के लिए, साइटों का सभी प्रमुख ब्राउज़र पर काम करना ज़रूरी है. [ज़्यादा जानें](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
   },
   "lighthouse-core/audits/manual/pwa-cross-browser.js | title": {
-    "message": "यह क्रॉस-ब्राउज़र वेबसाइट है जो अलग-अलग ब्राउज़र पर काम करती है"
+    "message": "यह वेबसाइट अलग-अलग ब्राउज़र पर काम करती है"
   },
   "lighthouse-core/audits/manual/pwa-each-page-has-url.js | description": {
-    "message": "पक्का करें कि अलग-अलग पेज, यूआरएल के ज़रिए डीप लिंक के लायक हैं. साथ ही, वे ऐसे खास यूआरएल हैं जो सोशल मीडिया पर शेयर करने के मकसद को पूरा करते हैं. [ज़्यादा जानें](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)."
+    "message": "पक्का करें कि हर पेज को यूआरएल के ज़रिए डीप लिंक किया जा सकता हो. साथ ही, वे ऐसे खास यूआरएल हों जिन्हें सोशल मीडिया पर शेयर किया जा सकता है. [ज़्यादा जानें](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)."
   },
   "lighthouse-core/audits/manual/pwa-each-page-has-url.js | title": {
     "message": "हर पेज का एक यूआरएल होता है"
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | description": {
-    "message": "नेटवर्क धीमा होने के बाद भी वेब ऐप्लिकेशन पर टैप करने पर ट्रांज़िशन की रफ़्तार तेज़ महसूस होनी चाहिए. यह आपके पेज की अच्छी परफ़ॉर्मेंस के लिए अहम है. [ज़्यादा जानें](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
+    "message": "नेटवर्क धीमा होने पर भी वेब ऐप्लिकेशन में टैप करने पर ट्रांज़िशन की रफ़्तार तेज़ महसूस होनी चाहिए. यह आपके पेज की अच्छी परफ़ॉर्मेंस के लिए अहम है. [ज़्यादा जानें](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | title": {
-    "message": "पेज ट्रांज़िशन को देखकर ऐसा नहीं लगना चाहिए जैसे वे नेटवर्क लोड होने का इंतज़ार कर रहे हैं"
+    "message": "पेज ट्रांज़िशन को देखकर ऐसा नहीं लगना चाहिए जैसे कि वे नेटवर्क लोड होने का इंतज़ार कर रहे हैं"
   },
   "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
     "message": "इनपुट के इंतज़ार के समय का अंदाज़ा, इस बात से लगाया जाता है कि पेज लोड होने की सबसे व्यस्त पांच सेकंड की विंडो के दौरान आपके ऐप्लिकेशन को इस्तेमाल करने वाले के इनपुट का जवाब देने में कितना समय लगेगा. इस समय का हिसाब मिलीसेकंड में लगाया जाता है. अगर इंतज़ार का समय 50 मिलीसेकंड से ज़्यादा हो, तो इस्तेमाल करने वाले आपके ऐप्लिकेशन को धीमा मान सकते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)."
@@ -837,7 +837,7 @@
     "message": "सर्वर बैकएंड के इंतज़ार का समय"
   },
   "lighthouse-core/audits/offline-start-url.js | description": {
-    "message": "सर्विस वर्कर की वजह से आपका वेब ऐप्लिकेशन ऑनलाइन, ऑफ़लाइन, और रुक-रुककर चलने वाले नेटवर्क कनेक्शन जैसी स्थितियों में भी अच्छे ढंग से काम करता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+    "message": "सर्विस वर्कर की वजह से आपका वेब ऐप्लिकेशन ऑनलाइन, ऑफ़लाइन, और रुक-रुककर चलने वाले नेटवर्क जैसी स्थितियों में भी अच्छे ढंग से काम करता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
   },
   "lighthouse-core/audits/offline-start-url.js | failureTitle": {
     "message": "`start_url` ऑफ़लाइन होने पर, \"200\" कोड का जवाब नहीं देता है"
@@ -1035,7 +1035,7 @@
     "message": "टैप की जाने वाली जगहें सही आकार में हैं"
   },
   "lighthouse-core/audits/service-worker.js | description": {
-    "message": "सर्विस वर्कर एक ऐसी तकनीक है जो आपके ऐप्लिकेशन के लिए कई प्रगतिशील वेब ऐप फ़ीचर जैसे कि ऑफ़लाइन, होमस्क्रीन पर जोड़ें, और पुश नोटिफ़िकेशन को चालू करती है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
+    "message": "सर्विस वर्कर एक ऐसी तकनीक है जो आपके ऐप्लिकेशन के लिए कई प्रगतिशील वेब ऐप्लिकेशन फ़ीचर जैसे कि ऑफ़लाइन, होमस्क्रीन पर जोड़ें, और पुश नोटिफ़िकेशन को चालू करती है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
   },
   "lighthouse-core/audits/service-worker.js | explanationBadManifest": {
     "message": "इस पेज का नियंत्रण सर्विस वर्कर के पास है. हालांकि, कोई `start_url` नहीं मिला, क्योंकि मेनिफ़ेस्ट को मान्य JSON के रूप में पार्स नहीं किया जा सका"
@@ -1044,10 +1044,10 @@
     "message": "इस पेज का नियंत्रण सर्विस वर्कर के पास है. हालांकि, `start_url` ({startUrl}) सर्विस वर्कर के दायरे में नहीं है ({scopeUrl})"
   },
   "lighthouse-core/audits/service-worker.js | explanationNoManifest": {
-    "message": "इस पेज का नियंत्रण सर्विस वर्कर के पास है. हालांकि, कोई `start_url` नहीं मिला, क्योंकि कोई पेज पर कोई मेनिफ़ेस्ट ही नहीं था."
+    "message": "इस पेज का नियंत्रण सर्विस वर्कर के पास है. हालांकि, कोई `start_url` नहीं मिला, क्योंकि किसी पेज पर कोई मेनिफ़ेस्ट ही नहीं था."
   },
   "lighthouse-core/audits/service-worker.js | explanationOutOfScope": {
-    "message": "इस मूल में एक या ज़्यादा सर्विस वर्कर हैं. हालांकि, पेज ({pageUrl}) दायरे में नहीं है."
+    "message": "यहां पर एक या ज़्यादा सर्विस वर्कर हैं. हालांकि, पेज ({pageUrl}) दायरे में नहीं है."
   },
   "lighthouse-core/audits/service-worker.js | failureTitle": {
     "message": "किसी ऐसे सर्विस वर्कर को रजिस्टर नहीं करता जो पेज और `start_url` को नियंत्रित करता है"
@@ -1056,7 +1056,7 @@
     "message": "किसी ऐसे सर्विस वर्कर को रजिस्टर करता है जो पेज और `start_url` को नियंत्रित करता है"
   },
   "lighthouse-core/audits/splash-screen.js | description": {
-    "message": "उपयोगकर्ता जब अपनी होमस्क्रीन से आपका ऐप्लिकेशन लॉन्च करते हैं, तो थीम वाली स्प्लैश स्क्रीन की वजह से इसका इस्तेमाल करने वालों को अच्छा अनुभव मिलता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
+    "message": "उपयोगकर्ता जब अपनी होमस्क्रीन से आपका ऐप्लिकेशन लॉन्च करते हैं, तो थीम वाली स्प्लैश स्क्रीन की वजह से इस्तेमाल करने वालों को अच्छा अनुभव मिलता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
   },
   "lighthouse-core/audits/splash-screen.js | failureTitle": {
     "message": "पसंद के मुताबिक स्प्लैश स्क्रीन के लिए कॉन्फ़िगर नहीं किया गया"
@@ -1065,13 +1065,13 @@
     "message": "पसंद के मुताबिक स्प्लैश स्क्रीन के लिए कॉन्फ़िगर किया गया"
   },
   "lighthouse-core/audits/themed-omnibox.js | description": {
-    "message": "ब्राउज़र का पता बार आपकी वेबसाइट से मेल खाए, इसके लिए थीम इस्तेमाल कर सकते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/address-bar)."
+    "message": "ब्राउज़र के 'पता बार' की थीम ऐसी हो सकती है जो आपकी वेबसाइट से मेल खाए. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/address-bar)."
   },
   "lighthouse-core/audits/themed-omnibox.js | failureTitle": {
-    "message": "पता बार के लिए थीम का रंग सेट नहीं करता है."
+    "message": "'पता बार' के लिए थीम का रंग सेट नहीं करता है."
   },
   "lighthouse-core/audits/themed-omnibox.js | title": {
-    "message": "पता बार के लिए थीम का रंग सेट करता है."
+    "message": "'पता बार' के लिए थीम का रंग सेट करता है."
   },
   "lighthouse-core/audits/third-party-summary.js | columnBlockingTime": {
     "message": "मुख्य थ्रेड ब्लॉक होने का समय"
@@ -1125,7 +1125,7 @@
     "message": "\"{securityOrigin}\" के लिए पहले से कनेक्ट किया गया <link> मिला, लेकिन ब्राउज़र ने इसका इस्तेमाल नहीं किया था. यह पक्का कर लें कि आप `crossorigin` विशेषता का इस्तेमाल ठीक से कर रहे हैं."
   },
   "lighthouse-core/audits/uses-rel-preconnect.js | description": {
-    "message": "तीसरे पक्ष के मूल से जल्दी कनेक्शन बनाने के लिए `preconnect` या `dns-prefetch` संसाधन संकेत जोड़ें. [ज़्यादा जानें](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect)."
+    "message": "तीसरे पक्ष से जल्दी कनेक्शन बनाने के लिए `preconnect` या `dns-prefetch` संसाधन संकेत जोड़ें. [ज़्यादा जानें](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect)."
   },
   "lighthouse-core/audits/uses-rel-preconnect.js | title": {
     "message": "ज़रूरी मूल से प्री-कनेक्ट करें"
@@ -1158,7 +1158,7 @@
     "message": "अगर इसकी स्क्रिप्ट उपलब्ध नहीं हैं, तो पेज के मुख्य हिस्से को कुछ सामग्री रेंडर करनी चाहिए या उसकी इमेज बनानी चाहिए."
   },
   "lighthouse-core/audits/without-javascript.js | failureTitle": {
-    "message": "यह वेब पेज, JavaScript उपलब्ध न होने पर फ़ॉलबैक सामग्री मुहैया नहीं करवाता है"
+    "message": "यह वेब पेज, JavaScript उपलब्ध न होने पर फ़ॉलबैक सामग्री मुहैया नहीं कराता है"
   },
   "lighthouse-core/audits/without-javascript.js | title": {
     "message": "यह वेब पेज, JavaScript उपलब्ध नहीं होने पर कुछ सामग्री दिखाता है"
@@ -1167,13 +1167,13 @@
     "message": "अगर आप एक प्रगतिशील वेब ऐप्लिकेशन बना रहे हैं, तो सर्विस वर्कर का इस्तेमाल करें, ताकि आपका ऐप्लिकेशन ऑफ़लाइन भी काम कर सके. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
   },
   "lighthouse-core/audits/works-offline.js | failureTitle": {
-    "message": "ऑफ़लाइन होने पर, मौजूदा पेज \"200\" कोड (एचटीटीपी स्टेटस कोड जिसका मतलब है कि एचटीटीपी स्तर पर ट्रांसमिशन ठीक है) का जवाब नहीं देता है"
+    "message": "ऑफ़लाइन होने पर, मौजूदा पेज \"200\" कोड का जवाब नहीं देता है"
   },
   "lighthouse-core/audits/works-offline.js | title": {
     "message": "ऑफ़लाइन होने पर, मौजूदा पेज \"200\" कोड का जवाब देता है"
   },
   "lighthouse-core/audits/works-offline.js | warningNoLoad": {
-    "message": "ऐसा हो सकता है कि ऑफ़लाइन होने पर पेज इसलिए लोड नहीं हो रहा है, क्योंकि आपका टेस्ट यूआरएल ({requested}) को \"{final}\" की ओर रीडायरेक्ट किया गया था. टेस्टिंग के लिए सीधे दूसरी यूआरएल आज़माएं."
+    "message": "ऐसा हो सकता है कि ऑफ़लाइन होने पर पेज इसलिए लोड नहीं हो रहा है, क्योंकि आपका टेस्ट यूआरएल ({requested}) को \"{final}\" की ओर रीडायरेक्ट किया गया था. टेस्टिंग के लिए सीधे दूसरा यूआरएल आज़माएं."
   },
   "lighthouse-core/config/default-config.js | a11yAriaGroupDescription": {
     "message": "ये आपके ऐप्लिकेशन में ARIA के इस्तेमाल को बेहतर बनाने के अवसर हैं, जिससे उपयोगकर्ताओं का स्क्रीन रीडर जैसी सहायक तकनीक का अनुभव बेहतर हो सकता है."
diff --git a/lighthouse-core/lib/i18n/locales/ja.json b/lighthouse-core/lib/i18n/locales/ja.json
index 6fa39301f..807d7ba82 100644
--- a/lighthouse-core/lib/i18n/locales/ja.json
+++ b/lighthouse-core/lib/i18n/locales/ja.json
@@ -363,7 +363,7 @@
     "message": "オフスクリーン画像の遅延読み込み"
   },
   "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": {
-    "message": "ページの初回ペイントをリソースがブロックしています。重要な JavaScript や CSS はインラインで配信し、それ以外の JavaScript やスタイルはすべて遅らせることをご検討ください。[詳細](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources)"
+    "message": "ページの First Paint をリソースがブロックしています。重要な JavaScript や CSS はインラインで配信し、それ以外の JavaScript やスタイルはすべて遅らせることをご検討ください。[詳細](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources)"
   },
   "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title": {
     "message": "レンダリングを妨げるリソースの除外"
@@ -783,10 +783,10 @@
     "message": "入力の推定待ち時間"
   },
   "lighthouse-core/audits/metrics/first-contentful-paint.js | description": {
-    "message": "コンテンツの初回ペイントは、テキストまたは画像が初めてペイントされるまでにかかった時間です。[詳細](https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint)"
+    "message": "First Contentful Paint は、テキストまたは画像が初めてペイントされるまでにかかった時間です。[詳細](https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint)"
   },
   "lighthouse-core/audits/metrics/first-contentful-paint.js | title": {
-    "message": "コンテンツの初回ペイント"
+    "message": "First Contentful Paint"
   },
   "lighthouse-core/audits/metrics/first-cpu-idle.js | description": {
     "message": "CPU の初回アイドルは、ページのメインスレッド処理が静止し、初めて入力の処理が可能になるまでにかかった時間です。[詳細](https://developers.google.com/web/tools/lighthouse/audits/first-interactive)"
@@ -795,10 +795,10 @@
     "message": "CPU の初回アイドル"
   },
   "lighthouse-core/audits/metrics/first-meaningful-paint.js | description": {
-    "message": "意味のあるコンテンツの初回ペイントは、ページの主要なコンテンツが可視化されるまでにかかった時間です。[詳細](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint)"
+    "message": "First Meaningful Paint は、ページの主要なコンテンツが可視化されるまでにかかった時間です。[詳細](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint)"
   },
   "lighthouse-core/audits/metrics/first-meaningful-paint.js | title": {
-    "message": "意味のあるコンテンツの初回ペイント"
+    "message": "First Meaningful Paint"
   },
   "lighthouse-core/audits/metrics/interactive.js | description": {
     "message": "操作可能になるまでの時間とは、ページが完全に操作可能になるのに要する時間です。[詳細](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive)"
@@ -1248,10 +1248,10 @@
     "message": "診断"
   },
   "lighthouse-core/config/default-config.js | firstPaintImprovementsGroupDescription": {
-    "message": "パフォーマンスの最も重要な点は、ピクセルをどのくらい速く画面にレンダリングできるかです。主要な指標: コンテンツの初回ペイント、意味のあるコンテンツの初回ペイント"
+    "message": "パフォーマンスの最も重要な点は、ピクセルをどのくらい速く画面にレンダリングできるかです。主要な指標: First Contentful Paint、First Meaningful Paint"
   },
   "lighthouse-core/config/default-config.js | firstPaintImprovementsGroupTitle": {
-    "message": "初回ペイントの改善点"
+    "message": "First Paint の改善点"
   },
   "lighthouse-core/config/default-config.js | loadOpportunitiesGroupDescription": {
     "message": "これらの提案を実施すると、ページの読み込み時間を短縮できる可能性があります。なお、パフォーマンス スコアには[直接影響](https://github.com/GoogleChrome/lighthouse/blob/d2ec9ffbb21de9ad1a0f86ed24575eda32c796f0/docs/scoring.md#how-are-the-scores-weighted)しません。"
diff --git a/lighthouse-core/lib/i18n/locales/ko.json b/lighthouse-core/lib/i18n/locales/ko.json
index b661ce518..fcc58ffa2 100644
--- a/lighthouse-core/lib/i18n/locales/ko.json
+++ b/lighthouse-core/lib/i18n/locales/ko.json
@@ -738,7 +738,7 @@
     "message": "시뮬레이션된 모바일 네트워크에서 페이지와 상호작용할 수 있게 될 때까지 걸린 시간: {timeInMs, number, seconds}초"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
-    "message": "페이지가 너무 느리게 로드되므로 10초 이내에 사용할 수 없습니다. '성능' 섹션에서 추천 내용과 진단을 확인하고 개선 방안을 알아보세요."
+    "message": "페이지가 너무 느리게 로드되므로 10초 이내에 사용할 수 없습니다. '성능' 섹션에서 추천 내용과 진단을 확인해 개선 방안을 알아보세요."
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "모바일 네트워크에서 페이지 로딩이 충분히 빠르지 않음"
@@ -1053,7 +1053,7 @@
     "message": "페이지와 `start_url`을(를) 제어하는 서비스 워커를 등록하지 않음"
   },
   "lighthouse-core/audits/service-worker.js | title": {
-    "message": "페이지와`start_url`을(를) 제어하는 서비스 워커를 등록함"
+    "message": "페이지와 `start_url`을(를) 제어하는 서비스 워커를 등록함"
   },
   "lighthouse-core/audits/splash-screen.js | description": {
     "message": "테마 스플래시 화면을 사용하면 사용자가 홈 화면에서 앱을 실행했을 때 고품질의 환경을 경험할 수 있습니다. [자세히 알아보기](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)"
@@ -1155,7 +1155,7 @@
     "message": "JavaScript가 사용 중지되었더라도 사용자에게 앱을 사용하려면 JavaScript가 필요하다는 경고 등의 일부 콘텐츠는 앱에 표시되어야 합니다. [자세히 알아보기](https://developers.google.com/web/tools/lighthouse/audits/no-js)"
   },
   "lighthouse-core/audits/without-javascript.js | explanation": {
-    "message": "스크립트를 사용할 수 없는 경우 페이지 본문에서 일부 콘텐츠를 렌더링해야 합니다."
+    "message": "페이지 본문에서 스크립트를 사용할 수 없는 경우 본문에서 콘텐츠를 일부 렌더링해야 합니다."
   },
   "lighthouse-core/audits/without-javascript.js | failureTitle": {
     "message": "JavaScript를 사용할 수 없는 경우 대체 콘텐츠를 제공하지 않음"
diff --git a/lighthouse-core/lib/i18n/locales/pt-PT.json b/lighthouse-core/lib/i18n/locales/pt-PT.json
index f7fe89e0e..4fd8ce7eb 100644
--- a/lighthouse-core/lib/i18n/locales/pt-PT.json
+++ b/lighthouse-core/lib/i18n/locales/pt-PT.json
@@ -1518,10 +1518,10 @@
     "message": "Um número de [plug-ins do WordPress](https://wordpress.org/plugins/search/minify+javascript/) pode acelerar o seu site ao concatenar, reduzir e comprimir os seus scripts. Poderá ainda utilizar um processo de criação para proceder previamente à redução se possível."
   },
   "stack-packs/packs/wordpress.js | unused_css_rules": {
-    "message": "Considere reduzir ou mudar o número de [plug-ins do WordPress](https://wordpress.org/plugins/) que carregam CSS não utilizadas na sua página. Para identificar plug-ins que estejam a adicionar CSS não reconhecido, experimente realizar a [cobertura de código](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) nas DevTools do Chrome. Pode identificar o tema/plug-in responsável a partir do URL da folha de estilos. Esteja atento a plug-ins que tenham muitas folhas de estilo na lista com muito vermelho na cobertura do código. Um plug-in só deve ter uma folha de estilos na fila de espera se esta for realmente utilizada na página."
+    "message": "Considere reduzir ou mudar o número de [plug-ins do WordPress](https://wordpress.org/plugins/) que carregam CSS não utilizadas na sua página. Para identificar plug-ins que estejam a adicionar CSS não reconhecido, experimente realizar a [cobertura de código](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) nas DevTools do Chrome. Pode identificar o tema/plug-in responsável a partir do URL da folha de estilos. Esteja atento a plug-ins que tenham muitas folhas de estilo na lista com muito vermelho na cobertura do código. Um plug-in só deve ter uma folha de estilos na lista de espera se esta for realmente utilizada na página."
   },
   "stack-packs/packs/wordpress.js | unused_javascript": {
-    "message": "Considere reduzir ou mudar o número de [plug-ins do WordPress](https://wordpress.org/plugins/) que carregam JavaScript não utilizado na sua página. Para identificar plug-ins que estejam a adicionar JS não reconhecido, experimente realizar a [cobertura de código](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) nas DevTools do Chrome. Pode identificar o tema/plug-in responsável a partir do URL do script. Esteja atento a plug-ins que tenham muitos scripts na lista com muito vermelho na cobertura do código. Um plug-in só deve ter um script na fila de espera se este for realmente utilizado na página."
+    "message": "Considere reduzir ou mudar o número de [plug-ins do WordPress](https://wordpress.org/plugins/) que carregam JavaScript não utilizado na sua página. Para identificar plug-ins que estejam a adicionar JS não reconhecido, experimente realizar a [cobertura de código](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) nas DevTools do Chrome. Pode identificar o tema/plug-in responsável a partir do URL do script. Esteja atento a plug-ins que tenham muitos scripts na lista com muito vermelho na cobertura do código. Um plug-in só deve ter um script na lista de espera se este for realmente utilizado na página."
   },
   "stack-packs/packs/wordpress.js | uses_long_cache_ttl": {
     "message": "Leia sobre [Colocação do navegador em cache no WordPress](https://codex.wordpress.org/WordPress_Optimization#Browser_Caching)."
diff --git a/lighthouse-core/lib/i18n/locales/ta.json b/lighthouse-core/lib/i18n/locales/ta.json
index e20bd6f09..9923c6ec9 100644
--- a/lighthouse-core/lib/i18n/locales/ta.json
+++ b/lighthouse-core/lib/i18n/locales/ta.json
@@ -282,7 +282,7 @@
     "message": "`[headers]` பண்புக்கூறைப் பயன்படுத்தும் `<table>` உறுப்பிலுள்ள கலங்கள் அதே அட்டவணையின் பிற கலங்களைக் குறிப்பிடுகின்றன."
   },
   "lighthouse-core/audits/accessibility/th-has-data-cells.js | description": {
-    "message": "அட்டவணைகளில் எளிதாகச் செல்வதற்கு ஸ்க்ரீன் ரீடர்களில் அம்சங்கள் உள்ளன. டேபிள் தலைப்புகள் எப்போதும் சில கலங்களின் தொகுப்பைக் குறிப்பிடுமாறு அமைப்பது ஸ்க்ரீன் ரீடர் பயனர்களின் அனுபவத்தை மேம்படுத்தக்கூடும். [மேலும் அறிக](https://web.dev/th-has-data-cells/)."
+    "message": "அட்டவணைகளில் எளிதாகச் செல்வதற்கு ஸ்க்ரீன் ரீடர்களில் அம்சங்கள் உள்ளன. அட்டவணைத் தலைப்புகள் எப்போதும் சில கலங்களின் தொகுப்பைக் குறிப்பிடுமாறு அமைப்பது ஸ்க்ரீன் ரீடர் பயனர்களின் அனுபவத்தை மேம்படுத்தக்கூடும். [மேலும் அறிக](https://web.dev/th-has-data-cells/)."
   },
   "lighthouse-core/audits/accessibility/th-has-data-cells.js | failureTitle": {
     "message": "`<th>` உறுப்புகளும் `[role=\"columnheader\"/\"rowheader\"]`ஐக் கொண்டுள்ள உறுப்புகளும் விவரிக்கும் தரவுக் கலங்கள் அவற்றுக்கு இல்லை."
diff --git a/lighthouse-core/lib/i18n/locales/vi.json b/lighthouse-core/lib/i18n/locales/vi.json
index 8c75577eb..fb70bd10d 100644
--- a/lighthouse-core/lib/i18n/locales/vi.json
+++ b/lighthouse-core/lib/i18n/locales/vi.json
@@ -318,7 +318,7 @@
     "message": "Các phần tử `<video>` chứa phần tử `<track>` có `[kind=\"description\"]`"
   },
   "lighthouse-core/audits/apple-touch-icon.js | description": {
-    "message": "Hãy xác định `apple-touch-icon` để hiển thị đúng cách trên iOS khi người dùng thêm một ứng dụng web tiến bộ vào màn hình chính. Thuộc tính này phải trỏ đến ảnh PNG vuông có kích thước 192px (hoặc 180px) ở dạng không trong suốt. [Tìm hiểu thêm](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)"
+    "message": "Hãy xác định `apple-touch-icon` để khi người dùng thêm một ứng dụng web tiến bộ vào màn hình chính trên iOS, biểu tượng ứng dụng sẽ hiển thị chính xác. Thuộc tính này phải trỏ đến ảnh PNG vuông có kích thước 192px (hoặc 180px) ở dạng không trong suốt. [Tìm hiểu thêm](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)"
   },
   "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
     "message": "Chưa cung cấp `apple-touch-icon` hợp lệ"
@@ -573,7 +573,7 @@
     "message": "Phiên bản"
   },
   "lighthouse-core/audits/dobetterweb/js-libraries.js | description": {
-    "message": "Phát hiện tất cả thư viện giao diện người dùng JavaScript trên trang này. [Tìm hiểu thêm](https://web.dev/js-libraries)"
+    "message": "Phát hiện tất cả thư viện JavaScript ở phía giao diện người dùng trên trang này. [Tìm hiểu thêm](https://web.dev/js-libraries)"
   },
   "lighthouse-core/audits/dobetterweb/js-libraries.js | title": {
     "message": "Thư viện JavaScript phát hiện được"
@@ -738,7 +738,7 @@
     "message": "Tương tác trên mạng di động mô phỏng ở giây thứ {timeInMs, number, seconds}"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
-    "message": "Trang của bạn tải quá chậm và không tương tác trong vòng 10 giây. Hãy xem các cơ hội và thông tin chẩn đoán trong phần \"Hiệu suất\" để tìm hiểu cách cải thiện."
+    "message": "Trang của bạn tải quá chậm và không tương tác trong vòng 10 giây. Hãy xem các lựa chọn tối ưu hóa và thông tin chẩn đoán trong phần \"Hiệu suất\" để tìm hiểu cách cải thiện."
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "Tốc độ tải trang không đủ nhanh trên mạng di động"
@@ -765,16 +765,16 @@
     "message": "Trang web hoạt động trên nhiều trình duyệt"
   },
   "lighthouse-core/audits/manual/pwa-each-page-has-url.js | description": {
-    "message": "Đảm bảo từng trang có thể liên kết sâu qua URL và URL là duy nhất, có như vậy thì mới chia sẻ được trên mạng xã hội. [Tìm hiểu thêm](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)"
+    "message": "Đảm bảo từng trang có thể liên kết sâu qua URL và URL là duy nhất để có thể chia sẻ trên mạng xã hội. [Tìm hiểu thêm](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)"
   },
   "lighthouse-core/audits/manual/pwa-each-page-has-url.js | title": {
     "message": "Mỗi trang có một URL"
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | description": {
-    "message": "Quá trình chuyển tiếp phải diễn ra ngay khi bạn nhấn vào, kể cả khi mạng chậm, một yếu tố then chốt để tăng hiệu suất cảm nhận. [Tìm hiểu thêm](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)"
+    "message": "Quá trình chuyển tiếp phải diễn ra ngay khi bạn nhấn vào, kể cả khi mạng chậm. Đây là một yếu tố quan trọng trong cảm nhận về hiệu suất. [Tìm hiểu thêm](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)"
   },
   "lighthouse-core/audits/manual/pwa-page-transitions.js | title": {
-    "message": "Quá trình chuyển tiếp trang không bị chậm trễ do mạng"
+    "message": "Người dùng không cảm thấy mạng chậm khi chuyển trang"
   },
   "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
     "message": "Thời gian chờ nhập thông tin theo ước tính là thời gian ước tính mà ứng dụng cần để phản hồi thông tin do người dùng nhập (tính bằng mili giây) trong khoảng thời gian 5 giây tải nhiều trang nhất. Nếu người dùng phải chờ quá 50 mili giây, thì ứng dụng của bạn bị coi là chạy chậm. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)."
@@ -840,10 +840,10 @@
     "message": "Trình chạy dịch vụ giúp ứng dụng web của bạn hoạt động ổn định trong điều kiện mạng chập chờn. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)"
   },
   "lighthouse-core/audits/offline-start-url.js | failureTitle": {
-    "message": "`start_url` không phản hồi kèm mã trạng thái 200 khi không có mạng"
+    "message": "`start_url` không trả về mã trạng thái 200 khi không có mạng"
   },
   "lighthouse-core/audits/offline-start-url.js | title": {
-    "message": "`start_url` phản hồi kèm mã trạng thái 200 khi không có mạng"
+    "message": "`start_url` trả về mã trạng thái 200 khi không có mạng"
   },
   "lighthouse-core/audits/offline-start-url.js | warningCantStart": {
     "message": "Lighthouse không đọc được `start_url` trong tệp kê khai. Do đó, `start_url` được giả định là URL của tài liệu. Thông báo lỗi: '{manifestWarning}'."
@@ -996,7 +996,7 @@
     "message": "Tài liệu tránh sử dụng plugin"
   },
   "lighthouse-core/audits/seo/robots-txt.js | description": {
-    "message": "Nếu định dạng của tệp robots.txt không đúng, thì trình thu thập thông tin có thể không hiểu được cách bạn muốn thu thập thông tin hoặc lập chỉ mục trang web. [Tìm hiểu thêm](https://web.dev/robots-txt)"
+    "message": "Nếu định dạng của tệp robots.txt không đúng, thì trình thu thập thông tin có thể không hiểu được cách bạn muốn thu thập thông tin hoặc lập chỉ mục trang web của mình. [Tìm hiểu thêm](https://web.dev/robots-txt)"
   },
   "lighthouse-core/audits/seo/robots-txt.js | displayValueHttpBadCode": {
     "message": "Yêu cầu robots.txt trả về trạng thái HTTP: {statusCode}"
@@ -1035,19 +1035,19 @@
     "message": "Mục tiêu nhấn có kích thước phù hợp"
   },
   "lighthouse-core/audits/service-worker.js | description": {
-    "message": "Trình chạy dịch vụ là công nghệ cho phép ứng dụng của bạn dùng nhiều tính năng của Ứng dụng web tiến bộ, chẳng hạn như thêm vào màn hình chính khi không có mạng và thông báo đẩy. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)"
+    "message": "Trình chạy dịch vụ là công nghệ cho phép ứng dụng của bạn dùng nhiều tính năng của Ứng dụng web tiến bộ, chẳng hạn như hoạt động khi không có mạng, thêm vào màn hình chính, và thông báo đẩy. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)"
   },
   "lighthouse-core/audits/service-worker.js | explanationBadManifest": {
     "message": "Trang này do một trình chạy dịch vụ kiểm soát. Tuy nhiên, không tìm thấy `start_url` vì tệp kê khai không thể phân tích cú pháp thành tệp JSON hợp lệ"
   },
   "lighthouse-core/audits/service-worker.js | explanationBadStartUrl": {
-    "message": "Trang này do một trình chạy dịch vụ kiểm soát. Tuy nhiên, `start_url` ({startUrl}) không thuộc phạm vi kiểm soát của trình chạy này ({scopeUrl})"
+    "message": "Trang này do một trình chạy dịch vụ kiểm soát. Tuy nhiên, `start_url` ({startUrl}) không thuộc phạm vi của trình chạy dịch vụ này ({scopeUrl})"
   },
   "lighthouse-core/audits/service-worker.js | explanationNoManifest": {
     "message": "Trang này do một trình chạy dịch vụ kiểm soát. Tuy nhiên, không tìm thấy `start_url` vì không tìm nạp được tệp kê khai."
   },
   "lighthouse-core/audits/service-worker.js | explanationOutOfScope": {
-    "message": "Nguồn gốc này có tối thiểu một trình chạy dịch vụ. Tuy nhiên, trang ({pageUrl}) không thuộc phạm vi kiểm soát của các trình chạy này."
+    "message": "Nguồn gốc này có một hoặc nhiều trình chạy dịch vụ. Tuy nhiên, trang ({pageUrl}) không thuộc phạm vi của các trình chạy dịch vụ này."
   },
   "lighthouse-core/audits/service-worker.js | failureTitle": {
     "message": "Không đăng ký một trình chạy dịch vụ kiểm soát trang và `start_url`"
@@ -1056,7 +1056,7 @@
     "message": "Đăng ký một trình chạy dịch vụ kiểm soát trang và `start_url`"
   },
   "lighthouse-core/audits/splash-screen.js | description": {
-    "message": "Màn hình chờ theo chủ đề giúp đảm bảo người dùng có trải nghiệm chất lượng cao khi chạy ứng dụng từ màn hình chính. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)"
+    "message": "Màn hình chờ có giao diện giúp đảm bảo người dùng có trải nghiệm chất lượng cao khi chạy ứng dụng từ màn hình chính. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)"
   },
   "lighthouse-core/audits/splash-screen.js | failureTitle": {
     "message": "Chưa được định cấu hình cho màn hình chờ tùy chỉnh"
@@ -1065,13 +1065,13 @@
     "message": "Đã định cấu hình cho màn hình chờ tùy chỉnh"
   },
   "lighthouse-core/audits/themed-omnibox.js | description": {
-    "message": "Bạn có thể đặt màu chủ đề cho thanh địa chỉ trên trình duyệt để phù hợp với trang web của mình. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/address-bar)"
+    "message": "Bạn có thể đặt giao diện cho thanh địa chỉ trên trình duyệt để phù hợp với trang web của mình. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/address-bar)"
   },
   "lighthouse-core/audits/themed-omnibox.js | failureTitle": {
-    "message": "Không đặt màu chủ đề cho thanh địa chỉ."
+    "message": "Không đặt màu giao diện cho thanh địa chỉ."
   },
   "lighthouse-core/audits/themed-omnibox.js | title": {
-    "message": "Đặt màu chủ đề cho thanh địa chỉ."
+    "message": "Đặt màu giao diện cho thanh địa chỉ."
   },
   "lighthouse-core/audits/third-party-summary.js | columnBlockingTime": {
     "message": "Thời gian chặn chuỗi chính"
@@ -1152,25 +1152,25 @@
     "message": "Có thẻ `<meta name=\"viewport\">` có `width` hoặc `initial-scale`"
   },
   "lighthouse-core/audits/without-javascript.js | description": {
-    "message": "Ứng dụng của bạn phải hiển thị nội dung nào đó khi JavaScript bị tắt, ngay cả khi đó chỉ là cảnh báo cho người dùng biết phải có JavaScript mới dùng được ứng dụng. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
+    "message": "Ứng dụng của bạn phải hiển thị một số nội dung khi JavaScript bị tắt, ngay cả khi đó chỉ là một cảnh báo cho người dùng biết phải có JavaScript mới dùng được ứng dụng. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
   },
   "lighthouse-core/audits/without-javascript.js | explanation": {
-    "message": "Phần nội dung trang phải hiển thị nội dung nào đó ngay cả khi không tải được tập lệnh"
+    "message": "Phần nội dung trang phải hiển thị một số nội dung nếu không có sẵn tập lệnh."
   },
   "lighthouse-core/audits/without-javascript.js | failureTitle": {
-    "message": "Không cung cấp nội dung dự phòng khi JavaScript không hoạt động"
+    "message": "Không cung cấp nội dung dự phòng khi JavaScript không có sẵn"
   },
   "lighthouse-core/audits/without-javascript.js | title": {
-    "message": "Chứa nội dung nào đó khi JavaScript không hoạt động"
+    "message": "Chứa một số nội dung khi JavaScript không có sẵn"
   },
   "lighthouse-core/audits/works-offline.js | description": {
     "message": "Nếu bạn đang tạo một Ứng dụng web tiến bộ, hãy cân nhắc dùng trình chạy dịch vụ để ứng dụng có thể hoạt động khi không có mạng. [Tìm hiểu thêm](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)"
   },
   "lighthouse-core/audits/works-offline.js | failureTitle": {
-    "message": "Trang hiện tại không phản hồi kèm mã trạng thái 200 khi không có mạng"
+    "message": "Trang hiện tại không trả về mã trạng thái 200 khi không có mạng"
   },
   "lighthouse-core/audits/works-offline.js | title": {
-    "message": "Trang hiện tại phản hồi kèm mã trạng thái 200 khi không có mạng"
+    "message": "Trang hiện tại trả về mã trạng thái 200 khi không có mạng"
   },
   "lighthouse-core/audits/works-offline.js | warningNoLoad": {
     "message": "Trang này có thể không tải được khi không có mạng vì URL kiểm tra ({requested}) đã được chuyển hướng tới \"{final}\". Hãy thử kiểm tra trực tiếp URL thứ hai."
@@ -1275,7 +1275,7 @@
     "message": "Những hoạt động kiểm tra này sẽ xác thực các khía cạnh của một Ứng dụng web tiến bộ. [Tìm hiểu thêm](https://developers.google.com/web/progressive-web-apps/checklist)."
   },
   "lighthouse-core/config/default-config.js | pwaCategoryManualDescription": {
-    "message": "Bạn phải tiến hành những hoạt động kiểm tra mà Lighthouse không tự động thực hiện trong [Danh sách kiểm tra ứng dụng web tiến bộ (PWA)](https://developers.google.com/web/progressive-web-apps/checklist). Các hoạt động kiểm tra này không ảnh hưởng đến điểm số của bạn, nhưng bạn phải xác minh theo cách thủ công."
+    "message": "Đây là những hoạt động kiểm tra được chỉ định trong [Danh sách kiểm tra ứng dụng web tiến bộ (PWA)](https://developers.google.com/web/progressive-web-apps/checklist) cơ bản, nhưng Lighthouse không tự động kiểm tra các mục này. Các hoạt động kiểm tra này không ảnh hưởng đến điểm số của bạn, nhưng bạn nên xác minh theo cách thủ công."
   },
   "lighthouse-core/config/default-config.js | pwaCategoryTitle": {
     "message": "Ứng dụng web tiến bộ"
diff --git a/lighthouse-core/lib/tracehouse/main-thread-tasks.js b/lighthouse-core/lib/tracehouse/main-thread-tasks.js
index 78ce49637..653ac3930 100644
--- a/lighthouse-core/lib/tracehouse/main-thread-tasks.js
+++ b/lighthouse-core/lib/tracehouse/main-thread-tasks.js
@@ -31,6 +31,7 @@ const {taskGroups, taskNameToGroup} = require('./task-groups.js');
  * @prop {LH.TraceEvent} event
  * @prop {TaskNode[]} children
  * @prop {TaskNode|undefined} parent
+ * @prop {boolean} unbounded Indicates that the task had an endTime that was inferred rather than specified in the trace. i.e. in the source trace this task was unbounded.
  * @prop {number} startTime
  * @prop {number} endTime
  * @prop {number} duration
@@ -64,6 +65,7 @@ class MainThreadTasks {
       duration: endTime - startTime,
 
       // These properties will be filled in later
+      unbounded: false,
       parent: undefined,
       children: [],
       attributableURLs: [],
@@ -181,10 +183,12 @@ class MainThreadTasks {
 
       /** @type {Pick<LH.TraceEvent, 'ph'|'ts'>} */
       let taskEndEvent;
+      let unbounded = false;
       if (matchedEventIndex === -1) {
         // If we couldn't find an end event, we'll assume it's the end of the trace.
         // If this creates invalid parent/child relationships it will be caught in the next step.
         taskEndEvent = {ph: 'E', ts: traceEndTs};
+        unbounded = true;
       } else if (matchedEventIndex === taskEndEventsReverseQueue.length - 1) {
         // Use .pop() in the common case where the immediately next event is needed.
         // It's ~25x faster, https://jsperf.com/pop-vs-splice.
@@ -193,7 +197,9 @@ class MainThreadTasks {
         taskEndEvent = taskEndEventsReverseQueue.splice(matchedEventIndex, 1)[0];
       }
 
-      tasks.push(MainThreadTasks._createNewTaskNode(taskStartEvent, taskEndEvent));
+      const task = MainThreadTasks._createNewTaskNode(taskStartEvent, taskEndEvent);
+      task.unbounded = unbounded;
+      tasks.push(task);
     }
 
     if (taskEndEventsReverseQueue.length) {
@@ -247,18 +253,29 @@ class MainThreadTasks {
         if (nextTask.endTime > currentTask.endTime) {
           const timeDelta = nextTask.endTime - currentTask.endTime;
           // The child task is taking longer than the parent task, which should be impossible.
-          //    If it's less than 1ms, we'll let it slide by increasing the duration of the parent.
-          //    If it's more, throw an error.
+          // In reality these situations happen, so we allow for some flexibility in trace event times.
           if (timeDelta < 1000) {
+            // It's less than 1ms, we'll let it slide by increasing the duration of the parent.
             currentTask.endTime = nextTask.endTime;
             currentTask.duration += timeDelta;
+          } else if (nextTask.unbounded) {
+            // It's ending at traceEndTs, it means we were missing the end event. We'll truncate it to the parent.
+            nextTask.endTime = currentTask.endTime;
+            nextTask.duration = nextTask.endTime - nextTask.startTime;
           } else {
-            // If we fell into this error, it's usually because of one of three reasons.
-            //    - We were missing an E event for a child task and we assumed the child ended at the end of the trace.
+            // None of our workarounds matched. It's time to throw an error.
+            // When we fall into this error, it's usually because of one of two reasons.
             //    - There was slop in the opposite direction (child started 1ms before parent) and the child was assumed to be parent instead.
             //    - The child timestamp ended more than 1ms after tha parent.
             // These have more complicated fixes, so handling separately https://github.com/GoogleChrome/lighthouse/pull/9491#discussion_r327331204.
-            throw new Error('Fatal trace logic error - child cannot end after parent');
+            /** @type {any} */
+            const error = new Error('Fatal trace logic error - child cannot end after parent');
+            error.timeDelta = timeDelta;
+            error.nextTaskEvent = nextTask.event;
+            error.nextTaskEndTime = nextTask.endTime;
+            error.currentTaskEvent = currentTask.event;
+            error.currentTaskEndTime = currentTask.endTime;
+            throw error;
           }
         }
 
diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js
index 7de3a0566..e848b3f7e 100644
--- a/lighthouse-core/report/html/renderer/report-ui-features.js
+++ b/lighthouse-core/report/html/renderer/report-ui-features.js
@@ -63,7 +63,6 @@ class ReportUIFeatures {
     this.onCopy = this.onCopy.bind(this);
     this.onDropDownMenuClick = this.onDropDownMenuClick.bind(this);
     this.onKeyUp = this.onKeyUp.bind(this);
-    this.onChevronClick = this.onChevronClick.bind(this);
     this.collapseAllDetails = this.collapseAllDetails.bind(this);
     this.expandAllDetails = this.expandAllDetails.bind(this);
     this._toggleDarkTheme = this._toggleDarkTheme.bind(this);
@@ -361,16 +360,6 @@ class ReportUIFeatures {
     }
   }
 
-  onChevronClick() {
-    const toggle = this._dom.find('.lh-config__settings-toggle', this._document);
-
-    if (toggle.hasAttribute('open')) {
-      toggle.removeAttribute('open');
-    } else {
-      toggle.setAttribute('open', 'true');
-    }
-  }
-
   /**
    * Resets the state of page before capturing the page for export.
    * When the user opens the exported HTML page, certain UI elements should
diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html
index 3cd8cdefc..57462c176 100644
--- a/lighthouse-core/report/html/templates.html
+++ b/lighthouse-core/report/html/templates.html
@@ -359,11 +359,34 @@ limitations under the License.
   </style>
 
   <div class="lh-topbar">
-    <!-- Flat Lighthouse logo.  -->
-    <svg class="lh-topbar__logo" viewBox="0 0 192 192">
+    <!-- Lighthouse logo.  -->
+    <svg class="lh-topbar__logo" viewBox="0 0 24 24">
+      <defs>
+        <linearGradient x1="57.456%" y1="13.086%" x2="18.259%" y2="72.322%" id="lh-topbar__logo--a">
+          <stop stop-color="#262626" stop-opacity=".1" offset="0%"/>
+          <stop stop-color="#262626" stop-opacity="0" offset="100%"/>
+        </linearGradient>
+        <linearGradient x1="100%" y1="50%" x2="0%" y2="50%" id="lh-topbar__logo--b">
+          <stop stop-color="#262626" stop-opacity=".1" offset="0%"/>
+          <stop stop-color="#262626" stop-opacity="0" offset="100%"/>
+        </linearGradient>
+        <linearGradient x1="58.764%" y1="65.756%" x2="36.939%" y2="50.14%" id="lh-topbar__logo--c">
+          <stop stop-color="#262626" stop-opacity=".1" offset="0%"/>
+          <stop stop-color="#262626" stop-opacity="0" offset="100%"/>
+        </linearGradient>
+        <linearGradient x1="41.635%" y1="20.358%" x2="72.863%" y2="85.424%" id="lh-topbar__logo--d">
+          <stop stop-color="#FFF" stop-opacity=".1" offset="0%"/>
+          <stop stop-color="#FFF" stop-opacity="0" offset="100%"/>
+        </linearGradient>
+      </defs>
       <g fill="none" fill-rule="evenodd">
-        <path d="M0 0h192v192H0z"/>
-        <path class="shape" d="M67.705 179.352l2.603-20.82 49.335-16.39 4.652 37.21A87.893 87.893 0 0 1 96 184a87.893 87.893 0 0 1-28.295-4.648zM52.44 172.48C25.894 157.328 8 128.754 8 96 8 47.399 47.399 8 96 8s88 39.399 88 88c0 32.754-17.894 61.328-44.44 76.48L130 96h6V80h-8V48L96 28 64 48v32h-8v16h6l-9.56 76.48zM113.875 96l2.882 23.05-43.318 14.433L78.125 96h35.75zM80 80V56.868l16-10 16 10V80H80z"/>
+        <path d="M12 3l4.125 2.625v3.75H18v2.25h-1.688l1.5 9.375H6.188l1.5-9.375H6v-2.25h1.875V5.648L12 3zm2.201 9.938L9.54 14.633 9 18.028l5.625-2.062-.424-3.028zM12.005 5.67l-1.88 1.207v2.498h3.75V6.86l-1.87-1.19z" fill="#F44B21"/>
+        <path fill="#FFF" d="M14.201 12.938L9.54 14.633 9 18.028l5.625-2.062z"/>
+        <path d="M6 18c-2.042 0-3.95-.01-5.813 0l1.5-9.375h4.326L6 18z" fill="url(#lh-topbar__logo--a)" fill-rule="nonzero" transform="translate(6 3)"/>
+        <path fill="#FFF176" fill-rule="nonzero" d="M13.875 9.375v-2.56l-1.87-1.19-1.88 1.207v2.543z"/>
+        <path fill="url(#lh-topbar__logo--b)" fill-rule="nonzero" d="M0 6.375h6v2.25H0z" transform="translate(6 3)"/>
+        <path fill="url(#lh-topbar__logo--c)" fill-rule="nonzero" d="M6 6.375H1.875v-3.75L6 0z" transform="translate(6 3)"/>
+        <path fill="url(#lh-topbar__logo--d)" fill-rule="nonzero" d="M6 0l4.125 2.625v3.75H12v2.25h-1.688l1.5 9.375H.188l1.5-9.375H0v-2.25h1.875V2.648z" transform="translate(6 3)"/>
       </g>
     </svg>
 
@@ -394,29 +417,6 @@ limitations under the License.
 <!-- Lighthouse header -->
 <template id="tmpl-lh-heading">
   <style>
-/*
-    TODO: Enable animating the clouds
-    .lh-lighthouse__clouds {
-      animation: panacross 30s linear infinite;
-      animation-play-state: paused;
-    }
-    @keyframes panacross {
-      0% { transform: translateX(0px); }
-      77% { transform: translateX(-680px); }
-      77.0001% { transform: translateX(195px); }
-      100% { transform: translateX(0px); }
-    } */
-
-    .score100 .lh-header-bg {
-      background-color: hsl(234, 64%, 19%);
-    }
-    .score100 .lh-metadata, .score100 .lh-toolbar__metadata, .score100 .lh-product-info {
-      color: #fff;
-    }
-    .score100 .lh-config {
-      color: #eee;
-    }
-
     /* CSS Fireworks. Originally by Eddie Lin
        https://codepen.io/paulirish/pen/yEVMbP
     */
diff --git a/lighthouse-core/scripts/compare-timings.js b/lighthouse-core/scripts/compare-timings.js
index f04b39911..a8f6f8d4a 100644
--- a/lighthouse-core/scripts/compare-timings.js
+++ b/lighthouse-core/scripts/compare-timings.js
@@ -8,6 +8,7 @@
 // Example:
 //     node lighthouse-core/scripts/compare-timings.js --name my-collection --collect -n 3 --lh-flags='--only-audits=unminified-javascript' --urls https://www.example.com https://www.nyt.com
 //     node lighthouse-core/scripts/compare-timings.js --name my-collection --summarize --measure-filter 'loadPage|connect'
+//     node lighthouse-core/scripts/compare-timings.js --name base --name pr --compare
 
 const fs = require('fs');
 const {execSync} = require('child_process');
@@ -19,7 +20,9 @@ const ROOT_OUTPUT_DIR = `${LH_ROOT}/timings-data`;
 const argv = yargs
   .help('help')
   .describe({
+    // common flags
     'name': 'Unique identifier, makes the folder for storing LHRs. Not a path',
+    'report-exclude': 'Regex of properties to exclude. Set to "none" to disable default',
     // --collect
     'collect': 'Saves LHRs to disk',
     'lh-flags': 'Lighthouse flags',
@@ -27,18 +30,33 @@ const argv = yargs
     'n': 'Number of times to run',
     // --summarize
     'summarize': 'Prints statistics report',
-    'measure-filter': 'Regex filter of measures to report. Optional',
+    'measure-filter': 'Regex of measures to include. Optional',
     'output': 'table, json',
+    // --compare
+    'compare': 'Compare two sets of LHRs',
+    'delta-property-sort': 'Property to sort by its delta',
+    'desc': 'Set to override default ascending sort',
   })
   .string('measure-filter')
+  .default('report-exclude', 'min|max|stdev|^n$')
+  .default('delta-property-sort', 'mean')
   .default('output', 'table')
   .array('urls')
   .string('lh-flags')
+  .default('desc', false)
   .default('lh-flags', '')
   .wrap(yargs.terminalWidth())
 .argv;
 
-const outputDir = `${ROOT_OUTPUT_DIR}/${argv.name}`;
+const reportExcludeRegex =
+  argv.reportExclude !== 'none' ? new RegExp(argv.reportExclude, 'i') : null;
+
+/**
+ * @param {string} name
+ */
+function dir(name) {
+  return `${ROOT_OUTPUT_DIR}/${name}`;
+}
 
 /**
  * @param {number[]} values
@@ -72,6 +90,7 @@ function round(value) {
 }
 
 function collect() {
+  const outputDir = dir(argv.name);
   if (!fs.existsSync(ROOT_OUTPUT_DIR)) fs.mkdirSync(ROOT_OUTPUT_DIR);
   if (fs.existsSync(outputDir)) throw new Error(`folder already exists: ${outputDir}`);
   fs.mkdirSync(outputDir);
@@ -91,11 +110,15 @@ function collect() {
   }
 }
 
-function summarize() {
+/**
+ * @param {string} name
+ */
+function aggregateResults(name) {
+  const outputDir = dir(name);
+
   // `${url}@@@${entry.name}` -> duration
   /** @type {Map<string, number[]>} */
   const durationsMap = new Map();
-  /** @type {RegExp|null} */
   const measureFilter = argv.measureFilter ? new RegExp(argv.measureFilter, 'i') : null;
 
   for (const lhrPath of fs.readdirSync(outputDir)) {
@@ -127,13 +150,14 @@ function summarize() {
     }
   }
 
-  const results = [...durationsMap].map(([key, durations]) => {
+  return [...durationsMap].map(([key, durations]) => {
     const [url, entryName] = key.split('@@@');
     const mean = average(durations);
     const min = Math.min(...durations);
     const max = Math.max(...durations);
     const stdev = sampleStdev(durations);
     return {
+      key,
       measure: entryName,
       url,
       n: durations.length,
@@ -148,7 +172,104 @@ function summarize() {
     if (measureComp !== 0) return measureComp;
     return a.url.localeCompare(b.url);
   });
+}
+
+/**
+ * @param {*[]} results
+ */
+function filter(results) {
+  if (!reportExcludeRegex) return;
+
+  for (const result of results) {
+    for (const key in result) {
+      if (reportExcludeRegex.test(key)) delete result[key];
+    }
+  }
+}
+
+/**
+ * @param {number=} value
+ * @return {value is number}
+ */
+function exists(value) {
+  return typeof value !== 'undefined';
+}
+
+function summarize() {
+  const results = aggregateResults(argv.name);
+  filter(results);
+  print(results);
+}
+
+/**
+ * @param {number=} base
+ * @param {number=} other
+ */
+function compareValues(base, other) {
+  const basePart = exists(base) ? base : 'N/A';
+  const otherPart = exists(other) ? other : 'N/A';
+  return {
+    description: `${basePart} -> ${otherPart}`,
+    delta: exists(base) && exists(other) ? (other - base) : undefined,
+  };
+}
+
+function compare() {
+  if (!Array.isArray(argv.name) || argv.name.length !== 2) {
+    throw new Error('expected two entries for name option');
+  }
+
+  const baseResults = aggregateResults(argv.name[0]);
+  const otherResults = aggregateResults(argv.name[1]);
+
+  const keys = [...new Set([...baseResults.map(r => r.key), ...otherResults.map(r => r.key)])];
+  const results = keys.map(key => {
+    const baseResult = baseResults.find(result => result.key === key);
+    const otherResult = otherResults.find(result => result.key === key);
+
+    const someResult = baseResult || otherResult;
+    if (!someResult) throw new Error('impossible');
+
+    const mean = compareValues(baseResult && baseResult.mean, otherResult && otherResult.mean);
+    const stdev = compareValues(baseResult && baseResult.stdev, otherResult && otherResult.stdev);
+    const min = compareValues(baseResult && baseResult.min, otherResult && otherResult.min);
+    const max = compareValues(baseResult && baseResult.max, otherResult && otherResult.max);
+
+    return {
+      'measure': someResult.measure,
+      'url': someResult.url,
+      'mean': mean.description,
+      'mean Δ': exists(mean.delta) ? round(mean.delta) : undefined,
+      'stdev': stdev.description,
+      'stdev Δ': exists(stdev.delta) ? round(stdev.delta) : undefined,
+      'min': min.description,
+      'min Δ': exists(min.delta) ? round(min.delta) : undefined,
+      'max': max.description,
+      'max Δ': exists(max.delta) ? round(max.delta) : undefined,
+    };
+  });
+
+  const sortByKey = `${argv.deltaPropertySort} Δ`;
+  results.sort((a, b) => {
+    // @ts-ignore - shhh tsc.
+    const aValue = a[sortByKey];
+    // @ts-ignore - shhh tsc.
+    const bValue = b[sortByKey];
+
+    // Always put the keys missing a result at the bottom of the table.
+    if (!exists(aValue)) return 1;
+    else if (!exists(bValue)) return -1;
 
+    return (argv.desc ? 1 : -1) * (Math.abs(aValue) - Math.abs(bValue));
+  });
+  filter(results);
+  print(results);
+}
+
+/**
+ * @param {*[]} results
+ */
+function print(results) {
   if (argv.output === 'table') {
     // eslint-disable-next-line no-console
     console.table(results);
@@ -161,6 +282,7 @@ function summarize() {
 function main() {
   if (argv.collect) collect();
   if (argv.summarize) summarize();
+  if (argv.compare) compare();
 }
 
 main();
diff --git a/package.json b/package.json
index 08a421912..730aab34a 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
     "debug": "node --inspect-brk ./lighthouse-cli/index.js",
     "start": "node ./lighthouse-cli/index.js",
     "test": "yarn diff:sample-json && yarn lint --quiet && yarn unit && yarn type-check",
+    "test-bundle": "node build/tests/bundle-smoke-test.js",
     "test-clients": "jest \"clients/\"",
     "test-viewer": "yarn unit-viewer && jest lighthouse-viewer/test/viewer-test-pptr.js",
     "test-lantern": "bash lighthouse-core/scripts/test-lantern.sh",
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index 63b56c509..bd7eeb017 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -375,7 +375,7 @@
             "id": "critical-request-chains",
             "score": null,
             "scoreDisplayMode": "informative",
-            "title": "Minimize Critical Requests Depth"
+            "title": "Avoid chaining critical requests"
         },
         "custom-controls-labels": {
             "description": "Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://web.dev/custom-controls-labels/).",
@@ -2607,7 +2607,7 @@
             "id": "third-party-summary",
             "score": 1.0,
             "scoreDisplayMode": "binary",
-            "title": "Third-Party usage"
+            "title": "Minimize third-party usage"
         },
         "time-to-first-byte": {
             "description": "Time To First Byte identifies the time at which your server sends a response. [Learn more](https://web.dev/time-to-first-byte).",
diff --git a/readme.md b/readme.md
index 925c123e4..efea1341d 100644
--- a/readme.md
+++ b/readme.md
@@ -301,6 +301,8 @@ This section details services that have integrated Lighthouse data. If you're wo
 
 * **[Web Page Test](https://www.webpagetest.org)** — An [open source](https://github.com/WPO-Foundation/webpagetest) tool for measuring and analyzing the performance of web pages on real devices. Users can choose to produce a Lighthouse report alongside the analysis of WebPageTest results.
 
+* **[is-website-vulnerable](https://github.com/lirantal/is-website-vulnerable)** - An open source Node.js CLI tool that finds publicly known security vulnerabilities in a website's frontend JavaScript libraries.
+
 ## Plugins
 
 * **[lighthouse-plugin-field-performance](https://github.com/treosh/lighthouse-plugin-field-performance)** - a plugin that adds real-user performance metrics for the URL using the data from [Chrome UX Report](https://developers.google.com/web/tools/chrome-user-experience-report/).
@@ -309,11 +311,14 @@ This section details services that have integrated Lighthouse data. If you're wo
 Other awesome open source projects that use Lighthouse.
 
 * **[Exthouse](https://github.com/treosh/exthouse)** - Analyze the impact of a browser extension on web performance.
-* **[Garie](https://github.com/boyney123/garie)** — An open source tool for monitoring performance using Lighthouse,  PageSpeed Insights, [Prometheus](https://prometheus.io/), [Grafana](https://grafana.com/) and [Docker](https://www.docker.com/).
+* **[Garie](https://github.com/boyney123/garie)** - An open source tool for monitoring performance using Lighthouse,  PageSpeed Insights, [Prometheus](https://prometheus.io/), [Grafana](https://grafana.com/) and [Docker](https://www.docker.com/).
 * **[Gimbal](https://labs.moduscreate.com/gimbal-web-performance-audit-budgeting)** - An [open source (MIT licensed)](https://github.com/ModusCreateOrg/gimbal) tool used to measure, analyze, and budget aspects of a web application. Gimbal also integrates reports with GitHub pull requests.
+* **[Gradle Lighthouse Plugin](https://github.com/Cognifide/gradle-lighthouse-plugin)** - An open source Gradle plugin that runs Lighthouse tests on multiple URLs and asserts category score thresholds (useful in continuous integration).
 * **[lightcrawler](https://github.com/github/lightcrawler)** - Crawl a website and run each page found through Lighthouse.
 * **[lighthouse-badges](https://github.com/emazzotta/lighthouse-badges)** - Generate gh-badges (shields.io) based on Lighthouse performance.
 * **[lighthouse-batch](https://www.npmjs.com/package/lighthouse-batch)** - Run Lighthouse over a number of sites and generate a summary of their metrics/scores.
+* **[lighthouse-check-action](https://github.com/foo-software/lighthouse-check-action)** - A Github Action to run Lighthouse in a workflow, featuring Slack notifications and report upload to S3.
+* **[lighthouse-check-orb](https://circleci.com/orbs/registry/orb/foo-software/lighthouse-check)** - A CircleCI Orb to run Lighthouse in a workflow, featuring Slack notifications and report upload to S3.
 * **[lighthouse-ci](https://github.com/andreasonny83/lighthouse-ci)** - Run Lighthouse and assert scores satisfy your custom thresholds.
 * **[lighthouse-ci-action](https://github.com/treosh/lighthouse-ci-action)** - A Github Action that makes it easy to run Lighthouse in CI and keep your pages small using performance budgets.
 * **[lighthouse-cron](https://github.com/thearegee/lighthouse-cron)** - Cron multiple batch Lighthouse audits and emit results for sending to remote server.
diff --git a/third-party/snyk/snapshot.json b/third-party/snyk/snapshot.json
index 79284f279..1765ffeba 100644
--- a/third-party/snyk/snapshot.json
+++ b/third-party/snyk/snapshot.json
@@ -1,25 +1,28 @@
 {
   "npm":{
     "angular":[
+      {"id":"SNYK-JS-ANGULAR-471885","severity":"medium","semver":{"vulnerable":["<1.6.3"]}},
+      {"id":"SNYK-JS-ANGULAR-471882","severity":"medium","semver":{"vulnerable":["<1.6.5"]}},
+      {"id":"SNYK-JS-ANGULAR-471879","severity":"medium","semver":{"vulnerable":["<1.6.0-rc.0"]}},
       {"id":"npm:angular:20180202","severity":"medium","semver":{"vulnerable":["<1.6.9"]}},
       {"id":"npm:angular:20171018","severity":"medium","semver":{"vulnerable":["<1.6.7"]}},
-      {"id":"npm:angular:20160527","severity":"medium","semver":{"vulnerable":["<1.2.30 >=1.0.0"]}},
-      {"id":"npm:angular:20160122","severity":"medium","semver":{"vulnerable":["<1.5.0-rc.2 >=1.3.0"]}},
+      {"id":"npm:angular:20160527","severity":"medium","semver":{"vulnerable":[">=1.0.0 <1.2.30"]}},
+      {"id":"npm:angular:20160122","severity":"medium","semver":{"vulnerable":[">=1.3.0 <1.5.0-rc.2"]}},
       {"id":"npm:angular:20140608","severity":"low","semver":{"vulnerable":["<1.3.0"]}},
       {"id":"npm:angular:20131113","severity":"high","semver":{"vulnerable":["<1.2.2"]}},
       {"id":"npm:angular:20140908","severity":"medium","semver":{"vulnerable":["<1.3.0-rc.4"]}},
-      {"id":"npm:angular:20161101","severity":"medium","semver":{"vulnerable":["<1.5.9 >=1.5.0"]}},
+      {"id":"npm:angular:20161101","severity":"medium","semver":{"vulnerable":[">=1.5.0 <1.5.9"]}},
       {"id":"npm:angular:20150909","severity":"high","semver":{"vulnerable":["<1.5.0-beta.2"]}},
       {"id":"npm:angular:20151205","severity":"medium","semver":{"vulnerable":["<1.5.0-rc.0"]}},
       {"id":"npm:angular:20151130","severity":"medium","semver":{"vulnerable":["<1.4.10"]}},
-      {"id":"npm:angular:20130622","severity":"medium","semver":{"vulnerable":["<1.2.0 >=1.0.0"]}},
-      {"id":"npm:angular:20150807-1","severity":"medium","semver":{"vulnerable":["<1.5.0-beta.0 >=1.3.1"]}},
-      {"id":"npm:angular:20150807","severity":"high","semver":{"vulnerable":["<1.5.0-beta.0 >=1.0.0"]}},
+      {"id":"npm:angular:20130622","severity":"medium","semver":{"vulnerable":[">=1.0.0 <1.2.0"]}},
+      {"id":"npm:angular:20150807-1","severity":"medium","semver":{"vulnerable":[">=1.3.1 <1.5.0-beta.0"]}},
+      {"id":"npm:angular:20150807","severity":"high","semver":{"vulnerable":[">=1.0.0 <1.5.0-beta.0"]}},
       {"id":"npm:angular:20150315","severity":"medium","semver":{"vulnerable":["<1.6.1"]}},
       {"id":"npm:angular:20150310","severity":"high","semver":{"vulnerable":["<1.5.0-beta.2"]}},
       {"id":"npm:angular:20141104","severity":"medium","semver":{"vulnerable":["<1.3.2"]}},
-      {"id":"npm:angular:20130621","severity":"medium","semver":{"vulnerable":["<=1.1.5"]}},
-      {"id":"npm:angular:20140909","severity":"high","semver":{"vulnerable":["<1.2.24 >=1.2.19"]}},
+      {"id":"npm:angular:20130621","severity":"medium","semver":{"vulnerable":["<1.2.0"]}},
+      {"id":"npm:angular:20140909","severity":"high","semver":{"vulnerable":[">=1.2.19 <1.2.24"]}},
       {"id":"npm:angular:20130625","severity":"high","semver":{"vulnerable":["<1.1.5"]}}
     ],
     "backbone":[
@@ -36,9 +39,11 @@
       {"id":"npm:bootstrap:20120510","severity":"medium","semver":{"vulnerable":["<2.1.0"]}}
     ],
     "dojo":[
+      {"id":"SNYK-JS-DOJO-174934","severity":"medium","semver":{"vulnerable":[">=1.0.0 <1.0.3",">=1.1.0 <1.1.2",">=1.2.0 <1.2.4",">=1.3.0 <1.3.3",">=1.4.0 <1.4.2"]}},
+      {"id":"SNYK-JS-DOJO-174933","severity":"medium","semver":{"vulnerable":["<1.2.0"]}},
       {"id":"SNYK-JS-DOJO-72305","severity":"medium","semver":{"vulnerable":["<1.14"]}},
       {"id":"npm:dojo:20180818","severity":"medium","semver":{"vulnerable":["<1.10.10 || >=1.11.0 <1.11.6 || >=1.12.0 <1.12.4 || >=1.13.0 <1.13.1"]}},
-      {"id":"npm:dojo:20160523","severity":"medium","semver":{"vulnerable":["<= 1.0.0"]}},
+      {"id":"npm:dojo:20160523","severity":"medium","semver":{"vulnerable":["<1.1.0"]}},
       {"id":"npm:dojo:20100614-6","severity":"medium","semver":{"vulnerable":["<1.4.2"]}},
       {"id":"npm:dojo:20100614","severity":"high","semver":{"vulnerable":[">=0.4 <0.4.4 || >=1.0 <1.0.3 || >=1.1 <1.1.2 || >=1.2 <1.2.4 || >=1.3 <1.3.3 || >=1.4 <1.4.2"]}},
       {"id":"npm:dojo:20090409","severity":"medium","semver":{"vulnerable":["<1.1"]}}
@@ -48,8 +53,12 @@
       {"id":"npm:foundation-sites:20150619","severity":"medium","semver":{"vulnerable":["<5.5.3"]}},
       {"id":"npm:foundation-sites:20120717","severity":"medium","semver":{"vulnerable":["<3.0.6 >=3.0.0"]}}
     ],
+    "google-closure-library":[
+      {"id":"SNYK-JS-GOOGLECLOSURELIBRARY-174519","severity":"medium","semver":{"vulnerable":[">=20190121.0.0 <20190301.0.0"]}}
+    ],
     "handlebars":[
-      {"id":"SNYK-JS-HANDLEBARS-174183","severity":"high","semver":{"vulnerable":[">=4.1.0 <4.1.2","<4.0.14"]}},
+      {"id":"SNYK-JS-HANDLEBARS-469063","severity":"high","semver":{"vulnerable":["<4.3.0"]}},
+      {"id":"SNYK-JS-HANDLEBARS-174183","severity":"high","semver":{"vulnerable":["<3.0.7",">=4.1.0 <4.1.2","<4.0.14"]}},
       {"id":"SNYK-JS-HANDLEBARS-173692","severity":"high","semver":{"vulnerable":["<4.0.13"]}},
       {"id":"npm:handlebars:20151207","severity":"medium","semver":{"vulnerable":["<4.0.0"]}},
       {"id":"npm:handlebars:20110425","severity":"medium","semver":{"vulnerable":["<=1.0.0-beta.3"]}}
@@ -59,25 +68,27 @@
     ],
     "jquery":[
       {"id":"SNYK-JS-JQUERY-174006","severity":"medium","semver":{"vulnerable":["<3.4.0"]}},
-      {"id":"npm:jquery:20160529","severity":"low","semver":{"vulnerable":["=3.0.0-rc1"]}},
+      {"id":"npm:jquery:20160529","severity":"low","semver":{"vulnerable":[">=3.0.0-rc1 <3.0.0"]}},
       {"id":"npm:jquery:20150627","severity":"medium","semver":{"vulnerable":["<1.12.2",">=1.12.3 <2.2.2",">=2.2.3 <3.0.0"]}},
       {"id":"npm:jquery:20140902","severity":"medium","semver":{"vulnerable":[">=1.4.2 <1.6.2"]}},
       {"id":"npm:jquery:20120206","severity":"medium","semver":{"vulnerable":[">=1.7.1 <1.9.0"]}},
       {"id":"npm:jquery:20110606","severity":"medium","semver":{"vulnerable":["<1.6.3"]}}
     ],
     "jquery-mobile":[
+      {"id":"SNYK-JS-JQUERYMOBILE-174599","severity":"medium","semver":{"vulnerable":["<=1.5.0-alpha.1"]}},
       {"id":"npm:jquery-mobile:20120802","severity":"medium","semver":{"vulnerable":["<1.2.0"]}}
     ],
     "jquery-ui":[
       {"id":"npm:jquery-ui:20121127","severity":"medium","semver":{"vulnerable":["<1.10.0"]}},
       {"id":"npm:jquery-ui:20100903","severity":"medium","semver":{"vulnerable":["<1.10.0"]}},
-      {"id":"npm:jquery-ui:20160721","severity":"high","semver":{"vulnerable":["<=1.11.4"]}}
+      {"id":"npm:jquery-ui:20160721","severity":"high","semver":{"vulnerable":["<1.12.0"]}}
     ],
     "knockout":[
       {"id":"npm:knockout:20180213","severity":"medium","semver":{"vulnerable":["<3.5.0-beta"]}},
       {"id":"npm:knockout:20130701","severity":"medium","semver":{"vulnerable":["<3.0.0 >=2.1.0-pre"]}}
     ],
     "lodash":[
+      {"id":"SNYK-JS-LODASH-450202","severity":"high","semver":{"vulnerable":["<4.17.12"]}},
       {"id":"SNYK-JS-LODASH-73639","severity":"medium","semver":{"vulnerable":["<4.17.11"]}},
       {"id":"SNYK-JS-LODASH-73638","severity":"high","semver":{"vulnerable":["<4.17.11"]}},
       {"id":"npm:lodash:20180130","severity":"medium","semver":{"vulnerable":["<4.17.5"]}}
@@ -85,7 +96,7 @@
     "moment":[
       {"id":"npm:moment:20170905","severity":"low","semver":{"vulnerable":["<2.19.3"]}},
       {"id":"npm:moment:20161019","severity":"medium","semver":{"vulnerable":["<2.15.2"]}},
-      {"id":"npm:moment:20160126","severity":"medium","semver":{"vulnerable":["<=2.11.1"]}}
+      {"id":"npm:moment:20160126","severity":"medium","semver":{"vulnerable":["<2.11.2"]}}
     ],
     "mustache":[
       {"id":"npm:mustache:20151207","severity":"medium","semver":{"vulnerable":["<2.2.1"]}},
@@ -106,7 +117,7 @@
       {"id":"npm:vue:20170829","severity":"medium","semver":{"vulnerable":["<2.4.3"]}},
       {"id":"npm:vue:20170401","severity":"medium","semver":{"vulnerable":["<2.3.0-beta.1"]}},
       {"id":"npm:vue:20180802","severity":"medium","semver":{"vulnerable":["<2.5.17"]}},
-      {"id":"npm:vue:20180222","severity":"low","semver":{"vulnerable":["<=2.5.14"]}}
+      {"id":"npm:vue:20180222","severity":"low","semver":{"vulnerable":["<2.5.14"]}}
     ],
     "yui":[
       {"id":"npm:yui:20130604","severity":"medium","semver":{"vulnerable":[">=3.0.0 <3.10.1 || =3.10.2"]}},
diff --git a/types/smokehouse.d.ts b/types/smokehouse.d.ts
index a512b481d..4f6863ecd 100644
--- a/types/smokehouse.d.ts
+++ b/types/smokehouse.d.ts
@@ -41,4 +41,11 @@
     config: LH.Config.Json;
     batch: string;
   }
+
+  export interface FirehouseOptions {
+    runLighthouse: (url: string, config: LH.Config.Json) => Promise<Omit<LH.RunnerResult, 'report'>>;
+    urlFilterRegex?: RegExp;
+    skip?: (test: TestDfn, expectation: ExpectedRunnerResult) => string | false;
+    modify?: (test: TestDfn, expectation: ExpectedRunnerResult) => void;
+  }
 }


Test patch:
diff --git a/build/tests/bundle-smoke-test.js b/build/tests/bundle-smoke-test.js
new file mode 100644
index 000000000..59dbc076d
--- /dev/null
+++ b/build/tests/bundle-smoke-test.js
@@ -0,0 +1,66 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+const fs = require('fs');
+const {promisify} = require('util');
+const {execFile} = require('child_process');
+const execFileAsync = promisify(execFile);
+const firehouse = require('../../lighthouse-cli/test/smokehouse/firehouse.js');
+const bundleBuilder = require('../build-bundle.js');
+const {server, serverForOffline} = require('../../lighthouse-cli/test/fixtures/static-server.js');
+
+const testEntryPath = `${__dirname}/../../lighthouse-core/index.js`;
+const testDistPath = `${__dirname}/../../dist/test-bundle.js`;
+
+/**
+ * Run Lighthouse using a CLI that shims lighthouse-core with the output of the bundler.
+ * @param {string} url
+ * @param {LH.Config.Json} config
+ */
+async function runLighthouseFromMinifiedBundle(url, config) {
+  const configPath = `${__dirname}/../../.tmp/bundle-smoke-test-config.json`;
+  const lhrPath = `${__dirname}/../../.tmp/bundle-smoke-test-lhr.json`;
+  const gatherPath = `${__dirname}/../../.tmp/bundle-smoke-test-gather`;
+
+  fs.writeFileSync(configPath, JSON.stringify(config));
+
+  await execFileAsync('node', [
+    `${__dirname}/bundled-lighthouse-cli.js`,
+    url,
+    `--config-path=${configPath}`,
+    `-GA=${gatherPath}`,
+    '--output=json',
+    `--output-path=${lhrPath}`,
+  ]);
+
+  const lhr = JSON.parse(fs.readFileSync(lhrPath, 'utf-8'));
+  const artifacts = JSON.parse(fs.readFileSync(`${gatherPath}/artifacts.json`, 'utf-8'));
+
+  return {
+    lhr,
+    artifacts,
+  };
+}
+
+async function main() {
+  await bundleBuilder.build(testEntryPath, testDistPath);
+
+  server.listen(10200, 'localhost');
+  serverForOffline.listen(10503, 'localhost');
+
+  const results = await firehouse.runSmokes({
+    runLighthouse: runLighthouseFromMinifiedBundle,
+    urlFilterRegex: /byte|dbw/,
+  });
+
+  await new Promise(resolve => server.close(resolve));
+  await new Promise(resolve => serverForOffline.close(resolve));
+
+  process.exit(results.success ? 0 : 1);
+}
+
+main();
diff --git a/build/tests/bundled-lighthouse-cli.js b/build/tests/bundled-lighthouse-cli.js
new file mode 100644
index 000000000..2b4958e33
--- /dev/null
+++ b/build/tests/bundled-lighthouse-cli.js
@@ -0,0 +1,104 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+/**
+ * @fileoverview Used to smoke test the build process.
+ *
+ * - The bundled code is a function that, given a module ID, returns the exports of that module.
+ * - We eval the bundle string to get a reference to this function (with some global hacks to
+ *   support unbundleable things).
+ * - We try to locate the lighthouse-core/index.js module by executing this function on every
+ *   possible number. This version of lighthouse-core/index.js will be wired to use all of the
+ *   bundled modules, not node requires.
+ * - Once we find the bundled lighthouse-core/index.js module, we stick it in node's require.cache
+ *   so that all node require invocations for lighthouse-core/index.js will use our bundled module
+ *   instead of the regular one.
+ * - Finally, we kick off the lighthouse-cli/index.js entrypoint that ends up requiring the
+ *   now-replaced lighthouse-core/index.js for its run.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const mkdirp = require('mkdirp');
+const rimraf = require('rimraf');
+const ChromeProtocol = require('../../lighthouse-core/gather/connections/cri.js');
+
+const LH_ROOT = path.resolve(__dirname, '../..');
+const corePath = `${LH_ROOT}/lighthouse-core/index.js`;
+
+// Oh yeahhhhh this works. Think of this as `requireBundled('../../lighthouse-core/index.js')`.
+const lighthouse = (function getLighthouseCoreBundled() {
+  // The eval will assign to `require`. Normally, that would be the require on the global object.
+  // This `let` protects the global reference to the native require.
+  // Doesn't need to have any value, but for good measure define a function that explicitly forbids
+  // its own usage.
+  // To be further convinced that this works (that the un-bundled files are not being loaded),
+  // add some console.log's somewhere like `driver.js`, and
+  // run `node build/tests/bundled-lighthouse-cli.js https://www.example.com`. You won't see them.
+  /* eslint-disable-next-line */
+  let require = () => {
+    throw new Error('illegal require');
+  };
+
+  const lighthouseBundledCode = fs.readFileSync('dist/test-bundle.js', 'utf-8')
+    // Some modules are impossible to bundle. So we cheat by leaning on global.
+    // cri.js will be able to use native require. It's a minor defect - it means that some usages
+    // of lh-error.js will not come from the bundled code.
+    // TODO: use `globalThis` when we drop Node 10.
+    .replace('new ChromeProtocol', 'new global.ChromeProtocol')
+    // Needed for asset-saver.js.
+    .replace(/mkdirp\./g, 'global.mkdirp.')
+    .replace(/rimraf\./g, 'global.rimraf.')
+    .replace(/fs\.(writeFileSync|createWriteStream)/g, 'global.$&');
+
+  /* eslint-disable no-undef */
+  // @ts-ignore
+  global.ChromeProtocol = ChromeProtocol;
+  // @ts-ignore
+  global.mkdirp = mkdirp;
+  // @ts-ignore
+  global.rimraf = rimraf;
+  // @ts-ignore
+  global.fs = fs;
+  /*  eslint-enable no-undef */
+
+  const bundledLighthouseRequire = eval(lighthouseBundledCode);
+
+  // Find the lighthouse module.
+  // Every module is given an id (starting at 1). The core lighthouse module
+  // is the only module that is a function named `lighthouse`.
+  /** @type {import('../../lighthouse-core/index.js')} */
+  let lighthouse;
+  for (let i = 1; i < 1000; i++) {
+    const module = bundledLighthouseRequire(i);
+    if (module.name === 'lighthouse') {
+      lighthouse = module;
+      break;
+    }
+  }
+
+  // @ts-ignore
+  if (!lighthouse) throw new Error('could not find lighthouse module');
+
+  return lighthouse;
+})();
+
+// Shim the core module with the bundled code.
+
+// @ts-ignore
+lighthouse.__PATCHED__ = true;
+require.cache[corePath] = {
+  exports: lighthouse,
+};
+
+// @ts-ignore
+if (!require('../../lighthouse-core/index.js').__PATCHED__) {
+  throw new Error('error patching core module');
+}
+
+// Kick off the CLI.
+require('../../lighthouse-cli/index.js');
diff --git a/lighthouse-cli/test/smokehouse/firehouse.js b/lighthouse-cli/test/smokehouse/firehouse.js
new file mode 100644
index 000000000..10f7309ca
--- /dev/null
+++ b/lighthouse-cli/test/smokehouse/firehouse.js
@@ -0,0 +1,68 @@
+/**
+ * @license Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+/**
+ * @fileoverview Smoke test runner.
+ * Used to test channels other than npm (`run-smoke.js` handles that).
+ * Supports skipping and modifiying expectations to match the environment.
+ */
+
+/* eslint-disable no-console */
+
+const log = require('lighthouse-logger');
+const smokeTests = require('./smoke-test-dfns.js');
+const {collateResults, report} = require('./smokehouse-report.js');
+
+/**
+ * @param {Smokehouse.FirehouseOptions} options
+ */
+async function runSmokes(options) {
+  const {runLighthouse, urlFilterRegex, skip, modify} = options;
+
+  let passingCount = 0;
+  let failingCount = 0;
+
+  for (const test of smokeTests) {
+    for (const expected of test.expectations) {
+      if (urlFilterRegex && !expected.lhr.requestedUrl.match(urlFilterRegex)) {
+        continue;
+      }
+
+      console.log(`====== ${expected.lhr.requestedUrl} ======`);
+      const reasonToSkip = skip && skip(test, expected);
+      if (reasonToSkip) {
+        console.log(`skipping: ${reasonToSkip}`);
+        continue;
+      }
+
+      modify && modify(test, expected);
+      const results = await runLighthouse(expected.lhr.requestedUrl, test.config);
+      console.log(`Asserting expected results match those found. (${expected.lhr.requestedUrl})`);
+      const collated = collateResults(results, expected);
+      const counts = report(collated);
+      passingCount += counts.passed;
+      failingCount += counts.failed;
+    }
+  }
+
+  if (passingCount) {
+    console.log(log.greenify(`${passingCount} passing`));
+  }
+  if (failingCount) {
+    console.log(log.redify(`${failingCount} failing`));
+  }
+
+  return {
+    success: passingCount > 0 && failingCount === 0,
+    passingCount,
+    failingCount,
+  };
+}
+
+module.exports = {
+  runSmokes,
+};
diff --git a/lighthouse-core/scripts/release/test.sh b/lighthouse-core/scripts/release/test.sh
index fb568567c..7ca22095f 100755
--- a/lighthouse-core/scripts/release/test.sh
+++ b/lighthouse-core/scripts/release/test.sh
@@ -23,9 +23,6 @@ yarn build-all
 echo "Running the standard test suite..."
 yarn test
 
-echo "Running the smoke tests...."
-yarn smoke || yarn smoke || yarn smoke
-
 echo "Testing the CLI..."
 yarn start "https://example.com" --view
 
@@ -39,7 +36,7 @@ cd /tmp/lighthouse-local-test
 
 npm init -y
 npm install "$LH_PRISTINE_ROOT/lighthouse-$VERSION.tgz"
-npm explore lighthouse -- npm run smoke
+npm explore lighthouse -- npm run smoke || npm explore lighthouse -- npm run smoke
 npm explore lighthouse -- npm run chrome # try the manual launcher
 npm explore lighthouse -- npm run fast -- http://example.com
 
diff --git a/lighthouse-core/test/lib/tracehouse/main-thread-tasks-test.js b/lighthouse-core/test/lib/tracehouse/main-thread-tasks-test.js
index a4a28283c..ad8bd8b5f 100644
--- a/lighthouse-core/test/lib/tracehouse/main-thread-tasks-test.js
+++ b/lighthouse-core/test/lib/tracehouse/main-thread-tasks-test.js
@@ -99,6 +99,7 @@ describe('Main Thread Tasks', () => {
       duration: 100,
       selfTime: 50,
       group: taskGroups.other,
+      unbounded: false,
     });
 
     expect(taskB).toEqual({
@@ -112,6 +113,7 @@ describe('Main Thread Tasks', () => {
       duration: 50,
       selfTime: 20,
       group: taskGroups.other,
+      unbounded: false,
     });
   });
 
@@ -232,6 +234,7 @@ describe('Main Thread Tasks', () => {
       duration: 110,
       selfTime: 5,
       group: taskGroups.other,
+      unbounded: true,
     });
 
     expect(taskB).toEqual({
@@ -245,6 +248,7 @@ describe('Main Thread Tasks', () => {
       duration: 105,
       selfTime: 95,
       group: taskGroups.other,
+      unbounded: true,
     });
   });
 
@@ -362,6 +366,7 @@ describe('Main Thread Tasks', () => {
         duration: 100,
         selfTime: 50,
         group: taskGroups.other,
+        unbounded: false,
       },
       {
         parent: taskA,
@@ -374,6 +379,7 @@ describe('Main Thread Tasks', () => {
         duration: 50,
         selfTime: 50,
         group: taskGroups.other,
+        unbounded: false,
       },
     ]);
   });
@@ -406,6 +412,7 @@ describe('Main Thread Tasks', () => {
         duration: 100,
         selfTime: 100,
         group: taskGroups.other,
+        unbounded: false,
       },
       {
         parent: undefined,
@@ -418,6 +425,7 @@ describe('Main Thread Tasks', () => {
         duration: 0,
         selfTime: 0,
         group: taskGroups.other,
+        unbounded: false,
       },
     ]);
   });
@@ -452,6 +460,7 @@ describe('Main Thread Tasks', () => {
         duration: 100,
         selfTime: 25,
         group: taskGroups.other,
+        unbounded: false,
       },
       {
         parent: taskA,
@@ -464,10 +473,61 @@ describe('Main Thread Tasks', () => {
         duration: 75,
         selfTime: 75,
         group: taskGroups.other,
+        unbounded: false,
       },
     ]);
   });
 
+  it('should handle child events that extend >1ms beyond parent event because missing E', () => {
+    /*
+    An artistic rendering of the below trace:
+    ████████████████TaskA██████████████████
+            █████████TaskB██████████████████
+    */
+    const traceEvents = [
+      ...boilerplateTrace,
+      {ph: 'B', name: 'TaskA', pid, tid, ts: baseTs, args},
+      {ph: 'B', name: 'TaskB', pid, tid, ts: baseTs + 25e3, args},
+      {ph: 'E', name: 'TaskA', pid, tid, ts: baseTs + 100e3, args},
+      {ph: 'I', name: 'MarkerToPushOutTraceEnd', pid, tid, ts: baseTs + 110e3, args},
+    ];
+
+    traceEvents.forEach(evt => Object.assign(evt, {cat: 'devtools.timeline'}));
+
+    const tasks = run({traceEvents});
+    const [taskA, taskB] = tasks;
+    expect(tasks).toEqual([
+      {
+        parent: undefined,
+        attributableURLs: [],
+
+        children: [taskB],
+        event: traceEvents.find(event => event.name === 'TaskA'),
+        startTime: 0,
+        endTime: 100,
+        duration: 100,
+        selfTime: 25,
+        group: taskGroups.other,
+        unbounded: false,
+      },
+      {
+        parent: taskA,
+        attributableURLs: [],
+
+        children: [],
+        event: traceEvents.find(event => event.name === 'TaskB' && event.ph === 'B'),
+        startTime: 25,
+        endTime: 100,
+        duration: 75,
+        selfTime: 75,
+        group: taskGroups.other,
+        unbounded: true,
+      },
+    ]);
+  });
+
+  // Invalid sets of events.
+  // All of these should have `traceEnd` pushed out to avoid falling into one of our mitigation scenarios.
   const invalidEventSets = [
     [
       // TaskA overlaps with TaskB, X first
@@ -499,12 +559,6 @@ describe('Main Thread Tasks', () => {
       {ph: 'X', name: 'TaskA', pid, tid, ts: baseTs, dur: 100e3, args},
       {ph: 'E', name: 'TaskB', pid, tid, ts: baseTs + 10e3, args},
     ],
-    [
-      {ph: 'I', name: 'MarkerToPushOutTraceEnd', pid, tid, ts: baseTs + 110e3, args},
-      // TaskB is missing an E event within an X
-      {ph: 'X', name: 'TaskA', pid, tid, ts: baseTs, dur: 100e3, args},
-      {ph: 'B', name: 'TaskB', pid, tid, ts: baseTs + 10e3, args},
-    ],
   ];
 
   for (const invalidEvents of invalidEventSets) {
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index c3ba3a1f9..ecbd88812 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -306,7 +306,7 @@
     },
     "critical-request-chains": {
       "id": "critical-request-chains",
-      "title": "Minimize Critical Requests Depth",
+      "title": "Avoid chaining critical requests",
       "description": "The Critical Request Chains below show you what resources are loaded with a high priority. Consider reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load. [Learn more](https://web.dev/critical-request-chains).",
       "score": null,
       "scoreDisplayMode": "informative",
@@ -1484,7 +1484,7 @@
     },
     "third-party-summary": {
       "id": "third-party-summary",
-      "title": "Third-Party usage",
+      "title": "Minimize third-party usage",
       "description": "Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).",
       "score": 1,
       "scoreDisplayMode": "binary",