mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-16 11:37:36 +02:00
218 lines
4.3 KiB
Go
218 lines
4.3 KiB
Go
|
package eval
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"github.com/mafredri/cdp"
|
||
|
"github.com/mafredri/cdp/protocol/page"
|
||
|
"github.com/mafredri/cdp/protocol/runtime"
|
||
|
|
||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||
|
)
|
||
|
|
||
|
const EmptyExecutionContextID = runtime.ExecutionContextID(-1)
|
||
|
|
||
|
type ExecutionContext struct {
|
||
|
client *cdp.Client
|
||
|
frame page.Frame
|
||
|
contextID runtime.ExecutionContextID
|
||
|
}
|
||
|
|
||
|
func NewExecutionContext(client *cdp.Client, frame page.Frame, contextID runtime.ExecutionContextID) *ExecutionContext {
|
||
|
ec := new(ExecutionContext)
|
||
|
ec.client = client
|
||
|
ec.frame = frame
|
||
|
ec.contextID = contextID
|
||
|
|
||
|
return ec
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) ID() runtime.ExecutionContextID {
|
||
|
return ec.contextID
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) Eval(ctx context.Context, exp string) error {
|
||
|
_, err := ec.eval(
|
||
|
ctx,
|
||
|
runtime.
|
||
|
NewEvaluateArgs(PrepareEval(exp)),
|
||
|
)
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) EvalWithReturn(ctx context.Context, exp string) (core.Value, error) {
|
||
|
return ec.eval(
|
||
|
ctx,
|
||
|
runtime.
|
||
|
NewEvaluateArgs(PrepareEval(exp)).
|
||
|
SetReturnByValue(true),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) EvalAsync(ctx context.Context, exp string) (core.Value, error) {
|
||
|
return ec.eval(
|
||
|
ctx,
|
||
|
runtime.
|
||
|
NewEvaluateArgs(PrepareEval(exp)).
|
||
|
SetReturnByValue(true).
|
||
|
SetAwaitPromise(true),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) eval(ctx context.Context, args *runtime.EvaluateArgs) (core.Value, error) {
|
||
|
if ec.contextID != EmptyExecutionContextID {
|
||
|
args.SetContextID(ec.contextID)
|
||
|
}
|
||
|
|
||
|
out, err := ec.client.Runtime.Evaluate(ctx, args)
|
||
|
|
||
|
if err != nil {
|
||
|
return values.None, err
|
||
|
}
|
||
|
|
||
|
if out.ExceptionDetails != nil {
|
||
|
ex := out.ExceptionDetails
|
||
|
|
||
|
return values.None, core.Error(
|
||
|
core.ErrUnexpected,
|
||
|
fmt.Sprintf("%s: %s", ex.Text, *ex.Exception.Description),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
if out.Result.Type != "undefined" && out.Result.Type != "null" {
|
||
|
return values.Unmarshal(out.Result.Value)
|
||
|
}
|
||
|
|
||
|
return Unmarshal(&out.Result)
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) CallMethod(
|
||
|
ctx context.Context,
|
||
|
objectID runtime.RemoteObjectID,
|
||
|
methodName string,
|
||
|
args []runtime.CallArgument,
|
||
|
) (*runtime.RemoteObject, error) {
|
||
|
callArgs := runtime.NewCallFunctionOnArgs(methodName).
|
||
|
SetObjectID(objectID).
|
||
|
SetArguments(args)
|
||
|
|
||
|
if ec.contextID != EmptyExecutionContextID {
|
||
|
callArgs.SetExecutionContextID(ec.contextID)
|
||
|
}
|
||
|
|
||
|
found, err := ec.client.Runtime.CallFunctionOn(
|
||
|
ctx,
|
||
|
callArgs,
|
||
|
)
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if found.ExceptionDetails != nil {
|
||
|
return nil, found.ExceptionDetails
|
||
|
}
|
||
|
|
||
|
if found.Result.ObjectID == nil {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
return &found.Result, nil
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) ReadProperty(
|
||
|
ctx context.Context,
|
||
|
objectID runtime.RemoteObjectID,
|
||
|
propName string,
|
||
|
) (core.Value, error) {
|
||
|
res, err := ec.client.Runtime.GetProperties(
|
||
|
ctx,
|
||
|
runtime.NewGetPropertiesArgs(objectID),
|
||
|
)
|
||
|
|
||
|
if err != nil {
|
||
|
return values.None, err
|
||
|
}
|
||
|
|
||
|
if res.ExceptionDetails != nil {
|
||
|
return values.None, res.ExceptionDetails
|
||
|
}
|
||
|
|
||
|
// all props
|
||
|
if propName == "" {
|
||
|
arr := values.NewArray(len(res.Result))
|
||
|
|
||
|
for _, prop := range res.Result {
|
||
|
val, err := Unmarshal(prop.Value)
|
||
|
|
||
|
if err != nil {
|
||
|
return values.None, err
|
||
|
}
|
||
|
|
||
|
arr.Push(val)
|
||
|
}
|
||
|
|
||
|
return arr, nil
|
||
|
}
|
||
|
|
||
|
for _, prop := range res.Result {
|
||
|
if prop.Name == propName {
|
||
|
return Unmarshal(prop.Value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return values.None, nil
|
||
|
}
|
||
|
|
||
|
func (ec *ExecutionContext) DispatchEvent(
|
||
|
ctx context.Context,
|
||
|
objectID runtime.RemoteObjectID,
|
||
|
eventName string,
|
||
|
) (values.Boolean, error) {
|
||
|
args := runtime.NewEvaluateArgs(PrepareEval(fmt.Sprintf(`
|
||
|
return new window.MouseEvent('%s', { bubbles: true, cancelable: true })
|
||
|
`, eventName)))
|
||
|
|
||
|
if ec.contextID != EmptyExecutionContextID {
|
||
|
args.SetContextID(ec.contextID)
|
||
|
}
|
||
|
|
||
|
evt, err := ec.client.Runtime.Evaluate(ctx, args)
|
||
|
|
||
|
if err != nil {
|
||
|
return values.False, nil
|
||
|
}
|
||
|
|
||
|
if evt.ExceptionDetails != nil {
|
||
|
return values.False, evt.ExceptionDetails
|
||
|
}
|
||
|
|
||
|
if evt.Result.ObjectID == nil {
|
||
|
return values.False, nil
|
||
|
}
|
||
|
|
||
|
evtID := evt.Result.ObjectID
|
||
|
|
||
|
// release the event object
|
||
|
defer ec.client.Runtime.ReleaseObject(ctx, runtime.NewReleaseObjectArgs(*evtID))
|
||
|
|
||
|
_, err = ec.CallMethod(
|
||
|
ctx,
|
||
|
objectID,
|
||
|
"dispatchEvent",
|
||
|
[]runtime.CallArgument{
|
||
|
{
|
||
|
ObjectID: evt.Result.ObjectID,
|
||
|
},
|
||
|
},
|
||
|
)
|
||
|
|
||
|
if err != nil {
|
||
|
return values.False, err
|
||
|
}
|
||
|
|
||
|
return values.True, nil
|
||
|
}
|