4145d8367d
- Adding chromedp-proxy command to help record chrome sessions - Changing the headless writeup to point to the knq chrome-headless docker image - Fixing issues with SendKeys action (should correctly work for all well known keys) - Updated to latest Chrome protocol.json and re-generated API - Other minor fixes
145 lines
3.8 KiB
Go
145 lines
3.8 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"
|
|
"github.com/knq/chromedp/kb"
|
|
)
|
|
|
|
// 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 *cdp.Node, opts ...MouseOption) Action {
|
|
return ActionFunc(func(ctxt context.Context, h cdp.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 will synthesize a keyDown, char, and keyUp event for each rune
|
|
// contained in keys along with any supplied key options. Well known KeyCode
|
|
// runes will not synthesize the char event.
|
|
//
|
|
// Note: KeyCodeCR and KeyCodeLF are exceptions to the above, and a char of
|
|
// KeyCodeCR ('\r') will be synthesized for both.
|
|
func KeyAction(keys string, opts ...KeyOption) Action {
|
|
return ActionFunc(func(ctxt context.Context, h cdp.FrameHandler) error {
|
|
var err error
|
|
|
|
for _, r := range keys {
|
|
for _, k := range kb.Encode(r) {
|
|
err = k.Do(ctxt, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// TODO: move to context
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// KeyActionNode dispatches a key event on a node.
|
|
func KeyActionNode(n *cdp.Node, keys string, opts ...KeyOption) Action {
|
|
return ActionFunc(func(ctxt context.Context, h cdp.FrameHandler) error {
|
|
err := dom.Focus(n.NodeID).Do(ctxt, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return KeyAction(keys, opts...).Do(ctxt, h)
|
|
})
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|