rrweb
rrweb copied to clipboard
feat: add support for recording and replaying adoptedStyleSheets API
Background
The adoptedStyleSheets property of the Document or shadowRoot is used for setting an array of constructed stylesheets to be used by the host. reference: https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
Nowadays, more and more web libraries and pages are using custom web components and this API like Lit, material wc. There’s also a related issue #669.
Implementation
This PR mostly follows @Yuyz0112's proposal: https://github.com/rrweb-io/rrweb/issues/669#issuecomment-937522437.
I create a StyleMirror which is similar to Node Mirror that we used to manage nodes and their serialized ids. In the recording side, if I get a constructed stylesheet from adoptedStyleSheets API, firstly ask StyleMirror whether it appears before or not. If it’s a new stylesheet, I add it to the StyleMirror, assign a new unique styleId, and serialize it into an event.
Here’s an example event to serialize a new stylesheet object:
{
type: EventType.IncrementalSnapshot,
data: {
source: IncrementalSource.AdoptedStyleSheet,
id: 1, // id indicates the node id of document or shadow DOMs' host element.
// serialize the new stylesheet
styles: [
{
rules: [
{
rule: 'div { color: yellow; }',
},
],
styleId: 1,
},
],
styleIds: [1], // style ids of adopted stylesheets
}
}
If it already has a styleId, I will reference (reuse) it directly like this:
{
type: EventType.IncrementalSnapshot,
data: {
source: IncrementalSource.AdoptedStyleSheet,
id: 1, // id indicates the node id of document or shadow DOMs' host element.
styleIds: [1], // reference styleIds directly
}
}
On the replaying side, I deserialize stylesheet objects from events and assign them to the target documents.
Changes
- add a new incremental event type: AdoptedStyleSheet.
- change the current styleSheetRule and styleDeclaration event and make them support tracking mutation of adoptedStyleSheets.
- totally remove virtualStylesheet from rrdom to reduce duplicate code.
- add support for replace and replaceSync APIs.
- refactor stylesheet-manager and move the main code of adopedStylesheets into it.
- 5 test cases (3 on the recording side and 2 on the replaying side).
- StyleMirror implementation and unit tests.
- refactor the stylesheet part in the replayer and add support for VirtualDom optimization.
- patch adoptedStyleSheet API to track its modification after the full snapshot is created.
Other things
Because adoptedStypeSheets is a new API, there is a browser compatibility issue for the replaying side. The replayer can replay adoptedStyleSheet events only when the browser supports the API. reference: https://caniuse.com/?search=adoptedStyleSheets
Special thanks to @lele0108 's sponsorship for making this pull request possible.
Effect
Before this PR:
https://user-images.githubusercontent.com/27533910/188832637-cf87e5e8-178d-4e02-a4fd-6991df04bec2.mp4
After this PR applied:
https://user-images.githubusercontent.com/27533910/188832739-0ce45aac-a079-4a8a-9b1d-72412eb2e31f.mp4
Thank for for raising this @Mark-Fenng! We have noticed styling issues in a lot of our replays, and I suspect a good portion of them would be resolved with this support. Can we get this change approved and merged?
@dbseel This PR is relatively huge and it may take some time for Yuyz and Justin to review. I believe that it will get merged finally.
Thanks for the suggestions. There are many changes related to @Juice10's code base. I'll merge it after his review.
@Juice10 Geat catches on compatibility issues of these APIs. I also added the eslint-plugin-compat and configured the browser list for each package. If some APIs are rarely used in the package, and they are polyfilled or have a workaround in older browsers, I think this lint rule can be disabled. Otherwise, the browser list has to be updated.