Decrease embedded img size for inlineImages (#836)

* Decrease embedded img size for inlineImages

* Fix the test

* Use webp for image snapshots

* Implemented optional param dataURLOptions
This commit is contained in:
Cristi Constantin
2022-02-25 04:45:53 +00:00
committed by GitHub
parent e9531d420a
commit e104300f25
5 changed files with 33 additions and 8 deletions

View File

@@ -7,6 +7,7 @@ import {
idNodeMap, idNodeMap,
MaskInputOptions, MaskInputOptions,
SlimDOMOptions, SlimDOMOptions,
DataURLOptions,
MaskTextFn, MaskTextFn,
MaskInputFn, MaskInputFn,
KeepIframeSrcFn, KeepIframeSrcFn,
@@ -378,6 +379,7 @@ function serializeNode(
maskInputOptions: MaskInputOptions; maskInputOptions: MaskInputOptions;
maskTextFn: MaskTextFn | undefined; maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined; maskInputFn: MaskInputFn | undefined;
dataURLOptions?: DataURLOptions,
inlineImages: boolean; inlineImages: boolean;
recordCanvas: boolean; recordCanvas: boolean;
keepIframeSrcFn: KeepIframeSrcFn; keepIframeSrcFn: KeepIframeSrcFn;
@@ -393,6 +395,7 @@ function serializeNode(
maskInputOptions = {}, maskInputOptions = {},
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
dataURLOptions = {},
inlineImages, inlineImages,
recordCanvas, recordCanvas,
keepIframeSrcFn, keepIframeSrcFn,
@@ -513,17 +516,17 @@ function serializeNode(
if ((n as ICanvas).__context === '2d') { if ((n as ICanvas).__context === '2d') {
// only record this on 2d canvas // only record this on 2d canvas
if (!is2DCanvasBlank(n as HTMLCanvasElement)) { if (!is2DCanvasBlank(n as HTMLCanvasElement)) {
attributes.rr_dataURL = (n as HTMLCanvasElement).toDataURL(); attributes.rr_dataURL = (n as HTMLCanvasElement).toDataURL(dataURLOptions.type, dataURLOptions.quality);
} }
} else if (!('__context' in n)) { } else if (!('__context' in n)) {
// context is unknown, better not call getContext to trigger it // context is unknown, better not call getContext to trigger it
const canvasDataURL = (n as HTMLCanvasElement).toDataURL(); const canvasDataURL = (n as HTMLCanvasElement).toDataURL(dataURLOptions.type, dataURLOptions.quality);
// create blank canvas of same dimensions // create blank canvas of same dimensions
const blankCanvas = document.createElement('canvas'); const blankCanvas = document.createElement('canvas');
blankCanvas.width = (n as HTMLCanvasElement).width; blankCanvas.width = (n as HTMLCanvasElement).width;
blankCanvas.height = (n as HTMLCanvasElement).height; blankCanvas.height = (n as HTMLCanvasElement).height;
const blankCanvasDataURL = blankCanvas.toDataURL(); const blankCanvasDataURL = blankCanvas.toDataURL(dataURLOptions.type, dataURLOptions.quality);
// no need to save dataURL if it's the same as blank canvas // no need to save dataURL if it's the same as blank canvas
if (canvasDataURL !== blankCanvasDataURL) { if (canvasDataURL !== blankCanvasDataURL) {
@@ -545,7 +548,7 @@ function serializeNode(
canvasService!.width = image.naturalWidth; canvasService!.width = image.naturalWidth;
canvasService!.height = image.naturalHeight; canvasService!.height = image.naturalHeight;
canvasCtx!.drawImage(image, 0, 0); canvasCtx!.drawImage(image, 0, 0);
attributes.rr_dataURL = canvasService!.toDataURL(); attributes.rr_dataURL = canvasService!.toDataURL(dataURLOptions.type, dataURLOptions.quality);
} catch (err) { } catch (err) {
console.warn( console.warn(
`Cannot inline img src=${image.currentSrc}! Error: ${err}`, `Cannot inline img src=${image.currentSrc}! Error: ${err}`,
@@ -774,6 +777,7 @@ export function serializeNodeWithId(
maskTextFn: MaskTextFn | undefined; maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined; maskInputFn: MaskInputFn | undefined;
slimDOMOptions: SlimDOMOptions; slimDOMOptions: SlimDOMOptions;
dataURLOptions?: DataURLOptions;
keepIframeSrcFn?: KeepIframeSrcFn; keepIframeSrcFn?: KeepIframeSrcFn;
inlineImages?: boolean; inlineImages?: boolean;
recordCanvas?: boolean; recordCanvas?: boolean;
@@ -796,6 +800,7 @@ export function serializeNodeWithId(
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
slimDOMOptions, slimDOMOptions,
dataURLOptions = {},
inlineImages = false, inlineImages = false,
recordCanvas = false, recordCanvas = false,
onSerialize, onSerialize,
@@ -814,6 +819,7 @@ export function serializeNodeWithId(
maskInputOptions, maskInputOptions,
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
dataURLOptions,
inlineImages, inlineImages,
recordCanvas, recordCanvas,
keepIframeSrcFn, keepIframeSrcFn,
@@ -880,6 +886,7 @@ export function serializeNodeWithId(
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
slimDOMOptions, slimDOMOptions,
dataURLOptions,
inlineImages, inlineImages,
recordCanvas, recordCanvas,
preserveWhiteSpace, preserveWhiteSpace,
@@ -933,6 +940,7 @@ export function serializeNodeWithId(
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
slimDOMOptions, slimDOMOptions,
dataURLOptions,
inlineImages, inlineImages,
recordCanvas, recordCanvas,
preserveWhiteSpace, preserveWhiteSpace,
@@ -966,6 +974,7 @@ function snapshot(
maskTextFn?: MaskTextFn; maskTextFn?: MaskTextFn;
maskInputFn?: MaskTextFn; maskInputFn?: MaskTextFn;
slimDOM?: boolean | SlimDOMOptions; slimDOM?: boolean | SlimDOMOptions;
dataURLOptions?: DataURLOptions,
inlineImages?: boolean; inlineImages?: boolean;
recordCanvas?: boolean; recordCanvas?: boolean;
preserveWhiteSpace?: boolean; preserveWhiteSpace?: boolean;
@@ -987,6 +996,7 @@ function snapshot(
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
slimDOM = false, slimDOM = false,
dataURLOptions,
preserveWhiteSpace, preserveWhiteSpace,
onSerialize, onSerialize,
onIframeLoad, onIframeLoad,
@@ -1051,6 +1061,7 @@ function snapshot(
maskTextFn, maskTextFn,
maskInputFn, maskInputFn,
slimDOMOptions, slimDOMOptions,
dataURLOptions,
inlineImages, inlineImages,
recordCanvas, recordCanvas,
preserveWhiteSpace, preserveWhiteSpace,

View File

@@ -112,6 +112,11 @@ export type SlimDOMOptions = Partial<{
headMetaVerification: boolean; headMetaVerification: boolean;
}>; }>;
export type DataURLOptions = Partial<{
type: string;
quality: number;
}>;
export type MaskTextFn = (text: string) => string; export type MaskTextFn = (text: string) => string;
export type MaskInputFn = (text: string) => string; export type MaskInputFn = (text: string) => string;

View File

@@ -199,14 +199,17 @@ iframe.contentDocument.querySelector('center').clientHeight
waitUntil: 'load', 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}); await page.evaluate(`${code}var snapshot = rrweb.snapshot(document, {
`); dataURLOptions: { type: "image/webp", quality: 0.8 },
inlineImages: true,
inlineStylesheet: false
})`);
await page.waitFor(100); await page.waitFor(100);
const snapshot = await page.evaluate( const snapshot = await page.evaluate(
'JSON.stringify(snapshot[0], null, 2);', '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/webp;base64,'));
}); });
}); });

View File

@@ -1,4 +1,4 @@
import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types'; import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types';
export declare const IGNORED_NODE = -2; export declare const IGNORED_NODE = -2;
export declare function absoluteToStylesheet(cssText: string | null, href: string): string; export declare function absoluteToStylesheet(cssText: string | null, href: string): string;
export declare function absoluteToDoc(doc: Document, attributeValue: string): string; export declare function absoluteToDoc(doc: Document, attributeValue: string): string;
@@ -18,6 +18,7 @@ export declare function serializeNodeWithId(n: Node | INode, options: {
maskTextFn: MaskTextFn | undefined; maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined; maskInputFn: MaskInputFn | undefined;
slimDOMOptions: SlimDOMOptions; slimDOMOptions: SlimDOMOptions;
dataURLOptions?: DataURLOptions;
keepIframeSrcFn?: KeepIframeSrcFn; keepIframeSrcFn?: KeepIframeSrcFn;
inlineImages?: boolean; inlineImages?: boolean;
recordCanvas?: boolean; recordCanvas?: boolean;
@@ -36,6 +37,7 @@ declare function snapshot(n: Document, options?: {
maskTextFn?: MaskTextFn; maskTextFn?: MaskTextFn;
maskInputFn?: MaskTextFn; maskInputFn?: MaskTextFn;
slimDOM?: boolean | SlimDOMOptions; slimDOM?: boolean | SlimDOMOptions;
dataURLOptions?: DataURLOptions;
inlineImages?: boolean; inlineImages?: boolean;
recordCanvas?: boolean; recordCanvas?: boolean;
preserveWhiteSpace?: boolean; preserveWhiteSpace?: boolean;

View File

@@ -91,6 +91,10 @@ export declare type SlimDOMOptions = Partial<{
headMetaAuthorship: boolean; headMetaAuthorship: boolean;
headMetaVerification: boolean; headMetaVerification: boolean;
}>; }>;
export declare type DataURLOptions = Partial<{
type: string;
quality: number;
}>;
export declare type MaskTextFn = (text: string) => string; export declare type MaskTextFn = (text: string) => string;
export declare type MaskInputFn = (text: string) => string; export declare type MaskInputFn = (text: string) => string;
export declare type KeepIframeSrcFn = (src: string) => boolean; export declare type KeepIframeSrcFn = (src: string) => boolean;