github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cdp/dom/document.go (about) 1 package dom 2 3 import ( 4 "context" 5 "hash/fnv" 6 7 "github.com/mafredri/cdp" 8 "github.com/mafredri/cdp/protocol/page" 9 "github.com/pkg/errors" 10 "github.com/rs/zerolog" 11 12 "github.com/MontFerret/ferret/pkg/drivers" 13 "github.com/MontFerret/ferret/pkg/drivers/cdp/eval" 14 "github.com/MontFerret/ferret/pkg/drivers/cdp/events" 15 "github.com/MontFerret/ferret/pkg/drivers/cdp/input" 16 "github.com/MontFerret/ferret/pkg/drivers/cdp/templates" 17 "github.com/MontFerret/ferret/pkg/drivers/common" 18 "github.com/MontFerret/ferret/pkg/runtime/core" 19 "github.com/MontFerret/ferret/pkg/runtime/logging" 20 "github.com/MontFerret/ferret/pkg/runtime/values" 21 ) 22 23 type HTMLDocument struct { 24 logger zerolog.Logger 25 client *cdp.Client 26 dom *Manager 27 input *input.Manager 28 eval *eval.Runtime 29 frameTree page.FrameTree 30 element *HTMLElement 31 } 32 33 func NewHTMLDocument( 34 logger zerolog.Logger, 35 client *cdp.Client, 36 domManager *Manager, 37 input *input.Manager, 38 exec *eval.Runtime, 39 rootElement *HTMLElement, 40 frames page.FrameTree, 41 ) *HTMLDocument { 42 doc := new(HTMLDocument) 43 doc.logger = logging.WithName(logger.With(), "html_document").Logger() 44 doc.client = client 45 doc.dom = domManager 46 doc.input = input 47 doc.eval = exec 48 doc.element = rootElement 49 doc.frameTree = frames 50 51 return doc 52 } 53 54 func (doc *HTMLDocument) MarshalJSON() ([]byte, error) { 55 return doc.element.MarshalJSON() 56 } 57 58 func (doc *HTMLDocument) Type() core.Type { 59 return drivers.HTMLDocumentType 60 } 61 62 func (doc *HTMLDocument) String() string { 63 return doc.frameTree.Frame.URL 64 } 65 66 func (doc *HTMLDocument) Unwrap() interface{} { 67 return doc.element 68 } 69 70 func (doc *HTMLDocument) Hash() uint64 { 71 h := fnv.New64a() 72 73 h.Write([]byte(doc.Type().String())) 74 h.Write([]byte(":")) 75 h.Write([]byte(doc.frameTree.Frame.ID)) 76 h.Write([]byte(doc.frameTree.Frame.URL)) 77 78 return h.Sum64() 79 } 80 81 func (doc *HTMLDocument) Copy() core.Value { 82 return values.None 83 } 84 85 func (doc *HTMLDocument) Compare(other core.Value) int64 { 86 switch other.Type() { 87 case drivers.HTMLDocumentType: 88 cdpDoc, ok := other.(*HTMLDocument) 89 90 if ok { 91 thisID := values.NewString(string(doc.Frame().Frame.ID)) 92 otherID := values.NewString(string(cdpDoc.Frame().Frame.ID)) 93 94 return thisID.Compare(otherID) 95 } 96 97 other := other.(drivers.HTMLDocument) 98 99 return values.NewString(doc.frameTree.Frame.URL).Compare(other.GetURL()) 100 case FrameIDType: 101 return values.NewString(string(doc.frameTree.Frame.ID)).Compare(values.NewString(other.String())) 102 default: 103 return drivers.Compare(doc.Type(), other.Type()) 104 } 105 } 106 107 func (doc *HTMLDocument) Iterate(ctx context.Context) (core.Iterator, error) { 108 return doc.element.Iterate(ctx) 109 } 110 111 func (doc *HTMLDocument) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) { 112 return common.GetInDocument(ctx, path, doc) 113 } 114 115 func (doc *HTMLDocument) SetIn(ctx context.Context, path []core.Value, value core.Value) core.PathError { 116 return common.SetInDocument(ctx, path, doc, value) 117 } 118 119 func (doc *HTMLDocument) Close() error { 120 return doc.element.Close() 121 } 122 123 func (doc *HTMLDocument) Frame() page.FrameTree { 124 return doc.frameTree 125 } 126 127 func (doc *HTMLDocument) GetNodeType(_ context.Context) (values.Int, error) { 128 return 9, nil 129 } 130 131 func (doc *HTMLDocument) GetNodeName(_ context.Context) (values.String, error) { 132 return "#document", nil 133 } 134 135 func (doc *HTMLDocument) GetChildNodes(ctx context.Context) (*values.Array, error) { 136 return doc.element.GetChildNodes(ctx) 137 } 138 139 func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) { 140 return doc.element.GetChildNode(ctx, idx) 141 } 142 143 func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector drivers.QuerySelector) (core.Value, error) { 144 return doc.element.QuerySelector(ctx, selector) 145 } 146 147 func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector drivers.QuerySelector) (*values.Array, error) { 148 return doc.element.QuerySelectorAll(ctx, selector) 149 } 150 151 func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector drivers.QuerySelector) (values.Int, error) { 152 return doc.element.CountBySelector(ctx, selector) 153 } 154 155 func (doc *HTMLDocument) ExistsBySelector(ctx context.Context, selector drivers.QuerySelector) (values.Boolean, error) { 156 return doc.element.ExistsBySelector(ctx, selector) 157 } 158 159 func (doc *HTMLDocument) GetTitle() values.String { 160 value, err := doc.eval.EvalValue(context.Background(), templates.GetTitle()) 161 162 if err != nil { 163 doc.logError(errors.Wrap(err, "failed to read document title")) 164 165 return values.EmptyString 166 } 167 168 return values.NewString(value.String()) 169 } 170 171 func (doc *HTMLDocument) GetName() values.String { 172 if doc.frameTree.Frame.Name != nil { 173 return values.NewString(*doc.frameTree.Frame.Name) 174 } 175 176 return values.EmptyString 177 } 178 179 func (doc *HTMLDocument) GetParentDocument(ctx context.Context) (drivers.HTMLDocument, error) { 180 if doc.frameTree.Frame.ParentID == nil { 181 return nil, nil 182 } 183 184 return doc.dom.GetFrameNode(ctx, *doc.frameTree.Frame.ParentID) 185 } 186 187 func (doc *HTMLDocument) GetChildDocuments(ctx context.Context) (*values.Array, error) { 188 arr := values.NewArray(len(doc.frameTree.ChildFrames)) 189 190 for _, childFrame := range doc.frameTree.ChildFrames { 191 frame, err := doc.dom.GetFrameNode(ctx, childFrame.Frame.ID) 192 193 if err != nil { 194 return nil, err 195 } 196 197 if frame != nil { 198 arr.Push(frame) 199 } 200 } 201 202 return arr, nil 203 } 204 205 func (doc *HTMLDocument) XPath(ctx context.Context, expression values.String) (core.Value, error) { 206 return doc.element.XPath(ctx, expression) 207 } 208 209 func (doc *HTMLDocument) Length() values.Int { 210 return doc.element.Length() 211 } 212 213 func (doc *HTMLDocument) GetElement() drivers.HTMLElement { 214 return doc.element 215 } 216 217 func (doc *HTMLDocument) GetURL() values.String { 218 return values.NewString(doc.frameTree.Frame.URL) 219 } 220 221 func (doc *HTMLDocument) MoveMouseByXY(ctx context.Context, x, y values.Float) error { 222 return doc.input.MoveMouseByXY(ctx, x, y) 223 } 224 225 func (doc *HTMLDocument) WaitForElement(ctx context.Context, selector drivers.QuerySelector, when drivers.WaitEvent) error { 226 task := events.NewEvalWaitTask( 227 doc.eval, 228 templates.WaitForElement(doc.element.id, selector, when), 229 events.DefaultPolling, 230 ) 231 232 _, err := task.Run(ctx) 233 234 return err 235 } 236 237 func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector drivers.QuerySelector, class values.String, when drivers.WaitEvent) error { 238 task := events.NewEvalWaitTask( 239 doc.eval, 240 templates.WaitForClassBySelector(doc.element.id, selector, class, when), 241 events.DefaultPolling, 242 ) 243 244 _, err := task.Run(ctx) 245 246 return err 247 } 248 249 func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector drivers.QuerySelector, class values.String, when drivers.WaitEvent) error { 250 task := events.NewEvalWaitTask( 251 doc.eval, 252 templates.WaitForClassBySelectorAll(doc.element.id, selector, class, when), 253 events.DefaultPolling, 254 ) 255 256 _, err := task.Run(ctx) 257 258 return err 259 } 260 261 func (doc *HTMLDocument) WaitForAttributeBySelector( 262 ctx context.Context, 263 selector drivers.QuerySelector, 264 name, 265 value values.String, 266 when drivers.WaitEvent, 267 ) error { 268 task := events.NewEvalWaitTask( 269 doc.eval, 270 templates.WaitForAttributeBySelector(doc.element.id, selector, name, value, when), 271 events.DefaultPolling, 272 ) 273 274 _, err := task.Run(ctx) 275 276 return err 277 } 278 279 func (doc *HTMLDocument) WaitForAttributeBySelectorAll( 280 ctx context.Context, 281 selector drivers.QuerySelector, 282 name, 283 value values.String, 284 when drivers.WaitEvent, 285 ) error { 286 task := events.NewEvalWaitTask( 287 doc.eval, 288 templates.WaitForAttributeBySelectorAll(doc.element.id, selector, name, value, when), 289 events.DefaultPolling, 290 ) 291 292 _, err := task.Run(ctx) 293 294 return err 295 } 296 297 func (doc *HTMLDocument) WaitForStyleBySelector(ctx context.Context, selector drivers.QuerySelector, name, value values.String, when drivers.WaitEvent) error { 298 task := events.NewEvalWaitTask( 299 doc.eval, 300 templates.WaitForStyleBySelector(doc.element.id, selector, name, value, when), 301 events.DefaultPolling, 302 ) 303 304 _, err := task.Run(ctx) 305 306 return err 307 } 308 309 func (doc *HTMLDocument) WaitForStyleBySelectorAll(ctx context.Context, selector drivers.QuerySelector, name, value values.String, when drivers.WaitEvent) error { 310 task := events.NewEvalWaitTask( 311 doc.eval, 312 templates.WaitForStyleBySelectorAll(doc.element.id, selector, name, value, when), 313 events.DefaultPolling, 314 ) 315 316 _, err := task.Run(ctx) 317 318 return err 319 } 320 321 func (doc *HTMLDocument) ScrollTop(ctx context.Context, options drivers.ScrollOptions) error { 322 return doc.input.ScrollTop(ctx, options) 323 } 324 325 func (doc *HTMLDocument) ScrollBottom(ctx context.Context, options drivers.ScrollOptions) error { 326 return doc.input.ScrollBottom(ctx, options) 327 } 328 329 func (doc *HTMLDocument) ScrollBySelector(ctx context.Context, selector drivers.QuerySelector, options drivers.ScrollOptions) error { 330 return doc.input.ScrollIntoViewBySelector(ctx, doc.element.id, selector, options) 331 } 332 333 func (doc *HTMLDocument) Scroll(ctx context.Context, options drivers.ScrollOptions) error { 334 return doc.input.ScrollByXY(ctx, options) 335 } 336 337 func (doc *HTMLDocument) Eval() *eval.Runtime { 338 return doc.eval 339 } 340 341 func (doc *HTMLDocument) logError(err error) *zerolog.Event { 342 return doc.logger. 343 Error(). 344 Timestamp(). 345 Str("url", doc.frameTree.Frame.URL). 346 Str("securityOrigin", doc.frameTree.Frame.SecurityOrigin). 347 Str("mimeType", doc.frameTree.Frame.MimeType). 348 Str("frameID", string(doc.frameTree.Frame.ID)). 349 Err(err) 350 }