Skip to content

Commit 1a8d5fe

Browse files
committed
Replace expensive DOM traversal with efficient regex-based CSS parsing
1 parent 3a78637 commit 1a8d5fe

File tree

3 files changed

+129
-54
lines changed

3 files changed

+129
-54
lines changed

dist/content-visibility.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* Content Visibility Custom Metric - Optimized Version
3+
*
4+
* Analyzes CSS usage of the content-visibility property using efficient
5+
* regex-based detection with performance optimizations.
6+
*/
7+
8+
//[content-visibility]
9+
10+
(() => {
11+
/**
12+
* Extract content-visibility declarations from CSS using regex
13+
* @param {string} css - The CSS string to analyze
14+
* @returns {Array} Array of content-visibility values found
15+
*/
16+
function extractContentVisibilityValues(css) {
17+
if (!css || typeof css !== 'string') {
18+
return [];
19+
}
20+
21+
const contentVisibilityValues = [];
22+
23+
// Remove CSS comments first
24+
css = css.replace(/\/\*[\s\S]*?\*\//g, '');
25+
26+
// Regex to match content-visibility property declarations
27+
// Matches: content-visibility: value; or content-visibility: value
28+
const contentVisibilityRegex = /content-visibility\s*:\s*([^;}\s]+(?:\s+[^;}\s]+)*)/gi;
29+
let regexMatch;
30+
31+
while ((regexMatch = contentVisibilityRegex.exec(css)) !== null) {
32+
const value = regexMatch[1].trim();
33+
if (value) {
34+
contentVisibilityValues.push(value);
35+
}
36+
}
37+
38+
return contentVisibilityValues;
39+
}
40+
41+
/**
42+
* Optimized function to get unique values without Set
43+
* @param {Array} values - Array of values
44+
* @returns {Array} Array of unique values
45+
*/
46+
function getUniqueValues(values) {
47+
const unique = [];
48+
const seen = {};
49+
50+
for (let valueIndex = 0; valueIndex < values.length; valueIndex++) {
51+
const value = values[valueIndex];
52+
if (!seen[value]) {
53+
seen[value] = true;
54+
unique.push(value);
55+
}
56+
}
57+
58+
return unique;
59+
}
60+
61+
/**
62+
* Custom metric to analyze content-visibility usage with optimizations
63+
* @returns {Object} Content visibility analysis results
64+
*/
65+
function contentVisibility() {
66+
const contentVisibilityValues = [];
67+
68+
// Process stylesheets first (usually largest source)
69+
const stylesheets = $WPT_BODIES.filter(body => body.type === 'Stylesheet');
70+
for (let stylesheetIndex = 0; stylesheetIndex < stylesheets.length; stylesheetIndex++) {
71+
const stylesheet = stylesheets[stylesheetIndex];
72+
if (stylesheet.response_body) {
73+
const values = extractContentVisibilityValues(stylesheet.response_body);
74+
contentVisibilityValues.push(...values);
75+
}
76+
}
77+
78+
// Process style blocks (usually fewer elements)
79+
const styleElements = document.querySelectorAll('style');
80+
for (let styleIndex = 0; styleIndex < styleElements.length; styleIndex++) {
81+
const styleElement = styleElements[styleIndex];
82+
if (styleElement.innerHTML) {
83+
const values = extractContentVisibilityValues(styleElement.innerHTML);
84+
contentVisibilityValues.push(...values);
85+
}
86+
}
87+
88+
// Process inline styles (most expensive - limit scope if possible)
89+
// Only process if we haven't found any content-visibility yet
90+
if (contentVisibilityValues.length === 0) {
91+
const elementsWithStyle = document.querySelectorAll('[style]');
92+
for (let elementIndex = 0; elementIndex < elementsWithStyle.length; elementIndex++) {
93+
const elementWithStyle = elementsWithStyle[elementIndex];
94+
const styleAttr = elementWithStyle.getAttribute('style');
95+
if (styleAttr && styleAttr.includes('content-visibility')) {
96+
const values = extractContentVisibilityValues(styleAttr);
97+
contentVisibilityValues.push(...values);
98+
}
99+
}
100+
}
101+
102+
return {
103+
used: contentVisibilityValues.length > 0,
104+
count: contentVisibilityValues.length,
105+
values: contentVisibilityValues,
106+
uniqueValues: getUniqueValues(contentVisibilityValues)
107+
};
108+
}
109+
110+
return contentVisibility();
111+
})();

dist/css.js

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -75,38 +75,4 @@ return JSON.stringify({
7575
externalCssInBody: countExternalCssInBody(),
7676
inlineCssInHead: countInlineCssInHead(),
7777
inlineCssInBody: countInlineCssInBody(),
78-
79-
content_visibility: (() => {
80-
// Detects elements using the content-visibility CSS property
81-
const elements = document.querySelectorAll('*');
82-
const contentVisibilityElements = [];
83-
const contentVisibilityValues = {};
84-
85-
for (const element of elements) {
86-
try {
87-
const computedStyle = getComputedStyle(element);
88-
const contentVisibility = computedStyle.getPropertyValue('content-visibility');
89-
90-
if (contentVisibility && contentVisibility !== 'visible') {
91-
contentVisibilityElements.push({
92-
tagName: element.tagName.toLowerCase(),
93-
contentVisibility: contentVisibility.trim(),
94-
className: element.className || '',
95-
id: element.id || ''
96-
});
97-
98-
const value = contentVisibility.trim();
99-
contentVisibilityValues[value] = (contentVisibilityValues[value] || 0) + 1;
100-
}
101-
} catch (e) {
102-
// continue regardless of error
103-
}
104-
}
105-
106-
return {
107-
total: contentVisibilityElements.length,
108-
elements: contentVisibilityElements,
109-
values: contentVisibilityValues
110-
};
111-
})()
11278
});

metric-summary.md

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -723,35 +723,33 @@ Example response:
723723
[5, 12, 11]
724724
```
725725

726-
## [css.js](https://github.com/HTTPArchive/custom-metrics/blob/main/dist/css.js) metrics
726+
## [content-visibility.js](https://github.com/HTTPArchive/custom-metrics/blob/main/dist/content-visibility.js) metrics
727727

728728
### content_visibility
729+
Detects CSS rules using the `content-visibility` property for performance optimization analysis. This metric uses efficient regex-based detection with performance optimizations to find content-visibility declarations in stylesheets, style blocks, and inline styles. Returns information about CSS rules that have content-visibility set to values other than 'visible' (such as 'auto', 'hidden', or 'skip').
729730

730-
Detects elements using the `content-visibility` CSS property for performance optimization analysis. Returns information about elements that have content-visibility set to values other than 'visible' (such as 'auto', 'hidden', or 'skip').
731+
**Performance Optimizations:**
732+
- Early exit for inline styles when content-visibility is found in stylesheets/style blocks
733+
- Pre-filtering of inline styles to avoid unnecessary regex processing
731734

732735
Example response:
733736

734737
```json
735738
{
736-
"total": 15,
737-
"elements": [
738-
{
739-
"tagName": "div",
740-
"contentVisibility": "auto",
741-
"className": "lazy-section",
742-
"id": "section-1"
743-
},
744-
{
745-
"tagName": "section",
746-
"contentVisibility": "hidden",
747-
"className": "hidden-content",
748-
"id": ""
749-
}
739+
"used": true,
740+
"count": 5,
741+
"values": [
742+
"auto",
743+
"hidden",
744+
"skip",
745+
"auto",
746+
"hidden"
750747
],
751-
"values": {
752-
"auto": 12,
753-
"hidden": 3
754-
}
748+
"uniqueValues": [
749+
"auto",
750+
"hidden",
751+
"skip"
752+
]
755753
}
756754
```
757755

0 commit comments

Comments
 (0)