mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-04 15:49:49 +00:00
290 lines
8.0 KiB
Go
290 lines
8.0 KiB
Go
package download
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/GopeedLab/gopeed/pkg/download/engine"
|
|
enginewebview "github.com/GopeedLab/gopeed/pkg/download/engine/webview"
|
|
"github.com/dop251/goja"
|
|
)
|
|
|
|
func injectGopeed(vm *goja.Runtime, gopeed *Instance) error {
|
|
gopeedObject := vm.NewObject()
|
|
if gopeed == nil {
|
|
return vm.Set("gopeed", gopeedObject)
|
|
}
|
|
if err := gopeedObject.Set("events", newJSEventsRuntime(vm, gopeed.Events)); err != nil {
|
|
return err
|
|
}
|
|
if err := gopeedObject.Set("info", gopeed.Info); err != nil {
|
|
return err
|
|
}
|
|
if err := gopeedObject.Set("logger", gopeed.Logger); err != nil {
|
|
return err
|
|
}
|
|
if err := gopeedObject.Set("settings", gopeed.Settings); err != nil {
|
|
return err
|
|
}
|
|
if err := gopeedObject.Set("storage", gopeed.Storage); err != nil {
|
|
return err
|
|
}
|
|
runtimeObject := vm.NewObject()
|
|
if gopeed.Runtime != nil {
|
|
if err := runtimeObject.Set("blob", newJSBlobRuntime(vm)); err != nil {
|
|
return err
|
|
}
|
|
if gopeed.Runtime.WebView != nil {
|
|
if err := runtimeObject.Set("webview", newJSWebViewRuntime(vm, gopeed.Runtime.WebView)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if err := gopeedObject.Set("runtime", runtimeObject); err != nil {
|
|
return err
|
|
}
|
|
return vm.Set("gopeed", gopeedObject)
|
|
}
|
|
|
|
func newJSBlobRuntime(vm *goja.Runtime) *goja.Object {
|
|
obj := vm.NewObject()
|
|
_ = obj.Set("createObjectURL", func(call goja.FunctionCall) goja.Value {
|
|
fn, ok := goja.AssertFunction(vm.Get("__gopeed_blob_create_object_url"))
|
|
if !ok {
|
|
panic(vm.ToValue(fmt.Errorf("blob runtime is not available")))
|
|
}
|
|
value, err := fn(goja.Undefined(), call.Argument(0), call.Argument(1))
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return value
|
|
})
|
|
_ = obj.Set("revokeObjectURL", func(call goja.FunctionCall) goja.Value {
|
|
fn, ok := goja.AssertFunction(vm.Get("__gopeed_blob_revoke_object_url"))
|
|
if !ok {
|
|
panic(vm.ToValue(fmt.Errorf("blob runtime is not available")))
|
|
}
|
|
if _, err := fn(goja.Undefined(), call.Argument(0)); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
return obj
|
|
}
|
|
|
|
func newJSEventsRuntime(vm *goja.Runtime, events InstanceEvents) *goja.Object {
|
|
obj := vm.NewObject()
|
|
register := func(event ActivationEvent, call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) == 0 {
|
|
panic(vm.ToValue(fmt.Errorf("missing handler")))
|
|
}
|
|
fnValue := call.Argument(0)
|
|
exported, ok := fnValue.Export().(func(goja.FunctionCall) goja.Value)
|
|
if !ok {
|
|
panic(vm.ToValue(fmt.Errorf("handler must be a function")))
|
|
}
|
|
events.register(event, engine.JSFunction(exported))
|
|
return goja.Undefined()
|
|
}
|
|
_ = obj.Set("onResolve", func(call goja.FunctionCall) goja.Value {
|
|
return register(EventOnResolve, call)
|
|
})
|
|
_ = obj.Set("onStart", func(call goja.FunctionCall) goja.Value {
|
|
return register(EventOnStart, call)
|
|
})
|
|
_ = obj.Set("onError", func(call goja.FunctionCall) goja.Value {
|
|
return register(EventOnError, call)
|
|
})
|
|
_ = obj.Set("onDone", func(call goja.FunctionCall) goja.Value {
|
|
return register(EventOnDone, call)
|
|
})
|
|
return obj
|
|
}
|
|
|
|
func newJSWebViewRuntime(vm *goja.Runtime, runtime *enginewebview.Runtime) *goja.Object {
|
|
obj := vm.NewObject()
|
|
_ = obj.Set("isAvailable", func(goja.FunctionCall) goja.Value {
|
|
return vm.ToValue(runtime.IsAvailable())
|
|
})
|
|
_ = obj.Set("open", func(call goja.FunctionCall) goja.Value {
|
|
page, err := runtime.Open(optionalMap(call.Argument(0)))
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return newJSWebViewPage(vm, page)
|
|
})
|
|
return obj
|
|
}
|
|
|
|
func newJSWebViewPage(vm *goja.Runtime, page *enginewebview.PageHandle) *goja.Object {
|
|
obj := vm.NewObject()
|
|
_ = obj.Set("addInitScript", func(call goja.FunctionCall) goja.Value {
|
|
script, err := requireStringArg(call, 0, "script")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
if err := page.AddInitScript(script); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("goto", func(call goja.FunctionCall) goja.Value {
|
|
url, err := requireStringArg(call, 0, "url")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
if err := page.Goto(
|
|
url,
|
|
optionalMap(call.Argument(1)),
|
|
); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("execute", func(call goja.FunctionCall) goja.Value {
|
|
expression, err := enginewebview.NormalizeExecutableValue(call.Argument(0))
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
result, err := page.Execute(expression, exportArgs(call.Arguments[1:])...)
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return vm.ToValue(result)
|
|
})
|
|
_ = obj.Set("focus", func(call goja.FunctionCall) goja.Value {
|
|
selector, err := requireStringArg(call, 0, "selector")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
if err := page.Focus(selector); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("click", func(call goja.FunctionCall) goja.Value {
|
|
selector, err := requireStringArg(call, 0, "selector")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
if err := page.Click(selector, optionalMap(call.Argument(1))); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("type", func(call goja.FunctionCall) goja.Value {
|
|
selector, err := requireStringArg(call, 0, "selector")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
text, err := requireStringArg(call, 1, "text")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
if err := page.Type(selector, text, optionalMap(call.Argument(2))); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("waitForSelector", func(call goja.FunctionCall) goja.Value {
|
|
selector, err := requireStringArg(call, 0, "selector")
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
result, err := page.WaitForSelector(
|
|
selector,
|
|
optionalMap(call.Argument(1)),
|
|
)
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return vm.ToValue(result)
|
|
})
|
|
_ = obj.Set("waitForFunction", func(call goja.FunctionCall) goja.Value {
|
|
expression, err := enginewebview.NormalizeExecutableValue(call.Argument(0))
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
args := exportArgs(call.Arguments[1:])
|
|
result, err := page.WaitForFunction(expression, args...)
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return vm.ToValue(result)
|
|
})
|
|
_ = obj.Set("getCookies", func(goja.FunctionCall) goja.Value {
|
|
result, err := page.GetCookies()
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return vm.ToValue(result)
|
|
})
|
|
_ = obj.Set("setCookie", func(call goja.FunctionCall) goja.Value {
|
|
if err := page.SetCookie(optionalMap(call.Argument(0))); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("deleteCookie", func(call goja.FunctionCall) goja.Value {
|
|
if err := page.DeleteCookie(optionalMap(call.Argument(0))); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("clearCookies", func(goja.FunctionCall) goja.Value {
|
|
if err := page.ClearCookies(); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
_ = obj.Set("url", func(goja.FunctionCall) goja.Value {
|
|
result, err := page.URL()
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return vm.ToValue(result)
|
|
})
|
|
_ = obj.Set("content", func(goja.FunctionCall) goja.Value {
|
|
result, err := page.Content()
|
|
if err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return vm.ToValue(result)
|
|
})
|
|
_ = obj.Set("close", func(goja.FunctionCall) goja.Value {
|
|
if err := page.Close(); err != nil {
|
|
panic(vm.ToValue(err))
|
|
}
|
|
return goja.Undefined()
|
|
})
|
|
return obj
|
|
}
|
|
|
|
func optionalMap(value goja.Value) map[string]any {
|
|
if value == nil || goja.IsUndefined(value) || goja.IsNull(value) {
|
|
return nil
|
|
}
|
|
if exported, ok := value.Export().(map[string]any); ok {
|
|
return exported
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func exportArgs(values []goja.Value) []any {
|
|
if len(values) == 0 {
|
|
return nil
|
|
}
|
|
args := make([]any, 0, len(values))
|
|
for _, value := range values {
|
|
args = append(args, value.Export())
|
|
}
|
|
return args
|
|
}
|
|
|
|
func requireStringArg(call goja.FunctionCall, index int, name string) (string, error) {
|
|
value := call.Argument(index)
|
|
if value == nil || goja.IsUndefined(value) || goja.IsNull(value) {
|
|
return "", fmt.Errorf(`missing or invalid "%s"`, name)
|
|
}
|
|
return value.String(), nil
|
|
}
|