1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-07-03 00:46:51 +02:00
Files
ferret/pkg/drivers/cdp/eval/runtime.go

171 lines
3.6 KiB
Go
Raw Normal View History

package eval
import (
"context"
"github.com/mafredri/cdp"
"github.com/mafredri/cdp/protocol/page"
"github.com/mafredri/cdp/protocol/runtime"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/logging"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
const EmptyExecutionContextID = runtime.ExecutionContextID(-1)
type Runtime struct {
logger zerolog.Logger
client *cdp.Client
frame page.Frame
contextID runtime.ExecutionContextID
resolver *Resolver
}
func Create(
ctx context.Context,
logger zerolog.Logger,
client *cdp.Client,
frameID page.FrameID,
) (*Runtime, error) {
world, err := client.Page.CreateIsolatedWorld(ctx, page.NewCreateIsolatedWorldArgs(frameID))
if err != nil {
return nil, err
}
return New(logger, client, world.ExecutionContextID), nil
}
func New(
logger zerolog.Logger,
client *cdp.Client,
contextID runtime.ExecutionContextID,
) *Runtime {
rt := new(Runtime)
rt.logger = logging.WithName(logger.With(), "js-eval").Logger()
rt.client = client
rt.contextID = contextID
rt.resolver = NewResolver(client.Runtime)
return rt
}
func (rt *Runtime) SetLoader(loader ValueLoader) *Runtime {
rt.resolver.SetLoader(loader)
return rt
}
func (rt *Runtime) ContextID() runtime.ExecutionContextID {
return rt.contextID
}
func (rt *Runtime) Eval(ctx context.Context, fn *Function) error {
_, err := rt.call(ctx, fn)
return err
}
func (rt *Runtime) EvalRef(ctx context.Context, fn *Function) (runtime.RemoteObject, error) {
out, err := rt.call(ctx, fn.returnRef())
if err != nil {
return runtime.RemoteObject{}, err
}
return out, nil
}
func (rt *Runtime) EvalValue(ctx context.Context, fn *Function) (core.Value, error) {
out, err := rt.call(ctx, fn.returnValue())
if err != nil {
return values.None, err
}
return rt.resolver.ToValue(ctx, out)
}
func (rt *Runtime) EvalElement(ctx context.Context, fn *Function) (drivers.HTMLElement, error) {
ref, err := rt.EvalRef(ctx, fn)
if err != nil {
return nil, err
}
return rt.resolver.ToElement(ctx, ref)
}
func (rt *Runtime) EvalElements(ctx context.Context, fn *Function) (*values.Array, error) {
ref, err := rt.EvalRef(ctx, fn)
if err != nil {
return nil, err
}
val, err := rt.resolver.ToValue(ctx, ref)
if err != nil {
return nil, err
}
arr, ok := val.(*values.Array)
if ok {
return arr, nil
}
return values.NewArrayWith(val), nil
}
func (rt *Runtime) call(ctx context.Context, fn *Function) (runtime.RemoteObject, error) {
log := rt.logger.With().
Str("expression", fn.String()).
Str("returns", fn.returnType.String()).
Bool("is-async", fn.async).
Str("owner", string(fn.ownerID)).
Array("arguments", fn.args).
Logger()
log.Trace().Msg("executing expression...")
repl, err := rt.client.Runtime.CallFunctionOn(ctx, fn.build(rt.contextID))
if err != nil {
log.Trace().Err(err).Msg("failed executing expression")
return runtime.RemoteObject{}, errors.Wrap(err, "runtime call")
}
if err := parseRuntimeException(repl.ExceptionDetails); err != nil {
log.Trace().Err(err).Msg("expression has failed with runtime exception")
return runtime.RemoteObject{}, err
}
var className string
if repl.Result.ClassName != nil {
className = *repl.Result.ClassName
}
var subtype string
if repl.Result.Subtype != nil {
subtype = *repl.Result.Subtype
}
log.Trace().
Str("return-type", repl.Result.Type).
Str("return-sub-type", subtype).
Str("return-class-name", className).
Str("return-value", string(repl.Result.Value)).
Msg("succeeded executing expression")
return repl.Result, nil
}