Skip to content

Commit c3391fb

Browse files
victorvrvkiacolbert
authored andcommitted
Connected chrome dev tool to front end (#26)
* commit to handle merges * Delete devtools_bundle.js * Delete devtools_bundle.js.map * added doubly linked list data structure * Added buttons back and forth time travel functionality * Merge last PR with the current chrome dev tools version. * removed console.log from extension * deleted console.log * Minor change to background, extension and inject_script. Commiting before pulling parser functionality * Connecting parser and chrome dev tools. Code is full of console.logs. * Connected dev tools with page. Injected script working, parsing working, time travel working * fixed wrong type for expected Number on TimeSlider
1 parent 35ae10c commit c3391fb

File tree

10 files changed

+2734
-121
lines changed

10 files changed

+2734
-121
lines changed

package-lock.json

Lines changed: 2598 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "index.js",
66
"scripts": {
77
"lint": "eslint \"*/**/*.{js,jsx}\"",
8-
"test": "echo \"Error: no test specified\" && exit 1"
8+
"test": "echo \"Error: no test specified\" && exit 1",
9+
"bundle_bg": "browserify src/browser/chrome/background.js -o src/browser/chrome/devtools_bundle/bg_bundle.js"
910
},
1011
"repository": {
1112
"type": "git",

src/app/components/App.jsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ class App extends Component {
7575

7676
// functionality to change 'play' button to 'stop'
7777
setIsPlaying() {
78-
if (this.state.isPlayingIndex === this.state.data.length - 1) {
79-
this.state.isPlayingIndex = 0;
78+
if (this.state.isPlayingIndex > this.state.data.length - 1) {
79+
this.setState({ isPlayingIndex: 0 });
8080
}
8181

82-
console.log('isplaying')
82+
console.log('isplaying');
8383
let { isPlaying } = this.state;
8484
isPlaying = !isPlaying;
8585
this.setState({ isPlaying });
@@ -98,14 +98,17 @@ class App extends Component {
9898
}
9999

100100
actionInPlay() {
101-
this.isPlayingIndex++;
101+
let { isPlayingIndex } = this.state;
102+
if (isPlayingIndex >= this.state.data.length - 1) isPlayingIndex = 0;
103+
104+
this.setState({ isPlayingIndex: isPlayingIndex + 1 });
105+
const { id, action, state } = this.state.data[isPlayingIndex + 1];
102106

103-
const { id, action, state } = this.state.data[this.isPlayingIndex];
104107
setTimeout(() => {
105108
this.setState((prev, props) => {
106-
return { ...prev, id, action, state }
109+
return { ...prev, id, action, state };
107110
});
108-
if (this.state.isPlaying && this.isPlayingIndex < this.state.data.length - 1) {
111+
if (this.state.isPlaying && isPlayingIndex + 1 < this.state.data.length - 1) {
109112
this.actionInPlay();
110113
} else {
111114
this.setState({ isPlaying: false });
@@ -142,7 +145,7 @@ class App extends Component {
142145
id,
143146
action,
144147
state,
145-
isPlayingIndex: e.target.value,
148+
isPlayingIndex: parseInt(e.target.value),
146149
});
147150
}
148151

src/app/container/TimeSlider.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ const TimeSlider = (props) => {
2222
<>
2323
<SliderWrapper>
2424
<Button onClick={setIsRecording}>{isRecording ? 'PAUSE' : 'RECORD'}</Button>
25-
<Button onClick={setIsPlaying}>{ isPlaying ? <text>||</text> : <text>&#9658;</text> }</Button>
26-
<input type="range" min="0" max={data.length - 1} value={isPlayingIndex}
27-
onChange={handleBarChange} />
25+
<Button onClick={setIsPlaying}>{isPlaying ? '||' : '►'}</Button>
26+
<input type="range" min="0" max={data.length - 1} value={isPlayingIndex}
27+
onChange={handleBarChange} />
2828
</SliderWrapper>
2929
</>
3030
);

src/browser/chrome/background.js

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const parseAndGenerate = require('./parser');
1+
const parseAndGenerate = require('./scripts/parser');
22

33
chrome.tabs.onUpdated.addListener((id, info, tab) => {
44
if (tab.status !== 'complete' || tab.url.startsWith('chrome')) return;
@@ -10,31 +10,37 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
1010
});
1111
});
1212

13-
chrome.runtime.onMessage.addListener((msg) => {
14-
if (msg.react_check) {
15-
console.log('Background got the react_check! Resending...');
16-
17-
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
18-
chrome.tabs.sendMessage(tabs[0].id, msg);
19-
});
20-
}
21-
});
22-
13+
let reqIndex = 0;
14+
const urlCache = {};
2315
chrome.webRequest.onBeforeRequest.addListener(
2416
(request) => {
25-
if (request.type === 'script' && !request.url.startsWith('chrome')) {
26-
console.log('redirecting... ORIGINAL: ', request);
27-
fetch(request.url)
28-
.then(r => r.text())
29-
.then((codeString) => {
30-
const editedCode = parseAndGenerate(codeString);
31-
if (!editedCode) return { redirectUrl: request.url };
32-
//TODO: Define sendMessage and redirectURL
33-
// sendMessageToContent(editedCode);
34-
// return { redirectUrl: 'javascript:' };
35-
});
17+
if (request.type === 'script' && !request.url.startsWith('chrome')
18+
&& request.frameId === 0) {
19+
// TODO: adjust comment
20+
// Else we need to check wether or not this contains the react
21+
// library. If it does, we need to send the edit javascript to
22+
// out content script, so it can inject into the page. If it doesnt,
23+
// we need to send the url to our content script so that it can
24+
// add it to the page <script src=URL> AND add it to our cache, so
25+
// that when we intercept it, we dont block it.
26+
const syncRequest = new XMLHttpRequest();
27+
syncRequest.open('GET', request.url, false);
28+
syncRequest.send(null);
29+
console.log(`Status: ${syncRequest.status} - Size of response: ${syncRequest.responseText.length}`);
30+
31+
sendMessageToContent(parseAndGenerate(syncRequest.responseText));
32+
33+
return { redirectUrl: 'javascript:' };
3634
}
3735
},
3836
{ urls: ['<all_urls>'] },
3937
['blocking'],
4038
);
39+
40+
function sendMessageToContent(codeString) {
41+
const index = reqIndex++;
42+
console.log(`Sending request ${index}.`);
43+
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
44+
chrome.tabs.sendMessage(tabs[0].id, { codeString, index });
45+
});
46+
}

src/browser/chrome/extension.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const port = chrome.runtime.connect({
33
});
44

55
port.onMessage.addListener((msg) => {
6+
// This is where we get messages from the App component.
7+
// We get an object { type: 'TIMETRAVEL', direction: 'forward' }
68
window.postMessage(msg);
79
});
810

src/browser/chrome/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
"default_popup": "page_action.html"
88
},
99
"background": {
10-
"scripts": ["bundle.js"]
10+
"scripts": ["devtools_bundle/bg_bundle.js"]
1111
},
1212
"content_scripts": [
1313
{
14-
"matches": ["<all_urls>", "http://*/*", "https://*/*"],
14+
"matches": ["<all_urls>"],
1515
"js": ["scripts/inject_script_tags.js"],
1616
"run_at":"document_start"
1717
}

src/browser/chrome/scripts/inject_script_tags.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,38 @@ timeTravelScript.src = chrome.runtime.getURL('scripts/time_travel.js');
1313
linkedListScript.onload = timeTravelScript.onload = function removeScriptTag() {
1414
this.remove();
1515
};
16+
17+
const scriptsToParse = [];
18+
let lastReceivedMsgTime = null;
19+
20+
chrome.runtime.onMessage.addListener(scheduleWork);
21+
22+
function scheduleWork(work) {
23+
// We want to add the scripts to a work array. When its been 100s
24+
// without receiving any new scripts, then we sort the array and
25+
// add all scripts to the page.
26+
if (work) scriptsToParse.push(work);
27+
28+
if (Date.now() - lastReceivedMsgTime > 100 && lastReceivedMsgTime !== null) {
29+
lastReceivedMsgTime = null;
30+
addScriptToPage();
31+
} else {
32+
if (work) lastReceivedMsgTime = Date.now();
33+
setTimeout(scheduleWork, 50);
34+
}
35+
}
36+
37+
function addScriptToPage() {
38+
// First we sort the array by index, then we add everything to page
39+
scriptsToParse.sort((a, b) => a.index - b.index);
40+
while (scriptsToParse.length > 0) {
41+
const { codeString } = scriptsToParse.shift();
42+
43+
const script = document.createElement('script');
44+
script.innerHTML = codeString;
45+
(document.head || document.documentElement).appendChild(script);
46+
script.onload = function removeScriptTag() {
47+
this.remove();
48+
};
49+
}
50+
}

src/browser/chrome/parser.js renamed to src/browser/chrome/scripts/parser.js

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ const escodegen = require('escodegen');
55
const _ = require('lodash');
66

77
// declare functions to insert
8-
// TODO: Un-comment timeTravelTracker
98
function useReducerReplacement() {
109
const dispatcher = resolveDispatcher();
1110
function reducerWithTracker(state, action) {
1211
const newState = reducer(state, action);
13-
// timeTravelTracker[timeTravelTracker.length - 1].actionDispatched = true;
12+
timeTravelTracker[timeTravelTracker.length - 1].actionDispatched = true;
1413
window.postMessage({
1514
type: 'DISPATCH',
1615
data: {
@@ -22,7 +21,15 @@ function useReducerReplacement() {
2221
}
2322
return dispatcher.useReducer(reducerWithTracker, initialArg, init);
2423
}
24+
2525
function commitAllHostEffectsReplacement() {
26+
if (Object.keys(funcStorage).length === 0) {
27+
funcStorage.commitDeletion = commitDeletion;
28+
funcStorage.commitPlacement = commitPlacement;
29+
funcStorage.commitWork = commitWork;
30+
funcStorage.prepareUpdate = prepareUpdate;
31+
}
32+
2633
while (nextEffect !== null) {
2734
{
2835
setCurrentFiber(nextEffect);
@@ -51,12 +58,9 @@ function commitAllHostEffectsReplacement() {
5158
case Placement:
5259
{
5360
// editbyme
54-
window.postMessage({
55-
type: 'EFFECT',
56-
data: {
61+
timeTravelTracker.push({
5762
primaryEffectTag: 'PLACEMENT',
5863
effect: _.cloneDeep(nextEffect),
59-
},
6064
});
6165

6266
commitPlacement(nextEffect);
@@ -84,13 +88,10 @@ function commitAllHostEffectsReplacement() {
8488
case Update:
8589
{
8690
// editbyme
87-
window.postMessage({
88-
type: 'EFFECT',
89-
data: {
90-
primaryEffectTag: 'UPDATE',
91-
effect: _.cloneDeep(nextEffect),
92-
current: _.cloneDeep(nextEffect.alternate),
93-
},
91+
timeTravelTracker.push({
92+
primaryEffectTag: 'UPDATE',
93+
effect: _.cloneDeep(nextEffect),
94+
current: _.cloneDeep(nextEffect.alternate),
9495
});
9596

9697
let _current2 = nextEffect.alternate;
@@ -100,12 +101,9 @@ function commitAllHostEffectsReplacement() {
100101
case Deletion:
101102
{
102103
// editbyme
103-
window.postMessage({
104-
type: 'EFFECT',
105-
data: {
106-
primaryEffectTag: 'DELETION',
107-
effect: _.cloneDeep(nextEffect),
108-
},
104+
timeTravelTracker.push({
105+
primaryEffectTag: 'DELETION',
106+
effect: _.cloneDeep(nextEffect),
109107
});
110108

111109
commitDeletion(nextEffect);
@@ -122,34 +120,37 @@ function commitAllHostEffectsReplacement() {
122120

123121
// traverse ast to find method and replace body with our node's body
124122
function traverseTree(replacementNode, functionName, ast) {
123+
console.log('traverse called');
125124
estraverse.replace(ast, {
126125
enter(node) {
127126
if (node.type === 'FunctionDeclaration') {
128127
if (node.id.name === functionName) {
129128
node.body = replacementNode.body[0].body;
129+
console.log('From parser. REPLACING!', node.id.name);
130130
}
131131
}
132132
},
133133
});
134134
}
135135

136136
const parseAndGenerate = (codeString) => {
137-
if (codeString.search('react')) {
137+
if (codeString.search('react') !== -1) {
138138
const ast = esprima.parseModule(codeString);
139+
139140
// parse react-dom code
140-
if (codeString.search('react-dom')) {
141-
const injectableCommitAllHostEffects = esprima.parseScript(commitAllHostEffectsReplacement.toString());
142-
traverseTree(injectableCommitAllHostEffects, 'commitAllHostEffects', ast);
143-
} else {
144-
// parse react code
145-
const injectableUseReducer = esprima.parseScript(useReducerReplacement.toString());
146-
traverseTree(injectableUseReducer, 'useReducer', ast);
147-
}
141+
const injectableCommitAllHostEffects = esprima.parseScript(commitAllHostEffectsReplacement.toString());
142+
traverseTree(injectableCommitAllHostEffects, 'commitAllHostEffects', ast);
143+
144+
// parse react code
145+
const injectableUseReducer = esprima.parseScript(useReducerReplacement.toString());
146+
traverseTree(injectableUseReducer, 'useReducer', ast);
147+
148148
const code = escodegen.generate(ast);
149+
console.log('returning code.');
149150
return code;
150151
}
151-
return -1;
152+
console.log('returning string.');
153+
return codeString;
152154
};
153155

154-
// }
155156
module.exports = parseAndGenerate;

0 commit comments

Comments
 (0)