Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for recording and replaying adoptedStyleSheets API #989

Merged
merged 51 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
9c7e058
test(recording side): add test case for adopted stylesheets in shadow…
YunFeng0817 Aug 14, 2022
4c2ea18
add type definition for adopted StyleSheets
YunFeng0817 Aug 18, 2022
755d9f7
create a StyleSheet Mirror
YunFeng0817 Aug 19, 2022
5ef9a77
enable to record the outermost document's adoptedStyleSheet
YunFeng0817 Aug 19, 2022
2dd6ba0
enable to serialize all stylesheets in documents (iframe) and shadow …
YunFeng0817 Aug 19, 2022
20279ae
enable to record adopted stylesheets while building full snapshot
YunFeng0817 Aug 19, 2022
6cf04c9
Merge branch 'master' into construct-style
YunFeng0817 Aug 20, 2022
2fbe2e2
test: add test case for mutations on adoptedStyleSheets
YunFeng0817 Aug 20, 2022
655e158
defer to record adoptedStyleSheets to avoid create events before full…
YunFeng0817 Aug 20, 2022
cdf9a37
feat: enable to track the mutation of AdoptedStyleSheets
YunFeng0817 Aug 20, 2022
8002fb4
Merge branch 'fix-shadowdom-record' into construct-style
YunFeng0817 Aug 24, 2022
25a033f
fix: incorrect id conditional judgement
YunFeng0817 Aug 24, 2022
05072dc
test: add a test case for replaying side
YunFeng0817 Aug 24, 2022
0c4a915
tweak the style mirror for replayer
YunFeng0817 Aug 25, 2022
d3227fb
feat: enable to replay adoptedStyleSheet events
YunFeng0817 Aug 26, 2022
22127e2
fix: rule index wasn't recorded when serializing the adoptedStyleSheets
YunFeng0817 Aug 26, 2022
2a36b45
add test case for mutation of stylesheet objects and add support for …
YunFeng0817 Aug 28, 2022
e020877
refactor: improve the code quality
YunFeng0817 Aug 28, 2022
edd094a
feat: monkey patch adoptedStyleSheet API to track its modification
YunFeng0817 Aug 28, 2022
040a987
feat: add support for checkouting fullsnapshot
YunFeng0817 Aug 29, 2022
1df970a
CI: fix failed type checks
YunFeng0817 Aug 29, 2022
ba0bcf8
test: add test case for nested shadow doms and iframe elements
YunFeng0817 Sep 5, 2022
ae4a0ec
feat: add support for adoptedStyleSheets in VirtualDom mode
YunFeng0817 Sep 5, 2022
b25f1c6
Merge branch 'master' into construct-style
YunFeng0817 Sep 5, 2022
d029ef6
style: format files
YunFeng0817 Sep 5, 2022
09797c3
test: improve the robustness of the test case
YunFeng0817 Sep 5, 2022
cf6ef45
Merge branch 'master' into construct-style
YunFeng0817 Sep 6, 2022
0c35311
CI: fix an eslint error
YunFeng0817 Sep 6, 2022
48eed10
test: improve the robustness of the test case
YunFeng0817 Sep 6, 2022
abf5135
fix: adoptedStyleSheets not applied in fast-forward mode (virtual dom…
YunFeng0817 Sep 7, 2022
89c9f91
refactor the data structure of adoptedStyleSheet event to make it mor…
YunFeng0817 Sep 7, 2022
64d3c4e
improve the robustness in the live mode
YunFeng0817 Sep 7, 2022
07adf2c
Merge branch 'master' into construct-style
YunFeng0817 Sep 7, 2022
ee4e879
apply Yanzhen's review suggestion
YunFeng0817 Sep 8, 2022
ef24e91
Merge branch 'master' into construct-style
YunFeng0817 Sep 9, 2022
b443645
update action name
YunFeng0817 Sep 9, 2022
aea3020
Merge branch 'master' into construct-style
YunFeng0817 Sep 15, 2022
ca9da9a
test: make the test case more robust for travis CI
YunFeng0817 Sep 15, 2022
7e633e5
Update packages/rrweb/src/record/constructableStyleSheets.d.ts
YunFeng0817 Sep 17, 2022
af458d3
Update packages/rrweb/src/record/constructableStyleSheets.d.ts
YunFeng0817 Sep 17, 2022
4fdb3e7
apply Justin's review suggestions
YunFeng0817 Sep 17, 2022
2d9aa0b
add eslint-plugin-compat and config
YunFeng0817 Sep 17, 2022
9aa01bd
fix record test type errors
YunFeng0817 Sep 17, 2022
1662f8f
make Mirror's replace function act the same with the original one whe…
YunFeng0817 Sep 17, 2022
e759e6f
Merge remote-tracking branch 'origin/master' into construct-style
YunFeng0817 Sep 22, 2022
f1c0406
test: increase the robustness of test cases
YunFeng0817 Sep 23, 2022
4e7c293
remove eslint disable in favor of feature detection
Juice10 Sep 26, 2022
6bd919a
Remove eslint-disable-next-line compat/compat
Juice10 Sep 26, 2022
7f0c3df
Standardize browserslist and remove lint exceptions (#1010)
Juice10 Sep 26, 2022
9006abe
test: revert deleting virtual style tests and rewrite them to fit the…
YunFeng0817 Sep 29, 2022
71916d6
Merge branch 'master' into construct-style
YunFeng0817 Sep 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/style-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: yarn lint:report
# Continue to the next step even if this fails
continue-on-error: true
- name: Upload ESLint report
- name: Upload ESLint Report
uses: actions/upload-artifact@v3
with:
name: eslint_report.json
Expand All @@ -50,7 +50,7 @@ jobs:
report-json: 'eslint_report.json'

prettier_check:
# In the forked PR, it's hard to format code and push to the branch directly.
# In the forked PR, it's hard to format code and push to the branch directly, so the action only check the format correctness.
if: github.event_name != 'push' && github.event.pull_request.head.repo.full_name != 'rrweb-io/rrweb'
runs-on: ubuntu-latest
name: Format Check
Expand All @@ -66,7 +66,7 @@ jobs:
cache: 'yarn'
- name: Install Dependencies
run: yarn
- name: Prettify code
- name: Prettier Check
run: yarn prettier --check '**/*.{ts,md}'

prettier:
Expand All @@ -86,9 +86,9 @@ jobs:
cache: 'yarn'
- name: Install Dependencies
run: yarn
- name: Prettify code
- name: Prettify Code
run: yarn prettier --write '**/*.{ts,md}'
- name: Commit changes
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Apply formatting changes
Expand Down
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,5 @@
"live-stream": "cd packages/rrweb && yarn live-stream",
"lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'",
"lint:report": "yarn eslint --output-file eslint_report.json --format json packages/*/src --ext .ts,.tsx,.js,.jsx"
},
"resolutions": {
"**/jsdom/cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz"
}
}
7 changes: 6 additions & 1 deletion packages/rrdom-nodejs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
},
"compileOnSave": true,
"exclude": ["test"],
"include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"],
"include": [
"src",
"test.d.ts",
"../rrweb/src/record/workers/workers.d.ts",
"../rrweb/src/record/constructableStyleSheets.d.ts"
],
"references": [
{
"path": "../rrdom"
Expand Down
127 changes: 16 additions & 111 deletions packages/rrdom/src/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type {
canvasEventWithTime,
inputData,
scrollData,
styleDeclarationData,
styleSheetRuleData,
} from 'rrweb/src/types';
import type {
IRRCDATASection,
Expand Down Expand Up @@ -79,6 +81,10 @@ export type ReplayerHandler = {
) => void;
applyInput: (data: inputData) => void;
applyScroll: (data: scrollData, isSync: boolean) => void;
applyStyleSheetMutation: (
data: styleDeclarationData | styleSheetRuleData,
styleSheet: CSSStyleSheet,
) => void;
};

export function diff(
Expand Down Expand Up @@ -159,20 +165,25 @@ export function diff(
}
break;
case 'STYLE':
applyVirtualStyleRulesToNode(
oldElement as HTMLStyleElement,
(newTree as RRStyleElement).rules,
);
{
const styleSheet = (oldElement as HTMLStyleElement).sheet;
styleSheet &&
(newTree as RRStyleElement).rules.forEach((data) =>
replayer.applyStyleSheetMutation(data, styleSheet),
);
}
break;
}
if (newRRElement.shadowRoot) {
if (!oldElement.shadowRoot) oldElement.attachShadow({ mode: 'open' });
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const oldChildren = oldElement.shadowRoot!.childNodes;
const newChildren = newRRElement.shadowRoot.childNodes;
if (oldChildren.length > 0 || newChildren.length > 0)
diffChildren(
Array.from(oldChildren),
newChildren,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
oldElement.shadowRoot!,
replayer,
rrnodeMirror,
Expand Down Expand Up @@ -333,6 +344,7 @@ function diffChildren(
}
indexInOld = oldIdToIndex[rrnodeMirror.getId(newStartNode)];
if (indexInOld) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const nodeToMove = oldChildren[indexInOld]!;
parentNode.insertBefore(nodeToMove, oldStartNode);
diff(nodeToMove, newStartNode, replayer, rrnodeMirror);
Expand Down Expand Up @@ -437,110 +449,3 @@ export function createOrGetNode(
if (sn) domMirror.add(node, { ...sn });
return node;
}

export function getNestedRule(
rules: CSSRuleList,
position: number[],
): CSSGroupingRule {
const rule = rules[position[0]] as CSSGroupingRule;
if (position.length === 1) {
return rule;
} else {
return getNestedRule(
(rule.cssRules[position[1]] as CSSGroupingRule).cssRules,
position.slice(2),
);
}
}

export enum StyleRuleType {
Insert,
Remove,
Snapshot,
SetProperty,
RemoveProperty,
}
type InsertRule = {
cssText: string;
type: StyleRuleType.Insert;
index?: number | number[];
};
type RemoveRule = {
type: StyleRuleType.Remove;
index: number | number[];
};
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 | SetPropertyRule | RemovePropertyRule
>;

export function getPositionsAndIndex(nestedIndex: number[]) {
const positions = [...nestedIndex];
const index = positions.pop();
return { positions, index };
}

export function applyVirtualStyleRulesToNode(
styleNode: HTMLStyleElement,
virtualStyleRules: VirtualStyleRules,
) {
const sheet = styleNode.sheet!;

virtualStyleRules.forEach((rule) => {
if (rule.type === StyleRuleType.Insert) {
try {
if (Array.isArray(rule.index)) {
const { positions, index } = getPositionsAndIndex(rule.index);
const nestedRule = getNestedRule(sheet.cssRules, positions);
nestedRule.insertRule(rule.cssText, index);
} else {
sheet.insertRule(rule.cssText, rule.index);
}
} catch (e) {
/**
* sometimes we may capture rules with browser prefix
* insert rule with prefixs in other browsers may cause Error
*/
}
} else if (rule.type === StyleRuleType.Remove) {
try {
if (Array.isArray(rule.index)) {
const { positions, index } = getPositionsAndIndex(rule.index);
const nestedRule = getNestedRule(sheet.cssRules, positions);
nestedRule.deleteRule(index || 0);
} else {
sheet.deleteRule(rule.index);
}
} catch (e) {
/**
* accessing styleSheet rules may cause SecurityError
* for specific access control settings
*/
}
} else if (rule.type === StyleRuleType.SetProperty) {
const nativeRule = (getNestedRule(
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(
sheet.cssRules,
rule.index,
) as unknown) as CSSStyleRule;
nativeRule.style.removeProperty(rule.property);
}
});
}
17 changes: 7 additions & 10 deletions packages/rrdom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import type {
canvasEventWithTime,
inputData,
scrollData,
styleSheetRuleData,
styleDeclarationData,
} from 'rrweb/src/types';
import type { VirtualStyleRules } from './diff';
import {
BaseRRNode as RRNode,
BaseRRCDATASectionImpl,
Expand Down Expand Up @@ -164,7 +165,7 @@ export class RRCanvasElement extends RRElement implements IRRElement {
}

export class RRStyleElement extends RRElement {
public rules: VirtualStyleRules = [];
public rules: (styleSheetRuleData | styleDeclarationData)[] = [];
}

export class RRIFrameElement extends RRElement {
Expand Down Expand Up @@ -312,7 +313,8 @@ export function buildFromDom(
}

if (node.nodeName === 'IFRAME') {
walk((node as HTMLIFrameElement).contentDocument!, rrNode);
const iframeDoc = (node as HTMLIFrameElement).contentDocument;
iframeDoc && walk(iframeDoc, rrNode);
} else if (
node.nodeType === NodeType.DOCUMENT_NODE ||
node.nodeType === NodeType.ELEMENT_NODE ||
Expand All @@ -323,6 +325,7 @@ export function buildFromDom(
node.nodeType === NodeType.ELEMENT_NODE &&
(node as HTMLElement).shadowRoot
)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
walk((node as HTMLElement).shadowRoot!, rrNode);
node.childNodes.forEach((childNode) => walk(childNode, rrNode));
}
Expand Down Expand Up @@ -475,11 +478,5 @@ function walk(node: IRRNode, mirror: IMirror<IRRNode>, blankSpace: string) {

export { RRNode };

export {
diff,
createOrGetNode,
StyleRuleType,
ReplayerHandler,
VirtualStyleRules,
} from './diff';
export { diff, createOrGetNode, ReplayerHandler } from './diff';
export * from './document';
Loading