improve robustness of inlineImages feature (#812)
* fix: add inlineImages option to rrrweb recorder and try to make the code more robust * Improve robustness
This commit is contained in:
@@ -227,10 +227,13 @@ function buildNode(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (tagName === 'img' && name === 'rr_dataURL') {
|
} else if (tagName === 'img' && name === 'rr_dataURL') {
|
||||||
const image = (node as HTMLImageElement);
|
const image = node as HTMLImageElement;
|
||||||
if (!image.currentSrc.startsWith('data:')) {
|
if (!image.currentSrc.startsWith('data:')) {
|
||||||
// backup original img src
|
// Backup original img src. It may not have been set yet.
|
||||||
image.setAttribute('data-rrweb-src', image.currentSrc);
|
image.setAttribute(
|
||||||
|
'rrweb-original-src',
|
||||||
|
n.attributes['src'] as string,
|
||||||
|
);
|
||||||
image.src = value;
|
image.src = value;
|
||||||
}
|
}
|
||||||
image.removeAttribute(name);
|
image.removeAttribute(name);
|
||||||
|
|||||||
@@ -78,17 +78,6 @@ function extractOrigin(url: string): string {
|
|||||||
let canvasService: HTMLCanvasElement | null;
|
let canvasService: HTMLCanvasElement | null;
|
||||||
let canvasCtx: CanvasRenderingContext2D | null;
|
let canvasCtx: CanvasRenderingContext2D | null;
|
||||||
|
|
||||||
function initCanvasService(doc: Document) {
|
|
||||||
if (!canvasService) {
|
|
||||||
canvasService = doc.createElement('canvas');
|
|
||||||
}
|
|
||||||
if (!canvasCtx) {
|
|
||||||
canvasCtx = canvasService.getContext('2d');
|
|
||||||
}
|
|
||||||
canvasService.width = 0;
|
|
||||||
canvasService.height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm;
|
const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm;
|
||||||
const RELATIVE_PATH = /^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/|#).*/;
|
const RELATIVE_PATH = /^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/|#).*/;
|
||||||
const DATA_URI = /^(data:)([^,]*),(.*)/i;
|
const DATA_URI = /^(data:)([^,]*),(.*)/i;
|
||||||
@@ -515,14 +504,27 @@ function serializeNode(
|
|||||||
attributes.rr_dataURL = (n as HTMLCanvasElement).toDataURL();
|
attributes.rr_dataURL = (n as HTMLCanvasElement).toDataURL();
|
||||||
}
|
}
|
||||||
// save image offline
|
// save image offline
|
||||||
if (tagName === 'img' && inlineImages && canvasService && canvasCtx) {
|
if (tagName === 'img' && inlineImages) {
|
||||||
const image = (n as HTMLImageElement);
|
if (!canvasService) {
|
||||||
|
canvasService = doc.createElement('canvas');
|
||||||
|
canvasCtx = canvasService.getContext('2d');
|
||||||
|
}
|
||||||
|
const image = n as HTMLImageElement;
|
||||||
|
const oldValue = image.crossOrigin;
|
||||||
image.crossOrigin = 'anonymous';
|
image.crossOrigin = 'anonymous';
|
||||||
try {
|
try {
|
||||||
canvasService.width = image.naturalWidth;
|
const recordInlineImage = () => {
|
||||||
canvasService.height = image.naturalHeight;
|
canvasService!.width = image.naturalWidth;
|
||||||
canvasCtx.drawImage(image, 0, 0);
|
canvasService!.height = image.naturalHeight;
|
||||||
attributes.rr_dataURL = canvasService.toDataURL();
|
canvasCtx!.drawImage(image, 0, 0);
|
||||||
|
attributes.rr_dataURL = canvasService!.toDataURL();
|
||||||
|
oldValue
|
||||||
|
? (attributes.crossOrigin = oldValue)
|
||||||
|
: delete attributes.crossOrigin;
|
||||||
|
};
|
||||||
|
// The image content may not have finished loading yet.
|
||||||
|
if (image.complete && image.naturalWidth !== 0) recordInlineImage();
|
||||||
|
else image.onload = recordInlineImage;
|
||||||
} catch {
|
} catch {
|
||||||
// ignore error
|
// ignore error
|
||||||
}
|
}
|
||||||
@@ -832,9 +834,6 @@ export function serializeNodeWithId(
|
|||||||
) {
|
) {
|
||||||
preserveWhiteSpace = false;
|
preserveWhiteSpace = false;
|
||||||
}
|
}
|
||||||
if (inlineImages) {
|
|
||||||
initCanvasService(doc);
|
|
||||||
}
|
|
||||||
const bypassOptions = {
|
const bypassOptions = {
|
||||||
doc,
|
doc,
|
||||||
map,
|
map,
|
||||||
|
|||||||
@@ -194,22 +194,20 @@ iframe.contentDocument.querySelector('center').clientHeight
|
|||||||
|
|
||||||
it('correctly saves images offline', async () => {
|
it('correctly saves images offline', async () => {
|
||||||
const page: puppeteer.Page = await browser.newPage();
|
const page: puppeteer.Page = await browser.newPage();
|
||||||
// console for debug
|
|
||||||
// tslint:disable-next-line: no-console
|
|
||||||
page.on('console', (msg) => console.log(msg.text()));
|
|
||||||
|
|
||||||
await page.goto('http://localhost:3030/html/picture.html', { waitUntil: 'load' });
|
await page.goto('http://localhost:3030/html/picture.html', {
|
||||||
|
waitUntil: 'load',
|
||||||
|
});
|
||||||
await page.waitForSelector('img', { timeout: 1000 });
|
await page.waitForSelector('img', { timeout: 1000 });
|
||||||
|
await page.evaluate(`${code}var snapshot = rrweb.snapshot(document, {inlineImages: true, inlineStylesheet: false});
|
||||||
const snapshot = await page.evaluate(`${code}
|
|
||||||
const [snap] = rrweb.snapshot(document, {inlineImages: true, inlineStylesheet: false});
|
|
||||||
JSON.stringify(snap, null, 2);
|
|
||||||
`);
|
`);
|
||||||
|
await page.waitFor(100);
|
||||||
|
const snapshot = await page.evaluate(
|
||||||
|
'JSON.stringify(snapshot[0], null, 2);',
|
||||||
|
);
|
||||||
assert(snapshot.includes('"rr_dataURL"'));
|
assert(snapshot.includes('"rr_dataURL"'));
|
||||||
assert(snapshot.includes('data:image/png;base64,'));
|
assert(snapshot.includes('data:image/png;base64,'));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('iframe integration tests', function (this: ISuite) {
|
describe('iframe integration tests', function (this: ISuite) {
|
||||||
|
|||||||
Reference in New Issue
Block a user