@@ -352,6 +352,16 @@ function record<T = eventWithTime>(
|
||||
},
|
||||
}),
|
||||
),
|
||||
styleDeclarationCb: (r) =>
|
||||
wrappedEmit(
|
||||
wrapEvent({
|
||||
type: EventType.IncrementalSnapshot,
|
||||
data: {
|
||||
source: IncrementalSource.StyleDeclaration,
|
||||
...r,
|
||||
},
|
||||
}),
|
||||
),
|
||||
canvasMutationCb: (p) =>
|
||||
wrappedEmit(
|
||||
wrapEvent({
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
fontCallback,
|
||||
fontParam,
|
||||
Mirror,
|
||||
styleDeclarationCallback,
|
||||
} from '../types';
|
||||
import MutationBuffer from './mutation';
|
||||
import { IframeManager } from './iframe-manager';
|
||||
@@ -472,16 +473,18 @@ function initInputObserver(
|
||||
};
|
||||
}
|
||||
|
||||
function getNestedCSSRulePositions(rule: CSSStyleRule): number[] {
|
||||
const positions: Array<number> = [];
|
||||
function recurse(rule: CSSRule, pos: number[]) {
|
||||
if (rule.parentRule instanceof CSSGroupingRule) {
|
||||
const rules = Array.from((rule.parentRule as CSSGroupingRule).cssRules);
|
||||
const index = rules.indexOf(rule);
|
||||
function getNestedCSSRulePositions(rule: CSSRule): number[] {
|
||||
const positions: number[] = [];
|
||||
function recurse(childRule: CSSRule, pos: number[]) {
|
||||
if (childRule.parentRule instanceof CSSGroupingRule) {
|
||||
const rules = Array.from(
|
||||
(childRule.parentRule as CSSGroupingRule).cssRules,
|
||||
);
|
||||
const index = rules.indexOf(childRule);
|
||||
pos.unshift(index);
|
||||
} else {
|
||||
const rules = Array.from(rule.parentStyleSheet!.cssRules);
|
||||
const index = rules.indexOf(rule);
|
||||
const rules = Array.from(childRule.parentStyleSheet!.cssRules);
|
||||
const index = rules.indexOf(childRule);
|
||||
pos.unshift(index);
|
||||
}
|
||||
return pos;
|
||||
@@ -560,6 +563,60 @@ function initStyleSheetObserver(
|
||||
};
|
||||
}
|
||||
|
||||
function initStyleDeclarationObserver(
|
||||
cb: styleDeclarationCallback,
|
||||
mirror: Mirror,
|
||||
): listenerHandler {
|
||||
const setProperty = CSSStyleDeclaration.prototype.setProperty;
|
||||
CSSStyleDeclaration.prototype.setProperty = function (
|
||||
this: CSSStyleDeclaration,
|
||||
property,
|
||||
value,
|
||||
priority,
|
||||
) {
|
||||
const id = mirror.getId(
|
||||
(this.parentRule?.parentStyleSheet?.ownerNode as unknown) as INode,
|
||||
);
|
||||
if (id !== -1) {
|
||||
cb({
|
||||
id,
|
||||
set: {
|
||||
property,
|
||||
value,
|
||||
priority,
|
||||
},
|
||||
index: getNestedCSSRulePositions(this.parentRule!),
|
||||
});
|
||||
}
|
||||
return setProperty.apply(this, arguments);
|
||||
};
|
||||
|
||||
const removeProperty = CSSStyleDeclaration.prototype.removeProperty;
|
||||
CSSStyleDeclaration.prototype.removeProperty = function (
|
||||
this: CSSStyleDeclaration,
|
||||
property,
|
||||
) {
|
||||
const id = mirror.getId(
|
||||
(this.parentRule?.parentStyleSheet?.ownerNode as unknown) as INode,
|
||||
);
|
||||
if (id !== -1) {
|
||||
cb({
|
||||
id,
|
||||
remove: {
|
||||
property,
|
||||
},
|
||||
index: getNestedCSSRulePositions(this.parentRule!),
|
||||
});
|
||||
}
|
||||
return removeProperty.apply(this, arguments);
|
||||
};
|
||||
|
||||
return () => {
|
||||
CSSStyleDeclaration.prototype.setProperty = setProperty;
|
||||
CSSStyleDeclaration.prototype.removeProperty = removeProperty;
|
||||
};
|
||||
}
|
||||
|
||||
function initMediaInteractionObserver(
|
||||
mediaInteractionCb: mediaInteractionCallback,
|
||||
blockClass: blockClass,
|
||||
@@ -725,6 +782,7 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
inputCb,
|
||||
mediaInteractionCb,
|
||||
styleSheetRuleCb,
|
||||
styleDeclarationCb,
|
||||
canvasMutationCb,
|
||||
fontCb,
|
||||
} = o;
|
||||
@@ -776,6 +834,12 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
||||
}
|
||||
styleSheetRuleCb(...p);
|
||||
};
|
||||
o.styleDeclarationCb = (...p: Arguments<styleDeclarationCallback>) => {
|
||||
if (hooks.styleDeclaration) {
|
||||
hooks.styleDeclaration(...p);
|
||||
}
|
||||
styleDeclarationCb(...p);
|
||||
};
|
||||
o.canvasMutationCb = (...p: Arguments<canvasMutationCallback>) => {
|
||||
if (hooks.canvasMutation) {
|
||||
hooks.canvasMutation(...p);
|
||||
@@ -854,6 +918,10 @@ export function initObservers(
|
||||
o.styleSheetRuleCb,
|
||||
o.mirror,
|
||||
);
|
||||
const styleDeclarationObserver = initStyleDeclarationObserver(
|
||||
o.styleDeclarationCb,
|
||||
o.mirror,
|
||||
);
|
||||
const canvasMutationObserver = o.recordCanvas
|
||||
? initCanvasMutationObserver(o.canvasMutationCb, o.blockClass, o.mirror)
|
||||
: () => {};
|
||||
@@ -873,6 +941,7 @@ export function initObservers(
|
||||
inputHandler();
|
||||
mediaInteractionHandler();
|
||||
styleSheetObserver();
|
||||
styleDeclarationObserver();
|
||||
canvasMutationObserver();
|
||||
fontObserver();
|
||||
pluginHandlers.forEach((h) => h());
|
||||
|
||||
@@ -79,10 +79,12 @@ const defaultMouseTailConfig = {
|
||||
} as const;
|
||||
|
||||
function indicatesTouchDevice(e: eventWithTime) {
|
||||
return e.type == EventType.IncrementalSnapshot &&
|
||||
return (
|
||||
e.type == EventType.IncrementalSnapshot &&
|
||||
(e.data.source == IncrementalSource.TouchMove ||
|
||||
(e.data.source == IncrementalSource.MouseInteraction &&
|
||||
e.data.type == MouseInteractions.TouchStart));
|
||||
(e.data.source == IncrementalSource.MouseInteraction &&
|
||||
e.data.type == MouseInteractions.TouchStart))
|
||||
);
|
||||
}
|
||||
|
||||
export class Replayer {
|
||||
@@ -266,7 +268,6 @@ export class Replayer {
|
||||
if (this.service.state.context.events.find(indicatesTouchDevice)) {
|
||||
this.mouse.classList.add('touch-device');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public on(event: string, handler: Handler) {
|
||||
@@ -489,7 +490,13 @@ export class Replayer {
|
||||
castFn();
|
||||
}
|
||||
if (this.mousePos) {
|
||||
this.moveAndHover(this.mousePos.x, this.mousePos.y, this.mousePos.id, true, this.mousePos.debugData);
|
||||
this.moveAndHover(
|
||||
this.mousePos.x,
|
||||
this.mousePos.y,
|
||||
this.mousePos.id,
|
||||
true,
|
||||
this.mousePos.debugData,
|
||||
);
|
||||
}
|
||||
this.mousePos = null;
|
||||
if (this.touchActive === true) {
|
||||
@@ -952,14 +959,14 @@ export class Replayer {
|
||||
void this.mouse.offsetWidth;
|
||||
this.mouse.classList.add('active');
|
||||
} else if (d.type === MouseInteractions.TouchStart) {
|
||||
void this.mouse.offsetWidth; // needed for the position update of moveAndHover to apply without the .touch-active transition
|
||||
void this.mouse.offsetWidth; // needed for the position update of moveAndHover to apply without the .touch-active transition
|
||||
this.mouse.classList.add('touch-active');
|
||||
} else if (d.type === MouseInteractions.TouchEnd) {
|
||||
this.mouse.classList.remove('touch-active');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MouseInteractions.TouchCancel:
|
||||
case MouseInteractions.TouchCancel:
|
||||
if (isSync) {
|
||||
this.touchActive = false;
|
||||
} else {
|
||||
@@ -1138,6 +1145,62 @@ export class Replayer {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IncrementalSource.StyleDeclaration: {
|
||||
// same with StyleSheetRule
|
||||
const target = this.mirror.getNode(d.id);
|
||||
if (!target) {
|
||||
return this.debugNodeNotFound(d, d.id);
|
||||
}
|
||||
|
||||
const styleEl = (target as Node) as HTMLStyleElement;
|
||||
const parent = (target.parentNode as unknown) as INode;
|
||||
const usingVirtualParent = this.fragmentParentMap.has(parent);
|
||||
|
||||
const styleSheet = usingVirtualParent ? null : styleEl.sheet;
|
||||
let rules: VirtualStyleRules = [];
|
||||
|
||||
if (!styleSheet) {
|
||||
if (this.virtualStyleRulesMap.has(target)) {
|
||||
rules = this.virtualStyleRulesMap.get(target) as VirtualStyleRules;
|
||||
} else {
|
||||
rules = [];
|
||||
this.virtualStyleRulesMap.set(target, rules);
|
||||
}
|
||||
}
|
||||
|
||||
if (d.set) {
|
||||
if (styleSheet) {
|
||||
const rule = (getNestedRule(
|
||||
styleSheet.rules,
|
||||
d.index,
|
||||
) as unknown) as CSSStyleRule;
|
||||
rule.style.setProperty(d.set.property, d.set.value, d.set.priority);
|
||||
} else {
|
||||
rules.push({
|
||||
type: StyleRuleType.SetProperty,
|
||||
index: d.index,
|
||||
...d.set,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (d.remove) {
|
||||
if (styleSheet) {
|
||||
const rule = (getNestedRule(
|
||||
styleSheet.rules,
|
||||
d.index,
|
||||
) as unknown) as CSSStyleRule;
|
||||
rule.style.removeProperty(d.remove.property);
|
||||
} else {
|
||||
rules.push({
|
||||
type: StyleRuleType.RemoveProperty,
|
||||
index: d.index,
|
||||
...d.remove,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IncrementalSource.CanvasMutation: {
|
||||
if (!this.config.UNSAFE_replayCanvas) {
|
||||
return;
|
||||
@@ -1581,7 +1644,13 @@ export class Replayer {
|
||||
}
|
||||
}
|
||||
|
||||
private moveAndHover(x: number, y: number, id: number, isSync: boolean, debugData: incrementalData) {
|
||||
private moveAndHover(
|
||||
x: number,
|
||||
y: number,
|
||||
id: number,
|
||||
isSync: boolean,
|
||||
debugData: incrementalData,
|
||||
) {
|
||||
const target = this.mirror.getNode(id);
|
||||
if (!target) {
|
||||
return this.debugNodeNotFound(debugData, id);
|
||||
|
||||
@@ -4,6 +4,8 @@ export enum StyleRuleType {
|
||||
Insert,
|
||||
Remove,
|
||||
Snapshot,
|
||||
SetProperty,
|
||||
RemoveProperty,
|
||||
}
|
||||
|
||||
type InsertRule = {
|
||||
@@ -19,8 +21,22 @@ type SnapshotRule = {
|
||||
type: StyleRuleType.Snapshot;
|
||||
cssTexts: string[];
|
||||
};
|
||||
type SetPropertyRule = {
|
||||
type: StyleRuleType.SetProperty;
|
||||
index: number[];
|
||||
property: string;
|
||||
value: string | null;
|
||||
priority: string | undefined;
|
||||
};
|
||||
type RemovePropertyRule = {
|
||||
type: StyleRuleType.RemoveProperty;
|
||||
index: number[];
|
||||
property: string;
|
||||
};
|
||||
|
||||
export type VirtualStyleRules = Array<InsertRule | RemoveRule | SnapshotRule>;
|
||||
export type VirtualStyleRules = Array<
|
||||
InsertRule | RemoveRule | SnapshotRule | SetPropertyRule | RemovePropertyRule
|
||||
>;
|
||||
export type VirtualStyleRulesMap = Map<INode, VirtualStyleRules>;
|
||||
|
||||
export function getNestedRule(
|
||||
@@ -88,6 +104,18 @@ export function applyVirtualStyleRulesToNode(
|
||||
}
|
||||
} else if (rule.type === StyleRuleType.Snapshot) {
|
||||
restoreSnapshotOfStyleRulesToNode(rule.cssTexts, styleNode);
|
||||
} else if (rule.type === StyleRuleType.SetProperty) {
|
||||
const nativeRule = (getNestedRule(
|
||||
styleNode.sheet!.cssRules,
|
||||
rule.index,
|
||||
) as unknown) as CSSStyleRule;
|
||||
nativeRule.style.setProperty(rule.property, rule.value, rule.priority);
|
||||
} else if (rule.type === StyleRuleType.RemoveProperty) {
|
||||
const nativeRule = (getNestedRule(
|
||||
styleNode.sheet!.cssRules,
|
||||
rule.index,
|
||||
) as unknown) as CSSStyleRule;
|
||||
nativeRule.style.removeProperty(rule.property);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ export enum IncrementalSource {
|
||||
Font,
|
||||
Log,
|
||||
Drag,
|
||||
StyleDeclaration,
|
||||
}
|
||||
|
||||
export type mutationData = {
|
||||
@@ -129,6 +130,10 @@ export type styleSheetRuleData = {
|
||||
source: IncrementalSource.StyleSheetRule;
|
||||
} & styleSheetRuleParam;
|
||||
|
||||
export type styleDeclarationData = {
|
||||
source: IncrementalSource.StyleDeclaration;
|
||||
} & styleDeclarationParam;
|
||||
|
||||
export type canvasMutationData = {
|
||||
source: IncrementalSource.CanvasMutation;
|
||||
} & canvasMutationParam;
|
||||
@@ -147,7 +152,8 @@ export type incrementalData =
|
||||
| mediaInteractionData
|
||||
| styleSheetRuleData
|
||||
| canvasMutationData
|
||||
| fontData;
|
||||
| fontData
|
||||
| styleDeclarationData;
|
||||
|
||||
export type event =
|
||||
| domContentLoadedEvent
|
||||
@@ -244,6 +250,7 @@ export type observerParam = {
|
||||
maskTextFn?: MaskTextFn;
|
||||
inlineStylesheet: boolean;
|
||||
styleSheetRuleCb: styleSheetRuleCallback;
|
||||
styleDeclarationCb: styleDeclarationCallback;
|
||||
canvasMutationCb: canvasMutationCallback;
|
||||
fontCb: fontCallback;
|
||||
sampling: SamplingStrategy;
|
||||
@@ -271,6 +278,7 @@ export type hooksParam = {
|
||||
input?: inputCallback;
|
||||
mediaInteaction?: mediaInteractionCallback;
|
||||
styleSheetRule?: styleSheetRuleCallback;
|
||||
styleDeclaration?: styleDeclarationCallback;
|
||||
canvasMutation?: canvasMutationCallback;
|
||||
font?: fontCallback;
|
||||
};
|
||||
@@ -407,6 +415,21 @@ export type styleSheetRuleParam = {
|
||||
|
||||
export type styleSheetRuleCallback = (s: styleSheetRuleParam) => void;
|
||||
|
||||
export type styleDeclarationParam = {
|
||||
id: number;
|
||||
index: number[];
|
||||
set?: {
|
||||
property: string;
|
||||
value: string | null;
|
||||
priority: string | undefined;
|
||||
};
|
||||
remove?: {
|
||||
property: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type styleDeclarationCallback = (s: styleDeclarationParam) => void;
|
||||
|
||||
export type canvasMutationCallback = (p: canvasMutationParam) => void;
|
||||
|
||||
export type canvasMutationParam = {
|
||||
|
||||
Reference in New Issue
Block a user