@daisy/ace-axe-runner-electron 1.2.3 → 1.2.6-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/init.js CHANGED
@@ -1,8 +1,31 @@
1
1
  'use strict';
2
2
 
3
+ // TOO LATE FOR JEST RUNNER DUE TO AFTER ELECTRON APP.READY!
4
+ // (see package.json patcher script "patchElectronJestRunner3")
5
+ // // NO_HTTP_ADD
6
+ // const electron = require('electron');
7
+ // const protocol = electron.protocol;
8
+ // const ACE_ELECTRON_HTTP_PROTOCOL = "acehttps";
9
+ // // app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
10
+ // protocol.registerSchemesAsPrivileged([{
11
+ // privileges: {
12
+ // allowServiceWorkers: false,
13
+ // bypassCSP: false,
14
+ // corsEnabled: true,
15
+ // secure: true,
16
+ // standard: true,
17
+ // stream: true,
18
+ // supportFetchAPI: true,
19
+ // },
20
+ // scheme: ACE_ELECTRON_HTTP_PROTOCOL,
21
+ // }]);
22
+
23
+ const mime = require('mime-types');
24
+
25
+ const nodeStream = require('stream');
26
+
3
27
  const path = require('path');
4
28
  const fs = require('fs');
5
- const url = require('url');
6
29
 
7
30
  const electron = require('electron');
8
31
  const app = electron.app;
@@ -13,13 +36,6 @@ const BrowserWindow = electron.BrowserWindow;
13
36
 
14
37
  const fsOriginal = require('original-fs');
15
38
 
16
- const express = require('express');
17
- const portfinder = require('portfinder');
18
- // const http = require('http');
19
- const https = require('https');
20
-
21
- const generateSelfSignedData = require('./selfsigned').generateSelfSignedData;
22
-
23
39
  const isDev = process && process.env && (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true');
24
40
  const showWindow = false;
25
41
 
@@ -32,16 +48,28 @@ const SESSION_PARTITION = "persist:axe";
32
48
 
33
49
  const HTTP_QUERY_PARAM = "AXE_RUNNER";
34
50
 
35
- let expressApp;
36
- let httpServer;
37
- let port;
38
- let ip;
39
- let proto;
40
- let rootUrl;
41
-
51
+ const ACE_ELECTRON_HTTP_PROTOCOL = "acehttps";
52
+
53
+ // NO_HTTP_REMOVE
54
+ // const express = require('express');
55
+ // const portfinder = require('portfinder');
56
+ // // const http = require('http');
57
+ // const https = require('https');
58
+ // const generateSelfSignedData = require('./selfsigned').generateSelfSignedData;
59
+ // let expressApp;
60
+ // let httpServer;
61
+ // let port;
62
+ // let ip;
63
+ // let proto;
64
+ // let rootUrl;
42
65
  let httpServerStartWasRequested = false;
43
66
  let httpServerStarted = false;
44
67
 
68
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner ELECTRON MODULE INSTANCE`);
69
+
70
+ // NO_HTTP_ADD
71
+ const rootUrl = `${ACE_ELECTRON_HTTP_PROTOCOL}://0.0.0.0`;
72
+
45
73
  let browserWindows = undefined;
46
74
 
47
75
  const jsCache = {};
@@ -50,6 +78,120 @@ let _firstTimeInit = true;
50
78
 
51
79
  let iHttpReq = 0;
52
80
 
81
+ // NO_HTTP_ADD
82
+ class BufferReadableStream extends nodeStream.Readable {
83
+ constructor(buffer) {
84
+ super();
85
+ this.buffer = buffer;
86
+ this.alreadyRead = 0;
87
+ }
88
+ _read(size) {
89
+ if (this.alreadyRead >= this.buffer.length) {
90
+ this.push(null);
91
+ return;
92
+ }
93
+
94
+ let chunk = this.alreadyRead ? this.buffer.slice(this.alreadyRead) : this.buffer;
95
+
96
+ if (size) {
97
+ let l = size;
98
+ if (size > chunk.length) {
99
+ l = chunk.length;
100
+ }
101
+
102
+ chunk = chunk.slice(0, l);
103
+ }
104
+
105
+ this.alreadyRead += chunk.length;
106
+ this.push(chunk);
107
+ }
108
+ }
109
+ function bufferToStream(buffer) {
110
+ return new BufferReadableStream(buffer);
111
+ }
112
+
113
+ let _streamProtocolHandler = undefined;
114
+ const streamProtocolHandler = async (req, callback) => {
115
+
116
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} streamProtocolHandler req.url: ${req.url}`);
117
+ const u = new URL(req.url);
118
+
119
+ if (LOG_DEBUG) {
120
+ Object.keys(req.headers).forEach(header => {
121
+ const val = req.headers[header];
122
+
123
+ console.log(`${ACE_LOG_PREFIX} streamProtocolHandler req.header: ${header} => ${val}`);
124
+
125
+ // if (val) {
126
+ // headers[header] = val;
127
+ // }
128
+ });
129
+ }
130
+
131
+ let ref = u.origin;
132
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} streamProtocolHandler u.origin: ${ref}`);
133
+ if (req.referrer && req.referrer.trim()) {
134
+ ref = req.referrer;
135
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} streamProtocolHandler req.referrer: ${ref}`);
136
+ }
137
+
138
+ const headers = {};
139
+
140
+ if (ref && ref !== "null" && !/^https?:\/\/localhost.+/.test(ref) && !/^https?:\/\/127\.0\.0\.1.+/.test(ref)) {
141
+ headers.referer = ref;
142
+ } else {
143
+ headers.referer = `${ACE_ELECTRON_HTTP_PROTOCOL}://0.0.0.0/`;
144
+ }
145
+
146
+ // CORS everything!
147
+ headers["Access-Control-Allow-Origin"] = "*";
148
+ headers["Access-Control-Allow-Methods"] = "GET, HEAD, OPTIONS"; // POST, DELETE, PUT, PATCH
149
+ // tslint:disable-next-line:max-line-length
150
+ headers["Access-Control-Allow-Headers"] = "Content-Type, Content-Length, Accept-Ranges, Content-Range, Range, Link, Transfer-Encoding, X-Requested-With, Authorization, Accept, Origin, User-Agent, DNT, Cache-Control, Keep-Alive, If-Modified-Since";
151
+ // tslint:disable-next-line:max-line-length
152
+ headers["Access-Control-Expose-Headers"] = "Content-Type, Content-Length, Accept-Ranges, Content-Range, Range, Link, Transfer-Encoding, X-Requested-With, Authorization, Accept, Origin, User-Agent, DNT, Cache-Control, Keep-Alive, If-Modified-Since";
153
+
154
+ if (!_streamProtocolHandler) {
155
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} !? _streamProtocolHandler`);
156
+
157
+ const buff = Buffer.from("<html><body><p>Internal Server Error</p><p>!_streamProtocolHandler</p></body></html>");
158
+ headers["Content-Length"] = buff.length.toString();
159
+ callback({
160
+ data: bufferToStream(buff),
161
+ headers,
162
+ statusCode: 500
163
+ });
164
+ return;
165
+ }
166
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} BEFORE _streamProtocolHandler ${req.url}`);
167
+ await _streamProtocolHandler(req, callback, headers);
168
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} AFTER _streamProtocolHandler ${req.url}`);
169
+ };
170
+ app.whenReady().then(async () => {
171
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} Electron app ready`);
172
+
173
+ // try {
174
+ // await clearSessions();
175
+ // } catch (err) {
176
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} clearSessions fail?`);
177
+ // }
178
+
179
+ // if (session.defaultSession) {
180
+ // session.defaultSession.protocol.registerStreamProtocol(
181
+ // ACE_ELECTRON_HTTP_PROTOCOL,
182
+ // streamProtocolHandler);
183
+ // }
184
+ const sess = session.fromPartition(SESSION_PARTITION, { cache: true });
185
+ if (sess) {
186
+ sess.protocol.registerStreamProtocol(ACE_ELECTRON_HTTP_PROTOCOL, streamProtocolHandler);
187
+
188
+ sess.setPermissionRequestHandler((wc, permission, callback) => {
189
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} setPermissionRequestHandler ${wc.getURL()} => ${permission}`);
190
+ callback(true);
191
+ });
192
+ }
193
+ });
194
+
53
195
  function loadUrl(browserWindow) {
54
196
  browserWindow.ace__loadUrlPending = undefined;
55
197
 
@@ -151,6 +293,8 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
151
293
  }
152
294
  axeRunnerInit.todo = false;
153
295
 
296
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunnerInit TODO ...`);
297
+
154
298
  const firstTimeInit = _firstTimeInit;
155
299
  _firstTimeInit = false;
156
300
 
@@ -171,7 +315,8 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
171
315
  webSecurity: true,
172
316
  webviewTag: false,
173
317
  enableRemoteModule: false,
174
- partition: SESSION_PARTITION
318
+ partition: SESSION_PARTITION,
319
+ nativeWindowOpen: false // The default of nativeWindowOpen is deprecated and will be changing from false to true in Electron 15. See https://github.com/electron/electron/issues/28511
175
320
  }
176
321
  });
177
322
 
@@ -181,7 +326,13 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
181
326
  browserWindow.webContents.session.webRequest.onBeforeRequest({
182
327
  urls: []
183
328
  }, (details, callback) => {
184
- if (details.url && /^https?:\/\//.test(details.url) && (rootUrl && !details.url.startsWith(rootUrl) || !rootUrl && !/^https?:\/\/127.0.0.1/.test(details.url))) {
329
+ if (details.url && (/^file:\/\//.test(details.url) || /^https?:\/\//.test(details.url)
330
+ // && (
331
+ // (rootUrl && !details.url.startsWith(rootUrl))
332
+ // ||
333
+ // (!rootUrl && !/^https?:\/\/127.0.0.1/.test(details.url))
334
+ // )
335
+ )) {
185
336
  if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} onBeforeRequest BLOCK: ${details.url} (${rootUrl})`);
186
337
 
187
338
  // causes ERR_BLOCKED_BY_CLIENT -20 did-fail-load
@@ -223,15 +374,18 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
223
374
  return;
224
375
  }
225
376
 
226
- app.on("certificate-error", (event, webContents, u, error, certificate, callback) => {
227
- if (u.indexOf(`${rootUrl}/`) === 0) {
228
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert error OKAY ${u}`);
229
- callback(true);
230
- return;
231
- }
232
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert error FAIL ${u}`);
233
- callback(false);
234
- });
377
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunnerInit firstTimeInit ...`);
378
+
379
+ // NO_HTTP_REMOVE
380
+ // app.on("certificate-error", (event, webContents, u, error, certificate, callback) => {
381
+ // if (u.indexOf(`${rootUrl}/`) === 0) {
382
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert error OKAY ${u}`);
383
+ // callback(true);
384
+ // return;
385
+ // }
386
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert error FAIL ${u}`);
387
+ // callback(false);
388
+ // });
235
389
 
236
390
  // const filter = { urls: ["*", "*://*/*"] };
237
391
 
@@ -256,25 +410,23 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
256
410
  // }
257
411
  // };
258
412
 
259
- const setCertificateVerifyProcCB = (request, callback) => {
260
-
261
- if (request.hostname === ip) {
262
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert verify OKAY ${request.hostname}`);
263
- callback(0); // OK
264
- return;
265
- }
266
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert verify FALLBACK ${request.hostname}`);
267
- callback(-3); // Chromium
268
- // callback(-2); // Fail
269
- };
270
-
271
- const sess = session.fromPartition(SESSION_PARTITION, { cache: true }); // || session.defaultSession;
272
-
273
- if (sess) {
274
- // sess.webRequest.onHeadersReceived(filter, onHeadersReceivedCB);
275
- // sess.webRequest.onBeforeSendHeaders(filter, onBeforeSendHeadersCB);
276
- sess.setCertificateVerifyProc(setCertificateVerifyProcCB);
277
- }
413
+ // NO_HTTP_REMOVE
414
+ // const setCertificateVerifyProcCB = (request, callback) => {
415
+ // if (request.hostname === ip) {
416
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert verify OKAY ${request.hostname}`);
417
+ // callback(0); // OK
418
+ // return;
419
+ // }
420
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTPS cert verify FALLBACK ${request.hostname}`);
421
+ // callback(-3); // Chromium
422
+ // // callback(-2); // Fail
423
+ // };
424
+ // const sess = session.fromPartition(SESSION_PARTITION, { cache: true }); // || session.defaultSession;
425
+ // if (sess) {
426
+ // // sess.webRequest.onHeadersReceived(filter, onHeadersReceivedCB);
427
+ // // sess.webRequest.onBeforeSendHeaders(filter, onBeforeSendHeadersCB);
428
+ // sess.setCertificateVerifyProc(setCertificateVerifyProcCB);
429
+ // }
278
430
 
279
431
  // ipcMain
280
432
  eventEmmitter.on('AXE_RUNNER_CLOSE', (event, arg) => {
@@ -299,13 +451,17 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
299
451
  }
300
452
  browserWindows = undefined;
301
453
 
454
+ // NO_HTTP_ADD
455
+ _streamProtocolHandler = undefined;
456
+
302
457
  httpServerStarted = false;
303
458
  httpServerStartWasRequested = false;
304
459
 
305
- if (httpServer) {
306
- httpServer.close();
307
- httpServer = undefined;
308
- }
460
+ // NO_HTTP_REMOVE
461
+ // if (httpServer) {
462
+ // httpServer.close();
463
+ // httpServer = undefined;
464
+ // }
309
465
 
310
466
  let _timeOutID = setTimeout(() => {
311
467
  _timeOutID = undefined;
@@ -337,6 +493,7 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
337
493
  }
338
494
  }
339
495
 
496
+ // clearSessions()
340
497
  const sess = session.fromPartition(SESSION_PARTITION, { cache: true }); // || session.defaultSession;
341
498
  if (sess) {
342
499
  setTimeout(async () => {
@@ -381,7 +538,8 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
381
538
  console.log(uarel);
382
539
  }
383
540
  // windows! file://C:\aa\bb\chapter.xhtml
384
- const uarelObj = url.parse(uarel.replace(/\\/g, "/"));
541
+
542
+ const uarelObj = new URL(uarel.replace(/\\/g, "/"));
385
543
  const windowsDrive = uarelObj.hostname ? `${uarelObj.hostname.toUpperCase()}:` : "";
386
544
  if (LOG_DEBUG_URLS) {
387
545
  console.log("######## URL 2");
@@ -428,8 +586,6 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
428
586
  return;
429
587
  }
430
588
 
431
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner free browser window in pool: ${browserWindow.ace__poolIndex}`);
432
-
433
589
  browserWindow.ace__eventEmmitterSender = sender;
434
590
  browserWindow.ace__replySent = false;
435
591
  browserWindow.ace__timeout = undefined;
@@ -437,13 +593,15 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
437
593
  browserWindow.ace__currentUrlOriginal = uarel;
438
594
  browserWindow.ace__currentUrl = httpUrl;
439
595
 
596
+ if (LOG_DEBUG) console.log(`\n\n${ACE_LOG_PREFIX} axeRunner free browser window in pool: ${browserWindow.ace__poolIndex} ${browserWindow.ace__previousUrl} ${browserWindow.ace__currentUrlOriginal} ${browserWindow.ace__currentUrl}\n\n`);
597
+
440
598
  browserWindow.webContents.once("did-start-loading", () => {
441
599
  if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner did-start-loading ${browserWindow.ace__poolIndex} ${browserWindow.ace__currentUrlOriginal} --- ${browserWindow.ace__currentUrl}`);
442
600
  });
443
601
  // browserWindow.webContents.once("did-stop-loading", () => {
444
602
  // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner did-stop-loading ${browserWindow.ace__poolIndex} ${browserWindow.ace__currentUrlOriginal} --- ${browserWindow.ace__currentUrl}`);
445
603
  // });
446
- browserWindow.webContents.once("did-fail-load", (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
604
+ const didFailLoadHandler = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
447
605
  if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner did-fail-load ${browserWindow.ace__poolIndex} ${browserWindow.ace__currentUrlOriginal} --- ${browserWindow.ace__currentUrl}`, "\n", `${errorCode} - ${errorDescription} - ${validatedURL} - ${isMainFrame} - ${frameProcessId} - ${frameRoutingId}`);
448
606
 
449
607
  // https://cs.chromium.org/chromium/src/net/base/net_error_list.h
@@ -468,11 +626,16 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) {
468
626
  err: `did-fail-load: ${errorCode} - ${errorDescription} - ${validatedURL} - ${isMainFrame} - ${frameProcessId} - ${frameRoutingId}`,
469
627
  url: browserWindow.ace__currentUrlOriginal
470
628
  });
471
- });
629
+ };
630
+ browserWindow.webContents.once("did-fail-load", didFailLoadHandler);
472
631
  // browserWindow.webContents.once("dom-ready", () => { // occurs early
473
632
  // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner dom-ready ${browserWindow.ace__poolIndex} ${browserWindow.ace__currentUrlOriginal} --- ${browserWindow.ace__currentUrl}`);
474
633
  // });
475
634
  browserWindow.webContents.once("did-finish-load", () => {
635
+ // browserWindow.webContents.setMaxListeners(11+) ?
636
+ // (node:5505) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 did-fail-load listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
637
+ browserWindow.webContents.removeListener("did-fail-load", didFailLoadHandler);
638
+
476
639
  if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner did-finish-load ${browserWindow.ace__poolIndex} ${browserWindow.ace__currentUrlOriginal} --- ${browserWindow.ace__currentUrl}`);
477
640
 
478
641
  browserWindow.ace__TIME_executeJavaScript = process.hrtime();
@@ -561,7 +724,7 @@ new Promise((resolve, reject) => {
561
724
  } else {
562
725
  browserWindow.ace__loadUrlPending = httpUrl;
563
726
  }
564
- }
727
+ } // poolPush()
565
728
 
566
729
  if (!httpServerStartWasRequested) {
567
730
  // lazy init
@@ -571,19 +734,30 @@ new Promise((resolve, reject) => {
571
734
 
572
735
  if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner starting server ...`);
573
736
 
574
- startAxeServer(basedir, scripts, scriptContents).then(() => {
575
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner server started`);
576
- httpServerStarted = true;
577
-
578
- poolCheck();
579
- }).catch(err => {
737
+ // NO_HTTP_ADD
738
+ try {
739
+ startAxeServer(basedir, scripts, scriptContents);
740
+ } catch (err) {
580
741
  if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner server error`);
581
742
  console.log(err);
582
743
  browserWindow.ace__eventEmmitterSender.send("AXE_RUNNER_RUN_", {
583
744
  err,
584
745
  url: browserWindow.ace__currentUrlOriginal
585
746
  });
586
- });
747
+ return;
748
+ }
749
+
750
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner server started`);
751
+ httpServerStarted = true;
752
+ poolCheck();
753
+
754
+ // NO_HTTP_REMOVE
755
+ // startAxeServer(basedir, scripts, scriptContents).then(() => {
756
+ // // ...
757
+ // httpServerStarted = true;
758
+ // }).catch((err) => {
759
+ // // ...
760
+ // });
587
761
  } else {
588
762
  poolPush();
589
763
  }
@@ -591,232 +765,362 @@ new Promise((resolve, reject) => {
591
765
  }
592
766
  axeRunnerInit.todo = true;
593
767
 
594
- const filePathsExpressStaticNotExist = {};
768
+ // const filePathsExpressStaticNotExist = {};
595
769
  function startAxeServer(basedir, scripts, scriptContents) {
596
770
 
597
- return new Promise((resolve, reject) => {
771
+ // NO_HTTP_REMOVE
772
+ // return new Promise((resolve, reject) => {
598
773
 
599
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner startAxeServer...`);
774
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner startAxeServer...`);
600
775
 
601
- let scriptsMarkup = "";
602
- scriptContents.forEach(scriptCode => {
603
- scriptsMarkup += `<script data-ace="" type="text/javascript">
776
+ let scriptsMarkup = "";
777
+ scriptContents.forEach(scriptCode => {
778
+ scriptsMarkup += `<script data-ace="" type="text/javascript">
604
779
  // <![CDATA[
605
780
  ${scriptCode}
606
781
  // ]]>
607
782
  </script>`;
608
- });
609
- scripts.forEach(scriptPath => {
610
- const filename = path.basename(scriptPath);
611
- scriptsMarkup += `<script data-ace="" src="/${HTTP_QUERY_PARAM}/${filename}"> </script>`;
612
- });
783
+ });
784
+ scripts.forEach(scriptPath => {
785
+ const filename = path.basename(scriptPath);
786
+ scriptsMarkup += `<script data-ace="" src="/${HTTP_QUERY_PARAM}/${filename}"> </script>`;
787
+ });
613
788
 
614
- expressApp = express();
615
- // expressApp.enable('strict routing');
616
-
617
- // expressApp.use("/", (req, res, next) => {
618
- // if (LOG_DEBUG) console.log("HTTP: " + req.url);
619
- // next();
620
- // });
621
-
622
- expressApp.basedir = basedir;
623
- expressApp.use("/", (req, res, next) => {
624
-
625
- for (const scriptPath of scripts) {
626
- const filename = path.basename(scriptPath);
627
- if (req.url.endsWith(`${HTTP_QUERY_PARAM}/${filename}`)) {
628
- let js = jsCache[scriptPath];
629
- if (!js) {
630
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP loading ${scriptPath}`);
631
- js = fs.readFileSync(scriptPath, { encoding: "utf8" });
632
- // if (LOG_DEBUG) console.log(js);
633
- jsCache[scriptPath] = js;
634
- } else {
635
- // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP already loaded ${scriptPath}`);
636
- }
637
- res.setHeader("Content-Type", "text/javascript");
638
- res.send(js);
639
- return;
789
+ // NO_HTTP_ADD
790
+ _streamProtocolHandler = async (req, callback, headers) => {
791
+ const u = new URL(req.url);
792
+
793
+ for (const scriptPath of scripts) {
794
+ const filename = path.basename(scriptPath);
795
+ if (req.url.endsWith(`${HTTP_QUERY_PARAM}/${filename}`)) {
796
+ let js = jsCache[scriptPath];
797
+ if (!js) {
798
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP loading ${scriptPath}`);
799
+ js = fs.readFileSync(scriptPath, { encoding: "utf8" });
800
+ // if (LOG_DEBUG) console.log(js);
801
+ jsCache[scriptPath] = js;
802
+ } else {
803
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP already loaded ${scriptPath}`);
640
804
  }
805
+ const buff = Buffer.from(js);
806
+ headers["Content-Length"] = buff.length.toString();
807
+ headers["Content-Type"] = "text/javascript";
808
+ callback({
809
+ data: bufferToStream(buff),
810
+ headers,
811
+ statusCode: 200
812
+ });
813
+ return;
641
814
  }
815
+ }
642
816
 
643
- if (req.query[HTTP_QUERY_PARAM]) {
644
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP intercept ${req.url}`);
817
+ const queryParam = u.searchParams.get(HTTP_QUERY_PARAM) || undefined;
818
+ if (queryParam) {
819
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP intercept ${req.url}`);
645
820
 
646
- if (LOG_DEBUG_URLS) {
647
- console.log(">>>>>>>>>> URL 1");
648
- console.log(req.url);
649
- }
650
- const ptn = url.parse(req.url).pathname;
651
- if (LOG_DEBUG_URLS) {
652
- console.log(">>>>>>>>>> URL 2");
653
- console.log(ptn);
654
- }
655
- const pn = decodeURI(ptn);
656
- if (LOG_DEBUG_URLS) {
657
- console.log(">>>>>>>>>> URL 3");
658
- console.log(pn);
659
- }
660
- let fileSystemPath = path.join(expressApp.basedir, pn);
661
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} filepath to read: ${fileSystemPath}`);
821
+ if (LOG_DEBUG_URLS) {
822
+ console.log(">>>>>>>>>> URL 1");
823
+ console.log(req.url);
824
+ }
825
+ const ptn = u.pathname;
826
+ if (LOG_DEBUG_URLS) {
827
+ console.log(">>>>>>>>>> URL 2");
828
+ console.log(ptn);
829
+ }
830
+ const pn = decodeURI(ptn);
831
+ if (LOG_DEBUG_URLS) {
832
+ console.log(">>>>>>>>>> URL 3");
833
+ console.log(pn);
834
+ }
835
+
836
+ let fileSystemPath = path.join(basedir, pn);
837
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} filepath to read: ${fileSystemPath}`);
838
+ if (!fs.existsSync(fileSystemPath)) {
839
+ fileSystemPath = pn;
840
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} filepath to read (corrected): ${fileSystemPath}`);
662
841
  if (!fs.existsSync(fileSystemPath)) {
663
- fileSystemPath = pn;
664
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} filepath to read (corrected): ${fileSystemPath}`);
842
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} FILE DOES NOT EXIST!! ${fileSystemPath}`);
843
+
844
+ const buff = Buffer.from(`<html><body><p>Internal Server Error</p><p>404?! ${fileSystemPath}</p></body></html>`);
845
+ headers["Content-Length"] = buff.length.toString();
846
+ callback({
847
+ data: bufferToStream(buff),
848
+ headers,
849
+ statusCode: 404
850
+ });
851
+ return;
665
852
  }
853
+ }
666
854
 
667
- // let html = fs.readFileSync(fileSystemPath, { encoding: "utf8" });
668
- fs.readFile(fileSystemPath, { encoding: "utf8" }, (err, html) => {
669
- if (err) {
670
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTML file load??! ${req.url}`);
671
- res.status(404).end();
672
- return;
673
- }
855
+ // let html = fs.readFileSync(fileSystemPath, { encoding: "utf8" });
856
+ fs.readFile(fileSystemPath, { encoding: "utf8" }, (err, html) => {
857
+ if (err) {
858
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTML file load??! ${req.url}`);
859
+ callback({
860
+ data: null,
861
+ headers,
862
+ statusCode: 404
863
+ });
864
+ return;
865
+ }
674
866
 
675
- // if (LOG_DEBUG) console.log(html);
867
+ // if (LOG_DEBUG) console.log(html);
676
868
 
677
- if (html.match(/<\/head>/)) {
678
- html = html.replace(/<\/head>/, `${scriptsMarkup}</head>`);
679
- } else if (html.match(/<\/body>/)) {
680
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTML no </head>? (using </body>) ${req.url}`);
681
- html = html.replace(/<\/body>/, `${scriptsMarkup}</body>`);
682
- } else {
683
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTML neither </head> nor </body>?! ${req.url}`);
684
- }
869
+ if (html.match(/<\/head>/)) {
870
+ html = html.replace(/<\/head>/, `${scriptsMarkup}</head>`);
871
+ } else if (html.match(/<\/body>/)) {
872
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTML no </head>? (using </body>) ${req.url}`);
873
+ html = html.replace(/<\/body>/, `${scriptsMarkup}</body>`);
874
+ } else {
875
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTML neither </head> nor </body>?! ${req.url}`);
876
+ }
685
877
 
686
- res.setHeader("Content-Type", "application/xhtml+xml");
687
- res.send(html);
878
+ const buff = Buffer.from(html);
879
+ headers["Content-Length"] = buff.length.toString();
880
+ headers["Content-Type"] = "application/xhtml+xml";
881
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} CALLBACK HEADERS ${req.url} ${JSON.stringify(headers)}`);
882
+ callback({
883
+ data: bufferToStream(buff),
884
+ headers,
885
+ statusCode: 200
688
886
  });
689
- return;
690
- }
887
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} POST-CALLBACK ${req.url}`);
888
+ });
889
+ return;
890
+ }
691
891
 
692
- next();
693
- });
892
+ // equivalent to Express static:
694
893
 
695
- if (isDev) {
696
- // handle WebInspector JS maps etc.
697
- expressApp.use("/", (req, res, next) => {
698
- // const url = new URL(`https://fake.org${req.url}`);
699
- // const pathname = url.pathname;
700
- const pathname = decodeURI(url.parse(req.url).pathname);
894
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} Express static emulate: ${req.url}`);
701
895
 
702
- const filePath = path.join(basedir, pathname);
703
- if (filePathsExpressStaticNotExist[filePath]) {
704
- res.status(404).send(filePathsExpressStaticNotExist[filePath]);
705
- return;
706
- }
707
- fsOriginal.exists(filePath, exists => {
708
- if (exists) {
709
- fsOriginal.readFile(filePath, undefined, (err, data) => {
710
- if (err) {
711
- if (LOG_DEBUG) {
712
- console.log(`${ACE_LOG_PREFIX} HTTP FAIL fsOriginal.exists && ERR ${basedir} + ${req.url} => ${filePath}`, err);
713
- }
714
- filePathsExpressStaticNotExist[filePath] = err.toString();
715
- res.status(404).send(filePathsExpressStaticNotExist[filePath]);
716
- } else {
717
- // if (LOG_DEBUG) {
718
- // console.log(`${ACE_LOG_PREFIX} HTTP OK fsOriginal.exists ${basedir} + ${req.url} => ${filePath}`);
719
- // }
720
- next();
721
- // res.send(data);
722
- }
723
- });
724
- } else {
725
- fs.exists(filePath, exists => {
726
- if (exists) {
727
- fs.readFile(filePath, undefined, (err, data) => {
728
- if (err) {
729
- if (LOG_DEBUG) {
730
- console.log(`${ACE_LOG_PREFIX} HTTP FAIL !fsOriginal.exists && fs.exists && ERR ${basedir} + ${req.url} => ${filePath}`, err);
731
- }
732
- filePathsExpressStaticNotExist[filePath] = err.toString();
733
- res.status(404).send(filePathsExpressStaticNotExist[filePath]);
734
- } else {
735
- if (LOG_DEBUG) {
736
- console.log(`${ACE_LOG_PREFIX} HTTP OK !fsOriginal.exists && fs.exists ${basedir} + ${req.url} => ${filePath}`);
737
- }
738
- next();
739
- // res.send(data);
740
- }
741
- });
742
- } else {
743
- if (LOG_DEBUG) {
744
- console.log(`${ACE_LOG_PREFIX} HTTP FAIL !fsOriginal.exists && !fs.exists ${basedir} + ${req.url} => ${filePath}`);
745
- }
746
- res.status(404).end();
747
- }
748
- });
749
- }
896
+ if (LOG_DEBUG_URLS) {
897
+ console.log(">>>>>>>>>>- URL 1");
898
+ console.log(req.url);
899
+ }
900
+ const ptn = u.pathname;
901
+ if (LOG_DEBUG_URLS) {
902
+ console.log(">>>>>>>>>>- URL 2");
903
+ console.log(ptn);
904
+ }
905
+ const pn = decodeURI(ptn);
906
+ if (LOG_DEBUG_URLS) {
907
+ console.log(">>>>>>>>>>- URL 3");
908
+ console.log(pn);
909
+ }
910
+ let fileSystemPath = path.join(basedir, pn);
911
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} --filepath to read: ${fileSystemPath}`);
912
+ if (!fs.existsSync(fileSystemPath)) {
913
+ fileSystemPath = pn;
914
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} --filepath to read (corrected): ${fileSystemPath}`);
915
+ if (!fs.existsSync(fileSystemPath)) {
916
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} --FILE DOES NOT EXIST!! ${fileSystemPath}`);
917
+
918
+ const buff = Buffer.from(`<html><body><p>Internal Server Error</p><p>404?! ${fileSystemPath}</p></body></html>`);
919
+ headers["Content-Length"] = buff.length.toString();
920
+ callback({
921
+ data: bufferToStream(buff),
922
+ headers,
923
+ statusCode: 404
750
924
  });
925
+ return;
926
+ }
927
+ }
928
+ try {
929
+ let mediaType = mime.lookup(fileSystemPath) || "stream/octet";
930
+ const stats = fs.statSync(fileSystemPath);
931
+ headers["Content-Length"] = stats.size;
932
+ headers["Content-Type"] = mediaType;
933
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} --CALLBACK HEADERS ${req.url} ${JSON.stringify(headers)}`);
934
+ const steam = fs.createReadStream(fileSystemPath);
935
+ callback({
936
+ data: steam,
937
+ headers,
938
+ statusCode: 200
939
+ });
940
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} --POST-CALLBACK ${req.url}`);
941
+ } catch (fsErr) {
942
+ if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} --fsErr ${fsErr}`);
943
+
944
+ const buff = Buffer.from(`<html><body><p>Internal Server Error</p><p>fsErr: ${fsErr}</p></body></html>`);
945
+ headers["Content-Length"] = buff.length.toString();
946
+ callback({
947
+ data: bufferToStream(buff),
948
+ headers,
949
+ statusCode: 500
751
950
  });
752
951
  }
753
952
 
754
- // https://expressjs.com/en/4x/api.html#express.static
755
- const staticOptions = {
756
- dotfiles: "ignore",
757
- etag: true,
758
- // fallthrough: false,
759
- immutable: true,
760
- // index: "index.html",
761
- maxAge: "1d",
762
- redirect: false
763
- // extensions: ["css", "otf"],
764
- // setHeaders: (res, _path, _stat) => {
765
- // // res.set('x-timestamp', Date.now())
766
- // setResponseCORS(res);
767
- // },
768
- };
769
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP static path ${basedir}`);
770
- expressApp.use("/", express.static(basedir, staticOptions));
953
+ // if (isDev) { // handle WebInspector JS maps etc.
954
+
955
+ // // const url = new URL(`https://fake.org${req.url}`);
956
+ // // const pathname = url.pathname;
957
+ // const pathname = decodeURI(u.pathname);
958
+
959
+ // const filePath = path.join(basedir, pathname);
960
+ // if (filePathsExpressStaticNotExist[filePath]) {
961
+
962
+ // const buff = Buffer.from(filePathsExpressStaticNotExist[filePath]);
963
+ // headers["Content-Length"] = buff.length.toString();
964
+ // headers["Content-Type"] = "plain/text";
965
+ // callback({
966
+ // data: bufferToStream(buff),
967
+ // headers,
968
+ // statusCode: 404,
969
+ // });
970
+ // return;
971
+ // }
972
+ // fsOriginal.exists(filePath, (exists) => {
973
+ // if (exists) {
974
+ // fsOriginal.readFile(filePath, undefined, (err, data) => {
975
+ // if (err) {
976
+ // if (LOG_DEBUG) {
977
+ // console.log(`${ACE_LOG_PREFIX} HTTP FAIL fsOriginal.exists && ERR ${basedir} + ${req.url} => ${filePath}`, err);
978
+ // }
979
+ // filePathsExpressStaticNotExist[filePath] = err.toString();
980
+ // const buff = Buffer.from(filePathsExpressStaticNotExist[filePath]);
981
+ // headers["Content-Length"] = buff.length.toString();
982
+ // headers["Content-Type"] = "plain/text";
983
+ // callback({
984
+ // data: bufferToStream(buff),
985
+ // headers,
986
+ // statusCode: 404,
987
+ // });
988
+ // } else {
989
+ // // if (LOG_DEBUG) {
990
+ // // console.log(`${ACE_LOG_PREFIX} HTTP OK fsOriginal.exists ${basedir} + ${req.url} => ${filePath}`);
991
+ // // }
992
+ // callback({
993
+ // data: null,
994
+ // headers,
995
+ // statusCode: 500,
996
+ // });
997
+ // }
998
+ // });
999
+ // } else {
1000
+ // fs.exists(filePath, (exists) => {
1001
+ // if (exists) {
1002
+ // fs.readFile(filePath, undefined, (err, data) => {
1003
+ // if (err) {
1004
+ // if (LOG_DEBUG) {
1005
+ // console.log(`${ACE_LOG_PREFIX} HTTP FAIL !fsOriginal.exists && fs.exists && ERR ${basedir} + ${req.url} => ${filePath}`, err);
1006
+ // }
1007
+ // filePathsExpressStaticNotExist[filePath] = err.toString();
1008
+ // const buff = Buffer.from(filePathsExpressStaticNotExist[filePath]);
1009
+ // headers["Content-Length"] = buff.length.toString();
1010
+ // headers["Content-Type"] = "plain/text";
1011
+ // callback({
1012
+ // data: bufferToStream(buff),
1013
+ // headers,
1014
+ // statusCode: 404,
1015
+ // });
1016
+ // } else {
1017
+ // if (LOG_DEBUG) {
1018
+ // console.log(`${ACE_LOG_PREFIX} HTTP OK !fsOriginal.exists && fs.exists ${basedir} + ${req.url} => ${filePath}`);
1019
+ // }
1020
+ // callback({
1021
+ // data: null,
1022
+ // headers,
1023
+ // statusCode: 500,
1024
+ // });
1025
+ // }
1026
+ // });
1027
+ // } else {
1028
+ // if (LOG_DEBUG) {
1029
+ // console.log(`${ACE_LOG_PREFIX} HTTP FAIL !fsOriginal.exists && !fs.exists ${basedir} + ${req.url} => ${filePath}`);
1030
+ // }
1031
+ // callback({
1032
+ // data: null,
1033
+ // headers,
1034
+ // statusCode: 404,
1035
+ // });
1036
+ // }
1037
+ // });
1038
+ // }
1039
+ // });
1040
+ // }
1041
+ };
771
1042
 
772
- const startHttp = function () {
773
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner generateSelfSignedData...`);
774
- generateSelfSignedData().then(certData => {
775
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner generateSelfSignedData OK.`);
1043
+ // NO_HTTP_REMOVE
1044
+ // expressApp = express();
1045
+ // // expressApp.enable('strict routing');
1046
+ // // expressApp.use("/", (req, res, next) => {
1047
+ // // if (LOG_DEBUG) console.log("HTTP: " + req.url);
1048
+ // // next();
1049
+ // // });
1050
+ // expressApp.basedir = basedir;
1051
+ // expressApp.use("/", (req, res, next) => {
1052
+ // next();
1053
+ // });
1054
+ // if (isDev) { // handle WebInspector JS maps etc.
1055
+ // expressApp.use("/", (req, res, next) => {
1056
+ // });
1057
+ // }
1058
+
1059
+ // // https://expressjs.com/en/4x/api.html#express.static
1060
+ // const staticOptions = {
1061
+ // dotfiles: "ignore",
1062
+ // etag: true,
1063
+ // // fallthrough: false,
1064
+ // immutable: true,
1065
+ // // index: "index.html",
1066
+ // maxAge: "1d",
1067
+ // redirect: false,
1068
+ // // extensions: ["css", "otf"],
1069
+ // // setHeaders: (res, _path, _stat) => {
1070
+ // // // res.set('x-timestamp', Date.now())
1071
+ // // setResponseCORS(res);
1072
+ // // },
1073
+ // };
1074
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP static path ${basedir}`);
1075
+ // expressApp.use("/", express.static(basedir, staticOptions));
776
1076
 
777
- httpServer = https.createServer({ key: certData.private, cert: certData.cert }, expressApp).listen(port, () => {
778
- const p = httpServer.address().port;
1077
+ // const startHttp = function () {
1078
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner generateSelfSignedData...`);
1079
+ // generateSelfSignedData().then((certData) => {
1080
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner generateSelfSignedData OK.`);
779
1081
 
780
- port = p;
781
- ip = "127.0.0.1";
782
- proto = "https";
783
- rootUrl = `${proto}://${ip}:${port}`;
784
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} server URL ${rootUrl}`);
1082
+ // httpServer = https.createServer({ key: certData.private, cert: certData.cert }, expressApp).listen(port, () => {
1083
+ // const p = httpServer.address().port;
785
1084
 
786
- resolve();
787
- });
788
- }).catch(err => {
789
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} generateSelfSignedData error!`);
790
- if (LOG_DEBUG) console.log(err);
791
- httpServer = expressApp.listen(port, () => {
792
- const p = httpServer.address().port;
793
-
794
- port = p;
795
- ip = "127.0.0.1";
796
- proto = "http";
797
- rootUrl = `${proto}://${ip}:${port}`;
798
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} server URL ${rootUrl}`);
799
-
800
- resolve();
801
- });
802
- });
803
- };
1085
+ // port = p;
1086
+ // ip = "127.0.0.1";
1087
+ // proto = "https";
1088
+ // rootUrl = `${proto}://${ip}:${port}`;
1089
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} server URL ${rootUrl}`);
804
1090
 
805
- portfinder.getPortPromise().then(p => {
806
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner HTTP port ${p}`);
807
- port = p;
808
- startHttp();
809
- }).catch(err => {
810
- if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner HTTP port error!`);
811
- console.log(err);
812
- port = 3000;
813
- startHttp();
814
- });
815
- });
1091
+ // resolve();
1092
+ // });
1093
+ // }).catch((err) => {
1094
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} generateSelfSignedData error!`);
1095
+ // if (LOG_DEBUG) console.log(err);
1096
+ // httpServer = expressApp.listen(port, () => {
1097
+ // const p = httpServer.address().port;
1098
+
1099
+ // port = p;
1100
+ // ip = "127.0.0.1";
1101
+ // proto = "http";
1102
+ // rootUrl = `${proto}://${ip}:${port}`;
1103
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} server URL ${rootUrl}`);
1104
+
1105
+ // resolve();
1106
+ // });
1107
+ // });
1108
+ // }
1109
+
1110
+ // portfinder.getPortPromise().then((p) => {
1111
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner HTTP port ${p}`);
1112
+ // port = p;
1113
+ // startHttp();
1114
+ // }).catch((err) => {
1115
+ // if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner HTTP port error!`);
1116
+ // console.log(err);
1117
+ // port = 3000;
1118
+ // startHttp();
1119
+ // });
1120
+ // });
816
1121
  }
817
1122
 
818
1123
  function prepareLaunch(eventEmmitter, CONCURRENT_INSTANCES) {
819
-
820
1124
  eventEmmitter.on('AXE_RUNNER_LAUNCH', (event, arg) => {
821
1125
  // const payload = eventEmmitter.ace_notElectronIpcMainRenderer ? event : arg;
822
1126
  const sender = eventEmmitter.ace_notElectronIpcMainRenderer ? eventEmmitter : event.sender;