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  }