Skip to content

Commit

Permalink
Merge branch 'main' into bfcache-category
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianaixba committed Nov 1, 2023
2 parents 535831d + b5b9b07 commit 58fc6b6
Show file tree
Hide file tree
Showing 79 changed files with 3,976 additions and 3,599 deletions.
5 changes: 5 additions & 0 deletions build/build-extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const packagePath = `${distDir}/../extension-${browserBrand}-package`;
const manifestVersion = readJson(`${sourceDir}/manifest.json`).version;

async function buildEntryPoint() {
const locales = fs.readdirSync(`${LH_ROOT}/shared/localization/locales`)
.filter(f => !f.includes('.ctc.json'))
.map(f => f.replace('.json', ''))
.filter(locale => !['en-XA', 'en-XL', 'ar-XB'].includes(locale));
await esbuild.build({
entryPoints: [`${sourceDir}/scripts/${sourceName}`],
outfile: `${distDir}/scripts/${distName}`,
Expand All @@ -38,6 +42,7 @@ async function buildEntryPoint() {
plugins.bulkLoader([
plugins.partialLoaders.replaceText({
'___BROWSER_BRAND___': browserBrand,
'__LOCALES__': JSON.stringify(locales),
}),
]),
],
Expand Down
1 change: 1 addition & 0 deletions clients/devtools/devtools-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function createConfig(categoryIDs, device) {
// In DevTools, emulation is applied _before_ Lighthouse starts (to deal with viewport emulation bugs). go/xcnjf
// As a result, we don't double-apply viewport emulation.
screenEmulation: {disabled: true},
ignoreStatusCode: true,
};
if (device === 'desktop') {
settings.throttling = constants.throttling.desktopDense4G;
Expand Down
96 changes: 54 additions & 42 deletions clients/extension/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,54 +19,66 @@
<img class="header__icon" src="images/lh_logo.svg" width="75" height="75"></svg>
<!-- <object data="images/lh_logo.svg" type="image/svg+xml"></object> -->
<button class="button button--generate">Generate report</button>
<button class="button button--configure">
<svg class="gear__icon" width="25" height="25" viewBox="0 0 14 15">
<path fill="#fff" d="M-1-1h16v17H-1z" />
<path fill="currentColor" d="M12.526 8.07c.029-.224.05-.448.05-.686 0-.238-.021-.462-.05-.686l1.518-1.155a.347.347 0 0 0 .087-.448l-1.44-2.422a.367.367 0 0 0-.439-.154l-1.791.7a5.295 5.295 0 0 0-1.217-.686L8.971.678a.348.348 0 0 0-.353-.294H5.74a.348.348 0 0 0-.353.294l-.273 1.855a5.568 5.568 0 0 0-1.216.686l-1.792-.7a.356.356 0 0 0-.44.154L.228 5.095a.339.339 0 0 0 .087.448l1.518 1.155c-.029.224-.05.455-.05.686 0 .23.021.462.05.686L.314 9.225a.347.347 0 0 0-.087.448l1.44 2.422c.086.154.28.21.439.154l1.792-.7c.374.28.777.51 1.216.686l.273 1.855a.348.348 0 0 0 .353.294h2.878c.18 0 .331-.126.353-.294l.273-1.855a5.568 5.568 0 0 0 1.217-.686l1.792.7c.165.063.352 0 .438-.154l1.44-2.422a.347.347 0 0 0-.087-.448L12.526 8.07zM7.179 9.834c-1.389 0-2.519-1.1-2.519-2.45 0-1.351 1.13-2.45 2.52-2.45 1.388 0 2.518 1.099 2.518 2.45 0 1.35-1.13 2.45-2.519 2.45z" />
</svg>
</button>
<span class="psi-disclaimer"><a href="https://developers.google.com/speed/docs/insights/v5/get-started?utm_source=lh-chrome-ext" title="https://developers.google.com/speed/docs/insights/v5/get-started" target="_blank" rel="noopener nofollow">Uses the PSI API</a></span>
<div class="errormsg"></div>
</div>
<div class="section section--secondary-panel">
<div class="hidden browser-brand browser-brand--chrome">
<h2 class="section--header">Chrome DevTools</h2>
<span class="section--description">
You can also run Lighthouse via the DevTools Lighthouse panel.
<br><br>
Shortcut to open DevTools: <span class="devtools-shortcut"><!-- filled dynamically --></span>
</span>
</div>
<div class="hidden browser-brand browser-brand--firefox">
<h2 class="section--header">Node CLI</h2>
<span class="section--description">
You can also run Lighthouse via the <a href="https://github.com/GoogleChrome/lighthouse#using-the-node-cli">Node CLI</a>.
</span>
</div>
<div class="section section--options">
<form class="options__form">
<div class="options__group">
<h3 class="options__title">Show results in:</h3>
<ul class="options__list options__backend">
<!-- filled dynamically -->
</ul>
</div>

<div class="options__group">
<h3 class="options__title">Device</h2>
<ul class="options__list options__device">
<li>
<label>
<input type="radio" name="device" value="mobile"></input><span>Mobile</span>
</label>
</li>
<li>
<label>
<input type="radio" name="device" value="desktop"></input><span>Desktop</span>
</label>
</li>
</ul>
</div>

<div class="options__group">
<h3 class="options__title">Categories</h3>
<ul class="options__list options__categories">
<!-- filled dynamically -->
</ul>
</div>

<div class="options__group options__group--locales">
<h3 class="options__title">Locale</h3>
<select class="options__select options__locales">
<!-- filled dynamically -->
</select>
</div>
</form>
</div>
</main>

<aside class="section section--options">
<form class="options__form">
<h3 class="options__title">Categories</h3>
<ul class="options__list options__categories">
<!-- filled dynamically -->
</ul>

<h3 class="options__title">Device</h2>
<ul class="options__list options__device">
<li>
<label>
<input type="radio" name="device" value="mobile"></input><span>Mobile</span>
</label>
</li>
<li>
<label>
<input type="radio" name="device" value="desktop"></input><span>Desktop</span>
</label>
</li>
</ul>
</form>
<aside class="section section--secondary-panel">
<div class="hidden browser-brand browser-brand--chrome">
<h2 class="section--header">Chrome DevTools</h2>
<span class="section--description">
This extension uses the PSI API, so it cannot be used to test sites hosted locally or behind auth. Instead, you can run Lighthouse via the DevTools Lighthouse panel.
<br><br>
Shortcut to open DevTools: <span class="devtools-shortcut"><!-- filled dynamically --></span>
</span>
</div>
<div class="hidden browser-brand browser-brand--firefox">
<h2 class="section--header">Node CLI</h2>
<span class="section--description">
This extension uses the PSI API, so it cannot be used to test sites hosted locally or behind auth. Instead, you can run Lighthouse via the <a href="https://github.com/GoogleChrome/lighthouse#using-the-node-cli">Node CLI</a>.
</span>
</div>
</aside>

<script src="scripts/popup-bundle.js"></script>
Expand Down
170 changes: 151 additions & 19 deletions clients/extension/scripts/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

import * as SettingsController from './settings-controller.js';

const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
const optionsVisibleClass = 'main--options-visible';
// Replaced with 'chrome' or 'firefox' in the build script.
/** @type {string} */
const BROWSER_BRAND = '___BROWSER_BRAND___';
/** @type {string[]} */
const LOCALES = JSON.parse('__LOCALES__');

const CHROME_STRINGS = {
localhostErrorMessage: 'Use DevTools to audit pages on localhost.',
Expand Down Expand Up @@ -61,17 +61,56 @@ function createOptionItem(text, id, isChecked) {
return listItem;
}

/**
* @param {string} name
* @param {string} text
* @param {string} id
* @param {boolean} isChecked
* @return {HTMLLIElement}
*/
function createRadioItem(name, text, id, isChecked) {
const input = document.createElement('input');
input.setAttribute('type', 'radio');
input.setAttribute('value', id);
input.setAttribute('name', name);
if (isChecked) {
input.setAttribute('checked', 'checked');
}

const label = document.createElement('label');
const span = document.createElement('span');
span.textContent = text;
label.append(input, span);
const listItem = document.createElement('li');
listItem.append(label);

return listItem;
}

/**
* Click event handler for Generate Report button.
* @param {string} backend
* @param {string} url
* @param {SettingsController.Settings} settings
*/
function onGenerateReportButtonClick(url, settings) {
const apiUrl = new URL(VIEWER_URL);
apiUrl.searchParams.append('psiurl', url);
apiUrl.searchParams.append('strategy', settings.device);
for (const category of settings.selectedCategories) {
apiUrl.searchParams.append('category', category);
function onGenerateReportButtonClick(backend, url, settings) {
let apiUrl;
if (backend === 'psi') {
apiUrl = new URL('https://pagespeed.web.dev/analysis');
apiUrl.searchParams.append('url', url);
apiUrl.searchParams.append('form_factor', settings.device);
for (const category of settings.selectedCategories) {
apiUrl.searchParams.append('category', category);
}
apiUrl.searchParams.append('hl', settings.locale);
} else {
apiUrl = new URL('https://googlechrome.github.io/lighthouse/viewer/');
apiUrl.searchParams.append('psiurl', url);
apiUrl.searchParams.append('strategy', settings.device);
for (const category of settings.selectedCategories) {
apiUrl.searchParams.append('category', category);
}
apiUrl.searchParams.append('locale', settings.locale);
}
apiUrl.searchParams.append('utm_source', 'lh-chrome-ext');
window.open(apiUrl.href);
Expand All @@ -82,7 +121,7 @@ function onGenerateReportButtonClick(url, settings) {
* for the categories.
* @param {SettingsController.Settings} settings
*/
function generateOptionsList(settings) {
function generateCategoryOptionsList(settings) {
const frag = document.createDocumentFragment();

SettingsController.DEFAULT_CATEGORIES.forEach(category => {
Expand All @@ -94,6 +133,99 @@ function generateOptionsList(settings) {
optionsCategoriesList.append(frag);
}


/**
* Generates a document fragment containing a list of backends.
* @param {SettingsController.Settings} settings
*/
function generateBackendOptionsList(settings) {
const frag = document.createDocumentFragment();

SettingsController.BACKENDS.forEach(backend => {
const isChecked = settings.backend === backend.id;
frag.append(createRadioItem('backend', backend.title, backend.id, isChecked));
});

const optionsCategoriesList = find('.options__backend');
optionsCategoriesList.append(frag);
}

/**
* From third_party/devtools-frontend/src/front_end/core/i18n/i18nImpl.ts
*
* Returns a string of the form:
* "German (Austria) - Deutsch (Österreich)"
* where the former locale representation is written in the currently enabled DevTools
* locale and the latter locale representation is written in the locale of `localeString`.
*
* Should the two locales match (i.e. have the same language) then the latter locale
* representation is written in English.
*
* @param {string} localeString
* @param {string} currentLocale
* @return {string}
*/
function getLocalizedLanguageRegion(localeString, currentLocale) {
const locale = new Intl.Locale(localeString);
const localLanguage = locale.language || 'en';
const localBaseName = locale.baseName || 'en-US';
const devtoolsLoc = new Intl.Locale(currentLocale);
const targetLanguage = localLanguage === devtoolsLoc.language ? 'en' : localBaseName;
const languageInCurrentLocale =
new Intl.DisplayNames([currentLocale], {type: 'language'}).of(localLanguage);
const languageInTargetLocale =
new Intl.DisplayNames([targetLanguage], {type: 'language'}).of(localLanguage);

let wrappedRegionInCurrentLocale = '';
let wrappedRegionInTargetLocale = '';

if (locale.region) {
const regionInCurrentLocale =
new Intl.DisplayNames([currentLocale], {type: 'region', style: 'short'}).of(locale.region);
const regionInTargetLocale =
new Intl.DisplayNames([targetLanguage], {type: 'region', style: 'short'}).of(locale.region);
wrappedRegionInCurrentLocale = ` (${regionInCurrentLocale})`;
wrappedRegionInTargetLocale = ` (${regionInTargetLocale})`;
}

const lhs = languageInCurrentLocale + wrappedRegionInCurrentLocale;
const rhs = languageInTargetLocale + wrappedRegionInTargetLocale;
if (lhs === rhs) {
return lhs;
}

return `${lhs} - ${rhs}`;
}

/**
* Generates a document fragment containing a list of locale options.
* @param {SettingsController.Settings} settings
*/
function generateLocaleOptionsList(settings) {
const frag = document.createDocumentFragment();

LOCALES.forEach(locale => {
const optionEl = document.createElement('option');
optionEl.textContent = getLocalizedLanguageRegion(locale, navigator.language);
optionEl.value = locale;
if (settings.locale === locale) {
optionEl.selected = true;
}
frag.append(optionEl);
});

const optionsLocalesList = find('.options__locales');
optionsLocalesList.append(frag);
}

/**
* @param {SettingsController.Settings} settings
*/
function configureVisibleSettings(settings) {
const optionsCategoriesList = find('.options__categories');
optionsCategoriesList.parentElement?.classList.toggle('hidden', settings.backend === 'psi');
}

function fillDevToolsShortcut() {
const el = find('.devtools-shortcut');
const isMac = /mac/i.test(navigator.platform);
Expand All @@ -107,11 +239,15 @@ function fillDevToolsShortcut() {
function readSettingsFromDomAndPersist() {
const optionsEl = find('.section--options');
// Save settings when options page is closed.
const backend = find('.options__backend input:checked').value;
const locale = find('select.options__locales').value;
const checkboxes = optionsEl.querySelectorAll('.options__categories input:checked');
const selectedCategories = Array.from(checkboxes).map(input => input.value);
const device = find('input[name="device"]:checked').value;

const settings = {
backend,
locale,
selectedCategories,
device,
};
Expand Down Expand Up @@ -151,10 +287,7 @@ async function initPopup() {
const browserBrandEl = find(`.browser-brand--${BROWSER_BRAND}`);
browserBrandEl.classList.remove('hidden');

const mainEl = find('main');
const optionsEl = find('.button--configure');
const generateReportButton = find('button.button--generate');
const configureButton = find('button.button--configure');
const psiDisclaimerEl = find('.psi-disclaimer');
const errorMessageEl = find('.errormsg');
const optionsFormEl = find('.options__form');
Expand All @@ -171,27 +304,26 @@ async function initPopup() {
// but it's very hard to keep an extension popup alive during a popup
// so we don't need to handle reacting to it.
generateReportButton.disabled = true;
configureButton.disabled = true;
psiDisclaimerEl.remove();
errorMessageEl.textContent = err.message;
return;
}

// Generate checkboxes from saved settings.
generateOptionsList(settings);
generateBackendOptionsList(settings);
generateCategoryOptionsList(settings);
generateLocaleOptionsList(settings);
configureVisibleSettings(settings);
const selectedDeviceEl = find(`.options__device input[value="${settings.device}"]`);
selectedDeviceEl.checked = true;

generateReportButton.addEventListener('click', () => {
onGenerateReportButtonClick(siteUrl.href, settings);
});

optionsEl.addEventListener('click', () => {
mainEl.classList.toggle(optionsVisibleClass);
onGenerateReportButtonClick(settings.backend, siteUrl.href, settings);
});

optionsFormEl.addEventListener('change', () => {
settings = readSettingsFromDomAndPersist();
configureVisibleSettings(settings);
});
}

Expand Down
Loading

0 comments on commit 58fc6b6

Please sign in to comment.