2017-01-24 15:09:23 +00:00
|
|
|
package chromedp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2017-02-28 01:35:00 +00:00
|
|
|
"fmt"
|
2017-01-24 15:09:23 +00:00
|
|
|
"time"
|
|
|
|
|
2017-12-27 02:30:28 +00:00
|
|
|
"github.com/chromedp/cdproto/cdp"
|
|
|
|
"github.com/chromedp/cdproto/dom"
|
|
|
|
"github.com/chromedp/cdproto/input"
|
2018-05-18 22:03:47 +00:00
|
|
|
|
2017-12-27 02:30:28 +00:00
|
|
|
"github.com/chromedp/chromedp/kb"
|
2017-01-24 15:09:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// MouseAction is a mouse action.
|
|
|
|
func MouseAction(typ input.MouseType, x, y int64, opts ...MouseOption) Action {
|
2017-07-13 00:46:32 +00:00
|
|
|
me := input.DispatchMouseEvent(typ, float64(x), float64(y))
|
2017-02-08 14:35:12 +00:00
|
|
|
|
|
|
|
// apply opts
|
2017-01-24 15:09:23 +00:00
|
|
|
for _, o := range opts {
|
2017-02-08 14:35:12 +00:00
|
|
|
me = o(me)
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 14:35:12 +00:00
|
|
|
return me
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 14:35:12 +00:00
|
|
|
// MouseClickXY sends a left mouse button click (ie, mousePressed and
|
|
|
|
// mouseReleased event) at the X, Y location.
|
2017-01-24 15:09:23 +00:00
|
|
|
func MouseClickXY(x, y int64, opts ...MouseOption) Action {
|
2017-12-27 02:30:28 +00:00
|
|
|
return ActionFunc(func(ctxt context.Context, h cdp.Executor) error {
|
2017-02-08 14:35:12 +00:00
|
|
|
me := &input.DispatchMouseEventParams{
|
|
|
|
Type: input.MousePressed,
|
2017-07-13 00:46:32 +00:00
|
|
|
X: float64(x),
|
|
|
|
Y: float64(y),
|
2017-02-08 14:35:12 +00:00
|
|
|
Button: input.ButtonLeft,
|
|
|
|
ClickCount: 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply opts
|
|
|
|
for _, o := range opts {
|
|
|
|
me = o(me)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := me.Do(ctxt, h)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
me.Type = input.MouseReleased
|
|
|
|
return me.Do(ctxt, h)
|
|
|
|
})
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 16:01:35 +00:00
|
|
|
// MouseClickNode dispatches a mouse left button click event at the center of a
|
|
|
|
// specified node.
|
2017-02-28 01:35:00 +00:00
|
|
|
//
|
|
|
|
// Note that the window will be scrolled if the node is not within the window's
|
|
|
|
// viewport.
|
2017-02-08 16:01:35 +00:00
|
|
|
func MouseClickNode(n *cdp.Node, opts ...MouseOption) Action {
|
2017-12-27 02:30:28 +00:00
|
|
|
return ActionFunc(func(ctxt context.Context, h cdp.Executor) error {
|
2017-02-28 01:35:00 +00:00
|
|
|
var pos []int
|
2019-03-05 13:14:50 +00:00
|
|
|
err := EvaluateAsDevTools(fmt.Sprintf(scrollIntoViewJS, n.FullXPath()), &pos).Do(ctxt, h)
|
2017-02-08 14:35:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-02-25 07:34:53 +00:00
|
|
|
}
|
2017-02-08 14:35:12 +00:00
|
|
|
|
2017-07-14 03:29:52 +00:00
|
|
|
box, err := dom.GetBoxModel().WithNodeID(n.NodeID).Do(ctxt, h)
|
2017-01-24 15:09:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c := len(box.Content)
|
2017-02-08 14:35:12 +00:00
|
|
|
if c%2 != 0 || c < 1 {
|
2017-01-24 15:09:23 +00:00
|
|
|
return ErrInvalidDimensions
|
|
|
|
}
|
|
|
|
|
|
|
|
var x, y int64
|
|
|
|
for i := 0; i < c; i += 2 {
|
|
|
|
x += int64(box.Content[i])
|
|
|
|
y += int64(box.Content[i+1])
|
|
|
|
}
|
2017-02-08 14:35:12 +00:00
|
|
|
x /= int64(c / 2)
|
|
|
|
y /= int64(c / 2)
|
2017-01-24 15:09:23 +00:00
|
|
|
|
2017-02-08 14:35:12 +00:00
|
|
|
return MouseClickXY(x, y, opts...).Do(ctxt, h)
|
2017-01-24 15:09:23 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// MouseOption is a mouse action option.
|
|
|
|
type MouseOption func(*input.DispatchMouseEventParams) *input.DispatchMouseEventParams
|
|
|
|
|
2017-02-08 14:35:12 +00:00
|
|
|
// Button is a mouse action option to set the button to click from a string.
|
|
|
|
func Button(btn string) MouseOption {
|
|
|
|
return ButtonType(input.ButtonType(btn))
|
|
|
|
}
|
|
|
|
|
|
|
|
// ButtonType is a mouse action option to set the button to click.
|
|
|
|
func ButtonType(button input.ButtonType) MouseOption {
|
2017-01-24 15:09:23 +00:00
|
|
|
return func(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
|
|
|
return p.WithButton(button)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 14:35:12 +00:00
|
|
|
// ButtonLeft is a mouse action option to set the button clicked as the left
|
|
|
|
// mouse button.
|
|
|
|
func ButtonLeft(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
|
|
|
return p.WithButton(input.ButtonLeft)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ButtonMiddle is a mouse action option to set the button clicked as the middle
|
|
|
|
// mouse button.
|
|
|
|
func ButtonMiddle(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
|
|
|
return p.WithButton(input.ButtonMiddle)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ButtonRight is a mouse action option to set the button clicked as the right
|
|
|
|
// mouse button.
|
|
|
|
func ButtonRight(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
|
|
|
return p.WithButton(input.ButtonRight)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ButtonNone is a mouse action option to set the button clicked as none (used
|
|
|
|
// for mouse movements).
|
|
|
|
func ButtonNone(p *input.DispatchMouseEventParams) *input.DispatchMouseEventParams {
|
|
|
|
return p.WithButton(input.ButtonNone)
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 14:35:12 +00:00
|
|
|
// ButtonModifiers is a mouse action option to add additional input modifiers
|
|
|
|
// for a button click.
|
2017-01-24 15:09:23 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-29 03:37:56 +00:00
|
|
|
// KeyAction will synthesize a keyDown, char, and keyUp event for each rune
|
2017-02-08 14:35:12 +00:00
|
|
|
// contained in keys along with any supplied key options.
|
2017-01-29 03:37:56 +00:00
|
|
|
//
|
2017-07-01 03:14:42 +00:00
|
|
|
// Only well-known, "printable" characters will have char events synthesized.
|
|
|
|
//
|
|
|
|
// Please see the chromedp/kb package for implementation details and the list
|
|
|
|
// of well-known keys.
|
2017-01-29 03:37:56 +00:00
|
|
|
func KeyAction(keys string, opts ...KeyOption) Action {
|
2017-12-27 02:30:28 +00:00
|
|
|
return ActionFunc(func(ctxt context.Context, h cdp.Executor) error {
|
2017-01-29 03:37:56 +00:00
|
|
|
for _, r := range keys {
|
|
|
|
for _, k := range kb.Encode(r) {
|
2019-03-05 13:14:50 +00:00
|
|
|
if err := k.Do(ctxt, h); err != nil {
|
2017-01-29 03:37:56 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 03:37:56 +00:00
|
|
|
// TODO: move to context
|
|
|
|
time.Sleep(5 * time.Millisecond)
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 03:37:56 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyActionNode dispatches a key event on a node.
|
|
|
|
func KeyActionNode(n *cdp.Node, keys string, opts ...KeyOption) Action {
|
2017-12-27 02:30:28 +00:00
|
|
|
return ActionFunc(func(ctxt context.Context, h cdp.Executor) error {
|
2017-07-14 03:29:52 +00:00
|
|
|
err := dom.Focus().WithNodeID(n.NodeID).Do(ctxt, h)
|
2017-01-24 15:09:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-01-29 03:37:56 +00:00
|
|
|
return KeyAction(keys, opts...).Do(ctxt, h)
|
|
|
|
})
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|