@yezack/chrome-devtools-mcp-108 0.0.0-stage → 1.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FORK-NOTICE.md +45 -0
- package/LICENSE +202 -0
- package/README.md +45 -2
- package/build/src/BrowserManager.js +401 -0
- package/build/src/McpContext.js +642 -0
- package/build/src/McpPage.js +850 -0
- package/build/src/McpResponse.js +1086 -0
- package/build/src/SlimMcpResponse.js +19 -0
- package/build/src/TextSnapshot.js +315 -0
- package/build/src/ToolHandler.js +322 -0
- package/build/src/bin/check-latest-version.js +51 -0
- package/build/src/bin/chrome-devtools-mcp-main.js +77 -0
- package/build/src/bin/chrome-devtools-mcp.js +23 -0
- package/build/src/bin/chrome-devtools.js +222 -0
- package/build/src/collectors/PageCollector.js +291 -0
- package/build/src/collectors/ServiceWorkerCollector.js +171 -0
- package/build/src/config/browser-options.js +125 -0
- package/build/src/config/category-options.js +89 -0
- package/build/src/config/cli-commands.js +26 -0
- package/build/src/config/cli-options.js +1473 -0
- package/build/src/config/mcp-options.js +417 -0
- package/build/src/daemon/client.js +221 -0
- package/build/src/daemon/daemon.js +266 -0
- package/build/src/daemon/types.js +7 -0
- package/build/src/daemon/utils.js +136 -0
- package/build/src/devtools/DevToolsCommentBridge.js +91 -0
- package/build/src/devtools/DevtoolsUtils.js +307 -0
- package/build/src/devtools/McpHostBindingAdapter.js +165 -0
- package/build/src/devtools/issueDescriptions.js +42 -0
- package/build/src/formatters/CommentFormatter.js +82 -0
- package/build/src/formatters/ConsoleFormatter.js +321 -0
- package/build/src/formatters/CssFormatter.js +812 -0
- package/build/src/formatters/HeapSnapshotFormatter.js +185 -0
- package/build/src/formatters/IssueFormatter.js +193 -0
- package/build/src/formatters/NetworkFormatter.js +241 -0
- package/build/src/formatters/SnapshotFormatter.js +135 -0
- package/build/src/index.js +237 -0
- package/build/src/processors/ChunkedTraceParser.js +361 -0
- package/build/src/processors/HeapSnapshotManager.js +270 -0
- package/build/src/processors/PerformanceTrace.js +132 -0
- package/build/src/telemetry/ClearcutLogger.js +242 -0
- package/build/src/telemetry/WatchdogClient.js +61 -0
- package/build/src/telemetry/errors.js +18 -0
- package/build/src/telemetry/flagUtils.js +90 -0
- package/build/src/telemetry/metricsRegistry.js +85 -0
- package/build/src/telemetry/persistence.js +82 -0
- package/build/src/telemetry/transformation.js +233 -0
- package/build/src/telemetry/types.js +36 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
- package/build/src/telemetry/watchdog/main.js +128 -0
- package/build/src/third_party/THIRD_PARTY_NOTICES +4077 -0
- package/build/src/third_party/bundled-packages.json +12 -0
- package/build/src/third_party/devtools-formatter-worker.js +15494 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +10979 -0
- package/build/src/third_party/index.js +200978 -0
- package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
- package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
- package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
- package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
- package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
- package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
- package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
- package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
- package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
- package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
- package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
- package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
- package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
- package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
- package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
- package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
- package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
- package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
- package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
- package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
- package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
- package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +9 -0
- package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
- package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
- package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
- package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
- package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
- package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestConfigBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestWellKnownBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
- package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
- package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
- package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
- package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
- package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
- package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
- package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
- package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
- package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
- package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
- package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
- package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
- package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
- package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
- package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +7 -0
- package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
- package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
- package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
- package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
- package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
- package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
- package/build/src/third_party/issue-descriptions/webInstallManifestMissingId.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallNoManifest.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallStartUrlInvalid.md +3 -0
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +58435 -0
- package/build/src/tools/ToolDefinition.js +80 -0
- package/build/src/tools/categories.js +33 -0
- package/build/src/tools/comments.js +169 -0
- package/build/src/tools/console.js +104 -0
- package/build/src/tools/css.js +45 -0
- package/build/src/tools/emulation.js +84 -0
- package/build/src/tools/extensions.js +103 -0
- package/build/src/tools/input.js +652 -0
- package/build/src/tools/lighthouse.js +149 -0
- package/build/src/tools/memory.js +424 -0
- package/build/src/tools/network.js +128 -0
- package/build/src/tools/pages.js +363 -0
- package/build/src/tools/performance.js +212 -0
- package/build/src/tools/pwa.js +128 -0
- package/build/src/tools/screencast.js +119 -0
- package/build/src/tools/screenshot.js +277 -0
- package/build/src/tools/script.js +232 -0
- package/build/src/tools/slim/tools.js +96 -0
- package/build/src/tools/snapshot.js +63 -0
- package/build/src/tools/thirdPartyDeveloper.js +85 -0
- package/build/src/tools/tools.js +55 -0
- package/build/src/tools/webmcp.js +66 -0
- package/build/src/types.js +10 -0
- package/build/src/utils/WaitForHelper.js +316 -0
- package/build/src/utils/bytes.js +105 -0
- package/build/src/utils/check-for-updates.js +75 -0
- package/build/src/utils/files.js +57 -0
- package/build/src/utils/id.js +16 -0
- package/build/src/utils/keyboard.js +297 -0
- package/build/src/utils/logger.js +52 -0
- package/build/src/utils/pagination.js +50 -0
- package/build/src/utils/polyfill.js +11 -0
- package/build/src/utils/url.js +116 -0
- package/build/src/version.js +10 -0
- package/package.json +41 -4
- package/skills/a11y-debugging/SKILL.md +91 -0
- package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
- package/skills/chrome-devtools/SKILL.md +74 -0
- package/skills/chrome-devtools-cli/SKILL.md +194 -0
- package/skills/chrome-devtools-cli/references/installation.md +14 -0
- package/skills/cookie-debugging/SKILL.md +149 -0
- package/skills/debug-optimize-lcp/SKILL.md +121 -0
- package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
- package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
- package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
- package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
- package/skills/memory-leak-debugging/SKILL.md +58 -0
- package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
- package/skills/troubleshooting/SKILL.md +99 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
7
|
+
if (value !== null && value !== void 0) {
|
|
8
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
9
|
+
var dispose, inner;
|
|
10
|
+
if (async) {
|
|
11
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
12
|
+
dispose = value[Symbol.asyncDispose];
|
|
13
|
+
}
|
|
14
|
+
if (dispose === void 0) {
|
|
15
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
16
|
+
dispose = value[Symbol.dispose];
|
|
17
|
+
if (async) inner = dispose;
|
|
18
|
+
}
|
|
19
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
20
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
21
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
22
|
+
}
|
|
23
|
+
else if (async) {
|
|
24
|
+
env.stack.push({ async: true });
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
29
|
+
return function (env) {
|
|
30
|
+
function fail(e) {
|
|
31
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
32
|
+
env.hasError = true;
|
|
33
|
+
}
|
|
34
|
+
var r, s = 0;
|
|
35
|
+
function next() {
|
|
36
|
+
while (r = env.stack.pop()) {
|
|
37
|
+
try {
|
|
38
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
39
|
+
if (r.dispose) {
|
|
40
|
+
var result = r.dispose.call(r.value);
|
|
41
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
42
|
+
}
|
|
43
|
+
else s |= 1;
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
fail(e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
50
|
+
if (env.hasError) throw env.error;
|
|
51
|
+
}
|
|
52
|
+
return next();
|
|
53
|
+
};
|
|
54
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
55
|
+
var e = new Error(message);
|
|
56
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
|
+
});
|
|
58
|
+
export function replaceHtmlElementsWithUids(schema) {
|
|
59
|
+
if (typeof schema === 'boolean') {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let isHtmlElement = false;
|
|
63
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
64
|
+
if (key === 'x-mcp-type' && value === 'HTMLElement') {
|
|
65
|
+
isHtmlElement = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (isHtmlElement) {
|
|
70
|
+
schema.properties = { uid: { type: 'string' } };
|
|
71
|
+
schema.required = ['uid'];
|
|
72
|
+
}
|
|
73
|
+
if (schema.properties) {
|
|
74
|
+
for (const key of Object.keys(schema.properties)) {
|
|
75
|
+
replaceHtmlElementsWithUids(schema.properties[key]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (schema.items) {
|
|
79
|
+
if (Array.isArray(schema.items)) {
|
|
80
|
+
for (const item of schema.items) {
|
|
81
|
+
replaceHtmlElementsWithUids(item);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
replaceHtmlElementsWithUids(schema.items);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (schema.anyOf) {
|
|
89
|
+
for (const s of schema.anyOf) {
|
|
90
|
+
replaceHtmlElementsWithUids(s);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (schema.allOf) {
|
|
94
|
+
for (const s of schema.allOf) {
|
|
95
|
+
replaceHtmlElementsWithUids(s);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (schema.oneOf) {
|
|
99
|
+
for (const s of schema.oneOf) {
|
|
100
|
+
replaceHtmlElementsWithUids(s);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
import { DevToolsCommentBridge } from './devtools/DevToolsCommentBridge.js';
|
|
105
|
+
import { createTargetUniverse, } from './devtools/DevtoolsUtils.js';
|
|
106
|
+
import { ConsoleCollector, NetworkCollector, } from './collectors/PageCollector.js';
|
|
107
|
+
import { TextSnapshot } from './TextSnapshot.js';
|
|
108
|
+
import { PredefinedNetworkConditions, DevTools, } from './third_party/index.js';
|
|
109
|
+
const DEFAULT_TIMEOUT = 5_000;
|
|
110
|
+
const NAVIGATION_TIMEOUT = 10_000;
|
|
111
|
+
import { logger } from './utils/logger.js';
|
|
112
|
+
import { getNetworkMultiplierFromString, WaitForHelper, } from './utils/WaitForHelper.js';
|
|
113
|
+
function isBackendNodeId(id) {
|
|
114
|
+
return typeof id === 'number';
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Per-page state wrapper. Consolidates dialog, snapshot, emulation,
|
|
118
|
+
* and metadata that were previously scattered across Maps in McpContext.
|
|
119
|
+
*
|
|
120
|
+
* Internal class consumed only by McpContext. Fields are public for direct
|
|
121
|
+
* read/write access. The dialog field is private because it requires an
|
|
122
|
+
* event listener lifecycle managed by the constructor/dispose pair.
|
|
123
|
+
*/
|
|
124
|
+
export class McpPage {
|
|
125
|
+
pptrPage;
|
|
126
|
+
id;
|
|
127
|
+
// Snapshot
|
|
128
|
+
textSnapshot = null;
|
|
129
|
+
uniqueBackendNodeIdToMcpId = new Map();
|
|
130
|
+
extraHandles = [];
|
|
131
|
+
// Emulation
|
|
132
|
+
emulationSettings = {};
|
|
133
|
+
// Metadata
|
|
134
|
+
isolatedContextName;
|
|
135
|
+
#devtoolsUniverse;
|
|
136
|
+
// Dialog
|
|
137
|
+
#dialog;
|
|
138
|
+
#dialogHandler;
|
|
139
|
+
thirdPartyDeveloperTools = [];
|
|
140
|
+
networkCollector;
|
|
141
|
+
consoleCollector;
|
|
142
|
+
#hasNetworkBlockOrAllowlist;
|
|
143
|
+
#locatorClass;
|
|
144
|
+
#navigationTimeout;
|
|
145
|
+
#sourceMaps;
|
|
146
|
+
#commentBridge;
|
|
147
|
+
#onNotification;
|
|
148
|
+
constructor(page, id, options) {
|
|
149
|
+
this.#hasNetworkBlockOrAllowlist = options.hasNetworkBlockOrAllowlist;
|
|
150
|
+
this.#locatorClass = options.locatorClass;
|
|
151
|
+
this.#navigationTimeout = options.navigationTimeout ?? NAVIGATION_TIMEOUT;
|
|
152
|
+
this.#sourceMaps = options.sourceMaps ?? true;
|
|
153
|
+
this.#onNotification = options.onNotification;
|
|
154
|
+
this.pptrPage = page;
|
|
155
|
+
this.id = id;
|
|
156
|
+
this.isolatedContextName = options.isolatedContextName;
|
|
157
|
+
this.#dialogHandler = (dialog) => {
|
|
158
|
+
this.#dialog = dialog;
|
|
159
|
+
};
|
|
160
|
+
page.on('dialog', this.#dialogHandler);
|
|
161
|
+
this.networkCollector = new NetworkCollector(page);
|
|
162
|
+
this.consoleCollector = new ConsoleCollector(page, collect => {
|
|
163
|
+
return {
|
|
164
|
+
console: event => {
|
|
165
|
+
collect(event);
|
|
166
|
+
},
|
|
167
|
+
uncaughtError: event => {
|
|
168
|
+
collect(event);
|
|
169
|
+
},
|
|
170
|
+
devtoolsAggregatedIssue: event => {
|
|
171
|
+
collect(event);
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async init() {
|
|
177
|
+
await Promise.allSettled([
|
|
178
|
+
this.#initDevToolsUniverseNoThrow(),
|
|
179
|
+
this.#initFocusEmulationNoThrow(),
|
|
180
|
+
]);
|
|
181
|
+
}
|
|
182
|
+
async #initFocusEmulationNoThrow() {
|
|
183
|
+
// We emulate a focused page for all pages to support multi-agent workflows.
|
|
184
|
+
void this.pptrPage.emulateFocusedPage(true).catch(error => {
|
|
185
|
+
logger?.('Error turning on focused page emulation', error);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async #initDevToolsUniverseNoThrow() {
|
|
189
|
+
if (this.#devtoolsUniverse) {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const session = await this.pptrPage.createCDPSession();
|
|
194
|
+
this.#devtoolsUniverse = await createTargetUniverse(session, {
|
|
195
|
+
sourceMaps: this.#sourceMaps,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
logger?.('Failed to initialize DevTools universe', e);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
get devtoolsUniverse() {
|
|
203
|
+
return this.#devtoolsUniverse;
|
|
204
|
+
}
|
|
205
|
+
getDialog() {
|
|
206
|
+
return this.#dialog;
|
|
207
|
+
}
|
|
208
|
+
clearDialog() {
|
|
209
|
+
this.#dialog = undefined;
|
|
210
|
+
}
|
|
211
|
+
throwIfDialogOpen() {
|
|
212
|
+
if (this.#dialog && !this.#dialog.handled) {
|
|
213
|
+
throw new Error(`A dialog is open (${this.#dialog.type()}: ${this.#dialog.message()}).`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
getThirdPartyDeveloperTools() {
|
|
217
|
+
return this.thirdPartyDeveloperTools;
|
|
218
|
+
}
|
|
219
|
+
async getToolGroups() {
|
|
220
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
221
|
+
try {
|
|
222
|
+
// Check if there is a `devtoolstooldiscovery` event listener
|
|
223
|
+
const windowHandle = __addDisposableResource(env_1, await this.pptrPage.evaluateHandle(() => window), false);
|
|
224
|
+
// @ts-expect-error internal API
|
|
225
|
+
const client = this.pptrPage._client();
|
|
226
|
+
const { listeners } = await client.send('DOMDebugger.getEventListeners', {
|
|
227
|
+
objectId: windowHandle.remoteObject().objectId,
|
|
228
|
+
});
|
|
229
|
+
if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
const toolGroups = await this.pptrPage.evaluate(() => {
|
|
233
|
+
if (window.__dtmcp) {
|
|
234
|
+
window.__dtmcp.toolGroups = [];
|
|
235
|
+
}
|
|
236
|
+
return new Promise(resolve => {
|
|
237
|
+
const event = new CustomEvent('devtoolstooldiscovery');
|
|
238
|
+
const groups = [];
|
|
239
|
+
// @ts-expect-error Adding custom property
|
|
240
|
+
event.respondWith = toolGroup => {
|
|
241
|
+
if (!window.__dtmcp) {
|
|
242
|
+
window.__dtmcp = {};
|
|
243
|
+
}
|
|
244
|
+
if (!window.__dtmcp.toolGroups) {
|
|
245
|
+
window.__dtmcp.toolGroups = [];
|
|
246
|
+
}
|
|
247
|
+
if (typeof toolGroup.name !== 'string' ||
|
|
248
|
+
(toolGroup.description &&
|
|
249
|
+
typeof toolGroup.description !== 'string') ||
|
|
250
|
+
!Array.isArray(toolGroup.tools)) {
|
|
251
|
+
console.error('Invalid toolGroup:', toolGroup);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
for (const tool of toolGroup.tools) {
|
|
255
|
+
if (typeof tool.name !== 'string' ||
|
|
256
|
+
typeof tool.description !== 'string' ||
|
|
257
|
+
typeof tool.inputSchema !== 'object' ||
|
|
258
|
+
typeof tool.execute !== 'function') {
|
|
259
|
+
console.error('Invalid tool:', tool);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
window.__dtmcp.toolGroups.push(toolGroup);
|
|
264
|
+
// When receiving a toolGroup for the first time, expose a simple execution helper
|
|
265
|
+
if (!window.__dtmcp.executeTool) {
|
|
266
|
+
window.__dtmcp.executeTool = async (toolName, args) => {
|
|
267
|
+
if (!window.__dtmcp?.toolGroups ||
|
|
268
|
+
window.__dtmcp.toolGroups.length === 0) {
|
|
269
|
+
throw new Error('No tools found on the page');
|
|
270
|
+
}
|
|
271
|
+
for (const group of window.__dtmcp.toolGroups) {
|
|
272
|
+
const tool = group.tools?.find(t => t.name === toolName);
|
|
273
|
+
if (tool) {
|
|
274
|
+
return await tool.execute(args);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
throw new Error(`Tool ${toolName} not found`);
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
groups.push(toolGroup);
|
|
281
|
+
};
|
|
282
|
+
window.dispatchEvent(event);
|
|
283
|
+
// If at least one toolGroup was added synchronously, resolve with the array.
|
|
284
|
+
// Otherwise, use setTimeout to allow for any microtask/asynchronous respondWith calls, or resolve with an empty array.
|
|
285
|
+
if (groups.length > 0) {
|
|
286
|
+
resolve(groups);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
if (groups.length > 0) {
|
|
291
|
+
resolve(groups);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
resolve([]);
|
|
295
|
+
}
|
|
296
|
+
}, 0);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
for (const group of toolGroups) {
|
|
301
|
+
for (const tool of group.tools ?? []) {
|
|
302
|
+
replaceHtmlElementsWithUids(tool.inputSchema);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.thirdPartyDeveloperTools = toolGroups;
|
|
306
|
+
return toolGroups;
|
|
307
|
+
}
|
|
308
|
+
catch (e_1) {
|
|
309
|
+
env_1.error = e_1;
|
|
310
|
+
env_1.hasError = true;
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
__disposeResources(env_1);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
getWebMcpTools() {
|
|
317
|
+
return this.pptrPage.webmcp.tools();
|
|
318
|
+
}
|
|
319
|
+
resolveCdpRequestId(cdpRequestId) {
|
|
320
|
+
if (!cdpRequestId) {
|
|
321
|
+
logger?.('no network request');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const request = this.networkCollector.find(request => {
|
|
325
|
+
// @ts-expect-error id is internal.
|
|
326
|
+
return request.id === cdpRequestId;
|
|
327
|
+
});
|
|
328
|
+
if (!request) {
|
|
329
|
+
logger?.('no network request for ' + cdpRequestId);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
return this.networkCollector.getIdForResource(request);
|
|
333
|
+
}
|
|
334
|
+
resolveReqidToCdpRequestId(reqid) {
|
|
335
|
+
const request = this.networkCollector.getById(reqid);
|
|
336
|
+
if (!request) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
// @ts-expect-error id is internal.
|
|
340
|
+
return request.id;
|
|
341
|
+
}
|
|
342
|
+
getNetworkRequests(includePreservedRequests) {
|
|
343
|
+
return this.networkCollector.getData(includePreservedRequests);
|
|
344
|
+
}
|
|
345
|
+
get commentBridge() {
|
|
346
|
+
return this.#commentBridge;
|
|
347
|
+
}
|
|
348
|
+
async ensureDevToolsCommentBridge(devtoolsPage) {
|
|
349
|
+
if (!this.#commentBridge) {
|
|
350
|
+
this.#commentBridge = new DevToolsCommentBridge({
|
|
351
|
+
onNotification: this.#onNotification,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
await this.#commentBridge.attach(devtoolsPage);
|
|
355
|
+
return this.#commentBridge;
|
|
356
|
+
}
|
|
357
|
+
async getDevToolsPage() {
|
|
358
|
+
try {
|
|
359
|
+
if (await this.pptrPage.hasDevTools()) {
|
|
360
|
+
return await this.pptrPage.openDevTools();
|
|
361
|
+
}
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// Prior to Chrome 144.0.7559.59, the command fails,
|
|
366
|
+
// Some Electron apps still use older version
|
|
367
|
+
// Fall back to not exposing DevTools at all.
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async openDevTools() {
|
|
372
|
+
const devtoolsPage = await this.pptrPage.openDevTools();
|
|
373
|
+
await this.ensureDevToolsCommentBridge(devtoolsPage);
|
|
374
|
+
return devtoolsPage;
|
|
375
|
+
}
|
|
376
|
+
getConsoleData(includePreservedMessages) {
|
|
377
|
+
return this.consoleCollector.getData(includePreservedMessages);
|
|
378
|
+
}
|
|
379
|
+
getConsoleMessageById(id) {
|
|
380
|
+
return this.consoleCollector.getById(id);
|
|
381
|
+
}
|
|
382
|
+
getNetworkRequestById(reqid) {
|
|
383
|
+
return this.networkCollector.getById(reqid);
|
|
384
|
+
}
|
|
385
|
+
get networkConditions() {
|
|
386
|
+
return this.emulationSettings.networkConditions ?? null;
|
|
387
|
+
}
|
|
388
|
+
get cpuThrottlingRate() {
|
|
389
|
+
return this.emulationSettings.cpuThrottlingRate ?? 1;
|
|
390
|
+
}
|
|
391
|
+
get geolocation() {
|
|
392
|
+
return this.emulationSettings.geolocation ?? null;
|
|
393
|
+
}
|
|
394
|
+
get viewport() {
|
|
395
|
+
return this.emulationSettings.viewport ?? null;
|
|
396
|
+
}
|
|
397
|
+
get userAgent() {
|
|
398
|
+
return this.emulationSettings.userAgent ?? null;
|
|
399
|
+
}
|
|
400
|
+
get colorScheme() {
|
|
401
|
+
return this.emulationSettings.colorScheme ?? null;
|
|
402
|
+
}
|
|
403
|
+
// Public for testability: tests spy on this method to verify throttle multipliers.
|
|
404
|
+
createWaitForHelper(cpuMultiplier, networkMultiplier) {
|
|
405
|
+
return new WaitForHelper(this.pptrPage, cpuMultiplier, networkMultiplier);
|
|
406
|
+
}
|
|
407
|
+
waitForEventsAfterAction(action, options) {
|
|
408
|
+
const helper = this.createWaitForHelper(this.cpuThrottlingRate, getNetworkMultiplierFromString(this.networkConditions));
|
|
409
|
+
return helper.waitForEventsAfterAction(action, options);
|
|
410
|
+
}
|
|
411
|
+
dispose() {
|
|
412
|
+
this.#commentBridge?.dispose();
|
|
413
|
+
this.#commentBridge = undefined;
|
|
414
|
+
this.pptrPage.off('dialog', this.#dialogHandler);
|
|
415
|
+
this.networkCollector.dispose();
|
|
416
|
+
this.consoleCollector.dispose();
|
|
417
|
+
const devtoolsUniverse = this.#devtoolsUniverse;
|
|
418
|
+
this.#devtoolsUniverse = undefined;
|
|
419
|
+
devtoolsUniverse?.universe.dispose();
|
|
420
|
+
void devtoolsUniverse?.session.detach().catch(e => {
|
|
421
|
+
logger?.('Failed to detach DevTools session', e);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
async executeThirdPartyDeveloperTool(toolName, params, response) {
|
|
425
|
+
// Creates array of ElementHandles from the UIDs in the params.
|
|
426
|
+
// We do not replace the uids with the ElementsHandles yet, because
|
|
427
|
+
// the `evaluate` function only turns them into DOM elements if they
|
|
428
|
+
// are passed as non-nested arguments.
|
|
429
|
+
const handles = [];
|
|
430
|
+
for (const value of Object.values(params)) {
|
|
431
|
+
if (value instanceof Object &&
|
|
432
|
+
'uid' in value &&
|
|
433
|
+
typeof value.uid === 'string' &&
|
|
434
|
+
Object.keys(value).length === 1) {
|
|
435
|
+
handles.push(await this.getElementByUid(value.uid));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const result = await this.pptrPage.evaluate(async (name, args, ...elements) => {
|
|
439
|
+
// Replace the UIDs with DOM elements.
|
|
440
|
+
for (const [key, value] of Object.entries(args)) {
|
|
441
|
+
if (value instanceof Object &&
|
|
442
|
+
'uid' in value &&
|
|
443
|
+
typeof value.uid === 'string' &&
|
|
444
|
+
Object.keys(value).length === 1) {
|
|
445
|
+
args[key] = elements.shift();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (!window.__dtmcp?.executeTool) {
|
|
449
|
+
throw new Error('No tools found on the page');
|
|
450
|
+
}
|
|
451
|
+
const toolResult = await window.__dtmcp.executeTool(name, args);
|
|
452
|
+
const stashDOMElement = (el) => {
|
|
453
|
+
if (!window.__dtmcp) {
|
|
454
|
+
window.__dtmcp = {};
|
|
455
|
+
}
|
|
456
|
+
if (window.__dtmcp.stashedElements === undefined) {
|
|
457
|
+
window.__dtmcp.stashedElements = [];
|
|
458
|
+
}
|
|
459
|
+
window.__dtmcp.stashedElements.push(el);
|
|
460
|
+
return {
|
|
461
|
+
stashedId: `stashed-${window.__dtmcp.stashedElements.length - 1}`,
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
const ancestors = [];
|
|
465
|
+
// Recursively walks the tool result:
|
|
466
|
+
// - Replaces DOM elements with an ID and stashes the DOM element on the window object
|
|
467
|
+
// - Replaces non-plain objects with a string representation of the object
|
|
468
|
+
// - Replaces circular references with the string '<Circular reference>'
|
|
469
|
+
// - Replaces functions with the string '<Function object>'
|
|
470
|
+
const processToolResult = (data, parentEl) => {
|
|
471
|
+
// 1. Handle DOM Elements
|
|
472
|
+
if (data instanceof Element) {
|
|
473
|
+
return stashDOMElement(data);
|
|
474
|
+
}
|
|
475
|
+
// 2. Handle Arrays
|
|
476
|
+
if (Array.isArray(data)) {
|
|
477
|
+
return data.map((item) => processToolResult(item, parentEl));
|
|
478
|
+
}
|
|
479
|
+
// 3. Handle Objects
|
|
480
|
+
if (data !== null && typeof data === 'object') {
|
|
481
|
+
while (ancestors.length > 0 && ancestors.at(-1) !== parentEl) {
|
|
482
|
+
ancestors.pop();
|
|
483
|
+
}
|
|
484
|
+
if (ancestors.includes(data)) {
|
|
485
|
+
return '<Circular reference>';
|
|
486
|
+
}
|
|
487
|
+
ancestors.push(data);
|
|
488
|
+
// If not a plain object, return a string representation of the object
|
|
489
|
+
if (Object.getPrototypeOf(data) !== Object.prototype) {
|
|
490
|
+
return `<${data.constructor.name} instance>`;
|
|
491
|
+
}
|
|
492
|
+
const processedObj = {};
|
|
493
|
+
for (const [key, value] of Object.entries(data)) {
|
|
494
|
+
processedObj[key] = processToolResult(value, data);
|
|
495
|
+
}
|
|
496
|
+
return processedObj;
|
|
497
|
+
}
|
|
498
|
+
// 4. Handle Functions
|
|
499
|
+
if (typeof data === 'function') {
|
|
500
|
+
return '<Function object>';
|
|
501
|
+
}
|
|
502
|
+
// 5. Return primitives (strings, numbers, booleans) as-is
|
|
503
|
+
return data;
|
|
504
|
+
};
|
|
505
|
+
return {
|
|
506
|
+
result: processToolResult(toolResult),
|
|
507
|
+
stashed: window.__dtmcp?.stashedElements?.length ?? 0,
|
|
508
|
+
};
|
|
509
|
+
}, toolName, params, ...handles);
|
|
510
|
+
const elementHandles = [];
|
|
511
|
+
for (let i = 0; i < (result.stashed ?? 0); i++) {
|
|
512
|
+
const elementHandle = await this.pptrPage.evaluateHandle(index => {
|
|
513
|
+
const el = window.__dtmcp?.stashedElements?.[index];
|
|
514
|
+
if (!el) {
|
|
515
|
+
throw new Error(`Stashed element at index ${index} not found`);
|
|
516
|
+
}
|
|
517
|
+
return el;
|
|
518
|
+
}, i);
|
|
519
|
+
elementHandles.push(elementHandle);
|
|
520
|
+
}
|
|
521
|
+
await this.pptrPage.evaluate(() => {
|
|
522
|
+
if (window.__dtmcp) {
|
|
523
|
+
window.__dtmcp.stashedElements = undefined;
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
if (elementHandles.length) {
|
|
527
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
528
|
+
try {
|
|
529
|
+
const stack = __addDisposableResource(env_2, new DisposableStack(), false);
|
|
530
|
+
for (const handle of this.extraHandles) {
|
|
531
|
+
stack.use(handle);
|
|
532
|
+
}
|
|
533
|
+
this.textSnapshot = await TextSnapshot.create(this, {
|
|
534
|
+
extraHandles: elementHandles,
|
|
535
|
+
});
|
|
536
|
+
response.includeSnapshot();
|
|
537
|
+
}
|
|
538
|
+
catch (e_2) {
|
|
539
|
+
env_2.error = e_2;
|
|
540
|
+
env_2.hasError = true;
|
|
541
|
+
}
|
|
542
|
+
finally {
|
|
543
|
+
__disposeResources(env_2);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const cdpElementIds = await Promise.all(elementHandles.map(async (elementHandle, index) => {
|
|
547
|
+
const backendNodeId = await elementHandle.backendNodeId();
|
|
548
|
+
if (!backendNodeId) {
|
|
549
|
+
logger?.(`No backendNodeId for stashed DOM element with index ${index}`);
|
|
550
|
+
return `stashed-${index}`;
|
|
551
|
+
}
|
|
552
|
+
const cdpElementId = this.textSnapshot?.resolveCdpElementId(backendNodeId);
|
|
553
|
+
if (!cdpElementId) {
|
|
554
|
+
logger?.(`Could not get cdpElementId for backend node ${backendNodeId}`);
|
|
555
|
+
return `stashed-${index}`;
|
|
556
|
+
}
|
|
557
|
+
return cdpElementId;
|
|
558
|
+
}));
|
|
559
|
+
const recursivelyReplaceStashedElements = (node) => {
|
|
560
|
+
if (Array.isArray(node)) {
|
|
561
|
+
return node.map(x => recursivelyReplaceStashedElements(x));
|
|
562
|
+
}
|
|
563
|
+
if (node !== null && typeof node === 'object') {
|
|
564
|
+
if ('stashedId' in node &&
|
|
565
|
+
typeof node.stashedId === 'string' &&
|
|
566
|
+
node.stashedId.startsWith('stashed-') &&
|
|
567
|
+
Object.keys(node).length === 1) {
|
|
568
|
+
const index = parseInt(node.stashedId.split('-')[1]);
|
|
569
|
+
return { uid: cdpElementIds[index] };
|
|
570
|
+
}
|
|
571
|
+
const resultObj = {};
|
|
572
|
+
for (const [key, value] of Object.entries(node)) {
|
|
573
|
+
resultObj[key] = recursivelyReplaceStashedElements(value);
|
|
574
|
+
}
|
|
575
|
+
return resultObj;
|
|
576
|
+
}
|
|
577
|
+
return node;
|
|
578
|
+
};
|
|
579
|
+
const resultWithUids = recursivelyReplaceStashedElements(result.result);
|
|
580
|
+
response.appendResponseLine(JSON.stringify(resultWithUids, null, 2));
|
|
581
|
+
}
|
|
582
|
+
async getElementByUid(uid) {
|
|
583
|
+
if (!this.textSnapshot) {
|
|
584
|
+
throw new Error(`No snapshot found for page ${this.id ?? '?'}. Use take_snapshot to capture one.`);
|
|
585
|
+
}
|
|
586
|
+
const node = this.textSnapshot.idToNode.get(uid);
|
|
587
|
+
if (!node) {
|
|
588
|
+
throw new Error(`Element uid "${uid}" not found on page ${this.id}.`);
|
|
589
|
+
}
|
|
590
|
+
return this.#resolveElementHandle(node, uid);
|
|
591
|
+
}
|
|
592
|
+
async #resolveElementHandle(node, uid) {
|
|
593
|
+
const message = `Element with uid ${uid} no longer exists on the page.`;
|
|
594
|
+
try {
|
|
595
|
+
const handle = await node.elementHandle();
|
|
596
|
+
if (!handle) {
|
|
597
|
+
throw new Error(message);
|
|
598
|
+
}
|
|
599
|
+
return handle;
|
|
600
|
+
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
throw new Error(message, {
|
|
603
|
+
cause: error,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
getAXNodeByUid(uid) {
|
|
608
|
+
return this.textSnapshot?.idToNode.get(uid);
|
|
609
|
+
}
|
|
610
|
+
async resolveBackendNodeId(backendNodeId) {
|
|
611
|
+
if (!this.textSnapshot) {
|
|
612
|
+
this.textSnapshot = await TextSnapshot.create(this);
|
|
613
|
+
}
|
|
614
|
+
let id = this.textSnapshot.resolveCdpElementId(backendNodeId);
|
|
615
|
+
if (!id) {
|
|
616
|
+
this.textSnapshot = await TextSnapshot.create(this);
|
|
617
|
+
id = this.textSnapshot.resolveCdpElementId(backendNodeId);
|
|
618
|
+
}
|
|
619
|
+
return id;
|
|
620
|
+
}
|
|
621
|
+
async resolveUidToBackendNodeId(uid) {
|
|
622
|
+
const target = this.pptrPage.target();
|
|
623
|
+
const targetId = Boolean(target) &&
|
|
624
|
+
'_targetId' in target &&
|
|
625
|
+
typeof target._targetId === 'string'
|
|
626
|
+
? target._targetId
|
|
627
|
+
: undefined;
|
|
628
|
+
const node = this.getAXNodeByUid(uid);
|
|
629
|
+
if (node?.backendNodeId !== undefined) {
|
|
630
|
+
return { backendNodeId: node.backendNodeId, targetId };
|
|
631
|
+
}
|
|
632
|
+
try {
|
|
633
|
+
const handle = await this.getElementByUid(uid);
|
|
634
|
+
const backendNodeId = await handle.backendNodeId();
|
|
635
|
+
return backendNodeId ? { backendNodeId, targetId } : undefined;
|
|
636
|
+
}
|
|
637
|
+
catch {
|
|
638
|
+
return undefined;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
async getMatchedStylesForUid(uid) {
|
|
642
|
+
if (!this.textSnapshot) {
|
|
643
|
+
throw new Error(`No snapshot found for page ${this.id ?? '?'}. Use take_snapshot to capture one.`);
|
|
644
|
+
}
|
|
645
|
+
const node = this.textSnapshot.idToNode.get(uid);
|
|
646
|
+
if (!node) {
|
|
647
|
+
throw new Error(`Element uid "${uid}" not found on page ${this.id}.`);
|
|
648
|
+
}
|
|
649
|
+
const backendNodeId = node.backendNodeId;
|
|
650
|
+
if (!isBackendNodeId(backendNodeId)) {
|
|
651
|
+
throw new Error(`Failed to resolve backend node ID for element with uid "${uid}".`);
|
|
652
|
+
}
|
|
653
|
+
if (!this.#devtoolsUniverse) {
|
|
654
|
+
throw new Error(`DevTools universe is not available for page ${this.id ?? '?'}.`);
|
|
655
|
+
}
|
|
656
|
+
const targetManager = this.#devtoolsUniverse.universe.context.get(DevTools.TargetManager);
|
|
657
|
+
let domNode;
|
|
658
|
+
let cssModel = null;
|
|
659
|
+
for (const dom of targetManager.models(DevTools.DOMModel.DOMModel)) {
|
|
660
|
+
const nodeMap = await dom.pushNodesByBackendIdsToFrontend(new Set([backendNodeId]));
|
|
661
|
+
const frontendNode = nodeMap?.get(backendNodeId);
|
|
662
|
+
if (frontendNode) {
|
|
663
|
+
domNode = frontendNode;
|
|
664
|
+
cssModel = dom.target().model(DevTools.CSSModel.CSSModel);
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (!domNode || !cssModel) {
|
|
669
|
+
throw new Error(`Element with uid "${uid}" was detached or no longer exists on the page. Please take a new snapshot with take_snapshot.`);
|
|
670
|
+
}
|
|
671
|
+
const targetElement = domNode.enclosingElementOrSelf();
|
|
672
|
+
if (!targetElement) {
|
|
673
|
+
throw new Error(`Element with uid "${uid}" is not an element node and has no parent element.`);
|
|
674
|
+
}
|
|
675
|
+
const matchedStyles = await cssModel.getMatchedStyles(targetElement.id);
|
|
676
|
+
if (!matchedStyles) {
|
|
677
|
+
throw new Error(`Could not retrieve matched styles for element with uid "${uid}".`);
|
|
678
|
+
}
|
|
679
|
+
return matchedStyles;
|
|
680
|
+
}
|
|
681
|
+
async getDevToolsData() {
|
|
682
|
+
try {
|
|
683
|
+
logger?.('Getting DevTools UI data');
|
|
684
|
+
const devtoolsPage = await this.getDevToolsPage();
|
|
685
|
+
if (!devtoolsPage) {
|
|
686
|
+
logger?.('No DevTools page detected');
|
|
687
|
+
return {};
|
|
688
|
+
}
|
|
689
|
+
const { cdpRequestId, cdpBackendNodeId } = await devtoolsPage.evaluate(async () => {
|
|
690
|
+
// @ts-expect-error no types
|
|
691
|
+
const UI = await import('/bundled/ui/legacy/legacy.js');
|
|
692
|
+
// @ts-expect-error no types
|
|
693
|
+
const SDK = await import('/bundled/core/sdk/sdk.js');
|
|
694
|
+
const request = UI.Context.Context.instance().flavor(SDK.NetworkRequest.NetworkRequest);
|
|
695
|
+
const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
|
|
696
|
+
return {
|
|
697
|
+
cdpRequestId: request?.requestId(),
|
|
698
|
+
cdpBackendNodeId: node?.backendNodeId(),
|
|
699
|
+
};
|
|
700
|
+
});
|
|
701
|
+
return { cdpBackendNodeId, cdpRequestId };
|
|
702
|
+
}
|
|
703
|
+
catch (err) {
|
|
704
|
+
logger?.('error getting devtools data', err);
|
|
705
|
+
}
|
|
706
|
+
return {};
|
|
707
|
+
}
|
|
708
|
+
async restoreEmulation() {
|
|
709
|
+
const currentSetting = this.emulationSettings;
|
|
710
|
+
await this.emulate(currentSetting);
|
|
711
|
+
}
|
|
712
|
+
async emulate(options) {
|
|
713
|
+
const page = this.pptrPage;
|
|
714
|
+
const newSettings = { ...this.emulationSettings };
|
|
715
|
+
// Skip network emulation if blocklist/allowlist is configured, as it conflicts with blocking rules in Puppeteer.
|
|
716
|
+
if (this.#hasNetworkBlockOrAllowlist) {
|
|
717
|
+
if (options.networkConditions !== undefined) {
|
|
718
|
+
throw new Error('Network throttling is not supported when network blocking (allowlist/blocklist) is configured.');
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
else if (!options.networkConditions) {
|
|
722
|
+
await page.emulateNetworkConditions(null);
|
|
723
|
+
delete newSettings.networkConditions;
|
|
724
|
+
}
|
|
725
|
+
else if (options.networkConditions === 'Offline') {
|
|
726
|
+
await page.emulateNetworkConditions({
|
|
727
|
+
offline: true,
|
|
728
|
+
download: 0,
|
|
729
|
+
upload: 0,
|
|
730
|
+
latency: 0,
|
|
731
|
+
});
|
|
732
|
+
newSettings.networkConditions = 'Offline';
|
|
733
|
+
}
|
|
734
|
+
else if (options.networkConditions in PredefinedNetworkConditions) {
|
|
735
|
+
const networkCondition = PredefinedNetworkConditions[options.networkConditions];
|
|
736
|
+
await page.emulateNetworkConditions(networkCondition);
|
|
737
|
+
newSettings.networkConditions = options.networkConditions;
|
|
738
|
+
}
|
|
739
|
+
const secondarySession = this.devtoolsUniverse?.session;
|
|
740
|
+
if (!options.cpuThrottlingRate) {
|
|
741
|
+
await page.emulateCPUThrottling(1);
|
|
742
|
+
if (secondarySession) {
|
|
743
|
+
await secondarySession.send('Emulation.setCPUThrottlingRate', {
|
|
744
|
+
rate: 1,
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
delete newSettings.cpuThrottlingRate;
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
await page.emulateCPUThrottling(options.cpuThrottlingRate);
|
|
751
|
+
if (secondarySession) {
|
|
752
|
+
await secondarySession.send('Emulation.setCPUThrottlingRate', {
|
|
753
|
+
rate: options.cpuThrottlingRate,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
newSettings.cpuThrottlingRate = options.cpuThrottlingRate;
|
|
757
|
+
}
|
|
758
|
+
if (!options.geolocation) {
|
|
759
|
+
await page.setGeolocation({ latitude: 0, longitude: 0 });
|
|
760
|
+
delete newSettings.geolocation;
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
await page.setGeolocation(options.geolocation);
|
|
764
|
+
newSettings.geolocation = options.geolocation;
|
|
765
|
+
}
|
|
766
|
+
if (!options.userAgent) {
|
|
767
|
+
await page.setUserAgent({ userAgent: undefined });
|
|
768
|
+
delete newSettings.userAgent;
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
await page.setUserAgent({ userAgent: options.userAgent });
|
|
772
|
+
newSettings.userAgent = options.userAgent;
|
|
773
|
+
}
|
|
774
|
+
if (!options.colorScheme || options.colorScheme === 'auto') {
|
|
775
|
+
await page.emulateMediaFeatures([
|
|
776
|
+
{ name: 'prefers-color-scheme', value: '' },
|
|
777
|
+
]);
|
|
778
|
+
delete newSettings.colorScheme;
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
await page.emulateMediaFeatures([
|
|
782
|
+
{ name: 'prefers-color-scheme', value: options.colorScheme },
|
|
783
|
+
]);
|
|
784
|
+
newSettings.colorScheme = options.colorScheme;
|
|
785
|
+
}
|
|
786
|
+
if (!options.viewport) {
|
|
787
|
+
delete newSettings.viewport;
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
const defaults = {
|
|
791
|
+
deviceScaleFactor: 1,
|
|
792
|
+
isMobile: false,
|
|
793
|
+
hasTouch: false,
|
|
794
|
+
isLandscape: false,
|
|
795
|
+
};
|
|
796
|
+
newSettings.viewport = { ...defaults, ...options.viewport };
|
|
797
|
+
}
|
|
798
|
+
if (options.extraHttpHeaders !== undefined) {
|
|
799
|
+
await page.setExtraHTTPHeaders(options.extraHttpHeaders);
|
|
800
|
+
newSettings.extraHttpHeaders = options.extraHttpHeaders;
|
|
801
|
+
if (Object.keys(options.extraHttpHeaders).length === 0) {
|
|
802
|
+
delete newSettings.extraHttpHeaders;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
this.emulationSettings = Object.keys(newSettings).length ? newSettings : {};
|
|
806
|
+
this.updateTimeouts();
|
|
807
|
+
// This should happen after updating the page timeouts.
|
|
808
|
+
// Setting the viewport can trigger a reload which we don't want to timeout.
|
|
809
|
+
await page.setViewport(newSettings.viewport ?? null);
|
|
810
|
+
}
|
|
811
|
+
updateTimeouts() {
|
|
812
|
+
// For waiters 5sec timeout should be sufficient.
|
|
813
|
+
// Increased in case we throttle the CPU
|
|
814
|
+
const cpuMultiplier = this.cpuThrottlingRate;
|
|
815
|
+
this.pptrPage.setDefaultTimeout(DEFAULT_TIMEOUT * cpuMultiplier);
|
|
816
|
+
// 10sec should be enough for the load event to be emitted during
|
|
817
|
+
// navigations.
|
|
818
|
+
// Increased in case we throttle the network requests or the CPU
|
|
819
|
+
const networkMultiplier = getNetworkMultiplierFromString(this.networkConditions);
|
|
820
|
+
this.pptrPage.setDefaultNavigationTimeout(this.#navigationTimeout * networkMultiplier * cpuMultiplier);
|
|
821
|
+
}
|
|
822
|
+
waitForTextOnPage(text, timeout) {
|
|
823
|
+
const frames = this.pptrPage.frames();
|
|
824
|
+
let locator = this.#locatorClass.race(frames.flatMap(frame => text.flatMap(value => [
|
|
825
|
+
frame.locator(`aria/${value}`),
|
|
826
|
+
frame.locator(`text/${value}`),
|
|
827
|
+
])));
|
|
828
|
+
if (timeout) {
|
|
829
|
+
locator = locator.setTimeout(timeout);
|
|
830
|
+
}
|
|
831
|
+
return locator.wait();
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* We need to ignore favicon request as they make our test flaky
|
|
835
|
+
*/
|
|
836
|
+
async setUpNetworkCollectorForTesting() {
|
|
837
|
+
this.networkCollector.dispose();
|
|
838
|
+
this.networkCollector = new NetworkCollector(this.pptrPage, undefined, collect => {
|
|
839
|
+
return {
|
|
840
|
+
request: req => {
|
|
841
|
+
if (req.url().includes('favicon.ico')) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
collect(req);
|
|
845
|
+
},
|
|
846
|
+
};
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
//# sourceMappingURL=McpPage.js.map
|