|
|
|
@ -7,7 +7,6 @@ import (
|
|
|
|
|
"hash/fnv"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/mafredri/cdp"
|
|
|
|
@ -37,25 +36,15 @@ type (
|
|
|
|
|
ObjectID runtime.RemoteObjectID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elementListeners struct {
|
|
|
|
|
pageReload events.ListenerID
|
|
|
|
|
childNodeInserted events.ListenerID
|
|
|
|
|
childNodeRemoved events.ListenerID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HTMLElement struct {
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
logger *zerolog.Logger
|
|
|
|
|
client *cdp.Client
|
|
|
|
|
dom *Manager
|
|
|
|
|
input *input.Manager
|
|
|
|
|
exec *eval.ExecutionContext
|
|
|
|
|
connected values.Boolean
|
|
|
|
|
id HTMLElementIdentity
|
|
|
|
|
nodeType html.NodeType
|
|
|
|
|
nodeName values.String
|
|
|
|
|
children []HTMLElementIdentity
|
|
|
|
|
listeners *elementListeners
|
|
|
|
|
logger *zerolog.Logger
|
|
|
|
|
client *cdp.Client
|
|
|
|
|
dom *Manager
|
|
|
|
|
input *input.Manager
|
|
|
|
|
exec *eval.ExecutionContext
|
|
|
|
|
id HTMLElementIdentity
|
|
|
|
|
nodeType html.NodeType
|
|
|
|
|
nodeName values.String
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
@ -113,7 +102,7 @@ func LoadHTMLElementWithID(
|
|
|
|
|
dom.
|
|
|
|
|
NewDescribeNodeArgs().
|
|
|
|
|
SetObjectID(id.ObjectID).
|
|
|
|
|
SetDepth(1),
|
|
|
|
|
SetDepth(0),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -129,7 +118,6 @@ func LoadHTMLElementWithID(
|
|
|
|
|
id,
|
|
|
|
|
node.Node.NodeType,
|
|
|
|
|
node.Node.NodeName,
|
|
|
|
|
createChildrenArray(node.Node.Children),
|
|
|
|
|
), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -142,7 +130,6 @@ func NewHTMLElement(
|
|
|
|
|
id HTMLElementIdentity,
|
|
|
|
|
nodeType int,
|
|
|
|
|
nodeName string,
|
|
|
|
|
children []HTMLElementIdentity,
|
|
|
|
|
) *HTMLElement {
|
|
|
|
|
el := new(HTMLElement)
|
|
|
|
|
el.logger = logger
|
|
|
|
@ -150,35 +137,14 @@ func NewHTMLElement(
|
|
|
|
|
el.dom = domManager
|
|
|
|
|
el.input = input
|
|
|
|
|
el.exec = exec
|
|
|
|
|
el.connected = values.True
|
|
|
|
|
el.id = id
|
|
|
|
|
el.nodeType = common.ToHTMLType(nodeType)
|
|
|
|
|
el.nodeName = values.NewString(nodeName)
|
|
|
|
|
el.children = children
|
|
|
|
|
el.listeners = &elementListeners{
|
|
|
|
|
pageReload: domManager.AddDocumentUpdatedListener(el.handlePageReload),
|
|
|
|
|
childNodeInserted: domManager.AddChildNodeInsertedListener(el.handleChildInserted),
|
|
|
|
|
childNodeRemoved: domManager.AddChildNodeRemovedListener(el.handleChildRemoved),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return el
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) Close() error {
|
|
|
|
|
el.mu.Lock()
|
|
|
|
|
defer el.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
// already closed
|
|
|
|
|
if !el.connected {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
el.connected = values.False
|
|
|
|
|
|
|
|
|
|
el.dom.RemoveReloadListener(el.listeners.pageReload)
|
|
|
|
|
el.dom.RemoveChildNodeInsertedListener(el.listeners.childNodeInserted)
|
|
|
|
|
el.dom.RemoveChildNodeRemovedListener(el.listeners.childNodeRemoved)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -247,18 +213,10 @@ func (el *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetValue(ctx context.Context) (core.Value, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.None, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return el.exec.ReadProperty(ctx, el.id.ObjectID, "value")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetValue(ctx context.Context, value core.Value) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return el.client.DOM.SetNodeValue(ctx, dom.NewSetNodeValueArgs(el.id.NodeID, value.String()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -271,14 +229,20 @@ func (el *HTMLElement) GetNodeName() values.String {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) Length() values.Int {
|
|
|
|
|
return values.NewInt(len(el.children))
|
|
|
|
|
value, err := el.exec.EvalWithArgumentsAndReturnValue(context.Background(), templates.GetChildrenCount(), runtime.CallArgument{
|
|
|
|
|
ObjectID: &el.id.ObjectID,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
el.logError(err)
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values.ToInt(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.NewObject(), drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value, err := el.exec.EvalWithArgumentsAndReturnValue(ctx, templates.GetStyles(), runtime.CallArgument{
|
|
|
|
|
ObjectID: &el.id.ObjectID,
|
|
|
|
|
})
|
|
|
|
@ -295,10 +259,6 @@ func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.Value, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.None, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
styles, err := el.GetStyles(ctx)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -315,10 +275,6 @@ func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.V
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetStyles(ctx context.Context, styles *values.Object) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if styles == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -341,10 +297,6 @@ func (el *HTMLElement) SetStyles(ctx context.Context, styles *values.Object) err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetStyle(ctx context.Context, name, value values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we manually set only those that are defined in attribute only
|
|
|
|
|
attrValue, err := el.GetAttribute(ctx, common.AttrNameStyle)
|
|
|
|
|
|
|
|
|
@ -374,10 +326,6 @@ func (el *HTMLElement) SetStyle(ctx context.Context, name, value values.String)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(names) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -409,10 +357,6 @@ func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetAttributes(ctx context.Context) (*values.Object, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.NewObject(), drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.NodeID))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -446,10 +390,6 @@ func (el *HTMLElement) GetAttributes(ctx context.Context) (*values.Object, error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) (core.Value, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.None, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.NodeID))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -483,10 +423,6 @@ func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) (co
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
attrs.ForEach(func(value core.Value, key string) bool {
|
|
|
|
@ -499,10 +435,6 @@ func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return el.client.DOM.SetAttributeValue(
|
|
|
|
|
ctx,
|
|
|
|
|
dom.NewSetAttributeValueArgs(el.id.NodeID, string(name), string(value)),
|
|
|
|
@ -510,10 +442,6 @@ func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.Stri
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, name := range names {
|
|
|
|
|
err := el.client.DOM.RemoveAttribute(
|
|
|
|
|
ctx,
|
|
|
|
@ -529,51 +457,45 @@ func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.Stri
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.NewArray(0), drivers.ErrDetached
|
|
|
|
|
out, err := el.exec.EvalWithArgumentsAndReturnReference(ctx, templates.GetChildren(),
|
|
|
|
|
runtime.CallArgument{
|
|
|
|
|
ObjectID: &el.id.ObjectID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res := values.NewArray(len(el.children))
|
|
|
|
|
val, err := el.convertEvalResult(ctx, out)
|
|
|
|
|
|
|
|
|
|
for _, childID := range el.children {
|
|
|
|
|
child, err := LoadHTMLElement(
|
|
|
|
|
ctx,
|
|
|
|
|
el.logger,
|
|
|
|
|
el.client,
|
|
|
|
|
el.dom,
|
|
|
|
|
el.input,
|
|
|
|
|
el.exec,
|
|
|
|
|
childID.NodeID,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
el.logError(err)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
el.logError(err).Msg("failed to load child elements")
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.Push(child)
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
|
arr, ok := val.(*values.Array)
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
return arr, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values.EmptyArray(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.None, drivers.ErrDetached
|
|
|
|
|
out, err := el.exec.EvalWithArgumentsAndReturnReference(ctx, templates.GetChildByIndex(int64(idx)),
|
|
|
|
|
runtime.CallArgument{
|
|
|
|
|
ObjectID: &el.id.ObjectID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nodeIdentity := el.children[idx]
|
|
|
|
|
|
|
|
|
|
return LoadHTMLElement(
|
|
|
|
|
ctx,
|
|
|
|
|
el.logger,
|
|
|
|
|
el.client,
|
|
|
|
|
el.dom,
|
|
|
|
|
el.input,
|
|
|
|
|
el.exec,
|
|
|
|
|
nodeIdentity.NodeID,
|
|
|
|
|
)
|
|
|
|
|
return el.convertEvalResult(ctx, out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetParentElement(ctx context.Context) (core.Value, error) {
|
|
|
|
@ -589,10 +511,6 @@ func (el *HTMLElement) GetNextElementSibling(ctx context.Context) (core.Value, e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) evalAndGetElement(ctx context.Context, expr string) (core.Value, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.None, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obj, err := el.exec.EvalWithArgumentsAndReturnReference(ctx, expr, runtime.CallArgument{
|
|
|
|
|
ObjectID: &el.id.ObjectID,
|
|
|
|
|
})
|
|
|
|
@ -626,10 +544,6 @@ func (el *HTMLElement) evalAndGetElement(ctx context.Context, expr string) (core
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String) (core.Value, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.None, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectorArgs := dom.NewQuerySelectorArgs(el.id.NodeID, selector.String())
|
|
|
|
|
found, err := el.client.DOM.QuerySelector(ctx, selectorArgs)
|
|
|
|
|
|
|
|
|
@ -659,15 +573,11 @@ func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.NewArray(0), drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.NodeID, selector.String())
|
|
|
|
|
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.NewArray(0), err
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arr := values.NewArray(len(res.NodeIDs))
|
|
|
|
@ -692,16 +602,7 @@ func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.Str
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
// close elements that are already loaded, but won't be used because of the error
|
|
|
|
|
if arr.Length() > 0 {
|
|
|
|
|
arr.ForEach(func(e core.Value, _ int) bool {
|
|
|
|
|
e.(*HTMLElement).Close()
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values.NewArray(0), err
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arr.Push(childEl)
|
|
|
|
@ -730,155 +631,18 @@ func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (res
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typeName := out.Type
|
|
|
|
|
|
|
|
|
|
// checking whether it's actually an array
|
|
|
|
|
if typeName == "object" {
|
|
|
|
|
if out.ClassName != nil && *out.ClassName == "Array" {
|
|
|
|
|
typeName = "array"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch typeName {
|
|
|
|
|
case "string", "number", "boolean":
|
|
|
|
|
return eval.Unmarshal(&out)
|
|
|
|
|
case "array":
|
|
|
|
|
if out.ObjectID == nil {
|
|
|
|
|
return values.None, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
props, err := el.client.Runtime.GetProperties(ctx, runtime.NewGetPropertiesArgs(*out.ObjectID).SetOwnProperties(true))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if props.ExceptionDetails != nil {
|
|
|
|
|
exception := *props.ExceptionDetails
|
|
|
|
|
|
|
|
|
|
return values.None, errors.New(exception.Text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := values.NewArray(len(props.Result))
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
if err != nil {
|
|
|
|
|
result.ForEach(func(value core.Value, idx int) bool {
|
|
|
|
|
el, ok := value.(*HTMLElement)
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
el.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
for _, descr := range props.Result {
|
|
|
|
|
if !descr.Enumerable {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if descr.Value == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// it's not a Node, it's an attr value
|
|
|
|
|
if descr.Value.ObjectID == nil {
|
|
|
|
|
var value interface{}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(descr.Value.Value, &value); err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Push(values.Parse(value))
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repl, err := el.client.DOM.RequestNode(ctx, dom.NewRequestNodeArgs(*descr.Value.ObjectID))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
el, err := LoadHTMLElementWithID(
|
|
|
|
|
ctx,
|
|
|
|
|
el.logger,
|
|
|
|
|
el.client,
|
|
|
|
|
el.dom,
|
|
|
|
|
el.input,
|
|
|
|
|
el.exec,
|
|
|
|
|
HTMLElementIdentity{
|
|
|
|
|
NodeID: repl.NodeID,
|
|
|
|
|
ObjectID: *descr.Value.ObjectID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Push(el)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
case "object":
|
|
|
|
|
if out.ObjectID == nil {
|
|
|
|
|
var value interface{}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(out.Value, &value); err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values.Parse(value), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repl, err := el.client.DOM.RequestNode(ctx, dom.NewRequestNodeArgs(*out.ObjectID))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LoadHTMLElementWithID(
|
|
|
|
|
ctx,
|
|
|
|
|
el.logger,
|
|
|
|
|
el.client,
|
|
|
|
|
el.dom,
|
|
|
|
|
el.input,
|
|
|
|
|
el.exec,
|
|
|
|
|
HTMLElementIdentity{
|
|
|
|
|
NodeID: repl.NodeID,
|
|
|
|
|
ObjectID: *out.ObjectID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
default:
|
|
|
|
|
return values.None, nil
|
|
|
|
|
}
|
|
|
|
|
return el.convertEvalResult(ctx, out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetInnerText(ctx context.Context) (values.String, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.EmptyString, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return getInnerText(ctx, el.client, el.exec, el.id, el.nodeType)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetInnerText(ctx context.Context, innerText values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return setInnerText(ctx, el.client, el.exec, el.id, innerText)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetInnerTextBySelector(ctx context.Context, selector values.String) (values.String, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.EmptyString, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, err := selector.MarshalJSON()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -904,10 +668,6 @@ func (el *HTMLElement) GetInnerTextBySelector(ctx context.Context, selector valu
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetInnerTextBySelector(ctx context.Context, selector, innerText values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, err := selector.MarshalJSON()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -936,10 +696,6 @@ func (el *HTMLElement) SetInnerTextBySelector(ctx context.Context, selector, inn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetInnerTextBySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.NewArray(0), drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, err := selector.MarshalJSON()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -958,39 +714,27 @@ func (el *HTMLElement) GetInnerTextBySelectorAll(ctx context.Context, selector v
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.NewArray(0), err
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arr, ok := out.(*values.Array)
|
|
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
return values.NewArray(0), errors.New("unexpected output")
|
|
|
|
|
return values.EmptyArray(), errors.New("unexpected output")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return arr, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetInnerHTML(ctx context.Context) (values.String, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.EmptyString, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return getInnerHTML(ctx, el.client, el.exec, el.id, el.nodeType)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetInnerHTML(ctx context.Context, innerHTML values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return setInnerHTML(ctx, el.client, el.exec, el.id, innerHTML)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetInnerHTMLBySelector(ctx context.Context, selector values.String) (values.String, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.EmptyString, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, err := selector.MarshalJSON()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -1016,10 +760,6 @@ func (el *HTMLElement) GetInnerHTMLBySelector(ctx context.Context, selector valu
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) SetInnerHTMLBySelector(ctx context.Context, selector, innerHTML values.String) error {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, err := selector.MarshalJSON()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -1048,14 +788,10 @@ func (el *HTMLElement) SetInnerHTMLBySelector(ctx context.Context, selector, inn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) GetInnerHTMLBySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.NewArray(0), drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, err := selector.MarshalJSON()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.NewArray(0), err
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out, err := el.exec.EvalWithArgumentsAndReturnValue(
|
|
|
|
@ -1070,23 +806,19 @@ func (el *HTMLElement) GetInnerHTMLBySelectorAll(ctx context.Context, selector v
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.NewArray(0), err
|
|
|
|
|
return values.EmptyArray(), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arr, ok := out.(*values.Array)
|
|
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
return values.NewArray(0), errors.New("unexpected output")
|
|
|
|
|
return values.EmptyArray(), errors.New("unexpected output")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return arr, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) CountBySelector(ctx context.Context, selector values.String) (values.Int, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.ZeroInt, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.NodeID, selector.String())
|
|
|
|
|
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
|
|
|
|
|
|
|
|
|
@ -1098,11 +830,6 @@ func (el *HTMLElement) CountBySelector(ctx context.Context, selector values.Stri
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector values.String) (values.Boolean, error) {
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return values.False, drivers.ErrDetached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
|
|
|
|
|
selectorArgs := dom.NewQuerySelectorArgs(el.id.NodeID, selector.String())
|
|
|
|
|
res, err := el.client.DOM.QuerySelector(ctx, selectorArgs)
|
|
|
|
|
|
|
|
|
@ -1270,80 +997,126 @@ func (el *HTMLElement) HoverBySelector(ctx context.Context, selector values.Stri
|
|
|
|
|
return el.input.MoveMouseBySelector(ctx, el.id.NodeID, selector.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) IsDetached() values.Boolean {
|
|
|
|
|
el.mu.Lock()
|
|
|
|
|
defer el.mu.Unlock()
|
|
|
|
|
func (el *HTMLElement) convertEvalResult(ctx context.Context, out runtime.RemoteObject) (core.Value, error) {
|
|
|
|
|
typeName := out.Type
|
|
|
|
|
|
|
|
|
|
return !el.connected
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) handlePageReload(_ context.Context) {
|
|
|
|
|
el.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) handleChildInserted(_ context.Context, parentNodeID, prevNodeID dom.NodeID, node dom.Node) {
|
|
|
|
|
if parentNodeID != el.id.NodeID {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetIDx := -1
|
|
|
|
|
prevID := prevNodeID
|
|
|
|
|
nextID := node.NodeID
|
|
|
|
|
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
el.mu.Lock()
|
|
|
|
|
defer el.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
for idx, id := range el.children {
|
|
|
|
|
if id.NodeID == prevID {
|
|
|
|
|
targetIDx = idx
|
|
|
|
|
break
|
|
|
|
|
// checking whether it's actually an array
|
|
|
|
|
if typeName == "object" {
|
|
|
|
|
if out.ClassName != nil {
|
|
|
|
|
switch *out.ClassName {
|
|
|
|
|
case "Array":
|
|
|
|
|
typeName = "array"
|
|
|
|
|
case "HTMLCollection":
|
|
|
|
|
typeName = "HTMLCollection"
|
|
|
|
|
default:
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if targetIDx == -1 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextIdentity := HTMLElementIdentity{
|
|
|
|
|
NodeID: nextID,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arr := el.children
|
|
|
|
|
el.children = append(arr[:targetIDx], append([]HTMLElementIdentity{nextIdentity}, arr[targetIDx:]...)...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) handleChildRemoved(_ context.Context, nodeID, prevNodeID dom.NodeID) {
|
|
|
|
|
if nodeID != el.id.NodeID {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetIDx := -1
|
|
|
|
|
targetID := prevNodeID
|
|
|
|
|
|
|
|
|
|
if el.IsDetached() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
el.mu.Lock()
|
|
|
|
|
defer el.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
for idx, id := range el.children {
|
|
|
|
|
if id.NodeID == targetID {
|
|
|
|
|
targetIDx = idx
|
|
|
|
|
break
|
|
|
|
|
switch typeName {
|
|
|
|
|
case "string", "number", "boolean":
|
|
|
|
|
return eval.Unmarshal(&out)
|
|
|
|
|
case "array":
|
|
|
|
|
if out.ObjectID == nil {
|
|
|
|
|
return values.None, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if targetIDx == -1 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
props, err := el.client.Runtime.GetProperties(ctx, runtime.NewGetPropertiesArgs(*out.ObjectID).SetOwnProperties(true))
|
|
|
|
|
|
|
|
|
|
arr := el.children
|
|
|
|
|
el.children = append(arr[:targetIDx], arr[targetIDx+1:]...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if props.ExceptionDetails != nil {
|
|
|
|
|
exception := *props.ExceptionDetails
|
|
|
|
|
|
|
|
|
|
return values.None, errors.New(exception.Text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := values.NewArray(len(props.Result))
|
|
|
|
|
|
|
|
|
|
for _, descr := range props.Result {
|
|
|
|
|
if !descr.Enumerable {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if descr.Value == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// it's not a Node, it's an attr value
|
|
|
|
|
if descr.Value.ObjectID == nil {
|
|
|
|
|
var value interface{}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(descr.Value.Value, &value); err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Push(values.Parse(value))
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repl, err := el.client.DOM.RequestNode(ctx, dom.NewRequestNodeArgs(*descr.Value.ObjectID))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
el, err := LoadHTMLElementWithID(
|
|
|
|
|
ctx,
|
|
|
|
|
el.logger,
|
|
|
|
|
el.client,
|
|
|
|
|
el.dom,
|
|
|
|
|
el.input,
|
|
|
|
|
el.exec,
|
|
|
|
|
HTMLElementIdentity{
|
|
|
|
|
NodeID: repl.NodeID,
|
|
|
|
|
ObjectID: *descr.Value.ObjectID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Push(el)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
case "object":
|
|
|
|
|
if out.ObjectID == nil {
|
|
|
|
|
var value interface{}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(out.Value, &value); err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values.Parse(value), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repl, err := el.client.DOM.RequestNode(ctx, dom.NewRequestNodeArgs(*out.ObjectID))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return values.None, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LoadHTMLElementWithID(
|
|
|
|
|
ctx,
|
|
|
|
|
el.logger,
|
|
|
|
|
el.client,
|
|
|
|
|
el.dom,
|
|
|
|
|
el.input,
|
|
|
|
|
el.exec,
|
|
|
|
|
HTMLElementIdentity{
|
|
|
|
|
NodeID: repl.NodeID,
|
|
|
|
|
ObjectID: *out.ObjectID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
default:
|
|
|
|
|
return values.None, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (el *HTMLElement) logError(err error) *zerolog.Event {
|
|
|
|
|