func (t *Tab) EvaluateScript(scriptSource string) (*gcdapi.RuntimeRemoteObject, error) {
	return t.evaluateScript(scriptSource, false)
}
Evaluates script in the global context.
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
}
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
}
func (t *Tab) GetAllElements() map[int]*Element {
	t.eleMutex.RLock()
	allElements := make(map[int]*Element, len(t.elements))
	for k, v := range t.elements {
		allElements[k] = v
	}
	t.eleMutex.RUnlock()
	return allElements
}
func (t *Tab) GetElementById(attributeId string) (*Element, bool, error) {
	return t.GetDocumentElementById(t.GetTopNodeId(), attributeId)
}
func (t *Tab) GetDocumentElementById(docNodeId int, attributeId string) (*Element, bool, error) {
	var err error
	docNode, ok := t.getElement(docNodeId)
	if !ok {
		return nil, false, &ElementNotFoundErr{Message: fmt.Sprintf("docNodeId %d not found", docNodeId)}
	}
	selector := "#" + attributeId
	nodeId, err := t.DOM.QuerySelector(docNode.id, selector)
	if err != nil {
		return nil, false, err
	}
	ele, ready := t.GetElementByNodeId(nodeId)
	return ele, ready, nil
}
func (t *Tab) GetElementsBySelector(selector string) ([]*Element, error) {
	return t.GetDocumentElementsBySelector(t.GetTopNodeId(), selector)
}
func (t *Tab) GetChildElements(element *Element) []*Element {
	return t.GetChildElementsOfType(element, "*")
}
func (t *Tab) GetChildElementsOfType(element *Element, tagType string) []*Element {
	elements := make([]*Element, 0)
	if element == nil || element.node == nil || element.node.Children == nil {
		return elements
	}
	t.recursivelyGetChildren(element.node.Children, &elements, tagType)
	return elements
}
func (t *Tab) GetChildrensCharacterData(element *Element) string {
	var buf bytes.Buffer
	for _, el := range t.GetChildElements(element) {
		if el.nodeType == int(TEXT_NODE) {
			buf.WriteString(el.characterData)
		}
	}
	return buf.String()
}
func (t *Tab) recursivelyGetChildren(children []*gcdapi.DOMNode, elements *[]*Element, tagType string) {
	for _, child := range children {
		ele, ready := t.GetElementByNodeId(child.NodeId)
		if ready == true && (tagType == "*" || tagType == ele.nodeName) {
			*elements = append(*elements, ele)
		}
		if ready == false || ele.node.Children == nil || len(ele.node.Children) == 0 {
			continue
		}
		t.recursivelyGetChildren(ele.node.Children, elements, tagType)
	}
}
func (t *Tab) GetDocumentElementsBySelector(docNodeId int, selector string) ([]*Element, error) {
	docNode, ok := t.getElement(docNodeId)
	if !ok {
		return nil, &ElementNotFoundErr{Message: fmt.Sprintf("docNodeId %d not found", docNodeId)}
	}
	nodeIds, errQuery := t.DOM.QuerySelectorAll(docNode.id, selector)
	if errQuery != nil {
		return nil, errQuery
	}
	elements := make([]*Element, len(nodeIds))
	for k, nodeId := range nodeIds {
		elements[k], _ = t.GetElementByNodeId(nodeId)
	}
	return elements, nil
}
func (t *Tab) GetElementsBySearch(selector string, includeUserAgentShadowDOM bool) ([]*Element, error) {
	var s gcdapi.DOMPerformSearchParams
	s.Query = selector
	s.IncludeUserAgentShadowDOM = includeUserAgentShadowDOM
	id, count, err := t.DOM.PerformSearchWithParams(&s)
	if err != nil {
		return nil, err
	}
	if count < 1 {
		return make([]*Element, 0), nil
	}
	var r gcdapi.DOMGetSearchResultsParams
	r.SearchId = id
	r.FromIndex = 0
	r.ToIndex = count
	nodeIds, errQuery := t.DOM.GetSearchResultsWithParams(&r)
	if errQuery != nil {
		return nil, errQuery
	}
	elements := make([]*Element, len(nodeIds))
	for k, nodeId := range nodeIds {
		elements[k], _ = t.GetElementByNodeId(nodeId)
	}
	return elements, nil
}
func (t *Tab) GetPageSource(docNodeId int) (string, error) {
	if docNodeId == 0 {
		docNodeId = t.GetTopNodeId()
	}
	doc, ok := t.getElement(docNodeId)
	if !ok {
		return "", &ElementNotFoundErr{Message: fmt.Sprintf("docNodeId %d not found", docNodeId)}
	}
	outerParams := &gcdapi.DOMGetOuterHTMLParams{NodeId: doc.id}
	return t.DOM.GetOuterHTMLWithParams(outerParams)
}
func (t *Tab) GetCurrentUrl() (string, error) {
	return t.GetDocumentCurrentUrl(t.GetTopNodeId())
}
func (t *Tab) GetDocumentCurrentUrl(docNodeId int) (string, error) {
	docNode, ok := t.getElement(docNodeId)
	if !ok {
		return "", &ElementNotFoundErr{Message: fmt.Sprintf("docNodeId %d not found", docNodeId)}
	}
	return docNode.node.DocumentURL, nil
}
func (t *Tab) Click(x, y float64) error {
	return t.click(x, y, 1)
}
func (t *Tab) click(x, y float64, clickCount int) error {
	mousePressedParams := &gcdapi.InputDispatchMouseEventParams{TheType: "mousePressed",
		X:          x,
		Y:          y,
		Button:     "left",
		ClickCount: clickCount,
	}
	if _, err := t.Input.DispatchMouseEventWithParams(mousePressedParams); err != nil {
		return err
	}
	mouseReleasedParams := &gcdapi.InputDispatchMouseEventParams{TheType: "mouseReleased",
		X:          x,
		Y:          y,
		Button:     "left",
		ClickCount: clickCount,
	}
	if _, err := t.Input.DispatchMouseEventWithParams(mouseReleasedParams); err != nil {
		return err
	}
	return nil
}
func (t *Tab) DoubleClick(x, y float64) error {
	return t.click(x, y, 2)
}
func (t *Tab) MoveMouse(x, y float64) error {
	mouseMovedParams := &gcdapi.InputDispatchMouseEventParams{TheType: "mouseMoved",
		X: x,
		Y: y,
	}
	_, err := t.Input.DispatchMouseEventWithParams(mouseMovedParams)
	return err
}
func (t *Tab) SendKeys(text string) error {
	inputParams := &gcdapi.InputDispatchKeyEventParams{TheType: "char"}
	for _, inputchar := range text {
		input := string(inputchar)
		switch input {
		case "\r", "\n", "\t", "\b":
			if err := t.pressSystemKey(input); err != nil {
				return err
			}
			continue
		}
		inputParams.Text = input
		_, err := t.Input.DispatchKeyEventWithParams(inputParams)
		if err != nil {
			return err
		}
	}
	return nil
}
func (t *Tab) pressSystemKey(systemKey string) error {
	inputParams := &gcdapi.InputDispatchKeyEventParams{TheType: "rawKeyDown"}
	switch systemKey {
	case "\b":
		inputParams.UnmodifiedText = "\b"
		inputParams.Text = "\b"
		inputParams.WindowsVirtualKeyCode = 8
		inputParams.NativeVirtualKeyCode = 8
	case "\t":
		inputParams.UnmodifiedText = "\t"
		inputParams.Text = "\t"
		inputParams.WindowsVirtualKeyCode = 9
		inputParams.NativeVirtualKeyCode = 9
	case "\r", "\n":
		inputParams.UnmodifiedText = "\r"
		inputParams.Text = "\r"
		inputParams.WindowsVirtualKeyCode = 13
		inputParams.NativeVirtualKeyCode = 13
	}
	if _, err := t.Input.DispatchKeyEventWithParams(inputParams); err != nil {
		return err
	}
	inputParams.TheType = "char"
	if _, err := t.Input.DispatchKeyEventWithParams(inputParams); err != nil {
		return err
	}
	inputParams.TheType = "keyUp"
	if _, err := t.Input.DispatchKeyEventWithParams(inputParams); err != nil {
		return err
	}
	return nil
}
func (t *Tab) InjectScriptOnLoad(scriptSource string) (string, error) {
	scriptId, err := t.Page.AddScriptToEvaluateOnLoad(scriptSource)
	if err != nil {
		return "", err
	}
	return scriptId, nil
}
func (t *Tab) RemoveScriptFromOnLoad(scriptId string) error {
	_, err := t.Page.RemoveScriptToEvaluateOnLoad(scriptId)
	return err
}