# DO NOT EDIT THIS FILE!
#
# This file is generated from the CDP specification. If you need to make
# changes, edit the generator and regenerate all of the modules.
#
# CDP domain: DOMSnapshot (experimental)

from __future__ import annotations
import typing
from dataclasses import dataclass
from .util import T_JSON_DICT
from . import dom
from . import dom_debugger
from . import page


@dataclass
class DOMNode:
    """A Node in the DOM tree."""
    #: ``Node``'s nodeType.
    node_type: int
    #: ``Node``'s nodeName.
    node_name: str
    #: ``Node``'s nodeValue.
    node_value: str
    #: ``Node``'s id, corresponds to DOM.Node.backendNodeId.
    backend_node_id: dom.BackendNodeId
    #: Only set for textarea elements, contains the text value.
    text_value: typing.Optional[str] = None
    #: Only set for input elements,
    # contains the input's associated text value.
    input_value: typing.Optional[str] = None
    #: Only set for radio and checkbox input elements,
    #: indicates if the element has been checked
    input_checked: typing.Optional[bool] = None
    #: Only set for option elements,
    #: indicates if the element has been selected
    option_selected: typing.Optional[bool] = None
    #: The indexes of the node's child nodes in the ``domNodes`` array
    #: returned by ``getSnapshot``, if any.
    child_node_indexes: typing.Optional[typing.List[int]] = None
    #: Attributes of an ``Element`` node.
    attributes: typing.Optional[typing.List[NameValue]] = None
    #: Indexes of pseudo elements associated with this node
    #: in the ``domNodes`` array returned by ``getSnapshot``, if any.
    pseudo_element_indexes: typing.Optional[typing.List[int]] = None
    #: The index of the node's related layout tree node
    #: in the ``layoutTreeNodes`` array returned by ``getSnapshot``, if any.
    layout_node_index: typing.Optional[int] = None
    #: Document URL that ``Document`` or ``FrameOwner`` node points to.
    document_url: typing.Optional[str] = None
    #: Base URL that `Document` or `FrameOwner` node uses for URL completion.
    base_url: typing.Optional[str] = None
    #: Only set for documents, contains the document's content language.
    content_language: typing.Optional[str] = None
    #: Only set for documents, contains the document's character set encoding.
    document_encoding: typing.Optional[str] = None
    #: ``DocumentType`` node's publicId.
    public_id: typing.Optional[str] = None
    #: ``DocumentType`` node's systemId.
    system_id: typing.Optional[str] = None
    #: Frame ID for frame owner elements and also for the document node.
    frame_id: typing.Optional[page.FrameId] = None
    #: The index of a frame owner element's content document
    #: in the ``domNodes`` array returned by ``getSnapshot``, if any.
    content_document_index: typing.Optional[int] = None
    #: Type of a pseudo element node.
    pseudo_type: typing.Optional[dom.PseudoType] = None
    #: Shadow root type.
    shadow_root_type: typing.Optional[dom.ShadowRootType] = None
    #: Whether this DOM node responds to mouse clicks.
    #: This includes nodes that have had click event
    #: listeners attached via JavaScript as well as anchor tags
    #: that naturally navigate when clicked.
    is_clickable: typing.Optional[bool] = None
    #: Details of the node's event listeners, if any.
    event_listeners: typing.Optional[
        typing.List[dom_debugger.EventListener]
    ] = None
    #: The selected url for nodes with a srcset attribute.
    current_source_url: typing.Optional[str] = None
    #: The url of the script (if any) that generates this node.
    origin_url: typing.Optional[str] = None
    #: Scroll offsets, set when this node is a Document.
    scroll_offset_x: typing.Optional[float] = None
    scroll_offset_y: typing.Optional[float] = None

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["nodeType"] = self.node_type
        json["nodeName"] = self.node_name
        json["nodeValue"] = self.node_value
        json["backendNodeId"] = self.backend_node_id.to_json()
        if self.text_value is not None:
            json["textValue"] = self.text_value
        if self.input_value is not None:
            json["inputValue"] = self.input_value
        if self.input_checked is not None:
            json["inputChecked"] = self.input_checked
        if self.option_selected is not None:
            json["optionSelected"] = self.option_selected
        if self.child_node_indexes is not None:
            json["childNodeIndexes"] = [i for i in self.child_node_indexes]
        if self.attributes is not None:
            json["attributes"] = [i.to_json() for i in self.attributes]
        if self.pseudo_element_indexes is not None:
            json["pseudoElementIndexes"] = [
                i for i in self.pseudo_element_indexes
            ]
        if self.layout_node_index is not None:
            json["layoutNodeIndex"] = self.layout_node_index
        if self.document_url is not None:
            json["documentURL"] = self.document_url
        if self.base_url is not None:
            json["baseURL"] = self.base_url
        if self.content_language is not None:
            json["contentLanguage"] = self.content_language
        if self.document_encoding is not None:
            json["documentEncoding"] = self.document_encoding
        if self.public_id is not None:
            json["publicId"] = self.public_id
        if self.system_id is not None:
            json["systemId"] = self.system_id
        if self.frame_id is not None:
            json["frameId"] = self.frame_id.to_json()
        if self.content_document_index is not None:
            json["contentDocumentIndex"] = self.content_document_index
        if self.pseudo_type is not None:
            json["pseudoType"] = self.pseudo_type.to_json()
        if self.shadow_root_type is not None:
            json["shadowRootType"] = self.shadow_root_type.to_json()
        if self.is_clickable is not None:
            json["isClickable"] = self.is_clickable
        if self.event_listeners is not None:
            json["eventListeners"] = [
                i.to_json() for i in self.event_listeners
            ]
        if self.current_source_url is not None:
            json["currentSourceURL"] = self.current_source_url
        if self.origin_url is not None:
            json["originURL"] = self.origin_url
        if self.scroll_offset_x is not None:
            json["scrollOffsetX"] = self.scroll_offset_x
        if self.scroll_offset_y is not None:
            json["scrollOffsetY"] = self.scroll_offset_y
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> DOMNode:
        return cls(
            node_type=int(json["nodeType"]),
            node_name=str(json["nodeName"]),
            node_value=str(json["nodeValue"]),
            backend_node_id=dom.BackendNodeId.from_json(json["backendNodeId"]),
            text_value=(
                str(json["textValue"])
                if json.get("textValue", None) is not None
                else None
            ),
            input_value=(
                str(json["inputValue"])
                if json.get("inputValue", None) is not None
                else None
            ),
            input_checked=(
                bool(json["inputChecked"])
                if json.get("inputChecked", None) is not None
                else None
            ),
            option_selected=(
                bool(json["optionSelected"])
                if json.get("optionSelected", None) is not None
                else None
            ),
            child_node_indexes=(
                [int(i) for i in json["childNodeIndexes"]]
                if json.get("childNodeIndexes", None) is not None
                else None
            ),
            attributes=(
                [NameValue.from_json(i) for i in json["attributes"]]
                if json.get("attributes", None) is not None
                else None
            ),
            pseudo_element_indexes=(
                [int(i) for i in json["pseudoElementIndexes"]]
                if json.get("pseudoElementIndexes", None) is not None
                else None
            ),
            layout_node_index=(
                int(json["layoutNodeIndex"])
                if json.get("layoutNodeIndex", None) is not None
                else None
            ),
            document_url=(
                str(json["documentURL"])
                if json.get("documentURL", None) is not None
                else None
            ),
            base_url=(
                str(json["baseURL"])
                if json.get("baseURL", None) is not None
                else None
            ),
            content_language=(
                str(json["contentLanguage"])
                if json.get("contentLanguage", None) is not None
                else None
            ),
            document_encoding=(
                str(json["documentEncoding"])
                if json.get("documentEncoding", None) is not None
                else None
            ),
            public_id=(
                str(json["publicId"])
                if json.get("publicId", None) is not None
                else None
            ),
            system_id=(
                str(json["systemId"])
                if json.get("systemId", None) is not None
                else None
            ),
            frame_id=(
                page.FrameId.from_json(json["frameId"])
                if json.get("frameId", None) is not None
                else None
            ),
            content_document_index=(
                int(json["contentDocumentIndex"])
                if json.get("contentDocumentIndex", None) is not None
                else None
            ),
            pseudo_type=(
                dom.PseudoType.from_json(json["pseudoType"])
                if json.get("pseudoType", None) is not None
                else None
            ),
            shadow_root_type=(
                dom.ShadowRootType.from_json(json["shadowRootType"])
                if json.get("shadowRootType", None) is not None
                else None
            ),
            is_clickable=(
                bool(json["isClickable"])
                if json.get("isClickable", None) is not None
                else None
            ),
            event_listeners=(
                [
                    dom_debugger.EventListener.from_json(i)
                    for i in json["eventListeners"]
                ]
                if json.get("eventListeners", None) is not None
                else None
            ),
            current_source_url=(
                str(json["currentSourceURL"])
                if json.get("currentSourceURL", None) is not None
                else None
            ),
            origin_url=(
                str(json["originURL"])
                if json.get("originURL", None) is not None
                else None
            ),
            scroll_offset_x=(
                float(json["scrollOffsetX"])
                if json.get("scrollOffsetX", None) is not None
                else None
            ),
            scroll_offset_y=(
                float(json["scrollOffsetY"])
                if json.get("scrollOffsetY", None) is not None
                else None
            ),
        )


@dataclass
class InlineTextBox:
    """
    Details of post layout rendered text positions.
    The exact layout should not be regarded as
    stable and may change between versions.
    """
    #: The bounding box in document coordinates.
    #: Note that scroll offset of the document is ignored.
    bounding_box: dom.Rect
    #: The starting index in characters,
    #: for this post layout textbox substring.
    #: Characters that would be represented as a surrogate pair in
    #: UTF-16 have length 2.
    start_character_index: int
    #: The number of characters in this post layout textbox substring.
    #: Characters that would be represented as a surrogate pair in
    #: UTF-16 have length 2.
    num_characters: int

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["boundingBox"] = self.bounding_box.to_json()
        json["startCharacterIndex"] = self.start_character_index
        json["numCharacters"] = self.num_characters
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> InlineTextBox:
        return cls(
            bounding_box=dom.Rect.from_json(json["boundingBox"]),
            start_character_index=int(json["startCharacterIndex"]),
            num_characters=int(json["numCharacters"]),
        )


@dataclass
class LayoutTreeNode:
    """Details of an element in the DOM tree with a LayoutObject."""
    #: The index of the related DOM node in the ``domNodes`` array
    #: returned by ``getSnapshot``.
    dom_node_index: int
    #: The bounding box in document coordinates.
    #: Note that scroll offset of the document is ignored.
    bounding_box: dom.Rect
    #: Contents of the LayoutText, if any.
    layout_text: typing.Optional[str] = None
    #: The post-layout inline text nodes, if any.
    inline_text_nodes: typing.Optional[typing.List[InlineTextBox]] = None
    #: Index into the ``computedStyles`` array returned by ``getSnapshot``.
    style_index: typing.Optional[int] = None
    #: Global paint order index, which is determined by the stacking order
    #: of the nodes. Nodes that are painted together will have the same index.
    #: Only provided if includePaintOrder in getSnapshot was true.
    paint_order: typing.Optional[int] = None
    #: Set to true to indicate the element begins a new stacking context.
    is_stacking_context: typing.Optional[bool] = None

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["domNodeIndex"] = self.dom_node_index
        json["boundingBox"] = self.bounding_box.to_json()
        if self.layout_text is not None:
            json["layoutText"] = self.layout_text
        if self.inline_text_nodes is not None:
            json["inlineTextNodes"] = [
                i.to_json() for i in self.inline_text_nodes
            ]
        if self.style_index is not None:
            json["styleIndex"] = self.style_index
        if self.paint_order is not None:
            json["paintOrder"] = self.paint_order
        if self.is_stacking_context is not None:
            json["isStackingContext"] = self.is_stacking_context
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> LayoutTreeNode:
        return cls(
            dom_node_index=int(json["domNodeIndex"]),
            bounding_box=dom.Rect.from_json(json["boundingBox"]),
            layout_text=(
                str(json["layoutText"])
                if json.get("layoutText", None) is not None
                else None
            ),
            inline_text_nodes=(
                [InlineTextBox.from_json(i) for i in json["inlineTextNodes"]]
                if json.get("inlineTextNodes", None) is not None
                else None
            ),
            style_index=(
                int(json["styleIndex"])
                if json.get("styleIndex", None) is not None
                else None
            ),
            paint_order=(
                int(json["paintOrder"])
                if json.get("paintOrder", None) is not None
                else None
            ),
            is_stacking_context=(
                bool(json["isStackingContext"])
                if json.get("isStackingContext", None) is not None
                else None
            ),
        )


@dataclass
class ComputedStyle:
    """
    A subset of the full ComputedStyle as defined by the request whitelist.
    """
    #: Name/value pairs of computed style properties.
    properties: typing.List[NameValue]

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["properties"] = [i.to_json() for i in self.properties]
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> ComputedStyle:
        return cls(
            properties=[NameValue.from_json(i) for i in json["properties"]],
        )


@dataclass
class NameValue:
    """A name/value pair."""
    #: Attribute/property name.
    name: str
    #: Attribute/property value.
    value: str

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["name"] = self.name
        json["value"] = self.value
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> NameValue:
        return cls(
            name=str(json["name"]),
            value=str(json["value"]),
        )


class StringIndex(int):
    """Index of the string in the strings table."""

    def to_json(self) -> int:
        return self

    @classmethod
    def from_json(cls, json: int) -> StringIndex:
        return cls(json)

    def __repr__(self):
        return "StringIndex({})".format(super().__repr__())


class ArrayOfStrings(list):
    """Index of the string in the strings table."""

    def to_json(self) -> typing.List[StringIndex]:
        return self

    @classmethod
    def from_json(cls, json: typing.List[StringIndex]) -> ArrayOfStrings:
        return cls(json)

    def __repr__(self):
        return "ArrayOfStrings({})".format(super().__repr__())


@dataclass
class RareStringData:
    """Data that is only present on rare nodes."""
    index: typing.List[int]
    value: typing.List[StringIndex]

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["index"] = [i for i in self.index]
        json["value"] = [i.to_json() for i in self.value]
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> RareStringData:
        return cls(
            index=[int(i) for i in json["index"]],
            value=[StringIndex.from_json(i) for i in json["value"]],
        )


@dataclass
class RareBooleanData:
    index: typing.List[int]

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["index"] = [i for i in self.index]
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> RareBooleanData:
        return cls(
            index=[int(i) for i in json["index"]],
        )


@dataclass
class RareIntegerData:
    index: typing.List[int]
    value: typing.List[int]

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["index"] = [i for i in self.index]
        json["value"] = [i for i in self.value]
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> RareIntegerData:
        return cls(
            index=[int(i) for i in json["index"]],
            value=[int(i) for i in json["value"]],
        )


class Rectangle(list):
    def to_json(self) -> typing.List[float]:
        return self

    @classmethod
    def from_json(cls, json: typing.List[float]) -> Rectangle:
        return cls(json)

    def __repr__(self):
        return "Rectangle({})".format(super().__repr__())


@dataclass
class DocumentSnapshot:
    """Document snapshot."""
    #: Document URL that ``Document`` or ``FrameOwner`` node points to.
    document_url: StringIndex
    #: Document title.
    title: StringIndex
    #: Base URL that `Document` or `FrameOwner` node uses for URL completion.
    base_url: StringIndex
    #: Contains the document's content language.
    content_language: StringIndex
    #: Contains the document's character set encoding.
    encoding_name: StringIndex
    #: ``DocumentType`` node's publicId.
    public_id: StringIndex
    #: ``DocumentType`` node's systemId.
    system_id: StringIndex
    #: Frame ID for frame owner elements and also for the document node.
    frame_id: StringIndex
    #: A table with dom nodes.
    nodes: NodeTreeSnapshot
    #: The nodes in the layout tree.
    layout: LayoutTreeSnapshot
    #: The post-layout inline text nodes.
    text_boxes: TextBoxSnapshot
    #: Horizontal scroll offset.
    scroll_offset_x: typing.Optional[float] = None
    #: Vertical scroll offset.
    scroll_offset_y: typing.Optional[float] = None
    #: Document content width.
    content_width: typing.Optional[float] = None
    #: Document content height.
    content_height: typing.Optional[float] = None

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["documentURL"] = self.document_url.to_json()
        json["title"] = self.title.to_json()
        json["baseURL"] = self.base_url.to_json()
        json["contentLanguage"] = self.content_language.to_json()
        json["encodingName"] = self.encoding_name.to_json()
        json["publicId"] = self.public_id.to_json()
        json["systemId"] = self.system_id.to_json()
        json["frameId"] = self.frame_id.to_json()
        json["nodes"] = self.nodes.to_json()
        json["layout"] = self.layout.to_json()
        json["textBoxes"] = self.text_boxes.to_json()
        if self.scroll_offset_x is not None:
            json["scrollOffsetX"] = self.scroll_offset_x
        if self.scroll_offset_y is not None:
            json["scrollOffsetY"] = self.scroll_offset_y
        if self.content_width is not None:
            json["contentWidth"] = self.content_width
        if self.content_height is not None:
            json["contentHeight"] = self.content_height
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> DocumentSnapshot:
        return cls(
            document_url=StringIndex.from_json(json["documentURL"]),
            title=StringIndex.from_json(json["title"]),
            base_url=StringIndex.from_json(json["baseURL"]),
            content_language=StringIndex.from_json(json["contentLanguage"]),
            encoding_name=StringIndex.from_json(json["encodingName"]),
            public_id=StringIndex.from_json(json["publicId"]),
            system_id=StringIndex.from_json(json["systemId"]),
            frame_id=StringIndex.from_json(json["frameId"]),
            nodes=NodeTreeSnapshot.from_json(json["nodes"]),
            layout=LayoutTreeSnapshot.from_json(json["layout"]),
            text_boxes=TextBoxSnapshot.from_json(json["textBoxes"]),
            scroll_offset_x=(
                float(json["scrollOffsetX"])
                if json.get("scrollOffsetX", None) is not None
                else None
            ),
            scroll_offset_y=(
                float(json["scrollOffsetY"])
                if json.get("scrollOffsetY", None) is not None
                else None
            ),
            content_width=(
                float(json["contentWidth"])
                if json.get("contentWidth", None) is not None
                else None
            ),
            content_height=(
                float(json["contentHeight"])
                if json.get("contentHeight", None) is not None
                else None
            ),
        )


@dataclass
class NodeTreeSnapshot:
    """Table containing nodes."""
    #: Parent node index.
    parent_index: typing.Optional[typing.List[int]] = None
    #: ``Node``'s nodeType.
    node_type: typing.Optional[typing.List[int]] = None
    #: Type of the shadow root the ``Node`` is in.
    #: String values are equal to the ``ShadowRootType`` enum.
    shadow_root_type: typing.Optional[RareStringData] = None
    #: ``Node``'s nodeName.
    node_name: typing.Optional[typing.List[StringIndex]] = None
    #: ``Node``'s nodeValue.
    node_value: typing.Optional[typing.List[StringIndex]] = None
    #: ``Node``'s id, corresponds to DOM.Node.backendNodeId.
    backend_node_id: typing.Optional[typing.List[dom.BackendNodeId]] = None
    #: Attributes of an ``Element`` node. Flatten name, value pairs.
    attributes: typing.Optional[typing.List[ArrayOfStrings]] = None
    #: Only set for textarea elements, contains the text value.
    text_value: typing.Optional[RareStringData] = None
    #: Only set for input elements, contains the input's associated text value.
    input_value: typing.Optional[RareStringData] = None
    #: Only set for radio and checkbox input elements,
    #: indicates if the element has been checked
    input_checked: typing.Optional[RareBooleanData] = None
    #: Only set for option elements, indicates if the element has been selected
    option_selected: typing.Optional[RareBooleanData] = None
    #: The index of the document in the list of the snapshot documents.
    content_document_index: typing.Optional[RareIntegerData] = None
    #: Type of a pseudo element node.
    pseudo_type: typing.Optional[RareStringData] = None
    #: Pseudo element identifier for this node. Only present if there is a
    #: valid pseudoType.
    pseudo_identifier: typing.Optional[RareStringData] = None
    #: Whether this DOM node responds to mouse clicks.
    #: This includes nodes that have had click
    #: event listeners attached via JavaScript
    #: as well as anchor tags that naturally navigate when clicked.
    is_clickable: typing.Optional[RareBooleanData] = None
    #: The selected url for nodes with a srcset attribute.
    current_source_url: typing.Optional[RareStringData] = None
    #: The url of the script (if any) that generates this node.
    origin_url: typing.Optional[RareStringData] = None

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        if self.parent_index is not None:
            json["parentIndex"] = [i for i in self.parent_index]
        if self.node_type is not None:
            json["nodeType"] = [i for i in self.node_type]
        if self.shadow_root_type is not None:
            json["shadowRootType"] = self.shadow_root_type.to_json()
        if self.node_name is not None:
            json["nodeName"] = [i.to_json() for i in self.node_name]
        if self.node_value is not None:
            json["nodeValue"] = [i.to_json() for i in self.node_value]
        if self.backend_node_id is not None:
            json["backendNodeId"] = [i.to_json() for i in self.backend_node_id]
        if self.attributes is not None:
            json["attributes"] = [i.to_json() for i in self.attributes]
        if self.text_value is not None:
            json["textValue"] = self.text_value.to_json()
        if self.input_value is not None:
            json["inputValue"] = self.input_value.to_json()
        if self.input_checked is not None:
            json["inputChecked"] = self.input_checked.to_json()
        if self.option_selected is not None:
            json["optionSelected"] = self.option_selected.to_json()
        if self.content_document_index is not None:
            json["contentDocumentIndex"] = (
                self.content_document_index.to_json()
            )
        if self.pseudo_type is not None:
            json["pseudoType"] = self.pseudo_type.to_json()
        if self.pseudo_identifier is not None:
            json["pseudoIdentifier"] = self.pseudo_identifier.to_json()
        if self.is_clickable is not None:
            json["isClickable"] = self.is_clickable.to_json()
        if self.current_source_url is not None:
            json["currentSourceURL"] = self.current_source_url.to_json()
        if self.origin_url is not None:
            json["originURL"] = self.origin_url.to_json()
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> NodeTreeSnapshot:
        return cls(
            parent_index=(
                [int(i) for i in json["parentIndex"]]
                if json.get("parentIndex", None) is not None
                else None
            ),
            node_type=(
                [int(i) for i in json["nodeType"]]
                if json.get("nodeType", None) is not None
                else None
            ),
            shadow_root_type=(
                RareStringData.from_json(json["shadowRootType"])
                if json.get("shadowRootType", None) is not None
                else None
            ),
            node_name=(
                [StringIndex.from_json(i) for i in json["nodeName"]]
                if json.get("nodeName", None) is not None
                else None
            ),
            node_value=(
                [StringIndex.from_json(i) for i in json["nodeValue"]]
                if json.get("nodeValue", None) is not None
                else None
            ),
            backend_node_id=(
                [dom.BackendNodeId.from_json(i) for i in json["backendNodeId"]]
                if json.get("backendNodeId", None) is not None
                else None
            ),
            attributes=(
                [ArrayOfStrings.from_json(i) for i in json["attributes"]]
                if json.get("attributes", None) is not None
                else None
            ),
            text_value=(
                RareStringData.from_json(json["textValue"])
                if json.get("textValue", None) is not None
                else None
            ),
            input_value=(
                RareStringData.from_json(json["inputValue"])
                if json.get("inputValue", None) is not None
                else None
            ),
            input_checked=(
                RareBooleanData.from_json(json["inputChecked"])
                if json.get("inputChecked", None) is not None
                else None
            ),
            option_selected=(
                RareBooleanData.from_json(json["optionSelected"])
                if json.get("optionSelected", None) is not None
                else None
            ),
            content_document_index=(
                RareIntegerData.from_json(json["contentDocumentIndex"])
                if json.get("contentDocumentIndex", None) is not None
                else None
            ),
            pseudo_type=(
                RareStringData.from_json(json["pseudoType"])
                if json.get("pseudoType", None) is not None
                else None
            ),
            pseudo_identifier=(
                RareStringData.from_json(json["pseudoIdentifier"])
                if json.get("pseudoIdentifier", None) is not None
                else None
            ),
            is_clickable=(
                RareBooleanData.from_json(json["isClickable"])
                if json.get("isClickable", None) is not None
                else None
            ),
            current_source_url=(
                RareStringData.from_json(json["currentSourceURL"])
                if json.get("currentSourceURL", None) is not None
                else None
            ),
            origin_url=(
                RareStringData.from_json(json["originURL"])
                if json.get("originURL", None) is not None
                else None
            ),
        )


@dataclass
class LayoutTreeSnapshot:
    """Table of details of an element in the DOM tree with a LayoutObject."""
    #: Index of the corresponding node in the ``NodeTreeSnapshot``
    #: array returned by ``captureSnapshot``.
    node_index: typing.List[int]
    #: Array of indexes specifying computed style strings,
    #: filtered according to the ``computedStyles`` parameter
    #: passed to ``captureSnapshot``.
    styles: typing.List[ArrayOfStrings]
    #: The absolute position bounding box.
    bounds: typing.List[Rectangle]
    #: Contents of the LayoutText, if any.
    text: typing.List[StringIndex]
    #: Stacking context information.
    stacking_contexts: RareBooleanData
    #: Global paint order index,
    #: which is determined by the stacking order of the nodes.
    #: Nodes that are painted together will have the same index.
    #: Only provided if includePaintOrder in captureSnapshot was true.
    paint_orders: typing.Optional[typing.List[int]] = None
    #: The offset rect of nodes.
    #: Only available when includeDOMRects is set to true
    offset_rects: typing.Optional[typing.List[Rectangle]] = None
    #: The scroll rect of nodes.
    #: Only available when includeDOMRects is set to true
    scroll_rects: typing.Optional[typing.List[Rectangle]] = None
    #: The client rect of nodes.
    #: Only available when includeDOMRects is set to true
    client_rects: typing.Optional[typing.List[Rectangle]] = None
    #: The list of background colors that are blended
    #: with colors of overlapping elements.
    blended_background_colors: typing.Optional[typing.List[StringIndex]] = None
    #: The list of computed text opacities.
    text_color_opacities: typing.Optional[typing.List[float]] = None

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["nodeIndex"] = [i for i in self.node_index]
        json["styles"] = [i.to_json() for i in self.styles]
        json["bounds"] = [i.to_json() for i in self.bounds]
        json["text"] = [i.to_json() for i in self.text]
        json["stackingContexts"] = self.stacking_contexts.to_json()
        if self.paint_orders is not None:
            json["paintOrders"] = [i for i in self.paint_orders]
        if self.offset_rects is not None:
            json["offsetRects"] = [i.to_json() for i in self.offset_rects]
        if self.scroll_rects is not None:
            json["scrollRects"] = [i.to_json() for i in self.scroll_rects]
        if self.client_rects is not None:
            json["clientRects"] = [i.to_json() for i in self.client_rects]
        if self.blended_background_colors is not None:
            json["blendedBackgroundColors"] = [
                i.to_json() for i in self.blended_background_colors
            ]
        if self.text_color_opacities is not None:
            json["textColorOpacities"] = [i for i in self.text_color_opacities]
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> LayoutTreeSnapshot:
        return cls(
            node_index=[int(i) for i in json["nodeIndex"]],
            styles=[ArrayOfStrings.from_json(i) for i in json["styles"]],
            bounds=[Rectangle.from_json(i) for i in json["bounds"]],
            text=[StringIndex.from_json(i) for i in json["text"]],
            stacking_contexts=RareBooleanData.from_json(
                json["stackingContexts"]
            ),
            paint_orders=(
                [int(i) for i in json["paintOrders"]]
                if json.get("paintOrders", None) is not None
                else None
            ),
            offset_rects=(
                [Rectangle.from_json(i) for i in json["offsetRects"]]
                if json.get("offsetRects", None) is not None
                else None
            ),
            scroll_rects=(
                [Rectangle.from_json(i) for i in json["scrollRects"]]
                if json.get("scrollRects", None) is not None
                else None
            ),
            client_rects=(
                [Rectangle.from_json(i) for i in json["clientRects"]]
                if json.get("clientRects", None) is not None
                else None
            ),
            blended_background_colors=(
                [
                    StringIndex.from_json(i)
                    for i in json["blendedBackgroundColors"]
                ]
                if json.get("blendedBackgroundColors", None) is not None
                else None
            ),
            text_color_opacities=(
                [float(i) for i in json["textColorOpacities"]]
                if json.get("textColorOpacities", None) is not None
                else None
            ),
        )


@dataclass
class TextBoxSnapshot:
    """
    Table of details of the post layout rendered text positions.
    The exact layout should not be regarded as
    stable and may change between versions.
    """
    #: Index of the layout tree node that owns this box collection.
    layout_index: typing.List[int]
    #: The absolute position bounding box.
    bounds: typing.List[Rectangle]
    #: The starting index in characters for this post layout textbox substring.
    #: Characters that would be represented as a surrogate pair in UTF-16
    #: have length 2.
    start: typing.List[int]
    #: The number of characters in this post layout textbox substring.
    #: Characters that would be represented as a surrogate pair in UTF-16
    #: have length 2.
    length: typing.List[int]

    def to_json(self) -> T_JSON_DICT:
        json: T_JSON_DICT = dict()
        json["layoutIndex"] = [i for i in self.layout_index]
        json["bounds"] = [i.to_json() for i in self.bounds]
        json["start"] = [i for i in self.start]
        json["length"] = [i for i in self.length]
        return json

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> TextBoxSnapshot:
        return cls(
            layout_index=[int(i) for i in json["layoutIndex"]],
            bounds=[Rectangle.from_json(i) for i in json["bounds"]],
            start=[int(i) for i in json["start"]],
            length=[int(i) for i in json["length"]],
        )


def disable() -> typing.Generator[T_JSON_DICT, T_JSON_DICT, None]:
    """Disables DOM snapshot agent for the given page."""
    cmd_dict: T_JSON_DICT = {
        "method": "DOMSnapshot.disable",
    }
    json = yield cmd_dict  # NOQA


def enable() -> typing.Generator[T_JSON_DICT, T_JSON_DICT, None]:
    """Enables DOM snapshot agent for the given page."""
    cmd_dict: T_JSON_DICT = {
        "method": "DOMSnapshot.enable",
    }
    json = yield cmd_dict  # NOQA


def get_snapshot(
    computed_style_whitelist: typing.List[str],
    include_event_listeners: typing.Optional[bool] = None,
    include_paint_order: typing.Optional[bool] = None,
    include_user_agent_shadow_tree: typing.Optional[bool] = None,
) -> typing.Generator[
    T_JSON_DICT,
    T_JSON_DICT,
    typing.Tuple[
        typing.List[DOMNode],
        typing.List[LayoutTreeNode],
        typing.List[ComputedStyle],
    ],
]:
    """
    Returns a document snapshot, including the full DOM tree of the root node
    (including iframes, template contents, and imported documents)
    in a flattened array, as well as layout and white-listed computed style
    information for the nodes. ShadowDOM in the returned DOM tree is flattened.
    .. deprecated:: 1.3
    :param computed_style_whitelist: Whitelist of computed styles to return.
    :param include_event_listeners: *(Optional)*
      Whether or not to retrieve details of DOM listeners (default false).
    :param include_paint_order: *(Optional)*
      Whether to determine and include the paint order index of LayoutTreeNodes
      (default false).
    :param include_user_agent_shadow_tree: *(Optional)*
      Whether to include UA shadow tree in the snapshot (default false).
    :returns: A tuple with the following items:
        0. **domNodes** - The nodes in the DOM tree.
        The DOMNode at index 0 corresponds to the root document.
        1. **layoutTreeNodes** - The nodes in the layout tree.
        2. **computedStyles** -
        Whitelisted ComputedStyle properties for each node in the layout tree.
    """
    params: T_JSON_DICT = dict()
    params["computedStyleWhitelist"] = [i for i in computed_style_whitelist]
    if include_event_listeners is not None:
        params["includeEventListeners"] = include_event_listeners
    if include_paint_order is not None:
        params["includePaintOrder"] = include_paint_order
    if include_user_agent_shadow_tree is not None:
        params["includeUserAgentShadowTree"] = include_user_agent_shadow_tree
    cmd_dict: T_JSON_DICT = {
        "method": "DOMSnapshot.getSnapshot",
        "params": params,
    }
    json = yield cmd_dict
    return (
        [DOMNode.from_json(i) for i in json["domNodes"]],
        [LayoutTreeNode.from_json(i) for i in json["layoutTreeNodes"]],
        [ComputedStyle.from_json(i) for i in json["computedStyles"]],
    )


def capture_snapshot(
    computed_styles: typing.List[str],
    include_paint_order: typing.Optional[bool] = None,
    include_dom_rects: typing.Optional[bool] = None,
    include_blended_background_colors: typing.Optional[bool] = None,
    include_text_color_opacities: typing.Optional[bool] = None,
) -> typing.Generator[
    T_JSON_DICT,
    T_JSON_DICT,
    typing.Tuple[typing.List[DocumentSnapshot], typing.List[str]],
]:
    """
    Returns a document snapshot, including the full DOM tree of the root node
    (including iframes, template contents, and imported documents)
    in a flattened array, as well as layout and white-listed computed style
    information for the nodes. Shadow DOM in the returned DOM tree is
    flattened.
    :param computed_styles: Whitelist of computed styles to return.
    :param include_paint_order: *(Optional)*
     Whether to include layout object paint orders into the snapshot.
    :param include_dom_rects: *(Optional)* Whether to include DOM rectangles
     (offsetRects, clientRects, scrollRects) into the snapshot
    :param include_blended_background_colors: **(EXPERIMENTAL)** *(Optional)*
     Whether to include blended background colors in the snapshot
     (default: false). Blended background color is achieved by blending
     background colors of all elements that overlap with the current element.
    :param include_text_color_opacities: **(EXPERIMENTAL)** *(Optional)*
     Whether to include text color opacity in the snapshot (default: false).
     An element might have the opacity property set that affects the text
     color of the element. The final text color opacity is computed based on
     the opacity of all overlapping elements.
    :returns: A tuple with the following items:
        0. **documents** - The nodes in the DOM tree.
        The DOMNode at index 0 corresponds to the root document.
        1. **strings** -
        Shared string table that all string properties refer to with indexes.
    """
    params: T_JSON_DICT = dict()
    params["computedStyles"] = [i for i in computed_styles]
    if include_paint_order is not None:
        params["includePaintOrder"] = include_paint_order
    if include_dom_rects is not None:
        params["includeDOMRects"] = include_dom_rects
    if include_blended_background_colors is not None:
        params["includeBlendedBackgroundColors"] = (
            include_blended_background_colors
        )
    if include_text_color_opacities is not None:
        params["includeTextColorOpacities"] = include_text_color_opacities
    cmd_dict: T_JSON_DICT = {
        "method": "DOMSnapshot.captureSnapshot",
        "params": params,
    }
    json = yield cmd_dict
    return (
        [DocumentSnapshot.from_json(i) for i in json["documents"]],
        [str(i) for i in json["strings"]],
    )
