2019-06-20 19:21:48 +02:00
|
|
|
package input
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
2019-07-03 20:05:02 +02:00
|
|
|
"github.com/mafredri/cdp"
|
|
|
|
"github.com/mafredri/cdp/protocol/dom"
|
2019-07-17 00:17:42 +02:00
|
|
|
"github.com/mafredri/cdp/protocol/runtime"
|
2019-07-03 20:05:02 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/drivers"
|
2019-06-20 19:21:48 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
2019-07-17 00:17:42 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/drivers/cdp/templates"
|
2019-06-20 19:21:48 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
|
|
|
)
|
|
|
|
|
2019-09-01 22:09:35 +02:00
|
|
|
type (
|
|
|
|
TypeParams struct {
|
2019-09-05 18:17:22 +02:00
|
|
|
Text string
|
|
|
|
Clear bool
|
|
|
|
Delay time.Duration
|
2019-09-01 22:09:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Manager struct {
|
|
|
|
client *cdp.Client
|
|
|
|
exec *eval.ExecutionContext
|
|
|
|
keyboard *Keyboard
|
|
|
|
mouse *Mouse
|
|
|
|
}
|
|
|
|
)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
func NewManager(
|
|
|
|
client *cdp.Client,
|
|
|
|
exec *eval.ExecutionContext,
|
|
|
|
keyboard *Keyboard,
|
|
|
|
mouse *Mouse,
|
|
|
|
) *Manager {
|
|
|
|
return &Manager{
|
|
|
|
client,
|
|
|
|
exec,
|
|
|
|
keyboard,
|
|
|
|
mouse,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) Keyboard() *Keyboard {
|
|
|
|
return m.keyboard
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) Mouse() *Mouse {
|
|
|
|
return m.mouse
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
func (m *Manager) ScrollTop(ctx context.Context) error {
|
|
|
|
return m.exec.Eval(ctx, templates.ScrollTop())
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
func (m *Manager) ScrollBottom(ctx context.Context) error {
|
|
|
|
return m.exec.Eval(ctx, templates.ScrollBottom())
|
|
|
|
}
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
func (m *Manager) ScrollIntoView(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
|
|
|
return m.exec.EvalWithArguments(
|
|
|
|
ctx,
|
|
|
|
templates.ScrollIntoView(),
|
|
|
|
runtime.CallArgument{
|
|
|
|
ObjectID: &objectID,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, selector string) error {
|
|
|
|
return m.exec.Eval(ctx, templates.ScrollIntoViewBySelector(selector))
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) ScrollByXY(ctx context.Context, x, y float64) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
return m.exec.Eval(
|
|
|
|
ctx,
|
2019-09-05 18:17:22 +02:00
|
|
|
templates.Scroll(eval.ParamFloat(x), eval.ParamFloat(y)),
|
2019-07-17 00:17:42 +02:00
|
|
|
)
|
|
|
|
}
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
func (m *Manager) Focus(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
|
|
|
err := m.ScrollIntoView(ctx, objectID)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
return m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID))
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
err := m.ScrollIntoViewBySelector(ctx, selector)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2019-07-17 00:17:42 +02:00
|
|
|
return nil
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
return m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-07 18:59:37 +02:00
|
|
|
func (m *Manager) Blur(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
|
|
|
return m.exec.EvalWithArguments(ctx, templates.Blur(), runtime.CallArgument{
|
|
|
|
ObjectID: &objectID,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) BlurBySelector(ctx context.Context, parentObjectID runtime.RemoteObjectID, selector string) error {
|
|
|
|
return m.exec.EvalWithArguments(ctx, templates.BlurBySelector(selector), runtime.CallArgument{
|
|
|
|
ObjectID: &parentObjectID,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
func (m *Manager) MoveMouse(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
|
|
|
if err := m.ScrollIntoView(ctx, objectID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
q, err := GetClickablePointByObjectID(ctx, m.client, objectID)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
return m.mouse.Move(ctx, q.X, q.Y)
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
if err := m.ScrollIntoViewBySelector(ctx, selector); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
q, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.mouse.Move(ctx, q.X, q.Y)
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) MoveMouseByXY(ctx context.Context, x, y float64) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
if err := m.ScrollByXY(ctx, x, y); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
return m.mouse.Move(ctx, x, y)
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-07 07:59:32 +02:00
|
|
|
func (m *Manager) Click(ctx context.Context, objectID runtime.RemoteObjectID, count int) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
if err := m.ScrollIntoView(ctx, objectID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-07 07:59:32 +02:00
|
|
|
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
|
|
|
|
|
|
|
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
2019-07-17 00:17:42 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-07 07:59:32 +02:00
|
|
|
func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, count int) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
if err := m.ScrollIntoViewBySelector(ctx, selector); err != nil {
|
2019-06-20 19:21:48 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
2019-07-17 00:17:42 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-07 07:59:32 +02:00
|
|
|
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
|
|
|
|
|
|
|
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
2019-06-20 19:21:48 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-09-07 07:59:32 +02:00
|
|
|
func (m *Manager) ClickBySelectorAll(ctx context.Context, parentNodeID dom.NodeID, selector string, count int) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
if err := m.ScrollIntoViewBySelector(ctx, selector); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
found, err := m.client.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(parentNodeID, selector))
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
for _, nodeID := range found.NodeIDs {
|
2019-09-07 07:59:32 +02:00
|
|
|
beforeTypeDelay := time.Duration(core.NumberLowerBoundary(drivers.DefaultMouseDelay*10)) * time.Millisecond
|
2019-07-17 00:17:42 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
time.Sleep(beforeTypeDelay)
|
2019-07-17 00:17:42 +02:00
|
|
|
|
|
|
|
points, err := GetClickablePointByNodeID(ctx, m.client, nodeID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-07 07:59:32 +02:00
|
|
|
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
|
|
|
|
|
|
|
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
2019-07-17 00:17:42 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 22:09:35 +02:00
|
|
|
func (m *Manager) Type(ctx context.Context, objectID runtime.RemoteObjectID, params TypeParams) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
err := m.ScrollIntoView(ctx, objectID)
|
|
|
|
|
|
|
|
if err != nil {
|
2019-06-20 19:21:48 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID))
|
|
|
|
|
|
|
|
if err != nil {
|
2019-06-20 19:21:48 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-01 22:09:35 +02:00
|
|
|
if params.Clear {
|
|
|
|
points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := m.ClearByXY(ctx, points); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
d := core.NumberLowerBoundary(float64(params.Delay))
|
|
|
|
beforeTypeDelay := time.Duration(d)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
time.Sleep(beforeTypeDelay)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
return m.keyboard.Type(ctx, params.Text, params.Delay)
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, params TypeParams) error {
|
2019-07-17 00:17:42 +02:00
|
|
|
err := m.ScrollIntoViewBySelector(ctx, selector)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2019-07-17 00:17:42 +02:00
|
|
|
return err
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 22:09:35 +02:00
|
|
|
if params.Clear {
|
|
|
|
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := m.ClearByXY(ctx, points); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
d := core.NumberLowerBoundary(float64(params.Delay))
|
|
|
|
beforeTypeDelay := time.Duration(d)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
time.Sleep(beforeTypeDelay)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
return m.keyboard.Type(ctx, params.Text, params.Delay)
|
2019-09-01 22:09:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) Clear(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
|
|
|
err := m.ScrollIntoView(ctx, objectID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.ClearByXY(ctx, points)
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) ClearBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
2019-09-01 22:09:35 +02:00
|
|
|
err := m.ScrollIntoViewBySelector(ctx, selector)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
2019-09-01 22:09:35 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.ClearByXY(ctx, points)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) ClearByXY(ctx context.Context, points Quad) error {
|
2019-09-07 07:59:32 +02:00
|
|
|
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
|
|
|
err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, 2)
|
2019-09-01 22:09:35 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.keyboard.Press(ctx, "Backspace")
|
2019-07-17 00:17:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) Select(ctx context.Context, objectID runtime.RemoteObjectID, value *values.Array) (*values.Array, error) {
|
|
|
|
if err := m.Focus(ctx, objectID); err != nil {
|
|
|
|
return values.NewArray(0), err
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
val, err := m.exec.EvalWithArgumentsAndReturnValue(ctx, templates.Select(value.String()), runtime.CallArgument{
|
|
|
|
ObjectID: &objectID,
|
|
|
|
})
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
arr, ok := val.(*values.Array)
|
2019-06-20 19:21:48 +02:00
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
if !ok {
|
|
|
|
return values.NewArray(0), core.ErrUnexpected
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
return arr, nil
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
func (m *Manager) SelectBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, value *values.Array) (*values.Array, error) {
|
2019-07-17 00:17:42 +02:00
|
|
|
if err := m.FocusBySelector(ctx, parentNodeID, selector); err != nil {
|
|
|
|
return values.NewArray(0), err
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:17:22 +02:00
|
|
|
res, err := m.exec.EvalWithReturnValue(ctx, templates.SelectBySelector(selector, value.String()))
|
2019-06-20 19:21:48 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2019-07-17 00:17:42 +02:00
|
|
|
return values.NewArray(0), err
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
arr, ok := res.(*values.Array)
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
if !ok {
|
|
|
|
return values.NewArray(0), core.ErrUnexpected
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 00:17:42 +02:00
|
|
|
return arr, nil
|
2019-06-20 19:21:48 +02:00
|
|
|
}
|