diff --git a/.gitignore b/.gitignore index 93a1df368..c214b9dec 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ src/extension/build.crx src/extension/build.pem bower_components sandboxes/manual-tests/NextJS/.next +.vscode \ No newline at end of file diff --git a/dev-reactime/linkFiber.js b/dev-reactime/linkFiber.js index 76dfc2d06..bf0941d4d 100644 --- a/dev-reactime/linkFiber.js +++ b/dev-reactime/linkFiber.js @@ -52,9 +52,9 @@ module.exports = (snap, mode) => { async function sendSnapshot() { // Don't send messages while jumping or while paused if (mode.jumping || mode.paused) return; - // console.log('PAYLOAD: before cleaning', snap.tree); + console.log('PAYLOAD: before cleaning', snap.tree); const payload = snap.tree.cleanTreeCopy();// snap.tree.getCopy(); - // console.log('PAYLOAD: after cleaning', payload); + console.log('PAYLOAD: after cleaning', payload); try { await window.postMessage({ action: 'recordSnap', @@ -91,7 +91,9 @@ module.exports = (snap, mode) => { // function createTree(currentFiber, tree = new Tree('root')) { // Base case: child or sibling pointed to null - if (!currentFiber) return tree; + if (!currentFiber) return null; + if (!tree) return tree; + // These have the newest state. We update state and then // called updateSnapshotTree() @@ -102,40 +104,72 @@ module.exports = (snap, mode) => { memoizedState, elementType, tag, + actualDuration, + actualStartTime, + selfBaseDuration, + treeBaseDuration, } = currentFiber; - let index; + let newState = null; + let componentData = {}; + let componentFound = false; // Check if node is a stateful component if (stateNode && stateNode.state && (tag === 0 || tag === 1)) { // Save component's state and setState() function to our record for future // time-travel state changing. Add record index to snapshot so we can retrieve. - index = componentActionsRecord.saveNew(stateNode.state, stateNode); - tree.appendChild(stateNode.state, elementType.name, index); // Add component to tree - } else { + componentData.index = componentActionsRecord.saveNew(stateNode.state, stateNode); + newState = stateNode.state; + componentFound = true; + // tree.appendToChild(stateNode.state, elementType.name, index); // Add component to tree + } else if (tag === 0 || tag === 1) { // grab stateless components here + newState = 'stateless'; + // tree.appendChild({}, elementType.name) // Add component to tree } // Check if node is a hooks function + let hooksIndex; if (memoizedState && (tag === 0 || tag === 1 || tag === 10)) { if (memoizedState.queue) { const hooksComponents = traverseHooks(memoizedState); hooksComponents.forEach(c => { - if (elementType.name) { - index = componentActionsRecord.saveNew(c.state, c.component); - tree.appendChild(c.state, elementType.name ? elementType.name : 'nameless', index); + hooksIndex = componentActionsRecord.saveNew(c.state, c.component); + if (newState.hooksState) { + newState.hooksState.push([c.state, hooksIndex]); + } else { + newState.hooksState = [[c.state, hooksIndex]]; } + componentFound = true; + // newState = { ...newState, hooksState: c.state }; + // tree.appendSibling(c.state, elementType.name ? elementType.name : 'nameless', index); }); } } - // Recurse on siblings - createTree(sibling, tree); + componentData = { + ...componentData, + actualDuration, + actualStartTime, + selfBaseDuration, + treeBaseDuration, + }; + + if (componentFound) { + tree.addChild(newState, elementType.name ? elementType.name : elementType, componentData); + } else if (newState === 'stateless') { + tree.addChild(newState, elementType.name ? elementType.name : elementType, componentData); + } + // Recurse on children - if (tree.children.length > 0) { - createTree(child, tree.children[0]); - } else { - createTree(child, tree); + if (child) { + if (tree.children.length > 0) { + createTree(child, tree.children[tree.children.length - 1]); + } else { + createTree(child, tree); + } } + // Recurse on siblings + if (sibling) createTree(sibling, tree); return tree; } @@ -179,8 +213,6 @@ module.exports = (snap, mode) => { const devTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; const reactInstance = devTools ? devTools.renderers.get(1) : null; const overrideHookState = reactInstance ? reactInstance.overrideHookState : null; - console.log('DEVTOOLS:', devTools); - console.log('roots:', reactInstance.getCurrentFiber()) if (reactInstance && reactInstance.version) { devTools.onCommitFiberRoot = (function (original) { diff --git a/dev-reactime/timeJump.js b/dev-reactime/timeJump.js index 20b3f3710..778b40880 100644 --- a/dev-reactime/timeJump.js +++ b/dev-reactime/timeJump.js @@ -20,20 +20,11 @@ module.exports = (origin, mode) => { // Carlos: target is past state we are travelling to function jump(target, originNode = origin.tree) { - console.log('origin (link to current app state) in jump():', origin); - console.log('target state is: ', target); // Set the state of the origin tree if the component is stateful if (!target) return; - const component = componentActionsRecord.getComponentByIndex(target.index); - if (component) { - console.log('time-travel component is true'); - if (component.setState) { - console.log('time-travel component setState is true'); - } - } - + if (target.state === 'stateless') target.children.forEach(child => jump(child)); + const component = componentActionsRecord.getComponentByIndex(target.componentData.index); if (component && component.setState) { - console.log('time-travel calling setState', component); component.setState(prevState => { Object.keys(prevState).forEach(key => { if (target.state[key] === undefined) { @@ -43,16 +34,24 @@ module.exports = (origin, mode) => { return target.state; // Iterate through new children after state has been set }, () => target.children.forEach(child => jump(child))); - } else if (component && component.dispatch) { - // ** not entering here - console.log('time-travel calling dispatch', component); - component.dispatch(target.state); + } + + if (target.state.hooksState) { + target.state.hooksState.forEach(hooksState => { + if (component && component.dispatch) { + const hooksComponent = componentActionsRecord.getComponentByIndex(hooksState[1]); + hooksComponent.dispatch(target.state.hooksState[0]); + } + }); target.children.forEach(child => jump(child)); - } else { - console.log('time-travel did not call setState nor dispatch', component); + } + + if ((!component || !component.state) && !target.state.hooksState) { target.children.forEach(child => jump(child)); } + + /* if (originNode.component.setState) { console.log('stateful component jump, originNode: ', originNode.component); diff --git a/dev-reactime/tree.js b/dev-reactime/tree.js index 9a00281bb..3b0ee519f 100644 --- a/dev-reactime/tree.js +++ b/dev-reactime/tree.js @@ -12,23 +12,22 @@ function scrubUnserializableMembers(tree) { // this is the current snapshot that is being sent to the snapshots array. class Tree { - constructor(state, name = 'nameless', index) { + constructor(state, name = 'nameless', componentData = {}) { this.state = state === 'root' ? 'root' : JSON.parse(JSON.stringify(state)); this.name = name; - this.index = index; + this.componentData = JSON.parse(JSON.stringify(componentData)); this.children = []; } - appendChild(state, name, index) { - const child = new Tree(state, name, index); - this.children.push(child); + addChild(state, name = this.name, componentData = this.componentData) { + this.children.push(new Tree(state, name, componentData)); } cleanTreeCopy() { - const copy = new Tree(this.state, this.name, this.index); + const copy = new Tree(this.state, this.name, this.componentData); let newChild; copy.children = this.children.map(child => { - newChild = new Tree(child.state, child.name, child.index); + newChild = new Tree(child.state, child.name, child.componentData); newChild.children = child.children; return scrubUnserializableMembers(newChild); }); @@ -42,7 +41,72 @@ class Tree { }); } return copy; - } + + // This is a regular old tree version of the above, but above version + // is better suited for D3 chart displays +// class Tree { +// constructor(state, name = 'nameless', index) { +// this.state = state === 'root' ? 'root' : JSON.parse(JSON.stringify(state)); +// this.name = name; +// this.index = index; +// this.child = null; +// this.sibling = null; +// } + +// setNode(state, name, index) { +// this.state = { ...this.child.state, ...state }; +// this.name = name; +// this.index = index; +// } + +// appendToChild(state, name = this.name, index = this.index) { +// if (this.child) { +// this.child.state = { ...this.child.state, ...state }; +// this.child.name = name; +// this.child.index = index; +// } else { +// this.child = new Tree(state, name, index); +// } +// } + +// appendToSibling(state, name = this.name, index = this.index) { +// if (this.sibling) { +// state = { ...this.sibling.state, ...state }; +// this.sibling.name = name; +// this.sibling.index = index; +// } else { +// this.sibling = new Tree(state, name, index); +// } +// } + +// cleanTreeCopy() { +// const copy = new Tree(this.state, this.name, this.index); +// if (this.sibling) { +// copy.sibling = new Tree(this.sibling.state, this.sibling.name, this.sibling.index); +// copy.sibling = scrubUnserializableMembers(copy.sibling); +// copy.sibling = this.sibling.sibling; +// copy.sibling.cleanTreeCopy(); +// } + +// if (this.child) { +// copy.child = new Tree(this.child.state, this.child.name, this.child.index); +// copy.child = scrubUnserializableMembers(copy.child); +// copy.child = this.child.child; +// copy.child.cleanTreeCopy(); +// } +// +// // unfinished: +// cleanD3Copy(children = []) { +// copy = D3Tree(this.state, this.name, this.index, this.isStateless); +// let nextSibling = this.sibling; +// while(nextSibling = this.sibling) { +// copy.cleanD3Copy(copy.) +// } + +} + +// return copy; +// } // print out the tree structure in the console // DEV: Process may be different for useState components diff --git a/src/app/components/Tree.jsx b/src/app/components/Tree.jsx index aef82eb66..83f4662ae 100644 --- a/src/app/components/Tree.jsx +++ b/src/app/components/Tree.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; const getItemString = (type, data) => { // check to make sure that we are on the tree node, not anything else if ( - Object.keys(data).length === 3 + Object.keys(data).length > 3 && typeof data.state === 'object' && typeof data.name === 'string' && Array.isArray(data.children) diff --git a/src/app/containers/ActionContainer.jsx b/src/app/containers/ActionContainer.jsx index e50745e54..80f817a43 100644 --- a/src/app/containers/ActionContainer.jsx +++ b/src/app/containers/ActionContainer.jsx @@ -38,16 +38,9 @@ function ActionContainer() { }); } } -<<<<<<< HEAD - }; - // gabi :: the hierarchy get set on the first click in the page, when page in refreshed we don't have a hierarchy so we need to check if hierarchy was inicialize involk displayArray to display the hierarchy - if (hierarchy) displayArray(hierarchy); - // console.log('this is hierarchyArr', hierarchyArr) -======= } // gabi :: the hierarchy get set on the first click in the page, when page in refreshed we don't have a hierarchy so we need to check if hierarchy was inicialize involk displayArray to display the hierarchy if (hierarchy) displayArray(hierarchy) ->>>>>>> master // Edwin: handles keyboard presses, function passes an event and index of each action-component function handleOnKeyDown(e, i) { diff --git a/src/app/reducers/mainReducer.js b/src/app/reducers/mainReducer.js index 32e97c5a8..fa9e1333a 100644 --- a/src/app/reducers/mainReducer.js +++ b/src/app/reducers/mainReducer.js @@ -10,7 +10,7 @@ export default (state, action) => produce(state, draft => { } = tabs[currentTab] || {}; - // gabi and nate :: function that find the index in the hiearachy and extract the name of the equivalent index to add to the post message + // gabi and nate :: function that find the index in the hierarchy and extract the name of the equivalent index to add to the post message const findName = (index, hierarchy) => { if(hierarchy.index == index){ return hierarchy.name; @@ -93,7 +93,7 @@ export default (state, action) => produce(state, draft => { } case types.CHANGE_SLIDER: { // gabi and nate :: finds the name by the action.payload, parsing through the hierarchy to send to background.js the current name in the jump action - const nameFromIndex = findName(action.payload, hierarchy) + const nameFromIndex = findName(action.payload, hierarchy); port.postMessage({ action: 'jumpToSnap', diff --git a/src/extension/background.js b/src/extension/background.js index 5d2f4baa3..dafd8f225 100644 --- a/src/extension/background.js +++ b/src/extension/background.js @@ -16,10 +16,10 @@ function createTabObj(title) { title, // snapshots is an array of ALL state snapshots for the reactime tab working on a specific user application snapshots: [], - // gabi :: record initial snapshot to refresh page in case empty function is called + // gabi :: record initial snapshot to refresh page in case empty function is called initialSnapshot: [], - // gabi and nate :: index here is the tab index that show total amount of state changes - index: 0, + // gabi and nate :: index here is the tab index that show total amount of state changes + index: 0, //* this is our pointer so we know what the current state the user is checking (this accounts for time travel aka when user clicks jump on the UI) currLocation: null, // gabi and nate :: point the node that will generate the next child set by newest node or jump @@ -28,7 +28,7 @@ function createTabObj(title) { currBranch: 0, //* inserting a new property to build out our hierarchy dataset for d3 hierarchy: null, - // gabi :: record initial hierarchy to refresh page in case empty function is called + // gabi :: record initial hierarchy to refresh page in case empty function is called initialHierarchy: null, mode: { persist: false, @@ -52,11 +52,6 @@ class Node { this.branch = tabObj.currBranch; this.stateSnapshot = obj; this.children = []; -<<<<<<< HEAD - //console.log('created node in background.js constructor'); - //console.log('tabsObj is: ', tabsObj); -======= ->>>>>>> master } } @@ -67,7 +62,7 @@ function sendToHierarchy(tabObj, newNode) { tabObj.currLocation = newNode; tabObj.hierarchy = newNode; } else { - console.log('currLocation exists') + console.log('currLocation exists'); tabObj.currLocation.children.push(newNode); // gabi and nate :: if the node's children's array is empty if (tabObj.currLocation.children.length > 1) { @@ -116,7 +111,6 @@ chrome.runtime.onConnect.addListener(port => { action: 'initialConnectSnapshots', payload: tabsObj, }); - } // every time devtool is closed, remove the port from portsArr @@ -149,19 +143,13 @@ chrome.runtime.onConnect.addListener(port => { case 'emptySnap': console.log('running emptySnap'); // gabi :: activate empty mode - tabsObj[tabId].mode.empty = true + tabsObj[tabId].mode.empty = true; // gabi :: record snapshot of page initial state tabsObj[tabId].initialSnapshot.push(tabsObj[tabId].snapshots[0]); // gabi :: reset snapshots to page last state recorded - tabsObj[tabId].snapshots = [tabsObj[tabId].snapshots[tabsObj[tabId].snapshots.length - 1] ]; + tabsObj[tabId].snapshots = [tabsObj[tabId].snapshots[tabsObj[tabId].snapshots.length - 1]]; // gabi :: record hierarchy of page initial state -<<<<<<< HEAD tabsObj[tabId].initialHierarchy = { ...tabsObj[tabId].hierarchy, children: [] }; -======= - // tabsObj[tabId].initialHierarchy = {...tabsObj[tabId].hierarchy}; - // tabsObj[tabId].initialHierarchy.children = []; - tabsObj[tabId].initialHierarchy = {...tabsObj[tabId].hierarchy, children: []}; ->>>>>>> master // gabi :: reset hierarchy tabsObj[tabId].hierarchy.children = []; // gabi :: reset hierarchy to page last state recorded @@ -195,16 +183,9 @@ chrome.runtime.onConnect.addListener(port => { // background.js recieves message from contentScript.js chrome.runtime.onMessage.addListener((request, sender) => { // IGNORE THE AUTOMATIC MESSAGE SENT BY CHROME WHEN CONTENT SCRIPT IS FIRST LOADED -<<<<<<< HEAD if (request.type === 'SIGN_CONNECT') { - console.log('in SIGN_CONNECT'); - return true; - }; -======= - if (request.type === 'SIGN_CONNECT'){ return true; } ->>>>>>> master const tabTitle = sender.tab.title; const tabId = sender.tab.id; const { action, index, name } = request; @@ -238,16 +219,16 @@ chrome.runtime.onMessage.addListener((request, sender) => { tabsObj[tabId].mode.paused = false; // dont remove snapshots if persisting if (!persist) { - if(empty){ - // gabi :: reset snapshots to page initial state recorded when empted + if (empty) { + // gabi :: reset snapshots to page initial state recorded when empted tabsObj[tabId].snapshots = tabsObj[tabId].initialSnapshot; - // gabi :: reset hierarchy to page initial state recorded when empted + // gabi :: reset hierarchy to page initial state recorded when empted tabsObj[tabId].hierarchy = tabsObj[tabId].initialHierarchy; } else { // gabi :: reset snapshots to page initial state tabsObj[tabId].snapshots.splice(1); // gabi :: reset hierarchy to page initial state - if(tabsObj[tabId].hierarchy){ + if (tabsObj[tabId].hierarchy) { tabsObj[tabId].hierarchy.children = []; // gabi :: reset currParent plus current state tabsObj[tabId].currParent = 1; @@ -256,13 +237,13 @@ chrome.runtime.onMessage.addListener((request, sender) => { tabsObj[tabId].currParent = 0; } } - // gabi :: reset currLocation to page initial state - console.log('running tabReload'); - tabsObj[tabId].currLocation = tabsObj[tabId].hierarchy; - // gabi :: reset index - tabsObj[tabId].index = 0; - // gabi :: reset currBranch - tabsObj[tabId].currBranch = 0; + // gabi :: reset currLocation to page initial state + console.log('running tabReload'); + tabsObj[tabId].currLocation = tabsObj[tabId].hierarchy; + // gabi :: reset index + tabsObj[tabId].index = 0; + // gabi :: reset currBranch + tabsObj[tabId].currBranch = 0; // send a message to devtools portsArr.forEach(bg => @@ -351,10 +332,6 @@ chrome.tabs.onRemoved.addListener(tabId => { // when tab is view change, put the tabid as the current tab chrome.tabs.onActivated.addListener(info => { // tell devtools which tab to be the current -<<<<<<< HEAD - console.log('this is info.tabId from chrome.tabs.onActivated.addListener', info); -======= ->>>>>>> master if (portsArr.length > 0) { portsArr.forEach(bg => bg.postMessage({