mirror of
https://github.com/MontFerret/ferret.git
synced 2025-01-04 03:02:02 +02:00
Removed caching and property tracking (#531)
* Removed caching and property tracking * Fixed linting issues * Removed old go version from CI/CD
This commit is contained in:
parent
de377775a9
commit
079ec3a3ce
@ -6,8 +6,7 @@ os:
|
||||
- linux
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- stable
|
||||
|
||||
env:
|
||||
|
@ -37,32 +37,24 @@ type (
|
||||
}
|
||||
|
||||
elementListeners struct {
|
||||
pageReload events.ListenerID
|
||||
attrModified events.ListenerID
|
||||
attrRemoved events.ListenerID
|
||||
childNodeCountUpdated events.ListenerID
|
||||
childNodeInserted events.ListenerID
|
||||
childNodeRemoved events.ListenerID
|
||||
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
|
||||
innerHTML *common.LazyValue
|
||||
innerText *common.LazyValue
|
||||
attributes *common.LazyValue
|
||||
style *common.LazyValue
|
||||
children []HTMLElementIdentity
|
||||
loadedChildren *common.LazyValue
|
||||
listeners *elementListeners
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
@ -161,19 +153,11 @@ func NewHTMLElement(
|
||||
el.id = id
|
||||
el.nodeType = common.ToHTMLType(nodeType)
|
||||
el.nodeName = values.NewString(nodeName)
|
||||
el.innerHTML = common.NewLazyValue(el.loadInnerHTML)
|
||||
el.innerText = common.NewLazyValue(el.loadInnerText)
|
||||
el.attributes = common.NewLazyValue(el.loadAttrs)
|
||||
el.style = common.NewLazyValue(el.parseStyle)
|
||||
el.loadedChildren = common.NewLazyValue(el.loadChildren)
|
||||
el.children = children
|
||||
el.listeners = &elementListeners{
|
||||
pageReload: domManager.AddDocumentUpdatedListener(el.handlePageReload),
|
||||
attrModified: domManager.AddAttrModifiedListener(el.handleAttrModified),
|
||||
attrRemoved: domManager.AddAttrRemovedListener(el.handleAttrRemoved),
|
||||
childNodeCountUpdated: domManager.AddChildNodeCountUpdatedListener(el.handleChildrenCountChanged),
|
||||
childNodeInserted: domManager.AddChildNodeInsertedListener(el.handleChildInserted),
|
||||
childNodeRemoved: domManager.AddChildNodeRemovedListener(el.handleChildRemoved),
|
||||
pageReload: domManager.AddDocumentUpdatedListener(el.handlePageReload),
|
||||
childNodeInserted: domManager.AddChildNodeInsertedListener(el.handleChildInserted),
|
||||
childNodeRemoved: domManager.AddChildNodeRemovedListener(el.handleChildRemoved),
|
||||
}
|
||||
|
||||
return el
|
||||
@ -191,9 +175,6 @@ func (el *HTMLElement) Close() error {
|
||||
el.connected = values.False
|
||||
|
||||
el.dom.RemoveReloadListener(el.listeners.pageReload)
|
||||
el.dom.RemoveAttrModifiedListener(el.listeners.attrModified)
|
||||
el.dom.RemoveAttrRemovedListener(el.listeners.attrRemoved)
|
||||
el.dom.RemoveChildNodeCountUpdatedListener(el.listeners.childNodeCountUpdated)
|
||||
el.dom.RemoveChildNodeInsertedListener(el.listeners.childNodeInserted)
|
||||
el.dom.RemoveChildNodeRemovedListener(el.listeners.childNodeRemoved)
|
||||
|
||||
@ -293,29 +274,39 @@ func (el *HTMLElement) Length() values.Int {
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) {
|
||||
val, err := el.style.Read(ctx)
|
||||
if el.IsDetached() {
|
||||
return values.NewObject(), drivers.ErrDetached
|
||||
}
|
||||
|
||||
value, err := el.GetAttribute(ctx, "style")
|
||||
|
||||
if err != nil {
|
||||
return values.NewObject(), err
|
||||
}
|
||||
|
||||
if val == values.None {
|
||||
if value == values.None {
|
||||
return values.NewObject(), nil
|
||||
}
|
||||
|
||||
styles := val.(*values.Object)
|
||||
if value.Type() != types.String {
|
||||
return values.NewObject(), nil
|
||||
}
|
||||
|
||||
return styles.Copy().(*values.Object), nil
|
||||
return common.DeserializeStyles(value.(values.String))
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.Value, error) {
|
||||
styles, err := el.style.Read(ctx)
|
||||
if el.IsDetached() {
|
||||
return values.None, drivers.ErrDetached
|
||||
}
|
||||
|
||||
styles, err := el.GetStyles(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
val, found := styles.(*values.Object).Get(name)
|
||||
val, found := styles.Get(name)
|
||||
|
||||
if !found {
|
||||
return values.None, nil
|
||||
@ -325,18 +316,20 @@ 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
|
||||
}
|
||||
|
||||
val, err := el.style.Read(ctx)
|
||||
currentStyles, err := el.GetStyles(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentStyles := val.(*values.Object)
|
||||
|
||||
styles.ForEach(func(value core.Value, key string) bool {
|
||||
currentStyles.Set(values.NewString(key), value)
|
||||
|
||||
@ -349,13 +342,16 @@ func (el *HTMLElement) SetStyles(ctx context.Context, styles *values.Object) err
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetStyle(ctx context.Context, name values.String, value core.Value) error {
|
||||
val, err := el.style.Read(ctx)
|
||||
if el.IsDetached() {
|
||||
return drivers.ErrDetached
|
||||
}
|
||||
|
||||
styles, err := el.GetStyles(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
styles := val.(*values.Object)
|
||||
styles.Set(name, value)
|
||||
|
||||
str := common.SerializeStyles(ctx, styles)
|
||||
@ -364,18 +360,20 @@ func (el *HTMLElement) SetStyle(ctx context.Context, name values.String, value c
|
||||
}
|
||||
|
||||
func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String) error {
|
||||
if el.IsDetached() {
|
||||
return drivers.ErrDetached
|
||||
}
|
||||
|
||||
if len(names) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
val, err := el.style.Read(ctx)
|
||||
styles, err := el.GetStyles(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
styles := val.(*values.Object)
|
||||
|
||||
for _, name := range names {
|
||||
styles.Remove(name)
|
||||
}
|
||||
@ -386,35 +384,59 @@ func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetAttributes(ctx context.Context) (*values.Object, error) {
|
||||
val, err := el.attributes.Read(ctx)
|
||||
if el.IsDetached() {
|
||||
return values.NewObject(), drivers.ErrDetached
|
||||
}
|
||||
|
||||
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return values.NewObject(), err
|
||||
}
|
||||
|
||||
attrs := val.(*values.Object)
|
||||
attrs := values.NewObject()
|
||||
|
||||
// returning shallow copy
|
||||
return attrs.Copy().(*values.Object), nil
|
||||
traverseAttrs(repl.Attributes, func(name, value string) bool {
|
||||
attrs.Set(values.NewString(name), values.NewString(value))
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) (core.Value, error) {
|
||||
attrs, err := el.attributes.Read(ctx)
|
||||
if el.IsDetached() {
|
||||
return values.None, drivers.ErrDetached
|
||||
}
|
||||
|
||||
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
val, found := attrs.(*values.Object).Get(name)
|
||||
result := values.EmptyString
|
||||
targetName := strings.ToLower(name.String())
|
||||
|
||||
if !found {
|
||||
return values.None, nil
|
||||
}
|
||||
traverseAttrs(repl.Attributes, func(name, value string) bool {
|
||||
if name == targetName {
|
||||
result = values.NewString(value)
|
||||
|
||||
return val, nil
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -427,6 +449,10 @@ 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)),
|
||||
@ -434,6 +460,10 @@ 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,
|
||||
@ -449,23 +479,51 @@ func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.Stri
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
||||
val, err := el.loadedChildren.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.NewArray(0), err
|
||||
if el.IsDetached() {
|
||||
return values.NewArray(0), drivers.ErrDetached
|
||||
}
|
||||
|
||||
return val.Copy().(*values.Array), nil
|
||||
res := values.NewArray(len(el.children))
|
||||
|
||||
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).Msg("failed to load child elements")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
res.Push(child)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) {
|
||||
val, err := el.loadedChildren.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
if el.IsDetached() {
|
||||
return values.None, drivers.ErrDetached
|
||||
}
|
||||
|
||||
return val.(*values.Array).Get(idx), nil
|
||||
nodeIdentity := el.children[idx]
|
||||
|
||||
return LoadHTMLElement(
|
||||
ctx,
|
||||
el.logger,
|
||||
el.client,
|
||||
el.dom,
|
||||
el.input,
|
||||
el.exec,
|
||||
nodeIdentity.NodeID,
|
||||
)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String) (core.Value, error) {
|
||||
@ -702,17 +760,11 @@ func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (res
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetInnerText(ctx context.Context) (values.String, error) {
|
||||
val, err := el.innerText.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.EmptyString, err
|
||||
if el.IsDetached() {
|
||||
return values.EmptyString, drivers.ErrDetached
|
||||
}
|
||||
|
||||
if val == values.None {
|
||||
return values.EmptyString, nil
|
||||
}
|
||||
|
||||
return val.(values.String), nil
|
||||
return getInnerText(ctx, el.client, el.exec, el.id, el.nodeType)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetInnerText(ctx context.Context, innerText values.String) error {
|
||||
@ -720,8 +772,6 @@ func (el *HTMLElement) SetInnerText(ctx context.Context, innerText values.String
|
||||
return drivers.ErrDetached
|
||||
}
|
||||
|
||||
el.innerText.Reset()
|
||||
|
||||
return setInnerText(ctx, el.client, el.exec, el.id, innerText)
|
||||
}
|
||||
|
||||
@ -822,17 +872,11 @@ func (el *HTMLElement) GetInnerTextBySelectorAll(ctx context.Context, selector v
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetInnerHTML(ctx context.Context) (values.String, error) {
|
||||
val, err := el.innerHTML.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.EmptyString, err
|
||||
if el.IsDetached() {
|
||||
return values.EmptyString, drivers.ErrDetached
|
||||
}
|
||||
|
||||
if val == values.None {
|
||||
return values.EmptyString, nil
|
||||
}
|
||||
|
||||
return val.(values.String), nil
|
||||
return getInnerHTML(ctx, el.client, el.exec, el.id, el.nodeType)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetInnerHTML(ctx context.Context, innerHTML values.String) error {
|
||||
@ -840,8 +884,6 @@ func (el *HTMLElement) SetInnerHTML(ctx context.Context, innerHTML values.String
|
||||
return drivers.ErrDetached
|
||||
}
|
||||
|
||||
el.innerHTML.Reset()
|
||||
|
||||
return setInnerHTML(ctx, el.client, el.exec, el.id, innerHTML)
|
||||
}
|
||||
|
||||
@ -1136,213 +1178,11 @@ func (el *HTMLElement) IsDetached() values.Boolean {
|
||||
return !el.connected
|
||||
}
|
||||
|
||||
func (el *HTMLElement) loadInnerHTML(ctx context.Context) (core.Value, error) {
|
||||
if el.IsDetached() {
|
||||
return values.None, drivers.ErrDetached
|
||||
}
|
||||
|
||||
return getInnerHTML(ctx, el.client, el.exec, el.id, el.nodeType)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) loadInnerText(ctx context.Context) (core.Value, error) {
|
||||
if !el.IsDetached() {
|
||||
text, err := getInnerText(ctx, el.client, el.exec, el.id, el.nodeType)
|
||||
|
||||
if err == nil {
|
||||
return text, nil
|
||||
}
|
||||
|
||||
el.logError(err).Msg("failed to get inner text from remote object")
|
||||
|
||||
// and just parse cached innerHTML
|
||||
}
|
||||
|
||||
h, err := el.GetInnerHTML(ctx)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to get inner html from remote object")
|
||||
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if h == values.EmptyString {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
parsed, err := parseInnerText(h.String())
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to parse inner html")
|
||||
|
||||
return values.EmptyString, err
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) loadAttrs(ctx context.Context) (core.Value, error) {
|
||||
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return parseAttrs(repl.Attributes), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) loadChildren(ctx context.Context) (core.Value, error) {
|
||||
if el.IsDetached() {
|
||||
return values.NewArray(0), nil
|
||||
}
|
||||
|
||||
loaded := values.NewArray(len(el.children))
|
||||
|
||||
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).Msg("failed to load child elements")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
loaded.Push(child)
|
||||
}
|
||||
|
||||
return loaded, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) parseStyle(ctx context.Context) (core.Value, error) {
|
||||
value, err := el.GetAttribute(ctx, "style")
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if value == values.None {
|
||||
return values.NewObject(), nil
|
||||
}
|
||||
|
||||
if value.Type() != types.String {
|
||||
return values.NewObject(), nil
|
||||
}
|
||||
|
||||
return common.DeserializeStyles(value.(values.String))
|
||||
}
|
||||
|
||||
func (el *HTMLElement) handlePageReload(_ context.Context) {
|
||||
el.Close()
|
||||
}
|
||||
|
||||
func (el *HTMLElement) handleAttrModified(ctx context.Context, nodeID dom.NodeID, name, value string) {
|
||||
// it's not for this el
|
||||
if nodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
// they are not event loaded
|
||||
// just ignore the event
|
||||
if !el.attributes.Ready() {
|
||||
return
|
||||
}
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.attributes.Mutate(ctx, func(v core.Value, err error) {
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to update element")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if name == "style" {
|
||||
el.style.Reset()
|
||||
}
|
||||
|
||||
attrs, ok := v.(*values.Object)
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
attrs.Set(values.NewString(name), values.NewString(value))
|
||||
})
|
||||
}
|
||||
|
||||
func (el *HTMLElement) handleAttrRemoved(ctx context.Context, nodeID dom.NodeID, name string) {
|
||||
// it's not for this el
|
||||
if nodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
// they are not event loaded
|
||||
// just ignore the event
|
||||
if !el.attributes.Ready() {
|
||||
return
|
||||
}
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.attributes.Mutate(ctx, func(v core.Value, err error) {
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to update element")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if name == "style" {
|
||||
el.style.Reset()
|
||||
}
|
||||
|
||||
attrs, ok := v.(*values.Object)
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
attrs.Remove(values.NewString(name))
|
||||
})
|
||||
}
|
||||
|
||||
func (el *HTMLElement) handleChildrenCountChanged(ctx context.Context, nodeID dom.NodeID, _ int) {
|
||||
if nodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.mu.Lock()
|
||||
defer el.mu.Unlock()
|
||||
|
||||
node, err := el.client.DOM.DescribeNode(
|
||||
ctx,
|
||||
dom.NewDescribeNodeArgs().SetObjectID(el.id.ObjectID),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to update element")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
el.children = createChildrenArray(node.Node.Children)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) handleChildInserted(ctx context.Context, parentNodeID, prevNodeID dom.NodeID, node dom.Node) {
|
||||
func (el *HTMLElement) handleChildInserted(_ context.Context, parentNodeID, prevNodeID dom.NodeID, node dom.Node) {
|
||||
if parentNodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
@ -1375,37 +1215,9 @@ func (el *HTMLElement) handleChildInserted(ctx context.Context, parentNodeID, pr
|
||||
|
||||
arr := el.children
|
||||
el.children = append(arr[:targetIDx], append([]HTMLElementIdentity{nextIdentity}, arr[targetIDx:]...)...)
|
||||
|
||||
if !el.loadedChildren.Ready() {
|
||||
return
|
||||
}
|
||||
|
||||
el.loadedChildren.Mutate(ctx, func(v core.Value, _ error) {
|
||||
loadedArr := v.(*values.Array)
|
||||
loadedEl, err := LoadHTMLElement(
|
||||
ctx,
|
||||
el.logger,
|
||||
el.client,
|
||||
el.dom,
|
||||
el.input,
|
||||
el.exec,
|
||||
nextID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to load an inserted element")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
loadedArr.Insert(values.NewInt(targetIDx), loadedEl)
|
||||
|
||||
el.innerHTML.Reset()
|
||||
el.innerText.Reset()
|
||||
})
|
||||
}
|
||||
|
||||
func (el *HTMLElement) handleChildRemoved(ctx context.Context, nodeID, prevNodeID dom.NodeID) {
|
||||
func (el *HTMLElement) handleChildRemoved(_ context.Context, nodeID, prevNodeID dom.NodeID) {
|
||||
if nodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
@ -1433,28 +1245,6 @@ func (el *HTMLElement) handleChildRemoved(ctx context.Context, nodeID, prevNodeI
|
||||
|
||||
arr := el.children
|
||||
el.children = append(arr[:targetIDx], arr[targetIDx+1:]...)
|
||||
|
||||
if !el.loadedChildren.Ready() {
|
||||
return
|
||||
}
|
||||
|
||||
el.loadedChildren.Mutate(ctx, func(v core.Value, err error) {
|
||||
if err != nil {
|
||||
el.logger.Error().
|
||||
Timestamp().
|
||||
Err(err).
|
||||
Int("nodeID", int(el.id.NodeID)).
|
||||
Msg("failed to update element")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
loadedArr := v.(*values.Array)
|
||||
loadedArr.RemoveAt(values.NewInt(targetIDx))
|
||||
|
||||
el.innerHTML.Reset()
|
||||
el.innerText.Reset()
|
||||
})
|
||||
}
|
||||
|
||||
func (el *HTMLElement) logError(err error) *zerolog.Event {
|
||||
|
@ -6,12 +6,10 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"golang.org/x/net/html"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/templates"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/common"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
@ -23,34 +21,18 @@ import (
|
||||
|
||||
var emptyExpires = time.Time{}
|
||||
|
||||
// parseAttrs is a helper function that parses a given interleaved array of node attribute names and values,
|
||||
// and returns an object that represents attribute keys and values.
|
||||
func parseAttrs(attrs []string) *values.Object {
|
||||
var attr values.String
|
||||
// traverseAttrs is a helper function that parses a given interleaved array of node attribute names and values,
|
||||
// and calls a given attribute on each key-value pair
|
||||
func traverseAttrs(attrs []string, predicate func(name, value string) bool) {
|
||||
count := len(attrs)
|
||||
|
||||
res := values.NewObject()
|
||||
|
||||
for _, el := range attrs {
|
||||
el = strings.TrimSpace(el)
|
||||
str := values.NewString(el)
|
||||
|
||||
if common.IsAttribute(el) {
|
||||
attr = str
|
||||
res.Set(str, values.EmptyString)
|
||||
} else {
|
||||
current, ok := res.Get(attr)
|
||||
|
||||
if ok {
|
||||
if current.String() != "" {
|
||||
res.Set(attr, current.(values.String).Concat(values.SpaceString).Concat(str))
|
||||
} else {
|
||||
res.Set(attr, str)
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
if i%2 != 0 {
|
||||
if predicate(attrs[i-1], attrs[i]) == false {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func setInnerHTML(ctx context.Context, client *cdp.Client, exec *eval.ExecutionContext, id HTMLElementIdentity, innerHTML values.String) error {
|
||||
|
@ -20,12 +20,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
eventDocumentUpdated = events.New("doc_updated")
|
||||
eventAttrModified = events.New("attr_modified")
|
||||
eventAttrRemoved = events.New("attr_removed")
|
||||
eventChildNodeCountUpdated = events.New("child_count_updated")
|
||||
eventChildNodeInserted = events.New("child_inserted")
|
||||
eventChildNodeRemoved = events.New("child_removed")
|
||||
eventDocumentUpdated = events.New("doc_updated")
|
||||
eventChildNodeInserted = events.New("child_inserted")
|
||||
eventChildNodeRemoved = events.New("child_removed")
|
||||
)
|
||||
|
||||
type (
|
||||
@ -68,7 +65,6 @@ func createContext() (context.Context, context.CancelFunc) {
|
||||
func New(
|
||||
logger *zerolog.Logger,
|
||||
client *cdp.Client,
|
||||
eventLoop *events.Loop,
|
||||
mouse *input.Mouse,
|
||||
keyboard *input.Keyboard,
|
||||
) (manager *Manager, err error) {
|
||||
@ -138,22 +134,12 @@ func New(
|
||||
|
||||
closers = append(closers, onChildNodeRemoved)
|
||||
|
||||
eventLoop := events.NewLoop()
|
||||
|
||||
eventLoop.AddSource(events.NewSource(eventDocumentUpdated, onDocUpdated, func(stream rpcc.Stream) (i interface{}, e error) {
|
||||
return stream.(dom.DocumentUpdatedClient).Recv()
|
||||
}))
|
||||
|
||||
eventLoop.AddSource(events.NewSource(eventAttrModified, onAttrModified, func(stream rpcc.Stream) (i interface{}, e error) {
|
||||
return stream.(dom.AttributeModifiedClient).Recv()
|
||||
}))
|
||||
|
||||
eventLoop.AddSource(events.NewSource(eventAttrRemoved, onAttrRemoved, func(stream rpcc.Stream) (i interface{}, e error) {
|
||||
return stream.(dom.AttributeRemovedClient).Recv()
|
||||
}))
|
||||
|
||||
eventLoop.AddSource(events.NewSource(eventChildNodeCountUpdated, onChildCountUpdated, func(stream rpcc.Stream) (i interface{}, e error) {
|
||||
return stream.(dom.ChildNodeCountUpdatedClient).Recv()
|
||||
}))
|
||||
|
||||
eventLoop.AddSource(events.NewSource(eventChildNodeInserted, onChildNodeInserted, func(stream rpcc.Stream) (i interface{}, e error) {
|
||||
return stream.(dom.ChildNodeInsertedClient).Recv()
|
||||
}))
|
||||
@ -171,6 +157,8 @@ func New(
|
||||
manager.frames = make(map[page.FrameID]Frame)
|
||||
manager.cancel = cancel
|
||||
|
||||
eventLoop.Start()
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
@ -178,12 +166,18 @@ func (m *Manager) Close() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
errs := make([]error, 0, len(m.frames)+1)
|
||||
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
m.cancel = nil
|
||||
}
|
||||
|
||||
errs := make([]error, 0, len(m.frames))
|
||||
err := m.events.Stop().Close()
|
||||
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range m.frames {
|
||||
// if initialized
|
||||
@ -324,48 +318,6 @@ func (m *Manager) RemoveReloadListener(listenerID events.ListenerID) {
|
||||
m.events.RemoveListener(eventDocumentUpdated, listenerID)
|
||||
}
|
||||
|
||||
func (m *Manager) AddAttrModifiedListener(listener AttrModifiedListener) events.ListenerID {
|
||||
return m.events.AddListener(eventAttrModified, func(ctx context.Context, message interface{}) bool {
|
||||
reply := message.(*dom.AttributeModifiedReply)
|
||||
|
||||
listener(ctx, reply.NodeID, reply.Name, reply.Value)
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) RemoveAttrModifiedListener(listenerID events.ListenerID) {
|
||||
m.events.RemoveListener(eventAttrModified, listenerID)
|
||||
}
|
||||
|
||||
func (m *Manager) AddAttrRemovedListener(listener AttrRemovedListener) events.ListenerID {
|
||||
return m.events.AddListener(eventAttrRemoved, func(ctx context.Context, message interface{}) bool {
|
||||
reply := message.(*dom.AttributeRemovedReply)
|
||||
|
||||
listener(ctx, reply.NodeID, reply.Name)
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) RemoveAttrRemovedListener(listenerID events.ListenerID) {
|
||||
m.events.RemoveListener(eventAttrRemoved, listenerID)
|
||||
}
|
||||
|
||||
func (m *Manager) AddChildNodeCountUpdatedListener(listener ChildNodeCountUpdatedListener) events.ListenerID {
|
||||
return m.events.AddListener(eventChildNodeCountUpdated, func(ctx context.Context, message interface{}) bool {
|
||||
reply := message.(*dom.ChildNodeCountUpdatedReply)
|
||||
|
||||
listener(ctx, reply.NodeID, reply.ChildNodeCount)
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) RemoveChildNodeCountUpdatedListener(listenerID events.ListenerID) {
|
||||
m.events.RemoveListener(eventChildNodeCountUpdated, listenerID)
|
||||
}
|
||||
|
||||
func (m *Manager) AddChildNodeInsertedListener(listener ChildNodeInsertedListener) events.ListenerID {
|
||||
return m.events.AddListener(eventChildNodeInserted, func(ctx context.Context, message interface{}) bool {
|
||||
reply := message.(*dom.ChildNodeInsertedReply)
|
||||
|
@ -41,7 +41,6 @@ type (
|
||||
func New(
|
||||
logger *zerolog.Logger,
|
||||
client *cdp.Client,
|
||||
eventLoop *events.Loop,
|
||||
) (*Manager, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@ -49,7 +48,7 @@ func New(
|
||||
m.logger = logger
|
||||
m.client = client
|
||||
m.headers = make(drivers.HTTPHeaders)
|
||||
m.eventLoop = eventLoop
|
||||
m.eventLoop = events.NewLoop()
|
||||
m.cancel = cancel
|
||||
m.response = new(sync.Map)
|
||||
|
||||
@ -85,6 +84,8 @@ func New(
|
||||
|
||||
m.responseListenerID = m.eventLoop.AddListener(responseReceived, m.onResponse)
|
||||
|
||||
m.eventLoop.Start()
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@ -95,6 +96,8 @@ func (m *Manager) Close() error {
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
m.cancel = nil
|
||||
|
||||
return m.eventLoop.Stop().Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/dom"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/events"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/input"
|
||||
net "github.com/MontFerret/ferret/pkg/drivers/cdp/network"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/common"
|
||||
@ -30,7 +29,6 @@ type HTMLPage struct {
|
||||
logger *zerolog.Logger
|
||||
conn *rpcc.Conn
|
||||
client *cdp.Client
|
||||
events *events.Loop
|
||||
network *net.Manager
|
||||
dom *dom.Manager
|
||||
mouse *input.Mouse
|
||||
@ -62,10 +60,7 @@ func LoadHTMLPage(
|
||||
}
|
||||
}()
|
||||
|
||||
eventLoop := events.NewLoop()
|
||||
closers = append(closers, eventLoop)
|
||||
|
||||
netManager, err := net.New(logger, client, eventLoop)
|
||||
netManager, err := net.New(logger, client)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -83,15 +78,12 @@ func LoadHTMLPage(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eventLoop.Start()
|
||||
|
||||
mouse := input.NewMouse(client)
|
||||
keyboard := input.NewKeyboard(client)
|
||||
|
||||
domManager, err := dom.New(
|
||||
logger,
|
||||
client,
|
||||
eventLoop,
|
||||
mouse,
|
||||
keyboard,
|
||||
)
|
||||
@ -106,7 +98,6 @@ func LoadHTMLPage(
|
||||
logger,
|
||||
conn,
|
||||
client,
|
||||
eventLoop,
|
||||
netManager,
|
||||
domManager,
|
||||
mouse,
|
||||
@ -175,7 +166,6 @@ func NewHTMLPage(
|
||||
logger *zerolog.Logger,
|
||||
conn *rpcc.Conn,
|
||||
client *cdp.Client,
|
||||
eventLoop *events.Loop,
|
||||
netManager *net.Manager,
|
||||
domManager *dom.Manager,
|
||||
mouse *input.Mouse,
|
||||
@ -186,14 +176,11 @@ func NewHTMLPage(
|
||||
p.logger = logger
|
||||
p.conn = conn
|
||||
p.client = client
|
||||
p.events = eventLoop
|
||||
p.network = netManager
|
||||
p.dom = domManager
|
||||
p.mouse = mouse
|
||||
p.keyboard = keyboard
|
||||
|
||||
eventLoop.AddListener(events.Error, events.Always(p.handleError))
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
@ -270,17 +257,8 @@ func (p *HTMLPage) Close() error {
|
||||
p.closed = values.True
|
||||
|
||||
doc := p.getCurrentDocument()
|
||||
err := p.events.Stop().Close()
|
||||
|
||||
if err != nil {
|
||||
p.logger.Warn().
|
||||
Timestamp().
|
||||
Str("url", doc.GetURL().String()).
|
||||
Err(err).
|
||||
Msg("failed to stop event loop")
|
||||
}
|
||||
|
||||
err = p.dom.Close()
|
||||
err := p.dom.Close()
|
||||
|
||||
if err != nil {
|
||||
p.logger.Warn().
|
||||
@ -602,19 +580,6 @@ func (p *HTMLPage) loadMainFrame(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *HTMLPage) handleError(_ context.Context, val interface{}) {
|
||||
err, ok := val.(error)
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p.logger.Error().
|
||||
Timestamp().
|
||||
Err(err).
|
||||
Msg("unexpected error")
|
||||
}
|
||||
|
||||
func (p *HTMLPage) getCurrentDocument() *dom.HTMLDocument {
|
||||
return p.dom.GetMainFrame()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user