Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions src/BeaconPreloadFonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ class BeaconPreloadFonts {
]);
}

/**
* Checks if a URL should be excluded from external font processing based on domain exclusions.
*
* @param {string} url - The URL to check.
* @returns {boolean} True if the URL should be excluded, false otherwise.
*/
isUrlExcludedFromExternalProcessing(url) {
if (!url) return false;

// Check both exclusion arrays for domain filtering
const externalFontExclusions = this.config.external_font_exclusions || [];
const preloadFontsExclusions = this.config.preload_fonts_exclusions || [];
const allExclusions = [...externalFontExclusions, ...preloadFontsExclusions];
return allExclusions.some((exclusion) => url.includes(exclusion));
}

/**
* Checks if a font family or URL should be excluded from preloading.
*
Expand Down Expand Up @@ -247,9 +263,8 @@ class BeaconPreloadFonts {
return false;
}

// Check exclusions instead of allowlist
const exclusions = this.config.external_font_exclusions || [];
return !exclusions.some(exclusion => link.href.includes(exclusion));
// Use the helper method for consistent exclusion logic
return !this.isUrlExcludedFromExternalProcessing(link.href);
} catch (e) {
return false;
}
Expand Down Expand Up @@ -427,6 +442,11 @@ class BeaconPreloadFonts {
try {
const importUrl = rule.href;

// Check if URL should be excluded based on external font exclusions
if (this.isUrlExcludedFromExternalProcessing(importUrl)) {
return;
}

// Prevent infinite loops by checking if URL already processed
if (processedUrls.has(importUrl)) {
return;
Expand Down Expand Up @@ -474,6 +494,11 @@ class BeaconPreloadFonts {
} catch (e) {
// If we can't access cssRules due to CORS, try to process the stylesheet content directly
if (e.name === 'SecurityError' && sheet.href) {
// Check if URL should be excluded based on external font exclusions
if (this.isUrlExcludedFromExternalProcessing(sheet.href)) {
return;
}

// Prevent infinite loops for CORS fallback too
if (processedUrls.has(sheet.href)) {
return;
Expand Down Expand Up @@ -502,6 +527,11 @@ class BeaconPreloadFonts {
while ((importMatch = importRegex.exec(cssText)) !== null) {
const importUrl = new URL(importMatch[1], sheet.href).href;

// Check if URL should be excluded based on external font exclusions
if (this.isUrlExcludedFromExternalProcessing(importUrl)) {
continue;
}

// Prevent infinite loops
if (processedUrls.has(importUrl)) {
continue;
Expand Down Expand Up @@ -553,6 +583,11 @@ class BeaconPreloadFonts {
while ((importMatch = importRegex.exec(cssText)) !== null) {
const importUrl = importMatch[1];

// Check if URL should be excluded based on external font exclusions
if (this.isUrlExcludedFromExternalProcessing(importUrl)) {
continue;
}

// Prevent infinite loops
if (processedUrls.has(importUrl)) {
continue;
Expand Down
46 changes: 46 additions & 0 deletions test/BeaconPreloadFonts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,40 @@ describe('BeaconPreloadFonts', () => {
sinon.restore(); // Restore sinon mocks
});

describe('isUrlExcludedFromExternalProcessing', () => {
it('should return false when no exclusions are configured', () => {
const result = beaconPreloadFonts.isUrlExcludedFromExternalProcessing('https://fonts.googleapis.com/css');
assert.strictEqual(result, false);
});

it('should return true when URL matches external font exclusion', () => {
beaconPreloadFonts.config.external_font_exclusions = ['fonts.googleapis.com'];
const result = beaconPreloadFonts.isUrlExcludedFromExternalProcessing('https://fonts.googleapis.com/css');
assert.strictEqual(result, true);
});

it('should return true when URL matches preload fonts exclusion', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['fonts.googleapis.com'];
beaconPreloadFonts.config.external_font_exclusions = []; // explicitly empty
const result = beaconPreloadFonts.isUrlExcludedFromExternalProcessing('https://fonts.googleapis.com/css');
assert.strictEqual(result, true);
});

it('should return false when URL does not match any exclusion', () => {
beaconPreloadFonts.config.external_font_exclusions = ['fonts.adobe.com'];
beaconPreloadFonts.config.preload_fonts_exclusions = ['Roboto'];
const result = beaconPreloadFonts.isUrlExcludedFromExternalProcessing('https://fonts.googleapis.com/css');
assert.strictEqual(result, false);
});

it('should return false for null or undefined URLs', () => {
beaconPreloadFonts.config.external_font_exclusions = ['fonts.googleapis.com'];
assert.strictEqual(beaconPreloadFonts.isUrlExcludedFromExternalProcessing(null), false);
assert.strictEqual(beaconPreloadFonts.isUrlExcludedFromExternalProcessing(undefined), false);
assert.strictEqual(beaconPreloadFonts.isUrlExcludedFromExternalProcessing(''), false);
});
});

describe('isExcluded', () => {
it('should return true when fontFamily exactly matches exclusion', () => {
beaconPreloadFonts.config.preload_fonts_exclusions = ['Arial'];
Expand Down Expand Up @@ -1117,6 +1151,18 @@ describe('BeaconPreloadFonts', () => {
assert.strictEqual(result.styleSheets.length, 0);
assert.deepStrictEqual(result.fontPairs, {});
});

it('should exclude links based on preload_fonts_exclusions config (backward compatibility)', async () => {
// Set preload_fonts_exclusions to exclude domain
beaconPreloadFonts.config.preload_fonts_exclusions = ['fonts.googleapis.com'];
beaconPreloadFonts.config.external_font_exclusions = []; // explicitly empty

const result = await beaconPreloadFonts.externalStylesheetsDoc();

// Should exclude the link since preload_fonts_exclusions can also contain domains
assert.strictEqual(result.styleSheets.length, 0);
assert.deepStrictEqual(result.fontPairs, {});
});

it('should process links from different origins only', async () => {
// Set up links with same and different origins
Expand Down