diff --git a/packages/cookie-banner/src/detectors/resource-detector.ts b/packages/cookie-banner/src/detectors/resource-detector.ts index 17856183e..fc12da4db 100644 --- a/packages/cookie-banner/src/detectors/resource-detector.ts +++ b/packages/cookie-banner/src/detectors/resource-detector.ts @@ -523,17 +523,54 @@ export class ResourceDetector implements Detector { ): void { if (typeof value !== "string" || value === "") return; - const rt = this.resourceTypeForElement(el, attrName); - if (rt === null) return; - + // Extension marking runs before classification because the + // current attribute alone is not always enough to know whether a + // load will happen. The canonical case is ``: a stylesheet + // load is only triggered once both `href` and `rel="stylesheet"` + // are set, in any order. If the extension sets `href` first we + // would otherwise miss tagging, and the eventual PerformanceObserver + // entry -- which has no extension frame on its stack -- would leak + // through as a page tracker. The same holds for `` + // and any future rel value that initiates a load. if (isExtensionCaller()) { - this.markExtension(el, this.identifierOf(value)); + if (this.couldLoadResource(el, attrName)) { + this.markExtension(el, this.identifierOf(value)); + } return; } + const rt = this.resourceTypeForElement(el, attrName); + if (rt === null) return; + this.processResource(value, rt); } + // couldLoadResource reports whether `el` is an element type that can + // ever initiate a network load via `attrName`, regardless of any + // other attributes that may or may not be set yet. It is a superset + // of resourceTypeForElement: it returns true for `` even + // when `rel` has not been set, because the rel may change later and + // turn the href into a real load. + private couldLoadResource(el: Element, attrName: string): boolean { + if (attrName === "src") { + return ( + el instanceof HTMLScriptElement + || el instanceof HTMLIFrameElement + || el instanceof HTMLImageElement + || (typeof HTMLSourceElement !== "undefined" && el instanceof HTMLSourceElement) + || (typeof HTMLEmbedElement !== "undefined" && el instanceof HTMLEmbedElement) + || (typeof HTMLMediaElement !== "undefined" && el instanceof HTMLMediaElement) + ); + } + if (attrName === "href") { + return el instanceof HTMLLinkElement; + } + if (attrName === "data") { + return typeof HTMLObjectElement !== "undefined" && el instanceof HTMLObjectElement; + } + return false; + } + private resourceTypeForElement(el: Element, attrName: string): ResourceType | null { if (attrName === "src") { if (el instanceof HTMLScriptElement) return "script";