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