call.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package chromedp
  2. import (
  3. "context"
  4. "encoding/json"
  5. "github.com/chromedp/cdproto/runtime"
  6. )
  7. // CallAction are actions that calls a JavaScript function using
  8. // runtime.CallFunctionOn.
  9. type CallAction Action
  10. // CallFunctionOn is an action to call a JavaScript function, unmarshaling
  11. // the result of the function to res.
  12. //
  13. // The handling of res is the same as that of Evaluate.
  14. //
  15. // Do not call the following methods on runtime.CallFunctionOnParams:
  16. // - WithReturnByValue: it will be set depending on the type of res;
  17. // - WithArguments: pass the arguments with args instead.
  18. //
  19. // Note: any exception encountered will be returned as an error.
  20. func CallFunctionOn(functionDeclaration string, res interface{}, opt CallOption, args ...interface{}) CallAction {
  21. return ActionFunc(func(ctx context.Context) error {
  22. _, err := callFunctionOn(ctx, functionDeclaration, res, opt, args...)
  23. return err
  24. })
  25. }
  26. func callFunctionOn(ctx context.Context, functionDeclaration string, res interface{}, opt CallOption, args ...interface{}) (*runtime.RemoteObject, error) {
  27. // set up parameters
  28. p := runtime.CallFunctionOn(functionDeclaration).
  29. WithSilent(true)
  30. switch res.(type) {
  31. case **runtime.RemoteObject:
  32. default:
  33. p = p.WithReturnByValue(true)
  34. }
  35. // apply opt
  36. if opt != nil {
  37. p = opt(p)
  38. }
  39. // arguments
  40. if len(args) > 0 {
  41. ea := &errAppender{args: make([]*runtime.CallArgument, 0, len(args))}
  42. for _, arg := range args {
  43. ea.append(arg)
  44. }
  45. if ea.err != nil {
  46. return nil, ea.err
  47. }
  48. p = p.WithArguments(ea.args)
  49. }
  50. // call
  51. v, exp, err := p.Do(ctx)
  52. if err != nil {
  53. return nil, err
  54. }
  55. if exp != nil {
  56. return nil, exp
  57. }
  58. return v, parseRemoteObject(v, res)
  59. }
  60. // CallOption is a function to modify the runtime.CallFunctionOnParams
  61. // to provide more information.
  62. type CallOption = func(params *runtime.CallFunctionOnParams) *runtime.CallFunctionOnParams
  63. // errAppender is to help accumulating the arguments and simplifying error checks.
  64. //
  65. // see https://blog.golang.org/errors-are-values
  66. type errAppender struct {
  67. args []*runtime.CallArgument
  68. err error
  69. }
  70. // append method calls the json.Marshal method to marshal the value and appends it to the slice.
  71. // It records the first error for future reference.
  72. // As soon as an error occurs, the append method becomes a no-op but the error value is saved.
  73. func (ea *errAppender) append(v interface{}) {
  74. if ea.err != nil {
  75. return
  76. }
  77. var b []byte
  78. b, ea.err = json.Marshal(v)
  79. ea.args = append(ea.args, &runtime.CallArgument{Value: b})
  80. }