diff --git a/tracker/tracker/CHANGELOG.md b/tracker/tracker/CHANGELOG.md
index c291f39d8..216f791df 100644
--- a/tracker/tracker/CHANGELOG.md
+++ b/tracker/tracker/CHANGELOG.md
@@ -8,6 +8,14 @@
- **[breaking]** new string dictionary message format
+## 15.0.7
+
+- fix for svg sprite handling
+
+## 15.0.6
+
+- fix for batch sending to prevent proxy wrappers
+
## 15.0.5
- update medv/finder to 4.0.2 for better support of css-in-js libs
diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts
index a016bab2e..4a4684f9f 100644
--- a/tracker/tracker/src/main/app/index.ts
+++ b/tracker/tracker/src/main/app/index.ts
@@ -324,6 +324,7 @@ export default class App {
fixedCanvasScaling: false,
disableCanvas: false,
captureIFrames: true,
+ disableSprites: false,
obscureTextEmails: true,
obscureTextNumbers: false,
crossdomain: {
diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts
index 006c3c7b6..7c13c0483 100644
--- a/tracker/tracker/src/main/app/observer/observer.ts
+++ b/tracker/tracker/src/main/app/observer/observer.ts
@@ -23,6 +23,7 @@ import {
} from '../guards.js'
const iconCache = {}
+const svgUrlCache = {}
const domParser = new DOMParser()
async function parseUseEl(useElement: SVGUseElement, mode: 'inline' | 'dataurl' | 'svgtext') {
@@ -43,15 +44,42 @@ async function parseUseEl(useElement: SVGUseElement, mode: 'inline' | 'dataurl'
return iconCache[symbolId]
}
- const response = await fetch(url)
- const svgText = await response.text()
+ let svgDoc: Document
+ if (svgUrlCache[url]) {
+ if (svgUrlCache[url] === 1) {
+ await new Promise((resolve) => {
+ let tries = 0
+ const interval = setInterval(() => {
+ if (tries > 100) {
+ clearInterval(interval)
+ resolve(false)
+ }
+ if (svgUrlCache[url] !== 1) {
+ svgDoc = svgUrlCache[url]
+ clearInterval(interval)
+ resolve(true)
+ } else {
+ tries++
+ }
+ }, 100)
+ })
+ } else {
+ svgDoc = svgUrlCache[url] ?? ``
+ }
+ } else {
+ svgUrlCache[url] = 1
+ const response = await fetch(url)
+ const svgText = await response.text()
+ svgDoc = domParser.parseFromString(svgText, 'image/svg+xml')
+ svgUrlCache[url] = svgDoc
+ }
- const svgDoc = domParser.parseFromString(svgText, 'image/svg+xml')
+ // @ts-ignore
const symbol = svgDoc.getElementById(symbolId)
if (!symbol) {
console.debug('Openreplay: Symbol not found in SVG.')
- return
+ return ''
}
if (mode === 'inline') {
@@ -136,10 +164,13 @@ export default abstract class Observer {
private readonly indexes: Array = []
private readonly attributesMap: Map> = new Map()
private readonly textSet: Set = new Set()
+ private readonly disableSprites: boolean = false
constructor(
protected readonly app: App,
protected readonly isTopContext = false,
+ options: { disableSprites: boolean } = { disableSprites: false },
) {
+ this.disableSprites = options.disableSprites
this.observer = createMutationObserver(
this.app.safe((mutations) => {
for (const mutation of mutations) {
@@ -249,7 +280,7 @@ export default abstract class Observer {
this.app.send(RemoveNodeAttribute(id, name))
}
- if (isUseElement(node) && name === 'href') {
+ if (isUseElement(node) && name === 'href' && !this.disableSprites) {
parseUseEl(node, 'svgtext')
.then((svgData) => {
if (svgData) {
diff --git a/tracker/tracker/src/main/app/observer/top_observer.ts b/tracker/tracker/src/main/app/observer/top_observer.ts
index 6c14e8083..c5f623170 100644
--- a/tracker/tracker/src/main/app/observer/top_observer.ts
+++ b/tracker/tracker/src/main/app/observer/top_observer.ts
@@ -11,6 +11,7 @@ import { IN_BROWSER, hasOpenreplayAttribute, canAccessIframe } from '../../utils
export interface Options {
captureIFrames: boolean
+ disableSprites: boolean
}
type Context = Window & typeof globalThis
@@ -24,14 +25,16 @@ export default class TopObserver extends Observer {
readonly app: App
constructor(params: { app: App; options: Partial }) {
- super(params.app, true)
- this.app = params.app
- this.options = Object.assign(
+ const opts = Object.assign(
{
captureIFrames: true,
+ disableSprites: false,
},
params.options,
)
+ super(params.app, true, opts)
+ this.app = params.app
+ this.options = opts
// IFrames
this.app.nodes.attachNodeCallback((node) => {
if (