//app 应用程序 import process; import win.reg; import wsock; import web.socket.jsonServer; import web.rpc.jsonServer; import string.aasdl; import winex; import winex.key; import chrome.path; import win.ui; import fsys.environment; import gdi; namespace chrome; class app { ctor(winform,chromePath){ if(_WINE) winform = null; this._form = winform; if(this._form){ if( this._form.isForm ? this._form.onEraseBkgnd === null ){ this._form.onEraseBkgnd = function(hwnd,message,wParam,lParam){ return 0; } } else { this._form.modifyStyle(0x20/*_WS_EX_TRANSPARENT*/,0x2000000/*_WS_CLIPCHILDREN*/); } } this.ws = ..web.socket.server(); this.ws.heartbeatInterval = 10; this.rpc = ..web.socket.jsonServer(this.ws); this.http = this.ws.httpServer; this.ws.onUpgradeToWebsocket = function(hSocket,request,response,protocol,origin){ if( request.pathInfo == "/rpc/ws" ){ return this.rpc.start(hSocket); } response.close(); } this.ws._onClientClosed = function(hSocket){ } this.ws.onOpen = function(hSocket){ this.rpc.notify(hSocket,"rpcClientId",tonumber(hSocket) ); } this.ws.onError = function(hSocket,err){ if(this.onError){ return this.onError(hSocket,err); } errput(err,"chrome/rpc error"); } this.ws.onClose = function(hSocket,err){ if ( this.hwndChrome && ( !..win.isWindow(this.hwndChrome) ) ){ if(this._form)this._form.close(); this.onQuit(); } if(this.onClose){ return this.onClose(hSocket,err); } } this.httpHandler = { ["/aardio.js" ] = function(response,request){ var ip,port = this.ws.getLocalIp(); var js = ..string.replace($"~\lib\chrome\.res\aardio\index.js" ,"@{{{$rpcServerPort}}}",tostring(port)); js = ..string.replace(js,"@{{{$rpcAasdl}}}",this.$aasdl); response.contentType = "text/javascript"; response.write(js) } ["/favicon.ico" ] = function(response,request){ if(this.favicon){ response.contentType = "image/x-icon"; response.write( ..string.loadBuffer(this.favicon) ) } } ["/rpc/http"] = function(response,request){ var jsonServer = ..web.rpc.jsonServer(); jsonServer.rpc.external = this.rpc.external; jsonServer.rpc.onError = this.onHttpRpcError; var rpcSocket = topointer( request.headers["rpc-client-id"] ); if(rpcSocket) { this.hActiveSocket = rpcSocket; jsonServer.rpc.run(rpcSocket); } else { response.errorStatus(400,"请求头缺少:rpc-client-id"); } } } this.http.run( function(response,request,session){ response.headers["Access-Control-Allow-Headers"] = "Content-Type, Rpc-Client-Id, *" if(type.isString(this.accessControlAllowOrigin)){ response.headers["Access-Control-Allow-Origin"] = this.accessControlAllowOrigin; } else{ var origin = request.headers["origin"] if( origin && this.accessControlAllowOrigin[[ origin ]]){ response.headers["Access-Control-Allow-Origin"] = origin response.headers["Vary"] = "Origin"; } } if(request.method != "OPTIONS"){ var handler = this.httpHandler[request.pathInfo]; if( handler ) { if(type.isString(handler)) return response.write(handler) return handler(response,request,session); } response.loadcode( request.path ); } else{ if(this.accessControlRequestPrivateNetwork) { response.headers["Access-Control-Request-Private-Network"] = this.accessControlRequestPrivateNetwork; } response.status = "204 No Content"; } } ); this.serverIp = "127.0.0.1"; this.serverPort = null; if(!chromePath){ if( !self.path ){ self.path = ..chrome.path(true); if(!self.path){ return null,"系统未安装chrome"; } } chromePath = self.path; } this.chromePath = chromePath; this.$indexReadyCallbacks = {}; this.accessControlRequestPrivateNetwork = "true"; this.accessControlAllowOrigin = "*"; }; publish = function(...){ this.rpc.publish(...) }; survey = function(...){ this.rpc.survey(...) }; xcall = function($,method,...){ if( type($) == "string" )return this.rpc.xcall(this.getActiveSocket(),method,...); return this.rpc.xcall($ : this.getActiveSocket(),method,...); }; notify = function($,method,...){ if( type($) == "string" )return this.rpc.notify(this.getActiveSocket(),method,...); return this.rpc.notify($ : this.getActiveSocket(),method,...); }; doScript = function($,js,...){ if( ... !== null ) js = ..string.format(js,...); return this.notify($,"doScript",js); }; callback = function(name,func){ if( type(name) != type.string ) error("参数@1必须是字符串",2); if( (func!==null) && (type(func) != type.function ) )error("参数@2必须是函数对象",2); this.rpc.xcallback[name] = func; }; getActiveSocket = function(){ return this.hActiveSocket; }; msgbox = function(str,title,style){ return ..win.msgbox(str,title,style,this.hwndChrome ) }; msgboxTest = function(str,title){ return ..win.msgboxTest(str,title,this.hwndChrome ) }; msgboxErr = function(str,title){ return ..win.msgboxErr(str,title,this.hwndChrome ) }; setPos = function(x,y,cx,cy,...){ if(this.hwndChrome){ var dx,dy = ..gdi.getDpiScale(,this.hwndChrome); if(x!==null) x = x * dx; if(y!==null) y = y * dy; if(cx!==null) cx = cx * dx; if(cy!==null) cy = cy * dy; ..win.setPos(this.hwndChrome,x,y,cx,cy,...); } else { this.$position = {x,y,cx,cy,...} } }; center = function(target){ if(this.hwndChrome) ..win.center(this.hwndChrome,target); else { this.$position = "center" } }; addArguments = function(...){ if( !this.chromeArguments )this.chromeArguments ={}; if( type(...) == type.table ) ..table.append(this.chromeArguments,...) else ..table.push( this.chromeArguments,... ); }; getUrl = function(url){ return this.http.getUrl(url); }; close = function(){ var hwnd = this.hwndChrome; if(!hwnd){ if(this.process && (!_WINE) ){ return ..win.close( ..win.wait( lambda() this.hwndChrome ) ); } } else {..win.close(this.hwndChrome);} }; onQuit = function(){ if(!this._form){ ..win.quitMessage() } }; indexReady = function(proc){ if(this.$indexReadySocket){proc(this.$indexReadySocket);} else {..table.push(this.$indexReadyCallbacks,proc);} }; start = function(indexUrl,laucher,timeout){ if(type(laucher)==type.number){ if(_STUDIO_INVOKED){ global.import("chrome.dev"); ..chrome.dev(this,indexUrl,laucher,timeout); return; } else { laucher = null; } } this.external = ..table.mix( this.external,{ $onUrlReady = function($,url){ if(!this.$indexReadySocket){ if(this.hwndChrome && this._form){ var rc = this._form.getRect(true); ..win.setRect(this.hwndChrome,rc,true) } this.$indexReadySocket = $; if( this.onIndexReady ) this.onIndexReady(); var $indexReadyCallbacks = this.$indexReadyCallbacks; for(i=1;#$indexReadyCallbacks;1){ var p = $indexReadyCallbacks[i] p($); } this.$indexReadyCallbacks = {}; } if(this.onUrlReady){ this.onUrlReady($,url); } }; quit = function(){ if(this.hwndChrome){ var hwnd = ..win.getRoot(this.hwndChrome); if(hwnd) ::PostMessage(hwnd,0x10/*_WM_CLOSE*/,0,0); } this.onQuit(); }; hitClose = function(){ if(this.hwndChrome){ var hwnd = ..win.getRoot(this.hwndChrome); if(hwnd) ::PostMessage(hwnd,0x10/*_WM_CLOSE*/,0,0); } }; hitCaption = function(){ if( this._form ){ error("禁止跨窗口发送标题栏拖动指令",2); return; } if(!this.hwndChrome)return; var hwnd = ..win.getRoot(this.hwndChrome); if( ..win.isZoomed(hwnd) ) return; ..win.releaseCapture(); ::PostMessage(hwnd,0x112/*_WM_SYSCOMMAND*/,0xF010/*_SC_MOVE*/ | 2/*_HTCAPTION*/,0) }; isZoomed = function(){ return ..win.isZoomed(hwnd); }; hitMax = function(){ if(!this.hwndChrome)return; var hwnd = ..win.getRoot(this.hwndChrome); var zoomed = ..win.isZoomed(hwnd); if(zoomed) ::PostMessage(hwnd,0x112/*_WM_SYSCOMMAND*/,0xF120/*_SC_RESTORE*/, 0); else ::PostMessage(hwnd,0x112/*_WM_SYSCOMMAND*/,0xF030/*_SC_MAXIMIZE*/,0); return !!zoomed; }; hitMin = function(){ if(!this.hwndChrome)return; var hwnd = ..win.getRoot(this.hwndChrome); ::PostMessage(hwnd,0x112/*_WM_SYSCOMMAND*/,0xF020/*_SC_MINIMIZE*/, 0); }; hitmax = function(){ return owner.hitMax(); }; hitmin = function(){ return owner.hitMin(); }; print = function(){ if(!this.hwndChrome)return; ..winex.key.combine(this.hwndChrome,"CTRL","P") }; showDevTools = function(){ if(!this.hwndChrome)return; ..winex.key.click(this.hwndChrome,"F12") }; zoom = function(n){ if(!this.hwndChrome)return; if( n > 0 ){ ..winex.key.combine(this.hwndChrome,"CTRL","+") } elseif(n < 0 ){ ..winex.key.combine(this.hwndChrome,"CTRL","-") } else { ..winex.key.combine(this.hwndChrome,"CTRL","0") } }; copy = function(){ if(!this.hwndChrome)return; ..winex.key.combine(this.hwndChrome,"CTRL","C") }; paste = function(){ if(!this.hwndChrome)return; ..winex.key.combine(this.hwndChrome,"CTRL","V") }; find = function(){ if(!this.hwndChrome)return; ..winex.key.combine(this.hwndChrome,"CTRL","F") }; fullscreen = function(){ if(!this.hwndChrome)return; ..winex.key.click(this.hwndChrome,"F11") }; $onLoadUrl = function($,url){}; }); this.rpc.external = this.external; this.ws.start(this.serverIp,this.serverPort); var chromeWins = {} for hwnd,title,threadId,processId in ..winex.each( "|") { chromeWins[hwnd] = processId; } if(!..string.indexOf(indexUrl,":") && ..io.localpath(indexUrl) ){ var indexPath,extraInfo = ..string.match(indexUrl,"([^\#\?]+)(.*)"); var tPath = ..io.splitpath(indexPath); if( ( ..string.cmp(tPath.file, "index.html")==0) || ( ..string.cmp(tPath.file, "index.aardio")==0) ){ indexUrl = this.http.spaUrl(..string.concat(tPath.file,extraInfo),tPath.dir); } else { indexUrl = this.http.spaUrl(indexUrl); } if(!this.favicon){ this.favicon = ..io.joinpath(..io.splitpath(indexUrl).dir,"favicon.ico"); } } var ip,port = this.ws.getLocalIp(); this.$aasdl = ..string.aasdl(this.external); this.indexUrl = ..inet.url.appendExtraInfo(indexUrl ,"rpcServerPort=" + port +"&rpcAasdl=" + ..inet.url.encode(this.$aasdl) ); var chromeArguments = { "--app=" + this.indexUrl;"--url=" + this.indexUrl;"--hide-controls" }; ..table.append(chromeArguments,this.chromeArguments); if( this.remoteDebuggingPort != null ){ if(!this.remoteDebuggingPort) this.remoteDebuggingPort = ..wsock.getFreePort(); ..table.push(chromeArguments,"--remote-debugging-port=" + this.remoteDebuggingPort); } if( this.disableWebSecurity ){ ..table.push(chromeArguments,`--disable-web-security`); if( !this.userDataDir ){ this.userDataDir = "%LocalAppData%\aardio\chrome.app.userdata"; } } if( this.userDataDir != null ){ ..table.push(chromeArguments,`--user-data-dir=` + ..fsys.environment.expand(this.userDataDir) ); } var threadLocked; if( laucher ){ var ok,err = laucher(chromeArguments); if(!ok) return null,err; } else { if(!_WINE) ..thread.lock("chrome.app.38C889D9-440E-48E8-9002-9B51D069902A"); var pid = ..process.execute(this.chromePath,chromeArguments); if( !pid ){ return false; } } if(!_WINE){ while( ..win.peekPumpMessage()!==null ){ for hwnd,title,threadId,processId in ..winex.each( "|") { if(!chromeWins[hwnd]){ if( !..win.isVisible(hwnd) ) continue; if( ..win.getOwner(hwnd) ) continue; if( this._form ){ ..win.setPos(hwnd,-9000,-9000); ..win.modifyStyle( hwnd ,0x400000/*_WS_DLGFRAME*/ | 0x40000/*_WS_THICKFRAME*/ |0x800000/*_WS_BORDER*/ | 0xC00000/*_WS_CAPTION*/ |0x80000/*_WS_SYSMENU*/ |0x20000/*_WS_MINIMIZEBOX*/ |0x10000/*_WS_MAXIMIZEBOX*/ |0x20000/*_WS_GROUP*/ |0x10000/*_WS_TABSTOP*/ |0x4000000/*_WS_CLIPSIBLINGS*/ ,0x40000000/*_WS_CHILD*/ ); ..win.modifyStyleEx( hwnd ,8/*_WS_EX_TOPMOST*/ |0x40000/*_WS_EX_APPWINDOW*/ | 0x100/*_WS_EX_WINDOWEDGE*/ ,_WS_EX_TRANSPARENT ); ..win.setParent(hwnd,this._form.hwnd); this._form.adjust = function(){ var rc = this._form.getRect(true); ..win.setRect(hwnd,rc,true) } } else { if(this.$position){ if(..win.isZoomed(hwnd)) ..win.show(hwnd,9/*_SW_RESTORE*/) if(this.$position == "center")..win.center(hwnd); else { var p = ..table.clone(this.$position); var dx,dy = ..gdi.getDpiScale(,hwnd); if(p[1]!==null) p[1] = p[1] * dx; if(p[2]!==null) p[2] = p[2] * dy; if(p[3]!==null) p[3] = p[3] * dx; if(p[4]!==null) p[4] = p[4] * dy; ..win.setPos(hwnd,..table.unpackArgs(p)); } } } ::User32.AttachThreadInput( threadId,..thread.getId(),true); this.hwndChrome = hwnd; this.chromeProcessId = processId; this.chromeThreadId = threadId; break 2; } } } } if(threadLocked) ..thread.unlock("chrome.app.38C889D9-440E-48E8-9002-9B51D069902A"); if( !this._form ){ var isWindow = ..win.isWindow; this.messageOnly = ..win._form().messageOnly(); if(this.hwndChrome){ this.messageOnly.setInterval( function(){ if( !isWindow(this.hwndChrome) ){ if(this._form)this._form.close(); this.onQuit(); return false; } },3000 ) } else { this.messageOnly.setInterval( function(){ if( this.ws.clientCount() < 1 ){ for(i=1;10;1){ ..win.delay(1000); if( this.ws.clientCount() ){ return; } } this.onQuit(); return false; } },3000 ) } } }; } /**intellisense() chrome.app = 调用系统已安装的 Chrome 创建应用程序,\n支持 Chrome 、 Edge(Chromium )、Supermium 等浏览器,\n\n如果没有找到可用的浏览器会下载安装 Microsoft Edge (Chromium)。\nWin7 则自动安装 Chrome 109,XP 自动安装 Chrome 49 chrome.app(.(父窗口,chrome启动程序路径) = 调用系统已安装的chrome创建应用程序\n所有参数可选,参数@2也可以指定CEF3启动程序cefclient.exe的路径\n使用CEF3支持在参数@1指定父窗口并嵌入浏览器到该窗口,\n原版Chrome嵌入父窗口会出现兼容性问题,\n\n!processchromeapp. chrome.app.path = 自定义Chrome.exe位置,\n支持Chrome,兼容Chrome启动参数的Chrome内核浏览器,\n也可以指定CEF3启动程序cefclient.exe chrome.app() = !processchromeapp. end intellisense**/ /**intellisense(!processchromeapp) _form = 创建chrome.app时指定的父窗口\n!static. ws = aardio创建的WebSocket服务器\n!stdwebsocketserver. rpc = aardio创建的JSON-RPC服务端\n!stdwebsocketjsonserver. accessControlAllowOrigin = 允许写入 Access-Control-Allow-Headers 响应头的站点地址。\n此属性必须为 null、字符串,或者指定为一个表对象,\n表的键为允许跨域调用的站点地址,值必须为 true\n站点地址应使用 https://host 格式,且结尾不要有斜杠,\n新的浏览器已经禁止在非 HTTPS 协议下调用本机地址,\n而老版浏览器则反之 accessControlRequestPrivateNetwork = 自定义 Access-Control-Request-Private-Network 响应头的值,\n默认为 "true",可改为 null http = aardio创建的HTTP服务器\n!stdasynHttpServer. serverIp = 指定嵌入服务器的监听IP\n默认值为 "127.0.0.1",仅限本机可以访问\n如果希望外网可以访问,可以赋值为null即可\n在调用start函数以前指定此属性才有效 serverPort = 指定嵌入服务器的监听端口\n建议保持默认值null\n不指定端口可以随机分配空闲端口,不会出现端口冲突的问题\n在调用start函数以前指定此属性才有效 publish(.(method,->->->) = 主动向所有客户端发送通知\nmethod指定网页客户端方法名,\n可添加任意个调用参数 survey(.(method,->->->) = 发起调查任务,\n调用所有网页客户端的同名函数,\nmethod指定客户端方法名,\n可添加任意个调用参数\n\n请使用callback函数指定调查结束后客户端回调的函数 notify(.($,"method",->->->) = 调用指定chrome函数,但不需要客户端回调反馈,\n\n参数@1指定客户端套接字句柄,\nRPC服务端远程回调函数名首字符为$时,\n第一个回调$参数即为当前客户端套接字句柄,\n除非熟悉getActiveSocket函数导致的潜在问题,请不要省略$参数 xcall(.($,"method",->->->) = 调用chrome函数,\n参数@1指定客户端套接字句柄,\nRPC服务端远程回调函数名首字符为$时,第一个回调$参数即为当前客户端套接字句柄,\n除非熟悉getActiveSocket函数导致的潜在问题,请不要省略$参数\n\n在chrome的js代码使用 aardio.on("method")\n添加允许aardio调用的js回调函数.\n\n可选使用callback函数指定一个同名回调函数按收本次调用chrome的返回值 callback(name,callback) = @.callback("__/*要接收返回值的JS函数名,\n回调叁数$为RPC客户端套接字句柄\n成功result为返回值,失败err为错误信息*/",function($,result,err){\n \n}) doScript(.($,js,->->->) = 在chrome中执行javascript代码,忽略返回值,\n在js参数后可选指定多个字符串格式化参数用于调用string.form格式化代码,\n\n参数@1指定客户端套接字句柄,\nRPC服务端远程回调函数名首字符为$时,\n第一个回调$参数即为当前客户端套接字句柄,\n除非熟悉 getActiveSocket 函数导致的潜在问题,请不要省略$参数 external = @.external = { __/*可以在这里指定允许chrome访问的对象和函数\n在chrome里引用虚拟的"/aardio.js"导入aardio对象即可访问这里的成员函数,\n必须在调用start函数以前设置此对象才能生效*/ } getActiveSocket() = 获取RPC服务端当前活动套接字句柄\n,任何触发消息处理、异步套接字处理程序的代码都有可能改变这个函数的返回值\n任何时候都不推荐使用此函数\n\n更好的替代方案是在RPC函数名前添加$字符,用于通知aardio在回调参数中添加$参数\n回调$参数可以稳定可靠的获取当前套接字 chromeProcessId = chrome进程ID\n注意chrome可能会使用一个进程启动多个独立的应用 chromeThreadId = chrome启动线程ID chromePath = chrome启动exe文件路径 hwndChrome =chrome窗口句柄\n如果在wine环境下运行,此窗口句柄为空值,依赖此句柄的部分chrome窗口操作函数将无效 getUrl("/__") = 参数指定资源文件路径,\n转换并返回为可以通过内嵌 HTTP 服务端访问的网址\n\n如果使用 start 函数启动了单页应用,且启动文件名为 "index.html" 或 "index.aardio",\nchrome.app 会自动将启动路径的上级目录设为 http.documentBase。\n此时 getUrl 的参数应当以 http.documentBase 为根目录 start(.(url) = 启动chrome应用\n参数@1可以指定资源目录下的aardio文件,\naardio 会自动使用嵌入HTTP服务器调用该文件。\n\n自动支持嵌入资源路径与 SPA 单页应用。\n如果同时文件名为 index.html 或 "index.aardio",\n则上级目录设为 http.documentBase 根目录,\n后续请求网址应当以 "/" 代替 http.documentBase 根目录 start(.(url,devPort,timeout) = 启动 Chrome 应用\n参数@1可以指定资源目录下的 aardio 文件,\n在开发环境中可选用devPort指定前端项目调试服务器端口\n\n自动支持嵌入资源路径与 SPA 单页应用。\n如果同时文件名为 index.html,则上级目录设为根目录,且路径转换为 "/index.html" start(.(url,laucher) = 启动 Chrome 应用\n参数@1可以指定资源目录下的 aardio 文件,\n可选在参数@2中指定一个函数对象用于自定义启动Chrome的代码,\n此启动函数唯一的回调参数是一个包含Chrome启动参数的数组\n自定义启动函数成功应返回true msgbox("__") = 弹出对话框\n参数@1指定显示的数据,如果是表对象先序列化为文本,\n其他对象调用tostring转换为文本\n\n此函数调用win.msgbox,但设定父窗口为当前chrome窗体 msgbox("__","标题") = 弹出对话框\n参数@1指定显示的数据,如果是表对象先序列化为文本,\n其他对象调用tostring转换为文本\n\n此函数调用win.msgbox,但设定父窗口为当前chrome窗体 msgboxErr("__") = 弹出错误对话框\n参数@1指定显示的数据,如果是表对象先序列化为文本,\n其他对象调用tostring转换为文本\n\n此函数调用win.msgbox,但设定父窗口为当前chrome窗体 msgboxErr("__","标题") = 弹出错误对话框\n参数@1指定显示的数据,如果是表对象先序列化为文本,\n其他对象调用tostring转换为文本\n\n此函数调用win.msgbox,但设定父窗口为当前chrome窗体 msgboxTest("__") = 弹出询问对话框\n参数@1指定显示的数据,如果是表对象先序列化为文本,\n其他对象调用tostring转换为文本\n\n此函数调用win.msgbox,但设定父窗口为当前chrome窗体 close() = 关闭chrome窗口 remoteDebuggingPort = 指定远程调试端口\n如果指定为0,则自动分配1025以后的空闲端口\n此属性启动后更新为实际开始的调试端口号 userDataDir = 设置用户数据目录,\n使用 fsys.environment自动转换%LocalAppData%等路径环境变量\n远程调试以及全屏功能,都要求使用同一用户数据目录时只有一个正在运行的chrome进程 indexReady = @.indexReady(\n function($){\n __/*如果加载首页已完成,\n并且页面上的DOM内容、aardio模块都已准备就绪,\n将立即执行在此注册的回调函数,\n可重复添加回调,并保证按添加的前后顺序调用且仅调用一次\n参数 $ 为触发此事件的网页RPC客户端套接字句柄*/\n } \n) onIndexReady = @.onIndexReady = function(){ __/*主窗口首次加载完成startEnviron.indexUrl指定的首页,\n并且页面上的DOM内容、aardio模块都已准备就绪*/ } onUrlReady = @.onUrlReady = function(hSocket,url){ __/*页面加载完成并且页面上的DOM内容、aardio模块都已准备就绪*/ } onQuit = @.onQuit = function(){ ..win.quitMessage();__/*Chrome关闭网页客户端触发此代码\n此事件触发以后hwndChrome会变为空值*/ } onError = @.onError = function($hSocket,err){ errput(err,"chrome/rpc error");__/*自定义RPC错误处理*/ } onClose = @.onClose = function($hSocket,err){ __/*一个网页窗口断开连接触发此事件*/ } httpHandler = @.httpHandler["/test.js" ] = function(response,request){ __/*自定义HTTP处理程序\n键为请求的路径,值为处理函数或者HTML代码*/ } favicon = 指定窗口图标路径\n默认为/res/favicon.ico setPos( = 调整窗口位置或排序,所有参数可选。\n如果要调用此函数,建议在调用 start 函数前调用。\n只有浏览器显示为独立窗口时才支持此函数 setPos(.(x坐标,y坐标,宽,高,插入位置,选项) = 调整窗口位置或排序,所有参数可选。\n此函数将根据当前 DPI 缩放设置自动缩放参数。\n同时指定 x,y 坐标则移动位置。\n同时指定宽高则改变大小。\n指定插入位置<句柄或_HWND前缀常量>则调整Z序。\n选项不用指定,可参考此函数源码了解细节 center( = 居中窗口,并调整以保证显示在可见范围内,\n如果要调用此函数,建议在调用 start 函数前调用,\n只有浏览器显示为独立窗口时才支持此函数 center(.(目标窗口句柄) = 居中窗口,并调整以保证显示在可见范围内,\n如果要调用此函数,建议在调用 start 函数前调用,\n目标窗口句柄如果为空则取父窗口或所有者窗口,为0表示桌面 addArguments(__) = 添加一个或多个Chrome启动参数,\n参数也可以是一个包含多个启动参数的数组\n注意参数中不必要使用引号,多个参数应分开写不要拼接成一个参数\n每个启动参数都是使用两个横杠开始的字符串\n[chrome启动参数大全]( https://peter.sh/experiments/chromium-command-line-switches/ ) end intellisense**/