From 3ab02c1b28ca28fe8805f65dd62b2cab736318f4 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Thu, 28 Mar 2024 17:28:43 +0000 Subject: [PATCH] Add an `asset` type which includes the element; a bit of prep work for handling stylesheet assets --- packages/rrweb-snapshot/src/snapshot.ts | 25 +++++++++++++------ packages/rrweb/src/record/index.ts | 4 +-- packages/rrweb/src/record/mutation.ts | 14 ++++++++--- .../src/record/observers/asset-manager.ts | 9 ++++++- packages/types/src/index.ts | 5 ++++ 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 0a8f4eb23c..88c888074f 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -12,6 +12,7 @@ import { serializedNodeWithId, NodeType, attributes, + asset, DataURLOptions, } from '@rrweb/types'; import { @@ -477,7 +478,7 @@ function serializeNode( * - `src` attribute in `img` tags. * - `srcset` attribute in `img` tags. */ - onAssetDetected?: (result: { urls: string[] }) => unknown; + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNode | false { const { @@ -646,7 +647,7 @@ function serializeElementNode( * - `src` attribute in `img` tags. * - `srcset` attribute in `img` tags. */ - onAssetDetected?: (result: { urls: string[] }) => unknown; + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNode | false { const { @@ -667,7 +668,7 @@ function serializeElementNode( const needBlock = _isBlockedElement(n, blockClass, blockSelector); const tagName = getValidTagName(n); let attributes: attributes = {}; - const assets: string[] = []; + const assets: asset[] = []; const len = n.attributes.length; for (let i = 0; i < len; i++) { const attr = n.attributes[i]; @@ -687,9 +688,17 @@ function serializeElementNode( isAttributeCapturable(n, attr.name) ) { if (attr.name === 'srcset') { - assets.push(...getUrlsFromSrcset(value)); + getUrlsFromSrcset(value).forEach((url) => { + assets.push({ + element: n, + url, + }); + }); } else { - assets.push(value); + assets.push({ + element: n, + url: value, + }); } name = `rr_captured_${name}`; } @@ -873,7 +882,7 @@ function serializeElementNode( } if (assets.length && onAssetDetected) { - onAssetDetected({ urls: assets }); + onAssetDetected(assets); } return { @@ -1032,7 +1041,7 @@ export function serializeNodeWithId( * - `src` attribute in `img` tags. * - `srcset` attribute in `img` tags. */ - onAssetDetected?: (result: { urls: string[] }) => unknown; + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNodeWithId | null { const { @@ -1359,7 +1368,7 @@ function snapshot( * - `src` attribute in `img` tags. * - `srcset` attribute in `img` tags. */ - onAssetDetected?: (result: { urls: string[] }) => unknown; + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNodeWithId | null { const { diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 3b306c950e..30d104278b 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -425,8 +425,8 @@ function record( stylesheetManager.attachLinkElement(linkEl, childSn); }, onAssetDetected: (assets) => { - assets.urls.forEach((url) => { - assetManager.capture(url); + assets.forEach((asset) => { + assetManager.capture(asset); }); }, keepIframeSrcFn, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 079b9ae18c..437cb4670d 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -338,8 +338,8 @@ export default class MutationBuffer { this.stylesheetManager.attachLinkElement(link, childSn); }, onAssetDetected: (assets) => { - assets.urls.forEach((url) => { - this.assetManager.capture(url); + assets.forEach((asset) => { + this.assetManager.capture(asset); }); }, }); @@ -634,10 +634,16 @@ export default class MutationBuffer { ) { if (attributeName === 'srcset') { getSourcesFromSrcset(transformedValue).forEach((url) => { - this.assetManager.capture(url); + this.assetManager.capture({ + element: target, + url, + }); }); } else { - this.assetManager.capture(transformedValue); + this.assetManager.capture({ + element: target, + url: transformedValue, + }); } attributeName = `rr_captured_${attributeName}`; } diff --git a/packages/rrweb/src/record/observers/asset-manager.ts b/packages/rrweb/src/record/observers/asset-manager.ts index c7e0981b85..27fd63f71f 100644 --- a/packages/rrweb/src/record/observers/asset-manager.ts +++ b/packages/rrweb/src/record/observers/asset-manager.ts @@ -3,6 +3,7 @@ import type { SerializedCanvasArg, eventWithTime, listenerHandler, + asset, } from '@rrweb/types'; import type { assetCallback } from '@rrweb/types'; import { encode } from 'base64-arraybuffer'; @@ -125,7 +126,13 @@ export default class AssetManager { } } - public capture(url: string): { + public capture(asset: asset): { + status: 'capturing' | 'captured' | 'error' | 'refused'; + } { + this.captureUrl(asset.url); + } + + public captureUrl(url): { status: 'capturing' | 'captured' | 'error' | 'refused'; } { if (this.shouldIgnore(url)) return { status: 'refused' }; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 038831dbb2..a72bbaf9c5 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -82,6 +82,11 @@ export type assetEvent = { data: assetParam; }; +export type asset = { + element: HTMLElement; + url: string; +}; + export enum IncrementalSource { Mutation, MouseMove,