Commit Graph

209 Commits

Author SHA1 Message Date
Yanzhen Yu
fa6fd6e2b9 catch unexpected errors during replay media interactions 2020-09-06 17:34:29 +08:00
Justin Halsall
df6d08db97 Only trigger waitForStylesheetLoad if not seeking (#326) 2020-09-06 16:49:44 +08:00
Justin Halsall
6934fab78d Update lastPlayedEvent in live mode (#327)
* Update lastPlayedEvent in live mode

* tricking travis-ci into running again
2020-09-05 17:26:48 +08:00
Justin Halsall
e717cda658 Fix live mode (#310)
* add failing test

* paused -> live now possible
2020-08-27 21:33:12 +08:00
yz-yu
772c0e021a record canvas mutations (#296)
* record canvas mutations

close #60, #261

This patch implements the canvas mutation observer.
It consists of both the record and the replay side changes.

In the record side, we add a `recordCanvas` flag to indicate
whether to record canvas elements and the flag defaults to false.
Different from our other observers, the canvas observer was
disabled by default. Because some applications with heavy canvas
usage may emit a lot of data as canvas changed, especially the
scenarios that use a lot of `drawImage` API.
So the behavior should be audited by users and only record canvas
when the flag was set to true.

In the replay side, we add a `UNSAFE_replayCanvas` flag to indicate
whether to replay canvas mutations.
Similar to the `recordCanvas` flag, `UNSAFE_replayCanvas` defaults
to false. But unlike the record canvas implementation is stable and
safe, the replay canvas implementation is UNSAFE.
It's unsafe because we need to add `allow-scripts` to the replay
sandbox, which may cause some unexpected script execution. Currently,
users should be aware of this implementation detail and enable this
feature carefully.

* update canvas integration test
2020-08-22 16:44:02 +08:00
yz-yu
5d83ee39fd close #280. Improve the performance of the DOM mutation observer. (#284)
This issue was originally reported in #280 but may also relate
to #167 and other potential performance issues in the recording.

In #206 I implemented the new mutation observer which will defer
the serialization of DOM, which helps us to have a consistent DOM
order for the replay.

In this implementation, we use an array to represent the `addQueue`.
Whenever we need to consume the queue, we will iterate it to make
sure there is no dead loop, and then shift the first item to see
whether it can be serialized at the new timing.

But this implementation may be very slow when there are a lot of newly
added DOM since it will do an O(n^2) iteration.

For example, if we have three newly added DOM `n1`, `n2`, `n3`,
the iteration looks like this:
```
[n1, n2, n3]
	n1 -> n2 -> n3, consume n3
[n1, n2]
	n1 -> n2, consume n2
[n1]
	n1, consume n1
```
We should have a better performance if te iteration looks like this:
```
[n1, n2, n3]
	n3, consume n3
[n1, n2]
	n2, consume n2
[n1]
	n1, consume n1
```

Simply reverse the mutation payload does not work, because it does
not always as same as the DOM order.

So in this patch, we replace the `addQueue` with a double linked list,
which can:
1. represent the DOM order in its data structure
2. has an O(1) time complexity when looking up the sibling of a list item
3. has an O(1) time complexity when removing a list item
2020-08-16 14:47:25 +08:00
Yanzhen Yu
be4cccd6e3 close #268 subscribe latest player state before resume 2020-08-09 13:05:55 +08:00
Yanzhen Yu
006b709c00 remove the internal use of resume API 2020-08-09 13:03:12 +08:00
Yanzhen Yu
54baa27b36 remove global body style 2020-08-09 12:50:38 +08:00
Yanzhen Yu
a90999d96e close #274 implement the new state management proposal 2020-08-08 17:07:37 +08:00
Eoghan Murray
6e53410e2b Reset the lastPlayedEvent as it would otherwise be used to discard events in machine.ts as follows: (#250) 2020-07-28 22:52:50 +08:00
Yanzhen Yu
7802916dfc close #263 Since we improved the block class strategy,
we need to check parent node before pushAdd.
2020-07-23 23:47:03 +08:00
Yanzhen Yu
6cf3253d94 close #254 hotfix: only use virtual parent in sync mode 2020-07-19 08:54:18 +08:00
Yanzhen Yu
ad32b8be8d close #240 fix block class to handle text node changes 2020-07-18 17:36:33 +08:00
Yanzhen Yu
6f08ec3ca7 add the patch function to utils 2020-07-18 16:56:22 +08:00
Yanzhen Yu
c5580f97f4 close #228 safe access content document, which may be destroyed manually 2020-07-18 15:29:01 +08:00
Yanzhen Yu
0756319b8e close #244 pass fullsnapshot event to the callback 2020-07-18 15:16:59 +08:00
yz-yu
7de7eb5e54 mask input options and sampling options (#252)
* part of #80, support mask input options

* close #188 enhance sampling options
Use a more general sampling strategy interface to describe the
configuration of sampling events collection.

Implemented mousmove, mouse interaction, scroll and input sampling
strategy.
2020-07-18 15:05:19 +08:00
Eoghan Murray
286b520907 Restore functioning of #200 - this got broken after #242 (#246)
- What was broken was that it would just play activity from the first page view, but then would stop at the second page view (meta) as actions after that had been discarded
 - This restores the functionality given by the comment 'return the events from last meta to the end.' - we never want to discard events that are after the baseline time
 - I believe 'session' is the incorrect terminology for this function name, as a session in web analytics usually means a series of page views
2020-07-16 00:12:37 +08:00
yz-yu
e2cc42e393 fix the skip event calculation (#242) 2020-07-11 17:06:56 +08:00
Yanzhen Yu
3e2b596664 tweak the code of getting last session, without splice events array 2020-07-11 11:03:40 +08:00
Yanzhen Yu
4bf533a675 fast-forward implementation v1
related to #6

Since the currently 'play at any time offset' implementation is pretty simple,
there are many things we can do to optimize its performance.

In this patch, we do the following optimizations:
1. Ignore some of the events during fast forward.
   For example, when we are going to fast forward to 10 minutes later,
   we do not need to perform mouse movement events during this period.
2. Use a fragment element as the 'virtual parent node'.
   So newly added DOM nodes will be appended to this fragment node,
   and finally being appended into the document as a batch operation.
These changes reduce a lot of time which was spent on reflow/repaint previously.
I've seen a 10 times performance improvement within these approaches.

And there are still some things we can do better but not in this patch.
1. We can build a virtual DOM tree to store the mutations of DOM.
   This will minimize the number of DOM operations.
2. Another thing that may help UX is to make the fast forward process async and cancellable.
   This may make the drag and drop interactions in the player's UI looks smooth.
2020-07-11 11:03:40 +08:00
Eoghan Murray
ef2c991012 Only execute events since most recent pageload when playing from an offset (#200)
On recordings with many full pageloads, dom state and mutations were being applied only to be discarded when a new pageload came in, resulting in very slow time to rebuild - and inability to interactively 'scrub' through these recordings
2020-07-11 10:21:46 +08:00
Eoghan Murray
6b0a0795ce Expose startTime and endTime in the getMetaData() call. I was using player.events[0].timestamp but player.events has gone away (since a78da77 I believe) (#235) 2020-07-10 13:37:56 +08:00
Eoghan Murray
43001ccbbd Ensure api method .play() can be called by external code without worrying about the internal state of the finite state machine (#236) 2020-07-10 13:36:57 +08:00
Yanzhen Yu
a3b1ab6d9e continue #199: redesign the bundled file structure
According to @eoghanmurray's suggestion, we can support three
main scenarios:
1. record only
2. replay only
3. all in one

Since we have implemented the packer feature, which has a big
influence in bundle size, we provide another three bundles:
1. record and pack
2. replay and unpack
3. all in one with pack and unpack
2020-06-15 18:14:37 +08:00
Yanzhen Yu
4cf196718c export utils as public API 2020-06-15 17:10:18 +08:00
Yanzhen Yu
7feb2c945c update typescript to 3.9.5 2020-06-12 18:32:48 +08:00
Eoghan Murray
8766335c82 Move mutation processing into it's own class (#223)
* Move mutation processing into it's own object.

This should stand on it's own as a refactor, but is intended as a basis
for exposing the new MutationBuffer object to further outside control e.g.
to 'mute' or batch up mutation emission when the page becomes inactive
from a https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
point of view

* The `processMutations` function needed to be bound to the `mutationBuffer` object, as otherwise `this` referred to the `MutationObserver` object itself

* Neglected to add this output of `npm run typings`

* Get around the binding problem by using Arrow function expressions

* Prettier formatting
2020-06-06 18:33:58 +08:00
Yanzhen Yu
ee73bd4919 close #216 rebuild first full snapshot when init the replayer 2020-05-31 16:35:42 +08:00
yz-yu
8913bcb9d6 Live mode 2 (#226)
* refactoring play, pause, resume, load style sheet to subscribe style code

* support live mode in state machine

* 1. upgrade @xstate/fsm
2. add toggle interact methods to the player
2020-05-31 15:40:17 +08:00
Eoghan Murray
7d062830ab Keep npm run typings happy (#222) 2020-05-27 10:04:43 +08:00
Eoghan Murray
0bbe79b42b This dimension is in px but is unitless according to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe (#214) 2020-05-23 15:24:19 +08:00
Rifaudeen
45a7f89f2b added custom-event emitter to replayer (#219) 2020-05-23 15:20:41 +08:00
yz-yu
7a666293b6 mutation observer v2 (#206)
There are some long-term issues in rrweb's mutation observer.

A scenario cause problem:
A list of DOM node: n1, n2, n3, n4, n5
Steps of modifying the nodes:
  1. remove n1, n2, n3, n4 sequentially
  2. append n4, n3, n2, n1 after n5 sequentially
Then we got the added node data like this:
  (id: n4, prev: null, next: n3  )
  (id: n3, prev: n4,   next: n2  )
  (id: n2, prev: n3,   next: n1  )
  (id: n1, prev: n2,   next: null)
The problem comes when we try to replay the first add node datum.
Since its prev node is null, we rely on its next sibling n3. But
n3 was not present at this moment, and in previous code, we fallback
to append n4 to the last of its parent node.

The solution is to defer the append of elements that missing
siblings. But it is also hard to tell which node is the first one
that needs to be appended.

Take a step back and rethink the design of the mutation observer,
we've found there are two implementations make things complicated.
1. We set the id to -1 when we seeing some nodes are not serialized yet.
2. We record both previous sibling and next sibling to determine the
position of the node.
But we can do better!
First, we can put nodes with un-serialized siblings
to a queue, and try to add it again later. Then we can just record the next
sibling as 'the single truth' so we can be sure which node is the last
one of its parent.

This patch has implemented the new observer strategy. Data recorded with
the new observer should no longer have any node with id -1. But for
compatibility consideration, we still keep some replayer code that helps
solve legacy data.
2020-05-02 14:08:10 +08:00
yz-yu
7a0fbaecd5 Bundles (#199)
* provide more bundle outputs

* update commonJS and es module entry to boot file
2020-04-15 22:54:12 +08:00
yz-yu
877e2ce958 impl basic player state machine (#198) 2020-04-12 15:16:30 +08:00
Yanzhen Yu
f159d7711f upgrade TS 2020-04-12 00:06:57 +08:00
Eoghan Murray
1bb7ffd8fc Fix for certain websites which don't scroll on their document.documentElement (#193)
- document.documentElement.scrollTop may be zero, but document.body.scrollTop may have the actual scrolling amount
 - main fallback idea taken from https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX
 - modified as `(document.documentElement || document.body).scrollTop` will incorrectly report zero.
 - version here supported by https://github.com/mochi/mochikit/blob/master/MochiKit/Position.js#L23
2020-04-11 23:15:10 +08:00
yz-yu
4f36d0e57d Packer (#172)
* introduce pako and add general packer interface

* add tests for packer

* use function API instead of class API for better tree shaking support

* refcatoring the rollup bundle config
2020-04-07 18:03:47 +08:00
Eoghan Murray
e12d240064 Keep track of pause/play state so that player doesn't accidentally 'unpause' a user pause action (#189) 2020-03-31 22:15:17 +08:00
Yanzhen Yu
3f30c47cfb remove useless console.log 2020-03-29 22:05:07 +08:00
Yanzhen Yu
380819ca70 tolerate insertRule error since browser may throw Error on wrong prefix 2020-03-29 21:18:39 +08:00
Yanzhen Yu
6e38ae4735 avoid style sheet rules index overflow 2020-03-29 21:14:45 +08:00
Yanzhen Yu
197526fbe8 ignore style sheet changes before the target DOM was serialized
The serialized DOM will contains all the styles, so this looks safe.
2020-03-22 00:36:20 +08:00
Filip Slatinac
fc6c6c43d5 Checking node existence (#174)
* added our package

* reverted back to old rrweb snapshot

* Array.from does not capture all elements added in the set, we have to manually iterate through the iterator

* package lock

* checking if nodes are in the parent before we try inserting them
2020-03-01 15:02:32 +08:00
Eoghan Murray
444570b7fc Was experiencing case when a TouchEnd event occurred on a text element i.e. (nodeType: 3 / nodeName: #text) (#180)
This was a recording taken with rrweb 0.7.27 (3afff63970) and rrweb-snapshot 0.7.21 (a0dc9481b2) so issue may have been fixed in the intervening commits
2020-03-01 15:00:32 +08:00
Yanzhen Yu
29296b6877 add trigger focus option to replayer, which may helps integration into other apps 2020-02-28 19:48:36 +08:00
David Cramer
9b7f8d6027 Add support for replaying StyleSheetRule events (#178) 2020-02-25 21:07:17 +08:00
David Cramer
046936b3e8 Add observers for stylesheet mutations (#177)
* hack together stylesheet observer

* Add test coverage for insertRule/deleteRule on stylesheets

* Add new observers

* update patch based on changes to master

* Functioning event recording

* Remove print statements

* Fix ID usage and mark add vs remove

* Correct type

Co-authored-by: Jon Perl <perl.jonathan@gmail.com>
2020-02-22 12:59:55 +08:00