| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as Protocol from '../../generated/protocol.js'; |
| import * as TextUtils from '../../models/text_utils/text_utils.js'; |
| import * as Common from '../common/common.js'; |
| import * as i18n from '../i18n/i18n.js'; |
| import * as Platform from '../platform/platform.js'; |
| |
| import {Attribute, type Cookie} from './Cookie.js'; |
| import {CookieModel} from './CookieModel.js'; |
| import {CookieParser} from './CookieParser.js'; |
| import * as HttpReasonPhraseStrings from './HttpReasonPhraseStrings.js'; |
| import { |
| Events as NetworkManagerEvents, |
| NetworkManager, |
| } from './NetworkManager.js'; |
| import {ServerSentEvents} from './ServerSentEvents.js'; |
| import {ServerTiming} from './ServerTiming.js'; |
| import {Type} from './Target.js'; |
| |
| // clang-format off |
| const UIStrings = { |
| /** |
| * @description Text in Network Request |
| */ |
| binary: '(binary)', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| secureOnly: 'This cookie was blocked because it had the "`Secure`" attribute and the connection was not secure.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| notOnPath: 'This cookie was blocked because its path was not an exact match for or a superdirectory of the request url\'s path.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| domainMismatch: 'This cookie was blocked because neither did the request URL\'s domain exactly match the cookie\'s domain, nor was the request URL\'s domain a subdomain of the cookie\'s Domain attribute value.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| sameSiteStrict: 'This cookie was blocked because it had the "`SameSite=Strict`" attribute and the request was made from a different site. This includes top-level navigation requests initiated by other sites.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| sameSiteLax: 'This cookie was blocked because it had the "`SameSite=Lax`" attribute and the request was made from a different site and was not initiated by a top-level navigation.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| sameSiteUnspecifiedTreatedAsLax: 'This cookie didn\'t specify a "`SameSite`" attribute when it was stored and was defaulted to "SameSite=Lax," and was blocked because the request was made from a different site and was not initiated by a top-level navigation. The cookie had to have been set with "`SameSite=None`" to enable cross-site usage.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| sameSiteNoneInsecure: 'This cookie was blocked because it had the "`SameSite=None`" attribute but was not marked "Secure". Cookies without SameSite restrictions must be marked "Secure" and sent over a secure connection.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| userPreferences: 'This cookie was blocked due to user preferences.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| thirdPartyPhaseout: 'This cookie was blocked either because of Chrome flags or browser configuration. Learn more in the Issues panel.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| unknownError: 'An unknown error was encountered when trying to send this cookie.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site |
| */ |
| schemefulSameSiteStrict: 'This cookie was blocked because it had the "`SameSite=Strict`" attribute but the request was cross-site. This includes top-level navigation requests initiated by other sites. This request is considered cross-site because the URL has a different scheme than the current site.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site |
| */ |
| schemefulSameSiteLax: 'This cookie was blocked because it had the "`SameSite=Lax`" attribute but the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site |
| */ |
| schemefulSameSiteUnspecifiedTreatedAsLax: 'This cookie didn\'t specify a "`SameSite`" attribute when it was stored, was defaulted to "`SameSite=Lax"`, and was blocked because the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to SameParty |
| */ |
| samePartyFromCrossPartyContext: 'This cookie was blocked because it had the "`SameParty`" attribute but the request was cross-party. The request was considered cross-party because the domain of the resource\'s URL and the domains of the resource\'s enclosing frames/documents are neither owners nor members in the same First-Party Set.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to exceeding the maximum size |
| */ |
| nameValuePairExceedsMaxSize: 'This cookie was blocked because it was too large. The combined size of the name and value must be less than or equal to 4096 characters.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| thisSetcookieWasBlockedDueToUser: 'This attempt to set a cookie via a `Set-Cookie` header was blocked due to user preferences.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| thisSetcookieWasBlockedDueThirdPartyPhaseout: 'Setting this cookie was blocked either because of Chrome flags or browser configuration. Learn more in the Issues panel.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| thisSetcookieHadInvalidSyntax: 'This `Set-Cookie` header had invalid syntax.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| thisSetcookieHadADisallowedCharacter: 'This `Set-Cookie` header contained a disallowed character (a forbidden ASCII control character, or the tab character if it appears in the middle of the cookie name, value, an attribute name, or an attribute value).', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| theSchemeOfThisConnectionIsNot: 'The scheme of this connection is not allowed to store cookies.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked |
| */ |
| anUnknownErrorWasEncounteredWhenTrying: 'An unknown error was encountered when trying to store this cookie.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site |
| * @example {SameSite=Strict} PH1 |
| */ |
| thisSetcookieWasBlockedBecauseItHadTheSamesiteStrictLax: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "{PH1}" attribute but came from a cross-site response which was not the response to a top-level navigation. This response is considered cross-site because the URL has a different scheme than the current site.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site |
| */ |
| thisSetcookieDidntSpecifyASamesite: 'This `Set-Cookie` header didn\'t specify a "`SameSite`" attribute, was defaulted to "`SameSite=Lax"`, and was blocked because it came from a cross-site response which was not the response to a top-level navigation. This response is considered cross-site because the URL has a different scheme than the current site.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to SameParty |
| */ |
| thisSetcookieWasBlockedBecauseItHadTheSameparty: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "`SameParty`" attribute but the request was cross-party. The request was considered cross-party because the domain of the resource\'s URL and the domains of the resource\'s enclosing frames/documents are neither owners nor members in the same First-Party Set.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked due to SameParty |
| */ |
| thisSetcookieWasBlockedBecauseItHadTheSamepartyAttribute: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "`SameParty`" attribute but also had other conflicting attributes. Chrome requires cookies that use the "`SameParty`" attribute to also have the "Secure" attribute, and to not be restricted to "`SameSite=Strict`".', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| blockedReasonSecureOnly: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "Secure" attribute but was not received over a secure connection.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| * @example {SameSite=Strict} PH1 |
| */ |
| blockedReasonSameSiteStrictLax: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "{PH1}" attribute but came from a cross-site response which was not the response to a top-level navigation.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| blockedReasonSameSiteUnspecifiedTreatedAsLax: 'This `Set-Cookie` header didn\'t specify a "`SameSite`" attribute and was defaulted to "`SameSite=Lax,`" and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The `Set-Cookie` had to have been set with "`SameSite=None`" to enable cross-site usage.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| blockedReasonSameSiteNoneInsecure: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "`SameSite=None`" attribute but did not have the "Secure" attribute, which is required in order to use "`SameSite=None`".', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| blockedReasonOverwriteSecure: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it was not sent over a secure connection and would have overwritten a cookie with the Secure attribute.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| blockedReasonInvalidDomain: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because its Domain attribute was invalid with regards to the current host url.', |
| /** |
| * @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. |
| */ |
| blockedReasonInvalidPrefix: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it used the "`__Secure-`" or "`__Host-`" prefix in its name and broke the additional rules applied to cookies with these prefixes as defined in `https://tools.ietf.org/html/draft-west-cookie-prefixes-05`.', |
| /** |
| * @description Tooltip to explain why a cookie was blocked when the size of the #name plus the size of the value exceeds the max size. |
| */ |
| thisSetcookieWasBlockedBecauseTheNameValuePairExceedsMaxSize: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because the cookie was too large. The combined size of the name and value must be less than or equal to 4096 characters.', |
| /** |
| * @description Text in Network Manager |
| * @example {https://example.com} PH1 |
| */ |
| setcookieHeaderIsIgnoredIn: 'Set-Cookie header is ignored in response from url: {PH1}. The combined size of the name and value must be less than or equal to 4096 characters.', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonUserSetting: 'This cookie is allowed by user preference.', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonTPCDMetadata: 'This cookie is allowed by a third-party cookie deprecation trial grace period. Learn more: goo.gle/dt-grace.', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonTPCDDeprecationTrial: 'This cookie is allowed by third-party cookie deprecation trial. Learn more: goo.gle/ps-dt.', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonTopLevelTPCDDeprecationTrial: 'This cookie is allowed by top-level third-party cookie deprecation trial. Learn more: goo.gle/ps-dt.', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonTPCDHeuristics: 'This cookie is allowed by third-party cookie heuristics. Learn more: goo.gle/hbe', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonEnterprisePolicy: 'This cookie is allowed by Chrome Enterprise policy. Learn more: goo.gle/ce-3pc', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonStorageAccessAPI: 'This cookie is allowed by the Storage Access API. Learn more: goo.gle/saa', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonTopLevelStorageAccessAPI: 'This cookie is allowed by the top-level Storage Access API. Learn more: goo.gle/saa-top', |
| /** |
| * @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted. |
| */ |
| exemptionReasonScheme: 'This cookie is allowed by the top-level url scheme', |
| } as const; |
| // clang-format on |
| |
| const str_ = i18n.i18n.registerUIStrings('core/sdk/NetworkRequest.ts', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| |
| export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements |
| TextUtils.ContentProvider.StreamingContentProvider { |
| #requestId: string; |
| #backendRequestId?: Protocol.Network.RequestId; |
| readonly #documentURL: Platform.DevToolsPath.UrlString; |
| readonly #frameId: Protocol.Page.FrameId|null; |
| readonly #loaderId: Protocol.Network.LoaderId|null; |
| readonly #hasUserGesture: boolean|undefined; |
| readonly #initiator: Protocol.Network.Initiator|null|undefined; |
| #redirectSource: NetworkRequest|null = null; |
| #preflightRequest: NetworkRequest|null = null; |
| #preflightInitiatorRequest: NetworkRequest|null = null; |
| #isRedirect = false; |
| #redirectDestination: NetworkRequest|null = null; |
| #issueTime = -1; |
| #startTime = -1; |
| #endTime = -1; |
| #blockedReason: Protocol.Network.BlockedReason|undefined = undefined; |
| #renderBlockingBehavior?: Protocol.Network.RenderBlockingBehavior; |
| #corsErrorStatus: Protocol.Network.CorsErrorStatus|undefined = undefined; |
| statusCode = 0; |
| statusText = ''; |
| requestMethod = ''; |
| requestTime = 0; |
| protocol = ''; |
| alternateProtocolUsage: Protocol.Network.AlternateProtocolUsage|undefined = undefined; |
| mixedContentType: Protocol.Security.MixedContentType = Protocol.Security.MixedContentType.None; |
| #initialPriority: Protocol.Network.ResourcePriority|null = null; |
| #currentPriority: Protocol.Network.ResourcePriority|null = null; |
| #signedExchangeInfo: Protocol.Network.SignedExchangeInfo|null = null; |
| #resourceType: Common.ResourceType.ResourceType = Common.ResourceType.resourceTypes.Other; |
| #contentData: Promise<TextUtils.ContentData.ContentDataOrError>|null = null; |
| #streamingContentData: Promise<TextUtils.StreamingContentData.StreamingContentDataOrError>|null = null; |
| readonly #frames: WebSocketFrame[] = []; |
| #responseHeaderValues: Record<string, string|undefined> = {}; |
| #responseHeadersText = ''; |
| #originalResponseHeaders: Protocol.Fetch.HeaderEntry[] = []; |
| #sortedOriginalResponseHeaders?: NameValue[]; |
| |
| // This field is only used when intercepting and overriding requests, because |
| // in that case 'this.responseHeaders' does not contain 'set-cookie' headers. |
| #setCookieHeaders: Protocol.Fetch.HeaderEntry[] = []; |
| |
| #requestHeaders: NameValue[] = []; |
| #requestHeaderValues: Record<string, string|undefined> = {}; |
| #remoteAddress = ''; |
| #remoteAddressSpace: Protocol.Network.IPAddressSpace = Protocol.Network.IPAddressSpace.Unknown; |
| #referrerPolicy: Protocol.Network.RequestReferrerPolicy|null = null; |
| #securityState: Protocol.Security.SecurityState = Protocol.Security.SecurityState.Unknown; |
| #securityDetails: Protocol.Network.SecurityDetails|null = null; |
| connectionId = '0'; |
| connectionReused = false; |
| hasNetworkData = false; |
| #formParametersPromise: Promise<NameValue[]|null>|null = null; |
| #requestFormDataPromise: Promise<string|null>|null = Promise.resolve(null); |
| #hasExtraRequestInfo = false; |
| #hasExtraResponseInfo = false; |
| #blockedRequestCookies: BlockedCookieWithReason[] = []; |
| #includedRequestCookies: IncludedCookieWithReason[] = []; |
| #blockedResponseCookies: BlockedSetCookieWithReason[] = []; |
| #exemptedResponseCookies: ExemptedSetCookieWithReason[] = []; |
| #responseCookiesPartitionKey: Protocol.Network.CookiePartitionKey|null = null; |
| #responseCookiesPartitionKeyOpaque: boolean|null = null; |
| #siteHasCookieInOtherPartition = false; |
| localizedFailDescription: string|null = null; |
| #url!: Platform.DevToolsPath.UrlString; |
| #responseReceivedTime!: number; |
| #transferSize!: number; |
| #finished!: boolean; |
| #failed!: boolean; |
| #canceled!: boolean; |
| #preserved!: boolean; |
| #mimeType!: string; |
| #charset!: string; |
| #parsedURL!: Common.ParsedURL.ParsedURL; |
| #name!: string|undefined; |
| #path!: string|undefined; |
| #clientSecurityState!:|Protocol.Network.ClientSecurityState|undefined; |
| #trustTokenParams!: Protocol.Network.TrustTokenParams|undefined; |
| #trustTokenOperationDoneEvent!:|Protocol.Network.TrustTokenOperationDoneEvent|undefined; |
| #responseCacheStorageCacheName?: string; |
| #serviceWorkerResponseSource?: Protocol.Network.ServiceWorkerResponseSource; |
| #wallIssueTime?: number; |
| #responseRetrievalTime?: Date; |
| #resourceSize?: number; |
| #fromMemoryCache?: boolean; |
| #fromDiskCache?: boolean; |
| #fromPrefetchCache?: boolean; |
| #fromEarlyHints?: boolean; |
| #fetchedViaServiceWorker?: boolean; |
| #serviceWorkerRouterInfo?: Protocol.Network.ServiceWorkerRouterInfo; |
| #timing?: Protocol.Network.ResourceTiming; |
| #requestHeadersText?: string; |
| #responseHeaders?: NameValue[]; |
| #earlyHintsHeaders?: NameValue[]; |
| #sortedResponseHeaders?: NameValue[]; |
| #responseCookies?: Cookie[]; |
| #serverTimings?: ServerTiming[]|null; |
| #queryString?: string|null; |
| #parsedQueryParameters?: NameValue[]; |
| #contentDataProvider?: () => Promise<TextUtils.ContentData.ContentDataOrError>; |
| #isSameSite: boolean|null = null; |
| #wasIntercepted = false; |
| #associatedData = new Map<string, object>(); |
| #hasOverriddenContent = false; |
| #hasThirdPartyCookiePhaseoutIssue = false; |
| #serverSentEvents?: ServerSentEvents; |
| responseReceivedPromise?: Promise<void>; |
| responseReceivedPromiseResolve?: () => void; |
| directSocketInfo?: DirectSocketInfo; |
| readonly #directSocketChunks: DirectSocketChunk[] = []; |
| #isAdRelated: boolean; |
| #appliedNetworkConditionsId?: string; |
| |
| constructor( |
| requestId: string, |
| backendRequestId: Protocol.Network.RequestId|undefined, |
| url: Platform.DevToolsPath.UrlString, |
| documentURL: Platform.DevToolsPath.UrlString, |
| frameId: Protocol.Page.FrameId|null, |
| loaderId: Protocol.Network.LoaderId|null, |
| initiator: Protocol.Network.Initiator|null, |
| hasUserGesture?: boolean, |
| ) { |
| super(); |
| |
| this.#requestId = requestId; |
| this.#backendRequestId = backendRequestId; |
| this.setUrl(url); |
| this.#documentURL = documentURL; |
| this.#frameId = frameId; |
| this.#loaderId = loaderId; |
| this.#initiator = initiator; |
| this.#hasUserGesture = hasUserGesture; |
| this.#isAdRelated = false; |
| } |
| |
| static create( |
| backendRequestId: Protocol.Network.RequestId, |
| url: Platform.DevToolsPath.UrlString, |
| documentURL: Platform.DevToolsPath.UrlString, |
| frameId: Protocol.Page.FrameId|null, |
| loaderId: Protocol.Network.LoaderId|null, |
| initiator: Protocol.Network.Initiator|null, |
| hasUserGesture?: boolean, |
| ): NetworkRequest { |
| return new NetworkRequest( |
| backendRequestId, |
| backendRequestId, |
| url, |
| documentURL, |
| frameId, |
| loaderId, |
| initiator, |
| hasUserGesture, |
| ); |
| } |
| |
| static createForSocket( |
| backendRequestId: Protocol.Network.RequestId, |
| requestURL: Platform.DevToolsPath.UrlString, |
| initiator?: Protocol.Network.Initiator, |
| ): NetworkRequest { |
| return new NetworkRequest( |
| backendRequestId, |
| backendRequestId, |
| requestURL, |
| Platform.DevToolsPath.EmptyUrlString, |
| null, |
| null, |
| initiator || null, |
| ); |
| } |
| |
| static createWithoutBackendRequest( |
| requestId: string, |
| url: Platform.DevToolsPath.UrlString, |
| documentURL: Platform.DevToolsPath.UrlString, |
| initiator: Protocol.Network.Initiator|null, |
| ): NetworkRequest { |
| return new NetworkRequest( |
| requestId, |
| undefined, |
| url, |
| documentURL, |
| null, |
| null, |
| initiator, |
| ); |
| } |
| |
| identityCompare(other: NetworkRequest): number { |
| const thisId = this.requestId(); |
| const thatId = other.requestId(); |
| if (thisId > thatId) { |
| return 1; |
| } |
| if (thisId < thatId) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| requestId(): string { |
| return this.#requestId; |
| } |
| |
| backendRequestId(): Protocol.Network.RequestId|undefined { |
| return this.#backendRequestId; |
| } |
| |
| url(): Platform.DevToolsPath.UrlString { |
| return this.#url; |
| } |
| |
| isBlobRequest(): boolean { |
| return Common.ParsedURL.schemeIs(this.#url, 'blob:'); |
| } |
| |
| setUrl(x: Platform.DevToolsPath.UrlString): void { |
| if (this.#url === x) { |
| return; |
| } |
| |
| this.#url = x; |
| this.#parsedURL = new Common.ParsedURL.ParsedURL(x); |
| this.#queryString = undefined; |
| this.#parsedQueryParameters = undefined; |
| this.#name = undefined; |
| this.#path = undefined; |
| } |
| |
| get documentURL(): Platform.DevToolsPath.UrlString { |
| return this.#documentURL; |
| } |
| |
| get parsedURL(): Common.ParsedURL.ParsedURL { |
| return this.#parsedURL; |
| } |
| |
| get frameId(): Protocol.Page.FrameId|null { |
| return this.#frameId; |
| } |
| |
| get loaderId(): Protocol.Network.LoaderId|null { |
| return this.#loaderId; |
| } |
| |
| get appliedNetworkConditionsId(): string|undefined { |
| return this.#appliedNetworkConditionsId; |
| } |
| |
| setRemoteAddress(ip: string, port: number): void { |
| this.#remoteAddress = ip + ':' + port; |
| this.dispatchEventToListeners(Events.REMOTE_ADDRESS_CHANGED, this); |
| } |
| |
| remoteAddress(): string { |
| return this.#remoteAddress; |
| } |
| |
| remoteAddressSpace(): Protocol.Network.IPAddressSpace { |
| return this.#remoteAddressSpace; |
| } |
| |
| /** |
| * The cache #name of the CacheStorage from where the response is served via |
| * the ServiceWorker. |
| */ |
| getResponseCacheStorageCacheName(): string|undefined { |
| return this.#responseCacheStorageCacheName; |
| } |
| |
| setResponseCacheStorageCacheName(x: string): void { |
| this.#responseCacheStorageCacheName = x; |
| } |
| |
| serviceWorkerResponseSource():|Protocol.Network.ServiceWorkerResponseSource|undefined { |
| return this.#serviceWorkerResponseSource; |
| } |
| |
| setServiceWorkerResponseSource( |
| serviceWorkerResponseSource: Protocol.Network.ServiceWorkerResponseSource, |
| ): void { |
| this.#serviceWorkerResponseSource = serviceWorkerResponseSource; |
| } |
| |
| setReferrerPolicy( |
| referrerPolicy: Protocol.Network.RequestReferrerPolicy, |
| ): void { |
| this.#referrerPolicy = referrerPolicy; |
| } |
| |
| referrerPolicy(): Protocol.Network.RequestReferrerPolicy|null { |
| return this.#referrerPolicy; |
| } |
| |
| securityState(): Protocol.Security.SecurityState { |
| return this.#securityState; |
| } |
| |
| setSecurityState(securityState: Protocol.Security.SecurityState): void { |
| this.#securityState = securityState; |
| } |
| |
| securityDetails(): Protocol.Network.SecurityDetails|null { |
| return this.#securityDetails; |
| } |
| |
| securityOrigin(): string { |
| return this.#parsedURL.securityOrigin(); |
| } |
| |
| setSecurityDetails(securityDetails: Protocol.Network.SecurityDetails): void { |
| this.#securityDetails = securityDetails; |
| } |
| |
| get startTime(): number { |
| return this.#startTime || -1; |
| } |
| |
| setIssueTime(monotonicTime: number, wallTime: number): void { |
| this.#issueTime = monotonicTime; |
| this.#wallIssueTime = wallTime; |
| this.#startTime = monotonicTime; |
| } |
| |
| issueTime(): number { |
| return this.#issueTime; |
| } |
| |
| pseudoWallTime(monotonicTime: number): number { |
| return this.#wallIssueTime ? this.#wallIssueTime - this.#issueTime + monotonicTime : monotonicTime; |
| } |
| |
| get responseReceivedTime(): number { |
| return this.#responseReceivedTime || -1; |
| } |
| |
| set responseReceivedTime(x: number) { |
| this.#responseReceivedTime = x; |
| } |
| |
| /** |
| * The time at which the returned response was generated. For cached |
| * responses, this is the last time the cache entry was validated. |
| */ |
| getResponseRetrievalTime(): Date|undefined { |
| return this.#responseRetrievalTime; |
| } |
| |
| setResponseRetrievalTime(x: Date): void { |
| this.#responseRetrievalTime = x; |
| } |
| |
| get endTime(): number { |
| return this.#endTime || -1; |
| } |
| |
| set endTime(x: number) { |
| if (this.timing?.requestTime) { |
| // Check against accurate responseReceivedTime. |
| this.#endTime = Math.max(x, this.responseReceivedTime); |
| } else { |
| // Prefer endTime since it might be from the network stack. |
| this.#endTime = x; |
| if (this.#responseReceivedTime > x) { |
| this.#responseReceivedTime = x; |
| } |
| } |
| this.dispatchEventToListeners(Events.TIMING_CHANGED, this); |
| } |
| |
| get duration(): number { |
| if (this.#endTime === -1 || this.#startTime === -1) { |
| return -1; |
| } |
| return this.#endTime - this.#startTime; |
| } |
| |
| get latency(): number { |
| if (this.#responseReceivedTime === -1 || this.#startTime === -1) { |
| return -1; |
| } |
| return this.#responseReceivedTime - this.#startTime; |
| } |
| |
| get resourceSize(): number { |
| return this.#resourceSize || 0; |
| } |
| |
| set resourceSize(x: number) { |
| this.#resourceSize = x; |
| } |
| |
| get transferSize(): number { |
| return this.#transferSize || 0; |
| } |
| |
| increaseTransferSize(x: number): void { |
| this.#transferSize = (this.#transferSize || 0) + x; |
| } |
| |
| setTransferSize(x: number): void { |
| this.#transferSize = x; |
| } |
| |
| get finished(): boolean { |
| return this.#finished; |
| } |
| |
| set finished(x: boolean) { |
| if (this.#finished === x) { |
| return; |
| } |
| |
| this.#finished = x; |
| |
| if (x) { |
| this.dispatchEventToListeners(Events.FINISHED_LOADING, this); |
| } |
| } |
| |
| get failed(): boolean { |
| return this.#failed; |
| } |
| |
| set failed(x: boolean) { |
| this.#failed = x; |
| } |
| |
| get canceled(): boolean { |
| return this.#canceled; |
| } |
| |
| set canceled(x: boolean) { |
| this.#canceled = x; |
| } |
| |
| get preserved(): boolean { |
| return this.#preserved; |
| } |
| |
| set preserved(x: boolean) { |
| this.#preserved = x; |
| } |
| |
| blockedReason(): Protocol.Network.BlockedReason|undefined { |
| return this.#blockedReason; |
| } |
| |
| setBlockedReason(reason: Protocol.Network.BlockedReason): void { |
| this.#blockedReason = reason; |
| } |
| |
| setRenderBlockingBehavior(renderBlocking: Protocol.Network.RenderBlockingBehavior): void { |
| this.#renderBlockingBehavior = renderBlocking; |
| } |
| |
| renderBlockingBehavior(): Protocol.Network.RenderBlockingBehavior|undefined { |
| return this.#renderBlockingBehavior; |
| } |
| |
| corsErrorStatus(): Protocol.Network.CorsErrorStatus|undefined { |
| return this.#corsErrorStatus; |
| } |
| |
| setCorsErrorStatus(corsErrorStatus: Protocol.Network.CorsErrorStatus): void { |
| this.#corsErrorStatus = corsErrorStatus; |
| } |
| |
| wasBlocked(): boolean { |
| return Boolean(this.#blockedReason); |
| } |
| |
| cached(): boolean { |
| return ((Boolean(this.#fromMemoryCache) || Boolean(this.#fromDiskCache)) && !this.#transferSize); |
| } |
| |
| cachedInMemory(): boolean { |
| return Boolean(this.#fromMemoryCache) && !this.#transferSize; |
| } |
| |
| fromPrefetchCache(): boolean { |
| return Boolean(this.#fromPrefetchCache); |
| } |
| |
| setFromMemoryCache(): void { |
| this.#fromMemoryCache = true; |
| this.#timing = undefined; |
| } |
| |
| get fromDiskCache(): boolean|undefined { |
| return this.#fromDiskCache; |
| } |
| |
| setFromDiskCache(): void { |
| this.#fromDiskCache = true; |
| } |
| |
| setFromPrefetchCache(): void { |
| this.#fromPrefetchCache = true; |
| } |
| |
| fromEarlyHints(): boolean { |
| return Boolean(this.#fromEarlyHints); |
| } |
| |
| setFromEarlyHints(): void { |
| this.#fromEarlyHints = true; |
| } |
| |
| /** |
| * Returns true if the request was intercepted by a service worker and it |
| * provided its own response. |
| */ |
| get fetchedViaServiceWorker(): boolean { |
| return Boolean(this.#fetchedViaServiceWorker); |
| } |
| |
| set fetchedViaServiceWorker(x: boolean) { |
| this.#fetchedViaServiceWorker = x; |
| } |
| |
| get serviceWorkerRouterInfo():|Protocol.Network.ServiceWorkerRouterInfo|undefined { |
| return this.#serviceWorkerRouterInfo; |
| } |
| |
| set serviceWorkerRouterInfo(x: Protocol.Network.ServiceWorkerRouterInfo) { |
| this.#serviceWorkerRouterInfo = x; |
| } |
| |
| /** |
| * Returns true if the request was matched to a route when using the |
| * ServiceWorker static routing API. |
| */ |
| hasMatchingServiceWorkerRouter(): boolean { |
| // See definitions in `browser_protocol.pdl` for justification. |
| return ( |
| this.#serviceWorkerRouterInfo !== undefined && this.serviceWorkerRouterInfo?.matchedSourceType !== undefined); |
| } |
| |
| /** |
| * Returns true if the request was sent by a service worker. |
| */ |
| initiatedByServiceWorker(): boolean { |
| const networkManager = NetworkManager.forRequest(this); |
| if (!networkManager) { |
| return false; |
| } |
| return networkManager.target().type() === Type.ServiceWorker; |
| } |
| |
| get timing(): Protocol.Network.ResourceTiming|undefined { |
| return this.#timing; |
| } |
| |
| set timing(timingInfo: Protocol.Network.ResourceTiming|undefined) { |
| if (!timingInfo || this.#fromMemoryCache) { |
| return; |
| } |
| // Take startTime and responseReceivedTime from timing data for better accuracy. |
| // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis. |
| this.#startTime = timingInfo.requestTime; |
| const headersReceivedTime = timingInfo.requestTime + timingInfo.receiveHeadersEnd / 1000.0; |
| if ((this.#responseReceivedTime || -1) < 0 || this.#responseReceivedTime > headersReceivedTime) { |
| this.#responseReceivedTime = headersReceivedTime; |
| } |
| if (this.#startTime > this.#responseReceivedTime) { |
| this.#responseReceivedTime = this.#startTime; |
| } |
| |
| this.#timing = timingInfo; |
| this.dispatchEventToListeners(Events.TIMING_CHANGED, this); |
| } |
| |
| private setConnectTimingFromExtraInfo( |
| connectTiming: Protocol.Network.ConnectTiming, |
| ): void { |
| this.#startTime = connectTiming.requestTime; |
| this.dispatchEventToListeners(Events.TIMING_CHANGED, this); |
| } |
| |
| get mimeType(): string { |
| return this.#mimeType; |
| } |
| |
| set mimeType(x: string) { |
| this.#mimeType = x; |
| if (x === Platform.MimeType.MimeType.EVENTSTREAM && !this.#serverSentEvents) { |
| const parseFromStreamedData = this.resourceType() !== Common.ResourceType.resourceTypes.EventSource; |
| this.#serverSentEvents = new ServerSentEvents( |
| this, |
| parseFromStreamedData, |
| ); |
| } |
| } |
| |
| get displayName(): string { |
| return this.#parsedURL.displayName; |
| } |
| |
| name(): string { |
| if (this.#name) { |
| return this.#name; |
| } |
| this.parseNameAndPathFromURL(); |
| return this.#name as string; |
| } |
| |
| path(): string { |
| if (this.#path) { |
| return this.#path; |
| } |
| this.parseNameAndPathFromURL(); |
| return this.#path as string; |
| } |
| |
| private parseNameAndPathFromURL(): void { |
| if (this.#parsedURL.isDataURL()) { |
| this.#name = this.#parsedURL.dataURLDisplayName(); |
| this.#path = ''; |
| } else if (this.#parsedURL.isBlobURL()) { |
| this.#name = this.#parsedURL.url; |
| this.#path = ''; |
| } else if (this.#parsedURL.isAboutBlank()) { |
| this.#name = this.#parsedURL.url; |
| this.#path = ''; |
| } else { |
| this.#path = this.#parsedURL.host + this.#parsedURL.folderPathComponents; |
| |
| const networkManager = NetworkManager.forRequest(this); |
| const inspectedURL = networkManager ? Common.ParsedURL.ParsedURL.fromString( |
| networkManager.target().inspectedURL(), |
| ) : |
| null; |
| this.#path = Platform.StringUtilities.trimURL( |
| this.#path, |
| inspectedURL ? inspectedURL.host : '', |
| ); |
| if (this.#parsedURL.lastPathComponent || this.#parsedURL.queryParams) { |
| this.#name = |
| this.#parsedURL.lastPathComponent + (this.#parsedURL.queryParams ? '?' + this.#parsedURL.queryParams : ''); |
| } else if (this.#parsedURL.folderPathComponents) { |
| this.#name = this.#parsedURL.folderPathComponents.substring( |
| this.#parsedURL.folderPathComponents.lastIndexOf('/') + 1, |
| ) + |
| '/'; |
| this.#path = this.#path.substring( |
| 0, |
| this.#path.lastIndexOf('/'), |
| ); |
| } else { |
| this.#name = this.#parsedURL.host; |
| this.#path = ''; |
| } |
| } |
| } |
| |
| get folder(): string { |
| let path: string = this.#parsedURL.path; |
| const indexOfQuery = path.indexOf('?'); |
| if (indexOfQuery !== -1) { |
| path = path.substring(0, indexOfQuery); |
| } |
| const lastSlashIndex = path.lastIndexOf('/'); |
| return lastSlashIndex !== -1 ? path.substring(0, lastSlashIndex) : ''; |
| } |
| |
| get pathname(): string { |
| return this.#parsedURL.path; |
| } |
| |
| resourceType(): Common.ResourceType.ResourceType { |
| return this.#resourceType; |
| } |
| |
| setResourceType(resourceType: Common.ResourceType.ResourceType): void { |
| this.#resourceType = resourceType; |
| } |
| |
| get domain(): string { |
| return this.#parsedURL.host; |
| } |
| |
| get scheme(): string { |
| return this.#parsedURL.scheme; |
| } |
| |
| getInferredStatusText(): string { |
| return (this.statusText || HttpReasonPhraseStrings.getStatusText(this.statusCode)); |
| } |
| |
| redirectSource(): NetworkRequest|null { |
| return this.#redirectSource; |
| } |
| |
| setRedirectSource(originatingRequest: NetworkRequest|null): void { |
| this.#redirectSource = originatingRequest; |
| } |
| |
| preflightRequest(): NetworkRequest|null { |
| return this.#preflightRequest; |
| } |
| |
| setPreflightRequest(preflightRequest: NetworkRequest|null): void { |
| this.#preflightRequest = preflightRequest; |
| } |
| |
| preflightInitiatorRequest(): NetworkRequest|null { |
| return this.#preflightInitiatorRequest; |
| } |
| |
| setPreflightInitiatorRequest( |
| preflightInitiatorRequest: NetworkRequest|null, |
| ): void { |
| this.#preflightInitiatorRequest = preflightInitiatorRequest; |
| } |
| |
| isPreflightRequest(): boolean { |
| return ( |
| this.#initiator !== null && this.#initiator !== undefined && |
| this.#initiator.type === Protocol.Network.InitiatorType.Preflight); |
| } |
| |
| redirectDestination(): NetworkRequest|null { |
| return this.#redirectDestination; |
| } |
| |
| setRedirectDestination(redirectDestination: NetworkRequest|null): void { |
| this.#redirectDestination = redirectDestination; |
| } |
| |
| requestHeaders(): NameValue[] { |
| return this.#requestHeaders; |
| } |
| |
| setRequestHeaders(headers: NameValue[]): void { |
| this.#requestHeaders = headers; |
| |
| this.dispatchEventToListeners(Events.REQUEST_HEADERS_CHANGED); |
| } |
| |
| requestHeadersText(): string|undefined { |
| return this.#requestHeadersText; |
| } |
| |
| setRequestHeadersText(text: string): void { |
| this.#requestHeadersText = text; |
| |
| this.dispatchEventToListeners(Events.REQUEST_HEADERS_CHANGED); |
| } |
| |
| requestHeaderValue(headerName: string): string|undefined { |
| if (this.#requestHeaderValues[headerName]) { |
| return this.#requestHeaderValues[headerName]; |
| } |
| this.#requestHeaderValues[headerName] = this.computeHeaderValue( |
| this.requestHeaders(), |
| headerName, |
| ); |
| return this.#requestHeaderValues[headerName]; |
| } |
| |
| requestFormData(): Promise<string|null> { |
| if (!this.#requestFormDataPromise) { |
| this.#requestFormDataPromise = NetworkManager.requestPostData(this); |
| } |
| return this.#requestFormDataPromise; |
| } |
| |
| setRequestFormData(hasData: boolean, data: string|null): void { |
| this.#requestFormDataPromise = hasData && data === null ? null : Promise.resolve(data); |
| this.#formParametersPromise = null; |
| } |
| |
| private filteredProtocolName(): string { |
| const protocol = this.protocol.toLowerCase(); |
| if (protocol === 'h2') { |
| return 'http/2.0'; |
| } |
| return protocol.replace(/^http\/2(\.0)?\+/, 'http/2.0+'); |
| } |
| |
| requestHttpVersion(): string { |
| const headersText = this.requestHeadersText(); |
| if (!headersText) { |
| const version = this.requestHeaderValue('version') || this.requestHeaderValue(':version'); |
| if (version) { |
| return version; |
| } |
| return this.filteredProtocolName(); |
| } |
| const firstLine = headersText.split(/\r\n/)[0]; |
| const match = firstLine.match(/(HTTP\/\d+\.\d+)$/); |
| return match ? match[1] : 'HTTP/0.9'; |
| } |
| |
| get responseHeaders(): NameValue[] { |
| return this.#responseHeaders || []; |
| } |
| |
| set responseHeaders(x: NameValue[]) { |
| this.#responseHeaders = x; |
| this.#sortedResponseHeaders = undefined; |
| this.#serverTimings = undefined; |
| this.#responseCookies = undefined; |
| this.#responseHeaderValues = {}; |
| |
| this.dispatchEventToListeners(Events.RESPONSE_HEADERS_CHANGED); |
| } |
| |
| get earlyHintsHeaders(): NameValue[] { |
| return this.#earlyHintsHeaders || []; |
| } |
| |
| set earlyHintsHeaders(x: NameValue[]) { |
| this.#earlyHintsHeaders = x; |
| } |
| |
| get originalResponseHeaders(): Protocol.Fetch.HeaderEntry[] { |
| return this.#originalResponseHeaders; |
| } |
| |
| set originalResponseHeaders(headers: Protocol.Fetch.HeaderEntry[]) { |
| this.#originalResponseHeaders = headers; |
| this.#sortedOriginalResponseHeaders = undefined; |
| } |
| |
| get setCookieHeaders(): Protocol.Fetch.HeaderEntry[] { |
| return this.#setCookieHeaders; |
| } |
| |
| set setCookieHeaders(headers: Protocol.Fetch.HeaderEntry[]) { |
| this.#setCookieHeaders = headers; |
| } |
| |
| get responseHeadersText(): string { |
| return this.#responseHeadersText; |
| } |
| |
| set responseHeadersText(x: string) { |
| this.#responseHeadersText = x; |
| |
| this.dispatchEventToListeners(Events.RESPONSE_HEADERS_CHANGED); |
| } |
| |
| get sortedResponseHeaders(): NameValue[] { |
| if (this.#sortedResponseHeaders !== undefined) { |
| return this.#sortedResponseHeaders; |
| } |
| |
| this.#sortedResponseHeaders = this.responseHeaders.slice(); |
| return this.#sortedResponseHeaders.sort(function(a, b) { |
| return Platform.StringUtilities.compare( |
| a.name.toLowerCase(), |
| b.name.toLowerCase(), |
| ); |
| }); |
| } |
| |
| get sortedOriginalResponseHeaders(): NameValue[] { |
| if (this.#sortedOriginalResponseHeaders !== undefined) { |
| return this.#sortedOriginalResponseHeaders; |
| } |
| |
| this.#sortedOriginalResponseHeaders = this.originalResponseHeaders.slice(); |
| return this.#sortedOriginalResponseHeaders.sort(function(a, b) { |
| return Platform.StringUtilities.compare( |
| a.name.toLowerCase(), |
| b.name.toLowerCase(), |
| ); |
| }); |
| } |
| |
| get overrideTypes(): OverrideType[] { |
| const types: OverrideType[] = []; |
| |
| if (this.hasOverriddenContent) { |
| types.push('content'); |
| } |
| |
| if (this.hasOverriddenHeaders()) { |
| types.push('headers'); |
| } |
| |
| return types; |
| } |
| |
| get hasOverriddenContent(): boolean { |
| return this.#hasOverriddenContent; |
| } |
| |
| set hasOverriddenContent(value: boolean) { |
| this.#hasOverriddenContent = value; |
| } |
| |
| #deduplicateHeaders(sortedHeaders: NameValue[]): NameValue[] { |
| const dedupedHeaders: NameValue[] = []; |
| for (const header of sortedHeaders) { |
| if (dedupedHeaders.length && dedupedHeaders[dedupedHeaders.length - 1].name === header.name) { |
| dedupedHeaders[dedupedHeaders.length - 1].value += `, ${header.value}`; |
| } else { |
| dedupedHeaders.push({name: header.name, value: header.value}); |
| } |
| } |
| return dedupedHeaders; |
| } |
| |
| hasOverriddenHeaders(): boolean { |
| if (!this.#originalResponseHeaders.length) { |
| return false; |
| } |
| const responseHeaders = this.#deduplicateHeaders( |
| this.sortedResponseHeaders, |
| ); |
| const originalResponseHeaders = this.#deduplicateHeaders( |
| this.sortedOriginalResponseHeaders, |
| ); |
| if (responseHeaders.length !== originalResponseHeaders.length) { |
| return true; |
| } |
| for (let i = 0; i < responseHeaders.length; i++) { |
| if (responseHeaders[i].name.toLowerCase() !== originalResponseHeaders[i].name.toLowerCase()) { |
| return true; |
| } |
| if (responseHeaders[i].value !== originalResponseHeaders[i].value) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| responseHeaderValue(headerName: string): string|undefined { |
| if (headerName in this.#responseHeaderValues) { |
| return this.#responseHeaderValues[headerName]; |
| } |
| this.#responseHeaderValues[headerName] = this.computeHeaderValue( |
| this.responseHeaders, |
| headerName, |
| ); |
| return this.#responseHeaderValues[headerName]; |
| } |
| |
| wasIntercepted(): boolean { |
| return this.#wasIntercepted; |
| } |
| |
| setWasIntercepted(wasIntercepted: boolean): void { |
| this.#wasIntercepted = wasIntercepted; |
| } |
| |
| setEarlyHintsHeaders(headers: NameValue[]): void { |
| this.earlyHintsHeaders = headers; |
| } |
| |
| get responseCookies(): Cookie[] { |
| if (!this.#responseCookies) { |
| this.#responseCookies = CookieParser.parseSetCookie( |
| this.responseHeaderValue('Set-Cookie'), |
| this.domain, |
| ) || |
| []; |
| if (this.#responseCookiesPartitionKey) { |
| for (const cookie of this.#responseCookies) { |
| if (cookie.partitioned()) { |
| cookie.setPartitionKey( |
| this.#responseCookiesPartitionKey.topLevelSite, |
| this.#responseCookiesPartitionKey.hasCrossSiteAncestor, |
| ); |
| } |
| } |
| } else if (this.#responseCookiesPartitionKeyOpaque) { |
| for (const cookie of this.#responseCookies) { |
| // Do not check cookie.partitioned() since most opaque partitions |
| // are fenced/credentialless frames partitioned by default. |
| cookie.setPartitionKeyOpaque(); |
| } |
| } |
| } |
| return this.#responseCookies; |
| } |
| |
| set responseCookies(responseCookies: Cookie[]) { |
| this.#responseCookies = responseCookies; |
| } |
| |
| responseLastModified(): string|undefined { |
| return this.responseHeaderValue('last-modified'); |
| } |
| |
| allCookiesIncludingBlockedOnes(): Cookie[] { |
| return [ |
| ...this.includedRequestCookies().map( |
| includedRequestCookie => includedRequestCookie.cookie, |
| ), |
| ...this.responseCookies, |
| ...this.blockedRequestCookies().map( |
| blockedRequestCookie => blockedRequestCookie.cookie, |
| ), |
| ...this.blockedResponseCookies().map( |
| blockedResponseCookie => blockedResponseCookie.cookie, |
| ), |
| ].filter(v => !!v); |
| } |
| |
| get serverTimings(): ServerTiming[]|null { |
| if (typeof this.#serverTimings === 'undefined') { |
| this.#serverTimings = ServerTiming.parseHeaders( |
| this.responseHeaders, |
| ); |
| } |
| return this.#serverTimings; |
| } |
| |
| queryString(): string|null { |
| if (this.#queryString !== undefined) { |
| return this.#queryString; |
| } |
| |
| let queryString: string|null = null; |
| const url = this.url(); |
| const questionMarkPosition = url.indexOf('?'); |
| if (questionMarkPosition !== -1) { |
| queryString = url.substring(questionMarkPosition + 1); |
| const hashSignPosition = queryString.indexOf('#'); |
| if (hashSignPosition !== -1) { |
| queryString = queryString.substring(0, hashSignPosition); |
| } |
| } |
| this.#queryString = queryString; |
| return this.#queryString; |
| } |
| |
| get queryParameters(): NameValue[]|null { |
| if (this.#parsedQueryParameters) { |
| return this.#parsedQueryParameters; |
| } |
| const queryString = this.queryString(); |
| if (!queryString) { |
| return null; |
| } |
| this.#parsedQueryParameters = this.parseParameters(queryString); |
| return this.#parsedQueryParameters; |
| } |
| |
| private async parseFormParameters(): Promise<NameValue[]|null> { |
| const requestContentType = this.requestContentType(); |
| |
| if (!requestContentType) { |
| return null; |
| } |
| |
| // Handling application/#x-www-form-urlencoded request bodies. |
| if (requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) { |
| const formData = await this.requestFormData(); |
| if (!formData) { |
| return null; |
| } |
| |
| return this.parseParameters(formData); |
| } |
| |
| // Handling multipart/form-data request bodies. |
| const multipartDetails = requestContentType.match( |
| /^multipart\/form-data\s*;\s*boundary\s*=\s*(\S+)\s*$/, |
| ); |
| |
| if (!multipartDetails) { |
| return null; |
| } |
| |
| const boundary = multipartDetails[1]; |
| if (!boundary) { |
| return null; |
| } |
| |
| const formData = await this.requestFormData(); |
| if (!formData) { |
| return null; |
| } |
| |
| return this.parseMultipartFormDataParameters(formData, boundary); |
| } |
| |
| formParameters(): Promise<NameValue[]|null> { |
| if (!this.#formParametersPromise) { |
| this.#formParametersPromise = this.parseFormParameters(); |
| } |
| return this.#formParametersPromise; |
| } |
| |
| responseHttpVersion(): string { |
| const headersText = this.#responseHeadersText; |
| if (!headersText) { |
| const version = this.responseHeaderValue('version') || this.responseHeaderValue(':version'); |
| if (version) { |
| return version; |
| } |
| return this.filteredProtocolName(); |
| } |
| const firstLine = headersText.split(/\r\n/)[0]; |
| const match = firstLine.match(/^(HTTP\/\d+\.\d+)/); |
| return match ? match[1] : 'HTTP/0.9'; |
| } |
| |
| private parseParameters(queryString: string): NameValue[] { |
| function parseNameValue(pair: string): {name: string, value: string} { |
| const position = pair.indexOf('='); |
| if (position === -1) { |
| return {name: pair, value: ''}; |
| } |
| return { |
| name: pair.substring(0, position), |
| value: pair.substring(position + 1), |
| }; |
| } |
| return queryString.split('&').map(parseNameValue); |
| } |
| |
| /** |
| * Parses multipart/form-data; boundary=boundaryString request bodies - |
| * --boundaryString |
| * Content-Disposition: form-data; #name="field-#name"; filename="r.gif" |
| * Content-Type: application/octet-stream |
| * |
| * optionalValue |
| * --boundaryString |
| * Content-Disposition: form-data; #name="field-#name-2" |
| * |
| * optionalValue2 |
| * --boundaryString-- |
| */ |
| private parseMultipartFormDataParameters( |
| data: string, |
| boundary: string, |
| ): NameValue[] { |
| const sanitizedBoundary = Platform.StringUtilities.escapeForRegExp(boundary); |
| const keyValuePattern = new RegExp( |
| // Header with an optional file #name. |
| '^\\r\\ncontent-disposition\\s*:\\s*form-data\\s*;\\s*name="([^"]*)"(?:\\s*;\\s*filename="([^"]*)")?' + |
| // Optional secondary header with the content type. |
| '(?:\\r\\ncontent-type\\s*:\\s*([^\\r\\n]*))?' + |
| // Padding. |
| '\\r\\n\\r\\n' + |
| // Value |
| '(.*)' + |
| // Padding. |
| '\\r\\n$', |
| 'is', |
| ); |
| const fields = data.split( |
| new RegExp(`--${sanitizedBoundary}(?:--\s*$)?`, 'g'), |
| ); |
| return fields.reduce(parseMultipartField, []); |
| |
| function parseMultipartField( |
| result: NameValue[], |
| field: string, |
| ): NameValue[] { |
| const [match, name, filename, contentType, value] = field.match(keyValuePattern) || []; |
| |
| if (!match) { |
| return result; |
| } |
| |
| const processedValue = filename || contentType ? i18nString(UIStrings.binary) : value; |
| result.push({name, value: processedValue}); |
| |
| return result; |
| } |
| } |
| |
| private computeHeaderValue( |
| headers: NameValue[], |
| headerName: string, |
| ): string|undefined { |
| headerName = headerName.toLowerCase(); |
| |
| const values = []; |
| for (let i = 0; i < headers.length; ++i) { |
| if (headers[i].name.toLowerCase() === headerName) { |
| values.push(headers[i].value); |
| } |
| } |
| if (!values.length) { |
| return undefined; |
| } |
| // Set-Cookie #values should be separated by '\n', not comma, otherwise cookies could not be parsed. |
| if (headerName === 'set-cookie') { |
| return values.join('\n'); |
| } |
| return values.join(', '); |
| } |
| |
| requestContentData(): Promise<TextUtils.ContentData.ContentDataOrError> { |
| if (this.#contentData) { |
| return this.#contentData; |
| } |
| if (this.#contentDataProvider) { |
| this.#contentData = this.#contentDataProvider(); |
| } else { |
| this.#contentData = NetworkManager.requestContentData(this); |
| } |
| return this.#contentData; |
| } |
| |
| setContentDataProvider( |
| dataProvider: () => Promise<TextUtils.ContentData.ContentDataOrError>, |
| ): void { |
| console.assert( |
| !this.#contentData, |
| 'contentData can only be set once.', |
| ); |
| this.#contentDataProvider = dataProvider; |
| } |
| |
| requestStreamingContent(): Promise<TextUtils.StreamingContentData.StreamingContentDataOrError> { |
| if (this.#streamingContentData) { |
| return this.#streamingContentData; |
| } |
| |
| const contentPromise = this.finished ? this.requestContentData() : NetworkManager.streamResponseBody(this); |
| this.#streamingContentData = contentPromise.then(contentData => { |
| if (TextUtils.ContentData.ContentData.isError(contentData)) { |
| return contentData; |
| } |
| // Note that this is save: "streamResponseBody()" always creates base64-based ContentData and |
| // for "contentData()" we'll never call "addChunk". |
| return TextUtils.StreamingContentData.StreamingContentData.from( |
| contentData, |
| ); |
| }); |
| |
| return this.#streamingContentData; |
| } |
| |
| contentURL(): Platform.DevToolsPath.UrlString { |
| return this.#url; |
| } |
| |
| contentType(): Common.ResourceType.ResourceType { |
| return this.#resourceType; |
| } |
| |
| async searchInContent( |
| query: string, |
| caseSensitive: boolean, |
| isRegex: boolean, |
| ): Promise<TextUtils.ContentProvider.SearchMatch[]> { |
| if (!this.#contentDataProvider) { |
| return await NetworkManager.searchInRequest( |
| this, |
| query, |
| caseSensitive, |
| isRegex, |
| ); |
| } |
| |
| const contentData = await this.requestContentData(); |
| if (TextUtils.ContentData.ContentData.isError(contentData) || !contentData.isTextContent) { |
| return []; |
| } |
| return TextUtils.TextUtils.performSearchInContentData( |
| contentData, |
| query, |
| caseSensitive, |
| isRegex, |
| ); |
| } |
| |
| requestContentType(): string|undefined { |
| return this.requestHeaderValue('Content-Type'); |
| } |
| |
| hasErrorStatusCode(): boolean { |
| return this.statusCode >= 400; |
| } |
| |
| setInitialPriority(priority: Protocol.Network.ResourcePriority): void { |
| this.#initialPriority = priority; |
| } |
| |
| initialPriority(): Protocol.Network.ResourcePriority|null { |
| return this.#initialPriority; |
| } |
| |
| setPriority(priority: Protocol.Network.ResourcePriority): void { |
| this.#currentPriority = priority; |
| } |
| |
| priority(): Protocol.Network.ResourcePriority|null { |
| return this.#currentPriority || this.#initialPriority || null; |
| } |
| |
| setSignedExchangeInfo(info: Protocol.Network.SignedExchangeInfo): void { |
| this.#signedExchangeInfo = info; |
| } |
| |
| signedExchangeInfo(): Protocol.Network.SignedExchangeInfo|null { |
| return this.#signedExchangeInfo; |
| } |
| |
| async populateImageSource(image: HTMLImageElement): Promise<void> { |
| const contentData = await this.requestContentData(); |
| if (TextUtils.ContentData.ContentData.isError(contentData)) { |
| return; |
| } |
| let imageSrc = contentData.asDataUrl(); |
| if (imageSrc === null && !this.#failed) { |
| const cacheControl = this.responseHeaderValue('cache-control') || ''; |
| if (!cacheControl.includes('no-cache')) { |
| imageSrc = this.#url; |
| } |
| } |
| if (imageSrc !== null) { |
| image.src = imageSrc; |
| } |
| } |
| |
| initiator(): Protocol.Network.Initiator|null { |
| return this.#initiator || null; |
| } |
| |
| hasUserGesture(): boolean|null { |
| return this.#hasUserGesture ?? null; |
| } |
| |
| frames(): WebSocketFrame[] { |
| return this.#frames; |
| } |
| |
| addProtocolFrameError(errorMessage: string, time: number): void { |
| this.addFrame({ |
| type: WebSocketFrameType.Error, |
| text: errorMessage, |
| time: this.pseudoWallTime(time), |
| opCode: -1, |
| mask: false, |
| }); |
| } |
| |
| addProtocolFrame( |
| response: Protocol.Network.WebSocketFrame, |
| time: number, |
| sent: boolean, |
| ): void { |
| const type = sent ? WebSocketFrameType.Send : WebSocketFrameType.Receive; |
| this.addFrame({ |
| type, |
| text: response.payloadData, |
| time: this.pseudoWallTime(time), |
| opCode: response.opcode, |
| mask: response.mask, |
| }); |
| } |
| |
| addFrame(frame: WebSocketFrame): void { |
| this.#frames.push(frame); |
| this.dispatchEventToListeners(Events.WEBSOCKET_FRAME_ADDED, frame); |
| } |
| |
| directSocketChunks(): DirectSocketChunk[] { |
| return this.#directSocketChunks; |
| } |
| |
| addDirectSocketChunk(chunk: DirectSocketChunk): void { |
| this.#directSocketChunks.push(chunk); |
| this.dispatchEventToListeners(Events.DIRECTSOCKET_CHUNK_ADDED, chunk); |
| } |
| |
| eventSourceMessages(): readonly EventSourceMessage[] { |
| return this.#serverSentEvents?.eventSourceMessages ?? []; |
| } |
| |
| addEventSourceMessage( |
| time: number, |
| eventName: string, |
| eventId: string, |
| data: string, |
| ): void { |
| this.#serverSentEvents?.onProtocolEventSourceMessageReceived( |
| eventName, |
| data, |
| eventId, |
| this.pseudoWallTime(time), |
| ); |
| } |
| |
| markAsRedirect(redirectCount: number): void { |
| this.#isRedirect = true; |
| this.#requestId = `${this.#backendRequestId}:redirected.${redirectCount}`; |
| } |
| |
| isRedirect(): boolean { |
| return this.#isRedirect; |
| } |
| |
| setRequestIdForTest(requestId: Protocol.Network.RequestId): void { |
| this.#backendRequestId = requestId; |
| this.#requestId = requestId; |
| } |
| |
| charset(): string|null { |
| return this.#charset ?? null; |
| } |
| |
| setCharset(charset: string): void { |
| this.#charset = charset; |
| } |
| |
| addExtraRequestInfo(extraRequestInfo: ExtraRequestInfo): void { |
| this.#blockedRequestCookies = extraRequestInfo.blockedRequestCookies; |
| this.setIncludedRequestCookies(extraRequestInfo.includedRequestCookies); |
| this.setRequestHeaders(extraRequestInfo.requestHeaders); |
| this.#hasExtraRequestInfo = true; |
| this.setRequestHeadersText(''); // Mark request headers as non-provisional |
| this.#clientSecurityState = extraRequestInfo.clientSecurityState; |
| this.#appliedNetworkConditionsId = extraRequestInfo.appliedNetworkConditionsId; |
| if (extraRequestInfo.connectTiming) { |
| this.setConnectTimingFromExtraInfo(extraRequestInfo.connectTiming); |
| } |
| this.#siteHasCookieInOtherPartition = extraRequestInfo.siteHasCookieInOtherPartition ?? false; |
| |
| this.#hasThirdPartyCookiePhaseoutIssue = this.#blockedRequestCookies.some( |
| item => item.blockedReasons.includes( |
| Protocol.Network.CookieBlockedReason.ThirdPartyPhaseout, |
| ), |
| ); |
| } |
| |
| setAppliedNetworkConditions(appliedNetworkConditionsId: string): void { |
| this.#appliedNetworkConditionsId = appliedNetworkConditionsId; |
| } |
| |
| hasExtraRequestInfo(): boolean { |
| return this.#hasExtraRequestInfo; |
| } |
| |
| blockedRequestCookies(): BlockedCookieWithReason[] { |
| return this.#blockedRequestCookies; |
| } |
| |
| setIncludedRequestCookies(includedRequestCookies: IncludedCookieWithReason[]): void { |
| this.#includedRequestCookies = includedRequestCookies; |
| } |
| |
| includedRequestCookies(): IncludedCookieWithReason[] { |
| return this.#includedRequestCookies; |
| } |
| |
| hasRequestCookies(): boolean { |
| return (this.#includedRequestCookies.length > 0 || this.#blockedRequestCookies.length > 0); |
| } |
| |
| siteHasCookieInOtherPartition(): boolean { |
| return this.#siteHasCookieInOtherPartition; |
| } |
| |
| // Parse the status text from the first line of the response headers text. |
| // See net::HttpResponseHeaders::GetStatusText. |
| static parseStatusTextFromResponseHeadersText( |
| responseHeadersText: string, |
| ): string { |
| const firstLineParts = responseHeadersText.split('\r')[0].split(' '); |
| return firstLineParts.slice(2).join(' '); |
| } |
| |
| addExtraResponseInfo(extraResponseInfo: ExtraResponseInfo): void { |
| this.#blockedResponseCookies = extraResponseInfo.blockedResponseCookies; |
| if (extraResponseInfo.exemptedResponseCookies) { |
| this.#exemptedResponseCookies = extraResponseInfo.exemptedResponseCookies; |
| } |
| this.#responseCookiesPartitionKey = |
| extraResponseInfo.cookiePartitionKey ? extraResponseInfo.cookiePartitionKey : null; |
| this.#responseCookiesPartitionKeyOpaque = extraResponseInfo.cookiePartitionKeyOpaque || null; |
| this.responseHeaders = extraResponseInfo.responseHeaders; |
| // We store a copy of the headers we initially received, so that after |
| // potential header overrides, we can compare actual with original headers. |
| this.originalResponseHeaders = extraResponseInfo.responseHeaders.map( |
| headerEntry => ({...headerEntry}), |
| ); |
| |
| if (extraResponseInfo.responseHeadersText) { |
| this.responseHeadersText = extraResponseInfo.responseHeadersText; |
| |
| if (!this.requestHeadersText()) { |
| // Generate request headers text from raw headers in extra request info because |
| // Network.requestWillBeSentExtraInfo doesn't include headers text. |
| let requestHeadersText = `${this.requestMethod} ${this.parsedURL.path}`; |
| if (this.parsedURL.queryParams) { |
| requestHeadersText += `?${this.parsedURL.queryParams}`; |
| } |
| requestHeadersText += ' HTTP/1.1\r\n'; |
| |
| for (const {name, value} of this.requestHeaders()) { |
| requestHeadersText += `${name}: ${value}\r\n`; |
| } |
| this.setRequestHeadersText(requestHeadersText); |
| } |
| |
| this.statusText = NetworkRequest.parseStatusTextFromResponseHeadersText( |
| extraResponseInfo.responseHeadersText, |
| ); |
| } |
| this.#remoteAddressSpace = extraResponseInfo.resourceIPAddressSpace; |
| |
| if (extraResponseInfo.statusCode) { |
| this.statusCode = extraResponseInfo.statusCode; |
| } |
| |
| this.#hasExtraResponseInfo = true; |
| |
| // TODO(crbug.com/1252463) Explore replacing this with a DevTools Issue. |
| const networkManager = NetworkManager.forRequest(this); |
| if (!networkManager) { |
| return; |
| } |
| for (const blockedCookie of this.#blockedResponseCookies) { |
| if (blockedCookie.blockedReasons.includes( |
| Protocol.Network.SetCookieBlockedReason.NameValuePairExceedsMaxSize, |
| )) { |
| const message = i18nString(UIStrings.setcookieHeaderIsIgnoredIn, { |
| PH1: this.url(), |
| }); |
| networkManager.dispatchEventToListeners( |
| NetworkManagerEvents.MessageGenerated, |
| {message, requestId: this.#requestId, warning: true}, |
| ); |
| } |
| } |
| |
| const cookieModel = networkManager.target().model(CookieModel); |
| if (!cookieModel) { |
| return; |
| } |
| for (const exemptedCookie of this.#exemptedResponseCookies) { |
| cookieModel.removeBlockedCookie(exemptedCookie.cookie); |
| } |
| for (const blockedCookie of this.#blockedResponseCookies) { |
| const cookie = blockedCookie.cookie; |
| if (!cookie) { |
| continue; |
| } |
| if (blockedCookie.blockedReasons.includes( |
| Protocol.Network.SetCookieBlockedReason.ThirdPartyPhaseout, |
| )) { |
| this.#hasThirdPartyCookiePhaseoutIssue = true; |
| } |
| cookieModel.addBlockedCookie( |
| cookie, |
| blockedCookie.blockedReasons.map(blockedReason => ({ |
| attribute: setCookieBlockedReasonToAttribute(blockedReason), |
| uiString: setCookieBlockedReasonToUiString(blockedReason), |
| })), |
| ); |
| } |
| } |
| |
| hasExtraResponseInfo(): boolean { |
| return this.#hasExtraResponseInfo; |
| } |
| |
| blockedResponseCookies(): BlockedSetCookieWithReason[] { |
| return this.#blockedResponseCookies; |
| } |
| |
| exemptedResponseCookies(): ExemptedSetCookieWithReason[] { |
| return this.#exemptedResponseCookies; |
| } |
| |
| nonBlockedResponseCookies(): Cookie[] { |
| const blockedCookieLines: Array<string|null> = this.blockedResponseCookies().map( |
| blockedCookie => blockedCookie.cookieLine, |
| ); |
| // Use array and remove 1 by 1 to handle the (potential) case of multiple |
| // identical cookies, only some of which are blocked. |
| const responseCookies = this.responseCookies.filter(cookie => { |
| const index = blockedCookieLines.indexOf(cookie.getCookieLine()); |
| if (index !== -1) { |
| blockedCookieLines[index] = null; |
| return false; |
| } |
| return true; |
| }); |
| return responseCookies; |
| } |
| |
| responseCookiesPartitionKey(): Protocol.Network.CookiePartitionKey|null { |
| return this.#responseCookiesPartitionKey; |
| } |
| |
| responseCookiesPartitionKeyOpaque(): boolean|null { |
| return this.#responseCookiesPartitionKeyOpaque; |
| } |
| |
| redirectSourceSignedExchangeInfoHasNoErrors(): boolean { |
| return ( |
| this.#redirectSource !== null && this.#redirectSource.#signedExchangeInfo !== null && |
| !this.#redirectSource.#signedExchangeInfo.errors); |
| } |
| |
| clientSecurityState(): Protocol.Network.ClientSecurityState|undefined { |
| return this.#clientSecurityState; |
| } |
| |
| setTrustTokenParams( |
| trustTokenParams: Protocol.Network.TrustTokenParams, |
| ): void { |
| this.#trustTokenParams = trustTokenParams; |
| } |
| |
| trustTokenParams(): Protocol.Network.TrustTokenParams|undefined { |
| return this.#trustTokenParams; |
| } |
| |
| setTrustTokenOperationDoneEvent( |
| doneEvent: Protocol.Network.TrustTokenOperationDoneEvent, |
| ): void { |
| this.#trustTokenOperationDoneEvent = doneEvent; |
| |
| this.dispatchEventToListeners(Events.TRUST_TOKEN_RESULT_ADDED); |
| } |
| |
| trustTokenOperationDoneEvent():|Protocol.Network.TrustTokenOperationDoneEvent|undefined { |
| return this.#trustTokenOperationDoneEvent; |
| } |
| |
| setIsSameSite(isSameSite: boolean): void { |
| this.#isSameSite = isSameSite; |
| } |
| |
| isSameSite(): boolean|null { |
| return this.#isSameSite; |
| } |
| |
| setIsAdRelated(isAdRelated: boolean): void { |
| this.#isAdRelated = isAdRelated; |
| } |
| |
| isAdRelated(): boolean { |
| return this.#isAdRelated; |
| } |
| |
| getAssociatedData(key: string): object|null { |
| return this.#associatedData.get(key) || null; |
| } |
| |
| setAssociatedData(key: string, data: object): void { |
| this.#associatedData.set(key, data); |
| } |
| |
| deleteAssociatedData(key: string): void { |
| this.#associatedData.delete(key); |
| } |
| |
| hasThirdPartyCookiePhaseoutIssue(): boolean { |
| return this.#hasThirdPartyCookiePhaseoutIssue; |
| } |
| |
| addDataReceivedEvent({ |
| timestamp, |
| dataLength, |
| encodedDataLength, |
| data, |
| }: Protocol.Network.DataReceivedEvent): void { |
| this.resourceSize += dataLength; |
| if (encodedDataLength !== -1) { |
| this.increaseTransferSize(encodedDataLength); |
| } |
| this.endTime = timestamp; |
| if (data) { |
| void this.#streamingContentData?.then(contentData => { |
| if (!TextUtils.StreamingContentData.isError(contentData)) { |
| contentData.addChunk(data); |
| } |
| }); |
| } |
| } |
| |
| waitForResponseReceived(): Promise<void> { |
| if (this.responseReceivedPromise) { |
| return this.responseReceivedPromise; |
| } |
| const {promise, resolve} = Promise.withResolvers<void>(); |
| this.responseReceivedPromise = promise; |
| this.responseReceivedPromiseResolve = resolve; |
| return this.responseReceivedPromise; |
| } |
| } |
| |
| export enum Events { |
| FINISHED_LOADING = 'FinishedLoading', |
| TIMING_CHANGED = 'TimingChanged', |
| REMOTE_ADDRESS_CHANGED = 'RemoteAddressChanged', |
| REQUEST_HEADERS_CHANGED = 'RequestHeadersChanged', |
| RESPONSE_HEADERS_CHANGED = 'ResponseHeadersChanged', |
| WEBSOCKET_FRAME_ADDED = 'WebsocketFrameAdded', |
| DIRECTSOCKET_CHUNK_ADDED = 'DirectsocketChunkAdded', |
| EVENT_SOURCE_MESSAGE_ADDED = 'EventSourceMessageAdded', |
| TRUST_TOKEN_RESULT_ADDED = 'TrustTokenResultAdded', |
| } |
| |
| export interface EventTypes { |
| [Events.FINISHED_LOADING]: NetworkRequest; |
| [Events.TIMING_CHANGED]: NetworkRequest; |
| [Events.REMOTE_ADDRESS_CHANGED]: NetworkRequest; |
| [Events.REQUEST_HEADERS_CHANGED]: void; |
| [Events.RESPONSE_HEADERS_CHANGED]: void; |
| [Events.WEBSOCKET_FRAME_ADDED]: WebSocketFrame; |
| [Events.DIRECTSOCKET_CHUNK_ADDED]: DirectSocketChunk; |
| [Events.DIRECTSOCKET_CHUNK_ADDED]: DirectSocketChunk; |
| [Events.EVENT_SOURCE_MESSAGE_ADDED]: EventSourceMessage; |
| [Events.TRUST_TOKEN_RESULT_ADDED]: void; |
| } |
| |
| export const enum InitiatorType { |
| OTHER = 'other', |
| PARSER = 'parser', |
| REDIRECT = 'redirect', |
| SCRIPT = 'script', |
| PRELOAD = 'preload', |
| SIGNED_EXCHANGE = 'signedExchange', |
| PREFLIGHT = 'preflight', |
| } |
| |
| export enum WebSocketFrameType { |
| /* eslint-disable @typescript-eslint/naming-convention -- Used by web_tests. */ |
| Send = 'send', |
| Receive = 'receive', |
| Error = 'error', |
| /* eslint-enable @typescript-eslint/naming-convention */ |
| } |
| |
| export const cookieExemptionReasonToUiString = function( |
| exemptionReason: Protocol.Network.CookieExemptionReason, |
| ): string { |
| switch (exemptionReason) { |
| case Protocol.Network.CookieExemptionReason.UserSetting: |
| return i18nString(UIStrings.exemptionReasonUserSetting); |
| case Protocol.Network.CookieExemptionReason.TPCDMetadata: |
| return i18nString(UIStrings.exemptionReasonTPCDMetadata); |
| case Protocol.Network.CookieExemptionReason.TopLevelTPCDDeprecationTrial: |
| return i18nString(UIStrings.exemptionReasonTopLevelTPCDDeprecationTrial); |
| case Protocol.Network.CookieExemptionReason.TPCDDeprecationTrial: |
| return i18nString(UIStrings.exemptionReasonTPCDDeprecationTrial); |
| case Protocol.Network.CookieExemptionReason.TPCDHeuristics: |
| return i18nString(UIStrings.exemptionReasonTPCDHeuristics); |
| case Protocol.Network.CookieExemptionReason.EnterprisePolicy: |
| return i18nString(UIStrings.exemptionReasonEnterprisePolicy); |
| case Protocol.Network.CookieExemptionReason.StorageAccess: |
| return i18nString(UIStrings.exemptionReasonStorageAccessAPI); |
| case Protocol.Network.CookieExemptionReason.TopLevelStorageAccess: |
| return i18nString(UIStrings.exemptionReasonTopLevelStorageAccessAPI); |
| case Protocol.Network.CookieExemptionReason.Scheme: |
| return i18nString(UIStrings.exemptionReasonScheme); |
| } |
| return ''; |
| }; |
| |
| export const cookieBlockedReasonToUiString = function( |
| blockedReason: Protocol.Network.CookieBlockedReason, |
| ): string { |
| switch (blockedReason) { |
| case Protocol.Network.CookieBlockedReason.SecureOnly: |
| return i18nString(UIStrings.secureOnly); |
| case Protocol.Network.CookieBlockedReason.NotOnPath: |
| return i18nString(UIStrings.notOnPath); |
| case Protocol.Network.CookieBlockedReason.DomainMismatch: |
| return i18nString(UIStrings.domainMismatch); |
| case Protocol.Network.CookieBlockedReason.SameSiteStrict: |
| return i18nString(UIStrings.sameSiteStrict); |
| case Protocol.Network.CookieBlockedReason.SameSiteLax: |
| return i18nString(UIStrings.sameSiteLax); |
| case Protocol.Network.CookieBlockedReason.SameSiteUnspecifiedTreatedAsLax: |
| return i18nString(UIStrings.sameSiteUnspecifiedTreatedAsLax); |
| case Protocol.Network.CookieBlockedReason.SameSiteNoneInsecure: |
| return i18nString(UIStrings.sameSiteNoneInsecure); |
| case Protocol.Network.CookieBlockedReason.UserPreferences: |
| return i18nString(UIStrings.userPreferences); |
| case Protocol.Network.CookieBlockedReason.UnknownError: |
| return i18nString(UIStrings.unknownError); |
| case Protocol.Network.CookieBlockedReason.SchemefulSameSiteStrict: |
| return i18nString(UIStrings.schemefulSameSiteStrict); |
| case Protocol.Network.CookieBlockedReason.SchemefulSameSiteLax: |
| return i18nString(UIStrings.schemefulSameSiteLax); |
| case Protocol.Network.CookieBlockedReason.SchemefulSameSiteUnspecifiedTreatedAsLax: |
| return i18nString(UIStrings.schemefulSameSiteUnspecifiedTreatedAsLax); |
| case Protocol.Network.CookieBlockedReason.SamePartyFromCrossPartyContext: |
| return i18nString(UIStrings.samePartyFromCrossPartyContext); |
| case Protocol.Network.CookieBlockedReason.NameValuePairExceedsMaxSize: |
| return i18nString(UIStrings.nameValuePairExceedsMaxSize); |
| case Protocol.Network.CookieBlockedReason.ThirdPartyPhaseout: |
| return i18nString(UIStrings.thirdPartyPhaseout); |
| } |
| return ''; |
| }; |
| |
| export const setCookieBlockedReasonToUiString = function( |
| blockedReason: Protocol.Network.SetCookieBlockedReason, |
| ): string { |
| switch (blockedReason) { |
| case Protocol.Network.SetCookieBlockedReason.SecureOnly: |
| return i18nString(UIStrings.blockedReasonSecureOnly); |
| case Protocol.Network.SetCookieBlockedReason.SameSiteStrict: |
| return i18nString(UIStrings.blockedReasonSameSiteStrictLax, { |
| PH1: 'SameSite=Strict', |
| }); |
| case Protocol.Network.SetCookieBlockedReason.SameSiteLax: |
| return i18nString(UIStrings.blockedReasonSameSiteStrictLax, { |
| PH1: 'SameSite=Lax', |
| }); |
| case Protocol.Network.SetCookieBlockedReason.SameSiteUnspecifiedTreatedAsLax: |
| return i18nString(UIStrings.blockedReasonSameSiteUnspecifiedTreatedAsLax); |
| case Protocol.Network.SetCookieBlockedReason.SameSiteNoneInsecure: |
| return i18nString(UIStrings.blockedReasonSameSiteNoneInsecure); |
| case Protocol.Network.SetCookieBlockedReason.UserPreferences: |
| return i18nString(UIStrings.thisSetcookieWasBlockedDueToUser); |
| case Protocol.Network.SetCookieBlockedReason.SyntaxError: |
| return i18nString(UIStrings.thisSetcookieHadInvalidSyntax); |
| case Protocol.Network.SetCookieBlockedReason.SchemeNotSupported: |
| return i18nString(UIStrings.theSchemeOfThisConnectionIsNot); |
| case Protocol.Network.SetCookieBlockedReason.OverwriteSecure: |
| return i18nString(UIStrings.blockedReasonOverwriteSecure); |
| case Protocol.Network.SetCookieBlockedReason.InvalidDomain: |
| return i18nString(UIStrings.blockedReasonInvalidDomain); |
| case Protocol.Network.SetCookieBlockedReason.InvalidPrefix: |
| return i18nString(UIStrings.blockedReasonInvalidPrefix); |
| case Protocol.Network.SetCookieBlockedReason.UnknownError: |
| return i18nString(UIStrings.anUnknownErrorWasEncounteredWhenTrying); |
| case Protocol.Network.SetCookieBlockedReason.SchemefulSameSiteStrict: |
| return i18nString( |
| UIStrings.thisSetcookieWasBlockedBecauseItHadTheSamesiteStrictLax, |
| {PH1: 'SameSite=Strict'}, |
| ); |
| case Protocol.Network.SetCookieBlockedReason.SchemefulSameSiteLax: |
| return i18nString( |
| UIStrings.thisSetcookieWasBlockedBecauseItHadTheSamesiteStrictLax, |
| {PH1: 'SameSite=Lax'}, |
| ); |
| case Protocol.Network.SetCookieBlockedReason.SchemefulSameSiteUnspecifiedTreatedAsLax: |
| return i18nString(UIStrings.thisSetcookieDidntSpecifyASamesite); |
| case Protocol.Network.SetCookieBlockedReason.SamePartyFromCrossPartyContext: |
| return i18nString( |
| UIStrings.thisSetcookieWasBlockedBecauseItHadTheSameparty, |
| ); |
| case Protocol.Network.SetCookieBlockedReason.SamePartyConflictsWithOtherAttributes: |
| return i18nString( |
| UIStrings.thisSetcookieWasBlockedBecauseItHadTheSamepartyAttribute, |
| ); |
| case Protocol.Network.SetCookieBlockedReason.NameValuePairExceedsMaxSize: |
| return i18nString( |
| UIStrings.thisSetcookieWasBlockedBecauseTheNameValuePairExceedsMaxSize, |
| ); |
| case Protocol.Network.SetCookieBlockedReason.DisallowedCharacter: |
| return i18nString(UIStrings.thisSetcookieHadADisallowedCharacter); |
| case Protocol.Network.SetCookieBlockedReason.ThirdPartyPhaseout: |
| return i18nString(UIStrings.thisSetcookieWasBlockedDueThirdPartyPhaseout); |
| } |
| return ''; |
| }; |
| |
| export const cookieBlockedReasonToAttribute = function( |
| blockedReason: Protocol.Network.CookieBlockedReason, |
| ): Attribute|null { |
| switch (blockedReason) { |
| case Protocol.Network.CookieBlockedReason.SecureOnly: |
| return Attribute.SECURE; |
| case Protocol.Network.CookieBlockedReason.NotOnPath: |
| return Attribute.PATH; |
| case Protocol.Network.CookieBlockedReason.DomainMismatch: |
| return Attribute.DOMAIN; |
| case Protocol.Network.CookieBlockedReason.SameSiteStrict: |
| case Protocol.Network.CookieBlockedReason.SameSiteLax: |
| case Protocol.Network.CookieBlockedReason.SameSiteUnspecifiedTreatedAsLax: |
| case Protocol.Network.CookieBlockedReason.SameSiteNoneInsecure: |
| case Protocol.Network.CookieBlockedReason.SchemefulSameSiteStrict: |
| case Protocol.Network.CookieBlockedReason.SchemefulSameSiteLax: |
| case Protocol.Network.CookieBlockedReason.SchemefulSameSiteUnspecifiedTreatedAsLax: |
| return Attribute.SAME_SITE; |
| case Protocol.Network.CookieBlockedReason.SamePartyFromCrossPartyContext: |
| case Protocol.Network.CookieBlockedReason.NameValuePairExceedsMaxSize: |
| case Protocol.Network.CookieBlockedReason.UserPreferences: |
| case Protocol.Network.CookieBlockedReason.ThirdPartyPhaseout: |
| case Protocol.Network.CookieBlockedReason.UnknownError: |
| return null; |
| } |
| return null; |
| }; |
| |
| export const setCookieBlockedReasonToAttribute = function( |
| blockedReason: Protocol.Network.SetCookieBlockedReason, |
| ): Attribute|null { |
| switch (blockedReason) { |
| case Protocol.Network.SetCookieBlockedReason.SecureOnly: |
| case Protocol.Network.SetCookieBlockedReason.OverwriteSecure: |
| return Attribute.SECURE; |
| case Protocol.Network.SetCookieBlockedReason.SameSiteStrict: |
| case Protocol.Network.SetCookieBlockedReason.SameSiteLax: |
| case Protocol.Network.SetCookieBlockedReason.SameSiteUnspecifiedTreatedAsLax: |
| case Protocol.Network.SetCookieBlockedReason.SameSiteNoneInsecure: |
| case Protocol.Network.SetCookieBlockedReason.SchemefulSameSiteStrict: |
| case Protocol.Network.SetCookieBlockedReason.SchemefulSameSiteLax: |
| case Protocol.Network.SetCookieBlockedReason.SchemefulSameSiteUnspecifiedTreatedAsLax: |
| return Attribute.SAME_SITE; |
| case Protocol.Network.SetCookieBlockedReason.InvalidDomain: |
| return Attribute.DOMAIN; |
| case Protocol.Network.SetCookieBlockedReason.InvalidPrefix: |
| return Attribute.NAME; |
| case Protocol.Network.SetCookieBlockedReason.SamePartyConflictsWithOtherAttributes: |
| case Protocol.Network.SetCookieBlockedReason.SamePartyFromCrossPartyContext: |
| case Protocol.Network.SetCookieBlockedReason.NameValuePairExceedsMaxSize: |
| case Protocol.Network.SetCookieBlockedReason.UserPreferences: |
| case Protocol.Network.SetCookieBlockedReason.ThirdPartyPhaseout: |
| case Protocol.Network.SetCookieBlockedReason.SyntaxError: |
| case Protocol.Network.SetCookieBlockedReason.SchemeNotSupported: |
| case Protocol.Network.SetCookieBlockedReason.UnknownError: |
| case Protocol.Network.SetCookieBlockedReason.DisallowedCharacter: |
| return null; |
| } |
| return null; |
| }; |
| |
| export interface NameValue { |
| name: string; |
| value: string; |
| } |
| |
| export interface WebSocketFrame { |
| type: WebSocketFrameType; |
| time: number; |
| text: string; |
| opCode: number; |
| mask: boolean; |
| } |
| |
| export interface BlockedSetCookieWithReason { |
| blockedReasons: Protocol.Network.SetCookieBlockedReason[]; |
| cookieLine: string; |
| cookie: Cookie|null; |
| } |
| |
| export interface BlockedCookieWithReason { |
| cookie: Cookie; |
| blockedReasons: Protocol.Network.CookieBlockedReason[]; |
| } |
| |
| export interface IncludedCookieWithReason { |
| cookie: Cookie; |
| exemptionReason: Protocol.Network.CookieExemptionReason|undefined; |
| } |
| |
| export interface ExemptedSetCookieWithReason { |
| cookie: Cookie; |
| cookieLine: string; |
| exemptionReason: Protocol.Network.CookieExemptionReason; |
| } |
| |
| export interface EventSourceMessage { |
| time: number; |
| eventName: string; |
| eventId: string; |
| data: string; |
| } |
| |
| export interface ExtraRequestInfo { |
| blockedRequestCookies: Array<{blockedReasons: Protocol.Network.CookieBlockedReason[], cookie: Cookie}>; |
| requestHeaders: NameValue[]; |
| includedRequestCookies: IncludedCookieWithReason[]; |
| clientSecurityState?: Protocol.Network.ClientSecurityState; |
| connectTiming: Protocol.Network.ConnectTiming; |
| siteHasCookieInOtherPartition?: boolean; |
| appliedNetworkConditionsId?: string; |
| } |
| |
| export interface ExtraResponseInfo { |
| blockedResponseCookies: |
| Array<{blockedReasons: Protocol.Network.SetCookieBlockedReason[], cookieLine: string, cookie: Cookie|null}>; |
| responseHeaders: NameValue[]; |
| responseHeadersText?: string; |
| resourceIPAddressSpace: Protocol.Network.IPAddressSpace; |
| statusCode: number|undefined; |
| cookiePartitionKey?: Protocol.Network.CookiePartitionKey; |
| cookiePartitionKeyOpaque: boolean|undefined; |
| exemptedResponseCookies:| |
| Array<{cookie: Cookie, cookieLine: string, exemptionReason: Protocol.Network.CookieExemptionReason}>|undefined; |
| } |
| |
| export interface EarlyHintsInfo { |
| responseHeaders: NameValue[]; |
| } |
| |
| export type OverrideType = 'content'|'headers'; |
| |
| export enum DirectSocketType { |
| TCP = 1, |
| UDP_BOUND = 2, |
| UDP_CONNECTED = 3, |
| } |
| |
| export enum DirectSocketStatus { |
| OPENING = 1, |
| OPEN = 2, |
| CLOSED = 3, |
| ABORTED = 4, |
| } |
| |
| export interface DirectSocketCreateOptions { |
| remoteAddr?: string; |
| remotePort?: number; |
| localAddr?: string; |
| localPort?: number; |
| noDelay?: boolean; |
| keepAliveDelay?: number; |
| sendBufferSize?: number; |
| receiveBufferSize?: number; |
| dnsQueryType?: Protocol.Network.DirectSocketDnsQueryType; |
| multicastLoopback?: boolean; |
| multicastTimeToLive?: number; |
| multicastAllowAddressSharing?: boolean; |
| } |
| |
| export interface DirectSocketOpenInfo { |
| remoteAddr?: string; |
| remotePort?: number; |
| localAddr?: string; |
| localPort?: number; |
| } |
| |
| export interface DirectSocketInfo { |
| type: DirectSocketType; |
| status: DirectSocketStatus; |
| errorMessage?: string; |
| createOptions: DirectSocketCreateOptions; |
| openInfo?: DirectSocketOpenInfo; |
| joinedMulticastGroups?: Set<string>; |
| } |
| |
| export interface DirectSocketChunk { |
| data: string; |
| type: DirectSocketChunkType; |
| timestamp: number; |
| // Only for bound udp socket. |
| remoteAddress?: string; |
| remotePort?: number; |
| } |
| |
| export enum DirectSocketChunkType { |
| SEND = 'send', |
| RECEIVE = 'receive', |
| } |