Apply textContent on flush (#865)
* Apply textContent on flush * fix typo * Style sheet rules applied after <style>'s textContent override should work
This commit is contained in:
@@ -40,6 +40,7 @@ import {
|
|||||||
mouseMovePos,
|
mouseMovePos,
|
||||||
IWindow,
|
IWindow,
|
||||||
canvasMutationCommand,
|
canvasMutationCommand,
|
||||||
|
textMutation,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
createMirror,
|
createMirror,
|
||||||
@@ -171,11 +172,17 @@ export class Replayer {
|
|||||||
this.virtualStyleRulesMap = new Map();
|
this.virtualStyleRulesMap = new Map();
|
||||||
|
|
||||||
this.emitter.on(ReplayerEvents.Flush, () => {
|
this.emitter.on(ReplayerEvents.Flush, () => {
|
||||||
const { scrollMap, inputMap } = this.treeIndex.flush();
|
const { scrollMap, inputMap, mutationData } = this.treeIndex.flush();
|
||||||
|
|
||||||
this.fragmentParentMap.forEach((parent, frag) =>
|
this.fragmentParentMap.forEach((parent, frag) =>
|
||||||
this.restoreRealParent(frag, parent),
|
this.restoreRealParent(frag, parent),
|
||||||
);
|
);
|
||||||
|
// apply text needs to happen before virtual style rules gets applied
|
||||||
|
// as it can overwrite the contents of a stylesheet
|
||||||
|
for (const d of mutationData.texts) {
|
||||||
|
this.applyText(d, mutationData);
|
||||||
|
}
|
||||||
|
|
||||||
for (const node of this.virtualStyleRulesMap.keys()) {
|
for (const node of this.virtualStyleRulesMap.keys()) {
|
||||||
// restore css rules of style elements after they are mounted
|
// restore css rules of style elements after they are mounted
|
||||||
this.restoreNodeSheet(node);
|
this.restoreNodeSheet(node);
|
||||||
@@ -896,7 +903,16 @@ export class Replayer {
|
|||||||
case IncrementalSource.Mutation: {
|
case IncrementalSource.Mutation: {
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
d.adds.forEach((m) => this.treeIndex.add(m));
|
d.adds.forEach((m) => this.treeIndex.add(m));
|
||||||
d.texts.forEach((m) => this.treeIndex.text(m));
|
d.texts.forEach((m) => {
|
||||||
|
const target = this.mirror.getNode(m.id);
|
||||||
|
const parent = (target?.parentNode as unknown) as INode | null;
|
||||||
|
// remove any style rules that pending
|
||||||
|
// for stylesheets where the contents get replaced
|
||||||
|
if (parent && this.virtualStyleRulesMap.has(parent))
|
||||||
|
this.virtualStyleRulesMap.delete(parent);
|
||||||
|
|
||||||
|
this.treeIndex.text(m);
|
||||||
|
});
|
||||||
d.attributes.forEach((m) => this.treeIndex.attribute(m));
|
d.attributes.forEach((m) => this.treeIndex.attribute(m));
|
||||||
d.removes.forEach((m) => this.treeIndex.remove(m, this.mirror));
|
d.removes.forEach((m) => this.treeIndex.remove(m, this.mirror));
|
||||||
}
|
}
|
||||||
@@ -1677,6 +1693,18 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private applyText(d: textMutation, mutation: mutationData) {
|
||||||
|
const target = this.mirror.getNode(d.id);
|
||||||
|
if (!target) {
|
||||||
|
return this.debugNodeNotFound(mutation, d.id);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
((target as Node) as HTMLElement).textContent = d.value;
|
||||||
|
} catch (error) {
|
||||||
|
// for safe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private legacy_resolveMissingNode(
|
private legacy_resolveMissingNode(
|
||||||
map: missingNodeMap,
|
map: missingNodeMap,
|
||||||
parent: Node,
|
parent: Node,
|
||||||
|
|||||||
@@ -56,27 +56,27 @@ html.rrweb-paused *, html.rrweb-paused ::before, html.rrweb-paused ::after { ani
|
|||||||
file-cid-1
|
file-cid-1
|
||||||
@charset \\"utf-8\\";
|
@charset \\"utf-8\\";
|
||||||
|
|
||||||
.c011xx { padding: 1.3125rem; flex: 0 0 auto; width: 100%; }
|
.css-added-at-500 { padding: 1.3125rem; flex: 0 0 auto; width: 100%; }
|
||||||
|
|
||||||
|
|
||||||
file-cid-2
|
file-cid-2
|
||||||
@charset \\"utf-8\\";
|
@charset \\"utf-8\\";
|
||||||
|
|
||||||
.c01x { opacity: 1; transform: translateX(0px); }
|
.css-added-at-200-overwritten-at-3000 { opacity: 1; transform: translateX(0px); }
|
||||||
|
|
||||||
.css-added-at-400 { border: 1px solid blue; }
|
.css-added-at-400-overwritten-at-3000 { border: 1px solid blue; }
|
||||||
|
|
||||||
|
|
||||||
file-cid-3
|
file-cid-3
|
||||||
@charset \\"utf-8\\";
|
@charset \\"utf-8\\";
|
||||||
|
|
||||||
.css-1uxxxx3 { position: fixed; top: 0px; right: 0px; left: 4rem; z-index: 15; flex-shrink: 0; height: 0.25rem; overflow: hidden; background-color: rgb(17, 171, 209); }
|
.css-added-at-200 { position: fixed; top: 0px; right: 0px; left: 4rem; z-index: 15; flex-shrink: 0; height: 0.25rem; overflow: hidden; background-color: rgb(17, 171, 209); }
|
||||||
|
|
||||||
.css-1c9xxxx { height: 0.25rem; background-color: rgb(190, 232, 242); opacity: 0; transition: opacity 0.5s ease 0s; }
|
.css-added-at-200.alt { height: 0.25rem; background-color: rgb(190, 232, 242); opacity: 0; transition: opacity 0.5s ease 0s; }
|
||||||
|
|
||||||
.css-added-at-1000-deleted-at-2500 { display: flex; flex-direction: column; min-width: 60rem; min-height: 100vh; color: blue; }
|
.css-added-at-1000-deleted-at-2500 { display: flex; flex-direction: column; min-width: 60rem; min-height: 100vh; color: blue; }
|
||||||
|
|
||||||
.css-lsxxx { padding-left: 4rem; }
|
.css-added-at-200.alt2 { padding-left: 4rem; }
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const events: eventWithTime[] = [
|
|||||||
type: 3,
|
type: 3,
|
||||||
isStyle: true,
|
isStyle: true,
|
||||||
textContent:
|
textContent:
|
||||||
'\n.c01x {\n opacity: 1;\n transform: translateX(0);\n}\n',
|
'\n.css-added-at-200-overwritten-at-3000 {\n opacity: 1;\n transform: translateX(0);\n}\n',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -64,7 +64,7 @@ const events: eventWithTime[] = [
|
|||||||
tagName: 'style',
|
tagName: 'style',
|
||||||
attributes: {
|
attributes: {
|
||||||
_cssText:
|
_cssText:
|
||||||
'.css-1uxxxx3 { position: fixed; top: 0px; right: 0px; left: 4rem; z-index: 15; flex-shrink: 0; height: 0.25rem; overflow: hidden; background-color: rgb(17, 171, 209); }.css-1c9xxxx { height: 0.25rem; background-color: rgb(190, 232, 242); opacity: 0; transition: opacity 0.5s ease 0s; }.css-lsxxx { padding-left: 4rem; }',
|
'.css-added-at-200 { position: fixed; top: 0px; right: 0px; left: 4rem; z-index: 15; flex-shrink: 0; height: 0.25rem; overflow: hidden; background-color: rgb(17, 171, 209); }.css-added-at-200.alt { height: 0.25rem; background-color: rgb(190, 232, 242); opacity: 0; transition: opacity 0.5s ease 0s; }.css-added-at-200.alt2 { padding-left: 4rem; }',
|
||||||
'data-emotion': 'css',
|
'data-emotion': 'css',
|
||||||
},
|
},
|
||||||
childNodes: [
|
childNodes: [
|
||||||
@@ -111,7 +111,8 @@ const events: eventWithTime[] = [
|
|||||||
id: 101,
|
id: 101,
|
||||||
adds: [
|
adds: [
|
||||||
{
|
{
|
||||||
rule: '.css-added-at-400{border: 1px solid blue;}',
|
rule:
|
||||||
|
'.css-added-at-400-overwritten-at-3000 {border: 1px solid blue;}',
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -141,7 +142,7 @@ const events: eventWithTime[] = [
|
|||||||
type: 3,
|
type: 3,
|
||||||
isStyle: true,
|
isStyle: true,
|
||||||
textContent:
|
textContent:
|
||||||
'\n.c011xx {\n padding: 1.3125rem;\n flex: none;\n width: 100%;\n}\n',
|
'\n.css-added-at-500 {\n padding: 1.3125rem;\n flex: none;\n width: 100%;\n}\n',
|
||||||
},
|
},
|
||||||
nextId: null,
|
nextId: null,
|
||||||
parentId: 255,
|
parentId: 255,
|
||||||
@@ -184,6 +185,37 @@ const events: eventWithTime[] = [
|
|||||||
type: EventType.IncrementalSnapshot,
|
type: EventType.IncrementalSnapshot,
|
||||||
timestamp: now + 2500,
|
timestamp: now + 2500,
|
||||||
},
|
},
|
||||||
|
// overwrite all contents of stylesheet
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
texts: [
|
||||||
|
{
|
||||||
|
id: 102,
|
||||||
|
value: '.all-css-overwritten-at-3000 { color: indigo; }',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attributes: [],
|
||||||
|
removes: [],
|
||||||
|
adds: [],
|
||||||
|
source: IncrementalSource.Mutation,
|
||||||
|
},
|
||||||
|
type: EventType.IncrementalSnapshot,
|
||||||
|
timestamp: now + 3000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
id: 101,
|
||||||
|
adds: [
|
||||||
|
{
|
||||||
|
rule: '.css-added-at-3100{color:blue;}',
|
||||||
|
index: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
source: IncrementalSource.StyleSheetRule,
|
||||||
|
},
|
||||||
|
type: EventType.IncrementalSnapshot,
|
||||||
|
timestamp: now + 3100,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default events;
|
export default events;
|
||||||
|
|||||||
@@ -223,6 +223,36 @@ describe('replayer', function () {
|
|||||||
expect(result).toEqual(false);
|
expect(result).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should overwrite all StyleSheetRules by replacing style element's textContent while fast-forwarding", async () => {
|
||||||
|
await page.evaluate(`events = ${JSON.stringify(styleSheetRuleEvents)}`);
|
||||||
|
const result = await page.evaluate(`
|
||||||
|
const { Replayer } = rrweb;
|
||||||
|
const replayer = new Replayer(events);
|
||||||
|
replayer.pause(3500);
|
||||||
|
const rules = [...replayer.iframe.contentDocument.styleSheets].map(
|
||||||
|
(sheet) => [...sheet.rules],
|
||||||
|
).flat();
|
||||||
|
rules.some((x) => x.selectorText === '.css-added-at-200-overwritten-at-3000');
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply fast-forwarded StyleSheetRules that came after stylesheet textContent overwrite', async () => {
|
||||||
|
await page.evaluate(`events = ${JSON.stringify(styleSheetRuleEvents)}`);
|
||||||
|
const result = await page.evaluate(`
|
||||||
|
const { Replayer } = rrweb;
|
||||||
|
const replayer = new Replayer(events);
|
||||||
|
replayer.pause(3500);
|
||||||
|
const rules = [...replayer.iframe.contentDocument.styleSheets].map(
|
||||||
|
(sheet) => [...sheet.rules],
|
||||||
|
).flat();
|
||||||
|
rules.some((x) => x.selectorText === '.css-added-at-3100');
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('can fast-forward mutation events containing nested iframe elements', async () => {
|
it('can fast-forward mutation events containing nested iframe elements', async () => {
|
||||||
await page.evaluate(`
|
await page.evaluate(`
|
||||||
events = ${JSON.stringify(iframeEvents)};
|
events = ${JSON.stringify(iframeEvents)};
|
||||||
|
|||||||
Reference in New Issue
Block a user