Initial commit
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// Dumps every same-origin CSS rule as { selector, cssText } into __CLONE_CAPTURE__.cssRules,
|
||||
// and extracts every url(...) asset reference (resolved to absolute URLs) into
|
||||
// __CLONE_CAPTURE__.cssAssets. Catches background-images set in stylesheets that the
|
||||
// response listener missed (e.g. @media-gated, ::before, mask-image, list-style-image, cursor).
|
||||
(() => {
|
||||
function isSameOrigin(href) {
|
||||
if (!href) return true;
|
||||
try {
|
||||
const u = new URL(href, location.href);
|
||||
return u.origin === location.origin;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveUrl(raw, baseHref) {
|
||||
if (!raw) return null;
|
||||
let s = raw.trim();
|
||||
if (s.startsWith('"') || s.startsWith("'")) s = s.slice(1, -1);
|
||||
if (s.startsWith('data:') || s.startsWith('#')) return null;
|
||||
try {
|
||||
return new URL(s, baseHref || location.href).href;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function collectFromRules(rules, baseHref, out) {
|
||||
if (!rules) return;
|
||||
for (const rule of rules) {
|
||||
const type = rule.constructor?.name || '';
|
||||
if (type === 'CSSStyleRule') {
|
||||
const text = rule.cssText || '';
|
||||
out.rules.push({ selector: rule.selectorText, cssText: text, baseHref });
|
||||
const urls = text.match(/url\(\s*([^)]+?)\s*\)/g) || [];
|
||||
for (const m of urls) {
|
||||
const inner = m
|
||||
.slice(4, -1)
|
||||
.trim()
|
||||
.replace(/^['"]|['"]$/g, '');
|
||||
const abs = resolveUrl(inner, baseHref);
|
||||
if (abs && !out.assetSet.has(abs)) {
|
||||
out.assetSet.add(abs);
|
||||
out.assets.push({ source_url: abs, selector: rule.selectorText });
|
||||
}
|
||||
}
|
||||
} else if (type === 'CSSMediaRule' || type === 'CSSSupportsRule' || type === 'CSSContainerRule') {
|
||||
const condition = rule.conditionText || rule.media?.mediaText;
|
||||
out.media.push({ condition, type });
|
||||
collectFromRules(rule.cssRules, baseHref, out);
|
||||
} else if (type === 'CSSImportRule') {
|
||||
// Recurse into imported stylesheet if same-origin and accessible
|
||||
try {
|
||||
const importedHref = rule.styleSheet?.href || baseHref;
|
||||
if (isSameOrigin(importedHref)) {
|
||||
collectFromRules(rule.styleSheet?.cssRules, importedHref, out);
|
||||
}
|
||||
} catch {}
|
||||
} else if (type === 'CSSFontFaceRule') {
|
||||
const text = rule.cssText || '';
|
||||
out.fontFaces.push({ cssText: text, baseHref });
|
||||
const urls = text.match(/url\(\s*([^)]+?)\s*\)/g) || [];
|
||||
for (const m of urls) {
|
||||
const inner = m
|
||||
.slice(4, -1)
|
||||
.trim()
|
||||
.replace(/^['"]|['"]$/g, '');
|
||||
const abs = resolveUrl(inner, baseHref);
|
||||
if (abs && !out.assetSet.has(abs)) {
|
||||
out.assetSet.add(abs);
|
||||
out.assets.push({ source_url: abs, selector: '@font-face' });
|
||||
}
|
||||
}
|
||||
} else if (type === 'CSSKeyframesRule') {
|
||||
const frames = [];
|
||||
for (const k of rule.cssRules || []) frames.push({ key: k.keyText, cssText: k.style?.cssText || '' });
|
||||
out.keyframes.push({ name: rule.name, frames, baseHref });
|
||||
} else if (rule.cssRules) {
|
||||
collectFromRules(rule.cssRules, baseHref, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function collect() {
|
||||
const out = {
|
||||
rules: [],
|
||||
media: [],
|
||||
fontFaces: [],
|
||||
keyframes: [],
|
||||
assets: [],
|
||||
assetSet: new Set(),
|
||||
crossOrigin: 0,
|
||||
};
|
||||
for (const sheet of document.styleSheets) {
|
||||
const baseHref = sheet.href || location.href;
|
||||
let rules;
|
||||
try {
|
||||
rules = sheet.cssRules;
|
||||
} catch {
|
||||
out.crossOrigin += 1;
|
||||
continue;
|
||||
}
|
||||
collectFromRules(rules, baseHref, out);
|
||||
}
|
||||
delete out.assetSet;
|
||||
window.__CLONE_CAPTURE__.cssRules = out;
|
||||
}
|
||||
|
||||
const prevFinalize = window.__CLONE_FINALIZE__;
|
||||
window.__CLONE_FINALIZE__ = function () {
|
||||
if (prevFinalize)
|
||||
try {
|
||||
prevFinalize();
|
||||
} catch {}
|
||||
try {
|
||||
collect();
|
||||
} catch (e) {
|
||||
window.__CLONE_CAPTURE__.cssRules = { error: String(e) };
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user