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 { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	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{ | ||||
| 		cdp.Navigate(`https://golang.org/pkg/time/`), | ||||
| 		cdp.WaitVisible(`#footer`), | ||||
| 		cdp.Click(`#pkg-overview`, cdp.ElementVisible), | ||||
| 		cdp.Click(`#pkg-overview`, cdp.NodeVisible), | ||||
| 		cdp.Sleep(150 * time.Second), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -45,6 +45,6 @@ func main() { | ||||
| func text(res *string) cdp.Tasks { | ||||
| 	return cdp.Tasks{ | ||||
| 		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() | ||||
| 
 | ||||
| 	var id cdp.FrameID | ||||
| 	var op FrameOp | ||||
| 	var op frameOp | ||||
| 
 | ||||
| 	switch e := ev.(type) { | ||||
| 	case *page.EventFrameNavigated: | ||||
| @ -569,7 +569,7 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) { | ||||
| 	} | ||||
| 
 | ||||
| 	var id cdp.NodeID | ||||
| 	var op NodeOp | ||||
| 	var op nodeOp | ||||
| 
 | ||||
| 	switch e := ev.(type) { | ||||
| 	case *dom.EventSetChildNodes: | ||||
|  | ||||
							
								
								
									
										6
									
								
								input.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								input.go
									
									
									
									
									
								
							| @ -2,7 +2,6 @@ package chromedp | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/knq/chromedp/cdp" | ||||
| @ -11,11 +10,6 @@ import ( | ||||
| 	"github.com/knq/chromedp/kb" | ||||
| ) | ||||
| 
 | ||||
| // Error types. | ||||
| var ( | ||||
| 	ErrInvalidDimensions = errors.New("invalid box dimensions") | ||||
| ) | ||||
| 
 | ||||
| // MouseAction is a mouse action. | ||||
| func MouseAction(typ input.MouseType, x, y int64, opts ...MouseOption) Action { | ||||
| 	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) | ||||
| } | ||||
| 
 | ||||
| // 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. | ||||
| func CaptureScreenshot(res *[]byte) Action { | ||||
| 	if res == nil { | ||||
| @ -113,16 +123,20 @@ func RemoveOnLoadScript(id page.ScriptIdentifier) Action { | ||||
| 	return page.RemoveScriptToEvaluateOnLoad(id) | ||||
| } | ||||
| 
 | ||||
| // Stop stops all navigation and pending resource retrieval. | ||||
| func Stop() Action { | ||||
| 	return page.StopLoading() | ||||
| } | ||||
| 
 | ||||
| // Location retrieves the URL location. | ||||
| // Location retrieves the document location. | ||||
| func Location(urlstr *string) Action { | ||||
| 	if urlstr == 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" | ||||
| ) | ||||
| 
 | ||||
| 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. | ||||
| type Pool struct { | ||||
| 	// start is the start port. | ||||
| @ -35,8 +27,8 @@ func NewPool(opts ...PoolOption) (*Pool, error) { | ||||
| 	var err error | ||||
| 
 | ||||
| 	p := &Pool{ | ||||
| 		start: DefaultStartPort, | ||||
| 		end:   DefaultEndPort, | ||||
| 		start: DefaultPoolStartPort, | ||||
| 		end:   DefaultPoolEndPort, | ||||
| 		res:   make(map[int]*Res), | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										225
									
								
								query.go
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								query.go
									
									
									
									
									
								
							| @ -12,17 +12,12 @@ import ( | ||||
| 
 | ||||
| 	"github.com/disintegration/imaging" | ||||
| 	"github.com/knq/chromedp/cdp" | ||||
| 	"github.com/knq/chromedp/cdp/css" | ||||
| 	"github.com/knq/chromedp/cdp/dom" | ||||
| 	"github.com/knq/chromedp/cdp/page" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// 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. | ||||
| // Nodes retrieves the document nodes matching the selector. | ||||
| func Nodes(sel interface{}, nodes *[]*cdp.Node, opts ...QueryOption) Action { | ||||
| 	if nodes == nil { | ||||
| 		panic("nodes cannot be nil") | ||||
| @ -34,7 +29,7 @@ func Nodes(sel interface{}, nodes *[]*cdp.Node, opts ...QueryOption) Action { | ||||
| 	}, 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 { | ||||
| 	if ids == nil { | ||||
| 		panic("nodes cannot be nil") | ||||
| @ -52,7 +47,7 @@ func NodeIDs(sel interface{}, ids *[]cdp.NodeID, opts ...QueryOption) Action { | ||||
| 	}, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -63,7 +58,7 @@ func Focus(sel interface{}, opts ...QueryOption) Action { | ||||
| 	}, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -75,14 +70,32 @@ func Blur(sel interface{}, opts ...QueryOption) Action { | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		if !res { | ||||
| 			return fmt.Errorf("could not blur node %d", nodes[0].NodeID) | ||||
| 		} | ||||
| 
 | ||||
| 		return nil | ||||
| 	}, 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 { | ||||
| 	if text == nil { | ||||
| 		panic("text cannot be nil") | ||||
| @ -97,7 +110,7 @@ func Text(sel interface{}, text *string, opts ...QueryOption) Action { | ||||
| 	}, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -138,23 +151,7 @@ func Clear(sel interface{}, opts ...QueryOption) Action { | ||||
| 	}, opts...) | ||||
| } | ||||
| 
 | ||||
| // Dimensions retrieves the box model dimensions for the first node matching | ||||
| // 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. | ||||
| // Value retrieves the value of the first node matching the selector. | ||||
| func Value(sel interface{}, value *string, opts ...QueryOption) Action { | ||||
| 	if value == nil { | ||||
| 		panic("value cannot be nil") | ||||
| @ -189,7 +186,7 @@ func SetValue(sel interface{}, value string, opts ...QueryOption) Action { | ||||
| 	}, 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 { | ||||
| 	if attributes == nil { | ||||
| 		panic("attributes cannot be nil") | ||||
| @ -215,19 +212,26 @@ func Attributes(sel interface{}, attributes *map[string]string, opts ...QueryOpt | ||||
| 	}, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| 			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...) | ||||
| } | ||||
| 
 | ||||
| // AttributeValue retrieves the name'd attribute value for the specified | ||||
| // element. | ||||
| // AttributeValue retrieves the attribute value for the first node matching the | ||||
| // selector. | ||||
| func AttributeValue(sel interface{}, name string, value *string, ok *bool, opts ...QueryOption) Action { | ||||
| 	if value == nil { | ||||
| 		panic("value cannot be nil") | ||||
| @ -260,7 +264,8 @@ func AttributeValue(sel interface{}, name string, value *string, ok *bool, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -271,7 +276,8 @@ func SetAttributeValue(sel interface{}, name, value string, opts ...QueryOption) | ||||
| 	}, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -282,7 +288,7 @@ func RemoveAttribute(sel interface{}, name string, opts ...QueryOption) Action { | ||||
| 	}, 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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -290,10 +296,11 @@ func Click(sel interface{}, opts ...QueryOption) Action { | ||||
| 		} | ||||
| 
 | ||||
| 		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 { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -301,34 +308,21 @@ func DoubleClick(sel interface{}, opts ...QueryOption) Action { | ||||
| 		} | ||||
| 
 | ||||
| 		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. | ||||
| // | ||||
| // 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. | ||||
| // 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. | ||||
| func SendKeys(sel interface{}, v string, 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 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 { | ||||
| 	if picbuf == nil { | ||||
| 		panic("picbuf cannot be nil") | ||||
| @ -387,11 +381,11 @@ func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) Action { | ||||
| 		*picbuf = croppedBuf.Bytes() | ||||
| 
 | ||||
| 		return nil | ||||
| 	}, append(opts, ElementVisible)...) | ||||
| 	}, append(opts, NodeVisible)...) | ||||
| } | ||||
| 
 | ||||
| // Submit is an action that submits whatever form the first element matching | ||||
| // the selector belongs to. | ||||
| // Submit is an action that submits the form of the first node matching the | ||||
| // selector belongs to. | ||||
| func Submit(sel interface{}, opts ...QueryOption) Action { | ||||
| 	return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error { | ||||
| 		if len(nodes) < 1 { | ||||
| @ -412,7 +406,7 @@ func Submit(sel interface{}, opts ...QueryOption) Action { | ||||
| 	}, 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. | ||||
| func Reset(sel interface{}, opts ...QueryOption) Action { | ||||
| 	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...) | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	// 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; | ||||
| // ComputedStyle retrieves the computed style of the first node matching the selector. | ||||
| func ComputedStyle(sel interface{}, style *[]*css.ComputedProperty, 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 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; | ||||
| 		computed, err := css.GetComputedStyleForNode(nodes[0].NodeID).Do(ctxt, h) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		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; | ||||
| 		*style = computed | ||||
| 
 | ||||
| 		return nil | ||||
| 	}, opts...) | ||||
| } | ||||
| 
 | ||||
| // MatchedStyle retrieves the matched style information for the first node | ||||
| // matching the selector. | ||||
| 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 | ||||
| 	// node. | ||||
| 	valueJS = `(function(a) { | ||||
| 		return a[0].value; | ||||
| 	})($x('%s'))` | ||||
| 	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) | ||||
| 		} | ||||
| 
 | ||||
| 	// 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')` | ||||
| ) | ||||
| 		var err error | ||||
| 		ret := &css.GetMatchedStylesForNodeReturns{} | ||||
| 		ret.InlineStyle, ret.AttributesStyle, ret.MatchedCSSRules, | ||||
| 			ret.PseudoElements, ret.Inherited, ret.CSSKeyframesRules, | ||||
| 			err = css.GetMatchedStylesForNode(nodes[0].NodeID).Do(ctxt, h) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| /* | ||||
| 		*style = ret | ||||
| 
 | ||||
| Title | ||||
| SetTitle | ||||
| OuterHTML | ||||
| SetOuterHTML | ||||
| 
 | ||||
| NodeName -- ? | ||||
| 
 | ||||
| Style(Matched) | ||||
| Style(Computed) | ||||
| SetStyle | ||||
| GetStyle(Inline) | ||||
| 
 | ||||
| */ | ||||
| 		return nil | ||||
| 	}, opts...) | ||||
| } | ||||
|  | ||||
							
								
								
									
										153
									
								
								sel.go
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								sel.go
									
									
									
									
									
								
							| @ -2,14 +2,12 @@ package chromedp | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/knq/chromedp/cdp" | ||||
| 	"github.com/knq/chromedp/cdp/css" | ||||
| 	"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. | ||||
| type Selector struct { | ||||
| 	sel   interface{} | ||||
| @ -61,7 +49,7 @@ func Query(sel interface{}, opts ...QueryOption) Action { | ||||
| 	} | ||||
| 
 | ||||
| 	if s.wait == nil { | ||||
| 		ElementReady(s) | ||||
| 		NodeReady(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. | ||||
| func ElementReady(s *Selector) { | ||||
| // NodeReady is a query option to wait until the element is ready. | ||||
| func NodeReady(s *Selector) { | ||||
| 	WaitFunc(s.waitReady(nil))(s) | ||||
| } | ||||
| 
 | ||||
| // ElementVisible is a query option to wait until the element is visible. | ||||
| func ElementVisible(s *Selector) { | ||||
| // NodeVisible is a query option to wait until the element is visible. | ||||
| func NodeVisible(s *Selector) { | ||||
| 	WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error { | ||||
| 		var err error | ||||
| 
 | ||||
| @ -327,8 +315,8 @@ func ElementVisible(s *Selector) { | ||||
| 	}))(s) | ||||
| } | ||||
| 
 | ||||
| // ElementNotVisible is a query option to wait until the element is not visible. | ||||
| func ElementNotVisible(s *Selector) { | ||||
| // NodeNotVisible is a query option to wait until the element is not visible. | ||||
| func NodeNotVisible(s *Selector) { | ||||
| 	WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error { | ||||
| 		var err error | ||||
| 
 | ||||
| @ -355,105 +343,8 @@ func ElementNotVisible(s *Selector) { | ||||
| 	}))(s) | ||||
| } | ||||
| 
 | ||||
| // ElementVisibleOld is a query option to wait until the element is visible. | ||||
| // | ||||
| // 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) { | ||||
| // NodeEnabled is a query option to wait until the element is enabled. | ||||
| func NodeEnabled(s *Selector) { | ||||
| 	WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error { | ||||
| 		n.RLock() | ||||
| 		defer n.RUnlock() | ||||
| @ -468,8 +359,8 @@ func ElementEnabled(s *Selector) { | ||||
| 	}))(s) | ||||
| } | ||||
| 
 | ||||
| // ElementSelected is a query option to wait until the element is selected. | ||||
| func ElementSelected(s *Selector) { | ||||
| // NodeSelected is a query option to wait until the element is selected. | ||||
| func NodeSelected(s *Selector) { | ||||
| 	WaitFunc(s.waitReady(func(ctxt context.Context, h cdp.Handler, n *cdp.Node) error { | ||||
| 		n.RLock() | ||||
| 		defer n.RUnlock() | ||||
| @ -484,9 +375,9 @@ func ElementSelected(s *Selector) { | ||||
| 	}))(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. | ||||
| func ElementNotPresent(s *Selector) { | ||||
| func NodeNotPresent(s *Selector) { | ||||
| 	s.exp = 0 | ||||
| 	WaitFunc(func(ctxt context.Context, h cdp.Handler, n *cdp.Node, ids ...cdp.NodeID) ([]*cdp.Node, error) { | ||||
| 		if len(ids) != 0 { | ||||
| @ -519,34 +410,26 @@ func WaitReady(sel interface{}, opts ...QueryOption) Action { | ||||
| 
 | ||||
| // WaitVisible waits until the selected element is visible. | ||||
| 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. | ||||
| 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 | ||||
| // attribute 'disabled'). | ||||
| 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'). | ||||
| 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. | ||||
| 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 = 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 cdp.FrameID = cdp.FrameID("") | ||||
| 
 | ||||
| 	// EmptyNodeID is the "non-existent" node id. | ||||
| 	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. | ||||
| @ -27,8 +103,8 @@ func UnmarshalMessage(msg *cdp.Message) (interface{}, error) { | ||||
| 	return util.UnmarshalMessage(msg) | ||||
| } | ||||
| 
 | ||||
| // FrameOp is a frame manipulation operation. | ||||
| type FrameOp func(*cdp.Frame) | ||||
| // frameOp is a frame manipulation operation. | ||||
| type frameOp func(*cdp.Frame) | ||||
| 
 | ||||
| /*func domContentEventFired(f *cdp.Frame) { | ||||
| } | ||||
| @ -36,7 +112,7 @@ type FrameOp func(*cdp.Frame) | ||||
| func loadEventFired(f *cdp.Frame) { | ||||
| }*/ | ||||
| 
 | ||||
| func frameAttached(id cdp.FrameID) FrameOp { | ||||
| func frameAttached(id cdp.FrameID) frameOp { | ||||
| 	return func(f *cdp.Frame) { | ||||
| 		f.ParentID = id | ||||
| 		setFrameState(f, cdp.FrameAttached) | ||||
| @ -82,8 +158,8 @@ func clearFrameState(f *cdp.Frame, fs cdp.FrameState) { | ||||
| 	f.State &^= fs | ||||
| } | ||||
| 
 | ||||
| // NodeOp is a node manipulation operation. | ||||
| type NodeOp func(*cdp.Node) | ||||
| // nodeOp is a node manipulation operation. | ||||
| type nodeOp func(*cdp.Node) | ||||
| 
 | ||||
| func walk(m map[cdp.NodeID]*cdp.Node, n *cdp.Node) { | ||||
| 	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) { | ||||
| 		n.Children = nodes | ||||
| 		walk(m, n) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func attributeModified(name, value string) NodeOp { | ||||
| func attributeModified(name, value string) nodeOp { | ||||
| 	return func(n *cdp.Node) { | ||||
| 		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) { | ||||
| 		var a []string | ||||
| 		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) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func characterDataModified(characterData string) NodeOp { | ||||
| func characterDataModified(characterData string) nodeOp { | ||||
| 	return func(n *cdp.Node) { | ||||
| 		n.Value = characterData | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func childNodeCountUpdated(count int64) NodeOp { | ||||
| func childNodeCountUpdated(count int64) nodeOp { | ||||
| 	return func(n *cdp.Node) { | ||||
| 		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) { | ||||
| 		n.Children = insertNode(n.Children, prevID, c) | ||||
| 		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) { | ||||
| 		n.Children = removeNode(n.Children, 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) { | ||||
| 		n.ShadowRoots = append(n.ShadowRoots, c) | ||||
| 		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) { | ||||
| 		n.ShadowRoots = removeNode(n.ShadowRoots, 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) { | ||||
| 		n.PseudoElements = append(n.PseudoElements, c) | ||||
| 		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) { | ||||
| 		n.PseudoElements = removeNode(n.PseudoElements, id) | ||||
| 		//delete(m, id) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func distributedNodesUpdated(nodes []*cdp.BackendNode) NodeOp { | ||||
| func distributedNodesUpdated(nodes []*cdp.BackendNode) nodeOp { | ||||
| 	return func(n *cdp.Node) { | ||||
| 		n.DistributedNodes = nodes | ||||
| 	} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user