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

Impl record iframe #481

Merged
merged 18 commits into from
Feb 10, 2021
Merged

Impl record iframe #481

merged 18 commits into from
Feb 10, 2021

Conversation

Yuyz0112
Copy link
Member

@Yuyz0112 Yuyz0112 commented Feb 6, 2021

The design of nest recording

Nest recording, including same-origin iframe, cross-origin iframe, and shadow DOM, is a common feature request for rrweb in the past two years.

In this patch, we've set up the general nest recording framework and implemented recording same-origin iframes.

add the concept of root id

The above scenarios are considered as nest recording because their DOMs are isolated from the top-level DOM. So in rrweb's serialization result of DOM nodes, we need to record which document these nodes belong to. The way we achieve this is by adding a root id to serialized nodes.

With the root id, the replayer can always get the correct document object from the mirror map.

observe the nested documents after load

There are some design decisions about when to start to observe the nested documents.

As a short conclusion, we decide to start observing after documents loaded. This implementation helps us set up a consistent flow between loaded iframe, loading iframe, manual merge iframe, and shadow DOM. Also, it makes the rrweb-snapshot lib only expose hooks while the rrweb lib handles nested document management.

the flow of record a nested document

In this section, I will use the same-origin iframe as an example to describe the flow.

  1. Call the snapshot function to take a full snapshot of the top-level document.
  2. Use the onSerialize hook to listen to node serialization. When an iframe node has been serialized, add it to the iframe manager.
  3. Use the onIframeLoad hook to listen to nested document load events. The rrweb-snapshot lib has already done some hacks to make sure load events will fire. When the iframe load, attach the loaded contents to the iframe via iframe manager.
  4. The iframe manager will trigger the observation after an iframe load.
  5. Whenever a DOM add mutation has been observed(no matter top-level or nested), go to step 2 since the serializeNodeWithId function also has the onSerialize hook.

calculate dimensions during replay

Things like mouse movement need to aware of the dimension of its document. We can calculate the dimensions on the record side or the replay side.

Since the calculation needs to call the getBoundingClientRect function which may cause a reflow, we decide to do it on the replay side to avoid potential performance issues.

things need a check

To minimize the changes to the implementation of the mutation buffer, we extend the single mutation buffer to an array. We may have better choices such as having a single buffer state management and multiple buffer instance, but it can be done in a separate patch.

things not working

  1. Some observers are implemented by patching window properties that may not be working in nested documents. We need to patch nested windows in nested scopes.
  2. Record a rrweb replayer will fail because of the __sn property confliction. Maybe we can use a symbol or a different property namespace to store serialized information on the replay side.

Yuyz0112 and others added 11 commits February 6, 2021 13:09
…playback is paused (#428)

set pauseAnimation to true by default
…"virtual parent" optimization (#427)

* fix: elements would lose some state like scroll position because of "virtual parent" optimization

* refactor: the bugfix code

bug: elements would lose some state like scroll position because of "virtual parent" optimization

* fix: an error occured at applyMutation(remove nodes part)

error message:
Uncaught (in promise) DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node
@Yuyz0112 Yuyz0112 mentioned this pull request Feb 6, 2021
@Yuyz0112
Copy link
Member Author

Yuyz0112 commented Feb 7, 2021

patch for rrweb-snapshot: rrweb-io/rrweb-snapshot#63

@Yuyz0112
Copy link
Member Author

Yuyz0112 commented Feb 7, 2021

@eoghanmurray I think the mutation buffer array needs a review from you.

@Yuyz0112 Yuyz0112 marked this pull request as ready for review February 8, 2021 01:50
@Yuyz0112 Yuyz0112 merged commit f3d7fa3 into master Feb 10, 2021
eoghanmurray pushed a commit to statcounter/rrweb that referenced this pull request Feb 22, 2021
* Impl record iframe

* iframe observe

* temp: add bundle file to git

* update bundle

* update with pick

* update bundle

* fix fragment map remove

* feat: add an option to determine whether to pause CSS animation when playback is paused (rrweb-io#428)

set pauseAnimation to true by default

* fix: elements would lose some states like scroll position because of "virtual parent" optimization (rrweb-io#427)

* fix: elements would lose some state like scroll position because of "virtual parent" optimization

* refactor: the bugfix code

bug: elements would lose some state like scroll position because of "virtual parent" optimization

* fix: an error occured at applyMutation(remove nodes part)

error message:
Uncaught (in promise) DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node

* pick fixes

* revert ignore file

* re-impl iframe record

* re-impl iframe replay

* code housekeeping

* move multi layer dimension calculation to replay side

* update test cases

* teardown test server

* upgrade rrweb-snapshot with iframe load timeout

Co-authored-by: Lucky Feng <[email protected]>
@DeweyNULL
Copy link

不好意思,请问一下,iframe滚动的事件有记录吗?
当前iframe存在滚动条时,好像还不能记录到iframe滚动的事件

@Yuyz0112
Copy link
Member Author

@DeweyNULL

things not working:
Some observers are implemented by patching window properties that may not be working in nested documents. We need to patch nested windows in nested scopes.

@wanglei5516
Copy link

要怎么使用才能支持 iframe 呢?

@CathalG
Copy link

CathalG commented Sep 4, 2021

@Yuyz0112 @eoghanmurray

Hey guys, excellent work on this project, really impressive stuff.

I've recently implemented rrweb in the early stages of a project I'm working on. Recording and playback work well and as expected, but I've noticed in replay iframe elements aren't displaying at all. I'm looking at this piece of work and apologies if missing something obvious but I have a couple of questions:

  1. Should the above code render an cross-origin iframe in replay? (For example a YT embed) or does it require rrweb on the document loaded in the cross-origin iframe?

  2. If not, is there a way you guys know of to implement a static screenshot, or even a fallback for these elements as opposed to blank space in the replayer?

Thanks in advance
Cathal

@eoghanmurray
Copy link
Contributor

@CathalG no, by design the containing page has no access to the contents of a 3rd party iframe. This is so that an arbitrary page can't maliciously include an iframe from a third party website in order to read (via javascript) the private contents of another website.
The containing page has no way of generating a screenshot for the same reason.

For the case of a YT embed, the rrweb approach should be to attempt to re-embed the youtube video, which may or may not give the same content as was originally seen on the website. I'm not sure if this was or was not working for you; this is a grey area and there could have been other security barriers to this working on your replay domain.

(Aside there's a slack channel that might be more suitable for this sort of inquiry)

@CathalG
Copy link

CathalG commented Sep 8, 2021

@eoghanmurray Thanks for the response, that makes sense. The YouTube embed in my case may well just have been blocked as I'm currently running locally. I'll take note to ask in the Slack channel going forward.

Cheers
Cathal

@vlazdimir
Copy link

vlazdimir commented Jul 28, 2022

@Yuyz0112 @eoghanmurray
Hey everyone, thanks for this lib, you are awesome!
I've took a short time investigating and using rrweb, and ran into a flow where recording same-origin (second-level domain actually) iframe emits events containing coordinates relative to that iframe. Is this the desired flow or I'm doing something wrong? Kinda expected for coordinates to be calculated based on root window.

Is this the PR that answers my question?

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

Successfully merging this pull request may close these issues.

7 participants