2021-09-02 11:09:48 -04:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
|
|
|
"github.com/mafredri/cdp"
|
|
|
|
"github.com/mafredri/cdp/protocol/page"
|
|
|
|
"github.com/mafredri/cdp/protocol/runtime"
|
|
|
|
"github.com/pkg/errors"
|
2021-09-16 21:40:20 -04:00
|
|
|
"github.com/rs/zerolog"
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
"github.com/MontFerret/ferret/pkg/drivers"
|
2021-09-02 11:09:48 -04:00
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
2021-09-16 21:40:20 -04:00
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/logging"
|
2021-09-02 11:09:48 -04:00
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
|
|
|
)
|
|
|
|
|
|
|
|
const EmptyExecutionContextID = runtime.ExecutionContextID(-1)
|
|
|
|
|
|
|
|
type Runtime struct {
|
2021-09-07 16:33:30 -04:00
|
|
|
logger zerolog.Logger
|
2021-09-02 11:09:48 -04:00
|
|
|
client *cdp.Client
|
|
|
|
frame page.Frame
|
|
|
|
contextID runtime.ExecutionContextID
|
2021-09-16 21:40:20 -04:00
|
|
|
resolver *Resolver
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
func Create(
|
|
|
|
ctx context.Context,
|
|
|
|
logger zerolog.Logger,
|
|
|
|
client *cdp.Client,
|
|
|
|
frameID page.FrameID,
|
|
|
|
) (*Runtime, error) {
|
2021-09-02 11:09:48 -04:00
|
|
|
world, err := client.Page.CreateIsolatedWorld(ctx, page.NewCreateIsolatedWorldArgs(frameID))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return New(logger, client, world.ExecutionContextID), nil
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
func New(
|
|
|
|
logger zerolog.Logger,
|
|
|
|
client *cdp.Client,
|
|
|
|
contextID runtime.ExecutionContextID,
|
|
|
|
) *Runtime {
|
2021-09-07 16:33:30 -04:00
|
|
|
rt := new(Runtime)
|
|
|
|
rt.logger = logging.WithName(logger.With(), "js-eval").Logger()
|
|
|
|
rt.client = client
|
|
|
|
rt.contextID = contextID
|
2021-09-16 21:40:20 -04:00
|
|
|
rt.resolver = NewResolver(client.Runtime)
|
|
|
|
|
|
|
|
return rt
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rt *Runtime) SetLoader(loader ValueLoader) *Runtime {
|
|
|
|
rt.resolver.SetLoader(loader)
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
return rt
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
func (rt *Runtime) ContextID() runtime.ExecutionContextID {
|
|
|
|
return rt.contextID
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
func (rt *Runtime) Eval(ctx context.Context, fn *Function) error {
|
|
|
|
_, err := rt.call(ctx, fn)
|
2021-09-02 11:09:48 -04:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
func (rt *Runtime) EvalRef(ctx context.Context, fn *Function) (runtime.RemoteObject, error) {
|
|
|
|
out, err := rt.call(ctx, fn.returnRef())
|
2021-09-02 11:09:48 -04:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return runtime.RemoteObject{}, err
|
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return out, nil
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
func (rt *Runtime) EvalValue(ctx context.Context, fn *Function) (core.Value, error) {
|
|
|
|
out, err := rt.call(ctx, fn.returnValue())
|
2021-09-02 11:09:48 -04:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return values.None, err
|
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
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
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return rt.resolver.ToElement(ctx, ref)
|
|
|
|
}
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
func (rt *Runtime) EvalElements(ctx context.Context, fn *Function) (*values.Array, error) {
|
|
|
|
ref, err := rt.EvalRef(ctx, fn)
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
val, err := rt.resolver.ToValue(ctx, ref)
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
arr, ok := val.(*values.Array)
|
2021-09-07 16:33:30 -04:00
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
if ok {
|
|
|
|
return arr, nil
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return values.NewArrayWith(val), nil
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
func (rt *Runtime) call(ctx context.Context, fn *Function) (runtime.RemoteObject, error) {
|
2021-09-07 16:33:30 -04:00
|
|
|
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))
|
2021-09-02 11:09:48 -04:00
|
|
|
|
|
|
|
if err != nil {
|
2021-09-07 16:33:30 -04:00
|
|
|
log.Trace().Err(err).Msg("failed executing expression")
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return runtime.RemoteObject{}, errors.Wrap(err, "runtime call")
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := parseRuntimeException(repl.ExceptionDetails); err != nil {
|
2021-09-07 16:33:30 -04:00
|
|
|
log.Trace().Err(err).Msg("expression has failed with runtime exception")
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return runtime.RemoteObject{}, err
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
var className string
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
if repl.Result.ClassName != nil {
|
|
|
|
className = *repl.Result.ClassName
|
|
|
|
}
|
|
|
|
|
|
|
|
var subtype string
|
2021-09-02 11:09:48 -04:00
|
|
|
|
2021-09-07 16:33:30 -04:00
|
|
|
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")
|
|
|
|
|
2021-09-16 21:40:20 -04:00
|
|
|
return repl.Result, nil
|
2021-09-02 11:09:48 -04:00
|
|
|
}
|