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

Conversation

YunFeng0817
Copy link
Member

@YunFeng0817 YunFeng0817 commented Sep 7, 2022

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: #669 (comment).

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

  1. add a new incremental event type: AdoptedStyleSheet.
  2. change the current styleSheetRule and styleDeclaration event and make them support tracking mutation of adoptedStyleSheets.
  3. totally remove virtualStylesheet from rrdom to reduce duplicate code.
  4. add support for replace and replaceSync APIs.
  5. refactor stylesheet-manager and move the main code of adopedStylesheets into it.
  6. 5 test cases (3 on the recording side and 2 on the replaying side).
  7. StyleMirror implementation and unit tests.
  8. refactor the stylesheet part in the replayer and add support for VirtualDom optimization.
  9. 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:

old-version.mp4

After this PR applied:

demo.mp4

YunFeng0817 and others added 30 commits August 14, 2022 19:26
@Juice10 Juice10 self-assigned this Sep 16, 2022
Copy link
Contributor

@Juice10 Juice10 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What a great addition to rrweb, thanks for that @Mark-Fenng!
I am however concerned about Safari and breaking playback in that browser, we currently aren't using any polyfills to add support for it in the playback. And on record we could also be breaking things there since we're setting properties which would otherwise be empty, preventing people from doing feature detection.
I made this mistake myself once in the past when building the first iteration of the stylesheet manager and I really wanted to add https://github.com/amilajack/eslint-plugin-compat to the project but as we where using tslint back then instead of eslint I wasn't able to. Maybe this is a good moment to add it since we are using eslint at the moment.

packages/rrweb/src/record/observer.ts Outdated Show resolved Hide resolved
packages/rrweb/src/record/observer.ts Show resolved Hide resolved
packages/rrweb/src/record/constructableStyleSheets.d.ts Outdated Show resolved Hide resolved
packages/rrweb/src/record/constructableStyleSheets.d.ts Outdated Show resolved Hide resolved
packages/rrweb/src/record/constructableStyleSheets.d.ts Outdated Show resolved Hide resolved
packages/rrweb/src/replay/index.ts Outdated Show resolved Hide resolved
YunFeng0817 and others added 4 commits September 17, 2022 09:34
Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
add more browser compatibility checks
@YunFeng0817
Copy link
Member Author

@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.

packages/rrweb/src/record/index.ts Outdated Show resolved Hide resolved
packages/rrweb/src/record/index.ts Outdated Show resolved Hide resolved
packages/rrweb/src/record/index.ts Outdated Show resolved Hide resolved
packages/rrweb/src/record/index.ts Outdated Show resolved Hide resolved
packages/rrdom/test/diff.test.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@Juice10 Juice10 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @Mark-Fenng! Thanks for the changes, I think this is a great addition to rrweb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot record web Component css
4 participants