chrome-devtools-mcp 0.17.1 → 0.17.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/README.md +4 -1
- package/package.json +3 -3
- package/build/src/DevToolsConnectionAdapter.js +0 -69
- package/build/src/DevtoolsUtils.js +0 -340
- package/build/src/McpContext.js +0 -603
- package/build/src/McpResponse.js +0 -490
- package/build/src/Mutex.js +0 -37
- package/build/src/PageCollector.js +0 -309
- package/build/src/WaitForHelper.js +0 -139
- package/build/src/browser.js +0 -182
- package/build/src/cli.js +0 -295
- package/build/src/formatters/ConsoleFormatter.js +0 -240
- package/build/src/formatters/IssueFormatter.js +0 -190
- package/build/src/formatters/NetworkFormatter.js +0 -226
- package/build/src/formatters/SnapshotFormatter.js +0 -134
- package/build/src/index.js +0 -21
- package/build/src/issue-descriptions.js +0 -39
- package/build/src/logger.js +0 -36
- package/build/src/main.js +0 -196
- package/build/src/polyfill.js +0 -7
- package/build/src/telemetry/ClearcutLogger.js +0 -102
- package/build/src/telemetry/WatchdogClient.js +0 -60
- package/build/src/telemetry/flagUtils.js +0 -45
- package/build/src/telemetry/metricUtils.js +0 -14
- package/build/src/telemetry/persistence.js +0 -53
- package/build/src/telemetry/types.js +0 -33
- package/build/src/telemetry/watchdog/ClearcutSender.js +0 -201
- package/build/src/telemetry/watchdog/main.js +0 -127
- package/build/src/third_party/THIRD_PARTY_NOTICES +0 -2011
- package/build/src/third_party/bundled-packages.json +0 -8
- package/build/src/third_party/devtools-formatter-worker.js +0 -15449
- package/build/src/third_party/index.js +0 -172791
- package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +0 -4
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +0 -8
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +0 -18
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +0 -7
- package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +0 -10
- package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +0 -5
- package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +0 -5
- package/build/src/third_party/issue-descriptions/LowTextContrast.md +0 -5
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +0 -9
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +0 -9
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +0 -8
- package/build/src/third_party/issue-descriptions/arInsecureContext.md +0 -7
- package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
- package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +0 -4
- package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +0 -18
- package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +0 -19
- package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +0 -8
- package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +0 -9
- package/build/src/third_party/issue-descriptions/arSourceIgnored.md +0 -13
- package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +0 -12
- package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +0 -10
- package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +0 -11
- package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +0 -3
- package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +0 -4
- package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +0 -4
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +0 -12
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +0 -12
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +0 -8
- package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +0 -12
- package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +0 -7
- package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +0 -10
- package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +0 -12
- package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +0 -4
- package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +0 -11
- package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +0 -8
- package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +0 -7
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +0 -6
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +0 -6
- package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +0 -11
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +0 -4
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +0 -4
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +0 -6
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +0 -6
- package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +0 -6
- package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +0 -7
- package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +0 -7
- package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +0 -5
- package/build/src/third_party/issue-descriptions/corsInsecurePrivateNetwork.md +0 -10
- package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +0 -7
- package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +0 -19
- package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +0 -5
- package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +0 -5
- package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +0 -6
- package/build/src/third_party/issue-descriptions/corsPreflightAllowPrivateNetworkError.md +0 -10
- package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +0 -5
- package/build/src/third_party/issue-descriptions/corsPrivateNetworkPermissionDenied.md +0 -10
- package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +0 -5
- package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +0 -8
- package/build/src/third_party/issue-descriptions/cspEvalViolation.md +0 -9
- package/build/src/third_party/issue-descriptions/cspInlineViolation.md +0 -10
- package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +0 -5
- package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +0 -8
- package/build/src/third_party/issue-descriptions/cspURLViolation.md +0 -10
- package/build/src/third_party/issue-descriptions/deprecation.md +0 -3
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +0 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +0 -1
- package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +0 -7
- package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +0 -8
- package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +0 -5
- package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +0 -7
- package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +0 -4
- package/build/src/third_party/issue-descriptions/heavyAd.md +0 -10
- package/build/src/third_party/issue-descriptions/mixedContent.md +0 -5
- package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +0 -7
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +0 -9
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +0 -9
- package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +0 -6
- package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +0 -6
- package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +0 -5
- package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +0 -7
- package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +0 -3
- package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +0 -3
- package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +0 -7
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +0 -7
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +0 -7
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +0 -3
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +0 -3
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +0 -3
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +0 -7
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +0 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +0 -3
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +0 -3
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +0 -14
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +0 -15
- package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +0 -8
- package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +0 -7
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +0 -11
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +0 -14
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +0 -15
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +0 -8
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +0 -13
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +0 -4
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +0 -5
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +0 -11
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +0 -12
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +0 -6
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +0 -8
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +0 -11
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +0 -11
- package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +0 -10
- package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +0 -5
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +0 -6
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +0 -11
- package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +0 -4
- package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +0 -3
- package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +0 -3
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +0 -12
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +0 -17
- package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +0 -14
- package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +0 -15
- package/build/src/tools/ToolDefinition.js +0 -20
- package/build/src/tools/categories.js +0 -24
- package/build/src/tools/console.js +0 -85
- package/build/src/tools/emulation.js +0 -169
- package/build/src/tools/extensions.js +0 -79
- package/build/src/tools/input.js +0 -343
- package/build/src/tools/network.js +0 -120
- package/build/src/tools/pages.js +0 -323
- package/build/src/tools/performance.js +0 -188
- package/build/src/tools/screenshot.js +0 -84
- package/build/src/tools/script.js +0 -71
- package/build/src/tools/snapshot.js +0 -52
- package/build/src/tools/tools.js +0 -31
- package/build/src/trace-processing/parse.js +0 -84
- package/build/src/utils/ExtensionRegistry.js +0 -35
- package/build/src/utils/keyboard.js +0 -296
- package/build/src/utils/pagination.js +0 -49
- package/build/src/utils/string.js +0 -36
- package/build/src/utils/types.js +0 -6
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import { FakeIssuesManager } from './DevtoolsUtils.js';
|
|
7
|
-
import { logger } from './logger.js';
|
|
8
|
-
import { DevTools } from './third_party/index.js';
|
|
9
|
-
export class UncaughtError {
|
|
10
|
-
details;
|
|
11
|
-
targetId;
|
|
12
|
-
constructor(details, targetId) {
|
|
13
|
-
this.details = details;
|
|
14
|
-
this.targetId = targetId;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function createIdGenerator() {
|
|
18
|
-
let i = 1;
|
|
19
|
-
return () => {
|
|
20
|
-
if (i === Number.MAX_SAFE_INTEGER) {
|
|
21
|
-
i = 0;
|
|
22
|
-
}
|
|
23
|
-
return i++;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
export const stableIdSymbol = Symbol('stableIdSymbol');
|
|
27
|
-
export class PageCollector {
|
|
28
|
-
#browser;
|
|
29
|
-
#listenersInitializer;
|
|
30
|
-
#listeners = new WeakMap();
|
|
31
|
-
#maxNavigationSaved = 3;
|
|
32
|
-
/**
|
|
33
|
-
* This maps a Page to a list of navigations with a sub-list
|
|
34
|
-
* of all collected resources.
|
|
35
|
-
* The newer navigations come first.
|
|
36
|
-
*/
|
|
37
|
-
storage = new WeakMap();
|
|
38
|
-
constructor(browser, listeners) {
|
|
39
|
-
this.#browser = browser;
|
|
40
|
-
this.#listenersInitializer = listeners;
|
|
41
|
-
}
|
|
42
|
-
async init(pages) {
|
|
43
|
-
for (const page of pages) {
|
|
44
|
-
this.addPage(page);
|
|
45
|
-
}
|
|
46
|
-
this.#browser.on('targetcreated', this.#onTargetCreated);
|
|
47
|
-
this.#browser.on('targetdestroyed', this.#onTargetDestroyed);
|
|
48
|
-
}
|
|
49
|
-
dispose() {
|
|
50
|
-
this.#browser.off('targetcreated', this.#onTargetCreated);
|
|
51
|
-
this.#browser.off('targetdestroyed', this.#onTargetDestroyed);
|
|
52
|
-
}
|
|
53
|
-
#onTargetCreated = async (target) => {
|
|
54
|
-
try {
|
|
55
|
-
const page = await target.page();
|
|
56
|
-
if (!page) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
this.addPage(page);
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
logger('Error getting a page for a target onTargetCreated', err);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
#onTargetDestroyed = async (target) => {
|
|
66
|
-
try {
|
|
67
|
-
const page = await target.page();
|
|
68
|
-
if (!page) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
this.cleanupPageDestroyed(page);
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
logger('Error getting a page for a target onTargetDestroyed', err);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
addPage(page) {
|
|
78
|
-
this.#initializePage(page);
|
|
79
|
-
}
|
|
80
|
-
#initializePage(page) {
|
|
81
|
-
if (this.storage.has(page)) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const idGenerator = createIdGenerator();
|
|
85
|
-
const storedLists = [[]];
|
|
86
|
-
this.storage.set(page, storedLists);
|
|
87
|
-
const listeners = this.#listenersInitializer(value => {
|
|
88
|
-
const withId = value;
|
|
89
|
-
withId[stableIdSymbol] = idGenerator();
|
|
90
|
-
const navigations = this.storage.get(page) ?? [[]];
|
|
91
|
-
navigations[0].push(withId);
|
|
92
|
-
});
|
|
93
|
-
listeners['framenavigated'] = (frame) => {
|
|
94
|
-
// Only split the storage on main frame navigation
|
|
95
|
-
if (frame !== page.mainFrame()) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
this.splitAfterNavigation(page);
|
|
99
|
-
};
|
|
100
|
-
for (const [name, listener] of Object.entries(listeners)) {
|
|
101
|
-
page.on(name, listener);
|
|
102
|
-
}
|
|
103
|
-
this.#listeners.set(page, listeners);
|
|
104
|
-
}
|
|
105
|
-
splitAfterNavigation(page) {
|
|
106
|
-
const navigations = this.storage.get(page);
|
|
107
|
-
if (!navigations) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
// Add the latest navigation first
|
|
111
|
-
navigations.unshift([]);
|
|
112
|
-
navigations.splice(this.#maxNavigationSaved);
|
|
113
|
-
}
|
|
114
|
-
cleanupPageDestroyed(page) {
|
|
115
|
-
const listeners = this.#listeners.get(page);
|
|
116
|
-
if (listeners) {
|
|
117
|
-
for (const [name, listener] of Object.entries(listeners)) {
|
|
118
|
-
page.off(name, listener);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
this.storage.delete(page);
|
|
122
|
-
}
|
|
123
|
-
getData(page, includePreservedData) {
|
|
124
|
-
const navigations = this.storage.get(page);
|
|
125
|
-
if (!navigations) {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
if (!includePreservedData) {
|
|
129
|
-
return navigations[0];
|
|
130
|
-
}
|
|
131
|
-
const data = [];
|
|
132
|
-
for (let index = this.#maxNavigationSaved; index >= 0; index--) {
|
|
133
|
-
if (navigations[index]) {
|
|
134
|
-
data.push(...navigations[index]);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return data;
|
|
138
|
-
}
|
|
139
|
-
getIdForResource(resource) {
|
|
140
|
-
return resource[stableIdSymbol] ?? -1;
|
|
141
|
-
}
|
|
142
|
-
getById(page, stableId) {
|
|
143
|
-
const navigations = this.storage.get(page);
|
|
144
|
-
if (!navigations) {
|
|
145
|
-
throw new Error('No requests found for selected page');
|
|
146
|
-
}
|
|
147
|
-
const item = this.find(page, item => item[stableIdSymbol] === stableId);
|
|
148
|
-
if (item) {
|
|
149
|
-
return item;
|
|
150
|
-
}
|
|
151
|
-
throw new Error('Request not found for selected page');
|
|
152
|
-
}
|
|
153
|
-
find(page, filter) {
|
|
154
|
-
const navigations = this.storage.get(page);
|
|
155
|
-
if (!navigations) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
for (const navigation of navigations) {
|
|
159
|
-
const item = navigation.find(filter);
|
|
160
|
-
if (item) {
|
|
161
|
-
return item;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
export class ConsoleCollector extends PageCollector {
|
|
168
|
-
#subscribedPages = new WeakMap();
|
|
169
|
-
addPage(page) {
|
|
170
|
-
super.addPage(page);
|
|
171
|
-
if (!this.#subscribedPages.has(page)) {
|
|
172
|
-
const subscriber = new PageEventSubscriber(page);
|
|
173
|
-
this.#subscribedPages.set(page, subscriber);
|
|
174
|
-
void subscriber.subscribe();
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
cleanupPageDestroyed(page) {
|
|
178
|
-
super.cleanupPageDestroyed(page);
|
|
179
|
-
this.#subscribedPages.get(page)?.unsubscribe();
|
|
180
|
-
this.#subscribedPages.delete(page);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
class PageEventSubscriber {
|
|
184
|
-
#issueManager = new FakeIssuesManager();
|
|
185
|
-
#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
|
|
186
|
-
#seenKeys = new Set();
|
|
187
|
-
#seenIssues = new Set();
|
|
188
|
-
#page;
|
|
189
|
-
#session;
|
|
190
|
-
#targetId;
|
|
191
|
-
constructor(page) {
|
|
192
|
-
this.#page = page;
|
|
193
|
-
// @ts-expect-error use existing CDP client (internal Puppeteer API).
|
|
194
|
-
this.#session = this.#page._client();
|
|
195
|
-
// @ts-expect-error use internal Puppeteer API to get target ID
|
|
196
|
-
this.#targetId = this.#session.target()._targetId;
|
|
197
|
-
}
|
|
198
|
-
#resetIssueAggregator() {
|
|
199
|
-
this.#issueManager = new FakeIssuesManager();
|
|
200
|
-
if (this.#issueAggregator) {
|
|
201
|
-
this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedissue);
|
|
202
|
-
}
|
|
203
|
-
this.#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
|
|
204
|
-
this.#issueAggregator.addEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedissue);
|
|
205
|
-
}
|
|
206
|
-
async subscribe() {
|
|
207
|
-
this.#resetIssueAggregator();
|
|
208
|
-
this.#page.on('framenavigated', this.#onFrameNavigated);
|
|
209
|
-
this.#session.on('Audits.issueAdded', this.#onIssueAdded);
|
|
210
|
-
this.#session.on('Runtime.exceptionThrown', this.#onExceptionThrown);
|
|
211
|
-
try {
|
|
212
|
-
await this.#session.send('Audits.enable');
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
logger('Error subscribing to issues', error);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
unsubscribe() {
|
|
219
|
-
this.#seenKeys.clear();
|
|
220
|
-
this.#seenIssues.clear();
|
|
221
|
-
this.#page.off('framenavigated', this.#onFrameNavigated);
|
|
222
|
-
this.#session.off('Audits.issueAdded', this.#onIssueAdded);
|
|
223
|
-
this.#session.off('Runtime.exceptionThrown', this.#onExceptionThrown);
|
|
224
|
-
if (this.#issueAggregator) {
|
|
225
|
-
this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedissue);
|
|
226
|
-
}
|
|
227
|
-
void this.#session.send('Audits.disable').catch(() => {
|
|
228
|
-
// might fail.
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
#onAggregatedissue = (event) => {
|
|
232
|
-
if (this.#seenIssues.has(event.data)) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
this.#seenIssues.add(event.data);
|
|
236
|
-
this.#page.emit('issue', event.data);
|
|
237
|
-
};
|
|
238
|
-
#onExceptionThrown = (event) => {
|
|
239
|
-
this.#page.emit('uncaughtError', new UncaughtError(event.exceptionDetails, this.#targetId));
|
|
240
|
-
};
|
|
241
|
-
// On navigation, we reset issue aggregation.
|
|
242
|
-
#onFrameNavigated = (frame) => {
|
|
243
|
-
// Only split the storage on main frame navigation
|
|
244
|
-
if (frame !== frame.page().mainFrame()) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
this.#seenKeys.clear();
|
|
248
|
-
this.#seenIssues.clear();
|
|
249
|
-
this.#resetIssueAggregator();
|
|
250
|
-
};
|
|
251
|
-
#onIssueAdded = (data) => {
|
|
252
|
-
try {
|
|
253
|
-
const inspectorIssue = data.issue;
|
|
254
|
-
const issue = DevTools.createIssuesFromProtocolIssue(null,
|
|
255
|
-
// @ts-expect-error Protocol types diverge.
|
|
256
|
-
inspectorIssue)[0];
|
|
257
|
-
if (!issue) {
|
|
258
|
-
logger('No issue mapping for for the issue: ', inspectorIssue.code);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
const primaryKey = issue.primaryKey();
|
|
262
|
-
if (this.#seenKeys.has(primaryKey)) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
this.#seenKeys.add(primaryKey);
|
|
266
|
-
this.#issueManager.dispatchEventToListeners("IssueAdded" /* DevTools.IssuesManagerEvents.ISSUE_ADDED */, {
|
|
267
|
-
issue,
|
|
268
|
-
// @ts-expect-error We don't care that issues model is null
|
|
269
|
-
issuesModel: null,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
catch (error) {
|
|
273
|
-
logger('Error creating a new issue', error);
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
export class NetworkCollector extends PageCollector {
|
|
278
|
-
constructor(browser, listeners = collect => {
|
|
279
|
-
return {
|
|
280
|
-
request: req => {
|
|
281
|
-
collect(req);
|
|
282
|
-
},
|
|
283
|
-
};
|
|
284
|
-
}) {
|
|
285
|
-
super(browser, listeners);
|
|
286
|
-
}
|
|
287
|
-
splitAfterNavigation(page) {
|
|
288
|
-
const navigations = this.storage.get(page) ?? [];
|
|
289
|
-
if (!navigations) {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
const requests = navigations[0];
|
|
293
|
-
const lastRequestIdx = requests.findLastIndex(request => {
|
|
294
|
-
return request.frame() === page.mainFrame()
|
|
295
|
-
? request.isNavigationRequest()
|
|
296
|
-
: false;
|
|
297
|
-
});
|
|
298
|
-
// Keep all requests since the last navigation request including that
|
|
299
|
-
// navigation request itself.
|
|
300
|
-
// Keep the reference
|
|
301
|
-
if (lastRequestIdx !== -1) {
|
|
302
|
-
const fromCurrentNavigation = requests.splice(lastRequestIdx);
|
|
303
|
-
navigations.unshift(fromCurrentNavigation);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
navigations.unshift([]);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import { logger } from './logger.js';
|
|
7
|
-
export class WaitForHelper {
|
|
8
|
-
#abortController = new AbortController();
|
|
9
|
-
#page;
|
|
10
|
-
#stableDomTimeout;
|
|
11
|
-
#stableDomFor;
|
|
12
|
-
#expectNavigationIn;
|
|
13
|
-
#navigationTimeout;
|
|
14
|
-
constructor(page, cpuTimeoutMultiplier, networkTimeoutMultiplier) {
|
|
15
|
-
this.#stableDomTimeout = 3000 * cpuTimeoutMultiplier;
|
|
16
|
-
this.#stableDomFor = 100 * cpuTimeoutMultiplier;
|
|
17
|
-
this.#expectNavigationIn = 100 * cpuTimeoutMultiplier;
|
|
18
|
-
this.#navigationTimeout = 3000 * networkTimeoutMultiplier;
|
|
19
|
-
this.#page = page;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* A wrapper that executes a action and waits for
|
|
23
|
-
* a potential navigation, after which it waits
|
|
24
|
-
* for the DOM to be stable before returning.
|
|
25
|
-
*/
|
|
26
|
-
async waitForStableDom() {
|
|
27
|
-
const stableDomObserver = await this.#page.evaluateHandle(timeout => {
|
|
28
|
-
let timeoutId;
|
|
29
|
-
function callback() {
|
|
30
|
-
clearTimeout(timeoutId);
|
|
31
|
-
timeoutId = setTimeout(() => {
|
|
32
|
-
domObserver.resolver.resolve();
|
|
33
|
-
domObserver.observer.disconnect();
|
|
34
|
-
}, timeout);
|
|
35
|
-
}
|
|
36
|
-
const domObserver = {
|
|
37
|
-
resolver: Promise.withResolvers(),
|
|
38
|
-
observer: new MutationObserver(callback),
|
|
39
|
-
};
|
|
40
|
-
// It's possible that the DOM is not gonna change so we
|
|
41
|
-
// need to start the timeout initially.
|
|
42
|
-
callback();
|
|
43
|
-
domObserver.observer.observe(document.body, {
|
|
44
|
-
childList: true,
|
|
45
|
-
subtree: true,
|
|
46
|
-
attributes: true,
|
|
47
|
-
});
|
|
48
|
-
return domObserver;
|
|
49
|
-
}, this.#stableDomFor);
|
|
50
|
-
this.#abortController.signal.addEventListener('abort', async () => {
|
|
51
|
-
try {
|
|
52
|
-
await stableDomObserver.evaluate(observer => {
|
|
53
|
-
observer.observer.disconnect();
|
|
54
|
-
observer.resolver.resolve();
|
|
55
|
-
});
|
|
56
|
-
await stableDomObserver.dispose();
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Ignored cleanup errors
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
return Promise.race([
|
|
63
|
-
stableDomObserver.evaluate(async (observer) => {
|
|
64
|
-
return await observer.resolver.promise;
|
|
65
|
-
}),
|
|
66
|
-
this.timeout(this.#stableDomTimeout).then(() => {
|
|
67
|
-
throw new Error('Timeout');
|
|
68
|
-
}),
|
|
69
|
-
]);
|
|
70
|
-
}
|
|
71
|
-
async waitForNavigationStarted() {
|
|
72
|
-
// Currently Puppeteer does not have API
|
|
73
|
-
// For when a navigation is about to start
|
|
74
|
-
const navigationStartedPromise = new Promise(resolve => {
|
|
75
|
-
const listener = (event) => {
|
|
76
|
-
if ([
|
|
77
|
-
'historySameDocument',
|
|
78
|
-
'historyDifferentDocument',
|
|
79
|
-
'sameDocument',
|
|
80
|
-
].includes(event.navigationType)) {
|
|
81
|
-
resolve(false);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
resolve(true);
|
|
85
|
-
};
|
|
86
|
-
this.#page._client().on('Page.frameStartedNavigating', listener);
|
|
87
|
-
this.#abortController.signal.addEventListener('abort', () => {
|
|
88
|
-
resolve(false);
|
|
89
|
-
this.#page._client().off('Page.frameStartedNavigating', listener);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
return await Promise.race([
|
|
93
|
-
navigationStartedPromise,
|
|
94
|
-
this.timeout(this.#expectNavigationIn).then(() => false),
|
|
95
|
-
]);
|
|
96
|
-
}
|
|
97
|
-
timeout(time) {
|
|
98
|
-
return new Promise(res => {
|
|
99
|
-
const id = setTimeout(res, time);
|
|
100
|
-
this.#abortController.signal.addEventListener('abort', () => {
|
|
101
|
-
res();
|
|
102
|
-
clearTimeout(id);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
async waitForEventsAfterAction(action, options) {
|
|
107
|
-
const navigationFinished = this.waitForNavigationStarted()
|
|
108
|
-
.then(navigationStated => {
|
|
109
|
-
if (navigationStated) {
|
|
110
|
-
return this.#page.waitForNavigation({
|
|
111
|
-
timeout: options?.timeout ?? this.#navigationTimeout,
|
|
112
|
-
signal: this.#abortController.signal,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
return;
|
|
116
|
-
})
|
|
117
|
-
.catch(error => logger(error));
|
|
118
|
-
try {
|
|
119
|
-
await action();
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
// Clear up pending promises
|
|
123
|
-
this.#abortController.abort();
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
try {
|
|
127
|
-
await navigationFinished;
|
|
128
|
-
// Wait for stable dom after navigation so we execute in
|
|
129
|
-
// the correct context
|
|
130
|
-
await this.waitForStableDom();
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
logger(error);
|
|
134
|
-
}
|
|
135
|
-
finally {
|
|
136
|
-
this.#abortController.abort();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
package/build/src/browser.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import os from 'node:os';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import { logger } from './logger.js';
|
|
10
|
-
import { puppeteer } from './third_party/index.js';
|
|
11
|
-
let browser;
|
|
12
|
-
function makeTargetFilter() {
|
|
13
|
-
const ignoredPrefixes = new Set([
|
|
14
|
-
'chrome://',
|
|
15
|
-
'chrome-extension://',
|
|
16
|
-
'chrome-untrusted://',
|
|
17
|
-
]);
|
|
18
|
-
return function targetFilter(target) {
|
|
19
|
-
if (target.url() === 'chrome://newtab/') {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
// Could be the only page opened in the browser.
|
|
23
|
-
if (target.url().startsWith('chrome://inspect')) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
for (const prefix of ignoredPrefixes) {
|
|
27
|
-
if (target.url().startsWith(prefix)) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return true;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export async function ensureBrowserConnected(options) {
|
|
35
|
-
const { channel } = options;
|
|
36
|
-
if (browser?.connected) {
|
|
37
|
-
return browser;
|
|
38
|
-
}
|
|
39
|
-
const connectOptions = {
|
|
40
|
-
targetFilter: makeTargetFilter(),
|
|
41
|
-
defaultViewport: null,
|
|
42
|
-
handleDevToolsAsPage: true,
|
|
43
|
-
};
|
|
44
|
-
if (options.wsEndpoint) {
|
|
45
|
-
connectOptions.browserWSEndpoint = options.wsEndpoint;
|
|
46
|
-
if (options.wsHeaders) {
|
|
47
|
-
connectOptions.headers = options.wsHeaders;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else if (options.browserURL) {
|
|
51
|
-
connectOptions.browserURL = options.browserURL;
|
|
52
|
-
}
|
|
53
|
-
else if (channel || options.userDataDir) {
|
|
54
|
-
const userDataDir = options.userDataDir;
|
|
55
|
-
if (userDataDir) {
|
|
56
|
-
// TODO: re-expose this logic via Puppeteer.
|
|
57
|
-
const portPath = path.join(userDataDir, 'DevToolsActivePort');
|
|
58
|
-
try {
|
|
59
|
-
const fileContent = await fs.promises.readFile(portPath, 'utf8');
|
|
60
|
-
const [rawPort, rawPath] = fileContent
|
|
61
|
-
.split('\n')
|
|
62
|
-
.map(line => {
|
|
63
|
-
return line.trim();
|
|
64
|
-
})
|
|
65
|
-
.filter(line => {
|
|
66
|
-
return !!line;
|
|
67
|
-
});
|
|
68
|
-
if (!rawPort || !rawPath) {
|
|
69
|
-
throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
|
|
70
|
-
}
|
|
71
|
-
const port = parseInt(rawPort, 10);
|
|
72
|
-
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
73
|
-
throw new Error(`Invalid port '${rawPort}' found`);
|
|
74
|
-
}
|
|
75
|
-
const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
|
|
76
|
-
connectOptions.browserWSEndpoint = browserWSEndpoint;
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
throw new Error(`Could not connect to Chrome in ${userDataDir}. Check if Chrome is running and remote debugging is enabled.`, {
|
|
80
|
-
cause: error,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
if (!channel) {
|
|
86
|
-
throw new Error('Channel must be provided if userDataDir is missing');
|
|
87
|
-
}
|
|
88
|
-
connectOptions.channel = (channel === 'stable' ? 'chrome' : `chrome-${channel}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
throw new Error('Either browserURL, wsEndpoint, channel or userDataDir must be provided');
|
|
93
|
-
}
|
|
94
|
-
logger('Connecting Puppeteer to ', JSON.stringify(connectOptions));
|
|
95
|
-
try {
|
|
96
|
-
browser = await puppeteer.connect(connectOptions);
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
throw new Error('Could not connect to Chrome. Check if Chrome is running and remote debugging is enabled by going to chrome://inspect/#remote-debugging.', {
|
|
100
|
-
cause: err,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
logger('Connected Puppeteer');
|
|
104
|
-
return browser;
|
|
105
|
-
}
|
|
106
|
-
export async function launch(options) {
|
|
107
|
-
const { channel, executablePath, headless, isolated } = options;
|
|
108
|
-
const profileDirName = channel && channel !== 'stable'
|
|
109
|
-
? `chrome-profile-${channel}`
|
|
110
|
-
: 'chrome-profile';
|
|
111
|
-
let userDataDir = options.userDataDir;
|
|
112
|
-
if (!isolated && !userDataDir) {
|
|
113
|
-
userDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
|
|
114
|
-
await fs.promises.mkdir(userDataDir, {
|
|
115
|
-
recursive: true,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
const args = [
|
|
119
|
-
...(options.chromeArgs ?? []),
|
|
120
|
-
'--hide-crash-restore-bubble',
|
|
121
|
-
];
|
|
122
|
-
const ignoreDefaultArgs = options.ignoreDefaultChromeArgs ?? false;
|
|
123
|
-
if (headless) {
|
|
124
|
-
args.push('--screen-info={3840x2160}');
|
|
125
|
-
}
|
|
126
|
-
let puppeteerChannel;
|
|
127
|
-
if (options.devtools) {
|
|
128
|
-
args.push('--auto-open-devtools-for-tabs');
|
|
129
|
-
}
|
|
130
|
-
if (!executablePath) {
|
|
131
|
-
puppeteerChannel =
|
|
132
|
-
channel && channel !== 'stable'
|
|
133
|
-
? `chrome-${channel}`
|
|
134
|
-
: 'chrome';
|
|
135
|
-
}
|
|
136
|
-
try {
|
|
137
|
-
const browser = await puppeteer.launch({
|
|
138
|
-
channel: puppeteerChannel,
|
|
139
|
-
targetFilter: makeTargetFilter(),
|
|
140
|
-
executablePath,
|
|
141
|
-
defaultViewport: null,
|
|
142
|
-
userDataDir,
|
|
143
|
-
pipe: true,
|
|
144
|
-
headless,
|
|
145
|
-
args,
|
|
146
|
-
ignoreDefaultArgs: ignoreDefaultArgs,
|
|
147
|
-
acceptInsecureCerts: options.acceptInsecureCerts,
|
|
148
|
-
handleDevToolsAsPage: true,
|
|
149
|
-
enableExtensions: options.enableExtensions,
|
|
150
|
-
});
|
|
151
|
-
if (options.logFile) {
|
|
152
|
-
// FIXME: we are probably subscribing too late to catch startup logs. We
|
|
153
|
-
// should expose the process earlier or expose the getRecentLogs() getter.
|
|
154
|
-
browser.process()?.stderr?.pipe(options.logFile);
|
|
155
|
-
browser.process()?.stdout?.pipe(options.logFile);
|
|
156
|
-
}
|
|
157
|
-
if (options.viewport) {
|
|
158
|
-
const [page] = await browser.pages();
|
|
159
|
-
await page?.resize({
|
|
160
|
-
contentWidth: options.viewport.width,
|
|
161
|
-
contentHeight: options.viewport.height,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
return browser;
|
|
165
|
-
}
|
|
166
|
-
catch (error) {
|
|
167
|
-
if (userDataDir &&
|
|
168
|
-
error.message.includes('The browser is already running')) {
|
|
169
|
-
throw new Error(`The browser is already running for ${userDataDir}. Use --isolated to run multiple browser instances.`, {
|
|
170
|
-
cause: error,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
throw error;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
export async function ensureBrowserLaunched(options) {
|
|
177
|
-
if (browser?.connected) {
|
|
178
|
-
return browser;
|
|
179
|
-
}
|
|
180
|
-
browser = await launch(options);
|
|
181
|
-
return browser;
|
|
182
|
-
}
|