chromedp/util.go

396 lines
8.0 KiB
Go
Raw Normal View History

2017-01-24 15:09:23 +00:00
package chromedp
import (
"time"
2017-01-26 07:28:34 +00:00
"github.com/knq/chromedp/cdp"
2017-01-24 15:09:23 +00:00
)
const (
// DefaultNewTargetTimeout is the default time to wait for a new target to
// be started.
DefaultNewTargetTimeout = 3 * time.Second
// 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
// 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)`
// scrollIntoViewJS is a javascript snippet that scrolls the specified node
// into the window's viewport (if needed), returning the actual window x/y
// after execution.
scrollIntoViewJS = `(function(a) {
a[0].scrollIntoViewIfNeeded(true);
return [window.scrollX, window.scrollY];
})($x('%s'))`
// 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'))`
2017-07-01 04:53:22 +00:00
// attributeJS is a javascript snippet that returns the attribute of a specified
// node.
2017-07-01 04:53:22 +00:00
attributeJS = `(function(a, n) {
return a[0][n];
})($x('%s'), '%s')`
2017-07-01 04:53:22 +00:00
// setAttributeJS is a javascript snippet that sets the value of the specified
// node, and returns the value.
2017-07-01 04:53:22 +00:00
setAttributeJS = `(function(a, n, v) {
return a[0][n] = v;
})($x('%s'), '%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) {
2017-07-01 04:53:22 +00:00
return a[0].offsetParent !== null;
})($x('%s'))`
2017-01-24 15:09:23 +00:00
)
// frameOp is a frame manipulation operation.
type frameOp func(*cdp.Frame)
2017-01-24 15:09:23 +00:00
2017-01-26 07:28:34 +00:00
/*func domContentEventFired(f *cdp.Frame) {
2017-01-24 15:09:23 +00:00
}
2017-01-26 07:28:34 +00:00
func loadEventFired(f *cdp.Frame) {
2017-01-24 15:09:23 +00:00
}*/
func frameAttached(id cdp.FrameID) frameOp {
2017-01-26 07:28:34 +00:00
return func(f *cdp.Frame) {
2017-01-24 15:09:23 +00:00
f.ParentID = id
2017-01-26 07:28:34 +00:00
setFrameState(f, cdp.FrameAttached)
2017-01-24 15:09:23 +00:00
}
}
2017-01-26 07:28:34 +00:00
/*func frameNavigated(f *cdp.Frame) {
setFrameState(f, cdp.FrameNavigated)
2017-01-24 15:09:23 +00:00
}*/
2017-01-26 07:28:34 +00:00
func frameDetached(f *cdp.Frame) {
f.ParentID = cdp.EmptyFrameID
2017-01-26 07:28:34 +00:00
clearFrameState(f, cdp.FrameAttached)
2017-01-24 15:09:23 +00:00
}
2017-01-26 07:28:34 +00:00
func frameStartedLoading(f *cdp.Frame) {
setFrameState(f, cdp.FrameLoading)
2017-01-24 15:09:23 +00:00
}
2017-01-26 07:28:34 +00:00
func frameStoppedLoading(f *cdp.Frame) {
clearFrameState(f, cdp.FrameLoading)
2017-01-24 15:09:23 +00:00
}
2017-01-26 07:28:34 +00:00
func frameScheduledNavigation(f *cdp.Frame) {
setFrameState(f, cdp.FrameScheduledNavigation)
2017-01-24 15:09:23 +00:00
}
2017-01-26 07:28:34 +00:00
func frameClearedScheduledNavigation(f *cdp.Frame) {
clearFrameState(f, cdp.FrameScheduledNavigation)
2017-01-24 15:09:23 +00:00
}
2017-01-26 07:28:34 +00:00
/*func frameResized(f *cdp.Frame) {
2017-01-24 15:09:23 +00:00
// TODO
}*/
// setFrameState sets the frame state via bitwise or (|).
2017-01-26 07:28:34 +00:00
func setFrameState(f *cdp.Frame, fs cdp.FrameState) {
2017-01-24 15:09:23 +00:00
f.State |= fs
}
// clearFrameState clears the frame state via bit clear (&^).
2017-01-26 07:28:34 +00:00
func clearFrameState(f *cdp.Frame, fs cdp.FrameState) {
2017-01-24 15:09:23 +00:00
f.State &^= fs
}
// nodeOp is a node manipulation operation.
type nodeOp func(*cdp.Node)
2017-01-24 15:09:23 +00:00
2017-01-26 07:28:34 +00:00
func walk(m map[cdp.NodeID]*cdp.Node, n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.RLock()
defer n.RUnlock()
2017-01-24 15:09:23 +00:00
m[n.NodeID] = n
for _, c := range n.Children {
2017-02-18 07:28:43 +00:00
c.Lock()
2017-01-24 15:09:23 +00:00
c.Parent = n
c.Invalidated = n.Invalidated
2017-02-18 07:28:43 +00:00
c.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, c)
}
for _, c := range n.ShadowRoots {
2017-02-18 07:28:43 +00:00
c.Lock()
2017-01-24 15:09:23 +00:00
c.Parent = n
c.Invalidated = n.Invalidated
2017-02-18 07:28:43 +00:00
c.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, c)
}
for _, c := range n.PseudoElements {
2017-02-18 07:28:43 +00:00
c.Lock()
2017-01-24 15:09:23 +00:00
c.Parent = n
c.Invalidated = n.Invalidated
2017-02-18 07:28:43 +00:00
c.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, c)
}
2017-01-26 07:28:34 +00:00
for _, c := range []*cdp.Node{n.ContentDocument, n.TemplateContent, n.ImportedDocument} {
2017-01-24 15:09:23 +00:00
if c == nil {
continue
}
2017-02-18 07:28:43 +00:00
c.Lock()
2017-01-24 15:09:23 +00:00
c.Parent = n
c.Invalidated = n.Invalidated
2017-02-18 07:28:43 +00:00
c.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, c)
}
}
func setChildNodes(m map[cdp.NodeID]*cdp.Node, nodes []*cdp.Node) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
2017-01-24 15:09:23 +00:00
n.Children = nodes
2017-02-18 07:28:43 +00:00
n.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, n)
}
}
func attributeModified(name, value string) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
2017-02-18 07:28:43 +00:00
var found bool
var i int
2017-01-24 15:09:23 +00:00
for ; i < len(n.Attributes); i += 2 {
if n.Attributes[i] == name {
found = true
break
}
}
if found {
n.Attributes[i] = name
n.Attributes[i+1] = value
} else {
n.Attributes = append(n.Attributes, name, value)
}
}
}
func attributeRemoved(name string) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
var a []string
for i := 0; i < len(n.Attributes); i += 2 {
if n.Attributes[i] == name {
continue
}
a = append(a, n.Attributes[i], n.Attributes[i+1])
}
n.Attributes = a
}
}
func inlineStyleInvalidated(ids []cdp.NodeID) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-01-24 15:09:23 +00:00
}
}
func characterDataModified(characterData string) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
n.Value = characterData
}
}
func childNodeCountUpdated(count int64) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
n.ChildNodeCount = count
}
}
func childNodeInserted(m map[cdp.NodeID]*cdp.Node, prevID cdp.NodeID, c *cdp.Node) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
2017-01-24 15:09:23 +00:00
n.Children = insertNode(n.Children, prevID, c)
2017-02-18 07:28:43 +00:00
n.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, n)
}
}
func childNodeRemoved(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
n.Children = removeNode(n.Children, id)
2017-02-18 07:28:43 +00:00
delete(m, id)
2017-01-24 15:09:23 +00:00
}
}
func shadowRootPushed(m map[cdp.NodeID]*cdp.Node, c *cdp.Node) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
2017-01-24 15:09:23 +00:00
n.ShadowRoots = append(n.ShadowRoots, c)
2017-02-18 07:28:43 +00:00
n.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, n)
}
}
func shadowRootPopped(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
n.ShadowRoots = removeNode(n.ShadowRoots, id)
2017-02-18 07:28:43 +00:00
delete(m, id)
2017-01-24 15:09:23 +00:00
}
}
func pseudoElementAdded(m map[cdp.NodeID]*cdp.Node, c *cdp.Node) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
2017-01-24 15:09:23 +00:00
n.PseudoElements = append(n.PseudoElements, c)
2017-02-18 07:28:43 +00:00
n.Unlock()
2017-01-24 15:09:23 +00:00
walk(m, n)
}
}
func pseudoElementRemoved(m map[cdp.NodeID]*cdp.Node, id cdp.NodeID) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
n.PseudoElements = removeNode(n.PseudoElements, id)
2017-02-18 07:28:43 +00:00
delete(m, id)
2017-01-24 15:09:23 +00:00
}
}
func distributedNodesUpdated(nodes []*cdp.BackendNode) nodeOp {
2017-01-26 07:28:34 +00:00
return func(n *cdp.Node) {
2017-02-18 07:28:43 +00:00
n.Lock()
defer n.Unlock()
2017-01-24 15:09:23 +00:00
n.DistributedNodes = nodes
}
}
2017-01-26 07:28:34 +00:00
func insertNode(n []*cdp.Node, prevID cdp.NodeID, c *cdp.Node) []*cdp.Node {
var i int
var found bool
2017-01-24 15:09:23 +00:00
for ; i < len(n); i++ {
if n[i].NodeID == prevID {
found = true
break
}
}
if !found {
return append(n, c)
}
i++
n = append(n, nil)
copy(n[i+1:], n[i:])
n[i] = c
return n
}
2017-01-26 07:28:34 +00:00
func removeNode(n []*cdp.Node, id cdp.NodeID) []*cdp.Node {
2017-01-24 15:09:23 +00:00
if len(n) == 0 {
return n
}
var found bool
var i int
2017-01-24 15:09:23 +00:00
for ; i < len(n); i++ {
if n[i].NodeID == id {
found = true
break
}
}
if !found {
return n
}
return append(n[:i], n[i+1:]...)
}
// isCouldNotComputeBoxModelError unwraps err as a MessageError and determines
// if it is a compute box model error.
func isCouldNotComputeBoxModelError(err error) bool {
e, ok := err.(*cdp.MessageError)
return ok && e.Code == -32000 && e.Message == "Could not compute box model."
}