172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
|
package chromedp
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"time"
|
||
|
|
||
|
. "github.com/knq/chromedp/cdp"
|
||
|
"github.com/knq/chromedp/cdp/dom"
|
||
|
"github.com/knq/chromedp/cdp/input"
|
||
|
)
|
||
|
|
||
|
// Error types.
|
||
|
var (
|
||
|
ErrInvalidDimensions = errors.New("invalid box dimensions")
|
||
|
)
|
||
|
|
||
|
// MouseAction is a mouse action.
|
||
|
func MouseAction(typ input.MouseType, x, y int64, opts ...MouseOption) Action {
|
||
|
f := input.DispatchMouseEvent(typ, x, y)
|
||
|
for _, o := range opts {
|
||
|
f = o(f)
|
||
|
}
|
||
|
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
// MouseClickXY sends a left mouse button click at the X, Y location.
|
||
|
func MouseClickXY(x, y int64, opts ...MouseOption) Action {
|
||
|
return Tasks{
|
||
|
MouseAction(input.MousePressed, x, y, append(opts, Button(input.ButtonLeft), ClickCount(1))...),
|
||
|
MouseAction(input.MouseReleased, x, y, append(opts, Button(input.ButtonLeft), ClickCount(1))...),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MouseActionNode dispatches a mouse event at the center of a specified node.
|
||
|
func MouseActionNode(n *Node, opts ...MouseOption) Action {
|
||
|
return ActionFunc(func(ctxt context.Context, h FrameHandler) error {
|
||
|
box, err := dom.GetBoxModel(n.NodeID).Do(ctxt, h)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
c := len(box.Content)
|
||
|
if c%2 != 0 {
|
||
|
return ErrInvalidDimensions
|
||
|
}
|
||
|
|
||
|
var x, y int64
|
||
|
for i := 0; i < c; i += 2 {
|
||
|
x += int64(box.Content[i])
|
||
|
y += int64(box.Content[i+1])
|
||
|
}
|
||
|
|
||
|
return MouseClickXY(x/int64(c/2), y/int64(c/2), opts...).Do(ctxt, h)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// MouseOption is a mouse action option.
|
||
|
type MouseOption func(*input.DispatchMouseEventParams) *input.DispatchMouseEventParams
|
||
|
|
||
|
// Button is a mouse action option to set the button to click.
|
||
|
func Button(button input.ButtonType) MouseOption {
|
||
|
return func(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
||
|
return p.WithButton(button)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ButtonString is a mouse action option to set the button to click as a
|
||
|
// string.
|
||
|
func ButtonString(btn string) MouseOption {
|
||
|
return Button(input.ButtonType(btn))
|
||
|
}
|
||
|
|
||
|
// ButtonModifiers is a mouse action option to add additional modifiers for the
|
||
|
// button.
|
||
|
func ButtonModifiers(modifiers ...input.Modifier) MouseOption {
|
||
|
return func(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
||
|
for _, m := range modifiers {
|
||
|
p.Modifiers |= m
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ClickCount is a mouse action option to set the click count.
|
||
|
func ClickCount(n int) MouseOption {
|
||
|
return func(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
||
|
return p.WithClickCount(int64(n))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// KeyAction contains information about a key action.
|
||
|
type KeyAction struct {
|
||
|
v string
|
||
|
opts []KeyOption
|
||
|
}
|
||
|
|
||
|
var keyNames = map[rune]string{
|
||
|
'\b': "Backspace",
|
||
|
'\t': "Tab",
|
||
|
'\r': "Enter",
|
||
|
'\n': "Enter",
|
||
|
}
|
||
|
|
||
|
// Do satisfies Action interface.
|
||
|
func (ka *KeyAction) Do(ctxt context.Context, h FrameHandler) error {
|
||
|
var err error
|
||
|
|
||
|
// apply opts
|
||
|
sysP := input.DispatchKeyEvent(input.KeyRawDown)
|
||
|
keyP := input.DispatchKeyEvent(input.KeyChar)
|
||
|
for _, o := range ka.opts {
|
||
|
sysP = o(sysP)
|
||
|
keyP = o(keyP)
|
||
|
}
|
||
|
|
||
|
for _, r := range ka.v {
|
||
|
s := string(r)
|
||
|
if n, ok := keyNames[r]; ok {
|
||
|
kc := int64(r)
|
||
|
if r == '\n' {
|
||
|
s = string('\r')
|
||
|
kc = int64('\r')
|
||
|
}
|
||
|
|
||
|
err = sysP.WithKey(n).
|
||
|
WithNativeVirtualKeyCode(kc).
|
||
|
WithWindowsVirtualKeyCode(kc).
|
||
|
WithUnmodifiedText(s).
|
||
|
WithText(s).
|
||
|
Do(ctxt, h)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = keyP.WithText(s).Do(ctxt, h)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// FIXME
|
||
|
time.Sleep(5 * time.Millisecond)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// KeyActionNode dispatches a key event on a node.
|
||
|
func KeyActionNode(n *Node, v string, opts ...KeyOption) Action {
|
||
|
return Tasks{
|
||
|
dom.Focus(n.NodeID),
|
||
|
MouseActionNode(n),
|
||
|
&KeyAction{v, opts},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// KeyOption is a key action option.
|
||
|
type KeyOption func(*input.DispatchKeyEventParams) *input.DispatchKeyEventParams
|
||
|
|
||
|
// KeyModifiers is a key action option to add additional modifiers on the key
|
||
|
// press.
|
||
|
func KeyModifiers(modifiers ...input.Modifier) KeyOption {
|
||
|
return func(p *input.DispatchKeyEventParams) *input.DispatchKeyEventParams {
|
||
|
for _, m := range modifiers {
|
||
|
p.Modifiers |= m
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
}
|