diff --git a/frontend/app/components/Session_/Subheader.js b/frontend/app/components/Session_/Subheader.js
index 160131728..2f6a84cf8 100644
--- a/frontend/app/components/Session_/Subheader.js
+++ b/frontend/app/components/Session_/Subheader.js
@@ -30,6 +30,8 @@ function SubHeader(props) {
exceptionsList,
eventList: eventsList,
endTime,
+ currentTab,
+ tabs,
} = store.get();
const enabledIntegration = useMemo(() => {
@@ -114,6 +116,11 @@ function SubHeader(props) {
>
)}
+ {tabs.map((tab, i) => (
+
+ Tab {i+1}
+
+ ))}
{
/**
* @returns last message with the time <= t.
* Assumed that the current message is already handled so
- * if pointer doesn't cahnge
is returned.
+ * if pointer doesn't change is returned.
*/
moveGetLast(t: number, index?: number): T | null {
let key: string = "time"; //TODO
@@ -130,6 +130,30 @@ export default class ListWalker {
return changed ? this.list[ this.p - 1 ] : null;
}
+ moveGetLastDebug(t: number, index?: number): T | null {
+ let key: string = "time"; //TODO
+ let val = t;
+ if (index) {
+ key = "_index";
+ val = index;
+ }
+
+ let changed = false;
+ while (this.p < this.length && this.list[this.p][key] <= val) {
+ this.moveNext()
+ changed = true;
+ }
+ while (this.p > 0 && this.list[ this.p - 1 ][key] > val) {
+ this.movePrev()
+ changed = true;
+ }
+
+ console.log(this.list[this.p - 1])
+ return changed ? this.list[ this.p - 1 ] : null;
+ }
+
+
+
/**
* Moves over the messages starting from the current+1 to the last one with the time <= t
* applying callback on each of them
diff --git a/frontend/app/player/web/MessageLoader.ts b/frontend/app/player/web/MessageLoader.ts
index 4e35aff91..9c30d1890 100644
--- a/frontend/app/player/web/MessageLoader.ts
+++ b/frontend/app/player/web/MessageLoader.ts
@@ -29,6 +29,7 @@ export default class MessageLoader {
private store: Store,
private messageManager: MessageManager,
private isClickmap: boolean,
+ private uiErrorHandler?: { error: (msg: string) => void }
) {}
createNewParser(shouldDecrypt = true, file?: string, toggleStatus?: (isLoading: boolean) => void) {
@@ -57,6 +58,9 @@ export default class MessageLoader {
this.messageManager._sortMessagesHack(sorted)
toggleStatus?.(false);
this.messageManager.setMessagesLoading(false)
+ }).catch(e => {
+ console.error(e)
+ this.uiErrorHandler?.error('Error parsing file: ' + e.message)
})
}
diff --git a/frontend/app/player/web/MessageManager.ts b/frontend/app/player/web/MessageManager.ts
index 82c294875..0d73c3076 100644
--- a/frontend/app/player/web/MessageManager.ts
+++ b/frontend/app/player/web/MessageManager.ts
@@ -3,12 +3,6 @@ import { Decoder } from "syncod";
import logger from 'App/logger';
import { TYPES as EVENT_TYPES } from 'Types/session/event';
-import { Log } from 'Player';
-import {
- ResourceType,
- getResourceFromResourceTiming,
- getResourceFromNetworkRequest
-} from 'Player'
import type { Store } from 'Player';
import ListWalker from '../common/ListWalker';
@@ -21,7 +15,6 @@ import WindowNodeCounter from './managers/WindowNodeCounter';
import ActivityManager from './managers/ActivityManager';
import { MouseThrashing, MType } from "./messages";
-import { isDOMType } from './messages/filters.gen';
import type {
Message,
SetPageLocation,
@@ -41,6 +34,8 @@ import Screen, {
import type { InitialLists } from './Lists'
import type { PerformanceChartPoint } from './managers/PerformanceTrackManager';
import type { SkipInterval } from './managers/ActivityManager';
+import TabManager from "Player/web/TabManager";
+import ActiveTabManager from "Player/web/managers/ActiveTabManager";
export interface State extends ScreenState, ListsState {
performanceChartData: PerformanceChartPoint[],
@@ -62,10 +57,12 @@ export interface State extends ScreenState, ListsState {
lastMessageTime: number,
firstVisualEvent: number,
messagesProcessed: boolean,
+ currentTab: string,
+ tabs: string[],
}
-const visualChanges = [
+export const visualChanges = [
MType.MouseMove,
MType.MouseClick,
MType.CreateElementNode,
@@ -88,6 +85,8 @@ export default class MessageManager {
firstVisualEvent: 0,
messagesProcessed: false,
messagesLoading: false,
+ currentTab: '',
+ tabs: [],
}
private locationEventManager: ListWalker/**/ = new ListWalker();
@@ -114,12 +113,14 @@ export default class MessageManager {
private navigationStartOffset: number = 0;
private lastMessageTime: number = 0;
private firstVisualEventSet = false;
+ private tabs: Record = {};
+ private activeTabManager = new ActiveTabManager()
constructor(
private readonly session: any /*Session*/,
private readonly state: Store,
private readonly screen: Screen,
- initialLists?: Partial,
+ private readonly initialLists?: Partial,
private readonly uiErrorHandler?: { error: (error: string) => void, },
) {
this.pagesManager = new PagesManager(screen, this.session.isMobile, this.setCSSLoading)
@@ -234,62 +235,8 @@ export default class MessageManager {
this.activityManager = new ActivityManager(this.session.duration.milliseconds);
}
- move(t: number, index?: number): void {
- const stateToUpdate: Partial = {};
- /* == REFACTOR_ME == */
- const lastLoadedLocationMsg = this.loadedLocationManager.moveGetLast(t, index);
- if (!!lastLoadedLocationMsg) {
- // TODO: page-wise resources list // setListsStartTime(lastLoadedLocationMsg.time)
- this.navigationStartOffset = lastLoadedLocationMsg.navigationStart - this.sessionStart;
- }
- const llEvent = this.locationEventManager.moveGetLast(t, index);
- if (!!llEvent) {
- if (llEvent.domContentLoadedTime != null) {
- stateToUpdate.domContentLoadedTime = {
- time: llEvent.domContentLoadedTime + this.navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db)
- value: llEvent.domContentLoadedTime,
- }
- }
- if (llEvent.loadTime != null) {
- stateToUpdate.loadTime = {
- time: llEvent.loadTime + this.navigationStartOffset,
- value: llEvent.loadTime,
- }
- }
- if (llEvent.domBuildingTime != null) {
- stateToUpdate.domBuildingTime = llEvent.domBuildingTime;
- }
- }
- /* === */
- const lastLocationMsg = this.locationManager.moveGetLast(t, index);
- if (!!lastLocationMsg) {
- stateToUpdate.location = lastLocationMsg.url;
- }
- const lastConnectionInfoMsg = this.connectionInfoManger.moveGetLast(t, index);
- if (!!lastConnectionInfoMsg) {
- stateToUpdate.connType = lastConnectionInfoMsg.type;
- stateToUpdate.connBandwidth = lastConnectionInfoMsg.downlink;
- }
- const lastPerformanceTrackMessage = this.performanceTrackManager.moveGetLast(t, index);
- if (!!lastPerformanceTrackMessage) {
- stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time;
- }
-
- Object.assign(stateToUpdate, this.lists.moveGetState(t))
- Object.keys(stateToUpdate).length > 0 && this.state.update(stateToUpdate);
-
- /* Sequence of the managers is important here */
- // Preparing the size of "screen"
- const lastResize = this.resizeManager.moveGetLast(t, index);
- if (!!lastResize) {
- this.setSize(lastResize)
- }
- this.pagesManager.moveReady(t).then(() => {
-
- const lastScroll = this.scrollManager.moveGetLast(t, index);
- if (!!lastScroll && this.screen.window) {
- this.screen.window.scrollTo(lastScroll.x, lastScroll.y);
- }
+ move(t: number): any {
+ this.activeTabManager.moveReady(t).then(tabId => {
// Moving mouse and setting :hover classes on ready view
this.mouseMoveManager.move(t);
const lastClick = this.clickManager.moveGetLast(t);
@@ -300,6 +247,11 @@ export default class MessageManager {
if (!!lastThrashing && t - lastThrashing.time < 300) {
this.screen.cursor.shake();
}
+
+ if (tabId && this.state.get().currentTab !== tabId) {
+ this.state.update({ currentTab: tabId })
+ }
+ this.tabs[this.state.get().currentTab].move(t)
})
if (this.waitingForFiles && this.lastMessageTime <= t && t !== this.session.duration.milliseconds) {
@@ -308,7 +260,21 @@ export default class MessageManager {
}
- distributeMessage = (msg: Message): void => {
+ distributeMessage = (msg: Message & { tabId: string }): void => {
+ if (!this.tabs[msg.tabId]) {
+ console.log(msg.tabId)
+ this.tabs[msg.tabId] = new TabManager(
+ this.session,
+ this.state,
+ this.screen,
+ msg.tabId,
+ this.setSize,
+ this.initialLists,
+ )
+ }
+
+ // return this.tabs[msg.tabId].distributeMessage(msg)
+
const lastMessageTime = Math.max(msg.time, this.lastMessageTime)
this.lastMessageTime = lastMessageTime
this.state.update({ lastMessageTime })
@@ -316,14 +282,9 @@ export default class MessageManager {
this.activityManager?.updateAcctivity(msg.time);
}
switch (msg.tp) {
- case MType.SetPageLocation:
- this.locationManager.append(msg);
- if (msg.navigationStart > 0) {
- this.loadedLocationManager.append(msg);
- }
- break;
- case MType.SetViewportSize:
- this.resizeManager.append(msg);
+ case MType.TabChange:
+ this.state.update({ tabs: this.state.get().tabs.concat(msg.tabId) })
+ this.activeTabManager.append(msg)
break;
case MType.MouseThrashing:
this.mouseThrashingManager.append(msg);
@@ -334,89 +295,73 @@ export default class MessageManager {
case MType.MouseClick:
this.clickManager.append(msg);
break;
- case MType.SetViewportScroll:
- this.scrollManager.append(msg);
- break;
- case MType.PerformanceTrack:
- this.performanceTrackManager.append(msg);
- break;
- case MType.SetPageVisibility:
- this.performanceTrackManager.handleVisibility(msg)
- break;
- case MType.ConnectionInformation:
- this.connectionInfoManger.append(msg);
- break;
- case MType.OTable:
- this.decoder.set(msg.key, msg.value);
- break;
- /* Lists: */
- case MType.ConsoleLog:
- if (msg.level === 'debug') break;
- this.lists.lists.log.append(
- // @ts-ignore : TODO: enums in the message schema
- Log(msg)
- )
- break;
- case MType.ResourceTimingDeprecated:
- case MType.ResourceTiming:
- // TODO: merge `resource` and `fetch` lists into one here instead of UI
- if (msg.initiator !== ResourceType.FETCH && msg.initiator !== ResourceType.XHR) {
- // @ts-ignore TODO: typing for lists
- this.lists.lists.resource.insert(getResourceFromResourceTiming(msg, this.sessionStart))
- }
- break;
- case MType.Fetch:
- case MType.NetworkRequest:
- this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart))
- break;
- case MType.Redux:
- this.lists.lists.redux.append(msg);
- break;
- case MType.NgRx:
- this.lists.lists.ngrx.append(msg);
- break;
- case MType.Vuex:
- this.lists.lists.vuex.append(msg);
- break;
- case MType.Zustand:
- this.lists.lists.zustand.append(msg)
- break
- case MType.MobX:
- this.lists.lists.mobx.append(msg);
- break;
- case MType.GraphQl:
- this.lists.lists.graphql.append(msg);
- break;
- case MType.Profiler:
- this.lists.lists.profiles.append(msg);
- break;
+ // /* Lists: */
+ // case MType.ConsoleLog:
+ // if (msg.level === 'debug') break;
+ // this.lists.lists.log.append(
+ // // @ts-ignore : TODO: enums in the message schema
+ // Log(msg)
+ // )
+ // break;
+ // case MType.ResourceTimingDeprecated:
+ // case MType.ResourceTiming:
+ // // TODO: merge `resource` and `fetch` lists into one here instead of UI
+ // if (msg.initiator !== ResourceType.FETCH && msg.initiator !== ResourceType.XHR) {
+ // // @ts-ignore TODO: typing for lists
+ // this.lists.lists.resource.insert(getResourceFromResourceTiming(msg, this.sessionStart))
+ // }
+ // break;
+ // case MType.Fetch:
+ // case MType.NetworkRequest:
+ // this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart))
+ // break;
+ // case MType.Redux:
+ // this.lists.lists.redux.append(msg);
+ // break;
+ // case MType.NgRx:
+ // this.lists.lists.ngrx.append(msg);
+ // break;
+ // case MType.Vuex:
+ // this.lists.lists.vuex.append(msg);
+ // break;
+ // case MType.Zustand:
+ // this.lists.lists.zustand.append(msg)
+ // break
+ // case MType.MobX:
+ // this.lists.lists.mobx.append(msg);
+ // break;
+ // case MType.GraphQl:
+ // this.lists.lists.graphql.append(msg);
+ // break;
+ // case MType.Profiler:
+ // this.lists.lists.profiles.append(msg);
+ // break;
/* ===|=== */
default:
switch (msg.tp) {
case MType.CreateDocument:
if (!this.firstVisualEventSet) {
- this.state.update({ firstVisualEvent: msg.time });
+ this.state.update({ firstVisualEvent: msg.time, currentTab: msg.tabId, tabs: [msg.tabId] });
this.firstVisualEventSet = true;
}
- this.windowNodeCounter.reset();
- this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
- break;
- case MType.CreateTextNode:
- case MType.CreateElementNode:
- this.windowNodeCounter.addNode(msg.id, msg.parentID);
- this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
- break;
- case MType.MoveNode:
- this.windowNodeCounter.moveNode(msg.id, msg.parentID);
- this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
- break;
- case MType.RemoveNode:
- this.windowNodeCounter.removeNode(msg.id);
- this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
- break;
+ // break;
+ // case MType.CreateTextNode:
+ // case MType.CreateElementNode:
+ // this.windowNodeCounter.addNode(msg.id, msg.parentID);
+ // this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
+ // break;
+ // case MType.MoveNode:
+ // this.windowNodeCounter.moveNode(msg.id, msg.parentID);
+ // this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
+ // break;
+ // case MType.RemoveNode:
+ // this.windowNodeCounter.removeNode(msg.id);
+ // this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
+ // break;
}
- this.performanceTrackManager.addNodeCountPointIfNeed(msg.time)
- isDOMType(msg.tp) && this.pagesManager.appendMessage(msg)
+ this.tabs[msg.tabId].distributeMessage(msg)
+ // this.performanceTrackManager.addNodeCountPointIfNeed(msg.time)
+ // isDOMType(msg.tp) && this.pagesManager.appendMessage(msg)
break;
}
}
diff --git a/frontend/app/player/web/TabManager.ts b/frontend/app/player/web/TabManager.ts
index f9b5ca59b..2890d67a6 100644
--- a/frontend/app/player/web/TabManager.ts
+++ b/frontend/app/player/web/TabManager.ts
@@ -18,6 +18,7 @@ import MouseMoveManager from "Player/web/managers/MouseMoveManager";
import ActivityManager from "Player/web/managers/ActivityManager";
import { getResourceFromNetworkRequest, getResourceFromResourceTiming, Log, ResourceType } from "Player";
import { isDOMType } from "Player/web/messages/filters.gen";
+import { State, visualChanges } from './MessageManager'
export default class TabManager {
private locationEventManager: ListWalker/**/ = new ListWalker();
@@ -39,7 +40,9 @@ export default class TabManager {
private readonly session: any,
private readonly state: Store<{}>,
private readonly screen: Screen,
- initialLists?: Partial
+ private readonly id: string,
+ private readonly setSize: ({ height, width }: { height: number, width: number }) => void,
+ initialLists?: Partial,
) {
this.pagesManager = new PagesManager(screen, this.session.isMobile, this.setCSSLoading)
this.lists = new Lists(initialLists)
@@ -84,15 +87,6 @@ export default class TabManager {
distributeMessage(msg: Message): void {
- const lastMessageTime = Math.max(msg.time, this.lastMessageTime)
- this.lastMessageTime = lastMessageTime
- this.state.update({ lastMessageTime })
- if (visualChanges.includes(msg.tp)) {
- this.activityManager?.updateAcctivity(msg.time);
- }
- let decoded;
- const time = msg.time;
- // console.log(msg)
switch (msg.tp) {
case MType.SetPageLocation:
this.locationManager.append(msg);
@@ -103,15 +97,6 @@ export default class TabManager {
case MType.SetViewportSize:
this.resizeManager.append(msg);
break;
- case MType.MouseThrashing:
- this.mouseThrashingManager.append(msg);
- break;
- case MType.MouseMove:
- this.mouseMoveManager.append(msg);
- break;
- case MType.MouseClick:
- this.clickManager.append(msg);
- break;
case MType.SetViewportScroll:
this.scrollManager.append(msg);
break;
@@ -171,10 +156,6 @@ export default class TabManager {
default:
switch (msg.tp) {
case MType.CreateDocument:
- if (!this.firstVisualEventSet) {
- this.state.update({ firstVisualEvent: msg.time });
- this.firstVisualEventSet = true;
- }
this.windowNodeCounter.reset();
this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count);
break;
@@ -197,4 +178,67 @@ export default class TabManager {
break;
}
}
+
+ move(t: number, index?: number): void {
+
+ const stateToUpdate: Partial = {};
+ /* == REFACTOR_ME == */
+ const lastLoadedLocationMsg = this.loadedLocationManager.moveGetLast(t, index);
+ if (!!lastLoadedLocationMsg) {
+ // TODO: page-wise resources list // setListsStartTime(lastLoadedLocationMsg.time)
+ this.navigationStartOffset = lastLoadedLocationMsg.navigationStart - this.sessionStart;
+ }
+ const llEvent = this.locationEventManager.moveGetLast(t, index);
+ if (!!llEvent) {
+ if (llEvent.domContentLoadedTime != null) {
+ stateToUpdate.domContentLoadedTime = {
+ time: llEvent.domContentLoadedTime + this.navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db)
+ value: llEvent.domContentLoadedTime,
+ }
+ }
+ if (llEvent.loadTime != null) {
+ stateToUpdate.loadTime = {
+ time: llEvent.loadTime + this.navigationStartOffset,
+ value: llEvent.loadTime,
+ }
+ }
+ if (llEvent.domBuildingTime != null) {
+ stateToUpdate.domBuildingTime = llEvent.domBuildingTime;
+ }
+ }
+ /* === */
+ const lastLocationMsg = this.locationManager.moveGetLast(t, index);
+ if (!!lastLocationMsg) {
+ stateToUpdate.location = lastLocationMsg.url;
+ }
+ const lastConnectionInfoMsg = this.connectionInfoManger.moveGetLast(t, index);
+ if (!!lastConnectionInfoMsg) {
+ stateToUpdate.connType = lastConnectionInfoMsg.type;
+ stateToUpdate.connBandwidth = lastConnectionInfoMsg.downlink;
+ }
+ const lastPerformanceTrackMessage = this.performanceTrackManager.moveGetLast(t, index);
+ if (!!lastPerformanceTrackMessage) {
+ stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time;
+ }
+
+ Object.assign(stateToUpdate, this.lists.moveGetState(t))
+ Object.keys(stateToUpdate).length > 0 && this.state.update(stateToUpdate);
+
+ /* Sequence of the managers is important here */
+ // Preparing the size of "screen"
+ const lastResize = this.resizeManager.moveGetLast(t, index);
+ if (!!lastResize) {
+ this.setSize(lastResize)
+ }
+ this.pagesManager.moveReady(t).then(() => {
+ const lastScroll = this.scrollManager.moveGetLast(t, index);
+ if (!!lastScroll && this.screen.window) {
+ this.screen.window.scrollTo(lastScroll.x, lastScroll.y);
+ }
+ })
+
+ // if (this.waitingForFiles && this.lastMessageTime <= t && t !== this.session.duration.milliseconds) {
+ // this.setMessagesLoading(true)
+ // }
+ }
}
\ No newline at end of file
diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts
index 407a69068..4e41e3ec4 100644
--- a/frontend/app/player/web/WebPlayer.ts
+++ b/frontend/app/player/web/WebPlayer.ts
@@ -53,7 +53,8 @@ export default class WebPlayer extends Player {
session,
wpState,
messageManager,
- isClickMap
+ isClickMap,
+ uiErrorHandler
)
super(wpState, messageManager)
this.screen = screen
diff --git a/frontend/app/player/web/managers/ActiveTabManager.ts b/frontend/app/player/web/managers/ActiveTabManager.ts
new file mode 100644
index 000000000..c9024571f
--- /dev/null
+++ b/frontend/app/player/web/managers/ActiveTabManager.ts
@@ -0,0 +1,18 @@
+import ListWalker from '../../common/ListWalker';
+import type { TabChange } from '../messages';
+
+export default class ActiveTabManager extends ListWalker {
+ currentTime = 0;
+
+ moveReady(t: number): Promise {
+
+ if (t < this.currentTime) {
+ this.reset()
+ }
+ this.currentTime = t
+ const msg = this.moveGetLastDebug(t)
+ console.log('move', t, msg, this.list)
+
+ return Promise.resolve(msg?.tabId || null)
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/player/web/messages/MFileReader.ts b/frontend/app/player/web/messages/MFileReader.ts
index 732ca74be..929fb972d 100644
--- a/frontend/app/player/web/messages/MFileReader.ts
+++ b/frontend/app/player/web/messages/MFileReader.ts
@@ -63,6 +63,7 @@ export default class MFileReader extends RawMessageReader {
}
}
+ currentTab = ''
readNext(): Message & { _index?: number } | null {
if (this.error || !this.hasNextByte()) {
return null
@@ -82,6 +83,10 @@ export default class MFileReader extends RawMessageReader {
return null
}
+ if (rMsg.tp === MType.TabData) {
+ this.currentTab = rMsg.tabId
+ return this.readNext()
+ }
if (rMsg.tp === MType.Timestamp) {
if (!this.startTime) {
this.startTime = rMsg.timestamp
@@ -93,6 +98,7 @@ export default class MFileReader extends RawMessageReader {
const index = this.noIndexes ? 0 : this.getLastMessageID()
const msg = Object.assign(rewriteMessage(rMsg), {
time: this.currentTime,
+ tabId: this.currentTab,
}, !this.noIndexes ? { _index: index } : {})
return msg
diff --git a/frontend/app/player/web/messages/RawMessageReader.gen.ts b/frontend/app/player/web/messages/RawMessageReader.gen.ts
index 3a18d019b..0fccc5564 100644
--- a/frontend/app/player/web/messages/RawMessageReader.gen.ts
+++ b/frontend/app/player/web/messages/RawMessageReader.gen.ts
@@ -674,7 +674,6 @@ export default class RawMessageReader extends PrimitiveReader {
}
case 118: {
- console.log('TabChange')
const tabId = this.readString(); if (tabId === null) { return resetPointer() }
return {
tp: MType.TabChange,
@@ -683,7 +682,6 @@ export default class RawMessageReader extends PrimitiveReader {
}
case 119: {
- console.log('TabData')
const tabId = this.readString(); if (tabId === null) { return resetPointer() }
return {
tp: MType.TabData,