github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cdp/dom/element.go (about)

     1  package dom
     2  
     3  import (
     4  	"context"
     5  	"hash/fnv"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/mafredri/cdp"
    10  	"github.com/mafredri/cdp/protocol/runtime"
    11  	"github.com/pkg/errors"
    12  	"github.com/rs/zerolog"
    13  	"github.com/wI2L/jettison"
    14  
    15  	"github.com/MontFerret/ferret/pkg/drivers"
    16  	"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
    17  	"github.com/MontFerret/ferret/pkg/drivers/cdp/events"
    18  	"github.com/MontFerret/ferret/pkg/drivers/cdp/input"
    19  	"github.com/MontFerret/ferret/pkg/drivers/cdp/templates"
    20  	"github.com/MontFerret/ferret/pkg/drivers/common"
    21  	"github.com/MontFerret/ferret/pkg/runtime/core"
    22  	"github.com/MontFerret/ferret/pkg/runtime/logging"
    23  	"github.com/MontFerret/ferret/pkg/runtime/values"
    24  )
    25  
    26  type HTMLElement struct {
    27  	logger   zerolog.Logger
    28  	client   *cdp.Client
    29  	dom      *Manager
    30  	input    *input.Manager
    31  	eval     *eval.Runtime
    32  	id       runtime.RemoteObjectID
    33  	nodeType *common.LazyValue
    34  	nodeName *common.LazyValue
    35  }
    36  
    37  func NewHTMLElement(
    38  	logger zerolog.Logger,
    39  	client *cdp.Client,
    40  	domManager *Manager,
    41  	input *input.Manager,
    42  	exec *eval.Runtime,
    43  	id runtime.RemoteObjectID,
    44  ) *HTMLElement {
    45  	el := new(HTMLElement)
    46  	el.logger = logging.
    47  		WithName(logger.With(), "dom_element").
    48  		Str("object_id", string(id)).
    49  		Logger()
    50  	el.client = client
    51  	el.dom = domManager
    52  	el.input = input
    53  	el.eval = exec
    54  	el.id = id
    55  	el.nodeType = common.NewLazyValue(func(ctx context.Context) (core.Value, error) {
    56  		return el.eval.EvalValue(ctx, templates.GetNodeType(el.id))
    57  	})
    58  	el.nodeName = common.NewLazyValue(func(ctx context.Context) (core.Value, error) {
    59  		return el.eval.EvalValue(ctx, templates.GetNodeName(el.id))
    60  	})
    61  
    62  	return el
    63  }
    64  
    65  func (el *HTMLElement) RemoteID() runtime.RemoteObjectID {
    66  	return el.id
    67  }
    68  
    69  func (el *HTMLElement) Close() error {
    70  	return nil
    71  }
    72  
    73  func (el *HTMLElement) Type() core.Type {
    74  	return drivers.HTMLElementType
    75  }
    76  
    77  func (el *HTMLElement) MarshalJSON() ([]byte, error) {
    78  	return jettison.MarshalOpts(el.String(), jettison.NoHTMLEscaping())
    79  }
    80  
    81  func (el *HTMLElement) String() string {
    82  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(drivers.DefaultWaitTimeout)*time.Millisecond)
    83  	defer cancel()
    84  
    85  	res, err := el.GetInnerHTML(ctx)
    86  
    87  	if err != nil {
    88  		el.logError(errors.Wrap(err, "HTMLElement.String"))
    89  
    90  		return ""
    91  	}
    92  
    93  	return res.String()
    94  }
    95  
    96  func (el *HTMLElement) Compare(other core.Value) int64 {
    97  	switch other.Type() {
    98  	case drivers.HTMLElementType:
    99  		other := other.(drivers.HTMLElement)
   100  
   101  		return int64(strings.Compare(el.String(), other.String()))
   102  	default:
   103  		return drivers.Compare(el.Type(), other.Type())
   104  	}
   105  }
   106  
   107  func (el *HTMLElement) Unwrap() interface{} {
   108  	return el
   109  }
   110  
   111  func (el *HTMLElement) Hash() uint64 {
   112  	h := fnv.New64a()
   113  
   114  	h.Write([]byte(el.Type().String()))
   115  	h.Write([]byte(":"))
   116  	h.Write([]byte(el.id))
   117  
   118  	return h.Sum64()
   119  }
   120  
   121  func (el *HTMLElement) Copy() core.Value {
   122  	return values.None
   123  }
   124  
   125  func (el *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) {
   126  	return common.NewIterator(el)
   127  }
   128  
   129  func (el *HTMLElement) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
   130  	return common.GetInElement(ctx, path, el)
   131  }
   132  
   133  func (el *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.Value) core.PathError {
   134  	return common.SetInElement(ctx, path, el, value)
   135  }
   136  
   137  func (el *HTMLElement) GetValue(ctx context.Context) (core.Value, error) {
   138  	return el.eval.EvalValue(ctx, templates.GetValue(el.id))
   139  }
   140  
   141  func (el *HTMLElement) SetValue(ctx context.Context, value core.Value) error {
   142  	return el.eval.Eval(ctx, templates.SetValue(el.id, value))
   143  }
   144  
   145  func (el *HTMLElement) GetNodeType(ctx context.Context) (values.Int, error) {
   146  	out, err := el.nodeType.Read(ctx)
   147  
   148  	if err != nil {
   149  		return values.ZeroInt, err
   150  	}
   151  
   152  	return values.ToInt(out), nil
   153  }
   154  
   155  func (el *HTMLElement) GetNodeName(ctx context.Context) (values.String, error) {
   156  	out, err := el.nodeName.Read(ctx)
   157  
   158  	if err != nil {
   159  		return values.EmptyString, err
   160  	}
   161  
   162  	return values.ToString(out), nil
   163  }
   164  
   165  func (el *HTMLElement) Length() values.Int {
   166  	value, err := el.eval.EvalValue(context.Background(), templates.GetChildrenCount(el.id))
   167  
   168  	if err != nil {
   169  		el.logError(err)
   170  
   171  		return 0
   172  	}
   173  
   174  	return values.ToInt(value)
   175  }
   176  
   177  func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) {
   178  	out, err := el.eval.EvalValue(ctx, templates.GetStyles(el.id))
   179  
   180  	if err != nil {
   181  		return values.NewObject(), err
   182  	}
   183  
   184  	return values.ToObject(ctx, out), nil
   185  }
   186  
   187  func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.Value, error) {
   188  	return el.eval.EvalValue(ctx, templates.GetStyle(el.id, name))
   189  }
   190  
   191  func (el *HTMLElement) SetStyles(ctx context.Context, styles *values.Object) error {
   192  	return el.eval.Eval(ctx, templates.SetStyles(el.id, styles))
   193  }
   194  
   195  func (el *HTMLElement) SetStyle(ctx context.Context, name, value values.String) error {
   196  	return el.eval.Eval(ctx, templates.SetStyle(el.id, name, value))
   197  }
   198  
   199  func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String) error {
   200  	return el.eval.Eval(ctx, templates.RemoveStyles(el.id, names))
   201  }
   202  
   203  func (el *HTMLElement) GetAttributes(ctx context.Context) (*values.Object, error) {
   204  	out, err := el.eval.EvalValue(ctx, templates.GetAttributes(el.id))
   205  
   206  	if err != nil {
   207  		return values.NewObject(), err
   208  	}
   209  
   210  	return values.ToObject(ctx, out), nil
   211  }
   212  
   213  func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) (core.Value, error) {
   214  	return el.eval.EvalValue(ctx, templates.GetAttribute(el.id, name))
   215  }
   216  
   217  func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object) error {
   218  	return el.eval.Eval(ctx, templates.SetAttributes(el.id, attrs))
   219  }
   220  
   221  func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.String) error {
   222  	return el.eval.Eval(ctx, templates.SetAttribute(el.id, name, value))
   223  }
   224  
   225  func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.String) error {
   226  	return el.eval.Eval(ctx, templates.RemoveAttributes(el.id, names))
   227  }
   228  
   229  func (el *HTMLElement) GetChildNodes(ctx context.Context) (*values.Array, error) {
   230  	return el.eval.EvalElements(ctx, templates.GetChildren(el.id))
   231  }
   232  
   233  func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) {
   234  	return el.eval.EvalElement(ctx, templates.GetChildByIndex(el.id, idx))
   235  }
   236  
   237  func (el *HTMLElement) GetParentElement(ctx context.Context) (core.Value, error) {
   238  	return el.eval.EvalElement(ctx, templates.GetParent(el.id))
   239  }
   240  
   241  func (el *HTMLElement) GetPreviousElementSibling(ctx context.Context) (core.Value, error) {
   242  	return el.eval.EvalElement(ctx, templates.GetPreviousElementSibling(el.id))
   243  }
   244  
   245  func (el *HTMLElement) GetNextElementSibling(ctx context.Context) (core.Value, error) {
   246  	return el.eval.EvalElement(ctx, templates.GetNextElementSibling(el.id))
   247  }
   248  
   249  func (el *HTMLElement) QuerySelector(ctx context.Context, selector drivers.QuerySelector) (core.Value, error) {
   250  	return el.eval.EvalElement(ctx, templates.QuerySelector(el.id, selector))
   251  }
   252  
   253  func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector drivers.QuerySelector) (*values.Array, error) {
   254  	return el.eval.EvalElements(ctx, templates.QuerySelectorAll(el.id, selector))
   255  }
   256  
   257  func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (result core.Value, err error) {
   258  	return el.eval.EvalValue(ctx, templates.XPath(el.id, expression))
   259  }
   260  
   261  func (el *HTMLElement) GetInnerText(ctx context.Context) (values.String, error) {
   262  	out, err := el.eval.EvalValue(ctx, templates.GetInnerText(el.id))
   263  
   264  	if err != nil {
   265  		return values.EmptyString, err
   266  	}
   267  
   268  	return values.ToString(out), nil
   269  }
   270  
   271  func (el *HTMLElement) SetInnerText(ctx context.Context, innerText values.String) error {
   272  	return el.eval.Eval(
   273  		ctx,
   274  		templates.SetInnerText(el.id, innerText),
   275  	)
   276  }
   277  
   278  func (el *HTMLElement) GetInnerTextBySelector(ctx context.Context, selector drivers.QuerySelector) (values.String, error) {
   279  	out, err := el.eval.EvalValue(ctx, templates.GetInnerTextBySelector(el.id, selector))
   280  
   281  	if err != nil {
   282  		return values.EmptyString, err
   283  	}
   284  
   285  	return values.ToString(out), nil
   286  }
   287  
   288  func (el *HTMLElement) SetInnerTextBySelector(ctx context.Context, selector drivers.QuerySelector, innerText values.String) error {
   289  	return el.eval.Eval(
   290  		ctx,
   291  		templates.SetInnerTextBySelector(el.id, selector, innerText),
   292  	)
   293  }
   294  
   295  func (el *HTMLElement) GetInnerTextBySelectorAll(ctx context.Context, selector drivers.QuerySelector) (*values.Array, error) {
   296  	out, err := el.eval.EvalValue(ctx, templates.GetInnerTextBySelectorAll(el.id, selector))
   297  
   298  	if err != nil {
   299  		return values.EmptyArray(), err
   300  	}
   301  
   302  	return values.ToArray(ctx, out), nil
   303  }
   304  
   305  func (el *HTMLElement) GetInnerHTML(ctx context.Context) (values.String, error) {
   306  	out, err := el.eval.EvalValue(ctx, templates.GetInnerHTML(el.id))
   307  
   308  	if err != nil {
   309  		return values.EmptyString, err
   310  	}
   311  
   312  	return values.ToString(out), nil
   313  }
   314  
   315  func (el *HTMLElement) SetInnerHTML(ctx context.Context, innerHTML values.String) error {
   316  	return el.eval.Eval(ctx, templates.SetInnerHTML(el.id, innerHTML))
   317  }
   318  
   319  func (el *HTMLElement) GetInnerHTMLBySelector(ctx context.Context, selector drivers.QuerySelector) (values.String, error) {
   320  	out, err := el.eval.EvalValue(ctx, templates.GetInnerHTMLBySelector(el.id, selector))
   321  
   322  	if err != nil {
   323  		return values.EmptyString, err
   324  	}
   325  
   326  	return values.ToString(out), nil
   327  }
   328  
   329  func (el *HTMLElement) SetInnerHTMLBySelector(ctx context.Context, selector drivers.QuerySelector, innerHTML values.String) error {
   330  	return el.eval.Eval(ctx, templates.SetInnerHTMLBySelector(el.id, selector, innerHTML))
   331  }
   332  
   333  func (el *HTMLElement) GetInnerHTMLBySelectorAll(ctx context.Context, selector drivers.QuerySelector) (*values.Array, error) {
   334  	out, err := el.eval.EvalValue(ctx, templates.GetInnerHTMLBySelectorAll(el.id, selector))
   335  
   336  	if err != nil {
   337  		return values.EmptyArray(), err
   338  	}
   339  
   340  	return values.ToArray(ctx, out), nil
   341  }
   342  
   343  func (el *HTMLElement) CountBySelector(ctx context.Context, selector drivers.QuerySelector) (values.Int, error) {
   344  	out, err := el.eval.EvalValue(ctx, templates.CountBySelector(el.id, selector))
   345  
   346  	if err != nil {
   347  		return values.ZeroInt, err
   348  	}
   349  
   350  	return values.ToInt(out), nil
   351  }
   352  
   353  func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector drivers.QuerySelector) (values.Boolean, error) {
   354  	out, err := el.eval.EvalValue(ctx, templates.ExistsBySelector(el.id, selector))
   355  
   356  	if err != nil {
   357  		return values.False, err
   358  	}
   359  
   360  	return values.ToBoolean(out), nil
   361  }
   362  
   363  func (el *HTMLElement) WaitForElement(ctx context.Context, selector drivers.QuerySelector, when drivers.WaitEvent) error {
   364  	task := events.NewEvalWaitTask(
   365  		el.eval,
   366  		templates.WaitForElement(el.id, selector, when),
   367  		events.DefaultPolling,
   368  	)
   369  
   370  	_, err := task.Run(ctx)
   371  
   372  	return err
   373  }
   374  
   375  func (el *HTMLElement) WaitForElementAll(ctx context.Context, selector drivers.QuerySelector, when drivers.WaitEvent) error {
   376  	task := events.NewEvalWaitTask(
   377  		el.eval,
   378  		templates.WaitForElementAll(el.id, selector, when),
   379  		events.DefaultPolling,
   380  	)
   381  
   382  	_, err := task.Run(ctx)
   383  
   384  	return err
   385  }
   386  
   387  func (el *HTMLElement) WaitForClass(ctx context.Context, class values.String, when drivers.WaitEvent) error {
   388  	task := events.NewEvalWaitTask(
   389  		el.eval,
   390  		templates.WaitForClass(el.id, class, when),
   391  		events.DefaultPolling,
   392  	)
   393  
   394  	_, err := task.Run(ctx)
   395  
   396  	return err
   397  }
   398  
   399  func (el *HTMLElement) WaitForClassBySelector(ctx context.Context, selector drivers.QuerySelector, class values.String, when drivers.WaitEvent) error {
   400  	task := events.NewEvalWaitTask(
   401  		el.eval,
   402  		templates.WaitForClassBySelector(el.id, selector, class, when),
   403  		events.DefaultPolling,
   404  	)
   405  
   406  	_, err := task.Run(ctx)
   407  
   408  	return err
   409  }
   410  
   411  func (el *HTMLElement) WaitForClassBySelectorAll(ctx context.Context, selector drivers.QuerySelector, class values.String, when drivers.WaitEvent) error {
   412  	task := events.NewEvalWaitTask(
   413  		el.eval,
   414  		templates.WaitForClassBySelectorAll(el.id, selector, class, when),
   415  		events.DefaultPolling,
   416  	)
   417  
   418  	_, err := task.Run(ctx)
   419  
   420  	return err
   421  }
   422  
   423  func (el *HTMLElement) WaitForAttribute(
   424  	ctx context.Context,
   425  	name values.String,
   426  	value core.Value,
   427  	when drivers.WaitEvent,
   428  ) error {
   429  	task := events.NewEvalWaitTask(
   430  		el.eval,
   431  		templates.WaitForAttribute(el.id, name, value, when),
   432  		events.DefaultPolling,
   433  	)
   434  
   435  	_, err := task.Run(ctx)
   436  
   437  	return err
   438  }
   439  
   440  func (el *HTMLElement) WaitForAttributeBySelector(ctx context.Context, selector drivers.QuerySelector, name values.String, value core.Value, when drivers.WaitEvent) error {
   441  	task := events.NewEvalWaitTask(
   442  		el.eval,
   443  		templates.WaitForAttributeBySelector(el.id, selector, name, value, when),
   444  		events.DefaultPolling,
   445  	)
   446  
   447  	_, err := task.Run(ctx)
   448  
   449  	return err
   450  }
   451  
   452  func (el *HTMLElement) WaitForAttributeBySelectorAll(ctx context.Context, selector drivers.QuerySelector, name values.String, value core.Value, when drivers.WaitEvent) error {
   453  	task := events.NewEvalWaitTask(
   454  		el.eval,
   455  		templates.WaitForAttributeBySelectorAll(el.id, selector, name, value, when),
   456  		events.DefaultPolling,
   457  	)
   458  
   459  	_, err := task.Run(ctx)
   460  
   461  	return err
   462  }
   463  
   464  func (el *HTMLElement) WaitForStyle(ctx context.Context, name values.String, value core.Value, when drivers.WaitEvent) error {
   465  	task := events.NewEvalWaitTask(
   466  		el.eval,
   467  		templates.WaitForStyle(el.id, name, value, when),
   468  		events.DefaultPolling,
   469  	)
   470  
   471  	_, err := task.Run(ctx)
   472  
   473  	return err
   474  }
   475  
   476  func (el *HTMLElement) WaitForStyleBySelector(ctx context.Context, selector drivers.QuerySelector, name values.String, value core.Value, when drivers.WaitEvent) error {
   477  	task := events.NewEvalWaitTask(
   478  		el.eval,
   479  		templates.WaitForStyleBySelector(el.id, selector, name, value, when),
   480  		events.DefaultPolling,
   481  	)
   482  
   483  	_, err := task.Run(ctx)
   484  
   485  	return err
   486  }
   487  
   488  func (el *HTMLElement) WaitForStyleBySelectorAll(ctx context.Context, selector drivers.QuerySelector, name values.String, value core.Value, when drivers.WaitEvent) error {
   489  	task := events.NewEvalWaitTask(
   490  		el.eval,
   491  		templates.WaitForStyleBySelectorAll(el.id, selector, name, value, when),
   492  		events.DefaultPolling,
   493  	)
   494  
   495  	_, err := task.Run(ctx)
   496  
   497  	return err
   498  }
   499  
   500  func (el *HTMLElement) Click(ctx context.Context, count values.Int) error {
   501  	return el.input.Click(ctx, el.id, int(count))
   502  }
   503  
   504  func (el *HTMLElement) ClickBySelector(ctx context.Context, selector drivers.QuerySelector, count values.Int) error {
   505  	return el.input.ClickBySelector(ctx, el.id, selector, count)
   506  }
   507  
   508  func (el *HTMLElement) ClickBySelectorAll(ctx context.Context, selector drivers.QuerySelector, count values.Int) error {
   509  	elements, err := el.QuerySelectorAll(ctx, selector)
   510  
   511  	if err != nil {
   512  		return err
   513  	}
   514  
   515  	elements.ForEach(func(value core.Value, idx int) bool {
   516  		found := value.(*HTMLElement)
   517  
   518  		if e := found.Click(ctx, count); e != nil {
   519  			err = e
   520  			return false
   521  		}
   522  
   523  		return true
   524  	})
   525  
   526  	return err
   527  }
   528  
   529  func (el *HTMLElement) Input(ctx context.Context, value core.Value, delay values.Int) error {
   530  	name, err := el.GetNodeName(ctx)
   531  
   532  	if err != nil {
   533  		return err
   534  	}
   535  
   536  	if strings.ToLower(string(name)) != "input" {
   537  		return core.Error(core.ErrInvalidOperation, "element is not an <input> element.")
   538  	}
   539  
   540  	return el.input.Type(ctx, el.id, input.TypeParams{
   541  		Text:  value.String(),
   542  		Clear: false,
   543  		Delay: time.Duration(delay) * time.Millisecond,
   544  	})
   545  }
   546  
   547  func (el *HTMLElement) InputBySelector(ctx context.Context, selector drivers.QuerySelector, value core.Value, delay values.Int) error {
   548  	return el.input.TypeBySelector(ctx, el.id, selector, input.TypeParams{
   549  		Text:  value.String(),
   550  		Clear: false,
   551  		Delay: time.Duration(delay) * time.Millisecond,
   552  	})
   553  }
   554  
   555  func (el *HTMLElement) Press(ctx context.Context, keys []values.String, count values.Int) error {
   556  	return el.input.Press(ctx, values.UnwrapStrings(keys), int(count))
   557  }
   558  
   559  func (el *HTMLElement) PressBySelector(ctx context.Context, selector drivers.QuerySelector, keys []values.String, count values.Int) error {
   560  	return el.input.PressBySelector(ctx, el.id, selector, values.UnwrapStrings(keys), int(count))
   561  }
   562  
   563  func (el *HTMLElement) Clear(ctx context.Context) error {
   564  	return el.input.Clear(ctx, el.id)
   565  }
   566  
   567  func (el *HTMLElement) ClearBySelector(ctx context.Context, selector drivers.QuerySelector) error {
   568  	return el.input.ClearBySelector(ctx, el.id, selector)
   569  }
   570  
   571  func (el *HTMLElement) Select(ctx context.Context, value *values.Array) (*values.Array, error) {
   572  	return el.input.Select(ctx, el.id, value)
   573  }
   574  
   575  func (el *HTMLElement) SelectBySelector(ctx context.Context, selector drivers.QuerySelector, value *values.Array) (*values.Array, error) {
   576  	return el.input.SelectBySelector(ctx, el.id, selector, value)
   577  }
   578  
   579  func (el *HTMLElement) ScrollIntoView(ctx context.Context, options drivers.ScrollOptions) error {
   580  	return el.input.ScrollIntoView(ctx, el.id, options)
   581  }
   582  
   583  func (el *HTMLElement) Focus(ctx context.Context) error {
   584  	return el.input.Focus(ctx, el.id)
   585  }
   586  
   587  func (el *HTMLElement) FocusBySelector(ctx context.Context, selector drivers.QuerySelector) error {
   588  	return el.input.FocusBySelector(ctx, el.id, selector)
   589  }
   590  
   591  func (el *HTMLElement) Blur(ctx context.Context) error {
   592  	return el.input.Blur(ctx, el.id)
   593  }
   594  
   595  func (el *HTMLElement) BlurBySelector(ctx context.Context, selector drivers.QuerySelector) error {
   596  	return el.input.BlurBySelector(ctx, el.id, selector)
   597  }
   598  
   599  func (el *HTMLElement) Hover(ctx context.Context) error {
   600  	return el.input.MoveMouse(ctx, el.id)
   601  }
   602  
   603  func (el *HTMLElement) HoverBySelector(ctx context.Context, selector drivers.QuerySelector) error {
   604  	return el.input.MoveMouseBySelector(ctx, el.id, selector)
   605  }
   606  
   607  func (el *HTMLElement) logError(err error) *zerolog.Event {
   608  	return el.logger.
   609  		Error().
   610  		Timestamp().
   611  		Str("objectID", string(el.id)).
   612  		Err(err)
   613  }