# 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: Audits (experimental) from __future__ import annotations import enum import typing from dataclasses import dataclass from .util import event_class, T_JSON_DICT from . import dom from . import network from . import page from . import runtime @dataclass class AffectedCookie: ''' Information about a cookie that is affected by an inspector issue. ''' #: The following three properties uniquely identify a cookie name: str path: str domain: str def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['name'] = self.name json['path'] = self.path json['domain'] = self.domain return json @classmethod def from_json(cls, json: T_JSON_DICT) -> AffectedCookie: return cls( name=str(json['name']), path=str(json['path']), domain=str(json['domain']), ) @dataclass class AffectedRequest: ''' Information about a request that is affected by an inspector issue. ''' url: str #: The unique request id. request_id: typing.Optional[network.RequestId] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['url'] = self.url if self.request_id is not None: json['requestId'] = self.request_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> AffectedRequest: return cls( url=str(json['url']), request_id=network.RequestId.from_json(json['requestId']) if json.get('requestId', None) is not None else None, ) @dataclass class AffectedFrame: ''' Information about the frame affected by an inspector issue. ''' frame_id: page.FrameId def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['frameId'] = self.frame_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> AffectedFrame: return cls( frame_id=page.FrameId.from_json(json['frameId']), ) class CookieExclusionReason(enum.Enum): EXCLUDE_SAME_SITE_UNSPECIFIED_TREATED_AS_LAX = "ExcludeSameSiteUnspecifiedTreatedAsLax" EXCLUDE_SAME_SITE_NONE_INSECURE = "ExcludeSameSiteNoneInsecure" EXCLUDE_SAME_SITE_LAX = "ExcludeSameSiteLax" EXCLUDE_SAME_SITE_STRICT = "ExcludeSameSiteStrict" EXCLUDE_INVALID_SAME_PARTY = "ExcludeInvalidSameParty" EXCLUDE_SAME_PARTY_CROSS_PARTY_CONTEXT = "ExcludeSamePartyCrossPartyContext" EXCLUDE_DOMAIN_NON_ASCII = "ExcludeDomainNonASCII" EXCLUDE_THIRD_PARTY_COOKIE_BLOCKED_IN_FIRST_PARTY_SET = "ExcludeThirdPartyCookieBlockedInFirstPartySet" EXCLUDE_THIRD_PARTY_PHASEOUT = "ExcludeThirdPartyPhaseout" EXCLUDE_PORT_MISMATCH = "ExcludePortMismatch" EXCLUDE_SCHEME_MISMATCH = "ExcludeSchemeMismatch" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> CookieExclusionReason: return cls(json) class CookieWarningReason(enum.Enum): WARN_SAME_SITE_UNSPECIFIED_CROSS_SITE_CONTEXT = "WarnSameSiteUnspecifiedCrossSiteContext" WARN_SAME_SITE_NONE_INSECURE = "WarnSameSiteNoneInsecure" WARN_SAME_SITE_UNSPECIFIED_LAX_ALLOW_UNSAFE = "WarnSameSiteUnspecifiedLaxAllowUnsafe" WARN_SAME_SITE_STRICT_LAX_DOWNGRADE_STRICT = "WarnSameSiteStrictLaxDowngradeStrict" WARN_SAME_SITE_STRICT_CROSS_DOWNGRADE_STRICT = "WarnSameSiteStrictCrossDowngradeStrict" WARN_SAME_SITE_STRICT_CROSS_DOWNGRADE_LAX = "WarnSameSiteStrictCrossDowngradeLax" WARN_SAME_SITE_LAX_CROSS_DOWNGRADE_STRICT = "WarnSameSiteLaxCrossDowngradeStrict" WARN_SAME_SITE_LAX_CROSS_DOWNGRADE_LAX = "WarnSameSiteLaxCrossDowngradeLax" WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE = "WarnAttributeValueExceedsMaxSize" WARN_DOMAIN_NON_ASCII = "WarnDomainNonASCII" WARN_THIRD_PARTY_PHASEOUT = "WarnThirdPartyPhaseout" WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION = "WarnCrossSiteRedirectDowngradeChangesInclusion" WARN_DEPRECATION_TRIAL_METADATA = "WarnDeprecationTrialMetadata" WARN_THIRD_PARTY_COOKIE_HEURISTIC = "WarnThirdPartyCookieHeuristic" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> CookieWarningReason: return cls(json) class CookieOperation(enum.Enum): SET_COOKIE = "SetCookie" READ_COOKIE = "ReadCookie" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> CookieOperation: return cls(json) class InsightType(enum.Enum): ''' Represents the category of insight that a cookie issue falls under. ''' GIT_HUB_RESOURCE = "GitHubResource" GRACE_PERIOD = "GracePeriod" HEURISTICS = "Heuristics" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> InsightType: return cls(json) @dataclass class CookieIssueInsight: ''' Information about the suggested solution to a cookie issue. ''' type_: InsightType #: Link to table entry in third-party cookie migration readiness list. table_entry_url: typing.Optional[str] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['type'] = self.type_.to_json() if self.table_entry_url is not None: json['tableEntryUrl'] = self.table_entry_url return json @classmethod def from_json(cls, json: T_JSON_DICT) -> CookieIssueInsight: return cls( type_=InsightType.from_json(json['type']), table_entry_url=str(json['tableEntryUrl']) if json.get('tableEntryUrl', None) is not None else None, ) @dataclass class CookieIssueDetails: ''' This information is currently necessary, as the front-end has a difficult time finding a specific cookie. With this, we can convey specific error information without the cookie. ''' cookie_warning_reasons: typing.List[CookieWarningReason] cookie_exclusion_reasons: typing.List[CookieExclusionReason] #: Optionally identifies the site-for-cookies and the cookie url, which #: may be used by the front-end as additional context. operation: CookieOperation #: If AffectedCookie is not set then rawCookieLine contains the raw #: Set-Cookie header string. This hints at a problem where the #: cookie line is syntactically or semantically malformed in a way #: that no valid cookie could be created. cookie: typing.Optional[AffectedCookie] = None raw_cookie_line: typing.Optional[str] = None site_for_cookies: typing.Optional[str] = None cookie_url: typing.Optional[str] = None request: typing.Optional[AffectedRequest] = None #: The recommended solution to the issue. insight: typing.Optional[CookieIssueInsight] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['cookieWarningReasons'] = [i.to_json() for i in self.cookie_warning_reasons] json['cookieExclusionReasons'] = [i.to_json() for i in self.cookie_exclusion_reasons] json['operation'] = self.operation.to_json() if self.cookie is not None: json['cookie'] = self.cookie.to_json() if self.raw_cookie_line is not None: json['rawCookieLine'] = self.raw_cookie_line if self.site_for_cookies is not None: json['siteForCookies'] = self.site_for_cookies if self.cookie_url is not None: json['cookieUrl'] = self.cookie_url if self.request is not None: json['request'] = self.request.to_json() if self.insight is not None: json['insight'] = self.insight.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> CookieIssueDetails: return cls( cookie_warning_reasons=[CookieWarningReason.from_json(i) for i in json['cookieWarningReasons']], cookie_exclusion_reasons=[CookieExclusionReason.from_json(i) for i in json['cookieExclusionReasons']], operation=CookieOperation.from_json(json['operation']), cookie=AffectedCookie.from_json(json['cookie']) if json.get('cookie', None) is not None else None, raw_cookie_line=str(json['rawCookieLine']) if json.get('rawCookieLine', None) is not None else None, site_for_cookies=str(json['siteForCookies']) if json.get('siteForCookies', None) is not None else None, cookie_url=str(json['cookieUrl']) if json.get('cookieUrl', None) is not None else None, request=AffectedRequest.from_json(json['request']) if json.get('request', None) is not None else None, insight=CookieIssueInsight.from_json(json['insight']) if json.get('insight', None) is not None else None, ) class MixedContentResolutionStatus(enum.Enum): MIXED_CONTENT_BLOCKED = "MixedContentBlocked" MIXED_CONTENT_AUTOMATICALLY_UPGRADED = "MixedContentAutomaticallyUpgraded" MIXED_CONTENT_WARNING = "MixedContentWarning" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> MixedContentResolutionStatus: return cls(json) class MixedContentResourceType(enum.Enum): ATTRIBUTION_SRC = "AttributionSrc" AUDIO = "Audio" BEACON = "Beacon" CSP_REPORT = "CSPReport" DOWNLOAD = "Download" EVENT_SOURCE = "EventSource" FAVICON = "Favicon" FONT = "Font" FORM = "Form" FRAME = "Frame" IMAGE = "Image" IMPORT = "Import" JSON = "JSON" MANIFEST = "Manifest" PING = "Ping" PLUGIN_DATA = "PluginData" PLUGIN_RESOURCE = "PluginResource" PREFETCH = "Prefetch" RESOURCE = "Resource" SCRIPT = "Script" SERVICE_WORKER = "ServiceWorker" SHARED_WORKER = "SharedWorker" SPECULATION_RULES = "SpeculationRules" STYLESHEET = "Stylesheet" TRACK = "Track" VIDEO = "Video" WORKER = "Worker" XML_HTTP_REQUEST = "XMLHttpRequest" XSLT = "XSLT" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> MixedContentResourceType: return cls(json) @dataclass class MixedContentIssueDetails: #: The way the mixed content issue is being resolved. resolution_status: MixedContentResolutionStatus #: The unsafe http url causing the mixed content issue. insecure_url: str #: The url responsible for the call to an unsafe url. main_resource_url: str #: The type of resource causing the mixed content issue (css, js, iframe, #: form,...). Marked as optional because it is mapped to from #: blink::mojom::RequestContextType, which will be replaced #: by network::mojom::RequestDestination resource_type: typing.Optional[MixedContentResourceType] = None #: The mixed content request. #: Does not always exist (e.g. for unsafe form submission urls). request: typing.Optional[AffectedRequest] = None #: Optional because not every mixed content issue is necessarily linked to a frame. frame: typing.Optional[AffectedFrame] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['resolutionStatus'] = self.resolution_status.to_json() json['insecureURL'] = self.insecure_url json['mainResourceURL'] = self.main_resource_url if self.resource_type is not None: json['resourceType'] = self.resource_type.to_json() if self.request is not None: json['request'] = self.request.to_json() if self.frame is not None: json['frame'] = self.frame.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> MixedContentIssueDetails: return cls( resolution_status=MixedContentResolutionStatus.from_json(json['resolutionStatus']), insecure_url=str(json['insecureURL']), main_resource_url=str(json['mainResourceURL']), resource_type=MixedContentResourceType.from_json(json['resourceType']) if json.get('resourceType', None) is not None else None, request=AffectedRequest.from_json(json['request']) if json.get('request', None) is not None else None, frame=AffectedFrame.from_json(json['frame']) if json.get('frame', None) is not None else None, ) class BlockedByResponseReason(enum.Enum): ''' Enum indicating the reason a response has been blocked. These reasons are refinements of the net error BLOCKED_BY_RESPONSE. ''' COEP_FRAME_RESOURCE_NEEDS_COEP_HEADER = "CoepFrameResourceNeedsCoepHeader" COOP_SANDBOXED_I_FRAME_CANNOT_NAVIGATE_TO_COOP_PAGE = "CoopSandboxedIFrameCannotNavigateToCoopPage" CORP_NOT_SAME_ORIGIN = "CorpNotSameOrigin" CORP_NOT_SAME_ORIGIN_AFTER_DEFAULTED_TO_SAME_ORIGIN_BY_COEP = "CorpNotSameOriginAfterDefaultedToSameOriginByCoep" CORP_NOT_SAME_ORIGIN_AFTER_DEFAULTED_TO_SAME_ORIGIN_BY_DIP = "CorpNotSameOriginAfterDefaultedToSameOriginByDip" CORP_NOT_SAME_ORIGIN_AFTER_DEFAULTED_TO_SAME_ORIGIN_BY_COEP_AND_DIP = "CorpNotSameOriginAfterDefaultedToSameOriginByCoepAndDip" CORP_NOT_SAME_SITE = "CorpNotSameSite" SRI_MESSAGE_SIGNATURE_MISMATCH = "SRIMessageSignatureMismatch" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> BlockedByResponseReason: return cls(json) @dataclass class BlockedByResponseIssueDetails: ''' Details for a request that has been blocked with the BLOCKED_BY_RESPONSE code. Currently only used for COEP/COOP, but may be extended to include some CSP errors in the future. ''' request: AffectedRequest reason: BlockedByResponseReason parent_frame: typing.Optional[AffectedFrame] = None blocked_frame: typing.Optional[AffectedFrame] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['request'] = self.request.to_json() json['reason'] = self.reason.to_json() if self.parent_frame is not None: json['parentFrame'] = self.parent_frame.to_json() if self.blocked_frame is not None: json['blockedFrame'] = self.blocked_frame.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> BlockedByResponseIssueDetails: return cls( request=AffectedRequest.from_json(json['request']), reason=BlockedByResponseReason.from_json(json['reason']), parent_frame=AffectedFrame.from_json(json['parentFrame']) if json.get('parentFrame', None) is not None else None, blocked_frame=AffectedFrame.from_json(json['blockedFrame']) if json.get('blockedFrame', None) is not None else None, ) class HeavyAdResolutionStatus(enum.Enum): HEAVY_AD_BLOCKED = "HeavyAdBlocked" HEAVY_AD_WARNING = "HeavyAdWarning" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> HeavyAdResolutionStatus: return cls(json) class HeavyAdReason(enum.Enum): NETWORK_TOTAL_LIMIT = "NetworkTotalLimit" CPU_TOTAL_LIMIT = "CpuTotalLimit" CPU_PEAK_LIMIT = "CpuPeakLimit" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> HeavyAdReason: return cls(json) @dataclass class HeavyAdIssueDetails: #: The resolution status, either blocking the content or warning. resolution: HeavyAdResolutionStatus #: The reason the ad was blocked, total network or cpu or peak cpu. reason: HeavyAdReason #: The frame that was blocked. frame: AffectedFrame def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['resolution'] = self.resolution.to_json() json['reason'] = self.reason.to_json() json['frame'] = self.frame.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> HeavyAdIssueDetails: return cls( resolution=HeavyAdResolutionStatus.from_json(json['resolution']), reason=HeavyAdReason.from_json(json['reason']), frame=AffectedFrame.from_json(json['frame']), ) class ContentSecurityPolicyViolationType(enum.Enum): K_INLINE_VIOLATION = "kInlineViolation" K_EVAL_VIOLATION = "kEvalViolation" K_URL_VIOLATION = "kURLViolation" K_SRI_VIOLATION = "kSRIViolation" K_TRUSTED_TYPES_SINK_VIOLATION = "kTrustedTypesSinkViolation" K_TRUSTED_TYPES_POLICY_VIOLATION = "kTrustedTypesPolicyViolation" K_WASM_EVAL_VIOLATION = "kWasmEvalViolation" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> ContentSecurityPolicyViolationType: return cls(json) @dataclass class SourceCodeLocation: url: str line_number: int column_number: int script_id: typing.Optional[runtime.ScriptId] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['url'] = self.url json['lineNumber'] = self.line_number json['columnNumber'] = self.column_number if self.script_id is not None: json['scriptId'] = self.script_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> SourceCodeLocation: return cls( url=str(json['url']), line_number=int(json['lineNumber']), column_number=int(json['columnNumber']), script_id=runtime.ScriptId.from_json(json['scriptId']) if json.get('scriptId', None) is not None else None, ) @dataclass class ContentSecurityPolicyIssueDetails: #: Specific directive that is violated, causing the CSP issue. violated_directive: str is_report_only: bool content_security_policy_violation_type: ContentSecurityPolicyViolationType #: The url not included in allowed sources. blocked_url: typing.Optional[str] = None frame_ancestor: typing.Optional[AffectedFrame] = None source_code_location: typing.Optional[SourceCodeLocation] = None violating_node_id: typing.Optional[dom.BackendNodeId] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['violatedDirective'] = self.violated_directive json['isReportOnly'] = self.is_report_only json['contentSecurityPolicyViolationType'] = self.content_security_policy_violation_type.to_json() if self.blocked_url is not None: json['blockedURL'] = self.blocked_url if self.frame_ancestor is not None: json['frameAncestor'] = self.frame_ancestor.to_json() if self.source_code_location is not None: json['sourceCodeLocation'] = self.source_code_location.to_json() if self.violating_node_id is not None: json['violatingNodeId'] = self.violating_node_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> ContentSecurityPolicyIssueDetails: return cls( violated_directive=str(json['violatedDirective']), is_report_only=bool(json['isReportOnly']), content_security_policy_violation_type=ContentSecurityPolicyViolationType.from_json(json['contentSecurityPolicyViolationType']), blocked_url=str(json['blockedURL']) if json.get('blockedURL', None) is not None else None, frame_ancestor=AffectedFrame.from_json(json['frameAncestor']) if json.get('frameAncestor', None) is not None else None, source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']) if json.get('sourceCodeLocation', None) is not None else None, violating_node_id=dom.BackendNodeId.from_json(json['violatingNodeId']) if json.get('violatingNodeId', None) is not None else None, ) class SharedArrayBufferIssueType(enum.Enum): TRANSFER_ISSUE = "TransferIssue" CREATION_ISSUE = "CreationIssue" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> SharedArrayBufferIssueType: return cls(json) @dataclass class SharedArrayBufferIssueDetails: ''' Details for a issue arising from an SAB being instantiated in, or transferred to a context that is not cross-origin isolated. ''' source_code_location: SourceCodeLocation is_warning: bool type_: SharedArrayBufferIssueType def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['sourceCodeLocation'] = self.source_code_location.to_json() json['isWarning'] = self.is_warning json['type'] = self.type_.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> SharedArrayBufferIssueDetails: return cls( source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']), is_warning=bool(json['isWarning']), type_=SharedArrayBufferIssueType.from_json(json['type']), ) @dataclass class LowTextContrastIssueDetails: violating_node_id: dom.BackendNodeId violating_node_selector: str contrast_ratio: float threshold_aa: float threshold_aaa: float font_size: str font_weight: str def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['violatingNodeId'] = self.violating_node_id.to_json() json['violatingNodeSelector'] = self.violating_node_selector json['contrastRatio'] = self.contrast_ratio json['thresholdAA'] = self.threshold_aa json['thresholdAAA'] = self.threshold_aaa json['fontSize'] = self.font_size json['fontWeight'] = self.font_weight return json @classmethod def from_json(cls, json: T_JSON_DICT) -> LowTextContrastIssueDetails: return cls( violating_node_id=dom.BackendNodeId.from_json(json['violatingNodeId']), violating_node_selector=str(json['violatingNodeSelector']), contrast_ratio=float(json['contrastRatio']), threshold_aa=float(json['thresholdAA']), threshold_aaa=float(json['thresholdAAA']), font_size=str(json['fontSize']), font_weight=str(json['fontWeight']), ) @dataclass class CorsIssueDetails: ''' Details for a CORS related issue, e.g. a warning or error related to CORS RFC1918 enforcement. ''' cors_error_status: network.CorsErrorStatus is_warning: bool request: AffectedRequest location: typing.Optional[SourceCodeLocation] = None initiator_origin: typing.Optional[str] = None resource_ip_address_space: typing.Optional[network.IPAddressSpace] = None client_security_state: typing.Optional[network.ClientSecurityState] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['corsErrorStatus'] = self.cors_error_status.to_json() json['isWarning'] = self.is_warning json['request'] = self.request.to_json() if self.location is not None: json['location'] = self.location.to_json() if self.initiator_origin is not None: json['initiatorOrigin'] = self.initiator_origin if self.resource_ip_address_space is not None: json['resourceIPAddressSpace'] = self.resource_ip_address_space.to_json() if self.client_security_state is not None: json['clientSecurityState'] = self.client_security_state.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> CorsIssueDetails: return cls( cors_error_status=network.CorsErrorStatus.from_json(json['corsErrorStatus']), is_warning=bool(json['isWarning']), request=AffectedRequest.from_json(json['request']), location=SourceCodeLocation.from_json(json['location']) if json.get('location', None) is not None else None, initiator_origin=str(json['initiatorOrigin']) if json.get('initiatorOrigin', None) is not None else None, resource_ip_address_space=network.IPAddressSpace.from_json(json['resourceIPAddressSpace']) if json.get('resourceIPAddressSpace', None) is not None else None, client_security_state=network.ClientSecurityState.from_json(json['clientSecurityState']) if json.get('clientSecurityState', None) is not None else None, ) class AttributionReportingIssueType(enum.Enum): PERMISSION_POLICY_DISABLED = "PermissionPolicyDisabled" UNTRUSTWORTHY_REPORTING_ORIGIN = "UntrustworthyReportingOrigin" INSECURE_CONTEXT = "InsecureContext" INVALID_HEADER = "InvalidHeader" INVALID_REGISTER_TRIGGER_HEADER = "InvalidRegisterTriggerHeader" SOURCE_AND_TRIGGER_HEADERS = "SourceAndTriggerHeaders" SOURCE_IGNORED = "SourceIgnored" TRIGGER_IGNORED = "TriggerIgnored" OS_SOURCE_IGNORED = "OsSourceIgnored" OS_TRIGGER_IGNORED = "OsTriggerIgnored" INVALID_REGISTER_OS_SOURCE_HEADER = "InvalidRegisterOsSourceHeader" INVALID_REGISTER_OS_TRIGGER_HEADER = "InvalidRegisterOsTriggerHeader" WEB_AND_OS_HEADERS = "WebAndOsHeaders" NO_WEB_OR_OS_SUPPORT = "NoWebOrOsSupport" NAVIGATION_REGISTRATION_WITHOUT_TRANSIENT_USER_ACTIVATION = "NavigationRegistrationWithoutTransientUserActivation" INVALID_INFO_HEADER = "InvalidInfoHeader" NO_REGISTER_SOURCE_HEADER = "NoRegisterSourceHeader" NO_REGISTER_TRIGGER_HEADER = "NoRegisterTriggerHeader" NO_REGISTER_OS_SOURCE_HEADER = "NoRegisterOsSourceHeader" NO_REGISTER_OS_TRIGGER_HEADER = "NoRegisterOsTriggerHeader" NAVIGATION_REGISTRATION_UNIQUE_SCOPE_ALREADY_SET = "NavigationRegistrationUniqueScopeAlreadySet" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> AttributionReportingIssueType: return cls(json) class SharedDictionaryError(enum.Enum): USE_ERROR_CROSS_ORIGIN_NO_CORS_REQUEST = "UseErrorCrossOriginNoCorsRequest" USE_ERROR_DICTIONARY_LOAD_FAILURE = "UseErrorDictionaryLoadFailure" USE_ERROR_MATCHING_DICTIONARY_NOT_USED = "UseErrorMatchingDictionaryNotUsed" USE_ERROR_UNEXPECTED_CONTENT_DICTIONARY_HEADER = "UseErrorUnexpectedContentDictionaryHeader" WRITE_ERROR_COSS_ORIGIN_NO_CORS_REQUEST = "WriteErrorCossOriginNoCorsRequest" WRITE_ERROR_DISALLOWED_BY_SETTINGS = "WriteErrorDisallowedBySettings" WRITE_ERROR_EXPIRED_RESPONSE = "WriteErrorExpiredResponse" WRITE_ERROR_FEATURE_DISABLED = "WriteErrorFeatureDisabled" WRITE_ERROR_INSUFFICIENT_RESOURCES = "WriteErrorInsufficientResources" WRITE_ERROR_INVALID_MATCH_FIELD = "WriteErrorInvalidMatchField" WRITE_ERROR_INVALID_STRUCTURED_HEADER = "WriteErrorInvalidStructuredHeader" WRITE_ERROR_INVALID_TTL_FIELD = "WriteErrorInvalidTTLField" WRITE_ERROR_NAVIGATION_REQUEST = "WriteErrorNavigationRequest" WRITE_ERROR_NO_MATCH_FIELD = "WriteErrorNoMatchField" WRITE_ERROR_NON_INTEGER_TTL_FIELD = "WriteErrorNonIntegerTTLField" WRITE_ERROR_NON_LIST_MATCH_DEST_FIELD = "WriteErrorNonListMatchDestField" WRITE_ERROR_NON_SECURE_CONTEXT = "WriteErrorNonSecureContext" WRITE_ERROR_NON_STRING_ID_FIELD = "WriteErrorNonStringIdField" WRITE_ERROR_NON_STRING_IN_MATCH_DEST_LIST = "WriteErrorNonStringInMatchDestList" WRITE_ERROR_NON_STRING_MATCH_FIELD = "WriteErrorNonStringMatchField" WRITE_ERROR_NON_TOKEN_TYPE_FIELD = "WriteErrorNonTokenTypeField" WRITE_ERROR_REQUEST_ABORTED = "WriteErrorRequestAborted" WRITE_ERROR_SHUTTING_DOWN = "WriteErrorShuttingDown" WRITE_ERROR_TOO_LONG_ID_FIELD = "WriteErrorTooLongIdField" WRITE_ERROR_UNSUPPORTED_TYPE = "WriteErrorUnsupportedType" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> SharedDictionaryError: return cls(json) class SRIMessageSignatureError(enum.Enum): MISSING_SIGNATURE_HEADER = "MissingSignatureHeader" MISSING_SIGNATURE_INPUT_HEADER = "MissingSignatureInputHeader" INVALID_SIGNATURE_HEADER = "InvalidSignatureHeader" INVALID_SIGNATURE_INPUT_HEADER = "InvalidSignatureInputHeader" SIGNATURE_HEADER_VALUE_IS_NOT_BYTE_SEQUENCE = "SignatureHeaderValueIsNotByteSequence" SIGNATURE_HEADER_VALUE_IS_PARAMETERIZED = "SignatureHeaderValueIsParameterized" SIGNATURE_HEADER_VALUE_IS_INCORRECT_LENGTH = "SignatureHeaderValueIsIncorrectLength" SIGNATURE_INPUT_HEADER_MISSING_LABEL = "SignatureInputHeaderMissingLabel" SIGNATURE_INPUT_HEADER_VALUE_NOT_INNER_LIST = "SignatureInputHeaderValueNotInnerList" SIGNATURE_INPUT_HEADER_VALUE_MISSING_COMPONENTS = "SignatureInputHeaderValueMissingComponents" SIGNATURE_INPUT_HEADER_INVALID_COMPONENT_TYPE = "SignatureInputHeaderInvalidComponentType" SIGNATURE_INPUT_HEADER_INVALID_COMPONENT_NAME = "SignatureInputHeaderInvalidComponentName" SIGNATURE_INPUT_HEADER_INVALID_HEADER_COMPONENT_PARAMETER = "SignatureInputHeaderInvalidHeaderComponentParameter" SIGNATURE_INPUT_HEADER_INVALID_DERIVED_COMPONENT_PARAMETER = "SignatureInputHeaderInvalidDerivedComponentParameter" SIGNATURE_INPUT_HEADER_KEY_ID_LENGTH = "SignatureInputHeaderKeyIdLength" SIGNATURE_INPUT_HEADER_INVALID_PARAMETER = "SignatureInputHeaderInvalidParameter" SIGNATURE_INPUT_HEADER_MISSING_REQUIRED_PARAMETERS = "SignatureInputHeaderMissingRequiredParameters" VALIDATION_FAILED_SIGNATURE_EXPIRED = "ValidationFailedSignatureExpired" VALIDATION_FAILED_INVALID_LENGTH = "ValidationFailedInvalidLength" VALIDATION_FAILED_SIGNATURE_MISMATCH = "ValidationFailedSignatureMismatch" VALIDATION_FAILED_INTEGRITY_MISMATCH = "ValidationFailedIntegrityMismatch" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> SRIMessageSignatureError: return cls(json) class UnencodedDigestError(enum.Enum): MALFORMED_DICTIONARY = "MalformedDictionary" UNKNOWN_ALGORITHM = "UnknownAlgorithm" INCORRECT_DIGEST_TYPE = "IncorrectDigestType" INCORRECT_DIGEST_LENGTH = "IncorrectDigestLength" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> UnencodedDigestError: return cls(json) @dataclass class AttributionReportingIssueDetails: ''' Details for issues around "Attribution Reporting API" usage. Explainer: https://github.com/WICG/attribution-reporting-api ''' violation_type: AttributionReportingIssueType request: typing.Optional[AffectedRequest] = None violating_node_id: typing.Optional[dom.BackendNodeId] = None invalid_parameter: typing.Optional[str] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['violationType'] = self.violation_type.to_json() if self.request is not None: json['request'] = self.request.to_json() if self.violating_node_id is not None: json['violatingNodeId'] = self.violating_node_id.to_json() if self.invalid_parameter is not None: json['invalidParameter'] = self.invalid_parameter return json @classmethod def from_json(cls, json: T_JSON_DICT) -> AttributionReportingIssueDetails: return cls( violation_type=AttributionReportingIssueType.from_json(json['violationType']), request=AffectedRequest.from_json(json['request']) if json.get('request', None) is not None else None, violating_node_id=dom.BackendNodeId.from_json(json['violatingNodeId']) if json.get('violatingNodeId', None) is not None else None, invalid_parameter=str(json['invalidParameter']) if json.get('invalidParameter', None) is not None else None, ) @dataclass class QuirksModeIssueDetails: ''' Details for issues about documents in Quirks Mode or Limited Quirks Mode that affects page layouting. ''' #: If false, it means the document's mode is "quirks" #: instead of "limited-quirks". is_limited_quirks_mode: bool document_node_id: dom.BackendNodeId url: str frame_id: page.FrameId loader_id: network.LoaderId def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['isLimitedQuirksMode'] = self.is_limited_quirks_mode json['documentNodeId'] = self.document_node_id.to_json() json['url'] = self.url json['frameId'] = self.frame_id.to_json() json['loaderId'] = self.loader_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> QuirksModeIssueDetails: return cls( is_limited_quirks_mode=bool(json['isLimitedQuirksMode']), document_node_id=dom.BackendNodeId.from_json(json['documentNodeId']), url=str(json['url']), frame_id=page.FrameId.from_json(json['frameId']), loader_id=network.LoaderId.from_json(json['loaderId']), ) @dataclass class NavigatorUserAgentIssueDetails: url: str location: typing.Optional[SourceCodeLocation] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['url'] = self.url if self.location is not None: json['location'] = self.location.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> NavigatorUserAgentIssueDetails: return cls( url=str(json['url']), location=SourceCodeLocation.from_json(json['location']) if json.get('location', None) is not None else None, ) @dataclass class SharedDictionaryIssueDetails: shared_dictionary_error: SharedDictionaryError request: AffectedRequest def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['sharedDictionaryError'] = self.shared_dictionary_error.to_json() json['request'] = self.request.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> SharedDictionaryIssueDetails: return cls( shared_dictionary_error=SharedDictionaryError.from_json(json['sharedDictionaryError']), request=AffectedRequest.from_json(json['request']), ) @dataclass class SRIMessageSignatureIssueDetails: error: SRIMessageSignatureError signature_base: str integrity_assertions: typing.List[str] request: AffectedRequest def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['error'] = self.error.to_json() json['signatureBase'] = self.signature_base json['integrityAssertions'] = [i for i in self.integrity_assertions] json['request'] = self.request.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> SRIMessageSignatureIssueDetails: return cls( error=SRIMessageSignatureError.from_json(json['error']), signature_base=str(json['signatureBase']), integrity_assertions=[str(i) for i in json['integrityAssertions']], request=AffectedRequest.from_json(json['request']), ) @dataclass class UnencodedDigestIssueDetails: error: UnencodedDigestError request: AffectedRequest def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['error'] = self.error.to_json() json['request'] = self.request.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> UnencodedDigestIssueDetails: return cls( error=UnencodedDigestError.from_json(json['error']), request=AffectedRequest.from_json(json['request']), ) class GenericIssueErrorType(enum.Enum): FORM_LABEL_FOR_NAME_ERROR = "FormLabelForNameError" FORM_DUPLICATE_ID_FOR_INPUT_ERROR = "FormDuplicateIdForInputError" FORM_INPUT_WITH_NO_LABEL_ERROR = "FormInputWithNoLabelError" FORM_AUTOCOMPLETE_ATTRIBUTE_EMPTY_ERROR = "FormAutocompleteAttributeEmptyError" FORM_EMPTY_ID_AND_NAME_ATTRIBUTES_FOR_INPUT_ERROR = "FormEmptyIdAndNameAttributesForInputError" FORM_ARIA_LABELLED_BY_TO_NON_EXISTING_ID = "FormAriaLabelledByToNonExistingId" FORM_INPUT_ASSIGNED_AUTOCOMPLETE_VALUE_TO_ID_OR_NAME_ATTRIBUTE_ERROR = "FormInputAssignedAutocompleteValueToIdOrNameAttributeError" FORM_LABEL_HAS_NEITHER_FOR_NOR_NESTED_INPUT = "FormLabelHasNeitherForNorNestedInput" FORM_LABEL_FOR_MATCHES_NON_EXISTING_ID_ERROR = "FormLabelForMatchesNonExistingIdError" FORM_INPUT_HAS_WRONG_BUT_WELL_INTENDED_AUTOCOMPLETE_VALUE_ERROR = "FormInputHasWrongButWellIntendedAutocompleteValueError" RESPONSE_WAS_BLOCKED_BY_ORB = "ResponseWasBlockedByORB" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> GenericIssueErrorType: return cls(json) @dataclass class GenericIssueDetails: ''' Depending on the concrete errorType, different properties are set. ''' #: Issues with the same errorType are aggregated in the frontend. error_type: GenericIssueErrorType frame_id: typing.Optional[page.FrameId] = None violating_node_id: typing.Optional[dom.BackendNodeId] = None violating_node_attribute: typing.Optional[str] = None request: typing.Optional[AffectedRequest] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['errorType'] = self.error_type.to_json() if self.frame_id is not None: json['frameId'] = self.frame_id.to_json() if self.violating_node_id is not None: json['violatingNodeId'] = self.violating_node_id.to_json() if self.violating_node_attribute is not None: json['violatingNodeAttribute'] = self.violating_node_attribute if self.request is not None: json['request'] = self.request.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> GenericIssueDetails: return cls( error_type=GenericIssueErrorType.from_json(json['errorType']), frame_id=page.FrameId.from_json(json['frameId']) if json.get('frameId', None) is not None else None, violating_node_id=dom.BackendNodeId.from_json(json['violatingNodeId']) if json.get('violatingNodeId', None) is not None else None, violating_node_attribute=str(json['violatingNodeAttribute']) if json.get('violatingNodeAttribute', None) is not None else None, request=AffectedRequest.from_json(json['request']) if json.get('request', None) is not None else None, ) @dataclass class DeprecationIssueDetails: ''' This issue tracks information needed to print a deprecation message. https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/frame/third_party/blink/renderer/core/frame/deprecation/README.md ''' source_code_location: SourceCodeLocation #: One of the deprecation names from third_party/blink/renderer/core/frame/deprecation/deprecation.json5 type_: str affected_frame: typing.Optional[AffectedFrame] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['sourceCodeLocation'] = self.source_code_location.to_json() json['type'] = self.type_ if self.affected_frame is not None: json['affectedFrame'] = self.affected_frame.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> DeprecationIssueDetails: return cls( source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']), type_=str(json['type']), affected_frame=AffectedFrame.from_json(json['affectedFrame']) if json.get('affectedFrame', None) is not None else None, ) @dataclass class BounceTrackingIssueDetails: ''' This issue warns about sites in the redirect chain of a finished navigation that may be flagged as trackers and have their state cleared if they don't receive a user interaction. Note that in this context 'site' means eTLD+1. For example, if the URL ``https://example.test:80/bounce`` was in the redirect chain, the site reported would be ``example.test``. ''' tracking_sites: typing.List[str] def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['trackingSites'] = [i for i in self.tracking_sites] return json @classmethod def from_json(cls, json: T_JSON_DICT) -> BounceTrackingIssueDetails: return cls( tracking_sites=[str(i) for i in json['trackingSites']], ) @dataclass class CookieDeprecationMetadataIssueDetails: ''' This issue warns about third-party sites that are accessing cookies on the current page, and have been permitted due to having a global metadata grant. Note that in this context 'site' means eTLD+1. For example, if the URL ``https://example.test:80/web_page`` was accessing cookies, the site reported would be ``example.test``. ''' allowed_sites: typing.List[str] opt_out_percentage: float is_opt_out_top_level: bool operation: CookieOperation def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['allowedSites'] = [i for i in self.allowed_sites] json['optOutPercentage'] = self.opt_out_percentage json['isOptOutTopLevel'] = self.is_opt_out_top_level json['operation'] = self.operation.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> CookieDeprecationMetadataIssueDetails: return cls( allowed_sites=[str(i) for i in json['allowedSites']], opt_out_percentage=float(json['optOutPercentage']), is_opt_out_top_level=bool(json['isOptOutTopLevel']), operation=CookieOperation.from_json(json['operation']), ) class ClientHintIssueReason(enum.Enum): META_TAG_ALLOW_LIST_INVALID_ORIGIN = "MetaTagAllowListInvalidOrigin" META_TAG_MODIFIED_HTML = "MetaTagModifiedHTML" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> ClientHintIssueReason: return cls(json) @dataclass class FederatedAuthRequestIssueDetails: federated_auth_request_issue_reason: FederatedAuthRequestIssueReason def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['federatedAuthRequestIssueReason'] = self.federated_auth_request_issue_reason.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> FederatedAuthRequestIssueDetails: return cls( federated_auth_request_issue_reason=FederatedAuthRequestIssueReason.from_json(json['federatedAuthRequestIssueReason']), ) class FederatedAuthRequestIssueReason(enum.Enum): ''' Represents the failure reason when a federated authentication reason fails. Should be updated alongside RequestIdTokenStatus in third_party/blink/public/mojom/devtools/inspector_issue.mojom to include all cases except for success. ''' SHOULD_EMBARGO = "ShouldEmbargo" TOO_MANY_REQUESTS = "TooManyRequests" WELL_KNOWN_HTTP_NOT_FOUND = "WellKnownHttpNotFound" WELL_KNOWN_NO_RESPONSE = "WellKnownNoResponse" WELL_KNOWN_INVALID_RESPONSE = "WellKnownInvalidResponse" WELL_KNOWN_LIST_EMPTY = "WellKnownListEmpty" WELL_KNOWN_INVALID_CONTENT_TYPE = "WellKnownInvalidContentType" CONFIG_NOT_IN_WELL_KNOWN = "ConfigNotInWellKnown" WELL_KNOWN_TOO_BIG = "WellKnownTooBig" CONFIG_HTTP_NOT_FOUND = "ConfigHttpNotFound" CONFIG_NO_RESPONSE = "ConfigNoResponse" CONFIG_INVALID_RESPONSE = "ConfigInvalidResponse" CONFIG_INVALID_CONTENT_TYPE = "ConfigInvalidContentType" CLIENT_METADATA_HTTP_NOT_FOUND = "ClientMetadataHttpNotFound" CLIENT_METADATA_NO_RESPONSE = "ClientMetadataNoResponse" CLIENT_METADATA_INVALID_RESPONSE = "ClientMetadataInvalidResponse" CLIENT_METADATA_INVALID_CONTENT_TYPE = "ClientMetadataInvalidContentType" IDP_NOT_POTENTIALLY_TRUSTWORTHY = "IdpNotPotentiallyTrustworthy" DISABLED_IN_SETTINGS = "DisabledInSettings" DISABLED_IN_FLAGS = "DisabledInFlags" ERROR_FETCHING_SIGNIN = "ErrorFetchingSignin" INVALID_SIGNIN_RESPONSE = "InvalidSigninResponse" ACCOUNTS_HTTP_NOT_FOUND = "AccountsHttpNotFound" ACCOUNTS_NO_RESPONSE = "AccountsNoResponse" ACCOUNTS_INVALID_RESPONSE = "AccountsInvalidResponse" ACCOUNTS_LIST_EMPTY = "AccountsListEmpty" ACCOUNTS_INVALID_CONTENT_TYPE = "AccountsInvalidContentType" ID_TOKEN_HTTP_NOT_FOUND = "IdTokenHttpNotFound" ID_TOKEN_NO_RESPONSE = "IdTokenNoResponse" ID_TOKEN_INVALID_RESPONSE = "IdTokenInvalidResponse" ID_TOKEN_IDP_ERROR_RESPONSE = "IdTokenIdpErrorResponse" ID_TOKEN_CROSS_SITE_IDP_ERROR_RESPONSE = "IdTokenCrossSiteIdpErrorResponse" ID_TOKEN_INVALID_REQUEST = "IdTokenInvalidRequest" ID_TOKEN_INVALID_CONTENT_TYPE = "IdTokenInvalidContentType" ERROR_ID_TOKEN = "ErrorIdToken" CANCELED = "Canceled" RP_PAGE_NOT_VISIBLE = "RpPageNotVisible" SILENT_MEDIATION_FAILURE = "SilentMediationFailure" THIRD_PARTY_COOKIES_BLOCKED = "ThirdPartyCookiesBlocked" NOT_SIGNED_IN_WITH_IDP = "NotSignedInWithIdp" MISSING_TRANSIENT_USER_ACTIVATION = "MissingTransientUserActivation" REPLACED_BY_ACTIVE_MODE = "ReplacedByActiveMode" INVALID_FIELDS_SPECIFIED = "InvalidFieldsSpecified" RELYING_PARTY_ORIGIN_IS_OPAQUE = "RelyingPartyOriginIsOpaque" TYPE_NOT_MATCHING = "TypeNotMatching" UI_DISMISSED_NO_EMBARGO = "UiDismissedNoEmbargo" CORS_ERROR = "CorsError" SUPPRESSED_BY_SEGMENTATION_PLATFORM = "SuppressedBySegmentationPlatform" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> FederatedAuthRequestIssueReason: return cls(json) @dataclass class FederatedAuthUserInfoRequestIssueDetails: federated_auth_user_info_request_issue_reason: FederatedAuthUserInfoRequestIssueReason def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['federatedAuthUserInfoRequestIssueReason'] = self.federated_auth_user_info_request_issue_reason.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> FederatedAuthUserInfoRequestIssueDetails: return cls( federated_auth_user_info_request_issue_reason=FederatedAuthUserInfoRequestIssueReason.from_json(json['federatedAuthUserInfoRequestIssueReason']), ) class FederatedAuthUserInfoRequestIssueReason(enum.Enum): ''' Represents the failure reason when a getUserInfo() call fails. Should be updated alongside FederatedAuthUserInfoRequestResult in third_party/blink/public/mojom/devtools/inspector_issue.mojom. ''' NOT_SAME_ORIGIN = "NotSameOrigin" NOT_IFRAME = "NotIframe" NOT_POTENTIALLY_TRUSTWORTHY = "NotPotentiallyTrustworthy" NO_API_PERMISSION = "NoApiPermission" NOT_SIGNED_IN_WITH_IDP = "NotSignedInWithIdp" NO_ACCOUNT_SHARING_PERMISSION = "NoAccountSharingPermission" INVALID_CONFIG_OR_WELL_KNOWN = "InvalidConfigOrWellKnown" INVALID_ACCOUNTS_RESPONSE = "InvalidAccountsResponse" NO_RETURNING_USER_FROM_FETCHED_ACCOUNTS = "NoReturningUserFromFetchedAccounts" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> FederatedAuthUserInfoRequestIssueReason: return cls(json) @dataclass class ClientHintIssueDetails: ''' This issue tracks client hints related issues. It's used to deprecate old features, encourage the use of new ones, and provide general guidance. ''' source_code_location: SourceCodeLocation client_hint_issue_reason: ClientHintIssueReason def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['sourceCodeLocation'] = self.source_code_location.to_json() json['clientHintIssueReason'] = self.client_hint_issue_reason.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> ClientHintIssueDetails: return cls( source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']), client_hint_issue_reason=ClientHintIssueReason.from_json(json['clientHintIssueReason']), ) @dataclass class FailedRequestInfo: #: The URL that failed to load. url: str #: The failure message for the failed request. failure_message: str request_id: typing.Optional[network.RequestId] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['url'] = self.url json['failureMessage'] = self.failure_message if self.request_id is not None: json['requestId'] = self.request_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> FailedRequestInfo: return cls( url=str(json['url']), failure_message=str(json['failureMessage']), request_id=network.RequestId.from_json(json['requestId']) if json.get('requestId', None) is not None else None, ) class PartitioningBlobURLInfo(enum.Enum): BLOCKED_CROSS_PARTITION_FETCHING = "BlockedCrossPartitionFetching" ENFORCE_NOOPENER_FOR_NAVIGATION = "EnforceNoopenerForNavigation" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> PartitioningBlobURLInfo: return cls(json) @dataclass class PartitioningBlobURLIssueDetails: #: The BlobURL that failed to load. url: str #: Additional information about the Partitioning Blob URL issue. partitioning_blob_url_info: PartitioningBlobURLInfo def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['url'] = self.url json['partitioningBlobURLInfo'] = self.partitioning_blob_url_info.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> PartitioningBlobURLIssueDetails: return cls( url=str(json['url']), partitioning_blob_url_info=PartitioningBlobURLInfo.from_json(json['partitioningBlobURLInfo']), ) class ElementAccessibilityIssueReason(enum.Enum): DISALLOWED_SELECT_CHILD = "DisallowedSelectChild" DISALLOWED_OPT_GROUP_CHILD = "DisallowedOptGroupChild" NON_PHRASING_CONTENT_OPTION_CHILD = "NonPhrasingContentOptionChild" INTERACTIVE_CONTENT_OPTION_CHILD = "InteractiveContentOptionChild" INTERACTIVE_CONTENT_LEGEND_CHILD = "InteractiveContentLegendChild" INTERACTIVE_CONTENT_SUMMARY_DESCENDANT = "InteractiveContentSummaryDescendant" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> ElementAccessibilityIssueReason: return cls(json) @dataclass class ElementAccessibilityIssueDetails: ''' This issue warns about errors in the select or summary element content model. ''' node_id: dom.BackendNodeId element_accessibility_issue_reason: ElementAccessibilityIssueReason has_disallowed_attributes: bool def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['nodeId'] = self.node_id.to_json() json['elementAccessibilityIssueReason'] = self.element_accessibility_issue_reason.to_json() json['hasDisallowedAttributes'] = self.has_disallowed_attributes return json @classmethod def from_json(cls, json: T_JSON_DICT) -> ElementAccessibilityIssueDetails: return cls( node_id=dom.BackendNodeId.from_json(json['nodeId']), element_accessibility_issue_reason=ElementAccessibilityIssueReason.from_json(json['elementAccessibilityIssueReason']), has_disallowed_attributes=bool(json['hasDisallowedAttributes']), ) class StyleSheetLoadingIssueReason(enum.Enum): LATE_IMPORT_RULE = "LateImportRule" REQUEST_FAILED = "RequestFailed" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> StyleSheetLoadingIssueReason: return cls(json) @dataclass class StylesheetLoadingIssueDetails: ''' This issue warns when a referenced stylesheet couldn't be loaded. ''' #: Source code position that referenced the failing stylesheet. source_code_location: SourceCodeLocation #: Reason why the stylesheet couldn't be loaded. style_sheet_loading_issue_reason: StyleSheetLoadingIssueReason #: Contains additional info when the failure was due to a request. failed_request_info: typing.Optional[FailedRequestInfo] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['sourceCodeLocation'] = self.source_code_location.to_json() json['styleSheetLoadingIssueReason'] = self.style_sheet_loading_issue_reason.to_json() if self.failed_request_info is not None: json['failedRequestInfo'] = self.failed_request_info.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> StylesheetLoadingIssueDetails: return cls( source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']), style_sheet_loading_issue_reason=StyleSheetLoadingIssueReason.from_json(json['styleSheetLoadingIssueReason']), failed_request_info=FailedRequestInfo.from_json(json['failedRequestInfo']) if json.get('failedRequestInfo', None) is not None else None, ) class PropertyRuleIssueReason(enum.Enum): INVALID_SYNTAX = "InvalidSyntax" INVALID_INITIAL_VALUE = "InvalidInitialValue" INVALID_INHERITS = "InvalidInherits" INVALID_NAME = "InvalidName" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> PropertyRuleIssueReason: return cls(json) @dataclass class PropertyRuleIssueDetails: ''' This issue warns about errors in property rules that lead to property registrations being ignored. ''' #: Source code position of the property rule. source_code_location: SourceCodeLocation #: Reason why the property rule was discarded. property_rule_issue_reason: PropertyRuleIssueReason #: The value of the property rule property that failed to parse property_value: typing.Optional[str] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['sourceCodeLocation'] = self.source_code_location.to_json() json['propertyRuleIssueReason'] = self.property_rule_issue_reason.to_json() if self.property_value is not None: json['propertyValue'] = self.property_value return json @classmethod def from_json(cls, json: T_JSON_DICT) -> PropertyRuleIssueDetails: return cls( source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']), property_rule_issue_reason=PropertyRuleIssueReason.from_json(json['propertyRuleIssueReason']), property_value=str(json['propertyValue']) if json.get('propertyValue', None) is not None else None, ) class UserReidentificationIssueType(enum.Enum): BLOCKED_FRAME_NAVIGATION = "BlockedFrameNavigation" BLOCKED_SUBRESOURCE = "BlockedSubresource" NOISED_CANVAS_READBACK = "NoisedCanvasReadback" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> UserReidentificationIssueType: return cls(json) @dataclass class UserReidentificationIssueDetails: ''' This issue warns about uses of APIs that may be considered misuse to re-identify users. ''' type_: UserReidentificationIssueType #: Applies to BlockedFrameNavigation and BlockedSubresource issue types. request: typing.Optional[AffectedRequest] = None #: Applies to NoisedCanvasReadback issue type. source_code_location: typing.Optional[SourceCodeLocation] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['type'] = self.type_.to_json() if self.request is not None: json['request'] = self.request.to_json() if self.source_code_location is not None: json['sourceCodeLocation'] = self.source_code_location.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> UserReidentificationIssueDetails: return cls( type_=UserReidentificationIssueType.from_json(json['type']), request=AffectedRequest.from_json(json['request']) if json.get('request', None) is not None else None, source_code_location=SourceCodeLocation.from_json(json['sourceCodeLocation']) if json.get('sourceCodeLocation', None) is not None else None, ) class InspectorIssueCode(enum.Enum): ''' A unique identifier for the type of issue. Each type may use one of the optional fields in InspectorIssueDetails to convey more specific information about the kind of issue. ''' COOKIE_ISSUE = "CookieIssue" MIXED_CONTENT_ISSUE = "MixedContentIssue" BLOCKED_BY_RESPONSE_ISSUE = "BlockedByResponseIssue" HEAVY_AD_ISSUE = "HeavyAdIssue" CONTENT_SECURITY_POLICY_ISSUE = "ContentSecurityPolicyIssue" SHARED_ARRAY_BUFFER_ISSUE = "SharedArrayBufferIssue" LOW_TEXT_CONTRAST_ISSUE = "LowTextContrastIssue" CORS_ISSUE = "CorsIssue" ATTRIBUTION_REPORTING_ISSUE = "AttributionReportingIssue" QUIRKS_MODE_ISSUE = "QuirksModeIssue" PARTITIONING_BLOB_URL_ISSUE = "PartitioningBlobURLIssue" NAVIGATOR_USER_AGENT_ISSUE = "NavigatorUserAgentIssue" GENERIC_ISSUE = "GenericIssue" DEPRECATION_ISSUE = "DeprecationIssue" CLIENT_HINT_ISSUE = "ClientHintIssue" FEDERATED_AUTH_REQUEST_ISSUE = "FederatedAuthRequestIssue" BOUNCE_TRACKING_ISSUE = "BounceTrackingIssue" COOKIE_DEPRECATION_METADATA_ISSUE = "CookieDeprecationMetadataIssue" STYLESHEET_LOADING_ISSUE = "StylesheetLoadingIssue" FEDERATED_AUTH_USER_INFO_REQUEST_ISSUE = "FederatedAuthUserInfoRequestIssue" PROPERTY_RULE_ISSUE = "PropertyRuleIssue" SHARED_DICTIONARY_ISSUE = "SharedDictionaryIssue" ELEMENT_ACCESSIBILITY_ISSUE = "ElementAccessibilityIssue" SRI_MESSAGE_SIGNATURE_ISSUE = "SRIMessageSignatureIssue" UNENCODED_DIGEST_ISSUE = "UnencodedDigestIssue" USER_REIDENTIFICATION_ISSUE = "UserReidentificationIssue" def to_json(self) -> str: return self.value @classmethod def from_json(cls, json: str) -> InspectorIssueCode: return cls(json) @dataclass class InspectorIssueDetails: ''' This struct holds a list of optional fields with additional information specific to the kind of issue. When adding a new issue code, please also add a new optional field to this type. ''' cookie_issue_details: typing.Optional[CookieIssueDetails] = None mixed_content_issue_details: typing.Optional[MixedContentIssueDetails] = None blocked_by_response_issue_details: typing.Optional[BlockedByResponseIssueDetails] = None heavy_ad_issue_details: typing.Optional[HeavyAdIssueDetails] = None content_security_policy_issue_details: typing.Optional[ContentSecurityPolicyIssueDetails] = None shared_array_buffer_issue_details: typing.Optional[SharedArrayBufferIssueDetails] = None low_text_contrast_issue_details: typing.Optional[LowTextContrastIssueDetails] = None cors_issue_details: typing.Optional[CorsIssueDetails] = None attribution_reporting_issue_details: typing.Optional[AttributionReportingIssueDetails] = None quirks_mode_issue_details: typing.Optional[QuirksModeIssueDetails] = None partitioning_blob_url_issue_details: typing.Optional[PartitioningBlobURLIssueDetails] = None navigator_user_agent_issue_details: typing.Optional[NavigatorUserAgentIssueDetails] = None generic_issue_details: typing.Optional[GenericIssueDetails] = None deprecation_issue_details: typing.Optional[DeprecationIssueDetails] = None client_hint_issue_details: typing.Optional[ClientHintIssueDetails] = None federated_auth_request_issue_details: typing.Optional[FederatedAuthRequestIssueDetails] = None bounce_tracking_issue_details: typing.Optional[BounceTrackingIssueDetails] = None cookie_deprecation_metadata_issue_details: typing.Optional[CookieDeprecationMetadataIssueDetails] = None stylesheet_loading_issue_details: typing.Optional[StylesheetLoadingIssueDetails] = None property_rule_issue_details: typing.Optional[PropertyRuleIssueDetails] = None federated_auth_user_info_request_issue_details: typing.Optional[FederatedAuthUserInfoRequestIssueDetails] = None shared_dictionary_issue_details: typing.Optional[SharedDictionaryIssueDetails] = None element_accessibility_issue_details: typing.Optional[ElementAccessibilityIssueDetails] = None sri_message_signature_issue_details: typing.Optional[SRIMessageSignatureIssueDetails] = None unencoded_digest_issue_details: typing.Optional[UnencodedDigestIssueDetails] = None user_reidentification_issue_details: typing.Optional[UserReidentificationIssueDetails] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() if self.cookie_issue_details is not None: json['cookieIssueDetails'] = self.cookie_issue_details.to_json() if self.mixed_content_issue_details is not None: json['mixedContentIssueDetails'] = self.mixed_content_issue_details.to_json() if self.blocked_by_response_issue_details is not None: json['blockedByResponseIssueDetails'] = self.blocked_by_response_issue_details.to_json() if self.heavy_ad_issue_details is not None: json['heavyAdIssueDetails'] = self.heavy_ad_issue_details.to_json() if self.content_security_policy_issue_details is not None: json['contentSecurityPolicyIssueDetails'] = self.content_security_policy_issue_details.to_json() if self.shared_array_buffer_issue_details is not None: json['sharedArrayBufferIssueDetails'] = self.shared_array_buffer_issue_details.to_json() if self.low_text_contrast_issue_details is not None: json['lowTextContrastIssueDetails'] = self.low_text_contrast_issue_details.to_json() if self.cors_issue_details is not None: json['corsIssueDetails'] = self.cors_issue_details.to_json() if self.attribution_reporting_issue_details is not None: json['attributionReportingIssueDetails'] = self.attribution_reporting_issue_details.to_json() if self.quirks_mode_issue_details is not None: json['quirksModeIssueDetails'] = self.quirks_mode_issue_details.to_json() if self.partitioning_blob_url_issue_details is not None: json['partitioningBlobURLIssueDetails'] = self.partitioning_blob_url_issue_details.to_json() if self.navigator_user_agent_issue_details is not None: json['navigatorUserAgentIssueDetails'] = self.navigator_user_agent_issue_details.to_json() if self.generic_issue_details is not None: json['genericIssueDetails'] = self.generic_issue_details.to_json() if self.deprecation_issue_details is not None: json['deprecationIssueDetails'] = self.deprecation_issue_details.to_json() if self.client_hint_issue_details is not None: json['clientHintIssueDetails'] = self.client_hint_issue_details.to_json() if self.federated_auth_request_issue_details is not None: json['federatedAuthRequestIssueDetails'] = self.federated_auth_request_issue_details.to_json() if self.bounce_tracking_issue_details is not None: json['bounceTrackingIssueDetails'] = self.bounce_tracking_issue_details.to_json() if self.cookie_deprecation_metadata_issue_details is not None: json['cookieDeprecationMetadataIssueDetails'] = self.cookie_deprecation_metadata_issue_details.to_json() if self.stylesheet_loading_issue_details is not None: json['stylesheetLoadingIssueDetails'] = self.stylesheet_loading_issue_details.to_json() if self.property_rule_issue_details is not None: json['propertyRuleIssueDetails'] = self.property_rule_issue_details.to_json() if self.federated_auth_user_info_request_issue_details is not None: json['federatedAuthUserInfoRequestIssueDetails'] = self.federated_auth_user_info_request_issue_details.to_json() if self.shared_dictionary_issue_details is not None: json['sharedDictionaryIssueDetails'] = self.shared_dictionary_issue_details.to_json() if self.element_accessibility_issue_details is not None: json['elementAccessibilityIssueDetails'] = self.element_accessibility_issue_details.to_json() if self.sri_message_signature_issue_details is not None: json['sriMessageSignatureIssueDetails'] = self.sri_message_signature_issue_details.to_json() if self.unencoded_digest_issue_details is not None: json['unencodedDigestIssueDetails'] = self.unencoded_digest_issue_details.to_json() if self.user_reidentification_issue_details is not None: json['userReidentificationIssueDetails'] = self.user_reidentification_issue_details.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> InspectorIssueDetails: return cls( cookie_issue_details=CookieIssueDetails.from_json(json['cookieIssueDetails']) if json.get('cookieIssueDetails', None) is not None else None, mixed_content_issue_details=MixedContentIssueDetails.from_json(json['mixedContentIssueDetails']) if json.get('mixedContentIssueDetails', None) is not None else None, blocked_by_response_issue_details=BlockedByResponseIssueDetails.from_json(json['blockedByResponseIssueDetails']) if json.get('blockedByResponseIssueDetails', None) is not None else None, heavy_ad_issue_details=HeavyAdIssueDetails.from_json(json['heavyAdIssueDetails']) if json.get('heavyAdIssueDetails', None) is not None else None, content_security_policy_issue_details=ContentSecurityPolicyIssueDetails.from_json(json['contentSecurityPolicyIssueDetails']) if json.get('contentSecurityPolicyIssueDetails', None) is not None else None, shared_array_buffer_issue_details=SharedArrayBufferIssueDetails.from_json(json['sharedArrayBufferIssueDetails']) if json.get('sharedArrayBufferIssueDetails', None) is not None else None, low_text_contrast_issue_details=LowTextContrastIssueDetails.from_json(json['lowTextContrastIssueDetails']) if json.get('lowTextContrastIssueDetails', None) is not None else None, cors_issue_details=CorsIssueDetails.from_json(json['corsIssueDetails']) if json.get('corsIssueDetails', None) is not None else None, attribution_reporting_issue_details=AttributionReportingIssueDetails.from_json(json['attributionReportingIssueDetails']) if json.get('attributionReportingIssueDetails', None) is not None else None, quirks_mode_issue_details=QuirksModeIssueDetails.from_json(json['quirksModeIssueDetails']) if json.get('quirksModeIssueDetails', None) is not None else None, partitioning_blob_url_issue_details=PartitioningBlobURLIssueDetails.from_json(json['partitioningBlobURLIssueDetails']) if json.get('partitioningBlobURLIssueDetails', None) is not None else None, navigator_user_agent_issue_details=NavigatorUserAgentIssueDetails.from_json(json['navigatorUserAgentIssueDetails']) if json.get('navigatorUserAgentIssueDetails', None) is not None else None, generic_issue_details=GenericIssueDetails.from_json(json['genericIssueDetails']) if json.get('genericIssueDetails', None) is not None else None, deprecation_issue_details=DeprecationIssueDetails.from_json(json['deprecationIssueDetails']) if json.get('deprecationIssueDetails', None) is not None else None, client_hint_issue_details=ClientHintIssueDetails.from_json(json['clientHintIssueDetails']) if json.get('clientHintIssueDetails', None) is not None else None, federated_auth_request_issue_details=FederatedAuthRequestIssueDetails.from_json(json['federatedAuthRequestIssueDetails']) if json.get('federatedAuthRequestIssueDetails', None) is not None else None, bounce_tracking_issue_details=BounceTrackingIssueDetails.from_json(json['bounceTrackingIssueDetails']) if json.get('bounceTrackingIssueDetails', None) is not None else None, cookie_deprecation_metadata_issue_details=CookieDeprecationMetadataIssueDetails.from_json(json['cookieDeprecationMetadataIssueDetails']) if json.get('cookieDeprecationMetadataIssueDetails', None) is not None else None, stylesheet_loading_issue_details=StylesheetLoadingIssueDetails.from_json(json['stylesheetLoadingIssueDetails']) if json.get('stylesheetLoadingIssueDetails', None) is not None else None, property_rule_issue_details=PropertyRuleIssueDetails.from_json(json['propertyRuleIssueDetails']) if json.get('propertyRuleIssueDetails', None) is not None else None, federated_auth_user_info_request_issue_details=FederatedAuthUserInfoRequestIssueDetails.from_json(json['federatedAuthUserInfoRequestIssueDetails']) if json.get('federatedAuthUserInfoRequestIssueDetails', None) is not None else None, shared_dictionary_issue_details=SharedDictionaryIssueDetails.from_json(json['sharedDictionaryIssueDetails']) if json.get('sharedDictionaryIssueDetails', None) is not None else None, element_accessibility_issue_details=ElementAccessibilityIssueDetails.from_json(json['elementAccessibilityIssueDetails']) if json.get('elementAccessibilityIssueDetails', None) is not None else None, sri_message_signature_issue_details=SRIMessageSignatureIssueDetails.from_json(json['sriMessageSignatureIssueDetails']) if json.get('sriMessageSignatureIssueDetails', None) is not None else None, unencoded_digest_issue_details=UnencodedDigestIssueDetails.from_json(json['unencodedDigestIssueDetails']) if json.get('unencodedDigestIssueDetails', None) is not None else None, user_reidentification_issue_details=UserReidentificationIssueDetails.from_json(json['userReidentificationIssueDetails']) if json.get('userReidentificationIssueDetails', None) is not None else None, ) class IssueId(str): ''' A unique id for a DevTools inspector issue. Allows other entities (e.g. exceptions, CDP message, console messages, etc.) to reference an issue. ''' def to_json(self) -> str: return self @classmethod def from_json(cls, json: str) -> IssueId: return cls(json) def __repr__(self): return 'IssueId({})'.format(super().__repr__()) @dataclass class InspectorIssue: ''' An inspector issue reported from the back-end. ''' code: InspectorIssueCode details: InspectorIssueDetails #: A unique id for this issue. May be omitted if no other entity (e.g. #: exception, CDP message, etc.) is referencing this issue. issue_id: typing.Optional[IssueId] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() json['code'] = self.code.to_json() json['details'] = self.details.to_json() if self.issue_id is not None: json['issueId'] = self.issue_id.to_json() return json @classmethod def from_json(cls, json: T_JSON_DICT) -> InspectorIssue: return cls( code=InspectorIssueCode.from_json(json['code']), details=InspectorIssueDetails.from_json(json['details']), issue_id=IssueId.from_json(json['issueId']) if json.get('issueId', None) is not None else None, ) def get_encoded_response( request_id: network.RequestId, encoding: str, quality: typing.Optional[float] = None, size_only: typing.Optional[bool] = None ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[typing.Optional[str], int, int]]: ''' Returns the response body and size if it were re-encoded with the specified settings. Only applies to images. :param request_id: Identifier of the network request to get content for. :param encoding: The encoding to use. :param quality: *(Optional)* The quality of the encoding (0-1). (defaults to 1) :param size_only: *(Optional)* Whether to only return the size information (defaults to false). :returns: A tuple with the following items: 0. **body** - *(Optional)* The encoded body as a base64 string. Omitted if sizeOnly is true. (Encoded as a base64 string when passed over JSON) 1. **originalSize** - Size before re-encoding. 2. **encodedSize** - Size after re-encoding. ''' params: T_JSON_DICT = dict() params['requestId'] = request_id.to_json() params['encoding'] = encoding if quality is not None: params['quality'] = quality if size_only is not None: params['sizeOnly'] = size_only cmd_dict: T_JSON_DICT = { 'method': 'Audits.getEncodedResponse', 'params': params, } json = yield cmd_dict return ( str(json['body']) if json.get('body', None) is not None else None, int(json['originalSize']), int(json['encodedSize']) ) def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]: ''' Disables issues domain, prevents further issues from being reported to the client. ''' cmd_dict: T_JSON_DICT = { 'method': 'Audits.disable', } json = yield cmd_dict def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]: ''' Enables issues domain, sends the issues collected so far to the client by means of the ``issueAdded`` event. ''' cmd_dict: T_JSON_DICT = { 'method': 'Audits.enable', } json = yield cmd_dict def check_contrast( report_aaa: typing.Optional[bool] = None ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]: ''' Runs the contrast check for the target page. Found issues are reported using Audits.issueAdded event. :param report_aaa: *(Optional)* Whether to report WCAG AAA level issues. Default is false. ''' params: T_JSON_DICT = dict() if report_aaa is not None: params['reportAAA'] = report_aaa cmd_dict: T_JSON_DICT = { 'method': 'Audits.checkContrast', 'params': params, } json = yield cmd_dict def check_forms_issues() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[GenericIssueDetails]]: ''' Runs the form issues check for the target page. Found issues are reported using Audits.issueAdded event. :returns: ''' cmd_dict: T_JSON_DICT = { 'method': 'Audits.checkFormsIssues', } json = yield cmd_dict return [GenericIssueDetails.from_json(i) for i in json['formIssues']] @event_class('Audits.issueAdded') @dataclass class IssueAdded: issue: InspectorIssue @classmethod def from_json(cls, json: T_JSON_DICT) -> IssueAdded: return cls( issue=InspectorIssue.from_json(json['issue']) )