/
_server.js
200 lines (169 loc) · 4.93 KB
/
_server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
const electron = require('electron');
const express = require('express');
global.LRUCache = require('./_lruCache');
const ngrok = require('ngrok');
const path = require('path');
const url = require('url');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const cache = new LRUCache(100);
const ipcMain = electron.ipcMain;
let listening = false;
const localhost = '127.0.0.1';
const shadowPort = 4000;
let shadowRes = null;
const shadowServer = express();
const shadyPort = 3000;
let shadyRes = null;
const shadyServer = express();
let win = null;
global.directory = 'file://' + __dirname + '/';
global.pageCache = new LRUCache(100);
/**
* Creates an instance of the Electron BrowserWindow with the DevTools open
* and spins up Express server.
*/
function createWindow() {
win = new BrowserWindow({width: 800, height: 600});
win.loadURL('data:text/html;charset:utf-8,<html></html>');
win.webContents.openDevTools();
win.on('closed', () => {
win = null;
});
// Setup for Express server
win.webContents.on('dom-ready', () => {
startServer();
});
}
/**
* Returns the string containing the HTML imports.
* @param {String} key The key of the HTML imports in the cache.
* @param {Object} res The response object.
*/
function startServer() {
if (!listening) {
shadyServer.listen(shadyPort);
shadowServer.listen(shadowPort,
() => {
shadowServer.emit('listening', null);
});
ngrok.connect(shadyPort,
(err, url) => {
console.log('Shady ngrok url: ' + url);
});
ngrok.connect(shadowPort,
(err, url) => {
console.log('Shadow ngrok url: ' + url);
});
}
}
shadowServer.on('listening', () => {
listening = true;
});
/**
* Closes the server ports.
*/
function stopServer() {
shadyServer.close();
shadowServer.close();
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
stopServer();
app.quit();
}
});
app.on('activate', () => {
if (win === null) {
createWindow();
}
});
// IPC functions
ipcMain.on('receiveSerializedDOM', (_, contents, isShady, setPageCache,
originalHTML) => {
if(setPageCache) {
pageCache.set('_' + isShady + '_' + originalHTML, contents);
}
if (isShady) {
shadyRes.end(contents);
} else {
shadowRes.end(contents);
}
});
ipcMain.on('setAsyncImports', (event, key, value) => {
cache.set('_asyncImport' + key + '.html', value);
event.returnValue = true;
});
shadyServer.get(/\/index[0-9]*.html/, (req, res) => {
win.loadURL('file://' + __dirname + req.url);
shadyRes = res;
win.webContents.on('did-finish-load', () => {
// To use the hybrid version instead, use this instead:
// shadyThenShadowGetDOMInsidePage();
shadyGetDOMInsidePage();
});
});
shadowServer.get(/\/index[0-9]*shadow.html/, (req, res) => {
win.loadURL('file://' + __dirname + req.url);
shadowRes = res;
win.webContents.on('did-finish-load', () => {
shadowGetDOMInsidePage();
});
});
shadyServer.get(/_asyncImport[0-9]+/, getAsyncImport);
shadowServer.get(/_asyncImport[0-9]+/, getAsyncImport);
/**
* Returns the string containing the HTML imports.
* @param {String} req The request to the server.
* @param {Object} res The response from the server.
*/
function getAsyncImport(req, res) {
res.end('' + cache.get(req.url.substring(1)));
}
/**
* Sends the requested file as the response without any modification.
* @param {Object} req The request to the server.
* @param {Object} res The response from the server.
*/
function returnRequest(req, res) {
const parsedUrl = url.parse(req.url, true);
const filename = parsedUrl.pathname.substr(1);
const p = path.join(__dirname, filename);
res.sendFile(p);
}
shadyServer.get('/*', returnRequest);
shadowServer.get('/*', returnRequest);
/* eslint no-unused-vars:
["error", {"varsIgnorePattern": "shadyThenShadowGetDOMInsidePage"}]*/
/**
* Accesses and modifies the DOM inside the BrowserWindow and
* sends a serialized HTMLString as the response.
* This is the hybrid version which serves Shady CSS and DOM initially.
* After the HTML imports, the elements are rendered with native shadow DOM.
*/
function shadyThenShadowGetDOMInsidePage() {
win.webContents.executeJavaScript(`
require('./_server_execute_js')().hybridVersion();
`);
}
/**
* Accesses and modifies the DOM inside the BrowserWindow and
* sends a serialized HTMLString as the response.
* This implementation is for apps that use shady DOM.
*/
function shadyGetDOMInsidePage() {
win.webContents.executeJavaScript(`
require('./_server_execute_js')().shadyVersion();
`);
}
/**
* Accesses and modifies the DOM inside the BrowserWindow and
* sends a serialized HTMLString as the response.
* This implementation is for apps that use shadow DOM.
*/
function shadowGetDOMInsidePage() {
win.webContents.executeJavaScript(`
require('./_server_execute_js')().shadowVersion();
`);
}