mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-21 01:21:34 +02:00
125 lines
2.7 KiB
Go
125 lines
2.7 KiB
Go
|
package input
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"github.com/mafredri/cdp/protocol/runtime"
|
||
|
"math"
|
||
|
|
||
|
"github.com/mafredri/cdp"
|
||
|
"github.com/mafredri/cdp/protocol/dom"
|
||
|
"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))
|
||
|
}
|