Skip to main content

web.view Quick Start Guide

1. Introduction

The web.view library in aardio is used to create browser controls based on the WebView2 (Edge/Chromium engine). WebView2 is excellent at simple interface,powerful performance, and robust stability .

More important, WebView2 is a built-in component of popular desktop operating systems like Windows 10 and Windows 11, enabling the creation of compact standalone EXE executables with minimal file size.

2. create web.view control

First, we need to import the necessary libraries and create a form:

import win.ui;
import web.view;

// create main form
var winform = win.form(text="web.view Example");

// create web.view
var wb = web.view(winform);

winform.show();
win.loopMessage();

Key Points:

  • use import web.view import web.view Lib
  • use web.view(winform) to create a web.view control, should specify a host control(either the main form or a custom control on the window).

The second constructor parameter of web.view can optionally be a table object specifying the startup parameters, for example:

var wb = web.view(winform, { 
extensions = true; // Optional field, enables loading browser extensions via `wb.loadExtension()`
language = "zh-CN"; // Optional field, sets browser UI language and Accept-Language request header
userDataDir = // Optional field, specifies user data directory (different directories isolate sessions)

/*
startArguments is also an optional field, specifies Chrome/Edge-compatible launch command-line arguments.
If startArguments is a table object, camelCase parameter names are auto-converted to hyphenated format with -- prefix.
*/
startArguments = {
proxyServer = "SOCKS5://proxy_address"; // Becomes --proxy-server
userAgent = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Chrome/100.0 Mobile" // Becomes --user-agent
};
})

The third and subsequent constructor parameters of web.view specify startArguments:

var wb = web.view(winform,userDataDir,startArguments,...) ;

Example:


// When multiple command line arguments are specified,
// command line escaping is handled by aardio and
// merged into a single space-separated string.
var wb = web.view(winform, ,`--user-agent=Mozilla/5.0`,`--accept-lang=zh-CN`)

// Space-separated command line arguments,
// escaping is handled in the original command line format
var wb = web.view(winform, ,`--user-agent=Mozilla/5.0 --accept-lang=zh-CN`)

3. Loading Web Content

The web.view control provides multiple methods to load web content:

// Load URL without specifying the HTTP Referer request header
wb.go("https://example.com");

// Navigate to URL, using the current URL as the HTTP Referer request header
wb.location = "https://example.com"

// Load HTML string
wb.html = "<html><body><h1>Hello World</h1></body></html>";

// Load local HTML file
wb.go("/web/index.html");

Notes:

  1. If the first parameter of wb.go specifies a [relative path under the application root directory (starting with a single slash or backslash)], and the path points to an embedded resource directory (packaged into the EXE file upon release), you must first import aardio's embedded HTTP server module. For example:
  • import wsock.tcp.simpleHttpServer (multi-threaded HTTP server)
  • import wsock.tcp.asynHttpServer (single-threaded asynchronous HTTP server)

aardio will automatically convert the resource path to a URL accessible via the embedded HTTP server. If wb.go specifies a path like index.html, the parent directory of index.html becomes the document root (documentBase, represented as / in the webpage), with automatic support for SPA (Single-Page Application) routing.

  1. If the second parameter of wb.go is a numeric value specifying a debugging port for frontend development, the web.view control will:
  • In the aardio IDE: Attempt to connect to the port and display a waiting page if unavailable.
  • In released executables: this parameter is ignored.
wb.go("\web\index.html", 37151);  // Debugging port 37151 (ignored in released builds)

4. How to call JavaScript in aardio.

First add the following JavaScript global function to the page:

<script> 
// Define the global function
window.add = function(a,b){
return a+ b; }
}
</script>

The above JS function can be called in aardio as follows:

//call the JS function
var result = wb.xcall(“add”,12,3);

//Display the return value
winform.msgbox(result);

wb.xcall uses JSON to convert function call parameters and return values between aardio and JS. The first argument specifies the JS expression from which the JS function can be retrieved.

wb.xcall blocks and waits for the return value of the JS function, if you don't need the return value, you can call the JS function asynchronously with wb.invoke instead. wb.xcall is identical to wb.invoke except for the difference of whether it waits for the return value of the JS function or not.

The wb.eval function allows you to directly execute a JS expression and get the return value. wb.eval uses JSON to convert the JS return value to an aardio object. Note that wb.eval is also a blocking caller.

We can also execute JS code through the wb.doScript function and optionally specify an asynchronous non-blocking callback function to get the JS return value, for example:

wb.doScript(window.location.href”, function(result){
winform.msgbox(result, “result is the value returned by JS)
})

Note: All functions that block the JS call will not get stuck, but will continue to process the window message while waiting.

5. Use wb.external to Export aardio Objects or Functions to JavaScript

The web.view control allows exporting aardio objects directly accessible to web pages via external. Example:

// Export aardio object to JavaScript  
wb.external = {
sayHello = function(name){
winform.msgbox("Hello, " + name);
}
}

// Invoke aardio function in the webpage
wb.html = /**
<!doctype html>
<html>
<body>
<button onclick="aardio.sayHello('World')">Click Me</button>
</body>
</html>
**/

Key Points:

  • aardio objects exported via wb.external take effect only if configured before loading the webpage or setting its HTML.
  • In JavaScript, use the global variable aardio to access the wb.external object.
  • All aardio objects, methods and properties of aardio objects are wrapped as Promise objects in JavaScript.
  • When calling a function exported from wb.external in JavaScript, the parameters and return values do not need to be converted to JSON. Except for the basic value types such as string, numeric, boolean, compatible array, buffer, etc., which can be passed directly, all other object types (object in JavaScript or table in aardio) are automatically converted to COM proxies, which allow us to manipulate cross-language native objects indirectly through the COM interface.

Note that such COM proxy objects cannot be used as native JavaScript objects, e.g., they cannot be used as data source objects for JavaScript charts or tables, whereas functions exported by wb.export do not have this restriction.

  • When JavaScript callback aardio function, you should not call back JavaScript function by blocking wb.eval, wb.xcall within the callback aardio function, but use non-blocking wb.invoke, wb.doScript to call JS function instead. Or call those synchronous blocking functions asynchronously via winform.setTimeout.

6. Use wb.export to export global Js objects

In aardio, you can use wb.export to export aardio objects as JavaScript global objects.

Point.

  • All exported aardio functions are Promise objects in JavaScript.
  • JavaScript calls to these aardio functions will convert parameters and return values automatically, across programming languages using JSON. The call parameters are converted to pure aardio objects, and the return values of the aardio functions are converted to pure JavaScript objects.

Example:

wb.export({
alert = function(msg){
winform.msgbox(msg)

// To avoid reentrancy, e.g., you can't invoke
// the alert function again in the alert callback
//wb.invoke(“alert(‘test’)”); };
};

nativeAdd = function(a,b) {
return a + b;
}
})

The parameters above are a table, where each element of the table has a key preceded by an equal sign named the name of the global variable exported to JavaScript, and a value followed by an equal sign named the global object exported to JavaScript.

The function exported by wb.export uses JSON protocol to automatically convert parameters and return values between aardio and JavaScript. JS functions usually support JSON-compatible parameters by default without special handling.

Note:

  • aardio objects exported via wb.external take effect only if configured before loading the webpage or setting its HTML.
  • When JavaScript callback aardio function, you should not call back JavaScript function by blocking wb.eval, wb.xcall in the aardio function, but use non-blocking wb.invoke, wb.doScript to call JS function instead. Or call wb.eval, wb.xcall asynchronously via winform.setTimeout.

7. Web Debugging

Right-click on a web page and select “Inspect” or press F12 to open DevTools, where you can view JS output and error messages in the console.

8. Waiting for web nodes

wb.waitEle waits for a specified HTML node in the current web page.

Usage:

wb.waitEle(selector,callback,timeout)
  • The selector parameter specifies a CSS selector that matches the waiting node.
  • callback is an optional parameter, which can be an aardio callback function or a string containing a JavaScript callback script.
  • If it is a callback function, the first callback parameter is the CSS selector if the specified node is found successfully, while the first callback parameter is null if it fails, and the second callback parameter is an error message.
  • If a JavaScript callback script is specified, the node found can be accessed by this object in the script.
  • If you do not specify a callback parameter, you wait synchronously (the code is executed backwards only when the function exits), otherwise you wait asynchronously (the code continues to be executed backwards).

In particular, wb.waitEle only works on a single page, opening another page will cause wb.waitEle to quit waiting.

wb.waitEle2 can wait elements across web pages, usage:

wb.waitEle2(selector,timeout)

The selector,timeout arguments are the same as wb.waitEle.

wb.waitEle2 does not have callback parameters, it can only wait synchronously. The advantage of wb.waitEle2 is that it works across web pages. If you need to open a different web page while waiting, you should use wb.waitEle2 that supports cross-web pages. you should use the wb.waitEle2 function to wait for a node that supports cross-web pages.

9. Full Example

//创建 winform 窗口
import win.ui;
var winform = win.form(text="窗口标题")

//创建 WebView2 浏览器控件
import web.view;
var wb = web.view(winform);

// 导出 aardio 函数
wb.external = {
add: function(a, b) {
return a + b;
}
}

// 指定网页 HTML 代码
wb.html = /******
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
</head><body>
<div id="result"></div>

<script>
(async ()=>{

//调用 aardio 导出的 wb.external.add 函数。
var num = await aardio.add(1, 2)

//显示结果
document.getElementById('result').innerText = num;
})()
</script>
******/;

//在当前页面等待指定节点,改用 wb.waitEle2 则支持跨网页等待
wb.waitEle("#result","this.style.color='red'");

//显示窗口
winform.show();

//启动界面消息循环
win.loopMessage();

10. 使用 wb.preloadScript 添加网页默认加载执行的 JavaScript。

wb.preloadScript 函数添加的 JavaScript 在网页初始化时执行,保证在 window.onload 事件前执行,每次添加都会增加 JavaScript 脚本而不会覆盖之前添加的脚本。

下面的示例使用 wb.preloadScript 实现禁止在网页按 F5,Ctrl + R 刷新:

import win.ui;
/*DSG{{*/
var winform = win.form(text="WebView2 浏览器控件 - 禁止按 F5,Ctrl + R 刷新")
/*}}*/

import web.view;
var wb = web.view(winform);

var initScript = /****

//禁止页面刷新
document.onkeydown = function (e) {
if (e.key == "F5" || (e.ctrlKey && e.key == "r") ) {
e.preventDefault();
}
}

//禁止滚轮缩放
document.addEventListener('wheel', function(e) {
if(e.ctrlKey) {
e.preventDefault();
}
}, { passive: false });

****/

//添加网页默认加载执行的 JavaScript
wb.preloadScript(initScript)

//打开网页
wb.go("https://www.example.com")

winform.show();
win.loopMessage();

10. CDP(Chrome DevTools Protocol) 接口

调用 CDP 命令的参数格式:

var result = wb.cdp(method,params,callback) 
  • method 参数指定 CDP 命令,例如 "Page.enable"
  • params 参数可选用一个表(table)或 JSON 字符串指定 CDP 参数
  • callback 参数可选指定一个回调函数,回调参数@1为 CDP 返回的表对象,回调参数@2 为错误代码。如果指定 callback 则 wb.cdp 不等待 CDP 命令执行结束而是异步回调 callback,如果不指定 callback 参数则 wb.cdp 会等待命令执行结束并且成功返回一个表对象( result ),失败返回 null,错误代码。

订阅 CDP 事件的参数格式:

wb.cdpSubscribe(event,callback)

调用 wb.cdpSubscribe 函数总是会先取消之前订阅的同名事件。

  • event 参数指定要订阅的事件名,例如 "Page.javascriptDialogOpening"。
  • callback 指定回调函数,如果不指定此参数(值为 null)则取消订阅。回调函数 callback 的回调参数 @1 是一个表对象。如果在回调函数内部返回一个函数对象,则先结束 CDP 事件,然后异步执行返回的那个函数对象。

下面是一个自动关闭弹框的例子:

import win.ui;
/*DSG{{*/
var winform = win.form(text="CDP 事件 - 自动关闭网页上弹出信息框")
/*}}*/

import web.view;
var wb = web.view(winform);
winform.show();

//调用 cdp 命令。
wb.cdp("Page.enable");

//订阅 CDP 事件
wb.cdpSubscribe("Page.javascriptDialogOpening",function(dlg){

//为避免阻塞导致某些网页出现异常,应返回异步执行的函数关闭弹框。
return function(){

//自动关闭弹框,CDP 参数为 {accept=true}
wb.cdp("Page.handleJavaScriptDialog",{accept=true});

//调用 JS 函数打印 dlg 参数:dlg.message 是对话框文本,dlg.type 是对话框类型,dlg.url 是对话框所在页面网址 。
wb.xcall("(v)=>document.write( JSON.stringify(v) )",dlg);
}
})

wb.html = /**
<script type="text/javascript">alert("测试弹框")</script>
**/
win.loopMessage();

使用 wb.cdpQuery 获取页面节点,参数格式:

var ele = wb.cdpQuery(selector,parent,callback)
  • 参数 selector 指定 CSS 选择器
  • 可选参数 parent 可用于指定父节点或父节点的 nodeId。不指定则以根节点为父节点。
  • 可选用参数 callback 指定回调函数,回调参数@1为 CDP 返回的表对象,回调参数@2 为错误代码。

如果不指定 callback 参数则成功返回表对象,wb.cdpQuery 失败返回 null,错误代码。如果指定 callback 参数则不会等待查询节果而是异步回调 callback 函数,wb.cdpQuery 成功返回 true,失败返回 null,错误代码。

wb.cdpWaitQuery 函数的用法与 wb.cdpQuery 类似,但会一直等待直到找到节点或者超时:

var ele = wb.wb.cdpWaitQuery(selector,parent,timeout)
  • 参数 selector 指定 CSS 选择器。
  • 可选参数 parent 可用于指定父节点或父节点的 nodeId。
  • 可选用 timeout 参数指定超时(以毫秒为单位), 其他与。

wb.cdpWaitQuery 与 wb.waitEle2 相同的是可以跨多页页面等待 CSS 选择器指定的节点出现。

示例:

import win.ui;
/*DSG{{*/
var winform = win.form(text="web.view - 调用 CDP 命令修改文件输入框路径")
/*}}*/

import web.view;
var wb = web.view(winform);

wb.html = `<input type="file">`

//获取控件
var fileInput = wb.cdpWaitQuery(`input[type="file"]`);

//设置文件路径
fileInput.files = { io._exepath };

//设置文件路径,fileInput 参数指定了 nodeId 与 files 这两个字段的值
wb.cdp("DOM.setFileInputFiles",fileInput)

winform.show();
win.loopMessage();

11. 多线程调用 web.view 对象

请参考: 多线程入门

web.view 对象可通过线程参数传入工作线程, 跨线程调用将回发到界面线程执行。

import win.ui;
/*DSG{{*/
var winform = win.form(text="多线程界面回调")
/*}}*/

import web.view;
var wb = web.view(winform);

//在网页 JS 脚本中通过全局变量名 aardio 调用 wb.external
wb.external = {
ping = function(domain){

//创建工作线程
thread.invoke(
function(wb,domain){

import process.popen;

//创建进程管道
var prcs = process.popen("ping "+ domain);

for stdout in prcs.each() {
wb.invoke("document.body.insertAdjacentText",'beforeend',stdout);
}

},wb,domain //线程启动参数
)
}
}

wb.html = /**
<body style="white-space: pre;">
<button
onclick="javascript:aardio.ping('www.example.com')"
>开始干活了</button>
**/

winform.show();
win.loopMessage();

12. 弹窗事件 wb.onNewWindow

wb.onNewWindow 可用于拦截网页弹窗,示例:

import win.ui;
/*DSG{{*/
var winform = win.form(text="拦截网页弹窗";right=818;bottom=507)
/*}}*/

import web.view;
var wb = web.view(winform);

//弹出新窗口触发
wb.onNewWindow = function(url){

//耗时操作应返回异步自动执行的函数(提前结束 onNewWindow)
return function(){

//如果打开的是 file: 前缀网址,例如拖放文件到网页上。
var filePath = inet.url.getFilePath(url)

if(filePath){
winform.msgbox(filePath,"本地文件");
}
else {
//用 wb.location 跳转才会指定 HTTP referrer 请求头
wb.location = url;
}
}
}

wb.html = /**
<html><head>
<base target="_blank" />
</head>

<a href="http://www.aardio.com">aardio.com</a>
<button onclick="window.open('http://www.aardio.com')" >aardio.com</button>
**/

winform.show();
win.loopMessage();