func (t *Tab) GetElementByLocation(x, y int) (*Element, error) {
	_, nodeId, err := t.DOM.GetNodeForLocation(x, y, false)
	if err != nil {
		return nil, err
	}
	ele, _ := t.GetElementByNodeId(nodeId)
	return ele, nil
}
Returns the element given the x, y coordinates on the page, or returns error.
package autogcd
import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"sync/atomic"
	"time"
	"github.com/wirepair/gcd"
	"github.com/wirepair/gcd/gcdapi"
)
const maximumTotalBufferSize = -1
const maximumResourceBufferSize = -1
const maximumPostDataSize = -1
type ElementNotFoundErr struct {
	Message string
}
func (e *ElementNotFoundErr) Error() string {
	return "Unable to find element " + e.Message
}
type InvalidTabErr struct {
	Message string
}
func (e *InvalidTabErr) Error() string {
	return "Unable to access tab: " + e.Message
}
type InvalidNavigationErr struct {
	Message string
}
func (e *InvalidNavigationErr) Error() string {
	return e.Message
}
type ScriptEvaluationErr struct {
	Message          string
	ExceptionText    string
	ExceptionDetails *gcdapi.RuntimeExceptionDetails
}
func (e *ScriptEvaluationErr) Error() string {
	return e.Message + " " + e.ExceptionText
}
type TimeoutErr struct {
	Message string
}
func (e *TimeoutErr) Error() string {
	return "Timed out " + e.Message
}
type GcdResponseFunc func(target *gcd.ChromeTarget, payload []byte)
type TabDisconnectedHandler func(tab *Tab, reason string)
type PromptHandlerFunc func(tab *Tab, message, promptType string)
type ConsoleMessageFunc func(tab *Tab, message *gcdapi.ConsoleConsoleMessage)
type NetworkRequestHandlerFunc func(tab *Tab, request *NetworkRequest)
type NetworkResponseHandlerFunc func(tab *Tab, response *NetworkResponse)
type NetworkFinishedHandlerFunc func(tab *Tab, requestId string, dataLength, timeStamp float64)
type StorageFunc func(tab *Tab, eventType string, eventDetails *StorageEvent)
type DomChangeHandlerFunc func(tab *Tab, change *NodeChangeEvent)
type ConditionalFunc func(tab *Tab) bool
type Tab struct {
	*gcd.ChromeTarget                             
	eleMutex              *sync.RWMutex           
	elements              map[int]*Element        
	topNodeId             atomic.Value            
	topFrameId            atomic.Value            
	isNavigatingFlag      atomic.Value            
	isTransitioningFlag   atomic.Value            
	debug                 bool                    
	nodeChange            chan *NodeChangeEvent   
	navigationCh          chan int                
	docUpdateCh           chan struct{}           
	crashedCh             chan string             
	exitCh                chan struct{}           
	shutdown              atomic.Value            
	disconnectedHandler   TabDisconnectedHandler  
	navigationTimeout     time.Duration           
	elementTimeout        time.Duration           
	stabilityTimeout      time.Duration           
	stableAfter           time.Duration           
	lastNodeChangeTimeVal atomic.Value            
	domChangeHandler      DomChangeHandlerFunc    
}
func open(target *gcd.ChromeTarget) (*Tab, error) {
	t := &Tab{ChromeTarget: target}
	t.eleMutex = &sync.RWMutex{}
	t.elements = make(map[int]*Element)
	t.nodeChange = make(chan *NodeChangeEvent)
	t.navigationCh = make(chan int, 1)   
	t.docUpdateCh = make(chan struct{})  
	t.crashedCh = make(chan string)      
	t.exitCh = make(chan struct{})
	t.navigationTimeout = 30 * time.Second  
	t.elementTimeout = 5 * time.Second      
	t.stabilityTimeout = 2 * time.Second    
	t.stableAfter = 300 * time.Millisecond  
	t.domChangeHandler = nil
	if _, err := t.Page.Enable(); err != nil {
		return nil, err
	}
	if _, err := t.DOM.Enable(); err != nil {
		return nil, err
	}
	if _, err := t.Console.Enable(); err != nil {
		return nil, err
	}
	if _, err := t.Debugger.Enable(); err != nil {
		return nil, err
	}
	t.disconnectedHandler = t.defaultDisconnectedHandler
	t.subscribeEvents()
	go t.listenDebuggerEvents()
	return t, nil
}
func (t *Tab) close() {
	if !t.IsShuttingDown() {
		close(t.exitCh)
	}
	t.setShutdownState(true)
}
func (t *Tab) IsShuttingDown() bool {
	if flag, ok := t.shutdown.Load().(bool); ok {
		return flag
	}
	return false
}
func (t *Tab) setShutdownState(val bool) {
	t.shutdown.Store(val)
}
func (t *Tab) Debug(enabled bool) {
	t.debug = enabled
}
func (t *Tab) SetDisconnectedHandler(handlerFn TabDisconnectedHandler) {
	t.disconnectedHandler = handlerFn
}
func (t *Tab) defaultDisconnectedHandler(tab *Tab, reason string) {
	t.debugf("tab %s tabId: %s", reason, tab.ChromeTarget.Target.Id)
}
func (t *Tab) SetNavigationTimeout(timeout time.Duration) {
	t.navigationTimeout = timeout
}
func (t *Tab) SetElementWaitTimeout(timeout time.Duration) {
	t.elementTimeout = timeout
}
func (t *Tab) SetStabilityTimeout(timeout time.Duration) {
	t.stabilityTimeout = timeout
}
func (t *Tab) SetStabilityTime(stableAfter time.Duration) {
	t.stableAfter = stableAfter
}
func (t *Tab) setIsNavigating(set bool) {
	t.isNavigatingFlag.Store(set)
}
func (t *Tab) IsNavigating() bool {
	if flag, ok := t.isNavigatingFlag.Load().(bool); ok {
		return flag
	}
	return false
}
func (t *Tab) setIsTransitioning(set bool) {
	t.isTransitioningFlag.Store(set)
}
func (t *Tab) IsTransitioning() bool {
	if flag, ok := t.isTransitioningFlag.Load().(bool); ok {
		return flag
	}
	return false
}
func (t *Tab) setTopFrameId(topFrameId string) {
	t.topFrameId.Store(topFrameId)
}
func (t *Tab) GetTopFrameId() string {
	if frameId, ok := t.topFrameId.Load().(string); ok {
		return frameId
	}
	return ""
}
func (t *Tab) setTopNodeId(nodeId int) {
	t.debugf("setting topNodeId: %d\n", nodeId)
	t.topNodeId.Store(nodeId)
}
func (t *Tab) GetTopNodeId() int {
	if topNodeId, ok := t.topNodeId.Load().(int); ok {
		return topNodeId
	}
	t.debugf("failed getting int from topNodeId")
	return -1
}
func (t *Tab) Navigate(url string) (string, string, error) {
	if t.IsNavigating() {
		return "", "", &InvalidNavigationErr{Message: "Unable to navigate, already navigating."}
	}
	t.setIsNavigating(true)
	t.debugf("navigating to %s", url)
	defer func() {
		t.setIsNavigating(false)
	}()
	navParams := &gcdapi.PageNavigateParams{Url: url, TransitionType: "typed"}
	frameId, _, errorText, err := t.Page.NavigateWithParams(navParams)
	if err != nil {
		return "", errorText, err
	}
	t.lastNodeChangeTimeVal.Store(time.Now())
	err = t.readyWait(url)
	if err != nil {
		return frameId, "", err
	}
	t.debugf("navigation complete")
	return frameId, "", err
}
func (t *Tab) DidNavigationFail() (bool, string) {
	rro, err := t.EvaluateScript("loadTimeData.data_.errorCode")
	if err != nil {
		return false, ""
	}
	if val, ok := rro.Value.(string); ok {
		return true, val
	}
	return false, ""
}
func (t *Tab) readyWait(url string) error {
	var navigated bool
	timeoutTimer := time.NewTimer(t.navigationTimeout)
	defer timeoutTimer.Stop()
	for {
		select {
		case <-t.navigationCh:
			navigated = true
		case <-t.docUpdateCh:
			return nil
		case <-timeoutTimer.C:
			msg := "navigating to: "
			if navigated == true {
				msg = "waiting for document updated failed for: "
			}
			return &TimeoutErr{Message: msg + url}
		}
	}
}
func (t *Tab) NavigationHistory() (int, []*gcdapi.PageNavigationEntry, error) {
	return t.Page.GetNavigationHistory()
}
func (t *Tab) Reload(ignoreCache bool, evalScript string) error {
	_, err := t.Page.Reload(ignoreCache, evalScript)
	return err
}
func (t *Tab) Forward() error {
	next, err := t.ForwardEntry()
	if err != nil {
		return err
	}
	_, err = t.Page.NavigateToHistoryEntry(next.Id)
	return err
}
func (t *Tab) ForwardEntry() (*gcdapi.PageNavigationEntry, error) {
	idx, entries, err := t.NavigationHistory()
	if err != nil {
		return nil, err
	}
	for i := 0; i < len(entries); i++ {
		if idx < entries[i].Id {
			return entries[i], nil
		}
	}
	return nil, &InvalidNavigationErr{Message: "Unable to navigate forward as we are on the latest navigation entry"}
}
func (t *Tab) Back() error {
	prev, err := t.BackEntry()
	if err != nil {
		return err
	}
	_, err = t.Page.NavigateToHistoryEntry(prev.Id)
	return err
}
func (t *Tab) BackEntry() (*gcdapi.PageNavigationEntry, error) {
	idx, entries, err := t.NavigationHistory()
	if err != nil {
		return nil, err
	}
	for i := len(entries); i > 0; i-- {
		if idx < entries[i].Id {
			return entries[i], nil
		}
	}
	return nil, &InvalidNavigationErr{Message: "Unable to navigate backward as we are on the first navigation entry"}
}
func (t *Tab) WaitFor(rate, timeout time.Duration, conditionFn ConditionalFunc) error {
	rateTicker := time.NewTicker(rate)
	timeoutTimer := time.NewTimer(timeout)
	defer func() {
		timeoutTimer.Stop()
		rateTicker.Stop()
	}()
	for {
		select {
		case <-timeoutTimer.C:
			return &TimeoutErr{Message: "waiting for conditional func to return true"}
		case <-rateTicker.C:
			ret := conditionFn(t)
			if ret == true {
				return nil
			}
		}
	}
}
func (t *Tab) WaitStable() error {
	checkRate := 150 * time.Millisecond
	timeoutTimer := time.NewTimer(t.stabilityTimeout)
	if t.stableAfter < checkRate {
		checkRate = t.stableAfter / 2  
	}
	stableCheck := time.NewTicker(checkRate)  
	defer func() {
		timeoutTimer.Stop()
		stableCheck.Stop()
	}()
	for {
		select {
		case <-timeoutTimer.C:
			return &TimeoutErr{Message: "waiting for DOM stability"}
		case <-stableCheck.C:
			if changeTime, ok := t.lastNodeChangeTimeVal.Load().(time.Time); ok {
				if time.Now().Sub(changeTime) >= t.stableAfter {
					return nil
				}
			} else {
				return &InvalidNavigationErr{Message: "WaitStable called when there was no last node change time"}
			}
		}
	}
	return nil
}
func (t *Tab) GetScriptSource(scriptId string) (string, error) {
	return t.Debugger.GetScriptSource(scriptId)
}
func (t *Tab) getDocument() (*Element, error) {
	doc, err := t.DOM.GetDocument(-1, false)
	if err != nil {
		return nil, err
	}
	t.setTopNodeId(doc.NodeId)
	t.setTopFrameId(doc.FrameId)
	t.addNodes(doc)
	eleDoc, _ := t.getElement(doc.NodeId)
	return eleDoc, nil
}
func (t *Tab) GetDocument() (*Element, error) {
	docEle, ok := t.getElement(t.GetTopNodeId())
	if !ok {
		return nil, &ElementNotFoundErr{Message: "top document node id not found."}
	}
	return docEle, nil
}
func (t *Tab) GetElementByNodeId(nodeId int) (*Element, bool) {
	t.eleMutex.RLock()
	ele, ok := t.elements[nodeId]
	t.eleMutex.RUnlock()
	if ok {
		return ele, true
	}
	newEle := newElement(t, nodeId)
	t.eleMutex.Lock()
	t.elements[nodeId] = newEle  
	t.eleMutex.Unlock()
	return newEle, false
}