@@ -60,7 +60,10 @@ type WindowWithAngularZone = Window & {
|
|||||||
|
|
||||||
export const mutationBuffers: MutationBuffer[] = [];
|
export const mutationBuffers: MutationBuffer[] = [];
|
||||||
|
|
||||||
const isCSSGroupingRuleSupported = typeof CSSGroupingRule !== "undefined"
|
const isCSSGroupingRuleSupported = typeof CSSGroupingRule !== 'undefined';
|
||||||
|
const isCSSMediaRuleSupported = typeof CSSMediaRule !== 'undefined';
|
||||||
|
const isCSSSupportsRuleSupported = typeof CSSSupportsRule !== 'undefined';
|
||||||
|
const isCSSConditionRuleSupported = typeof CSSConditionRule !== 'undefined';
|
||||||
|
|
||||||
function getEventTarget(event: Event): EventTarget | null {
|
function getEventTarget(event: Event): EventTarget | null {
|
||||||
try {
|
try {
|
||||||
@@ -475,12 +478,32 @@ function initInputObserver(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GroupingCSSRule =
|
||||||
|
| CSSGroupingRule
|
||||||
|
| CSSMediaRule
|
||||||
|
| CSSSupportsRule
|
||||||
|
| CSSConditionRule;
|
||||||
|
type GroupingCSSRuleTypes =
|
||||||
|
| typeof CSSGroupingRule
|
||||||
|
| typeof CSSMediaRule
|
||||||
|
| typeof CSSSupportsRule
|
||||||
|
| typeof CSSConditionRule;
|
||||||
|
|
||||||
function getNestedCSSRulePositions(rule: CSSRule): number[] {
|
function getNestedCSSRulePositions(rule: CSSRule): number[] {
|
||||||
const positions: number[] = [];
|
const positions: number[] = [];
|
||||||
function recurse(childRule: CSSRule, pos: number[]) {
|
function recurse(childRule: CSSRule, pos: number[]) {
|
||||||
if (isCSSGroupingRuleSupported && childRule.parentRule instanceof CSSGroupingRule) {
|
if (
|
||||||
|
(isCSSGroupingRuleSupported &&
|
||||||
|
childRule.parentRule instanceof CSSGroupingRule) ||
|
||||||
|
(isCSSMediaRuleSupported &&
|
||||||
|
childRule.parentRule instanceof CSSMediaRule) ||
|
||||||
|
(isCSSSupportsRuleSupported &&
|
||||||
|
childRule.parentRule instanceof CSSSupportsRule) ||
|
||||||
|
(isCSSConditionRuleSupported &&
|
||||||
|
childRule.parentRule instanceof CSSConditionRule)
|
||||||
|
) {
|
||||||
const rules = Array.from(
|
const rules = Array.from(
|
||||||
(childRule.parentRule as CSSGroupingRule).cssRules,
|
(childRule.parentRule as GroupingCSSRule).cssRules,
|
||||||
);
|
);
|
||||||
const index = rules.indexOf(childRule);
|
const index = rules.indexOf(childRule);
|
||||||
pos.unshift(index);
|
pos.unshift(index);
|
||||||
@@ -522,53 +545,78 @@ function initStyleSheetObserver(
|
|||||||
return deleteRule.apply(this, arguments);
|
return deleteRule.apply(this, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isCSSGroupingRuleSupported) {
|
const supportedNestedCSSRuleTypes: {
|
||||||
return () => {
|
[key: string]: GroupingCSSRuleTypes;
|
||||||
CSSStyleSheet.prototype.insertRule = insertRule;
|
} = {};
|
||||||
CSSStyleSheet.prototype.deleteRule = deleteRule;
|
if (isCSSGroupingRuleSupported) {
|
||||||
};
|
supportedNestedCSSRuleTypes['CSSGroupingRule'] = CSSGroupingRule;
|
||||||
|
} else {
|
||||||
|
// Some browsers (Safari) don't support CSSGroupingRule
|
||||||
|
// https://caniuse.com/?search=cssgroupingrule
|
||||||
|
// fall back to monkey patching classes that would have inherited from CSSGroupingRule
|
||||||
|
|
||||||
|
if (isCSSMediaRuleSupported) {
|
||||||
|
supportedNestedCSSRuleTypes['CSSMediaRule'] = CSSMediaRule;
|
||||||
|
}
|
||||||
|
if (isCSSConditionRuleSupported) {
|
||||||
|
supportedNestedCSSRuleTypes['CSSConditionRule'] = CSSConditionRule;
|
||||||
|
}
|
||||||
|
if (isCSSSupportsRuleSupported) {
|
||||||
|
supportedNestedCSSRuleTypes['CSSSupportsRule'] = CSSSupportsRule;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupingInsertRule = CSSGroupingRule.prototype.insertRule;
|
const unmodifiedFunctions: {
|
||||||
CSSGroupingRule.prototype.insertRule = function (
|
[key: string]: {
|
||||||
rule: string,
|
insertRule: (rule: string, index?: number) => number;
|
||||||
index?: number,
|
deleteRule: (index: number) => void;
|
||||||
) {
|
};
|
||||||
const id = mirror.getId(this.parentStyleSheet.ownerNode as INode);
|
} = {};
|
||||||
if (id !== -1) {
|
|
||||||
cb({
|
|
||||||
id,
|
|
||||||
adds: [
|
|
||||||
{
|
|
||||||
rule,
|
|
||||||
index: [
|
|
||||||
...getNestedCSSRulePositions(this),
|
|
||||||
index || 0, // defaults to 0
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return groupingInsertRule.apply(this, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
const groupingDeleteRule = CSSGroupingRule.prototype.deleteRule;
|
Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => {
|
||||||
CSSGroupingRule.prototype.deleteRule = function (index: number) {
|
unmodifiedFunctions[typeKey] = {
|
||||||
const id = mirror.getId(this.parentStyleSheet.ownerNode as INode);
|
insertRule: (type as GroupingCSSRuleTypes).prototype.insertRule,
|
||||||
if (id !== -1) {
|
deleteRule: (type as GroupingCSSRuleTypes).prototype.deleteRule,
|
||||||
cb({
|
};
|
||||||
id,
|
|
||||||
removes: [{ index: [...getNestedCSSRulePositions(this), index] }],
|
type.prototype.insertRule = function (rule: string, index?: number) {
|
||||||
});
|
const id = mirror.getId(this.parentStyleSheet.ownerNode as INode);
|
||||||
}
|
if (id !== -1) {
|
||||||
return groupingDeleteRule.apply(this, arguments);
|
cb({
|
||||||
};
|
id,
|
||||||
|
adds: [
|
||||||
|
{
|
||||||
|
rule,
|
||||||
|
index: [
|
||||||
|
...getNestedCSSRulePositions(this),
|
||||||
|
index || 0, // defaults to 0
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return unmodifiedFunctions[typeKey].insertRule.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
type.prototype.deleteRule = function (index: number) {
|
||||||
|
const id = mirror.getId(this.parentStyleSheet.ownerNode as INode);
|
||||||
|
if (id !== -1) {
|
||||||
|
cb({
|
||||||
|
id,
|
||||||
|
removes: [{ index: [...getNestedCSSRulePositions(this), index] }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return unmodifiedFunctions[typeKey].deleteRule.apply(this, arguments);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
CSSStyleSheet.prototype.insertRule = insertRule;
|
CSSStyleSheet.prototype.insertRule = insertRule;
|
||||||
CSSStyleSheet.prototype.deleteRule = deleteRule;
|
CSSStyleSheet.prototype.deleteRule = deleteRule;
|
||||||
CSSGroupingRule.prototype.insertRule = groupingInsertRule;
|
Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => {
|
||||||
CSSGroupingRule.prototype.deleteRule = groupingDeleteRule;
|
type.prototype.insertRule = unmodifiedFunctions[typeKey].insertRule;
|
||||||
|
type.prototype.deleteRule = unmodifiedFunctions[typeKey].deleteRule;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ describe('record', function (this: ISuite) {
|
|||||||
assertSnapshot(this.events, __filename, 'stylesheet-rules');
|
assertSnapshot(this.events, __filename, 'stylesheet-rules');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('captures nested stylesheet rules', async () => {
|
const captureNestedStylesheetRulesTest = async () => {
|
||||||
await this.page.evaluate(() => {
|
await this.page.evaluate(() => {
|
||||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||||
|
|
||||||
@@ -295,6 +295,21 @@ describe('record', function (this: ISuite) {
|
|||||||
expect(addRuleCount).to.equal(2);
|
expect(addRuleCount).to.equal(2);
|
||||||
expect(removeRuleCount).to.equal(1);
|
expect(removeRuleCount).to.equal(1);
|
||||||
assertSnapshot(this.events, __filename, 'nested-stylesheet-rules');
|
assertSnapshot(this.events, __filename, 'nested-stylesheet-rules');
|
||||||
|
};
|
||||||
|
it('captures nested stylesheet rules', captureNestedStylesheetRulesTest);
|
||||||
|
|
||||||
|
describe('without CSSGroupingRule support', () => {
|
||||||
|
// Safari currently doesn't support CSSGroupingRule, let's test without that
|
||||||
|
// https://caniuse.com/?search=CSSGroupingRule
|
||||||
|
beforeEach(async () => {
|
||||||
|
await this.page.evaluate(() => {
|
||||||
|
/* @ts-ignore: override CSSGroupingRule */
|
||||||
|
CSSGroupingRule = undefined;
|
||||||
|
});
|
||||||
|
// load a fresh rrweb recorder without CSSGroupingRule
|
||||||
|
await this.page.evaluate(this.code);
|
||||||
|
});
|
||||||
|
it('captures nested stylesheet rules', captureNestedStylesheetRulesTest);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user