tracker: fix spritemap parser, add svgdoc cache
This commit is contained in:
parent
c025b2f1a5
commit
0ba1382c16
4 changed files with 51 additions and 8 deletions
|
|
@ -8,6 +8,14 @@
|
||||||
|
|
||||||
- **[breaking]** new string dictionary message format
|
- **[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
|
## 15.0.5
|
||||||
|
|
||||||
- update medv/finder to 4.0.2 for better support of css-in-js libs
|
- update medv/finder to 4.0.2 for better support of css-in-js libs
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,7 @@ export default class App {
|
||||||
fixedCanvasScaling: false,
|
fixedCanvasScaling: false,
|
||||||
disableCanvas: false,
|
disableCanvas: false,
|
||||||
captureIFrames: true,
|
captureIFrames: true,
|
||||||
|
disableSprites: false,
|
||||||
obscureTextEmails: true,
|
obscureTextEmails: true,
|
||||||
obscureTextNumbers: false,
|
obscureTextNumbers: false,
|
||||||
crossdomain: {
|
crossdomain: {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
} from '../guards.js'
|
} from '../guards.js'
|
||||||
|
|
||||||
const iconCache = {}
|
const iconCache = {}
|
||||||
|
const svgUrlCache = {}
|
||||||
const domParser = new DOMParser()
|
const domParser = new DOMParser()
|
||||||
|
|
||||||
async function parseUseEl(useElement: SVGUseElement, mode: 'inline' | 'dataurl' | 'svgtext') {
|
async function parseUseEl(useElement: SVGUseElement, mode: 'inline' | 'dataurl' | 'svgtext') {
|
||||||
|
|
@ -43,15 +44,42 @@ async function parseUseEl(useElement: SVGUseElement, mode: 'inline' | 'dataurl'
|
||||||
return iconCache[symbolId]
|
return iconCache[symbolId]
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url)
|
let svgDoc: Document
|
||||||
const svgText = await response.text()
|
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] ?? `<svg xmlns="http://www.w3.org/2000/svg"></svg>`
|
||||||
|
}
|
||||||
|
} 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)
|
const symbol = svgDoc.getElementById(symbolId)
|
||||||
|
|
||||||
if (!symbol) {
|
if (!symbol) {
|
||||||
console.debug('Openreplay: Symbol not found in SVG.')
|
console.debug('Openreplay: Symbol not found in SVG.')
|
||||||
return
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode === 'inline') {
|
if (mode === 'inline') {
|
||||||
|
|
@ -136,10 +164,13 @@ export default abstract class Observer {
|
||||||
private readonly indexes: Array<number> = []
|
private readonly indexes: Array<number> = []
|
||||||
private readonly attributesMap: Map<number, Set<string>> = new Map()
|
private readonly attributesMap: Map<number, Set<string>> = new Map()
|
||||||
private readonly textSet: Set<number> = new Set()
|
private readonly textSet: Set<number> = new Set()
|
||||||
|
private readonly disableSprites: boolean = false
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly app: App,
|
protected readonly app: App,
|
||||||
protected readonly isTopContext = false,
|
protected readonly isTopContext = false,
|
||||||
|
options: { disableSprites: boolean } = { disableSprites: false },
|
||||||
) {
|
) {
|
||||||
|
this.disableSprites = options.disableSprites
|
||||||
this.observer = createMutationObserver(
|
this.observer = createMutationObserver(
|
||||||
this.app.safe((mutations) => {
|
this.app.safe((mutations) => {
|
||||||
for (const mutation of mutations) {
|
for (const mutation of mutations) {
|
||||||
|
|
@ -249,7 +280,7 @@ export default abstract class Observer {
|
||||||
this.app.send(RemoveNodeAttribute(id, name))
|
this.app.send(RemoveNodeAttribute(id, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUseElement(node) && name === 'href') {
|
if (isUseElement(node) && name === 'href' && !this.disableSprites) {
|
||||||
parseUseEl(node, 'svgtext')
|
parseUseEl(node, 'svgtext')
|
||||||
.then((svgData) => {
|
.then((svgData) => {
|
||||||
if (svgData) {
|
if (svgData) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { IN_BROWSER, hasOpenreplayAttribute, canAccessIframe } from '../../utils
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
captureIFrames: boolean
|
captureIFrames: boolean
|
||||||
|
disableSprites: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context = Window & typeof globalThis
|
type Context = Window & typeof globalThis
|
||||||
|
|
@ -24,14 +25,16 @@ export default class TopObserver extends Observer {
|
||||||
readonly app: App
|
readonly app: App
|
||||||
|
|
||||||
constructor(params: { app: App; options: Partial<Options> }) {
|
constructor(params: { app: App; options: Partial<Options> }) {
|
||||||
super(params.app, true)
|
const opts = Object.assign(
|
||||||
this.app = params.app
|
|
||||||
this.options = Object.assign(
|
|
||||||
{
|
{
|
||||||
captureIFrames: true,
|
captureIFrames: true,
|
||||||
|
disableSprites: false,
|
||||||
},
|
},
|
||||||
params.options,
|
params.options,
|
||||||
)
|
)
|
||||||
|
super(params.app, true, opts)
|
||||||
|
this.app = params.app
|
||||||
|
this.options = opts
|
||||||
// IFrames
|
// IFrames
|
||||||
this.app.nodes.attachNodeCallback((node) => {
|
this.app.nodes.attachNodeCallback((node) => {
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue