package cdp

// Code generated by cdproto-gen. DO NOT EDIT.

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/chromedp/sysutil"
)

// Executor is the common interface for executing a command.
type Executor interface {
	// Execute executes the command.
	Execute(context.Context, string, any, any) error
}

// contextKey is the context key type.
type contextKey int

// context keys.
const (
	executorKey contextKey = iota
)

// WithExecutor sets the message executor for the context.
func WithExecutor(parent context.Context, executor Executor) context.Context {
	return context.WithValue(parent, executorKey, executor)
}

// ExecutorFromContext returns the message executor for the context.
func ExecutorFromContext(ctx context.Context) Executor {
	return ctx.Value(executorKey).(Executor)
}

// Execute uses the context's message executor to send a command or event
// method marshaling the provided parameters, and unmarshaling to res.
func Execute(ctx context.Context, method string, params, res any) error {
	if executor := ctx.Value(executorKey); executor != nil {
		return executor.(Executor).Execute(ctx, method, params, res)
	}
	return ErrInvalidContext
}

// Error is a error.
type Error string

// Error values.
const (
	// ErrInvalidContext is the invalid context error.
	ErrInvalidContext Error = "invalid context"

	// ErrMsgMissingParamsOrResult is the msg missing params or result error.
	ErrMsgMissingParamsOrResult Error = "msg missing params or result"
)

// Error satisfies the error interface.
func (err Error) Error() string {
	return string(err)
}

// ErrUnknownCommandOrEvent is an unknown command or event error.
type ErrUnknownCommandOrEvent string

// Error satisfies the error interface.
func (err ErrUnknownCommandOrEvent) Error() string {
	return fmt.Sprintf("unknown command or event %q", string(err))
}

// BrowserContextID [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Browser#type-BrowserContextID
type BrowserContextID string

// String returns the BrowserContextID as string value.
func (t BrowserContextID) String() string {
	return string(t)
}

// NodeID unique DOM node identifier.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-NodeId
type NodeID int64

// Int64 returns the NodeID as int64 value.
func (t NodeID) Int64() int64 {
	return int64(t)
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *NodeID) UnmarshalJSON(buf []byte) error {
	if l := len(buf); l > 2 && buf[0] == '"' && buf[l-1] == '"' {
		buf = buf[1 : l-1]
	}

	v, err := strconv.ParseInt(string(buf), 10, 64)
	if err != nil {
		return err
	}

	*t = NodeID(v)
	return nil
}

// BackendNodeID unique DOM node identifier used to reference a node that may
// not have been pushed to the front-end.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-BackendNodeId
type BackendNodeID int64

// Int64 returns the BackendNodeID as int64 value.
func (t BackendNodeID) Int64() int64 {
	return int64(t)
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *BackendNodeID) UnmarshalJSON(buf []byte) error {
	if l := len(buf); l > 2 && buf[0] == '"' && buf[l-1] == '"' {
		buf = buf[1 : l-1]
	}

	v, err := strconv.ParseInt(string(buf), 10, 64)
	if err != nil {
		return err
	}

	*t = BackendNodeID(v)
	return nil
}

// BackendNode backend node with a friendly name.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-BackendNode
type BackendNode struct {
	NodeType      NodeType      `json:"nodeType"` // Node's nodeType.
	NodeName      string        `json:"nodeName"` // Node's nodeName.
	BackendNodeID BackendNodeID `json:"backendNodeId"`
}

// PseudoType pseudo element type.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-PseudoType
type PseudoType string

// String returns the PseudoType as string value.
func (t PseudoType) String() string {
	return string(t)
}

// PseudoType values.
const (
	PseudoTypeFirstLine                   PseudoType = "first-line"
	PseudoTypeFirstLetter                 PseudoType = "first-letter"
	PseudoTypeCheckmark                   PseudoType = "checkmark"
	PseudoTypeBefore                      PseudoType = "before"
	PseudoTypeAfter                       PseudoType = "after"
	PseudoTypePickerIcon                  PseudoType = "picker-icon"
	PseudoTypeMarker                      PseudoType = "marker"
	PseudoTypeBackdrop                    PseudoType = "backdrop"
	PseudoTypeColumn                      PseudoType = "column"
	PseudoTypeSelection                   PseudoType = "selection"
	PseudoTypeSearchText                  PseudoType = "search-text"
	PseudoTypeTargetText                  PseudoType = "target-text"
	PseudoTypeSpellingError               PseudoType = "spelling-error"
	PseudoTypeGrammarError                PseudoType = "grammar-error"
	PseudoTypeHighlight                   PseudoType = "highlight"
	PseudoTypeFirstLineInherited          PseudoType = "first-line-inherited"
	PseudoTypeScrollMarker                PseudoType = "scroll-marker"
	PseudoTypeScrollMarkerGroup           PseudoType = "scroll-marker-group"
	PseudoTypeScrollButton                PseudoType = "scroll-button"
	PseudoTypeScrollbar                   PseudoType = "scrollbar"
	PseudoTypeScrollbarThumb              PseudoType = "scrollbar-thumb"
	PseudoTypeScrollbarButton             PseudoType = "scrollbar-button"
	PseudoTypeScrollbarTrack              PseudoType = "scrollbar-track"
	PseudoTypeScrollbarTrackPiece         PseudoType = "scrollbar-track-piece"
	PseudoTypeScrollbarCorner             PseudoType = "scrollbar-corner"
	PseudoTypeResizer                     PseudoType = "resizer"
	PseudoTypeInputListButton             PseudoType = "input-list-button"
	PseudoTypeViewTransition              PseudoType = "view-transition"
	PseudoTypeViewTransitionGroup         PseudoType = "view-transition-group"
	PseudoTypeViewTransitionImagePair     PseudoType = "view-transition-image-pair"
	PseudoTypeViewTransitionGroupChildren PseudoType = "view-transition-group-children"
	PseudoTypeViewTransitionOld           PseudoType = "view-transition-old"
	PseudoTypeViewTransitionNew           PseudoType = "view-transition-new"
	PseudoTypePlaceholder                 PseudoType = "placeholder"
	PseudoTypeFileSelectorButton          PseudoType = "file-selector-button"
	PseudoTypeDetailsContent              PseudoType = "details-content"
	PseudoTypePicker                      PseudoType = "picker"
	PseudoTypePermissionIcon              PseudoType = "permission-icon"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *PseudoType) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch PseudoType(s) {
	case PseudoTypeFirstLine:
		*t = PseudoTypeFirstLine
	case PseudoTypeFirstLetter:
		*t = PseudoTypeFirstLetter
	case PseudoTypeCheckmark:
		*t = PseudoTypeCheckmark
	case PseudoTypeBefore:
		*t = PseudoTypeBefore
	case PseudoTypeAfter:
		*t = PseudoTypeAfter
	case PseudoTypePickerIcon:
		*t = PseudoTypePickerIcon
	case PseudoTypeMarker:
		*t = PseudoTypeMarker
	case PseudoTypeBackdrop:
		*t = PseudoTypeBackdrop
	case PseudoTypeColumn:
		*t = PseudoTypeColumn
	case PseudoTypeSelection:
		*t = PseudoTypeSelection
	case PseudoTypeSearchText:
		*t = PseudoTypeSearchText
	case PseudoTypeTargetText:
		*t = PseudoTypeTargetText
	case PseudoTypeSpellingError:
		*t = PseudoTypeSpellingError
	case PseudoTypeGrammarError:
		*t = PseudoTypeGrammarError
	case PseudoTypeHighlight:
		*t = PseudoTypeHighlight
	case PseudoTypeFirstLineInherited:
		*t = PseudoTypeFirstLineInherited
	case PseudoTypeScrollMarker:
		*t = PseudoTypeScrollMarker
	case PseudoTypeScrollMarkerGroup:
		*t = PseudoTypeScrollMarkerGroup
	case PseudoTypeScrollButton:
		*t = PseudoTypeScrollButton
	case PseudoTypeScrollbar:
		*t = PseudoTypeScrollbar
	case PseudoTypeScrollbarThumb:
		*t = PseudoTypeScrollbarThumb
	case PseudoTypeScrollbarButton:
		*t = PseudoTypeScrollbarButton
	case PseudoTypeScrollbarTrack:
		*t = PseudoTypeScrollbarTrack
	case PseudoTypeScrollbarTrackPiece:
		*t = PseudoTypeScrollbarTrackPiece
	case PseudoTypeScrollbarCorner:
		*t = PseudoTypeScrollbarCorner
	case PseudoTypeResizer:
		*t = PseudoTypeResizer
	case PseudoTypeInputListButton:
		*t = PseudoTypeInputListButton
	case PseudoTypeViewTransition:
		*t = PseudoTypeViewTransition
	case PseudoTypeViewTransitionGroup:
		*t = PseudoTypeViewTransitionGroup
	case PseudoTypeViewTransitionImagePair:
		*t = PseudoTypeViewTransitionImagePair
	case PseudoTypeViewTransitionGroupChildren:
		*t = PseudoTypeViewTransitionGroupChildren
	case PseudoTypeViewTransitionOld:
		*t = PseudoTypeViewTransitionOld
	case PseudoTypeViewTransitionNew:
		*t = PseudoTypeViewTransitionNew
	case PseudoTypePlaceholder:
		*t = PseudoTypePlaceholder
	case PseudoTypeFileSelectorButton:
		*t = PseudoTypeFileSelectorButton
	case PseudoTypeDetailsContent:
		*t = PseudoTypeDetailsContent
	case PseudoTypePicker:
		*t = PseudoTypePicker
	case PseudoTypePermissionIcon:
		*t = PseudoTypePermissionIcon
	default:
		return fmt.Errorf("unknown PseudoType value: %v", s)
	}
	return nil
}

// ShadowRootType shadow root type.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-ShadowRootType
type ShadowRootType string

// String returns the ShadowRootType as string value.
func (t ShadowRootType) String() string {
	return string(t)
}

// ShadowRootType values.
const (
	ShadowRootTypeUserAgent ShadowRootType = "user-agent"
	ShadowRootTypeOpen      ShadowRootType = "open"
	ShadowRootTypeClosed    ShadowRootType = "closed"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *ShadowRootType) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch ShadowRootType(s) {
	case ShadowRootTypeUserAgent:
		*t = ShadowRootTypeUserAgent
	case ShadowRootTypeOpen:
		*t = ShadowRootTypeOpen
	case ShadowRootTypeClosed:
		*t = ShadowRootTypeClosed
	default:
		return fmt.Errorf("unknown ShadowRootType value: %v", s)
	}
	return nil
}

// CompatibilityMode document compatibility mode.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-CompatibilityMode
type CompatibilityMode string

// String returns the CompatibilityMode as string value.
func (t CompatibilityMode) String() string {
	return string(t)
}

// CompatibilityMode values.
const (
	CompatibilityModeQuirksMode        CompatibilityMode = "QuirksMode"
	CompatibilityModeLimitedQuirksMode CompatibilityMode = "LimitedQuirksMode"
	CompatibilityModeNoQuirksMode      CompatibilityMode = "NoQuirksMode"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *CompatibilityMode) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch CompatibilityMode(s) {
	case CompatibilityModeQuirksMode:
		*t = CompatibilityModeQuirksMode
	case CompatibilityModeLimitedQuirksMode:
		*t = CompatibilityModeLimitedQuirksMode
	case CompatibilityModeNoQuirksMode:
		*t = CompatibilityModeNoQuirksMode
	default:
		return fmt.Errorf("unknown CompatibilityMode value: %v", s)
	}
	return nil
}

// Node DOM interaction is implemented in terms of mirror objects that
// represent the actual DOM nodes. DOMNode is a base node mirror type.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-Node
type Node struct {
	NodeID            NodeID            `json:"nodeId"`                              // Node identifier that is passed into the rest of the DOM messages as the nodeId. Backend will only push node with given id once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client.
	ParentID          NodeID            `json:"parentId,omitempty,omitzero"`         // The id of the parent node if any.
	BackendNodeID     BackendNodeID     `json:"backendNodeId"`                       // The BackendNodeId for this node.
	NodeType          NodeType          `json:"nodeType"`                            // Node's nodeType.
	NodeName          string            `json:"nodeName"`                            // Node's nodeName.
	LocalName         string            `json:"localName"`                           // Node's localName.
	NodeValue         string            `json:"nodeValue"`                           // Node's nodeValue.
	ChildNodeCount    int64             `json:"childNodeCount,omitempty,omitzero"`   // Child count for Container nodes.
	Children          []*Node           `json:"children,omitempty,omitzero"`         // Child nodes of this node when requested with children.
	Attributes        []string          `json:"attributes,omitempty,omitzero"`       // Attributes of the Element node in the form of flat array [name1, value1, name2, value2].
	DocumentURL       string            `json:"documentURL,omitempty,omitzero"`      // Document URL that Document or FrameOwner node points to.
	BaseURL           string            `json:"baseURL,omitempty,omitzero"`          // Base URL that Document or FrameOwner node uses for URL completion.
	PublicID          string            `json:"publicId,omitempty,omitzero"`         // DocumentType's publicId.
	SystemID          string            `json:"systemId,omitempty,omitzero"`         // DocumentType's systemId.
	InternalSubset    string            `json:"internalSubset,omitempty,omitzero"`   // DocumentType's internalSubset.
	XMLVersion        string            `json:"xmlVersion,omitempty,omitzero"`       // Document's XML version in case of XML documents.
	Name              string            `json:"name,omitempty,omitzero"`             // Attr's name.
	Value             string            `json:"value,omitempty,omitzero"`            // Attr's value.
	PseudoType        PseudoType        `json:"pseudoType,omitempty,omitzero"`       // Pseudo element type for this node.
	PseudoIdentifier  string            `json:"pseudoIdentifier,omitempty,omitzero"` // Pseudo element identifier for this node. Only present if there is a valid pseudoType.
	ShadowRootType    ShadowRootType    `json:"shadowRootType,omitempty,omitzero"`   // Shadow root type.
	FrameID           FrameID           `json:"frameId,omitempty,omitzero"`          // Frame ID for frame owner elements.
	ContentDocument   *Node             `json:"contentDocument,omitempty,omitzero"`  // Content document for frame owner elements.
	ShadowRoots       []*Node           `json:"shadowRoots,omitempty,omitzero"`      // Shadow root list for given element host.
	TemplateContent   *Node             `json:"templateContent,omitempty,omitzero"`  // Content document fragment for template elements.
	PseudoElements    []*Node           `json:"pseudoElements,omitempty,omitzero"`   // Pseudo elements associated with this node.
	DistributedNodes  []*BackendNode    `json:"distributedNodes,omitempty,omitzero"` // Distributed nodes for given insertion point.
	IsSVG             bool              `json:"isSVG"`                               // Whether the node is SVG.
	CompatibilityMode CompatibilityMode `json:"compatibilityMode,omitempty,omitzero"`
	AssignedSlot      *BackendNode      `json:"assignedSlot,omitempty,omitzero"`
	IsScrollable      bool              `json:"isScrollable"`
	Parent            *Node             `json:"-"` // Parent node.
	Invalidated       chan struct{}     `json:"-"` // Invalidated channel.
	State             NodeState         `json:"-"` // Node state.
	sync.RWMutex      `json:"-"`        // Read write mutex.
}

// AttributeValue returns the named attribute for the node.
func (n *Node) AttributeValue(name string) string {
	value, _ := n.Attribute(name)
	return value
}

// Attribute returns the named attribute for the node and if it exists.
func (n *Node) Attribute(name string) (string, bool) {
	n.RLock()
	defer n.RUnlock()

	for i := 0; i < len(n.Attributes); i += 2 {
		if n.Attributes[i] == name {
			return n.Attributes[i+1], true
		}
	}

	return "", false
}

// xpath builds the xpath string.
func (n *Node) xpath(stopAtDocument, stopAtID bool) string {
	n.RLock()
	defer n.RUnlock()

	p, pos, id := "", "", n.AttributeValue("id")
	switch {
	case n.Parent == nil:
		return n.LocalName

	case stopAtDocument && n.NodeType == NodeTypeDocument:
		return ""

	case stopAtID && id != "":
		p = "/"
		pos = `[@id='` + id + `']`

	case n.Parent != nil:
		var i int
		var found bool

		n.Parent.RLock()
		for j := 0; j < len(n.Parent.Children); j++ {
			if n.Parent.Children[j].LocalName == n.LocalName {
				i++
			}
			if n.Parent.Children[j].NodeID == n.NodeID {
				found = true
				break
			}
		}
		n.Parent.RUnlock()

		if found {
			pos = "[" + strconv.Itoa(i) + "]"
		}

		p = n.Parent.xpath(stopAtDocument, stopAtID)
	}

	localName := n.LocalName
	if n.IsSVG {
		localName = `*[local-name()='` + localName + `']`
	}
	return p + "/" + localName + pos
}

// PartialXPathByID returns the partial XPath for the node, stopping at the
// first parent with an id attribute or at nearest parent document node.
func (n *Node) PartialXPathByID() string {
	return n.xpath(true, true)
}

// PartialXPath returns the partial XPath for the node, stopping at the nearest
// parent document node.
func (n *Node) PartialXPath() string {
	return n.xpath(true, false)
}

// FullXPathByID returns the full XPath for the node, stopping at the top most
// document root or at the closest parent node with an id attribute.
func (n *Node) FullXPathByID() string {
	return n.xpath(false, true)
}

// FullXPath returns the full XPath for the node, stopping only at the top most
// document root.
func (n *Node) FullXPath() string {
	return n.xpath(false, false)
}

// WriteTo writes a readable representation of the node and all its children to w.
func (n *Node) WriteTo(w io.Writer, prefix, indent string, nodeIDs bool) (int, error) {
	if n == nil {
		return w.Write([]byte(prefix + "<nil>"))
	}

	n.RLock()
	defer n.RUnlock()

	var err error
	var nn, c int

	// prefix
	if c, err = w.Write([]byte(prefix)); err != nil {
		return nn + c, err
	}
	nn += c

	// node name
	if n.LocalName != "" {
		if c, err = w.Write([]byte(n.LocalName)); err != nil {
			return nn + c, err
		}
		nn += c
	} else {
		if c, err = w.Write([]byte(n.NodeName)); err != nil {
			return nn + c, err
		}
		nn += c
	}

	// add #id
	var hasID int
	for i := 0; i < len(n.Attributes); i += 2 {
		if strings.ToLower(n.Attributes[i]) == "id" {
			if c, err = w.Write([]byte("#" + n.Attributes[i+1])); err != nil {
				return nn + c, err
			}
			nn += c
			hasID = 2
			break
		}
	}

	// node type
	if n.NodeType != NodeTypeElement && n.NodeType != NodeTypeText {
		if c, err = fmt.Fprintf(w, " <%s>", n.NodeType); err != nil {
			return nn + c, err
		}
		nn += c
	}

	// node value
	if n.NodeType == NodeTypeText {
		v := n.NodeValue
		if len(v) > 15 {
			v = v[:15] + "..."
		}
		if c, err = fmt.Fprintf(w, " %q", v); err != nil {
			return nn + c, err
		}
		nn += c
	}

	// attributes
	if n.NodeType == NodeTypeElement && len(n.Attributes) > hasID {
		if c, err = w.Write([]byte(" [")); err != nil {
			return nn + c, err
		}
		nn += c
		for i, space := 0, ""; i < len(n.Attributes); i += 2 {
			if strings.ToLower(n.Attributes[i]) == "id" {
				continue
			}
			if c, err = fmt.Fprintf(w, "%s%s=%q", space, n.Attributes[i], n.Attributes[i+1]); err != nil {
				return nn + c, err
			}
			nn += c
			if space == "" {
				space = " "
			}
		}
		if c, err = w.Write([]byte{']'}); err != nil {
			return nn + c, err
		}
		nn += c
	}

	// node id
	if nodeIDs {
		if c, err = fmt.Fprintf(w, " (%d)", n.NodeID); err != nil {
			return nn + c, err
		}
		nn += c
	}

	// children
	for i := 0; i < len(n.Children); i++ {
		if c, err = fmt.Fprintln(w); err != nil {
			return nn + c, err
		}
		nn += c
		if c, err = n.Children[i].WriteTo(w, prefix+indent, indent, nodeIDs); err != nil {
			return nn + c, err
		}
		nn += c
	}
	return nn, nil
}

// Dump builds a printable string representation of the node and its children.
func (n *Node) Dump(prefix, indent string, nodeIDs bool) string {
	var buf bytes.Buffer
	_, _ = n.WriteTo(&buf, prefix, indent, nodeIDs)
	return buf.String()
}

// NodeState is the state of a DOM node.
type NodeState uint8

// NodeState enum values.
const (
	NodeReady NodeState = 1 << (7 - iota)
	NodeVisible
	NodeHighlighted
)

// nodeStateNames are the names of the node states.
var nodeStateNames = map[NodeState]string{
	NodeReady:       "Ready",
	NodeVisible:     "Visible",
	NodeHighlighted: "Highlighted",
}

// String satisfies stringer interface.
func (ns NodeState) String() string {
	var s []string
	for k, v := range nodeStateNames {
		if ns&k != 0 {
			s = append(s, v)
		}
	}
	return "[" + strings.Join(s, " ") + "]"
}

// EmptyNodeID is the "non-existent" node id.
const EmptyNodeID = NodeID(0)

// RGBA a structure holding an RGBA color.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-RGBA
type RGBA struct {
	R int64   `json:"r"` // The red component, in the [0-255] range.
	G int64   `json:"g"` // The green component, in the [0-255] range.
	B int64   `json:"b"` // The blue component, in the [0-255] range.
	A float64 `json:"a"` // The alpha component, in the [0-1] range (default: 1).
}

// NodeType node type.
//
// See: https://developer.mozilla.org/en/docs/Web/API/Node/nodeType
type NodeType int64

// Int64 returns the NodeType as int64 value.
func (t NodeType) Int64() int64 {
	return int64(t)
}

// NodeType values.
const (
	NodeTypeElement               NodeType = 1
	NodeTypeAttribute             NodeType = 2
	NodeTypeText                  NodeType = 3
	NodeTypeCDATA                 NodeType = 4
	NodeTypeEntityReference       NodeType = 5
	NodeTypeEntity                NodeType = 6
	NodeTypeProcessingInstruction NodeType = 7
	NodeTypeComment               NodeType = 8
	NodeTypeDocument              NodeType = 9
	NodeTypeDocumentType          NodeType = 10
	NodeTypeDocumentFragment      NodeType = 11
	NodeTypeNotation              NodeType = 12
)

// String satisfies the [fmt.Stringer] interface.
func (t NodeType) String() string {
	switch t {
	case NodeTypeElement:
		return "Element"
	case NodeTypeAttribute:
		return "Attribute"
	case NodeTypeText:
		return "Text"
	case NodeTypeCDATA:
		return "CDATA"
	case NodeTypeEntityReference:
		return "EntityReference"
	case NodeTypeEntity:
		return "Entity"
	case NodeTypeProcessingInstruction:
		return "ProcessingInstruction"
	case NodeTypeComment:
		return "Comment"
	case NodeTypeDocument:
		return "Document"
	case NodeTypeDocumentType:
		return "DocumentType"
	case NodeTypeDocumentFragment:
		return "DocumentFragment"
	case NodeTypeNotation:
		return "Notation"
	}
	return fmt.Sprintf("NodeType(%d)", t)
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *NodeType) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	v, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return err
	}
	switch NodeType(v) {
	case NodeTypeElement:
		*t = NodeTypeElement
	case NodeTypeAttribute:
		*t = NodeTypeAttribute
	case NodeTypeText:
		*t = NodeTypeText
	case NodeTypeCDATA:
		*t = NodeTypeCDATA
	case NodeTypeEntityReference:
		*t = NodeTypeEntityReference
	case NodeTypeEntity:
		*t = NodeTypeEntity
	case NodeTypeProcessingInstruction:
		*t = NodeTypeProcessingInstruction
	case NodeTypeComment:
		*t = NodeTypeComment
	case NodeTypeDocument:
		*t = NodeTypeDocument
	case NodeTypeDocumentType:
		*t = NodeTypeDocumentType
	case NodeTypeDocumentFragment:
		*t = NodeTypeDocumentFragment
	case NodeTypeNotation:
		*t = NodeTypeNotation
	default:
		return fmt.Errorf("unknown NodeType value: %v", s)
	}
	return nil
}

// LoaderID unique loader identifier.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Network#type-LoaderId
type LoaderID string

// String returns the LoaderID as string value.
func (t LoaderID) String() string {
	return string(t)
}

// TimeSinceEpoch UTC time in seconds, counted from January 1, 1970.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Network#type-TimeSinceEpoch
type TimeSinceEpoch time.Time

// Time returns the TimeSinceEpoch as time.Time value.
func (t TimeSinceEpoch) Time() time.Time {
	return time.Time(t)
}

// MarshalJSON satisfies [json.Marshaler].
func (t TimeSinceEpoch) MarshalJSON() ([]byte, error) {
	v := float64(time.Time(t).UnixNano() / int64(time.Second))
	return strconv.AppendFloat(make([]byte, 0, 20), v, 'f', -1, 64), nil
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *TimeSinceEpoch) UnmarshalJSON(buf []byte) error {
	f, err := strconv.ParseFloat(string(buf), 64)
	if err != nil {
		return err
	}
	*t = TimeSinceEpoch(time.Unix(0, int64(f*float64(time.Second))))
	return nil
}

// MonotonicTime monotonically increasing time in seconds since an arbitrary
// point in the past.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Network#type-MonotonicTime
type MonotonicTime time.Time

// Time returns the MonotonicTime as time.Time value.
func (t MonotonicTime) Time() time.Time {
	return time.Time(t)
}

// MonotonicTimeEpoch is the MonotonicTime time epoch.
var MonotonicTimeEpoch *time.Time

func init() {
	// initialize epoch
	bt := sysutil.BootTime()
	MonotonicTimeEpoch = &bt
}

// MarshalJSON satisfies [json.Marshaler].
func (t MonotonicTime) MarshalJSON() ([]byte, error) {
	v := float64(time.Time(t).Sub(*MonotonicTimeEpoch)) / float64(time.Second)
	return strconv.AppendFloat(make([]byte, 0, 20), v, 'f', -1, 64), nil
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *MonotonicTime) UnmarshalJSON(buf []byte) error {
	f, err := strconv.ParseFloat(string(buf), 64)
	if err != nil {
		return err
	}
	*t = MonotonicTime(MonotonicTimeEpoch.Add(time.Duration(f * float64(time.Second))))
	return nil
}

// TimeSinceEpochMilli special timestamp type for Response's responseTime
// field.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Network#type-TimeSinceEpochMilli
type TimeSinceEpochMilli time.Time

// Time returns the TimeSinceEpochMilli as time.Time value.
func (t TimeSinceEpochMilli) Time() time.Time {
	return time.Time(t)
}

// MarshalJSON satisfies [json.Marshaler].
func (t TimeSinceEpochMilli) MarshalJSON() ([]byte, error) {
	v := float64(time.Time(t).UnixNano() / int64(time.Millisecond))
	return strconv.AppendFloat(make([]byte, 0, 20), v, 'f', -1, 64), nil
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *TimeSinceEpochMilli) UnmarshalJSON(buf []byte) error {
	f, err := strconv.ParseFloat(string(buf), 64)
	if err != nil {
		return err
	}
	*t = TimeSinceEpochMilli(time.Unix(0, int64(f*float64(time.Millisecond))))
	return nil
}

// FrameID unique frame identifier.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-FrameId
type FrameID string

// String returns the FrameID as string value.
func (t FrameID) String() string {
	return string(t)
}

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *FrameID) UnmarshalJSON(buf []byte) error {
	if l := len(buf); l > 2 && buf[0] == '"' && buf[l-1] == '"' {
		buf = buf[1 : l-1]
	}

	*t = FrameID(buf)
	return nil
}

// AdFrameType indicates whether a frame has been identified as an ad.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-AdFrameType
type AdFrameType string

// String returns the AdFrameType as string value.
func (t AdFrameType) String() string {
	return string(t)
}

// AdFrameType values.
const (
	AdFrameTypeNone  AdFrameType = "none"
	AdFrameTypeChild AdFrameType = "child"
	AdFrameTypeRoot  AdFrameType = "root"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *AdFrameType) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch AdFrameType(s) {
	case AdFrameTypeNone:
		*t = AdFrameTypeNone
	case AdFrameTypeChild:
		*t = AdFrameTypeChild
	case AdFrameTypeRoot:
		*t = AdFrameTypeRoot
	default:
		return fmt.Errorf("unknown AdFrameType value: %v", s)
	}
	return nil
}

// AdFrameExplanation [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-AdFrameExplanation
type AdFrameExplanation string

// String returns the AdFrameExplanation as string value.
func (t AdFrameExplanation) String() string {
	return string(t)
}

// AdFrameExplanation values.
const (
	AdFrameExplanationParentIsAd          AdFrameExplanation = "ParentIsAd"
	AdFrameExplanationCreatedByAdScript   AdFrameExplanation = "CreatedByAdScript"
	AdFrameExplanationMatchedBlockingRule AdFrameExplanation = "MatchedBlockingRule"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *AdFrameExplanation) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch AdFrameExplanation(s) {
	case AdFrameExplanationParentIsAd:
		*t = AdFrameExplanationParentIsAd
	case AdFrameExplanationCreatedByAdScript:
		*t = AdFrameExplanationCreatedByAdScript
	case AdFrameExplanationMatchedBlockingRule:
		*t = AdFrameExplanationMatchedBlockingRule
	default:
		return fmt.Errorf("unknown AdFrameExplanation value: %v", s)
	}
	return nil
}

// AdFrameStatus indicates whether a frame has been identified as an ad and
// why.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-AdFrameStatus
type AdFrameStatus struct {
	AdFrameType  AdFrameType          `json:"adFrameType"`
	Explanations []AdFrameExplanation `json:"explanations,omitempty,omitzero"`
}

// SecureContextType indicates whether the frame is a secure context and why
// it is the case.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-SecureContextType
type SecureContextType string

// String returns the SecureContextType as string value.
func (t SecureContextType) String() string {
	return string(t)
}

// SecureContextType values.
const (
	SecureContextTypeSecure           SecureContextType = "Secure"
	SecureContextTypeSecureLocalhost  SecureContextType = "SecureLocalhost"
	SecureContextTypeInsecureScheme   SecureContextType = "InsecureScheme"
	SecureContextTypeInsecureAncestor SecureContextType = "InsecureAncestor"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *SecureContextType) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch SecureContextType(s) {
	case SecureContextTypeSecure:
		*t = SecureContextTypeSecure
	case SecureContextTypeSecureLocalhost:
		*t = SecureContextTypeSecureLocalhost
	case SecureContextTypeInsecureScheme:
		*t = SecureContextTypeInsecureScheme
	case SecureContextTypeInsecureAncestor:
		*t = SecureContextTypeInsecureAncestor
	default:
		return fmt.Errorf("unknown SecureContextType value: %v", s)
	}
	return nil
}

// CrossOriginIsolatedContextType indicates whether the frame is cross-origin
// isolated and why it is the case.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-CrossOriginIsolatedContextType
type CrossOriginIsolatedContextType string

// String returns the CrossOriginIsolatedContextType as string value.
func (t CrossOriginIsolatedContextType) String() string {
	return string(t)
}

// CrossOriginIsolatedContextType values.
const (
	CrossOriginIsolatedContextTypeIsolated                   CrossOriginIsolatedContextType = "Isolated"
	CrossOriginIsolatedContextTypeNotIsolated                CrossOriginIsolatedContextType = "NotIsolated"
	CrossOriginIsolatedContextTypeNotIsolatedFeatureDisabled CrossOriginIsolatedContextType = "NotIsolatedFeatureDisabled"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *CrossOriginIsolatedContextType) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch CrossOriginIsolatedContextType(s) {
	case CrossOriginIsolatedContextTypeIsolated:
		*t = CrossOriginIsolatedContextTypeIsolated
	case CrossOriginIsolatedContextTypeNotIsolated:
		*t = CrossOriginIsolatedContextTypeNotIsolated
	case CrossOriginIsolatedContextTypeNotIsolatedFeatureDisabled:
		*t = CrossOriginIsolatedContextTypeNotIsolatedFeatureDisabled
	default:
		return fmt.Errorf("unknown CrossOriginIsolatedContextType value: %v", s)
	}
	return nil
}

// GatedAPIFeatures [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-GatedAPIFeatures
type GatedAPIFeatures string

// String returns the GatedAPIFeatures as string value.
func (t GatedAPIFeatures) String() string {
	return string(t)
}

// GatedAPIFeatures values.
const (
	GatedAPIFeaturesSharedArrayBuffers                GatedAPIFeatures = "SharedArrayBuffers"
	GatedAPIFeaturesSharedArrayBuffersTransferAllowed GatedAPIFeatures = "SharedArrayBuffersTransferAllowed"
	GatedAPIFeaturesPerformanceMeasureMemory          GatedAPIFeatures = "PerformanceMeasureMemory"
	GatedAPIFeaturesPerformanceProfile                GatedAPIFeatures = "PerformanceProfile"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *GatedAPIFeatures) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch GatedAPIFeatures(s) {
	case GatedAPIFeaturesSharedArrayBuffers:
		*t = GatedAPIFeaturesSharedArrayBuffers
	case GatedAPIFeaturesSharedArrayBuffersTransferAllowed:
		*t = GatedAPIFeaturesSharedArrayBuffersTransferAllowed
	case GatedAPIFeaturesPerformanceMeasureMemory:
		*t = GatedAPIFeaturesPerformanceMeasureMemory
	case GatedAPIFeaturesPerformanceProfile:
		*t = GatedAPIFeaturesPerformanceProfile
	default:
		return fmt.Errorf("unknown GatedAPIFeatures value: %v", s)
	}
	return nil
}

// OriginTrialTokenStatus origin
// Trial(https://www.chromium.org/blink/origin-trials) support. Status for an
// Origin Trial token.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-OriginTrialTokenStatus
type OriginTrialTokenStatus string

// String returns the OriginTrialTokenStatus as string value.
func (t OriginTrialTokenStatus) String() string {
	return string(t)
}

// OriginTrialTokenStatus values.
const (
	OriginTrialTokenStatusSuccess                OriginTrialTokenStatus = "Success"
	OriginTrialTokenStatusNotSupported           OriginTrialTokenStatus = "NotSupported"
	OriginTrialTokenStatusInsecure               OriginTrialTokenStatus = "Insecure"
	OriginTrialTokenStatusExpired                OriginTrialTokenStatus = "Expired"
	OriginTrialTokenStatusWrongOrigin            OriginTrialTokenStatus = "WrongOrigin"
	OriginTrialTokenStatusInvalidSignature       OriginTrialTokenStatus = "InvalidSignature"
	OriginTrialTokenStatusMalformed              OriginTrialTokenStatus = "Malformed"
	OriginTrialTokenStatusWrongVersion           OriginTrialTokenStatus = "WrongVersion"
	OriginTrialTokenStatusFeatureDisabled        OriginTrialTokenStatus = "FeatureDisabled"
	OriginTrialTokenStatusTokenDisabled          OriginTrialTokenStatus = "TokenDisabled"
	OriginTrialTokenStatusFeatureDisabledForUser OriginTrialTokenStatus = "FeatureDisabledForUser"
	OriginTrialTokenStatusUnknownTrial           OriginTrialTokenStatus = "UnknownTrial"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *OriginTrialTokenStatus) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch OriginTrialTokenStatus(s) {
	case OriginTrialTokenStatusSuccess:
		*t = OriginTrialTokenStatusSuccess
	case OriginTrialTokenStatusNotSupported:
		*t = OriginTrialTokenStatusNotSupported
	case OriginTrialTokenStatusInsecure:
		*t = OriginTrialTokenStatusInsecure
	case OriginTrialTokenStatusExpired:
		*t = OriginTrialTokenStatusExpired
	case OriginTrialTokenStatusWrongOrigin:
		*t = OriginTrialTokenStatusWrongOrigin
	case OriginTrialTokenStatusInvalidSignature:
		*t = OriginTrialTokenStatusInvalidSignature
	case OriginTrialTokenStatusMalformed:
		*t = OriginTrialTokenStatusMalformed
	case OriginTrialTokenStatusWrongVersion:
		*t = OriginTrialTokenStatusWrongVersion
	case OriginTrialTokenStatusFeatureDisabled:
		*t = OriginTrialTokenStatusFeatureDisabled
	case OriginTrialTokenStatusTokenDisabled:
		*t = OriginTrialTokenStatusTokenDisabled
	case OriginTrialTokenStatusFeatureDisabledForUser:
		*t = OriginTrialTokenStatusFeatureDisabledForUser
	case OriginTrialTokenStatusUnknownTrial:
		*t = OriginTrialTokenStatusUnknownTrial
	default:
		return fmt.Errorf("unknown OriginTrialTokenStatus value: %v", s)
	}
	return nil
}

// OriginTrialStatus status for an Origin Trial.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-OriginTrialStatus
type OriginTrialStatus string

// String returns the OriginTrialStatus as string value.
func (t OriginTrialStatus) String() string {
	return string(t)
}

// OriginTrialStatus values.
const (
	OriginTrialStatusEnabled               OriginTrialStatus = "Enabled"
	OriginTrialStatusValidTokenNotProvided OriginTrialStatus = "ValidTokenNotProvided"
	OriginTrialStatusOSNotSupported        OriginTrialStatus = "OSNotSupported"
	OriginTrialStatusTrialNotAllowed       OriginTrialStatus = "TrialNotAllowed"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *OriginTrialStatus) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch OriginTrialStatus(s) {
	case OriginTrialStatusEnabled:
		*t = OriginTrialStatusEnabled
	case OriginTrialStatusValidTokenNotProvided:
		*t = OriginTrialStatusValidTokenNotProvided
	case OriginTrialStatusOSNotSupported:
		*t = OriginTrialStatusOSNotSupported
	case OriginTrialStatusTrialNotAllowed:
		*t = OriginTrialStatusTrialNotAllowed
	default:
		return fmt.Errorf("unknown OriginTrialStatus value: %v", s)
	}
	return nil
}

// OriginTrialUsageRestriction [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-OriginTrialUsageRestriction
type OriginTrialUsageRestriction string

// String returns the OriginTrialUsageRestriction as string value.
func (t OriginTrialUsageRestriction) String() string {
	return string(t)
}

// OriginTrialUsageRestriction values.
const (
	OriginTrialUsageRestrictionNone   OriginTrialUsageRestriction = "None"
	OriginTrialUsageRestrictionSubset OriginTrialUsageRestriction = "Subset"
)

// UnmarshalJSON satisfies [json.Unmarshaler].
func (t *OriginTrialUsageRestriction) UnmarshalJSON(buf []byte) error {
	s := string(buf)
	s = strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`)

	switch OriginTrialUsageRestriction(s) {
	case OriginTrialUsageRestrictionNone:
		*t = OriginTrialUsageRestrictionNone
	case OriginTrialUsageRestrictionSubset:
		*t = OriginTrialUsageRestrictionSubset
	default:
		return fmt.Errorf("unknown OriginTrialUsageRestriction value: %v", s)
	}
	return nil
}

// OriginTrialToken [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-OriginTrialToken
type OriginTrialToken struct {
	Origin           string                      `json:"origin"`
	MatchSubDomains  bool                        `json:"matchSubDomains"`
	TrialName        string                      `json:"trialName"`
	ExpiryTime       *TimeSinceEpoch             `json:"expiryTime"`
	IsThirdParty     bool                        `json:"isThirdParty"`
	UsageRestriction OriginTrialUsageRestriction `json:"usageRestriction"`
}

// OriginTrialTokenWithStatus [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-OriginTrialTokenWithStatus
type OriginTrialTokenWithStatus struct {
	RawTokenText string                 `json:"rawTokenText"`
	ParsedToken  *OriginTrialToken      `json:"parsedToken,omitempty,omitzero"` // parsedToken is present only when the token is extractable and parsable.
	Status       OriginTrialTokenStatus `json:"status"`
}

// OriginTrial [no description].
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-OriginTrial
type OriginTrial struct {
	TrialName        string                        `json:"trialName"`
	Status           OriginTrialStatus             `json:"status"`
	TokensWithStatus []*OriginTrialTokenWithStatus `json:"tokensWithStatus"`
}

// SecurityOriginDetails additional information about the frame document's
// security origin.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-SecurityOriginDetails
type SecurityOriginDetails struct {
	IsLocalhost bool `json:"isLocalhost"` // Indicates whether the frame document's security origin is one of the local hostnames (e.g. "localhost") or IP addresses (IPv4 127.0.0.0/8 or IPv6 ::1).
}

// Frame information about the Frame on the page.
//
// See: https://chromedevtools.github.io/devtools-protocol/tot/Page#type-Frame
type Frame struct {
	ID                             FrameID                        `json:"id"`                                       // Frame unique identifier.
	ParentID                       FrameID                        `json:"parentId,omitempty,omitzero"`              // Parent frame identifier.
	LoaderID                       LoaderID                       `json:"loaderId"`                                 // Identifier of the loader associated with this frame.
	Name                           string                         `json:"name,omitempty,omitzero"`                  // Frame's name as specified in the tag.
	URL                            string                         `json:"url"`                                      // Frame document's URL without fragment.
	URLFragment                    string                         `json:"urlFragment,omitempty,omitzero"`           // Frame document's URL fragment including the '#'.
	DomainAndRegistry              string                         `json:"domainAndRegistry"`                        // Frame document's registered domain, taking the public suffixes list into account. Extracted from the Frame's url. Example URLs: http://www.google.com/file.html -> "google.com" http://a.b.co.uk/file.html      -> "b.co.uk"
	SecurityOrigin                 string                         `json:"securityOrigin"`                           // Frame document's security origin.
	SecurityOriginDetails          *SecurityOriginDetails         `json:"securityOriginDetails,omitempty,omitzero"` // Additional details about the frame document's security origin.
	MimeType                       string                         `json:"mimeType"`                                 // Frame document's mimeType as determined by the browser.
	UnreachableURL                 string                         `json:"unreachableUrl,omitempty,omitzero"`        // If the frame failed to load, this contains the URL that could not be loaded. Note that unlike url above, this URL may contain a fragment.
	AdFrameStatus                  *AdFrameStatus                 `json:"adFrameStatus,omitempty,omitzero"`         // Indicates whether this frame was tagged as an ad and why.
	SecureContextType              SecureContextType              `json:"secureContextType"`                        // Indicates whether the main document is a secure context and explains why that is the case.
	CrossOriginIsolatedContextType CrossOriginIsolatedContextType `json:"crossOriginIsolatedContextType"`           // Indicates whether this is a cross origin isolated context.
	GatedAPIFeatures               []GatedAPIFeatures             `json:"gatedAPIFeatures"`                         // Indicated which gated APIs / features are available.
	State                          FrameState                     `json:"-"`                                        // Frame state.
	Root                           *Node                          `json:"-"`                                        // Frame document root.
	Nodes                          map[NodeID]*Node               `json:"-"`                                        // Frame nodes.
	sync.RWMutex                   `json:"-"`                     // Read write mutex.
}

// FrameState is the state of a Frame.
type FrameState uint16

// FrameState enum values.
const (
	FrameDOMContentEventFired FrameState = 1 << (15 - iota)
	FrameLoadEventFired
	FrameAttached
	FrameNavigated
	FrameLoading
	FrameScheduledNavigation
)

// frameStateNames are the names of the frame states.
var frameStateNames = map[FrameState]string{
	FrameDOMContentEventFired: "DOMContentEventFired",
	FrameLoadEventFired:       "LoadEventFired",
	FrameAttached:             "Attached",
	FrameNavigated:            "Navigated",
	FrameLoading:              "Loading",
	FrameScheduledNavigation:  "ScheduledNavigation",
}

// String satisfies stringer interface.
func (fs FrameState) String() string {
	var s []string
	for k, v := range frameStateNames {
		if fs&k != 0 {
			s = append(s, v)
		}
	}
	return "[" + strings.Join(s, " ") + "]"
}

// EmptyFrameID is the "non-existent" frame id.
const EmptyFrameID = FrameID("")
