6.6.3 - Alpha5 - webview 元素支持基于 JsBridge 的 Web 页面布局 (Ref to Auto.js Pro) (issue #281)

This commit is contained in:
SuperMonster003
2025-05-25 21:04:08 +08:00
parent 6159302829
commit 299c20ad72
30 changed files with 1454 additions and 30 deletions

View File

@@ -0,0 +1,163 @@
( /* @ModuleIIFE */ () => {
// @Reference to module __ui__.js from Auto.js Pro 9.3.11 on May 24, 2025.
let JsBridge = (function () {
let ResultAdapter = require('result-adapter');
let EVENT_REQUEST = '$autojs:internal:request';
let EVENT_RESPONSE = '$autojs:internal:response';
function JavaScriptBridgeImpl(webview) {
let self = this;
events.__asEmitter__(self);
self.nextId = 1;
self.requestHandlers = new Map();
self.webview = webview;
webview.setJavascriptEventCallback({
onWebJavaScriptEvent(event, args) {
let obj = unwrapJson(args) || [];
self.emit.apply(self, [ event, { name: event } ].concat(obj));
},
});
self.on(EVENT_REQUEST, function (e, request) {
let handler = self.requestHandlers.get(request.channel) ?? self.requestHandlers.get('');
if (!handler) {
self.sendResponseError(request, new Error('no handler for action: ' + request.channel));
return;
}
let event = {
channel: request.channel,
arguments: request.args,
};
let result;
try {
result = handler.apply(void 0, [ event ].concat(event.arguments));
} catch (e) {
self.sendResponseError(request, e);
return;
}
if (isPromise(result)) {
result.then(function (r) {
self.sendResponse(request, r);
}).catch(function (err) {
self.sendResponseError(request, err);
});
} else {
self.sendResponse(request, result);
}
});
return self;
}
JavaScriptBridgeImpl.prototype.sendResponse = function (request, result, error) {
this.send(EVENT_RESPONSE + ':' + request.id, {
result: result,
error: error,
});
};
JavaScriptBridgeImpl.prototype.sendResponseError = function (request, error) {
this.sendResponse(request, undefined, error.toString());
};
JavaScriptBridgeImpl.prototype.invoke = function (channel) {
let self = this;
let args = [];
for (let _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
let id = this.nextId++;
return new Promise(function (resolve, reject) {
self.once(EVENT_RESPONSE + ':' + id, function (event, result) {
if (result.error) {
reject(new Error('Error occurred while handling invoke: channel = ' + channel + ', error = ' + result.error));
} else {
resolve(result.result);
}
});
self.send(EVENT_REQUEST, {
id: id,
channel: channel,
args: args,
});
});
};
JavaScriptBridgeImpl.prototype.send = function (event) {
let args = [];
for (let i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
this.webview.sendEventToWebJavaScript(event, wrapJson(args));
};
JavaScriptBridgeImpl.prototype.handle = function (channel, handler) {
this.requestHandlers.set(channel !== null && channel !== void 0 ? channel : '', handler);
return this;
};
JavaScriptBridgeImpl.prototype.eval = function (code) {
let self = this;
return new Promise(function (resolve, reject) {
ResultAdapter.promise(self.webview.__eval(code))
.then(result => resolve(JSON.parse(String(result))))
.catch(err => reject(err));
});
};
function unwrapJson(maybeJson) {
if (!maybeJson) {
return undefined;
}
return JSON.parse(maybeJson);
}
function wrapJson(obj) {
if (typeof obj === 'undefined') {
return undefined;
}
return JSON.stringify(obj);
}
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
return JavaScriptBridgeImpl;
})();
/**
* @param {org.autojs.autojs.core.ui.widget.JsWebView} webview
*/
function initWebView(webview) {
webview.jsBridge = new JsBridge(webview);
let emitter = events.emitter();
webview.events = emitter;
webview.setSyncWebViewEventCallback({
onSyncWebViewEvent(event) {
dispatchJavaEvent(event, emitter);
},
});
webview.setSyncEventEnabled('', true);
function dispatchJavaEvent(event, emitter) {
let eventName = event.getName();
let args = Array.from(event.getArguments());
let _returnValue;
let returnValueSet = false;
let e = {
name: eventName,
arguments: args,
consumed: false,
};
Object.defineProperty(e, 'returnValue', {
get: function () {
return _returnValue;
},
set: function (value) {
_returnValue = value;
returnValueSet = true;
},
});
emitter.emit.apply(emitter, [ eventName, e ].concat(args));
}
}
module.exports = initWebView;
})();

View File

@@ -0,0 +1,90 @@
'use strict';
( /* @IIFE */ () => {
// @Reference to .../asset/web/dist/autojs.sdk.v1.js from Auto.js Pro 9.3.11 on May 24, 2025.
const EVENT_RESPONSE = '$autojs:internal:response';
const EVENT_REQUEST = '$autojs:internal:request';
// noinspection JSValidateTypes
/** @type {import('events')} */
let nodejsEvents = events;
let nextId = 1;
let helper = {
isPromise: o => o && typeof o.then === 'function',
unwrapJson: o => o ? JSON.parse(o) : undefined,
wrapJson: o => o === undefined ? o : JSON.stringify(o),
};
Object.setPrototypeOf($autojs, new nodejsEvents.EventEmitter());
/** @type {Internal.AutoJsBridge} */
let AutoJsBridge = {
requestHandlers: {},
onEventInternal(event, args) {
this.emit(event, ...helper.unwrapJson(args));
},
send(event, ...args) {
this.sendEventInternal(event, helper.wrapJson(args));
},
invoke(channel, ...args) {
let id = nextId++;
return new Promise((resolve, reject) => {
this.once(EVENT_RESPONSE + ':' + id, (result) => {
if (result.error) {
reject(new Error('Error occurred while handling invoke: channel = ' + channel + ', error = ' + result.error));
} else {
resolve(result.result);
}
});
this.send(EVENT_REQUEST, {
id, channel, args,
});
});
},
/** @this {Internal.AutoJsBridge} */
handle(channel, handler) {
this.requestHandlers[channel || ''] = handler;
return this;
},
removeHandler(channel) {
delete this.requestHandlers[channel];
},
handleRequest(request) {
let handler = this.requestHandlers[request.channel] || this.requestHandlers[''];
if (!handler) {
return;
}
let event = {
channel: request.channel,
arguments: request.args,
};
let result;
try {
result = handler(event, ...event.arguments);
} catch (e) {
this.sendResponse(request, undefined, e);
return;
}
if (helper.isPromise(request)) {
result.then((r) => {
this.sendResponse(request, r);
}).catch((err) => {
this.sendResponse(request, undefined, err);
});
} else {
this.sendResponse(request, result);
}
},
sendResponse(request, result, error) {
this.send(EVENT_RESPONSE + ':' + request.id, { result, error });
},
};
$autojs = Object.assign($autojs, AutoJsBridge);
$autojs.on(EVENT_REQUEST, AutoJsBridge.handleRequest.bind($autojs));
})();

File diff suppressed because one or more lines are too long