mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-25 01:32:13 +02:00
7ce6797e9c
* Implemented XPath for CDP driver * Added XPATH function * Added e2e tests for CDP * Fixed linting issues * Added support of XPath to HTTP driver * Fixed linting issues
125 lines
2.7 KiB
Go
125 lines
2.7 KiB
Go
package input
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
|
|
"github.com/mafredri/cdp"
|
|
"github.com/mafredri/cdp/protocol/dom"
|
|
"github.com/mafredri/cdp/protocol/runtime"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type Quad struct {
|
|
X float64
|
|
Y float64
|
|
}
|
|
|
|
func fromProtocolQuad(quad dom.Quad) []Quad {
|
|
return []Quad{
|
|
{
|
|
X: quad[0],
|
|
Y: quad[1],
|
|
},
|
|
{
|
|
X: quad[2],
|
|
Y: quad[3],
|
|
},
|
|
{
|
|
X: quad[4],
|
|
Y: quad[5],
|
|
},
|
|
{
|
|
X: quad[6],
|
|
Y: quad[7],
|
|
},
|
|
}
|
|
}
|
|
|
|
func computeQuadArea(quads []Quad) float64 {
|
|
var area float64
|
|
|
|
for i := range quads {
|
|
p1 := quads[i]
|
|
p2 := quads[(i+1)%len(quads)]
|
|
area += (p1.X*p2.Y - p2.X*p1.Y) / 2
|
|
}
|
|
|
|
return math.Abs(area)
|
|
}
|
|
|
|
func intersectQuadWithViewport(quad []Quad, width, height float64) []Quad {
|
|
quads := make([]Quad, 0, len(quad))
|
|
|
|
for _, point := range quad {
|
|
quads = append(quads, Quad{
|
|
X: math.Min(math.Max(point.X, 0), width),
|
|
Y: math.Min(math.Max(point.Y, 0), height),
|
|
})
|
|
}
|
|
|
|
return quads
|
|
}
|
|
|
|
func getClickablePoint(ctx context.Context, client *cdp.Client, qargs *dom.GetContentQuadsArgs) (Quad, error) {
|
|
contentQuadsReply, err := client.DOM.GetContentQuads(ctx, qargs)
|
|
|
|
if err != nil {
|
|
return Quad{}, err
|
|
}
|
|
|
|
if contentQuadsReply.Quads == nil || len(contentQuadsReply.Quads) == 0 {
|
|
return Quad{}, errors.New("node is either not visible or not an HTMLElement")
|
|
}
|
|
|
|
layoutMetricsReply, err := client.Page.GetLayoutMetrics(ctx)
|
|
|
|
if err != nil {
|
|
return Quad{}, err
|
|
}
|
|
|
|
clientWidth := layoutMetricsReply.LayoutViewport.ClientWidth
|
|
clientHeight := layoutMetricsReply.LayoutViewport.ClientHeight
|
|
|
|
quads := make([][]Quad, 0, len(contentQuadsReply.Quads))
|
|
|
|
for _, q := range contentQuadsReply.Quads {
|
|
quad := intersectQuadWithViewport(fromProtocolQuad(q), float64(clientWidth), float64(clientHeight))
|
|
|
|
if computeQuadArea(quad) > 1 {
|
|
quads = append(quads, quad)
|
|
}
|
|
}
|
|
|
|
if len(quads) == 0 {
|
|
return Quad{}, errors.New("node is either not visible or not an HTMLElement")
|
|
}
|
|
|
|
// Return the middle point of the first quad.
|
|
quad := quads[0]
|
|
var x float64
|
|
var y float64
|
|
|
|
for _, q := range quad {
|
|
x += q.X
|
|
y += q.Y
|
|
}
|
|
|
|
return Quad{
|
|
X: x / 4,
|
|
Y: y / 4,
|
|
}, nil
|
|
}
|
|
|
|
func GetClickablePointByNodeID(ctx context.Context, client *cdp.Client, nodeID dom.NodeID) (Quad, error) {
|
|
return getClickablePoint(ctx, client, dom.NewGetContentQuadsArgs().SetNodeID(nodeID))
|
|
}
|
|
|
|
func GetClickablePointByObjectID(ctx context.Context, client *cdp.Client, objectID runtime.RemoteObjectID) (Quad, error) {
|
|
return getClickablePoint(ctx, client, dom.NewGetContentQuadsArgs().SetObjectID(objectID))
|
|
}
|
|
|
|
func GetClickablePointByBackendID(ctx context.Context, client *cdp.Client, backendID dom.BackendNodeID) (Quad, error) {
|
|
return getClickablePoint(ctx, client, dom.NewGetContentQuadsArgs().SetBackendNodeID(backendID))
|
|
}
|