forked from raintean/blink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
interop.go
400 lines (357 loc) · 10.7 KB
/
interop.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
package blink
//#include "interop.h"
import "C"
import (
"bytes"
"errors"
"fmt"
"reflect"
"strings"
"text/template"
"time"
"unsafe"
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
)
//js内容模板
var jsTemplate, _ = template.New("js").Parse(`
window.BlinkData={};
{{range $key,$value := .Data}}
window.BlinkData.{{$key}}=JSON.parse("{{$value}}", __date_parser__);
{{end}}
window.BlinkFunc={};
{{range $name,$func := .Func}}
window.BlinkFunc.{{$name}}=__blink_invoker__.bind(null,"{{$name}}");
{{end}}
`)
//js预制内容
var jsContent = `
//时间解析器
window.__date_parser__ = function (key, value) {
if (typeof value === 'string') {
let segments = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d*)Z$/.exec(value);
if (segments)
return new Date(Date.UTC(+segments[1], +segments[2] - 1, +segments[3], +segments[4], +segments[5], +segments[6], +segments[7]));
}
return value;
};
window.__blink_invoker__ = function (methodName) {
let args = Array.prototype.slice.call(arguments);
args.splice(0, 1);
let callback = null;
if (args.length > 0)
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
let promise = new Promise(function(resolve, reject) {
__invokeProxy(JSON.stringify({
MethodName: methodName,
Params: args.map(JSON.stringify)
}),function(resultString) {
try{
let result = JSON.parse(resultString, __date_parser__);
if(result.Success){
resolve(result.ReturnValue);
}else{
reject(new Error(result.Message));
}
}catch (e) {
reject(e);
}
});
});
//判断最后一个参数
if(callback){
promise.then(function(returnValue){
callback.apply(null, [undefined].concat(returnValue));
}).catch(function(error){
callback.apply(null, [error]);
});
}else{
return promise;
}
};
window.__blink_runjs__ = function(path) {
try {
//获取path值
let pathSegments = path.split(".");
let value = window;
for (let pathSegment of pathSegments){
//值存在
if(value[pathSegment] !== undefined){
value = value[pathSegment];
}else
throw new Error("指定的值/函数不存在:" + path);
}
if (value === window) {
throw new Error("不允许获取window的值,请设置path");
}
if(typeof value === "function"){
//如果是一个函数,则调用他
let args = Array.prototype.slice.call(arguments);
args.splice(0, 1);
let result = value.apply(null, args.map(it => JSON.parse(it, __date_parser__)))
return JSON.stringify({
Success: true,
ReturnValue: JSON.stringify(result)
})
} else {
//如果是一个普通的值,将其格式化并传回
return JSON.stringify({
Success: true,
ReturnValue: JSON.stringify(value)
})
}
}catch (e) {
return JSON.stringify({
Success: false,
Message: e.message
})
}
}
`
func init() {
//注册时间的json编码器和解码器,方便和js做时间交互
jsoniter.RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if !iter.ReadNil() {
token := iter.Read()
if reflect2.TypeOf(token).String() == "string" {
timeValue, _ := time.Parse("2006-01-02T15:04:05.000Z07:00", token.(string))
*((*time.Time)(ptr)) = timeValue
}
}
})
jsoniter.RegisterTypeEncoderFunc("time.Time", func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
stream.WriteString((*(*time.Time)(ptr)).UTC().Format("2006-01-02T15:04:05.000Z07:00"))
}, func(pointer unsafe.Pointer) bool { return false })
}
//export goInvokeDispatcher
func goInvokeDispatcher(window C.wkeWebView, callback C.jsValue, invocationString *C.char) {
//获取调用的基本信息
//对invocationString的取值放在go func的外部,因为go func是异步的
//防止方法返回后MB回收这个字符串内存
invocationJson := jsoniter.Get([]byte(C.GoString(invocationString)))
methodName := invocationJson.Get("MethodName").ToString()
go func() (returnValue []interface{}, err error) {
//defer函数,用于处理返回值
defer func() {
result := struct {
Success bool
Message string
ReturnValue []interface{}
}{}
//检查是否存在未捕获的异常
if e := recover(); e != nil {
result.Success = false
result.Message = fmt.Sprint(e)
} else {
if err != nil {
//如果有错误
result.Success = false
result.Message = err.Error()
} else {
result.Success = true
result.ReturnValue = returnValue
}
}
//返回值
jsonData, _ := jsoniter.Marshal(result)
jobQueue <- func() {
view := getWebViewByWindow(window)
if view != nil {
if !view.IsDestroy {
C.callbackProxy(window, callback, C.CString(string(jsonData)))
}
}
}
}()
//拿到对应的view
view := getWebViewByWindow(window)
//查找对应的方法
if _, exist := view.jsFunc[methodName]; !exist {
return nil, fmt.Errorf("找不到方法%s", methodName)
}
method := view.jsFunc[methodName]
v := reflect.ValueOf(method)
t := v.Type()
//参数集合
var params = make([]reflect.Value, t.NumIn())
//挨个格式化参数
for index := 0; index < t.NumIn(); index++ {
paramType := t.In(index)
var paramValue reflect.Value
//判断参数是引用还是值,取到正确的类型
if paramType.Kind() == reflect.Ptr {
paramValue = reflect.New(paramType.Elem())
} else {
paramValue = reflect.New(paramType)
}
//拿到参数对应的值,并通过json格式化出来
paramJsonString := invocationJson.Get("Params", index).ToString()
jsoniter.UnmarshalFromString(paramJsonString, paramValue.Interface())
//判断参数是引用还是值
if paramType.Kind() == reflect.Ptr {
params[index] = paramValue
} else {
params[index] = paramValue.Elem()
}
}
//调用,并获取结果
invokeResult := v.Call(params)
//真实的返回值,不是reflect.Value
var realReturnValues []interface{}
if len(invokeResult) > 0 {
//判断最后返回值是不error
lastReturnValue := invokeResult[len(invokeResult)-1]
t := lastReturnValue.Type()
//Error方法存不存在
if m, exist := t.MethodByName("Error"); exist {
//返回值是不是一个
if m.Type.NumOut() == 1 {
//返回值的类型是不是String
if m.Type.Out(0).Kind() == reflect.String {
//是Error无疑了
//判断error是否为空
if lastReturnValue.IsNil() {
realReturnValues = make([]interface{}, len(invokeResult)-1)
for index, value := range invokeResult[0 : len(invokeResult)-1] {
realReturnValues[index] = value.Interface()
}
return realReturnValues, nil
} else {
//不为空
return nil, lastReturnValue.Interface().(error)
}
}
}
}
//不是Error,所有的返回值都需要处理
realReturnValues = make([]interface{}, len(invokeResult))
for index, value := range invokeResult {
realReturnValues[index] = value.Interface()
}
return realReturnValues, nil
} else {
//没有返回值
return nil, nil
}
}()
}
//export goGetInteropJS
func goGetInteropJS(window C.wkeWebView) *C.char {
view := getWebViewByWindow(window)
var buffer bytes.Buffer
buffer.WriteString(jsContent)
jsTemplate.Execute(&buffer, struct {
Data map[string]string
Func map[string]interface{}
}{
Data: view.jsData,
Func: view.jsFunc,
})
return C.CString(buffer.String())
}
//注入一个方法或者数据
func (view *WebView) Inject(key string, value interface{}) {
t := reflect.TypeOf(value)
if t.Kind() == reflect.Invalid || t.Kind() == reflect.Chan || t.Kind() == reflect.UnsafePointer {
logger.Println("注入错误,不支持类型:", t.Kind())
}
if t.Kind() == reflect.Func {
//如果是一个函数
if _, exist := view.jsFunc[key]; exist {
logger.Printf("%s方法已经存在,请检查重复\n", key)
}
view.jsFunc[key] = value
} else {
//如果是一个值
if _, exist := view.jsData[key]; exist {
logger.Printf("%s数据已经存在,请检查重复\n", key)
}
//将其变成json字符串
jsonData, err := jsoniter.Marshal(value)
if err != nil {
return
}
view.jsData[key] = template.JSEscapeString(string(jsonData))
}
}
//调用js中方法 or 获取js中的值
func (view *WebView) Invoke(path string, args ...interface{}) (returnValue jsoniter.Any, err error) {
if view.IsDestroy {
return nil, errors.New("WebView已经被销毁")
}
//所有的调用必须等待文档ready,且没有webview没有destroy
select {
case <-view.DocumentReady:
break
case <-view.Destroy:
//view已经destroy
return nil, errors.New("WebView已经被销毁")
}
defer func() {
//处理未捕获的异常
if e := recover(); e != nil {
returnValue = nil
err = errors.New(fmt.Sprint(e))
}
}()
done := make(chan string)
jobQueue <- func() {
if len(args) > 0 {
paramJsonStrings := make([]string, len(args))
for index, value := range args {
json, err := jsoniter.Marshal(value)
if err != nil {
paramJsonStrings[index] = "null"
continue
}
paramJsonStrings[index] = `"` + template.JSEscapeString(string(json)) + `"`
}
argsList := strings.Join(paramJsonStrings, ",")
result := C.runJSProxy(view.window, C.CString(fmt.Sprintf(`return __blink_runjs__('%s', %s);`, path, argsList)))
done <- C.GoString(result)
} else {
result := C.runJSProxy(view.window, C.CString(fmt.Sprintf(`return __blink_runjs__('%s');`, path)))
done <- C.GoString(result)
}
}
resultJson := jsoniter.Get([]byte(<-done))
if resultJson.Get("Success").ToBool() {
return jsoniter.Get([]byte(resultJson.Get("ReturnValue").ToString())), nil
} else {
return nil, errors.New(resultJson.Get("Message").ToString())
}
}
//执行js代码,返回值
func (view *WebView) RunJS(code string) (returnValue string, err error) {
if view.IsDestroy {
return "", errors.New("WebView已经被销毁")
}
//所有的调用必须等待文档ready,且没有webview没有destroy
select {
case <-view.DocumentReady:
break
case <-view.Destroy:
//view已经destroy
return "", errors.New("WebView已经被销毁")
default:
//允许未ready时执行
break
//return "", errors.New("WebView未加载画面")
}
defer func() {
//处理未捕获的异常
if e := recover(); e != nil {
returnValue = ""
err = errors.New(fmt.Sprint(e))
}
}()
done := make(chan string)
jobQueue <- func() {
result := C.runJSProxy(view.window, C.CString(code))
done <- C.GoString(result)
}
return <-done, nil
}