Code cleanup
- Refactored API calls to be cleaner - Changed types that shoudn't be exported to not-exported - Updated examples with API changes - Added unit test for Title action
This commit is contained in:
parent
f73c429109
commit
3673164aef
|
@ -54,9 +54,17 @@ func TestNavigate(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(urlstr, "https://www.google.") {
|
if !strings.HasPrefix(urlstr, "https://www.google.") {
|
||||||
t.Errorf("expected to be on google, got: %v", urlstr)
|
t.Errorf("expected to be on google domain, at: %s", urlstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var title string
|
||||||
|
err = c.Run(defaultContext, Title(&title))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.ToLower(title), "google") {
|
||||||
|
t.Errorf("expected title to contain google, instead title is: %s", title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
39
errors.go
Normal file
39
errors.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package chromedp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error types.
|
||||||
|
var (
|
||||||
|
// ErrInvalidDimensions is the error returned when the retrieved box model is
|
||||||
|
// invalid.
|
||||||
|
ErrInvalidDimensions = errors.New("invalid dimensions")
|
||||||
|
|
||||||
|
// ErrNoResults is the error returned when there are no matching nodes.
|
||||||
|
ErrNoResults = errors.New("no results")
|
||||||
|
|
||||||
|
// ErrHasResults is the error returned when there should not be any
|
||||||
|
// matching nodes.
|
||||||
|
ErrHasResults = errors.New("has results")
|
||||||
|
|
||||||
|
// ErrNotVisible is the error returned when a non-visible node should be
|
||||||
|
// visible.
|
||||||
|
ErrNotVisible = errors.New("not visible")
|
||||||
|
|
||||||
|
// ErrVisible is the error returned when a visible node should be
|
||||||
|
// non-visible.
|
||||||
|
ErrVisible = errors.New("visible")
|
||||||
|
|
||||||
|
// ErrDisabled is the error returned when a disabled node should be
|
||||||
|
// enabled.
|
||||||
|
ErrDisabled = errors.New("disabled")
|
||||||
|
|
||||||
|
// ErrNotSelected is the error returned when a non-selected node should be
|
||||||
|
// selected.
|
||||||
|
ErrNotSelected = errors.New("not selected")
|
||||||
|
|
||||||
|
// ErrInvalidBoxModel is the error returned when the retrieved box model
|
||||||
|
// data is invalid.
|
||||||
|
ErrInvalidBoxModel = errors.New("invalid box model")
|
||||||
|
)
|
|
@ -44,7 +44,7 @@ func click() cdp.Tasks {
|
||||||
return cdp.Tasks{
|
return cdp.Tasks{
|
||||||
cdp.Navigate(`https://golang.org/pkg/time/`),
|
cdp.Navigate(`https://golang.org/pkg/time/`),
|
||||||
cdp.WaitVisible(`#footer`),
|
cdp.WaitVisible(`#footer`),
|
||||||
cdp.Click(`#pkg-overview`, cdp.ElementVisible),
|
cdp.Click(`#pkg-overview`, cdp.NodeVisible),
|
||||||
cdp.Sleep(150 * time.Second),
|
cdp.Sleep(150 * time.Second),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,6 @@ func main() {
|
||||||
func text(res *string) cdp.Tasks {
|
func text(res *string) cdp.Tasks {
|
||||||
return cdp.Tasks{
|
return cdp.Tasks{
|
||||||
cdp.Navigate(`https://golang.org/pkg/time/`),
|
cdp.Navigate(`https://golang.org/pkg/time/`),
|
||||||
cdp.Text(`#pkg-overview`, res, cdp.ElementVisible, cdp.ByID),
|
cdp.Text(`#pkg-overview`, res, cdp.NodeVisible, cdp.ByID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -504,7 +504,7 @@ func (h *TargetHandler) pageEvent(ctxt context.Context, ev interface{}) {
|
||||||
defer h.pageWaitGroup.Done()
|
defer h.pageWaitGroup.Done()
|
||||||
|
|
||||||
var id cdp.FrameID
|
var id cdp.FrameID
|
||||||
var op FrameOp
|
var op frameOp
|
||||||
|
|
||||||
switch e := ev.(type) {
|
switch e := ev.(type) {
|
||||||
case *page.EventFrameNavigated:
|
case *page.EventFrameNavigated:
|
||||||
|
@ -569,7 +569,7 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var id cdp.NodeID
|
var id cdp.NodeID
|
||||||
var op NodeOp
|
var op nodeOp
|
||||||
|
|
||||||
switch e := ev.(type) {
|
switch e := ev.(type) {
|
||||||
case *dom.EventSetChildNodes:
|
case *dom.EventSetChildNodes:
|
||||||
|
|
6
input.go
6
input.go
|
@ -2,7 +2,6 @@ package chromedp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/knq/chromedp/cdp"
|
"github.com/knq/chromedp/cdp"
|
||||||
|
@ -11,11 +10,6 @@ import (
|
||||||
"github.com/knq/chromedp/kb"
|
"github.com/knq/chromedp/kb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error types.
|
|
||||||
var (
|
|
||||||
ErrInvalidDimensions = errors.New("invalid box dimensions")
|
|
||||||
)
|
|
||||||
|
|
||||||
// MouseAction is a mouse action.
|
// MouseAction is a mouse action.
|
||||||
func MouseAction(typ input.MouseType, x, y int64, opts ...MouseOption) Action {
|
func MouseAction(typ input.MouseType, x, y int64, opts ...MouseOption) Action {
|
||||||
me := input.DispatchMouseEvent(typ, x, y)
|
me := input.DispatchMouseEvent(typ, x, y)
|
||||||
|
|
28
nav.go
28
nav.go
|
@ -82,6 +82,16 @@ func NavigateForward(ctxt context.Context, h cdp.Handler) error {
|
||||||
return page.NavigateToHistoryEntry(entries[i+1].ID).Do(ctxt, h)
|
return page.NavigateToHistoryEntry(entries[i+1].ID).Do(ctxt, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop stops all navigation and pending resource retrieval.
|
||||||
|
func Stop() Action {
|
||||||
|
return page.StopLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload reloads the current page.
|
||||||
|
func Reload() Action {
|
||||||
|
return page.Reload()
|
||||||
|
}
|
||||||
|
|
||||||
// CaptureScreenshot captures takes a full page screenshot.
|
// CaptureScreenshot captures takes a full page screenshot.
|
||||||
func CaptureScreenshot(res *[]byte) Action {
|
func CaptureScreenshot(res *[]byte) Action {
|
||||||
if res == nil {
|
if res == nil {
|
||||||
|
@ -113,16 +123,20 @@ func RemoveOnLoadScript(id page.ScriptIdentifier) Action {
|
||||||
return page.RemoveScriptToEvaluateOnLoad(id)
|
return page.RemoveScriptToEvaluateOnLoad(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops all navigation and pending resource retrieval.
|
// Location retrieves the document location.
|
||||||
func Stop() Action {
|
|
||||||
return page.StopLoading()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Location retrieves the URL location.
|
|
||||||
func Location(urlstr *string) Action {
|
func Location(urlstr *string) Action {
|
||||||
if urlstr == nil {
|
if urlstr == nil {
|
||||||
panic("urlstr cannot be nil")
|
panic("urlstr cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
return EvaluateAsDevTools(`location.toString()`, urlstr)
|
return EvaluateAsDevTools(`document.location.toString()`, urlstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title retrieves the document title.
|
||||||
|
func Title(title *string) Action {
|
||||||
|
if title == nil {
|
||||||
|
panic("title cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvaluateAsDevTools(`document.title`, title)
|
||||||
}
|
}
|
||||||
|
|
12
pool.go
12
pool.go
|
@ -8,14 +8,6 @@ import (
|
||||||
"github.com/knq/chromedp/runner"
|
"github.com/knq/chromedp/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// DefaultStartPort is the default start port number.
|
|
||||||
DefaultStartPort = 9000
|
|
||||||
|
|
||||||
// DefaultEndPort is the default end port number.
|
|
||||||
DefaultEndPort = 10000
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pool manages a pool of running Chrome processes.
|
// Pool manages a pool of running Chrome processes.
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
// start is the start port.
|
// start is the start port.
|
||||||
|
@ -35,8 +27,8 @@ func NewPool(opts ...PoolOption) (*Pool, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
p := &Pool{
|
p := &Pool{
|
||||||
start: DefaultStartPort,
|
start: DefaultPoolStartPort,
|
||||||
end: DefaultEndPort,
|
end: DefaultPoolEndPort,
|
||||||
res: make(map[int]*Res),
|
res: make(map[int]*Res),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
229
query.go
229
query.go
|
@ -12,17 +12,12 @@ import (
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
"github.com/knq/chromedp/cdp"
|
"github.com/knq/chromedp/cdp"
|
||||||
|
"github.com/knq/chromedp/cdp/css"
|
||||||
"github.com/knq/chromedp/cdp/dom"
|
"github.com/knq/chromedp/cdp/dom"
|
||||||
"github.com/knq/chromedp/cdp/page"
|
"github.com/knq/chromedp/cdp/page"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// Nodes retrieves the document nodes matching the selector.
|
||||||
// ErrInvalidBoxModel is the error returned when the retrieved box model is
|
|
||||||
// invalid.
|
|
||||||
ErrInvalidBoxModel = errors.New("invalid box model")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Nodes retrieves the DOM nodes matching the selector.
|
|
||||||
func Nodes(sel interface{}, nodes *[]*cdp.Node, opts ...QueryOption) Action {
|
func Nodes(sel interface{}, nodes *[]*cdp.Node, opts ...QueryOption) Action {
|
||||||
if nodes == nil {
|
if nodes == nil {
|
||||||
panic("nodes cannot be nil")
|
panic("nodes cannot be nil")
|
||||||
|
@ -34,7 +29,7 @@ func Nodes(sel interface{}, nodes *[]*cdp.Node, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeIDs returns the node IDs of the matching selector.
|
// NodeIDs retrieves the node IDs matching the selector.
|
||||||
func NodeIDs(sel interface{}, ids *[]cdp.NodeID, opts ...QueryOption) Action {
|
func NodeIDs(sel interface{}, ids *[]cdp.NodeID, opts ...QueryOption) Action {
|
||||||
if ids == nil {
|
if ids == nil {
|
||||||
panic("nodes cannot be nil")
|
panic("nodes cannot be nil")
|
||||||
|
@ -52,7 +47,7 @@ func NodeIDs(sel interface{}, ids *[]cdp.NodeID, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus focuses the first element returned by the selector.
|
// Focus focuses the first node matching the selector.
|
||||||
func Focus(sel interface{}, opts ...QueryOption) Action {
|
func Focus(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -63,7 +58,7 @@ func Focus(sel interface{}, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blur unfocuses (blurs) the first element returned by the selector.
|
// Blur unfocuses (blurs) the first node matching the selector.
|
||||||
func Blur(sel interface{}, opts ...QueryOption) Action {
|
func Blur(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -75,14 +70,32 @@ func Blur(sel interface{}, opts ...QueryOption) Action {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !res {
|
if !res {
|
||||||
return fmt.Errorf("could not blur node %d", nodes[0].NodeID)
|
return fmt.Errorf("could not blur node %d", nodes[0].NodeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text retrieves the text of the first element matching the selector.
|
// Dimensions retrieves the box model dimensions for the first node matching
|
||||||
|
// the selector.
|
||||||
|
func Dimensions(sel interface{}, model **dom.BoxModel, opts ...QueryOption) Action {
|
||||||
|
if model == nil {
|
||||||
|
panic("model cannot be nil")
|
||||||
|
}
|
||||||
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
|
if len(nodes) < 1 {
|
||||||
|
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
*model, err = dom.GetBoxModel(nodes[0].NodeID).Do(ctxt, h)
|
||||||
|
return err
|
||||||
|
}, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text retrieves the visible text of the first node matching the selector.
|
||||||
func Text(sel interface{}, text *string, opts ...QueryOption) Action {
|
func Text(sel interface{}, text *string, opts ...QueryOption) Action {
|
||||||
if text == nil {
|
if text == nil {
|
||||||
panic("text cannot be nil")
|
panic("text cannot be nil")
|
||||||
|
@ -97,7 +110,7 @@ func Text(sel interface{}, text *string, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear clears input and textarea fields of their values.
|
// Clear clears the values of any input/textarea nodes matching the selector.
|
||||||
func Clear(sel interface{}, opts ...QueryOption) Action {
|
func Clear(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -138,23 +151,7 @@ func Clear(sel interface{}, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dimensions retrieves the box model dimensions for the first node matching
|
// Value retrieves the value of the first node matching the selector.
|
||||||
// the specified selector.
|
|
||||||
func Dimensions(sel interface{}, model **dom.BoxModel, opts ...QueryOption) Action {
|
|
||||||
if model == nil {
|
|
||||||
panic("model cannot be nil")
|
|
||||||
}
|
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
|
||||||
if len(nodes) < 1 {
|
|
||||||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
*model, err = dom.GetBoxModel(nodes[0].NodeID).Do(ctxt, h)
|
|
||||||
return err
|
|
||||||
}, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value retrieves the value of an element.
|
|
||||||
func Value(sel interface{}, value *string, opts ...QueryOption) Action {
|
func Value(sel interface{}, value *string, opts ...QueryOption) Action {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
panic("value cannot be nil")
|
panic("value cannot be nil")
|
||||||
|
@ -189,7 +186,7 @@ func SetValue(sel interface{}, value string, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes retrieves the attributes for the specified element.
|
// Attributes retrieves the attributes for the first node matching the selector.
|
||||||
func Attributes(sel interface{}, attributes *map[string]string, opts ...QueryOption) Action {
|
func Attributes(sel interface{}, attributes *map[string]string, opts ...QueryOption) Action {
|
||||||
if attributes == nil {
|
if attributes == nil {
|
||||||
panic("attributes cannot be nil")
|
panic("attributes cannot be nil")
|
||||||
|
@ -215,19 +212,26 @@ func Attributes(sel interface{}, attributes *map[string]string, opts ...QueryOpt
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAttributes sets the attributes for the specified element.
|
// SetAttributes sets the attributes for the first node matching the selector.
|
||||||
func SetAttributes(sel interface{}, attributes map[string]string, opts ...QueryOption) Action {
|
func SetAttributes(sel interface{}, attributes map[string]string, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
return errors.New("expected at least one element")
|
return errors.New("expected at least one element")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
attrs := make([]string, len(attributes)*2)
|
||||||
|
i := 0
|
||||||
|
for k, v := range attributes {
|
||||||
|
attrs[i], attrs[i+1] = k, v
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return dom.SetAttributesAsText(nodes[0].NodeID, strings.Join(attrs, " ")).Do(ctxt, h)
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttributeValue retrieves the name'd attribute value for the specified
|
// AttributeValue retrieves the attribute value for the first node matching the
|
||||||
// element.
|
// selector.
|
||||||
func AttributeValue(sel interface{}, name string, value *string, ok *bool, opts ...QueryOption) Action {
|
func AttributeValue(sel interface{}, name string, value *string, ok *bool, opts ...QueryOption) Action {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
panic("value cannot be nil")
|
panic("value cannot be nil")
|
||||||
|
@ -260,7 +264,8 @@ func AttributeValue(sel interface{}, name string, value *string, ok *bool, opts
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAttributeValue sets an element's attribute with name to value.
|
// SetAttributeValue sets the attribute with name to value on the first node
|
||||||
|
// matching the selector.
|
||||||
func SetAttributeValue(sel interface{}, name, value string, opts ...QueryOption) Action {
|
func SetAttributeValue(sel interface{}, name, value string, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -271,7 +276,8 @@ func SetAttributeValue(sel interface{}, name, value string, opts ...QueryOption)
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAttribute removes an element's attribute with name.
|
// RemoveAttribute removes the attribute with name from the first node matching
|
||||||
|
// the selector.
|
||||||
func RemoveAttribute(sel interface{}, name string, opts ...QueryOption) Action {
|
func RemoveAttribute(sel interface{}, name string, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -282,7 +288,7 @@ func RemoveAttribute(sel interface{}, name string, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click sends a click to the first element returned by the selector.
|
// Click sends a mouse click event to the first node matching the selector.
|
||||||
func Click(sel interface{}, opts ...QueryOption) Action {
|
func Click(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -290,10 +296,11 @@ func Click(sel interface{}, opts ...QueryOption) Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
return MouseClickNode(nodes[0]).Do(ctxt, h)
|
return MouseClickNode(nodes[0]).Do(ctxt, h)
|
||||||
}, append(opts, ElementVisible)...)
|
}, append(opts, NodeVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoubleClick does a double click on the first element returned by selector.
|
// DoubleClick sends a mouse double click event to the first node matching the
|
||||||
|
// selector.
|
||||||
func DoubleClick(sel interface{}, opts ...QueryOption) Action {
|
func DoubleClick(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -301,34 +308,21 @@ func DoubleClick(sel interface{}, opts ...QueryOption) Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
return MouseClickNode(nodes[0], ClickCount(2)).Do(ctxt, h)
|
return MouseClickNode(nodes[0], ClickCount(2)).Do(ctxt, h)
|
||||||
}, append(opts, ElementVisible)...)
|
}, append(opts, NodeVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: temporarily disabling this until a proper unit test can be written.
|
// SendKeys synthesizes the key up, char, and down events as needed for the
|
||||||
//
|
// runes in v, sending them to the first node matching the selector.
|
||||||
// Hover hovers (moves) the mouse over the first element returned by the
|
|
||||||
// selector.
|
|
||||||
//func Hover(sel interface{}, opts ...QueryOption) Action {
|
|
||||||
// return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
|
||||||
// if len(nodes) < 1 {
|
|
||||||
// return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return MouseClickNode(nodes[0], ButtonNone).Do(ctxt, h)
|
|
||||||
// }, append(opts, ElementVisible)...)
|
|
||||||
//}
|
|
||||||
|
|
||||||
// SendKeys sends keys to the first element returned by selector.
|
|
||||||
func SendKeys(sel interface{}, v string, opts ...QueryOption) Action {
|
func SendKeys(sel interface{}, v string, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||||
}
|
}
|
||||||
return KeyActionNode(nodes[0], v).Do(ctxt, h)
|
return KeyActionNode(nodes[0], v).Do(ctxt, h)
|
||||||
}, append(opts, ElementVisible)...)
|
}, append(opts, NodeVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Screenshot takes a screenshot of the first element matching the selector.
|
// Screenshot takes a screenshot of the first node matching the selector.
|
||||||
func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) Action {
|
func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) Action {
|
||||||
if picbuf == nil {
|
if picbuf == nil {
|
||||||
panic("picbuf cannot be nil")
|
panic("picbuf cannot be nil")
|
||||||
|
@ -387,11 +381,11 @@ func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) Action {
|
||||||
*picbuf = croppedBuf.Bytes()
|
*picbuf = croppedBuf.Bytes()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, append(opts, ElementVisible)...)
|
}, append(opts, NodeVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit is an action that submits whatever form the first element matching
|
// Submit is an action that submits the form of the first node matching the
|
||||||
// the selector belongs to.
|
// selector belongs to.
|
||||||
func Submit(sel interface{}, opts ...QueryOption) Action {
|
func Submit(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
|
@ -412,7 +406,7 @@ func Submit(sel interface{}, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset is an action that resets whatever form the first element matching the
|
// Reset is an action that resets the form of the first node matching the
|
||||||
// selector belongs to.
|
// selector belongs to.
|
||||||
func Reset(sel interface{}, opts ...QueryOption) Action {
|
func Reset(sel interface{}, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
|
@ -434,84 +428,51 @@ func Reset(sel interface{}, opts ...QueryOption) Action {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// ComputedStyle retrieves the computed style of the first node matching the selector.
|
||||||
// textJS is a javascript snippet that returns the concatenated textContent
|
func ComputedStyle(sel interface{}, style *[]*css.ComputedProperty, opts ...QueryOption) Action {
|
||||||
// of all visible (ie, offsetParent !== null) children.
|
if style == nil {
|
||||||
textJS = `(function(a) {
|
panic("style cannot be nil")
|
||||||
var s = '';
|
}
|
||||||
for (var i = 0; i < a.length; i++) {
|
|
||||||
if (a[i].offsetParent !== null) {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
s += a[i].textContent;
|
if len(nodes) < 1 {
|
||||||
}
|
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||||
}
|
}
|
||||||
return s;
|
|
||||||
})($x("%s/node()"))`
|
|
||||||
|
|
||||||
// blurJS is a javscript snippet that blurs the specified element.
|
computed, err := css.GetComputedStyleForNode(nodes[0].NodeID).Do(ctxt, h)
|
||||||
blurJS = `(function(a) {
|
if err != nil {
|
||||||
a[0].blur();
|
return err
|
||||||
return true;
|
|
||||||
})($x('%s'))`
|
|
||||||
|
|
||||||
// scrollJS is a javascript snippet that scrolls the window to the
|
|
||||||
// specified x, y coordinates and then returns the actual window x/y after
|
|
||||||
// execution.
|
|
||||||
scrollJS = `(function(x, y) {
|
|
||||||
window.scrollTo(x, y);
|
|
||||||
return [window.scrollX, window.scrollY];
|
|
||||||
})(%d, %d)`
|
|
||||||
|
|
||||||
// submitJS is a javascript snippet that will call the containing form's
|
|
||||||
// submit function, returning true or false if the call was successful.
|
|
||||||
submitJS = `(function(a) {
|
|
||||||
if (a[0].nodeName === 'FORM') {
|
|
||||||
a[0].submit();
|
|
||||||
return true;
|
|
||||||
} else if (a[0].form !== null) {
|
|
||||||
a[0].form.submit();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
})($x('%s'))`
|
|
||||||
|
|
||||||
// resetJS is a javascript snippet that will call the containing form's
|
*style = computed
|
||||||
// reset function, returning true or false if the call was successful.
|
|
||||||
resetJS = `(function(a) {
|
return nil
|
||||||
if (a[0].nodeName === 'FORM') {
|
}, opts...)
|
||||||
a[0].reset();
|
}
|
||||||
return true;
|
|
||||||
} else if (a[0].form !== null) {
|
// MatchedStyle retrieves the matched style information for the first node
|
||||||
a[0].form.reset();
|
// matching the selector.
|
||||||
return true;
|
func MatchedStyle(sel interface{}, style **css.GetMatchedStylesForNodeReturns, opts ...QueryOption) Action {
|
||||||
|
if style == nil {
|
||||||
|
panic("style cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
|
if len(nodes) < 1 {
|
||||||
|
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
})($x('%s'))`
|
|
||||||
|
|
||||||
// valueJS is a javascript snippet that returns the value of a specified
|
var err error
|
||||||
// node.
|
ret := &css.GetMatchedStylesForNodeReturns{}
|
||||||
valueJS = `(function(a) {
|
ret.InlineStyle, ret.AttributesStyle, ret.MatchedCSSRules,
|
||||||
return a[0].value;
|
ret.PseudoElements, ret.Inherited, ret.CSSKeyframesRules,
|
||||||
})($x('%s'))`
|
err = css.GetMatchedStylesForNode(nodes[0].NodeID).Do(ctxt, h)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// setValueJS is a javascript snippet that sets the value of the specified
|
*style = ret
|
||||||
// node, and returns the value.
|
|
||||||
setValueJS = `(function(a, val) {
|
|
||||||
return a[0].value = val;
|
|
||||||
})($x('%s'), '%s')`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
return nil
|
||||||
|
}, opts...)
|
||||||
Title
|
}
|
||||||
SetTitle
|
|
||||||
OuterHTML
|
|
||||||
SetOuterHTML
|
|
||||||
|
|
||||||
NodeName -- ?
|
|
||||||
|
|
||||||
Style(Matched)
|
|
||||||
Style(Computed)
|
|
||||||
SetStyle
|
|
||||||
GetStyle(Inline)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
153
sel.go
153
sel.go
|
@ -2,14 +2,12 @@ package chromedp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/knq/chromedp/cdp"
|
"github.com/knq/chromedp/cdp"
|
||||||
"github.com/knq/chromedp/cdp/css"
|
|
||||||
"github.com/knq/chromedp/cdp/dom"
|
"github.com/knq/chromedp/cdp/dom"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,16 +22,6 @@ tagname
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Error types.
|
|
||||||
var (
|
|
||||||
ErrNoResults = errors.New("no results")
|
|
||||||
ErrHasResults = errors.New("has results")
|
|
||||||
ErrNotVisible = errors.New("not visible")
|
|
||||||
ErrVisible = errors.New("visible")
|
|
||||||
ErrDisabled = errors.New("disabled")
|
|
||||||
ErrNotSelected = errors.New("not selected")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Selector holds information pertaining to an element query select action.
|
// Selector holds information pertaining to an element query select action.
|
||||||
type Selector struct {
|
type Selector struct {
|
||||||
sel interface{}
|
sel interface{}
|
||||||
|
@ -61,7 +49,7 @@ func Query(sel interface{}, opts ...QueryOption) Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.wait == nil {
|
if s.wait == nil {
|
||||||
ElementReady(s)
|
NodeReady(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
@ -294,13 +282,13 @@ func WaitFunc(wait func(context.Context, cdp.Handler, *cdp.Node, ...cdp.NodeID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementReady is a query option to wait until the element is ready.
|
// NodeReady is a query option to wait until the element is ready.
|
||||||
func ElementReady(s *Selector) {
|
func NodeReady(s *Selector) {
|
||||||
WaitFunc(s.waitReady(nil))(s)
|
WaitFunc(s.waitReady(nil))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementVisible is a query option to wait until the element is visible.
|
// NodeVisible is a query option to wait until the element is visible.
|
||||||
func ElementVisible(s *Selector) {
|
func NodeVisible(s *Selector) {
|
||||||
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -327,8 +315,8 @@ func ElementVisible(s *Selector) {
|
||||||
}))(s)
|
}))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementNotVisible is a query option to wait until the element is not visible.
|
// NodeNotVisible is a query option to wait until the element is not visible.
|
||||||
func ElementNotVisible(s *Selector) {
|
func NodeNotVisible(s *Selector) {
|
||||||
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -355,105 +343,8 @@ func ElementNotVisible(s *Selector) {
|
||||||
}))(s)
|
}))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementVisibleOld is a query option to wait until the element is visible.
|
// NodeEnabled is a query option to wait until the element is enabled.
|
||||||
//
|
func NodeEnabled(s *Selector) {
|
||||||
// This is the old, complicated, implementation (deprecated).
|
|
||||||
func ElementVisibleOld(s *Selector) {
|
|
||||||
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// check node has box model
|
|
||||||
_, err = dom.GetBoxModel(n.NodeID).Do(ctxt, h)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if any of the parents are not visible ...
|
|
||||||
var hidden bool
|
|
||||||
for ; n.Parent != nil; n = n.Parent {
|
|
||||||
// get style
|
|
||||||
style, err := css.GetComputedStyleForNode(n.NodeID).Do(ctxt, h)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if hidden
|
|
||||||
for _, c := range style {
|
|
||||||
switch c.Name {
|
|
||||||
case "display":
|
|
||||||
//log.Printf("%d >>>> %s=%s", n.NodeID, c.Name, c.Value)
|
|
||||||
hidden = c.Value == "none"
|
|
||||||
|
|
||||||
case "visibility":
|
|
||||||
//log.Printf("%d >>>> %s=%s", n.NodeID, c.Name, c.Value)
|
|
||||||
hidden = c.Value != "visible"
|
|
||||||
|
|
||||||
case "hidden":
|
|
||||||
//log.Printf("%d >>>> %s=%s", n.NodeID, c.Name, c.Value)
|
|
||||||
hidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if hidden {
|
|
||||||
return ErrNotVisible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}))(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ElementNotVisibleOld is a query option to wait until the element is not
|
|
||||||
// visible.
|
|
||||||
//
|
|
||||||
// This is the old, complicated, implementation (deprecated).
|
|
||||||
func ElementNotVisibleOld(s *Selector) {
|
|
||||||
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// check node has box model
|
|
||||||
_, err = dom.GetBoxModel(n.NodeID).Do(ctxt, h)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if any of the parents are not visible ...
|
|
||||||
var hidden bool
|
|
||||||
for ; n.Parent != nil; n = n.Parent {
|
|
||||||
// get style
|
|
||||||
style, err := css.GetComputedStyleForNode(n.NodeID).Do(ctxt, h)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if hidden
|
|
||||||
for _, c := range style {
|
|
||||||
switch c.Name {
|
|
||||||
case "display":
|
|
||||||
//log.Printf("%d >>>> %s=%s", n.NodeID, c.Name, c.Value)
|
|
||||||
hidden = c.Value == "none"
|
|
||||||
|
|
||||||
case "visibility":
|
|
||||||
//log.Printf("%d >>>> %s=%s", n.NodeID, c.Name, c.Value)
|
|
||||||
hidden = c.Value != "visible"
|
|
||||||
|
|
||||||
case "hidden":
|
|
||||||
//log.Printf("%d >>>> %s=%s", n.NodeID, c.Name, c.Value)
|
|
||||||
hidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if hidden {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrVisible
|
|
||||||
}))(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ElementEnabled is a query option to wait until the element is enabled.
|
|
||||||
func ElementEnabled(s *Selector) {
|
|
||||||
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
||||||
n.RLock()
|
n.RLock()
|
||||||
defer n.RUnlock()
|
defer n.RUnlock()
|
||||||
|
@ -468,8 +359,8 @@ func ElementEnabled(s *Selector) {
|
||||||
}))(s)
|
}))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementSelected is a query option to wait until the element is selected.
|
// NodeSelected is a query option to wait until the element is selected.
|
||||||
func ElementSelected(s *Selector) {
|
func NodeSelected(s *Selector) {
|
||||||
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error {
|
||||||
n.RLock()
|
n.RLock()
|
||||||
defer n.RUnlock()
|
defer n.RUnlock()
|
||||||
|
@ -484,9 +375,9 @@ func ElementSelected(s *Selector) {
|
||||||
}))(s)
|
}))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementNotPresent is a query option to wait until no elements match are
|
// NodeNotPresent is a query option to wait until no elements match are
|
||||||
// present matching the selector.
|
// present matching the selector.
|
||||||
func ElementNotPresent(s *Selector) {
|
func NodeNotPresent(s *Selector) {
|
||||||
s.exp = 0
|
s.exp = 0
|
||||||
WaitFunc(func(ctxt context.Context, h cdp.Handler, n *cdp.Node, ids ...cdp.NodeID) ([]*cdp.Node, error) {
|
WaitFunc(func(ctxt context.Context, h cdp.Handler, n *cdp.Node, ids ...cdp.NodeID) ([]*cdp.Node, error) {
|
||||||
if len(ids) != 0 {
|
if len(ids) != 0 {
|
||||||
|
@ -519,34 +410,26 @@ func WaitReady(sel interface{}, opts ...QueryOption) Action {
|
||||||
|
|
||||||
// WaitVisible waits until the selected element is visible.
|
// WaitVisible waits until the selected element is visible.
|
||||||
func WaitVisible(sel interface{}, opts ...QueryOption) Action {
|
func WaitVisible(sel interface{}, opts ...QueryOption) Action {
|
||||||
return Query(sel, append(opts, ElementVisible)...)
|
return Query(sel, append(opts, NodeVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitNotVisible waits until the selected element is not visible.
|
// WaitNotVisible waits until the selected element is not visible.
|
||||||
func WaitNotVisible(sel interface{}, opts ...QueryOption) Action {
|
func WaitNotVisible(sel interface{}, opts ...QueryOption) Action {
|
||||||
return Query(sel, append(opts, ElementNotVisible)...)
|
return Query(sel, append(opts, NodeNotVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitEnabled waits until the selected element is enabled (does not have
|
// WaitEnabled waits until the selected element is enabled (does not have
|
||||||
// attribute 'disabled').
|
// attribute 'disabled').
|
||||||
func WaitEnabled(sel interface{}, opts ...QueryOption) Action {
|
func WaitEnabled(sel interface{}, opts ...QueryOption) Action {
|
||||||
return Query(sel, append(opts, ElementEnabled)...)
|
return Query(sel, append(opts, NodeEnabled)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitSelected waits until the element is selected (has attribute 'selected').
|
// WaitSelected waits until the element is selected (has attribute 'selected').
|
||||||
func WaitSelected(sel interface{}, opts ...QueryOption) Action {
|
func WaitSelected(sel interface{}, opts ...QueryOption) Action {
|
||||||
return Query(sel, append(opts, ElementSelected)...)
|
return Query(sel, append(opts, NodeSelected)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitNotPresent waits until no elements match the specified selector.
|
// WaitNotPresent waits until no elements match the specified selector.
|
||||||
func WaitNotPresent(sel interface{}, opts ...QueryOption) Action {
|
func WaitNotPresent(sel interface{}, opts ...QueryOption) Action {
|
||||||
return Query(sel, append(opts, ElementNotPresent)...)
|
return Query(sel, append(opts, NodeNotPresent)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
// visibleJS is a javascript snippet that returns true or false depending
|
|
||||||
// on if the specified node's offsetParent is not null.
|
|
||||||
visibleJS = `(function(a) {
|
|
||||||
return a[0].offsetParent !== null
|
|
||||||
})($x('%s'))`
|
|
||||||
)
|
|
||||||
|
|
112
util.go
112
util.go
|
@ -15,11 +15,87 @@ const (
|
||||||
// DefaultCheckDuration is the default time to sleep between a check.
|
// DefaultCheckDuration is the default time to sleep between a check.
|
||||||
DefaultCheckDuration = 50 * time.Millisecond
|
DefaultCheckDuration = 50 * time.Millisecond
|
||||||
|
|
||||||
|
// DefaultPoolStartPort is the default start port number.
|
||||||
|
DefaultPoolStartPort = 9000
|
||||||
|
|
||||||
|
// DefaultPoolEndPort is the default end port number.
|
||||||
|
DefaultPoolEndPort = 10000
|
||||||
|
|
||||||
// EmptyFrameID is the "non-existent" (ie current) frame.
|
// EmptyFrameID is the "non-existent" (ie current) frame.
|
||||||
EmptyFrameID cdp.FrameID = cdp.FrameID("")
|
EmptyFrameID cdp.FrameID = cdp.FrameID("")
|
||||||
|
|
||||||
// EmptyNodeID is the "non-existent" node id.
|
// EmptyNodeID is the "non-existent" node id.
|
||||||
EmptyNodeID cdp.NodeID = cdp.NodeID(0)
|
EmptyNodeID cdp.NodeID = cdp.NodeID(0)
|
||||||
|
|
||||||
|
// textJS is a javascript snippet that returns the concatenated textContent
|
||||||
|
// of all visible (ie, offsetParent !== null) children.
|
||||||
|
textJS = `(function(a) {
|
||||||
|
var s = '';
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
if (a[i].offsetParent !== null) {
|
||||||
|
s += a[i].textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
})($x("%s/node()"))`
|
||||||
|
|
||||||
|
// blurJS is a javscript snippet that blurs the specified element.
|
||||||
|
blurJS = `(function(a) {
|
||||||
|
a[0].blur();
|
||||||
|
return true;
|
||||||
|
})($x('%s'))`
|
||||||
|
|
||||||
|
// scrollJS is a javascript snippet that scrolls the window to the
|
||||||
|
// specified x, y coordinates and then returns the actual window x/y after
|
||||||
|
// execution.
|
||||||
|
scrollJS = `(function(x, y) {
|
||||||
|
window.scrollTo(x, y);
|
||||||
|
return [window.scrollX, window.scrollY];
|
||||||
|
})(%d, %d)`
|
||||||
|
|
||||||
|
// submitJS is a javascript snippet that will call the containing form's
|
||||||
|
// submit function, returning true or false if the call was successful.
|
||||||
|
submitJS = `(function(a) {
|
||||||
|
if (a[0].nodeName === 'FORM') {
|
||||||
|
a[0].submit();
|
||||||
|
return true;
|
||||||
|
} else if (a[0].form !== null) {
|
||||||
|
a[0].form.submit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})($x('%s'))`
|
||||||
|
|
||||||
|
// resetJS is a javascript snippet that will call the containing form's
|
||||||
|
// reset function, returning true or false if the call was successful.
|
||||||
|
resetJS = `(function(a) {
|
||||||
|
if (a[0].nodeName === 'FORM') {
|
||||||
|
a[0].reset();
|
||||||
|
return true;
|
||||||
|
} else if (a[0].form !== null) {
|
||||||
|
a[0].form.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})($x('%s'))`
|
||||||
|
|
||||||
|
// valueJS is a javascript snippet that returns the value of a specified
|
||||||
|
// node.
|
||||||
|
valueJS = `(function(a) {
|
||||||
|
return a[0].value;
|
||||||
|
})($x('%s'))`
|
||||||
|
|
||||||
|
// setValueJS is a javascript snippet that sets the value of the specified
|
||||||
|
// node, and returns the value.
|
||||||
|
setValueJS = `(function(a, val) {
|
||||||
|
return a[0].value = val;
|
||||||
|
})($x('%s'), '%s')`
|
||||||
|
|
||||||
|
// visibleJS is a javascript snippet that returns true or false depending
|
||||||
|
// on if the specified node's offsetParent is not null.
|
||||||
|
visibleJS = `(function(a) {
|
||||||
|
return a[0].offsetParent !== null
|
||||||
|
})($x('%s'))`
|
||||||
)
|
)
|
||||||
|
|
||||||
// UnmarshalMessage unmarshals the message result or params.
|
// UnmarshalMessage unmarshals the message result or params.
|
||||||
|
@ -27,8 +103,8 @@ func UnmarshalMessage(msg *cdp.Message) (interface{}, error) {
|
||||||
return util.UnmarshalMessage(msg)
|
return util.UnmarshalMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrameOp is a frame manipulation operation.
|
// frameOp is a frame manipulation operation.
|
||||||
type FrameOp func(*cdp.Frame)
|
type frameOp func(*cdp.Frame)
|
||||||
|
|
||||||
/*func domContentEventFired(f *cdp.Frame) {
|
/*func domContentEventFired(f *cdp.Frame) {
|
||||||
}
|
}
|
||||||
|
@ -36,7 +112,7 @@ type FrameOp func(*cdp.Frame)
|
||||||
func loadEventFired(f *cdp.Frame) {
|
func loadEventFired(f *cdp.Frame) {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
func frameAttached(id cdp.FrameID) FrameOp {
|
func frameAttached(id cdp.FrameID) frameOp {
|
||||||
return func(f *cdp.Frame) {
|
return func(f *cdp.Frame) {
|
||||||
f.ParentID = id
|
f.ParentID = id
|
||||||
setFrameState(f, cdp.FrameAttached)
|
setFrameState(f, cdp.FrameAttached)
|
||||||
|
@ -82,8 +158,8 @@ func clearFrameState(f *cdp.Frame, fs cdp.FrameState) {
|
||||||
f.State &^= fs
|
f.State &^= fs
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeOp is a node manipulation operation.
|
// nodeOp is a node manipulation operation.
|
||||||
type NodeOp func(*cdp.Node)
|
type nodeOp func(*cdp.Node)
|
||||||
|
|
||||||
func walk(m map[cdp.NodeID]*cdp.Node, n *cdp.Node) {
|
func walk(m map[cdp.NodeID]*cdp.Node, n *cdp.Node) {
|
||||||
m[n.NodeID] = n
|
m[n.NodeID] = n
|
||||||
|
@ -117,14 +193,14 @@ func walk(m map[cdp.NodeID]*cdp.Node, n *cdp.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setChildNodes(m map[cdp.NodeID]*cdp.Node, nodes []*cdp.Node) NodeOp {
|
func setChildNodes(m map[cdp.NodeID]*cdp.Node, nodes []*cdp.Node) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.Children = nodes
|
n.Children = nodes
|
||||||
walk(m, n)
|
walk(m, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func attributeModified(name, value string) NodeOp {
|
func attributeModified(name, value string) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
var found bool
|
var found bool
|
||||||
|
|
||||||
|
@ -145,7 +221,7 @@ func attributeModified(name, value string) NodeOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func attributeRemoved(name string) NodeOp {
|
func attributeRemoved(name string) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
var a []string
|
var a []string
|
||||||
for i := 0; i < len(n.Attributes); i += 2 {
|
for i := 0; i < len(n.Attributes); i += 2 {
|
||||||
|
@ -158,66 +234,66 @@ func attributeRemoved(name string) NodeOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func inlineStyleInvalidated(ids []cdp.NodeID) NodeOp {
|
func inlineStyleInvalidated(ids []cdp.NodeID) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func characterDataModified(characterData string) NodeOp {
|
func characterDataModified(characterData string) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.Value = characterData
|
n.Value = characterData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func childNodeCountUpdated(count int64) NodeOp {
|
func childNodeCountUpdated(count int64) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.ChildNodeCount = count
|
n.ChildNodeCount = count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func childNodeInserted(m map[cdp.NodeID]*cdp.Node, prevID cdp.NodeID, c *cdp.Node) NodeOp {
|
func childNodeInserted(m map[cdp.NodeID]*cdp.Node, prevID cdp.NodeID, c *cdp.Node) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.Children = insertNode(n.Children, prevID, c)
|
n.Children = insertNode(n.Children, prevID, c)
|
||||||
walk(m, n)
|
walk(m, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func childNodeRemoved(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) NodeOp {
|
func childNodeRemoved(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.Children = removeNode(n.Children, id)
|
n.Children = removeNode(n.Children, id)
|
||||||
//delete(m, id)
|
//delete(m, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shadowRootPushed(m map[cdp.NodeID]*cdp.Node, c *cdp.Node) NodeOp {
|
func shadowRootPushed(m map[cdp.NodeID]*cdp.Node, c *cdp.Node) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.ShadowRoots = append(n.ShadowRoots, c)
|
n.ShadowRoots = append(n.ShadowRoots, c)
|
||||||
walk(m, n)
|
walk(m, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shadowRootPopped(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) NodeOp {
|
func shadowRootPopped(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.ShadowRoots = removeNode(n.ShadowRoots, id)
|
n.ShadowRoots = removeNode(n.ShadowRoots, id)
|
||||||
//delete(m, id)
|
//delete(m, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pseudoElementAdded(m map[cdp.NodeID]*cdp.Node, c *cdp.Node) NodeOp {
|
func pseudoElementAdded(m map[cdp.NodeID]*cdp.Node, c *cdp.Node) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.PseudoElements = append(n.PseudoElements, c)
|
n.PseudoElements = append(n.PseudoElements, c)
|
||||||
walk(m, n)
|
walk(m, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pseudoElementRemoved(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) NodeOp {
|
func pseudoElementRemoved(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.PseudoElements = removeNode(n.PseudoElements, id)
|
n.PseudoElements = removeNode(n.PseudoElements, id)
|
||||||
//delete(m, id)
|
//delete(m, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func distributedNodesUpdated(nodes []*cdp.BackendNode) NodeOp {
|
func distributedNodesUpdated(nodes []*cdp.BackendNode) nodeOp {
|
||||||
return func(n *cdp.Node) {
|
return func(n *cdp.Node) {
|
||||||
n.DistributedNodes = nodes
|
n.DistributedNodes = nodes
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user