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

     1  package input
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/mafredri/cdp"
     8  	"github.com/mafredri/cdp/protocol/dom"
     9  	"github.com/mafredri/cdp/protocol/runtime"
    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/templates"
    15  	"github.com/MontFerret/ferret/pkg/runtime/core"
    16  	"github.com/MontFerret/ferret/pkg/runtime/logging"
    17  	"github.com/MontFerret/ferret/pkg/runtime/values"
    18  )
    19  
    20  type (
    21  	TypeParams struct {
    22  		Text  string
    23  		Clear bool
    24  		Delay time.Duration
    25  	}
    26  
    27  	Manager struct {
    28  		logger   zerolog.Logger
    29  		client   *cdp.Client
    30  		exec     *eval.Runtime
    31  		keyboard *Keyboard
    32  		mouse    *Mouse
    33  	}
    34  )
    35  
    36  func New(
    37  	logger zerolog.Logger,
    38  	client *cdp.Client,
    39  	exec *eval.Runtime,
    40  	keyboard *Keyboard,
    41  	mouse *Mouse,
    42  ) *Manager {
    43  	logger = logging.WithName(logger.With(), "input_manager").Logger()
    44  
    45  	return &Manager{
    46  		logger,
    47  		client,
    48  		exec,
    49  		keyboard,
    50  		mouse,
    51  	}
    52  }
    53  
    54  func (m *Manager) Keyboard() *Keyboard {
    55  	return m.keyboard
    56  }
    57  
    58  func (m *Manager) Mouse() *Mouse {
    59  	return m.mouse
    60  }
    61  
    62  func (m *Manager) ScrollTop(ctx context.Context, options drivers.ScrollOptions) error {
    63  	m.logger.Trace().
    64  		Str("behavior", options.Behavior.String()).
    65  		Str("block", options.Block.String()).
    66  		Str("inline", options.Inline.String()).
    67  		Msg("scrolling to the top")
    68  
    69  	if err := m.exec.Eval(ctx, templates.ScrollTop(options)); err != nil {
    70  		m.logger.Trace().Err(err).Msg("failed to scroll to the top")
    71  
    72  		return err
    73  	}
    74  
    75  	m.logger.Trace().Msg("scrolled to the top")
    76  
    77  	return nil
    78  }
    79  
    80  func (m *Manager) ScrollBottom(ctx context.Context, options drivers.ScrollOptions) error {
    81  	m.logger.Trace().
    82  		Str("behavior", options.Behavior.String()).
    83  		Str("block", options.Block.String()).
    84  		Str("inline", options.Inline.String()).
    85  		Msg("scrolling to the bottom")
    86  
    87  	if err := m.exec.Eval(ctx, templates.ScrollBottom(options)); err != nil {
    88  		m.logger.Trace().Err(err).Msg("failed to scroll to the bottom")
    89  
    90  		return err
    91  	}
    92  
    93  	m.logger.Trace().Msg("scrolled to the bottom")
    94  
    95  	return nil
    96  }
    97  
    98  func (m *Manager) ScrollIntoView(ctx context.Context, id runtime.RemoteObjectID, options drivers.ScrollOptions) error {
    99  	m.logger.Trace().
   100  		Str("object_id", string(id)).
   101  		Str("behavior", options.Behavior.String()).
   102  		Str("block", options.Block.String()).
   103  		Str("inline", options.Inline.String()).
   104  		Msg("scrolling to an element")
   105  
   106  	if err := m.exec.Eval(ctx, templates.ScrollIntoView(id, options)); err != nil {
   107  		m.logger.Trace().Err(err).Msg("failed to scroll to an element")
   108  
   109  		return err
   110  	}
   111  
   112  	m.logger.Trace().Msg("scrolled to an element")
   113  
   114  	return nil
   115  }
   116  
   117  func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector, options drivers.ScrollOptions) error {
   118  	m.logger.Trace().
   119  		Str("selector", selector.String()).
   120  		Str("behavior", options.Behavior.String()).
   121  		Str("block", options.Block.String()).
   122  		Str("inline", options.Inline.String()).
   123  		Msg("scrolling to an element by selector")
   124  
   125  	if err := m.exec.Eval(ctx, templates.ScrollIntoViewBySelector(id, selector, options)); err != nil {
   126  		m.logger.Trace().Err(err).Msg("failed to scroll to an element by selector")
   127  
   128  		return err
   129  	}
   130  
   131  	m.logger.Trace().Msg("scrolled to an element by selector")
   132  
   133  	return nil
   134  }
   135  
   136  func (m *Manager) ScrollByXY(ctx context.Context, options drivers.ScrollOptions) error {
   137  	m.logger.Trace().
   138  		Float64("x", float64(options.Top)).
   139  		Float64("y", float64(options.Left)).
   140  		Str("behavior", options.Behavior.String()).
   141  		Str("block", options.Block.String()).
   142  		Str("inline", options.Inline.String()).
   143  		Msg("scrolling to an element by given coordinates")
   144  
   145  	if err := m.exec.Eval(ctx, templates.Scroll(options)); err != nil {
   146  		m.logger.Trace().Err(err).Msg("failed to scroll to an element by coordinates")
   147  
   148  		return err
   149  	}
   150  
   151  	m.logger.Trace().Msg("scrolled to an element by given coordinates")
   152  
   153  	return nil
   154  }
   155  
   156  func (m *Manager) Focus(ctx context.Context, objectID runtime.RemoteObjectID) error {
   157  	m.logger.Trace().
   158  		Str("object_id", string(objectID)).
   159  		Msg("focusing on an element")
   160  
   161  	err := m.ScrollIntoView(ctx, objectID, drivers.ScrollOptions{
   162  		Behavior: drivers.ScrollBehaviorAuto,
   163  		Block:    drivers.ScrollVerticalAlignmentCenter,
   164  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   165  	})
   166  
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	if err := m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID)); err != nil {
   172  		m.logger.Trace().Err(err).Msg("failed focusing on an element")
   173  
   174  		return err
   175  	}
   176  
   177  	m.logger.Trace().Msg("focused on an element")
   178  
   179  	return nil
   180  }
   181  
   182  func (m *Manager) FocusBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector) error {
   183  	m.logger.Trace().
   184  		Str("parent_object_id", string(id)).
   185  		Str("selector", selector.String()).
   186  		Msg("focusing on an element by selector")
   187  
   188  	err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
   189  		Behavior: drivers.ScrollBehaviorAuto,
   190  		Block:    drivers.ScrollVerticalAlignmentCenter,
   191  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   192  	})
   193  
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	m.logger.Trace().Msg("resolving an element by selector")
   199  
   200  	found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
   201  
   202  	if err != nil {
   203  		m.logger.Trace().
   204  			Err(err).
   205  			Msg("failed resolving an element by selector")
   206  
   207  		return err
   208  	}
   209  
   210  	if found.ObjectID == nil {
   211  		m.logger.Trace().
   212  			Err(core.ErrNotFound).
   213  			Msg("element not found by selector")
   214  
   215  		return core.ErrNotFound
   216  	}
   217  
   218  	if err := m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(*found.ObjectID)); err != nil {
   219  		m.logger.Trace().
   220  			Err(err).
   221  			Msg("failed focusing on an element by selector")
   222  
   223  		return err
   224  	}
   225  
   226  	m.logger.Trace().Msg("focused on an element")
   227  
   228  	return nil
   229  }
   230  
   231  func (m *Manager) Blur(ctx context.Context, objectID runtime.RemoteObjectID) error {
   232  	m.logger.Trace().
   233  		Str("object_id", string(objectID)).
   234  		Msg("removing focus from an element")
   235  
   236  	if err := m.exec.Eval(ctx, templates.Blur(objectID)); err != nil {
   237  		m.logger.Trace().
   238  			Err(err).
   239  			Msg("failed removing focus from an element")
   240  
   241  		return err
   242  	}
   243  
   244  	m.logger.Trace().Msg("removed focus from an element")
   245  
   246  	return nil
   247  }
   248  
   249  func (m *Manager) BlurBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector) error {
   250  	m.logger.Trace().
   251  		Str("parent_object_id", string(id)).
   252  		Str("selector", selector.String()).
   253  		Msg("removing focus from an element by selector")
   254  
   255  	if err := m.exec.Eval(ctx, templates.BlurBySelector(id, selector)); err != nil {
   256  		m.logger.Trace().
   257  			Err(err).
   258  			Msg("failed removing focus from an element by selector")
   259  
   260  		return err
   261  	}
   262  
   263  	m.logger.Trace().Msg("removed focus from an element by selector")
   264  
   265  	return nil
   266  }
   267  
   268  func (m *Manager) MoveMouse(ctx context.Context, objectID runtime.RemoteObjectID) error {
   269  	m.logger.Trace().
   270  		Str("object_id", string(objectID)).
   271  		Msg("starting to move the mouse towards an element")
   272  
   273  	if err := m.ScrollIntoView(ctx, objectID, drivers.ScrollOptions{}); err != nil {
   274  		m.logger.Trace().Err(err).Msg("could not scroll into the object. failed to move the mouse")
   275  
   276  		return err
   277  	}
   278  
   279  	m.logger.Trace().Msg("calculating clickable element points")
   280  
   281  	q, err := GetClickablePointByObjectID(ctx, m.client, objectID)
   282  
   283  	if err != nil {
   284  		m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   285  
   286  		return err
   287  	}
   288  
   289  	m.logger.Trace().Float64("x", q.X).Float64("y", q.Y).Msg("calculated clickable element points")
   290  
   291  	if err := m.mouse.Move(ctx, q.X, q.Y); err != nil {
   292  		m.logger.Trace().Err(err).Msg("failed to move the mouse")
   293  
   294  		return err
   295  	}
   296  
   297  	m.logger.Trace().Msg("moved the mouse")
   298  
   299  	return nil
   300  }
   301  
   302  func (m *Manager) MoveMouseBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector) error {
   303  	m.logger.Trace().
   304  		Str("parent_object_id", string(id)).
   305  		Str("selector", selector.String()).
   306  		Msg("starting to move the mouse towards an element by selector")
   307  
   308  	if err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{}); err != nil {
   309  		return err
   310  	}
   311  
   312  	m.logger.Trace().Msg("looking up for an element by selector")
   313  
   314  	found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
   315  
   316  	if err != nil {
   317  		m.logger.Trace().Err(err).Msg("failed to find an element by selector")
   318  
   319  		return err
   320  	}
   321  
   322  	if found.ObjectID == nil {
   323  		m.logger.Trace().
   324  			Err(core.ErrNotFound).
   325  			Msg("element not found by selector")
   326  
   327  		return core.ErrNotFound
   328  	}
   329  
   330  	m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("calculating clickable element points")
   331  
   332  	points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
   333  
   334  	if err != nil {
   335  		m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   336  
   337  		return err
   338  	}
   339  
   340  	m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   341  
   342  	if err := m.mouse.Move(ctx, points.X, points.Y); err != nil {
   343  		m.logger.Trace().Err(err).Msg("failed to move the mouse")
   344  
   345  		return err
   346  	}
   347  
   348  	m.logger.Trace().Msg("moved the mouse")
   349  
   350  	return nil
   351  }
   352  
   353  func (m *Manager) MoveMouseByXY(ctx context.Context, xv, yv values.Float) error {
   354  	x := float64(xv)
   355  	y := float64(yv)
   356  
   357  	m.logger.Trace().
   358  		Float64("x", x).
   359  		Float64("y", y).
   360  		Msg("starting to move the mouse towards an element by given coordinates")
   361  
   362  	if err := m.ScrollByXY(ctx, drivers.ScrollOptions{
   363  		Top:  xv,
   364  		Left: yv,
   365  	}); err != nil {
   366  		return err
   367  	}
   368  
   369  	if err := m.mouse.Move(ctx, x, y); err != nil {
   370  		m.logger.Trace().Err(err).Msg("failed to move the mouse towards an element by given coordinates")
   371  
   372  		return err
   373  	}
   374  
   375  	m.logger.Trace().Msg("moved the mouse")
   376  
   377  	return nil
   378  }
   379  
   380  func (m *Manager) Click(ctx context.Context, objectID runtime.RemoteObjectID, count int) error {
   381  	m.logger.Trace().
   382  		Str("object_id", string(objectID)).
   383  		Msg("starting to click on an element")
   384  
   385  	if err := m.ScrollIntoView(ctx, objectID, drivers.ScrollOptions{
   386  		Behavior: drivers.ScrollBehaviorAuto,
   387  		Block:    drivers.ScrollVerticalAlignmentCenter,
   388  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   389  	}); err != nil {
   390  		return err
   391  	}
   392  
   393  	m.logger.Trace().Msg("calculating clickable element points")
   394  
   395  	points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
   396  
   397  	if err != nil {
   398  		m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   399  
   400  		return err
   401  	}
   402  
   403  	m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   404  
   405  	delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
   406  
   407  	if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
   408  		m.logger.Trace().
   409  			Err(err).
   410  			Msg("failed to click on an element")
   411  
   412  		return err
   413  	}
   414  
   415  	m.logger.Trace().
   416  		Err(err).
   417  		Msg("clicked on an element")
   418  
   419  	return nil
   420  }
   421  
   422  func (m *Manager) ClickBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector, count values.Int) error {
   423  	m.logger.Trace().
   424  		Str("parent_object_id", string(id)).
   425  		Str("selector", selector.String()).
   426  		Int64("count", int64(count)).
   427  		Msg("clicking on an element by selector")
   428  
   429  	if err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
   430  		Behavior: drivers.ScrollBehaviorAuto,
   431  		Block:    drivers.ScrollVerticalAlignmentCenter,
   432  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   433  	}); err != nil {
   434  		return err
   435  	}
   436  
   437  	m.logger.Trace().Msg("looking up for an element by selector")
   438  
   439  	found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
   440  
   441  	if err != nil {
   442  		m.logger.Trace().Err(err).Msg("failed to find an element by selector")
   443  
   444  		return err
   445  	}
   446  
   447  	if found.ObjectID == nil {
   448  		m.logger.Trace().
   449  			Err(core.ErrNotFound).
   450  			Msg("element not found by selector")
   451  
   452  		return core.ErrNotFound
   453  	}
   454  
   455  	m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("calculating clickable element points")
   456  
   457  	points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
   458  
   459  	if err != nil {
   460  		m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   461  
   462  		return err
   463  	}
   464  
   465  	m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   466  
   467  	delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
   468  
   469  	if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, int(count)); err != nil {
   470  		m.logger.Trace().Err(err).Msg("failed to click on an element")
   471  		return nil
   472  	}
   473  
   474  	m.logger.Trace().Msg("clicked on an element")
   475  
   476  	return nil
   477  }
   478  
   479  func (m *Manager) Type(ctx context.Context, objectID runtime.RemoteObjectID, params TypeParams) error {
   480  	m.logger.Trace().
   481  		Str("object_id", string(objectID)).
   482  		Msg("starting to type text")
   483  
   484  	err := m.ScrollIntoView(ctx, objectID, drivers.ScrollOptions{
   485  		Behavior: drivers.ScrollBehaviorAuto,
   486  		Block:    drivers.ScrollVerticalAlignmentCenter,
   487  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   488  	})
   489  
   490  	if err != nil {
   491  		return err
   492  	}
   493  
   494  	m.logger.Trace().Msg("focusing on an element")
   495  
   496  	if err := m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID)); err != nil {
   497  		m.logger.Trace().Msg("failed to focus on an element")
   498  
   499  		return err
   500  	}
   501  
   502  	m.logger.Trace().Bool("clear", params.Clear).Msg("is clearing text required?")
   503  
   504  	if params.Clear {
   505  		m.logger.Trace().Msg("calculating clickable element points")
   506  
   507  		points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
   508  
   509  		if err != nil {
   510  			m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   511  
   512  			return err
   513  		}
   514  
   515  		m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   516  
   517  		if err := m.ClearByXY(ctx, points); err != nil {
   518  			return err
   519  		}
   520  	}
   521  
   522  	d := core.NumberLowerBoundary(float64(params.Delay))
   523  	beforeTypeDelay := time.Duration(d)
   524  
   525  	m.logger.Trace().Float64("delay", d).Msg("calculated pause delay")
   526  
   527  	time.Sleep(beforeTypeDelay)
   528  
   529  	m.logger.Trace().Msg("starting to type text")
   530  
   531  	if err := m.keyboard.Type(ctx, params.Text, params.Delay); err != nil {
   532  		m.logger.Trace().Err(err).Msg("failed to type text")
   533  
   534  		return err
   535  	}
   536  
   537  	m.logger.Trace().Msg("typed text")
   538  
   539  	return nil
   540  }
   541  
   542  func (m *Manager) TypeBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector, params TypeParams) error {
   543  	m.logger.Trace().
   544  		Str("parent_object_id", string(id)).
   545  		Str("selector", selector.String()).
   546  		Msg("starting to type text by selector")
   547  
   548  	err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
   549  		Behavior: drivers.ScrollBehaviorAuto,
   550  		Block:    drivers.ScrollVerticalAlignmentCenter,
   551  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   552  	})
   553  
   554  	if err != nil {
   555  		return err
   556  	}
   557  
   558  	m.logger.Trace().Msg("looking up for an element by selector")
   559  
   560  	found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
   561  
   562  	if err != nil {
   563  		m.logger.Trace().Err(err).Msg("failed to find an element by selector")
   564  
   565  		return err
   566  	}
   567  
   568  	if found.ObjectID == nil {
   569  		m.logger.Trace().
   570  			Err(core.ErrNotFound).
   571  			Msg("element not found by selector")
   572  
   573  		return core.ErrNotFound
   574  	}
   575  
   576  	m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("focusing on an element")
   577  
   578  	err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(*found.ObjectID))
   579  
   580  	if err != nil {
   581  		m.logger.Trace().Err(err).Msg("failed to focus on an element")
   582  
   583  		return err
   584  	}
   585  
   586  	m.logger.Trace().Bool("clear", params.Clear).Msg("is clearing text required?")
   587  
   588  	if params.Clear {
   589  		m.logger.Trace().Msg("calculating clickable element points")
   590  
   591  		points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
   592  
   593  		if err != nil {
   594  			m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   595  
   596  			return err
   597  		}
   598  
   599  		m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   600  
   601  		if err := m.ClearByXY(ctx, points); err != nil {
   602  			return err
   603  		}
   604  	}
   605  
   606  	d := core.NumberLowerBoundary(float64(params.Delay))
   607  	beforeTypeDelay := time.Duration(d)
   608  
   609  	m.logger.Trace().Float64("delay", d).Msg("calculated pause delay")
   610  
   611  	time.Sleep(beforeTypeDelay)
   612  
   613  	m.logger.Trace().Msg("starting to type text")
   614  
   615  	if err := m.keyboard.Type(ctx, params.Text, params.Delay); err != nil {
   616  		m.logger.Trace().Err(err).Msg("failed to type text")
   617  
   618  		return err
   619  	}
   620  
   621  	m.logger.Trace().Msg("typed text")
   622  
   623  	return nil
   624  }
   625  
   626  func (m *Manager) Clear(ctx context.Context, objectID runtime.RemoteObjectID) error {
   627  	m.logger.Trace().
   628  		Str("object_id", string(objectID)).
   629  		Msg("starting to clear element")
   630  
   631  	err := m.ScrollIntoView(ctx, objectID, drivers.ScrollOptions{
   632  		Behavior: drivers.ScrollBehaviorAuto,
   633  		Block:    drivers.ScrollVerticalAlignmentCenter,
   634  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   635  	})
   636  
   637  	if err != nil {
   638  		return err
   639  	}
   640  
   641  	m.logger.Trace().Msg("calculating clickable element points")
   642  
   643  	points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
   644  
   645  	if err != nil {
   646  		m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   647  
   648  		return err
   649  	}
   650  
   651  	m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   652  	m.logger.Trace().Msg("focusing on an element")
   653  
   654  	err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID))
   655  
   656  	if err != nil {
   657  		m.logger.Trace().Err(err).Msg("failed to focus on an element")
   658  
   659  		return err
   660  	}
   661  
   662  	m.logger.Trace().Msg("clearing element")
   663  
   664  	if err := m.ClearByXY(ctx, points); err != nil {
   665  		m.logger.Trace().Err(err).Msg("failed to clear element")
   666  
   667  		return err
   668  	}
   669  
   670  	m.logger.Trace().Msg("cleared element")
   671  
   672  	return nil
   673  }
   674  
   675  func (m *Manager) ClearBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector) error {
   676  	m.logger.Trace().
   677  		Str("parent_object_id", string(id)).
   678  		Str("selector", selector.String()).
   679  		Msg("starting to clear element by selector")
   680  
   681  	err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
   682  		Behavior: drivers.ScrollBehaviorAuto,
   683  		Block:    drivers.ScrollVerticalAlignmentCenter,
   684  		Inline:   drivers.ScrollHorizontalAlignmentCenter,
   685  	})
   686  
   687  	if err != nil {
   688  		return err
   689  	}
   690  
   691  	m.logger.Trace().Msg("looking up for an element by selector")
   692  
   693  	found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
   694  
   695  	if err != nil {
   696  		m.logger.Trace().Err(err).Msg("failed to find an element by selector")
   697  
   698  		return err
   699  	}
   700  
   701  	if found.ObjectID == nil {
   702  		m.logger.Trace().
   703  			Err(core.ErrNotFound).
   704  			Msg("element not found by selector")
   705  
   706  		return core.ErrNotFound
   707  	}
   708  
   709  	m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("calculating clickable element points")
   710  
   711  	points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
   712  
   713  	if err != nil {
   714  		m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
   715  
   716  		return err
   717  	}
   718  
   719  	m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
   720  
   721  	m.logger.Trace().Msg("focusing on an element")
   722  
   723  	err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(*found.ObjectID))
   724  
   725  	if err != nil {
   726  		m.logger.Trace().Err(err).Msg("failed to focus on an element")
   727  
   728  		return err
   729  	}
   730  
   731  	m.logger.Trace().Msg("clearing element")
   732  
   733  	if err := m.ClearByXY(ctx, points); err != nil {
   734  		m.logger.Trace().Err(err).Msg("failed to clear element")
   735  
   736  		return err
   737  	}
   738  
   739  	m.logger.Trace().Msg("cleared element")
   740  
   741  	return nil
   742  }
   743  
   744  func (m *Manager) ClearByXY(ctx context.Context, points Quad) error {
   745  	m.logger.Trace().
   746  		Float64("x", points.X).
   747  		Float64("y", points.Y).
   748  		Msg("starting to clear element by coordinates")
   749  
   750  	delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
   751  
   752  	m.logger.Trace().Dur("delay", delay).Msg("clicking mouse button to select text")
   753  
   754  	err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, 3)
   755  
   756  	if err != nil {
   757  		m.logger.Trace().Err(err).Msg("failed to click mouse button")
   758  
   759  		return err
   760  	}
   761  
   762  	delay = time.Duration(drivers.DefaultKeyboardDelay) * time.Millisecond
   763  
   764  	m.logger.Trace().Dur("delay", delay).Msg("pressing 'Backspace'")
   765  
   766  	if err := m.keyboard.Press(ctx, []string{"Backspace"}, 1, delay); err != nil {
   767  		m.logger.Trace().Err(err).Msg("failed to press 'Backspace'")
   768  
   769  		return err
   770  	}
   771  
   772  	return err
   773  }
   774  
   775  func (m *Manager) Press(ctx context.Context, keys []string, count int) error {
   776  	delay := time.Duration(drivers.DefaultKeyboardDelay) * time.Millisecond
   777  
   778  	m.logger.Trace().
   779  		Strs("keys", keys).
   780  		Int("count", count).
   781  		Dur("delay", delay).
   782  		Msg("pressing keyboard keys")
   783  
   784  	if err := m.keyboard.Press(ctx, keys, count, delay); err != nil {
   785  		m.logger.Trace().Err(err).Msg("failed to press keyboard keys")
   786  
   787  		return err
   788  	}
   789  
   790  	return nil
   791  }
   792  
   793  func (m *Manager) PressBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector, keys []string, count int) error {
   794  	m.logger.Trace().
   795  		Str("parent_object_id", string(id)).
   796  		Str("selector", selector.String()).
   797  		Strs("keys", keys).
   798  		Int("count", count).
   799  		Msg("starting to press keyboard keys by selector")
   800  
   801  	if err := m.FocusBySelector(ctx, id, selector); err != nil {
   802  		return err
   803  	}
   804  
   805  	return m.Press(ctx, keys, count)
   806  }
   807  
   808  func (m *Manager) Select(ctx context.Context, id runtime.RemoteObjectID, value *values.Array) (*values.Array, error) {
   809  	m.logger.Trace().
   810  		Str("object_id", string(id)).
   811  		Msg("starting to select values")
   812  
   813  	if err := m.Focus(ctx, id); err != nil {
   814  		return values.NewArray(0), err
   815  	}
   816  
   817  	m.logger.Trace().Msg("selecting values")
   818  	m.logger.Trace().Msg("evaluating a JS function")
   819  
   820  	val, err := m.exec.EvalValue(ctx, templates.Select(id, value))
   821  
   822  	if err != nil {
   823  		m.logger.Trace().Err(err).Msg("failed to evaluate a JS function")
   824  
   825  		return nil, err
   826  	}
   827  
   828  	m.logger.Trace().Msg("validating JS result")
   829  
   830  	arr, ok := val.(*values.Array)
   831  
   832  	if !ok {
   833  		m.logger.Trace().Err(err).Msg("JS result validation failed")
   834  
   835  		return values.NewArray(0), core.ErrUnexpected
   836  	}
   837  
   838  	m.logger.Trace().Msg("selected values")
   839  
   840  	return arr, nil
   841  }
   842  
   843  func (m *Manager) SelectBySelector(ctx context.Context, id runtime.RemoteObjectID, selector drivers.QuerySelector, value *values.Array) (*values.Array, error) {
   844  	m.logger.Trace().
   845  		Str("parent_object_id", string(id)).
   846  		Str("selector", selector.String()).
   847  		Msg("starting to select values by selector")
   848  
   849  	if err := m.FocusBySelector(ctx, id, selector); err != nil {
   850  		return values.NewArray(0), err
   851  	}
   852  
   853  	m.logger.Trace().Msg("selecting values")
   854  	m.logger.Trace().Msg("evaluating a JS function")
   855  
   856  	res, err := m.exec.EvalValue(ctx, templates.SelectBySelector(id, selector, value))
   857  
   858  	if err != nil {
   859  		m.logger.Trace().Err(err).Msg("failed to evaluate a JS function")
   860  
   861  		return values.NewArray(0), err
   862  	}
   863  
   864  	m.logger.Trace().Msg("validating JS result")
   865  
   866  	arr, ok := res.(*values.Array)
   867  
   868  	if !ok {
   869  		m.logger.Trace().Err(err).Msg("JS result validation failed")
   870  
   871  		return values.NewArray(0), core.ErrUnexpected
   872  	}
   873  
   874  	m.logger.Trace().Msg("selected values")
   875  
   876  	return arr, nil
   877  }