func (e *Element) GetChildNodeIds() ([]int, error) {
	e.lock.RLock()
	defer e.lock.RUnlock()
	if !e.ready {
		return nil, &ElementNotReadyErr{}
	}
	if e.node == nil || e.node.Children == nil {
		return nil, &ElementHasNoChildrenErr{}
	}
	ids := make([]int, len(e.node.Children))
	for k, child := range e.node.Children {
		ids[k] = child.NodeId
	}
	return ids, nil
}
Get child node ids, returns nil if node is not ready
package autogcd
import (
	"fmt"
	"strings"
	"sync"
	"time"
	"github.com/wirepair/gcd/gcdapi"
)
type IncorrectElementTypeErr struct {
	NodeName     string
	ExpectedName string
}
func (e *IncorrectElementTypeErr) Error() string {
	return "incorrect element type, expected " + e.ExpectedName + " but this type is " + e.NodeName
}
type InvalidElementErr struct {
}
func (e *InvalidElementErr) Error() string {
	return "this element has been invalidated"
}
type ElementHasNoChildrenErr struct {
}
func (e *ElementHasNoChildrenErr) Error() string {
	return "this element has no child elements"
}
type ElementNotReadyErr struct {
}
func (e *ElementNotReadyErr) Error() string {
	return "this element is not ready"
}
type InvalidDimensionsErr struct {
	Message string
}
func (e *InvalidDimensionsErr) Error() string {
	return "invalid dimensions " + e.Message
}
type Element struct {
	lock           *sync.RWMutex      
	attributes     map[string]string  
	nodeName       string             
	characterData  string             
	childNodeCount int                
	nodeType       int                
	tab            *Tab               
	node           *gcdapi.DOMNode    
	readyGate      chan struct{}      
	id             int                
	ready          bool               
	invalidated    bool               
}
func newElement(tab *Tab, nodeId int) *Element {
	e := &Element{}
	e.tab = tab
	e.attributes = make(map[string]string)
	e.readyGate = make(chan struct{})
	e.lock = &sync.RWMutex{}
	e.id = nodeId
	return e
}
func newReadyElement(tab *Tab, node *gcdapi.DOMNode) *Element {
	e := &Element{}
	e.tab = tab
	e.attributes = make(map[string]string)
	e.readyGate = make(chan struct{})
	e.nodeName = strings.ToLower(node.NodeName)
	e.lock = &sync.RWMutex{}
	e.populateElement(node)
	return e
}
func (e *Element) populateElement(node *gcdapi.DOMNode) {
	e.lock.Lock()
	e.node = node
	e.id = node.NodeId
	e.nodeType = node.NodeType
	e.nodeName = strings.ToLower(node.NodeName)
	e.childNodeCount = node.ChildNodeCount
	if node.NodeType == int(TEXT_NODE) {
		e.characterData = node.NodeValue
	}
	e.lock.Unlock()
	for i := 0; i < len(node.Attributes); i += 2 {
		e.updateAttribute(node.Attributes[i], node.Attributes[i+1])
	}
	if !e.ready {
		close(e.readyGate)
	}
	e.lock.Lock()
	defer e.lock.Unlock()
	e.ready = true
}
func (e *Element) IsReady() bool {
	e.lock.RLock()
	defer e.lock.RUnlock()
	return (e.ready && !e.invalidated)
}
func (e *Element) IsReadyInvalid() bool {
	e.lock.RLock()
	defer e.lock.RUnlock()
	return (e.ready && e.invalidated)
}
func (e *Element) IsInvalid() bool {
	e.lock.RLock()
	defer e.lock.RUnlock()
	return e.invalidated
}
func (e *Element) setInvalidated(invalid bool) {
	e.lock.Lock()
	e.invalidated = invalid
	e.lock.Unlock()
}
func (e *Element) WaitForReady() error {
	e.lock.RLock()
	ready := e.ready
	e.lock.RUnlock()
	if ready {
		return nil
	}
	timeout := time.NewTimer(e.tab.elementTimeout)
	defer timeout.Stop()
	select {
	case <-e.readyGate:
		return nil
	case <-timeout.C:
		return &ElementNotReadyErr{}
	}
}
func (e *Element) GetSource() (string, error) {
	e.lock.RLock()
	id := e.id
	e.lock.RUnlock()
	if e.invalidated {
		return "", &InvalidElementErr{}
	}
	e.tab.debugf("id: %d\n", id)
	outerParams := &gcdapi.DOMGetOuterHTMLParams{NodeId: id}
	return e.tab.DOM.GetOuterHTMLWithParams(outerParams)
}
func (e *Element) IsDocument() (bool, error) {
	e.lock.RLock()
	defer e.lock.RUnlock()
	if !e.ready {
		return false, &ElementNotReadyErr{}
	}
	return (e.nodeType == int(DOCUMENT_NODE)), nil
}
func (e *Element) FrameId() (string, error) {
	if !e.IsReady() {
		return "", &ElementNotReadyErr{}
	}
	isDoc, err := e.IsDocument()
	if err != nil {
		return "", err
	}
	if !isDoc {
		return "", nil
	}
	e.lock.RLock()
	defer e.lock.RUnlock()
	return e.node.FrameId, nil
}
func (e *Element) GetFrameDocumentNodeId() (int, error) {
	if !e.IsReady() {
		return -1, &ElementNotReadyErr{}
	}
	e.lock.RLock()
	defer e.lock.RUnlock()
	if e.node != nil && e.node.ContentDocument != nil {
		return e.node.ContentDocument.NodeId, nil
	}
	return -1, &IncorrectElementTypeErr{ExpectedName: "(i)frame", NodeName: e.nodeName}
}
func (e *Element) NodeId() int {
	e.lock.RLock()
	defer e.lock.RUnlock()
	return e.id
}
func (e *Element) GetEventListeners() ([]*gcdapi.DOMDebuggerEventListener, error) {
	e.lock.RLock()
	id := e.id
	e.lock.RUnlock()
	params := &gcdapi.DOMResolveNodeParams{
		NodeId: id,
	}
	rro, err := e.tab.DOM.ResolveNodeWithParams(params)
	if err != nil {
		return nil, err
	}
	eventListeners, err := e.tab.DOMDebugger.GetEventListeners(rro.ObjectId, 1, false)
	if err != nil {
		return nil, err
	}
	return eventListeners, nil
}
func (e *Element) GetDebuggerDOMNode() (*gcdapi.DOMNode, error) {
	if !e.IsReady() {
		return nil, &ElementNotReadyErr{}
	}
	e.lock.RLock()
	defer e.lock.RUnlock()
	if e.invalidated {
		return nil, &InvalidElementErr{}
	}
	return e.node, nil
}
func (e *Element) updateAttribute(name, value string) {
	e.lock.Lock()
	defer e.lock.Unlock()
	e.attributes[name] = value
}
func (e *Element) removeAttribute(name string) {
	e.lock.Lock()
	defer e.lock.Unlock()
	delete(e.attributes, name)
}
func (e *Element) updateCharacterData(newValue string) {
	e.lock.Lock()
	defer e.lock.Unlock()
	e.characterData = newValue
}
func (e *Element) updateChildNodeCount(newValue int) {
	e.lock.Lock()
	defer e.lock.Unlock()
	e.childNodeCount = newValue
}
func (e *Element) addChild(child *gcdapi.DOMNode) {
	e.lock.Lock()
	defer e.lock.Unlock()
	if e.node == nil {
		return
	}
	if e.node.Children == nil {
		e.node.Children = make([]*gcdapi.DOMNode, 0)
	}
	e.node.Children = append(e.node.Children, child)
	e.childNodeCount++
}
func (e *Element) addChildren(childNodes []*gcdapi.DOMNode) {
	for _, child := range childNodes {
		if child != nil {
			e.addChild(child)
		}
	}
}
func (e *Element) removeChild(removedNodeId int) {
	var idx int
	var child *gcdapi.DOMNode
	e.lock.Lock()
	defer e.lock.Unlock()
	if e.node == nil || e.node.Children == nil {
		return
	}
	for idx, child = range e.node.Children {
		if child != nil && child.NodeId == removedNodeId {
			e.node.Children = append(e.node.Children[:idx], e.node.Children[idx+1:]...)
			e.childNodeCount = e.childNodeCount - 1
			break
		}
	}
}