github.com/MontFerret/ferret@v0.18.0/pkg/drivers/http/element.go (about) 1 package http 2 3 import ( 4 "context" 5 "hash/fnv" 6 "strings" 7 8 "golang.org/x/net/html" 9 10 "github.com/PuerkitoBio/goquery" 11 "github.com/wI2L/jettison" 12 13 "github.com/MontFerret/ferret/pkg/drivers" 14 "github.com/MontFerret/ferret/pkg/drivers/common" 15 "github.com/MontFerret/ferret/pkg/runtime/core" 16 "github.com/MontFerret/ferret/pkg/runtime/values" 17 ) 18 19 type HTMLElement struct { 20 selection *goquery.Selection 21 attrs *values.Object 22 styles *values.Object 23 children *values.Array 24 } 25 26 func NewHTMLElement(node *goquery.Selection) (drivers.HTMLElement, error) { 27 if node == nil { 28 return nil, core.Error(core.ErrMissedArgument, "element selection") 29 } 30 31 return &HTMLElement{node, nil, nil, nil}, nil 32 } 33 34 func (el *HTMLElement) MarshalJSON() ([]byte, error) { 35 return jettison.MarshalOpts(el.String(), jettison.NoHTMLEscaping()) 36 } 37 38 func (el *HTMLElement) Type() core.Type { 39 return drivers.HTMLElementType 40 } 41 42 func (el *HTMLElement) String() string { 43 ih, err := el.GetInnerHTML(context.Background()) 44 45 if err != nil { 46 return "" 47 } 48 49 return ih.String() 50 } 51 52 func (el *HTMLElement) Compare(other core.Value) int64 { 53 switch other.Type() { 54 case drivers.HTMLElementType: 55 other := other.(drivers.HTMLElement) 56 57 return int64(strings.Compare(el.String(), other.String())) 58 default: 59 return drivers.Compare(el.Type(), other.Type()) 60 } 61 } 62 63 func (el *HTMLElement) Unwrap() interface{} { 64 return el.selection 65 } 66 67 func (el *HTMLElement) Hash() uint64 { 68 str, err := el.selection.Html() 69 70 if err != nil { 71 return 0 72 } 73 74 h := fnv.New64a() 75 76 h.Write([]byte(el.Type().String())) 77 h.Write([]byte(":")) 78 h.Write([]byte(str)) 79 80 return h.Sum64() 81 } 82 83 func (el *HTMLElement) Copy() core.Value { 84 c, _ := NewHTMLElement(el.selection.Clone()) 85 86 return c 87 } 88 89 func (el *HTMLElement) GetNodeType(_ context.Context) (values.Int, error) { 90 nodes := el.selection.Nodes 91 92 if len(nodes) == 0 { 93 return 0, nil 94 } 95 96 return values.NewInt(common.FromHTMLType(nodes[0].Type)), nil 97 } 98 99 func (el *HTMLElement) Close() error { 100 return nil 101 } 102 103 func (el *HTMLElement) GetNodeName(_ context.Context) (values.String, error) { 104 return values.NewString(goquery.NodeName(el.selection)), nil 105 } 106 107 func (el *HTMLElement) Length() values.Int { 108 if el.children == nil { 109 el.children = el.parseChildren() 110 } 111 112 return el.children.Length() 113 } 114 115 func (el *HTMLElement) GetValue(_ context.Context) (core.Value, error) { 116 val, ok := el.selection.Attr("value") 117 118 if ok { 119 return values.NewString(val), nil 120 } 121 122 return values.EmptyString, nil 123 } 124 125 func (el *HTMLElement) SetValue(_ context.Context, value core.Value) error { 126 el.selection.SetAttr("value", value.String()) 127 128 return nil 129 } 130 131 func (el *HTMLElement) GetInnerText(_ context.Context) (values.String, error) { 132 return values.NewString(el.selection.Text()), nil 133 } 134 135 func (el *HTMLElement) SetInnerText(_ context.Context, innerText values.String) error { 136 el.selection.SetText(innerText.String()) 137 138 return nil 139 } 140 141 func (el *HTMLElement) GetInnerHTML(_ context.Context) (values.String, error) { 142 h, err := el.selection.Html() 143 144 if err != nil { 145 return values.EmptyString, err 146 } 147 148 return values.NewString(h), nil 149 } 150 151 func (el *HTMLElement) SetInnerHTML(_ context.Context, value values.String) error { 152 el.selection.SetHtml(value.String()) 153 154 return nil 155 } 156 157 func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) { 158 if err := el.ensureStyles(ctx); err != nil { 159 return values.NewObject(), err 160 } 161 162 return el.styles.Copy().(*values.Object), nil 163 } 164 165 func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.Value, error) { 166 if err := el.ensureStyles(ctx); err != nil { 167 return values.None, err 168 } 169 170 return el.styles.MustGet(name), nil 171 } 172 173 func (el *HTMLElement) SetStyle(ctx context.Context, name, value values.String) error { 174 if err := el.ensureStyles(ctx); err != nil { 175 return err 176 } 177 178 el.styles.Set(name, value) 179 180 str := common.SerializeStyles(ctx, el.styles) 181 182 return el.SetAttribute(ctx, "style", str) 183 } 184 185 func (el *HTMLElement) SetStyles(ctx context.Context, newStyles *values.Object) error { 186 if newStyles == nil { 187 return nil 188 } 189 190 if err := el.ensureStyles(ctx); err != nil { 191 return err 192 } 193 194 newStyles.ForEach(func(i core.Value, key string) bool { 195 el.styles.Set(values.NewString(key), i) 196 197 return true 198 }) 199 200 str := common.SerializeStyles(ctx, el.styles) 201 202 return el.SetAttribute(ctx, "style", str) 203 } 204 205 func (el *HTMLElement) RemoveStyle(ctx context.Context, name ...values.String) error { 206 if len(name) == 0 { 207 return nil 208 } 209 210 if err := el.ensureStyles(ctx); err != nil { 211 return err 212 } 213 214 for _, s := range name { 215 el.styles.Remove(s) 216 } 217 218 str := common.SerializeStyles(ctx, el.styles) 219 220 return el.SetAttribute(ctx, "style", str) 221 } 222 223 func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object) error { 224 if attrs == nil { 225 return nil 226 } 227 228 el.ensureAttrs() 229 230 var err error 231 232 attrs.ForEach(func(value core.Value, key string) bool { 233 err = el.SetAttribute(ctx, values.NewString(key), values.NewString(value.String())) 234 235 return err == nil 236 }) 237 238 return err 239 } 240 241 func (el *HTMLElement) GetAttributes(_ context.Context) (*values.Object, error) { 242 el.ensureAttrs() 243 244 return el.attrs.Copy().(*values.Object), nil 245 } 246 247 func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) (core.Value, error) { 248 el.ensureAttrs() 249 250 if name == common.AttrNameStyle { 251 return el.GetStyles(ctx) 252 } 253 254 return el.attrs.MustGet(name), nil 255 } 256 257 func (el *HTMLElement) SetAttribute(_ context.Context, name, value values.String) error { 258 el.ensureAttrs() 259 260 if name == common.AttrNameStyle { 261 el.styles = nil 262 } 263 264 el.attrs.Set(name, value) 265 el.selection.SetAttr(string(name), string(value)) 266 267 return nil 268 } 269 270 func (el *HTMLElement) RemoveAttribute(_ context.Context, name ...values.String) error { 271 el.ensureAttrs() 272 273 for _, attr := range name { 274 el.attrs.Remove(attr) 275 el.selection.RemoveAttr(attr.String()) 276 } 277 278 return nil 279 } 280 281 func (el *HTMLElement) GetChildNodes(_ context.Context) (*values.Array, error) { 282 if el.children == nil { 283 el.children = el.parseChildren() 284 } 285 286 return el.children.Copy().(*values.Array), nil 287 } 288 289 func (el *HTMLElement) GetChildNode(_ context.Context, idx values.Int) (core.Value, error) { 290 if el.children == nil { 291 el.children = el.parseChildren() 292 } 293 294 return el.children.Get(idx), nil 295 } 296 297 func (el *HTMLElement) QuerySelector(_ context.Context, selector drivers.QuerySelector) (core.Value, error) { 298 if selector.Kind() == drivers.CSSSelector { 299 selection := el.selection.Find(selector.String()) 300 301 if selection.Length() == 0 { 302 return values.None, drivers.ErrNotFound 303 } 304 305 res, err := NewHTMLElement(selection) 306 307 if err != nil { 308 return values.None, err 309 } 310 311 return res, nil 312 } 313 314 found, err := EvalXPathToNode(el.selection, selector.String()) 315 316 if err != nil { 317 return values.None, err 318 } 319 320 if found == nil { 321 return values.None, drivers.ErrNotFound 322 } 323 324 return found, nil 325 } 326 327 func (el *HTMLElement) QuerySelectorAll(_ context.Context, selector drivers.QuerySelector) (*values.Array, error) { 328 if selector.Kind() == drivers.CSSSelector { 329 selection := el.selection.Find(selector.String()) 330 331 if selection.Length() == 0 { 332 return values.NewArray(0), nil 333 } 334 335 arr := values.NewArray(selection.Length()) 336 337 selection.Each(func(i int, selection *goquery.Selection) { 338 el, err := NewHTMLElement(selection) 339 340 if err == nil { 341 arr.Push(el) 342 } 343 }) 344 345 return arr, nil 346 } 347 348 return EvalXPathToNodes(el.selection, selector.String()) 349 } 350 351 func (el *HTMLElement) XPath(_ context.Context, expression values.String) (core.Value, error) { 352 return EvalXPathTo(el.selection, expression.String()) 353 } 354 355 func (el *HTMLElement) SetInnerHTMLBySelector(ctx context.Context, selector drivers.QuerySelector, innerHTML values.String) error { 356 if selector.Kind() == drivers.CSSSelector { 357 selection := el.selection.Find(selector.String()) 358 359 if selection.Length() == 0 { 360 return drivers.ErrNotFound 361 } 362 363 selection.SetHtml(innerHTML.String()) 364 } 365 366 found, err := EvalXPathToElement(el.selection, selector.String()) 367 368 if err != nil { 369 return err 370 } 371 372 if found == nil { 373 return drivers.ErrNotFound 374 } 375 376 return found.SetInnerHTML(ctx, innerHTML) 377 } 378 379 func (el *HTMLElement) GetInnerHTMLBySelector(ctx context.Context, selector drivers.QuerySelector) (values.String, error) { 380 if selector.Kind() == drivers.CSSSelector { 381 selection := el.selection.Find(selector.String()) 382 383 if selection.Length() == 0 { 384 return values.EmptyString, drivers.ErrNotFound 385 } 386 387 str, err := selection.Html() 388 389 if err != nil { 390 return values.EmptyString, err 391 } 392 393 return values.NewString(str), nil 394 } 395 396 found, err := EvalXPathToElement(el.selection, selector.String()) 397 398 if err != nil { 399 return values.EmptyString, err 400 } 401 402 if found == nil { 403 return values.EmptyString, drivers.ErrNotFound 404 } 405 406 return found.GetInnerHTML(ctx) 407 } 408 409 func (el *HTMLElement) GetInnerHTMLBySelectorAll(ctx context.Context, selector drivers.QuerySelector) (*values.Array, error) { 410 if selector.Kind() == drivers.CSSSelector { 411 var err error 412 selection := el.selection.Find(selector.String()) 413 arr := values.NewArray(selection.Length()) 414 415 selection.EachWithBreak(func(_ int, selection *goquery.Selection) bool { 416 str, e := selection.Html() 417 418 if e != nil { 419 err = e 420 return false 421 } 422 423 arr.Push(values.NewString(strings.TrimSpace(str))) 424 425 return true 426 }) 427 428 if err != nil { 429 return values.NewArray(0), err 430 } 431 432 return arr, nil 433 } 434 435 return EvalXPathToNodesWith(el.selection, selector.String(), func(node *html.Node) (core.Value, error) { 436 n, err := parseXPathNode(node) 437 438 if err != nil { 439 return values.None, err 440 } 441 442 found, err := drivers.ToElement(n) 443 444 if err != nil { 445 return values.None, err 446 } 447 448 return found.GetInnerHTML(ctx) 449 }) 450 } 451 452 func (el *HTMLElement) GetInnerTextBySelector(ctx context.Context, selector drivers.QuerySelector) (values.String, error) { 453 if selector.Kind() == drivers.CSSSelector { 454 selection := el.selection.Find(selector.String()) 455 456 if selection.Length() == 0 { 457 return values.EmptyString, drivers.ErrNotFound 458 } 459 460 return values.NewString(selection.Text()), nil 461 } 462 463 found, err := EvalXPathToElement(el.selection, selector.String()) 464 465 if err != nil { 466 return values.EmptyString, err 467 } 468 469 if found == nil { 470 return values.EmptyString, drivers.ErrNotFound 471 } 472 473 return found.GetInnerText(ctx) 474 } 475 476 func (el *HTMLElement) SetInnerTextBySelector(ctx context.Context, selector drivers.QuerySelector, innerText values.String) error { 477 if selector.Kind() == drivers.CSSSelector { 478 selection := el.selection.Find(selector.String()) 479 480 if selection.Length() == 0 { 481 return drivers.ErrNotFound 482 } 483 484 selection.SetHtml(innerText.String()) 485 486 return nil 487 } 488 489 found, err := EvalXPathToElement(el.selection, selector.String()) 490 491 if err != nil { 492 return err 493 } 494 495 if found == nil { 496 return drivers.ErrNotFound 497 } 498 499 return found.SetInnerText(ctx, innerText) 500 } 501 502 func (el *HTMLElement) GetInnerTextBySelectorAll(ctx context.Context, selector drivers.QuerySelector) (*values.Array, error) { 503 if selector.Kind() == drivers.CSSSelector { 504 selection := el.selection.Find(selector.String()) 505 arr := values.NewArray(selection.Length()) 506 507 selection.Each(func(_ int, selection *goquery.Selection) { 508 arr.Push(values.NewString(selection.Text())) 509 }) 510 511 return arr, nil 512 } 513 514 return EvalXPathToNodesWith(el.selection, selector.String(), func(node *html.Node) (core.Value, error) { 515 n, err := parseXPathNode(node) 516 517 if err != nil { 518 return values.None, err 519 } 520 521 found, err := drivers.ToElement(n) 522 523 if err != nil { 524 return values.None, err 525 } 526 527 return found.GetInnerText(ctx) 528 }) 529 } 530 531 func (el *HTMLElement) CountBySelector(_ context.Context, selector drivers.QuerySelector) (values.Int, error) { 532 if selector.Kind() == drivers.CSSSelector { 533 selection := el.selection.Find(selector.String()) 534 535 return values.NewInt(selection.Length()), nil 536 } 537 538 arr, err := EvalXPathToNodesWith(el.selection, selector.String(), func(node *html.Node) (core.Value, error) { 539 return values.None, nil 540 }) 541 542 if err != nil { 543 return values.ZeroInt, err 544 } 545 546 return arr.Length(), nil 547 } 548 549 func (el *HTMLElement) ExistsBySelector(_ context.Context, selector drivers.QuerySelector) (values.Boolean, error) { 550 if selector.Kind() == drivers.CSSSelector { 551 selection := el.selection.Find(selector.String()) 552 553 if selection.Length() == 0 { 554 return values.False, nil 555 } 556 557 return values.True, nil 558 } 559 560 found, err := EvalXPathToNode(el.selection, selector.String()) 561 562 if err != nil { 563 return values.False, err 564 } 565 566 return values.NewBoolean(found != nil), nil 567 } 568 569 func (el *HTMLElement) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) { 570 return common.GetInElement(ctx, path, el) 571 } 572 573 func (el *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.Value) core.PathError { 574 return common.SetInElement(ctx, path, el, value) 575 } 576 577 func (el *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) { 578 return common.NewIterator(el) 579 } 580 581 func (el *HTMLElement) GetParentElement(_ context.Context) (core.Value, error) { 582 parent := el.selection.Parent() 583 584 if parent == nil { 585 return values.None, nil 586 } 587 588 return NewHTMLElement(parent) 589 } 590 591 func (el *HTMLElement) GetPreviousElementSibling(_ context.Context) (core.Value, error) { 592 sibling := el.selection.Prev() 593 594 if sibling == nil { 595 return values.None, nil 596 } 597 598 return NewHTMLElement(sibling) 599 } 600 601 func (el *HTMLElement) GetNextElementSibling(_ context.Context) (core.Value, error) { 602 sibling := el.selection.Next() 603 604 if sibling == nil { 605 return values.None, nil 606 } 607 608 return NewHTMLElement(sibling) 609 } 610 611 func (el *HTMLElement) Click(_ context.Context, _ values.Int) error { 612 return core.ErrNotSupported 613 } 614 615 func (el *HTMLElement) ClickBySelector(_ context.Context, _ drivers.QuerySelector, _ values.Int) error { 616 return core.ErrNotSupported 617 } 618 619 func (el *HTMLElement) ClickBySelectorAll(_ context.Context, _ drivers.QuerySelector, _ values.Int) error { 620 return core.ErrNotSupported 621 } 622 623 func (el *HTMLElement) Clear(_ context.Context) error { 624 return core.ErrNotSupported 625 } 626 627 func (el *HTMLElement) ClearBySelector(_ context.Context, _ drivers.QuerySelector) error { 628 return core.ErrNotSupported 629 } 630 631 func (el *HTMLElement) Input(_ context.Context, _ core.Value, _ values.Int) error { 632 return core.ErrNotSupported 633 } 634 635 func (el *HTMLElement) InputBySelector(_ context.Context, _ drivers.QuerySelector, _ core.Value, _ values.Int) error { 636 return core.ErrNotSupported 637 } 638 639 func (el *HTMLElement) Press(_ context.Context, _ []values.String, _ values.Int) error { 640 return core.ErrNotSupported 641 } 642 643 func (el *HTMLElement) PressBySelector(_ context.Context, _ drivers.QuerySelector, _ []values.String, _ values.Int) error { 644 return core.ErrNotSupported 645 } 646 647 func (el *HTMLElement) Select(_ context.Context, _ *values.Array) (*values.Array, error) { 648 return nil, core.ErrNotSupported 649 } 650 651 func (el *HTMLElement) SelectBySelector(_ context.Context, _ drivers.QuerySelector, _ *values.Array) (*values.Array, error) { 652 return nil, core.ErrNotSupported 653 } 654 655 func (el *HTMLElement) ScrollIntoView(_ context.Context, _ drivers.ScrollOptions) error { 656 return core.ErrNotSupported 657 } 658 659 func (el *HTMLElement) Focus(_ context.Context) error { 660 return core.ErrNotSupported 661 } 662 663 func (el *HTMLElement) FocusBySelector(_ context.Context, _ drivers.QuerySelector) error { 664 return core.ErrNotSupported 665 } 666 667 func (el *HTMLElement) Blur(_ context.Context) error { 668 return core.ErrNotSupported 669 } 670 671 func (el *HTMLElement) BlurBySelector(_ context.Context, _ drivers.QuerySelector) error { 672 return core.ErrNotSupported 673 } 674 675 func (el *HTMLElement) Hover(_ context.Context) error { 676 return core.ErrNotSupported 677 } 678 679 func (el *HTMLElement) HoverBySelector(_ context.Context, _ drivers.QuerySelector) error { 680 return core.ErrNotSupported 681 } 682 683 func (el *HTMLElement) WaitForClass(_ context.Context, _ values.String, _ drivers.WaitEvent) error { 684 return core.ErrNotSupported 685 } 686 687 func (el *HTMLElement) WaitForElement(_ context.Context, _ drivers.QuerySelector, _ drivers.WaitEvent) error { 688 return core.ErrNotSupported 689 } 690 691 func (el *HTMLElement) WaitForElementAll(_ context.Context, _ drivers.QuerySelector, _ drivers.WaitEvent) error { 692 return core.ErrNotSupported 693 } 694 695 func (el *HTMLElement) WaitForAttribute(_ context.Context, _ values.String, _ core.Value, _ drivers.WaitEvent) error { 696 return core.ErrNotSupported 697 } 698 699 func (el *HTMLElement) WaitForAttributeBySelector(_ context.Context, _ drivers.QuerySelector, _ values.String, _ core.Value, _ drivers.WaitEvent) error { 700 return core.ErrNotSupported 701 } 702 703 func (el *HTMLElement) WaitForAttributeBySelectorAll(_ context.Context, _ drivers.QuerySelector, _ values.String, _ core.Value, _ drivers.WaitEvent) error { 704 return core.ErrNotSupported 705 } 706 707 func (el *HTMLElement) WaitForStyle(_ context.Context, _ values.String, _ core.Value, _ drivers.WaitEvent) error { 708 return core.ErrNotSupported 709 } 710 711 func (el *HTMLElement) WaitForStyleBySelector(_ context.Context, _ drivers.QuerySelector, _ values.String, _ core.Value, _ drivers.WaitEvent) error { 712 return core.ErrNotSupported 713 } 714 715 func (el *HTMLElement) WaitForStyleBySelectorAll(_ context.Context, _ drivers.QuerySelector, _ values.String, _ core.Value, _ drivers.WaitEvent) error { 716 return core.ErrNotSupported 717 } 718 719 func (el *HTMLElement) WaitForClassBySelector(_ context.Context, _ drivers.QuerySelector, _ values.String, _ drivers.WaitEvent) error { 720 return core.ErrNotSupported 721 } 722 723 func (el *HTMLElement) WaitForClassBySelectorAll(_ context.Context, _ drivers.QuerySelector, _ values.String, _ drivers.WaitEvent) error { 724 return core.ErrNotSupported 725 } 726 727 func (el *HTMLElement) ensureStyles(ctx context.Context) error { 728 if el.styles == nil { 729 styles, err := el.parseStyles(ctx) 730 731 if err != nil { 732 return err 733 } 734 735 el.styles = styles 736 } 737 738 return nil 739 } 740 741 func (el *HTMLElement) parseStyles(ctx context.Context) (*values.Object, error) { 742 str, err := el.GetAttribute(ctx, "style") 743 744 if err != nil { 745 return values.NewObject(), err 746 } 747 748 if str == values.None { 749 return values.NewObject(), nil 750 } 751 752 styles, err := common.DeserializeStyles(values.NewString(str.String())) 753 754 if err != nil { 755 return nil, err 756 } 757 758 return styles, nil 759 } 760 761 func (el *HTMLElement) ensureAttrs() { 762 if el.attrs == nil { 763 el.attrs = el.parseAttrs() 764 } 765 } 766 767 func (el *HTMLElement) parseAttrs() *values.Object { 768 obj := values.NewObject() 769 770 if len(el.selection.Nodes) == 0 { 771 return obj 772 } 773 774 node := el.selection.Nodes[0] 775 776 for _, attr := range node.Attr { 777 obj.Set(values.NewString(attr.Key), values.NewString(attr.Val)) 778 } 779 780 return obj 781 } 782 783 func (el *HTMLElement) parseChildren() *values.Array { 784 children := el.selection.Children() 785 786 arr := values.NewArray(10) 787 788 children.Each(func(i int, selection *goquery.Selection) { 789 child, err := NewHTMLElement(selection) 790 791 if err == nil { 792 arr.Push(child) 793 } 794 }) 795 796 return arr 797 }