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

     1  package eval
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  
     7  	"github.com/mafredri/cdp"
     8  	"github.com/mafredri/cdp/protocol/page"
     9  	"github.com/mafredri/cdp/protocol/runtime"
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/MontFerret/ferret/pkg/drivers"
    13  	"github.com/MontFerret/ferret/pkg/runtime/core"
    14  	"github.com/MontFerret/ferret/pkg/runtime/values"
    15  )
    16  
    17  type (
    18  	ValueLoader interface {
    19  		Load(
    20  			ctx context.Context,
    21  			frameID page.FrameID,
    22  			remoteType RemoteObjectType,
    23  			remoteClass RemoteClassName,
    24  			id runtime.RemoteObjectID,
    25  		) (core.Value, error)
    26  	}
    27  
    28  	ValueLoaderFn func(
    29  		ctx context.Context,
    30  		frameID page.FrameID,
    31  		remoteType RemoteObjectType,
    32  		remoteClass RemoteClassName,
    33  		id runtime.RemoteObjectID,
    34  	) (core.Value, error)
    35  
    36  	Resolver struct {
    37  		runtime cdp.Runtime
    38  		frameID page.FrameID
    39  		loader  ValueLoader
    40  	}
    41  )
    42  
    43  func (f ValueLoaderFn) Load(
    44  	ctx context.Context,
    45  	frameID page.FrameID,
    46  	remoteType RemoteObjectType,
    47  	remoteClass RemoteClassName,
    48  	id runtime.RemoteObjectID,
    49  ) (core.Value, error) {
    50  	return f(ctx, frameID, remoteType, remoteClass, id)
    51  }
    52  
    53  func NewResolver(runtime cdp.Runtime, frameID page.FrameID) *Resolver {
    54  	return &Resolver{runtime, frameID, nil}
    55  }
    56  
    57  func (r *Resolver) SetLoader(loader ValueLoader) *Resolver {
    58  	r.loader = loader
    59  
    60  	return r
    61  }
    62  
    63  func (r *Resolver) ToValue(ctx context.Context, ref runtime.RemoteObject) (core.Value, error) {
    64  	// It's not an actual ref but rather a plain value
    65  	if ref.ObjectID == nil {
    66  		if ref.Value != nil {
    67  			return values.Unmarshal(ref.Value)
    68  		}
    69  
    70  		return values.None, nil
    71  	}
    72  
    73  	subtype := ToRemoteObjectType(ref)
    74  
    75  	switch subtype {
    76  	case NullObjectType, UndefinedObjectType:
    77  		return values.None, nil
    78  	case ArrayObjectType:
    79  		props, err := r.runtime.GetProperties(ctx, runtime.NewGetPropertiesArgs(*ref.ObjectID).SetOwnProperties(true))
    80  
    81  		if err != nil {
    82  			return values.None, err
    83  		}
    84  
    85  		if props.ExceptionDetails != nil {
    86  			exception := *props.ExceptionDetails
    87  
    88  			return values.None, errors.New(exception.Text)
    89  		}
    90  
    91  		result := values.NewArray(len(props.Result))
    92  
    93  		for _, descr := range props.Result {
    94  			if !descr.Enumerable {
    95  				continue
    96  			}
    97  
    98  			if descr.Value == nil {
    99  				continue
   100  			}
   101  
   102  			el, err := r.ToValue(ctx, *descr.Value)
   103  
   104  			if err != nil {
   105  				return values.None, err
   106  			}
   107  
   108  			result.Push(el)
   109  		}
   110  
   111  		return result, nil
   112  	case NodeObjectType:
   113  		// is it even possible?
   114  		if ref.ObjectID == nil {
   115  			return values.Unmarshal(ref.Value)
   116  		}
   117  
   118  		return r.loadValue(ctx, NodeObjectType, ToRemoteClassName(ref), *ref.ObjectID)
   119  	default:
   120  		switch ToRemoteType(ref) {
   121  		case StringType:
   122  			str, err := strconv.Unquote(string(ref.Value))
   123  
   124  			if err != nil {
   125  				return values.None, err
   126  			}
   127  
   128  			return values.NewString(str), nil
   129  		case ObjectType:
   130  			if subtype == NullObjectType || subtype == UnknownObjectType {
   131  				return values.None, nil
   132  			}
   133  
   134  			return values.Unmarshal(ref.Value)
   135  		default:
   136  			return values.Unmarshal(ref.Value)
   137  		}
   138  	}
   139  }
   140  
   141  func (r *Resolver) ToElement(ctx context.Context, ref runtime.RemoteObject) (drivers.HTMLElement, error) {
   142  	if ref.ObjectID == nil {
   143  		return nil, core.Error(core.ErrInvalidArgument, "ref id")
   144  	}
   145  
   146  	val, err := r.loadValue(ctx, ToRemoteObjectType(ref), ToRemoteClassName(ref), *ref.ObjectID)
   147  
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	return drivers.ToElement(val)
   153  }
   154  
   155  func (r *Resolver) ToProperty(
   156  	ctx context.Context,
   157  	id runtime.RemoteObjectID,
   158  	propName string,
   159  ) (core.Value, error) {
   160  	res, err := r.runtime.GetProperties(
   161  		ctx,
   162  		runtime.NewGetPropertiesArgs(id),
   163  	)
   164  
   165  	if err != nil {
   166  		return values.None, err
   167  	}
   168  
   169  	if err := parseRuntimeException(res.ExceptionDetails); err != nil {
   170  		return values.None, err
   171  	}
   172  
   173  	for _, prop := range res.Result {
   174  		if prop.Name == propName {
   175  			if prop.Value != nil {
   176  				return r.ToValue(ctx, *prop.Value)
   177  			}
   178  
   179  			return values.None, nil
   180  		}
   181  	}
   182  
   183  	return values.None, nil
   184  }
   185  
   186  func (r *Resolver) ToProperties(
   187  	ctx context.Context,
   188  	id runtime.RemoteObjectID,
   189  ) (*values.Array, error) {
   190  	res, err := r.runtime.GetProperties(
   191  		ctx,
   192  		runtime.NewGetPropertiesArgs(id),
   193  	)
   194  
   195  	if err != nil {
   196  		return values.EmptyArray(), err
   197  	}
   198  
   199  	if err := parseRuntimeException(res.ExceptionDetails); err != nil {
   200  		return values.EmptyArray(), err
   201  	}
   202  
   203  	arr := values.NewArray(len(res.Result))
   204  
   205  	for _, prop := range res.Result {
   206  		if prop.Value != nil {
   207  			val, err := r.ToValue(ctx, *prop.Value)
   208  
   209  			if err != nil {
   210  				return values.EmptyArray(), err
   211  			}
   212  
   213  			arr.Push(val)
   214  		}
   215  	}
   216  
   217  	return arr, nil
   218  }
   219  
   220  func (r *Resolver) loadValue(ctx context.Context, remoteType RemoteObjectType, remoteClass RemoteClassName, id runtime.RemoteObjectID) (core.Value, error) {
   221  	if r.loader == nil {
   222  		return values.None, core.Error(core.ErrNotImplemented, "ValueLoader")
   223  	}
   224  
   225  	return r.loader.Load(ctx, r.frameID, remoteType, remoteClass, id)
   226  }