github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cdp/input/quad.go (about) 1 package input 2 3 import ( 4 "context" 5 "math" 6 7 "github.com/mafredri/cdp" 8 "github.com/mafredri/cdp/protocol/dom" 9 "github.com/mafredri/cdp/protocol/runtime" 10 "github.com/pkg/errors" 11 12 "github.com/MontFerret/ferret/pkg/drivers/cdp/utils" 13 ) 14 15 type Quad struct { 16 X float64 17 Y float64 18 } 19 20 func fromProtocolQuad(quad dom.Quad) []Quad { 21 return []Quad{ 22 { 23 X: quad[0], 24 Y: quad[1], 25 }, 26 { 27 X: quad[2], 28 Y: quad[3], 29 }, 30 { 31 X: quad[4], 32 Y: quad[5], 33 }, 34 { 35 X: quad[6], 36 Y: quad[7], 37 }, 38 } 39 } 40 41 func computeQuadArea(quads []Quad) float64 { 42 var area float64 43 44 for i := range quads { 45 p1 := quads[i] 46 p2 := quads[(i+1)%len(quads)] 47 area += (p1.X*p2.Y - p2.X*p1.Y) / 2 48 } 49 50 return math.Abs(area) 51 } 52 53 func intersectQuadWithViewport(quad []Quad, width, height float64) []Quad { 54 quads := make([]Quad, 0, len(quad)) 55 56 for _, point := range quad { 57 quads = append(quads, Quad{ 58 X: math.Min(math.Max(point.X, 0), width), 59 Y: math.Min(math.Max(point.Y, 0), height), 60 }) 61 } 62 63 return quads 64 } 65 66 func getClickablePoint(ctx context.Context, client *cdp.Client, qargs *dom.GetContentQuadsArgs) (Quad, error) { 67 contentQuadsReply, err := client.DOM.GetContentQuads(ctx, qargs) 68 69 if err != nil { 70 return Quad{}, err 71 } 72 73 if contentQuadsReply.Quads == nil || len(contentQuadsReply.Quads) == 0 { 74 return Quad{}, errors.New("node is either not visible or not an HTMLElement") 75 } 76 77 layoutMetricsReply, err := client.Page.GetLayoutMetrics(ctx) 78 79 if err != nil { 80 return Quad{}, err 81 } 82 83 clientWidth, clientHeight := utils.GetLayoutViewportWH(layoutMetricsReply) 84 quads := make([][]Quad, 0, len(contentQuadsReply.Quads)) 85 86 for _, q := range contentQuadsReply.Quads { 87 quad := intersectQuadWithViewport(fromProtocolQuad(q), float64(clientWidth), float64(clientHeight)) 88 89 if computeQuadArea(quad) > 1 { 90 quads = append(quads, quad) 91 } 92 } 93 94 if len(quads) == 0 { 95 return Quad{}, errors.New("node is either not visible or not an HTMLElement") 96 } 97 98 // Return the middle point of the first quad. 99 quad := quads[0] 100 var x float64 101 var y float64 102 103 for _, q := range quad { 104 x += q.X 105 y += q.Y 106 } 107 108 return Quad{ 109 X: x / 4, 110 Y: y / 4, 111 }, nil 112 } 113 114 func GetClickablePointByObjectID(ctx context.Context, client *cdp.Client, objectID runtime.RemoteObjectID) (Quad, error) { 115 return getClickablePoint(ctx, client, dom.NewGetContentQuadsArgs().SetObjectID(objectID)) 116 }