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), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										225
									
								
								query.go
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								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) { |  | ||||||
| 				s += a[i].textContent; |  | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	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 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 false; |  | ||||||
| 	})($x('%s'))` |  | ||||||
| 
 | 
 | ||||||
| 	// valueJS is a javascript snippet that returns the value of a specified | 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||||
| 	// node. | 		if len(nodes) < 1 { | ||||||
| 	valueJS = `(function(a) { | 			return fmt.Errorf("selector `%s` did not return any nodes", sel) | ||||||
| 		return a[0].value; | 		} | ||||||
| 	})($x('%s'))` |  | ||||||
| 
 | 
 | ||||||
| 	// setValueJS is a javascript snippet that sets the value of the specified | 		var err error | ||||||
| 	// node, and returns the value. | 		ret := &css.GetMatchedStylesForNodeReturns{} | ||||||
| 	setValueJS = `(function(a, val) { | 		ret.InlineStyle, ret.AttributesStyle, ret.MatchedCSSRules, | ||||||
| 		return a[0].value = val; | 			ret.PseudoElements, ret.Inherited, ret.CSSKeyframesRules, | ||||||
| 	})($x('%s'), '%s')` | 			err = css.GetMatchedStylesForNode(nodes[0].NodeID).Do(ctxt, h) | ||||||
| ) | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| /* | 		*style = ret | ||||||
| 
 | 
 | ||||||
| Title | 		return nil | ||||||
| SetTitle | 	}, opts...) | ||||||
| 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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user