2017-01-24 15:09:23 +00:00
|
|
|
package chromedp
|
|
|
|
|
|
|
|
import (
|
2019-03-05 13:14:50 +00:00
|
|
|
"time"
|
|
|
|
|
2017-12-27 02:30:28 +00:00
|
|
|
"github.com/chromedp/cdproto"
|
|
|
|
"github.com/chromedp/cdproto/cdp"
|
2017-01-24 15:09:23 +00:00
|
|
|
)
|
|
|
|
|
2019-03-05 13:14:50 +00:00
|
|
|
const (
|
|
|
|
// DefaultCheckDuration is the default time to sleep between a check.
|
|
|
|
DefaultCheckDuration = 50 * time.Millisecond
|
|
|
|
)
|
|
|
|
|
2017-02-12 07:08:40 +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
|
|
|
}*/
|
|
|
|
|
2017-02-12 07:08:40 +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) {
|
2017-02-18 08:36:24 +00:00
|
|
|
f.ParentID = cdp.EmptyFrameID
|
2017-01-26 07:28:34 +00:00
|
|
|
clearFrameState(f, cdp.FrameAttached)
|
2017-01-24 15:09:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-28 21:21:52 +00:00
|
|
|
func frameStartedLoading(f *cdp.Frame) {
|
2017-01-26 07:28:34 +00:00
|
|
|
setFrameState(f, cdp.FrameLoading)
|
2019-03-28 21:21:52 +00:00
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 07:08:40 +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 {
|
2017-02-18 08:36:24 +00:00
|
|
|
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
|
2017-02-18 08:36:24 +00:00
|
|
|
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:]...)
|
|
|
|
}
|
2017-02-08 16:01:35 +00:00
|
|
|
|
2017-02-14 08:41:23 +00:00
|
|
|
// isCouldNotComputeBoxModelError unwraps err as a MessageError and determines
|
|
|
|
// if it is a compute box model error.
|
2017-02-08 16:01:35 +00:00
|
|
|
func isCouldNotComputeBoxModelError(err error) bool {
|
2017-12-27 02:30:28 +00:00
|
|
|
e, ok := err.(*cdproto.Error)
|
2017-02-08 16:01:35 +00:00
|
|
|
return ok && e.Code == -32000 && e.Message == "Could not compute box model."
|
|
|
|
}
|