2 app
, BrowserWindow
, Menu
, shell
, clipboard
,
3 session
, protocol
, net
} = require('electron')
6 if(!app
.requestSingleInstanceLock())
9 app
.on('ready', createWindow
);
10 app
.on('second-instance', (event
, args
, cwd
) => {
11 // 当已经有运行的实例时,我们激活窗口而不是创建新的窗口
13 if (win
.isMinimized()) {
18 cmdlineProcess(args
,cwd
,1);
25 const fs
= require('fs');
26 const readline
= require('readline');
27 const path
= require('path')
28 const process
= require('process')
39 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" +
40 process
.versions
.chrome
+" Safari/537.36";
41 app
.userAgentFallback
= defaultUA
;
42 var historyFile
= path
.join(__dirname
,'history.rec');
44 fs
.readFile(path
.join(__dirname
,'redirect.json'), 'utf8', (err
, jsonString
) => {
47 redirects
= JSON
.parse(jsonString
);
51 async
function createWindow () {
52 let json
= await fs
.promises
.readFile(path
.join(__dirname
,'uas.json'), 'utf8');
54 useragents
= JSON
.parse(json
);
59 const readInterface
= readline
.createInterface ({
60 input
: fs
.createReadStream (path
.join(__dirname
,'config'), 'utf8'),
63 for await (const line
of readInterface
) {
69 win
= new BrowserWindow(
70 {width
: 800, height
: 600,autoHideMenuBar
: true,
72 nodeIntegration
: true,
73 contextIsolation
: false,
76 win
.setMenuBarVisibility(false);
77 win
.on('closed', function () {
81 win
.loadFile('index.html');
82 fs
.readFile(path
.join(__dirname
,'gredirect.json'), 'utf8', (err
, jsonString
) => {
85 gredirects
= JSON
.parse(jsonString
);
89 fs
.readFile(path
.join(__dirname
,'proxy.json'), 'utf8', (err
, jsonString
) => {
92 proxies
= JSON
.parse(jsonString
, (key
,val
)=>{
93 if(!proxy
&& key
==="proxyRules"){
94 proxy
= {proxyRules
:val
};
101 cmdlineProcess(process
.argv
, process
.cwd(), 0);
102 //app.commandLine.appendSwitch ('trace-warnings');
104 win
.webContents
.on('page-title-updated',(event
,cmd
)=>{
108 win
.webContents
.on('console-message',cbConsoleMsg
);
109 //protocol.handle("https",cbScheme_https);
112 app
.on('window-all-closed', function () {
116 app
.on('activate', function () {
122 app
.on('will-quit', () => {
125 app
.on ('web-contents-created', (event
, contents
) => {
126 if (contents
.getType () === 'webview') {
127 contents
.setWindowOpenHandler(cbWindowOpenHandler
);
128 contents
.on('context-menu',onContextMenu
);
129 contents
.on('page-title-updated',cbTitleUpdate
);
130 //contents.on('console-message',cbConsoleMsg);
131 //contents.on('focus', ()=>{cbFocus(contents)});
132 //contents.on('blur',()=>{cbBlur()});
133 contents
.session
.webRequest
.onBeforeRequest(interceptRequest
);
134 contents
.on('did-finish-load',()=>{cbFinishLoad(contents
)});
138 function addrCommand(cmd
){
139 if(cmd
.length
<3) return;
140 let c0
= cmd
.charCodeAt(0);
143 args
= cmd
.substring(1).split(/\s+/);
147 session
.defaultSession
.setCertificateVerifyProc((request
, callback
) => {
151 session
.defaultSession
.setCertificateVerifyProc(null);
159 session
.defaultSession
.clearCache();
162 session
.defaultSession
.clearHostResolverCache();
165 session
.defaultSession
.clearStorageData();
170 session
.defaultSession
.loadExtension(args
[1]);
173 bHistory
= false; return;
175 bHistory
= true; return;
181 session
.defaultSession
.setProxy ({mode
:"direct"});
185 proxy
= proxies
[args
[1]]; //retrieve proxy
187 session
.defaultSession
.setProxy(proxy
);
191 bRedirect
= false; return;
193 bRedirect
= true; return;
196 session
.defaultSession
.setUserAgent(useragents
[args
[1]]);
198 session
.defaultSession
.setUserAgent(defaultUA
);
204 function cbConsoleMsg(e
, level
, msg
, line
, sourceid
){
206 console
.log(sourceid
);
210 function cbFinishLoad(webContents
){
211 if(!bHistory
) return;
212 let histItem
= webContents
.getTitle()+" "+webContents
.getURL()+"\n";
213 fs
.appendFile(historyFile
, histItem
, (err
) => {});
216 function cbFocus(webContents
){
217 let js
= "if(focusMesg){let m=focusMesg;focusMesg=null;m}";
218 win
.webContents
.executeJavaScript(js
,false).then((r
)=>{
219 //focusMesg as js code
221 if(r
) webContents
.executeJavaScript(r
,false);
225 function interceptRequest(details
, callback
){
226 if(!bJS
&& details
.url
.endsWith(".js")){
227 callback({ cancel
: true });
232 if(!details
.url
.startsWith("http")) break;
233 if(!details
.url
.startsWith(gredirect
)){
234 if(details
.resourceType
=== 'mainFrame'){
235 let wc
= details
.webContents
;
236 let url
= details
.url
;
238 let nUrl
= gredirect
+url
;
239 fetch(nUrl
).then(res
=>{
240 if(res
.ok
) return res
.arrayBuffer();
241 throw new Error(`Err: ${res.status} - ${res.statusText}`);
243 const u8
= new Uint8Array(aBuf
);
244 const b64
= Buffer
.from (u8
).toString('base64');
245 const dataUrl
= `data:text/html;base64,${b64}`;
246 wc
.loadURL(dataUrl
,{baseURLForDataURL
:url
});
248 console
.log(nUrl
+" err:",e
);
250 callback({ cancel
: true });
254 let newUrl
= gredirect
+ details
.url
;
255 callback({ cancel
: false, redirectURL
: newUrl
});
261 if(!bRedirect
||(details
.resourceType
!== 'mainFrame' &&
262 details
.resourceType
!== 'subFrame')) break;
263 let oURL
= new URL(details
.url
);
264 let domain
= oURL
.hostname
;
267 let newDomain
= redirects
[domain
];
268 if(!newDomain
) break;
269 newUrl
= "https://"+newDomain
+oURL
.pathname
+oURL
.search
+oURL
.hash
;
271 callback({ cancel
: false, redirectURL
: newUrl
});
274 callback({ cancel
: false });
277 function cbWindowOpenHandler(details
){
278 let url
= details
.url
;
279 let js
= "newTab();tabs.children[tabs.children.length-1].src='"+
281 switch(details
.disposition
){
282 case "foreground-tab":
284 js
= js
+ "switchTab(tabs.children.length-1)";
286 win
.webContents
.executeJavaScript(js
,false);
287 return { action
: "deny" };
289 function cbTitleUpdate(event
,title
){
292 function menuArray(labelprefix
, linkUrl
){
293 const menuTemplate
= [
295 label
: labelprefix
+'Open Link',
297 shell
.openExternal(linkUrl
);
301 label
: labelprefix
+'Copy Link',
303 clipboard
.writeText(linkUrl
);
307 label
: labelprefix
+'Download',
309 win
.contentView
.children
[i
].webContents
.downloadURL(linkUrl
);
316 function onContextMenu(event
, params
){
317 let url
= params
.linkURL
;
320 mTemplate
.push({label
:url
,enabled
:false});
321 mTemplate
.push
.apply(mTemplate
,menuArray("",url
));
322 if((url
=params
.srcURL
))
323 mTemplate
.push
.apply(mTemplate
,menuArray("src: ",url
));
324 }else if((url
=params
.srcURL
)){
325 mTemplate
.push({label
:url
,enabled
:false});
326 mTemplate
.push
.apply(mTemplate
,menuArray("src: ",url
));
330 const contextMenu
= Menu
.buildFromTemplate(mTemplate
);
335 const menuTemplate
= [
339 { label
: '', accelerator
: 'Ctrl+G', click
: ()=>{
340 let js
="{let q=document.forms[0].q;q.focus();q.value=tabs.children[iTab].src}"
341 win
.webContents
.executeJavaScript(js
,false)
343 { label
: '', accelerator
: 'Ctrl+L', click
:()=>{
344 win
.webContents
.executeJavaScript("document.forms[0].q.select()",false);
346 { label
: '', accelerator
: 'Ctrl+T', click
:()=>{
347 let js
= "newTab();document.forms[0].q.select();switchTab(tabs.children.length-1)";
348 win
.webContents
.executeJavaScript(js
,false);
350 { label
: '', accelerator
: 'Ctrl+R', click
: ()=>{
353 { label
: '', accelerator
: 'Ctrl+Shift+R', click
: ()=>{
354 if(0==gredirects
.length
) return;
355 gredirect
=gredirects
[0];
357 { label
: '', accelerator
: 'Ctrl+W', click
: ()=>{
358 win
.webContents
.executeJavaScript("tabClose()",false).then((r
)=>{
359 if(""===r
) win
.close();
360 else win
.setTitle(r
);
363 { label
: '', accelerator
: 'Ctrl+Tab', click
: ()=>{
364 let js
="tabInc(1);getWinTitle()";
365 win
.webContents
.executeJavaScript(js
,false).then((r
)=>{
369 { label
: '', accelerator
: 'Ctrl+Shift+Tab', click
: ()=>{
370 let js
="tabDec(-1);getWinTitle()";
371 win
.webContents
.executeJavaScript(js
,false).then((r
)=>{
375 { label
: '', accelerator
: 'Ctrl+Left', click
: ()=>{
376 let js
="tabs.children[iTab].goBack()";
377 win
.webContents
.executeJavaScript(js
,false);
379 { label
: '', accelerator
: 'Ctrl+Right', click
: ()=>{
380 let js
="tabs.children[iTab].goForward()";
381 win
.webContents
.executeJavaScript(js
,false);
383 { label
: '', accelerator
: 'Esc', click
: ()=>{
384 let js
= `{let e=document.activeElement;
385 if(e)e.blur();try{tabs.children[iTab].stopFindInPage('clearSelection')}catch(er){}}`;
386 win
.webContents
.executeJavaScript(js
,false);
388 { label
: '', accelerator
: 'F5', click
: ()=>{
389 win
.webContents
.executeJavaScript("tabs.children[iTab].reload()",false);
391 { label
: '', accelerator
: 'F12', click
: ()=>{
392 let js
= "try{tabs.children[iTab].openDevTools()}catch(e){console.log(e)}";
393 win
.webContents
.executeJavaScript(js
,false);
399 const menu
= Menu
.buildFromTemplate(menuTemplate
);
400 Menu
.setApplicationMenu(menu
);
403 function cmdlineProcess(argv
,cwd
,extra
){
404 let i1st
= 2+extra
; //index for the first query item
405 if(argv
.length
>i1st
){
406 if(i1st
+1==argv
.length
){//local file
407 let fname
= path
.join(cwd
, argv
[i1st
]);
408 if(fs
.existsSync(fname
)){
409 let js
= "tabs.children[iTab].src='file://"+fname
+"'";
410 win
.webContents
.executeJavaScript(js
,false);
411 win
.setTitle(argv
[i1st
]);
415 let url
=argv
.slice(i1st
).join(" ");
416 win
.webContents
.executeJavaScript("handleQuery(`"+url
+"`)",false);