LoginSignup
2
1

More than 1 year has passed since last update.

webview.hostObjects が async で失敗するのをなんとかする

Last updated at Posted at 2022-01-11

ver.2:message を汚染せず匿名関数でやりくりするタイプ
https://qiita.com/MoyomonDQ/items/f86f7726973693c20c95
 

JavaScript
addEventListener('_exe.Ready',e=>{

    var url = `https://script.google.com/macros/s/SampleDeployId/exec`;
    var text = `SampleText: Fixed newly exposed boat not showing up in the boat yard menu 
Changed contraptions to drop resources and inventory objects (crates) to drop resources even 
when killed with fire. Previously anything killed by fire would drop nothing.`

    chrome.webview.hostObjects.exe.PostError(url,text).then(r=>{
        console.log(r)// null、async失敗
    })
    _exe.post(url,text).then(r=>{
        console.log(r)// async成功
    })
})

_exe = new class {
    constructor(){
        this.promise = {}
        //this.on = {}
        addEventListener('Bridge.Ready',e=>{
            chrome.webview.hostObjects.exe.GetMethods().then(a=>{
                a.forEach(s=>this.addMethod(...s.split(',')))
                chrome.webview.addEventListener('message',e=>{
                    this.message(JSON.parse(e.data))
                })
                dispatchEvent(new Event('_exe.Ready'))
            })
        },true)
    }
    addMethod(m,attr){
        var s = m[0].toLowerCase() + m.slice(1);
        if(this[s]) return
        if(attr=='async'){
            this[s] = (...a)=>new _exe.Promise.Message(m,...a)
        }else{
            this[s] = (...a)=>new _exe.Promise.Json(m,...a)
        }
    }
    message(v){
        switch(true){
            //case ('type' in v): _exe.on[v.type](v);                   break
            case ('id' in v)    : this.promise[v.id].resolve(v.value);  break
            default:console.log(v)
        }
    }
}
_exe.Promise = class _exePromise{
    constructor(){this.hook = ()=>{}}
    then(resolve){
        this.hook = resolve
        return this
    }
}
_exe.Promise.id = 0
_exe.Promise.Json = class _exePromiseJson extends _exe.Promise{
    constructor(s,...a){super()
        chrome.webview.hostObjects.exe[s](...a).then(r=>{
            try{this.hook(JSON.parse(r))}
            catch(e){this.hook(r)}
        })
    }
}
_exe.Promise.Message = class _exePromiseMessage extends _exe.Promise{
    constructor(s,...a){super()
        this.id = ++_exe.Promise.id
        _exe.promise[this.id] = this
        chrome.webview.hostObjects.exe[s](...a,this.id)
    }
    resolve(r){
        this.hook(r)
        delete _exe.promise[this.id]
    }
}
Bridge.cs
public class Bridge{
    protected CoreWebView2 core = null!;
    public Bridge(WebView2 view) {
        view.NavigationCompleted+=(s,e) => {
            core = view.CoreWebView2;
            core.AddHostObjectToScript("exe",this);
            Dispatch("Bridge.Ready");
        };
    }

    //nullが返る
    public async Task<string> PostError(string url,string data) {
        var content = new StringContent(data,Encoding.UTF8);
        using(var client = new HttpClient()) {
            var response = await client.PostAsync(url,content);
            var s = await response.Content.ReadAsStringAsync();
            return s;
        }
    }
    //手動でResolve 
    public async void Post(string url,string data,int resolve) {
        var content = new StringContent(data, Encoding.UTF8);
        using (var client = new HttpClient()){
            var response = await client.PostAsync(url, content);
            var s = await response.Content.ReadAsStringAsync();
            Resolve(resolve,s);
        }
    }

    class ResolveData {
        public int id { get; set; } = 0;
        public object value { get; set; } = null!;
    }
    protected void Resolve(int i,object v) {
        var r = new ResolveData() { id=i,value=v };
        var s = JsonSerializer.Serialize(r);
        core?.PostWebMessageAsString(s);
    }

    protected async void Dispatch(string type) {
        if(core!=null) {
            var s = $"dispatchEvent(new Event('{type}'))";
            var r = await core.ExecuteScriptAsync(s);
        }
    }

    public string[] GetMethods(){
        var objectMethods = typeof(Object).GetMethods();
        var async = typeof(AsyncStateMachineAttribute);
        var methods = new List<string>();
        foreach(MethodInfo m in GetType().GetMethods()) {
            if(!Array.Exists(objectMethods,v => { return v.Name==m.Name; })){
                if(m.GetCustomAttribute(async)==null) {
                    methods.Add(m.Name);
                } else {
                    methods.Add($"{m.Name},async");
                }
            }
        }
        return methods.ToArray();
    }
}
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1