chrome-devtools-frontend
Version:
Chrome DevTools UI
1,097 lines (1,067 loc) • 49.4 kB
text/typescript
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../../ui/components/report_view/report_view.js';
import '../../ui/legacy/components/data_grid/data_grid.js';
import type * as Common from '../../core/common/common.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Protocol from '../../generated/protocol.js';
import * as UI from '../../ui/legacy/legacy.js';
import {Directives, html, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
import {
DeviceBoundSessionModelEvents,
type DeviceBoundSessionsModel,
type SessionAndEvents
} from './DeviceBoundSessionsModel.js';
import deviceBoundSessionsViewStyles from './deviceBoundSessionsView.css.js';
const {widgetConfig} = UI.Widget;
const UIStrings = {
/**
*@description Label for a site, e.g. https://example.com/.
*/
keySite: 'Site',
/**
*@description Label for the ID of a session.
*/
keyId: 'ID',
/**
*@description Label that shows the URL that can be used to refresh a session.
*/
refreshUrl: 'Refresh URL',
/**
*@description Section header for how a session's scope is defined.
*/
scope: 'Scope',
/**
*@description Section header for HTTP cookies.
*/
cookieCravings: 'Cookies',
/**
*@description Label for the name of an HTTP cookie.
*/
name: 'Name',
/**
*@description Label for an expiration date.
*/
expiryDate: 'Expiry date',
/**
*@description Label for a cryptographic string challenge that has been cached for a session.
*/
cachedChallenge: 'Cached challenge',
/**
*@description Label for the HTTP initiator that is allowed to trigger a refresh of a session.
*/
allowedRefreshInitiators: 'Allowed refresh initiators',
/**
*@description Section header for a session's basic configuration.
*/
sessionConfig: 'Session config',
/**
*@description Label for an HTTP origin.
*/
origin: 'Origin',
/**
*@description Text for whether a site is included.
*/
includeSite: 'Include site',
/**
*@description Value for a label that indicates that a site is included.
*/
yes: 'Yes',
/**
*@description Value for a label that indicates that a site is not included.
*/
no: 'No',
/**
*@description Label the host pattern of a URL, e.g. *.example.com
*/
ruleHostPattern: 'Host pattern',
/**
*@description Label for the path prefix of a URL, e.g. /path/1/2/3
*/
rulePathPrefix: 'Path prefix',
/**
*@description The type of a rule. The possible types are "exclude" or "include".
*/
ruleType: 'Rule type',
/**
*@description Text describing that a rule excludes something.
*/
ruleTypeExclude: 'Exclude',
/**
*@description Text describing that a rule includes something.
*/
ruleTypeInclude: 'Include',
/**
*@description Label for an event that has created something.
*/
creation: 'Creation',
/**
*@description Label for an event that has refreshed something.
*/
refresh: 'Refresh',
/**
*@description Label for an event that has set a cryptographic string challenge.
*/
challenge: 'Challenge',
/**
*@description Label for an event that has terminated something.
*/
termination: 'Termination',
/**
*@description Label for an event whose type is not known.
*/
unknown: 'Unknown',
/**
*@description Heading for a section that will display events that have occurred.
*/
events: 'Events',
/**
*@description Section header for details about an event.
*/
eventDetails: 'Event details',
/**
*@description Placeholder text when no row is selected in a table of events.
*/
selectEventToViewDetails: 'Select an event row to view more details.',
/**
*@description Column heading for the type of event that has occurred.
*/
type: 'Type',
/**
*@description Column heading for the date + time that an event occurred.
*/
timestamp: 'Date',
/**
*@description Column heading for the result of an event (whether it succeeded or had an error).
*/
result: 'Result',
/**
*@description Notes the result status of an event was that it succeeded.
*/
success: 'Success',
/**
*@description Notes the result status of an event was that it had an error.
*/
error: 'Error',
/**
*@description Default message when no events have appeared yet.
*/
noEvents: 'No events have been logged yet.',
/**
*@description Text to preserve the log of events after refreshing.
*/
preserveLog: 'Preserve log',
/**
*@description Tooltip text that appears on the preserve log setting when hovering over it.
*/
doNotClearLogOnPageReload: 'Do not clear log on page reload/navigation.',
/**
*@description Label for the ID of a session.
*/
sessionId: 'Session ID',
/**
*@description Label for the result of an event (whether it succeeded or had an error).
*/
eventResult: 'Event result',
/**
*@description Label for the result of fetching new session information.
*/
fetchResult: 'Fetch result',
/**
*@description Label for whether a session's basic configuration was updated. The corresponding value is yes or no.
*/
updatedSessionConfig: 'Updated session config',
/**
*@description Label for the result of an attempted refresh.
*/
refreshResult: 'Refresh result',
/**
*@description Label for whether a particular event caused any HTTP request to be deferred (i.e. paused and
* later unpaused). The corresponding value is yes or no.
*/
causedAnyRequestDeferrals: 'Caused any request deferrals',
/**
*@description Label for the result of attempting to set a cryptographic string challenge.
*/
challengeResult: 'Challenge result',
/**
*@description Label for the reason why a session was deleted.
*/
deletionReason: 'Deletion reason',
/**
*@description Explanation for an event outcome. Key refers to a cryptographic key.
*/
keyError: 'Key error',
/**
*@description Explanation for an event outcome. Signing refers to cryptographic signing.
*/
signingError: 'Signing error',
/**
*@description Explanation for an event outcome.
*/
serverRequestedTermination: 'Server requested termination',
/**
*@description Explanation for an event outcome.
*/
invalidSessionId: 'Invalid session ID',
/**
*@description Explanation for an event outcome. Challenge refers to a cryptographic string challenge.
*/
invalidChallenge: 'Invalid challenge',
/**
*@description Explanation for an event outcome. Challenge refers to a cryptographic string challenge.
*/
tooManyChallenges: 'Too many challenges',
/**
*@description Explanation for an event outcome.
*/
invalidFetcherUrl: 'Invalid fetcher URL',
/**
*@description Explanation for an event outcome.
*/
invalidRefreshUrl: 'Invalid refresh URL',
/**
*@description Explanation for an event outcome.
*/
transientHttpError: 'Transient HTTP error',
/**
*@description Explanation for an event outcome. This means there is a URL origin written into a session configuration's scope that is causing failures because it's for a different site.
*/
scopeOriginSameSiteMismatch: 'Same-site mismatch scope origin',
/**
*@description Explanation for an event outcome. This means the session configuration's URL for refreshing is causing failures because it's for a different site.
*/
refreshUrlSameSiteMismatch: 'Same-site mismatch refresh URL',
/**
*@description Explanation for an event outcome. This means the session configuration's session ID does not match the relevant session ID.
*/
mismatchedSessionId: 'Mismatched session ID',
/**
*@description Explanation for an event outcome.
*/
missingScope: 'Missing scope',
/**
*@description Explanation for an event outcome. This means the credentials field in the session configuration is missing.
*/
noCredentials: 'No credentials',
/**
*@description Explanation for an event outcome.
*/
subdomainRegistrationWellKnownUnavailable: 'Subdomain registration .well-known unavailable',
/**
*@description Explanation for an event outcome.
*/
subdomainRegistrationUnauthorized: '.well-known did not authorize registration by subdomain',
/**
*@description Explanation for an event outcome.
*/
subdomainRegistrationWellKnownMalformed: 'Subdomain registration .well-known content malformed',
/**
*@description Explanation for an event outcome.
*/
sessionProviderWellKnownUnavailable: 'Session provider .well-known unavailable',
/**
*@description Explanation for an event outcome.
*/
relyingPartyWellKnownUnavailable: 'Relying party .well-known unavailable',
/**
*@description Explanation for an event outcome. This refers to a JSON Web Key thumbprint (https://www.rfc-editor.org/rfc/rfc7638). Federated sessions are described in https://w3c.github.io/webappsec-dbsc/.
*/
federatedKeyThumbprintMismatch: 'Federated key had incorrect thumbprint',
/**
*@description Explanation for an event outcome. Federated sessions are described in https://w3c.github.io/webappsec-dbsc/.
*/
invalidFederatedSessionUrl: 'Federated provider URL not valid',
/**
*@description Explanation for an event outcome. Federated sessions are described in https://w3c.github.io/webappsec-dbsc/.
*/
invalidFederatedKey: 'Federated key invalid',
/**
*@description Explanation for an event outcome. Origin labels are described in https://w3c.github.io/webappsec-dbsc/.
*/
tooManyRelyingOriginLabels: 'Too many relying origin labels in .well-known',
/**
*@description Explanation for an event outcome.
*/
boundCookieSetForbidden: 'Registration in a context that cannot set bound cookies',
/**
*@description Explanation for an event outcome.
*/
netError: 'Network error',
/**
*@description Explanation for an event outcome.
*/
proxyError: 'Proxy error',
/**
*@description Explanation for an event outcome.
*/
emptySessionConfig: 'Empty session configuration for registration',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsConfig: 'Invalid credentials configuration',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsType: 'Invalid credentials - empty or non-cookie type',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsEmptyName: 'Invalid credentials - empty name',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookie: 'Invalid credentials - cookie invalid',
/**
*@description Explanation for an event outcome.
*/
persistentHttpError: 'Persistent HTTP error',
/**
*@description Explanation for an event outcome. Challenge refers to a cryptographic string challenge.
*/
registrationAttemptedChallenge: 'Registration returned challenge error response code',
/**
*@description Explanation for an event outcome. This refers to a URL's origin.
*/
invalidScopeOrigin: 'Invalid scope origin',
/**
*@description Explanation for an event outcome. This refers to an URL's path / origin.
*/
scopeOriginContainsPath: 'Scope origin contains a path',
/**
*@description Explanation for an event outcome. This refers to an HTTP request's initiator.
*/
refreshInitiatorNotString: 'Allowed refresh initiator is not a string',
/**
*@description Explanation for an event outcome. This refers to an HTTP request's initiator and a URL's host.
*/
refreshInitiatorInvalidHostPattern: 'Allowed refresh initiator has invalid host pattern',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidScopeSpecification: 'Invalid scope specification',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
missingScopeSpecificationType: 'Missing scope specification type',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
emptyScopeSpecificationDomain: 'Empty scope specification domain',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
emptyScopeSpecificationPath: 'Empty scope specification path',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidScopeSpecificationType: 'Scope specification type is neiher include or exclude',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidScopeIncludeSite: 'Invalid include_site in scope',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
missingScopeIncludeSite: 'Missing include_site in scope',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
federatedNotAuthorizedByProvider: 'Federated session not authorized by provider .well-known',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
federatedNotAuthorizedByRelyingParty: 'Federated session not authorized by relying party .well-known',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
sessionProviderWellKnownMalformed: 'Session provider .well-known content malformed',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
sessionProviderWellKnownHasProviderOrigin: 'Session provider .well-known content has provider_origin',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
relyingPartyWellKnownMalformed: 'Relying party .well-known content malformed',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
relyingPartyWellKnownHasRelyingOrigins: 'Relying party .well-known content has relying_origins',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidFederatedSessionProviderSessionMissing: 'Federated session invalid due to provider session not found',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidFederatedSessionWrongProviderOrigin: 'Federated session invalid due to provider origin mismatch',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookieCreationTime: 'Invalid credentials - cookie creation time invalid',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookieName: 'Invalid credentials - cookie name invalid',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookieParsing: 'Invalid credentials - cookie parsing failed',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookieUnpermittedAttribute: 'Invalid credentials - cookie attribute not permitted',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookieInvalidDomain: 'Invalid credentials - cookie invalid domain',
/**
*@description Explanation for an event outcome.
*/
invalidCredentialsCookiePrefix: 'Invalid credentials - cookie invalid prefix',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidScopeRulePath: 'Invalid scope rule path',
/**
*@description Explanation for an event outcome. Scope specification is defined in https://w3c.github.io/webappsec-dbsc/.
*/
invalidScopeRuleHostPattern: 'Invalid scope rule host pattern',
/**
*@description Explanation for an event outcome. A session can be scoped to just a specific URL origin. This error means that the session's origin does not match the provided URL host pattern.
*/
scopeRuleOriginScopedHostPatternMismatch: 'Origin-scoped session has mismatch between host pattern and origin',
/**
*@description Explanation for an event outcome. A session can be scoped to an entire site. This error means that the session's site does not match the provided URL host pattern.
*/
scopeRuleSiteScopedHostPatternMismatch: 'Site-scoped session has mismatch between host pattern and site',
/**
*@description Explanation for an event outcome. This refers to cryptographic signing.
*/
signingQuotaExceeded: 'Signing quota exceeded',
/**
*@description Explanation for an event outcome.
*/
invalidConfigJson: 'Invalid session configuration JSON',
/**
*@description Explanation for an event outcome. Federated sessions are defined in https://w3c.github.io/webappsec-dbsc/. Key refers to a cryptographic key.
*/
invalidFederatedSessionProviderFailedToRestoreKey:
'Federated session invalid due to failure to restore session provider key',
/**
*@description Explanation for an event outcome. Key refers to a cryptographic key.
*/
failedToUnwrapKey: 'Failed to unwrap key',
/**
*@description Explanation for an event outcome.
*/
sessionDeletedDuringRefresh: 'Session deleted during refresh',
/**
*@description Explanation for an event outcome.
*/
refreshed: 'Refreshed',
/**
*@description Explanation for an event outcome.
*/
initializedService: 'Service initialized',
/**
*@description Explanation for an event outcome.
*/
unreachable: 'Endpoint unreachable',
/**
*@description Explanation for an event outcome.
*/
serverError: 'Endpoint transient error',
/**
*@description Explanation for an event outcome.
*/
refreshQuotaExceeded: 'Refresh quota exceeded',
/**
*@description Explanation for an event outcome.
*/
fatalError: 'Fatal error',
/**
*@description Explanation for an event outcome.
*/
noSessionId: 'No session ID',
/**
*@description Explanation for an event outcome.
*/
noSessionMatch: 'No matching session ID',
/**
*@description Explanation for an event outcome.
*/
cantSetBoundCookie: 'Not allowed to set bound cookie',
/**
*@description Explanation for an event outcome.
*/
expired: 'Expired',
/**
*@description Explanation for an event outcome. Key refers to a cryptographic key. This means there was an attempt to read a key from disk but it failed.
*/
failedToRestoreKey: 'Failed to restore key from disk',
/**
*@description Explanation for an event outcome.
*/
storagePartitionCleared: 'Removed from storage partition',
/**
*@description Explanation for an event outcome.
*/
clearBrowsingData: 'User-initiated browser data removal',
/**
*@description Explanation for an event outcome.
*/
invalidSessionParams: 'Invalid session parameters',
/**
*@description Explanation for an event outcome.
*/
refreshFatalError: 'Fatal error during refresh',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/application/DeviceBoundSessionsView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
interface ViewInput {
sessionAndEvents?: SessionAndEvents;
preserveLogSetting?: Common.Settings.Setting<boolean>;
defaultTitle?: string;
defaultDescription?: string;
selectedEvent?: Protocol.Network.DeviceBoundSessionEventOccurredEvent;
onEventRowSelected?(selectedEvent?: Protocol.Network.DeviceBoundSessionEventOccurredEvent|undefined): void;
}
type ViewOutput = object;
export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTMLElement): void => {
const {sessionAndEvents, preserveLogSetting, defaultTitle, defaultDescription, selectedEvent, onEventRowSelected} =
input;
const toolbarHtml = preserveLogSetting ?
html`
<devtools-toolbar class="device-bound-sessions-toolbar">
<devtools-checkbox title=${i18nString(UIStrings.doNotClearLogOnPageReload)} ${
UI.UIUtils.bindToSetting(preserveLogSetting)}>${i18nString(UIStrings.preserveLog)}</devtools-checkbox>
</devtools-toolbar>
` :
nothing;
if (!sessionAndEvents) {
if (!defaultTitle || !defaultDescription) {
render(nothing, target);
return;
}
render(
html`
<style>${UI.inspectorCommonStyles}</style>
<style>${deviceBoundSessionsViewStyles}</style>
${toolbarHtml}
<devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget, {
header: defaultTitle,
text: defaultDescription
})} jslog=${VisualLogging.pane('device-bound-sessions-empty')}></devtools-widget>
`,
target);
return;
}
let sessionDetailsHtml: TemplateResult|undefined;
if (sessionAndEvents.session) {
const {key, inclusionRules, cookieCravings} = sessionAndEvents.session;
sessionDetailsHtml = html`
<devtools-report>
<devtools-report-section-header>${i18nString(UIStrings.sessionConfig)}</devtools-report-section-header>
<devtools-report-key>${i18nString(UIStrings.keySite)}</devtools-report-key>
<devtools-report-value>${key.site}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.keyId)}</devtools-report-key>
<devtools-report-value>${key.id}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.refreshUrl)}</devtools-report-key>
<devtools-report-value>${sessionAndEvents.session.refreshUrl}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.expiryDate)}</devtools-report-key>
<devtools-report-value>${
new Date(sessionAndEvents.session.expiryDate * 1000).toLocaleString()}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.cachedChallenge)}</devtools-report-key>
<devtools-report-value>${sessionAndEvents.session.cachedChallenge || ''}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.allowedRefreshInitiators)}</devtools-report-key>
<devtools-report-value>${sessionAndEvents.session.allowedRefreshInitiators.join(', ')}</devtools-report-value>
<devtools-report-section-header>${i18nString(UIStrings.scope)}</devtools-report-section-header>
<devtools-report-key>${i18nString(UIStrings.origin)}</devtools-report-key>
<devtools-report-value>${inclusionRules.origin}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.includeSite)}</devtools-report-key>
<devtools-report-value>${boolToString(inclusionRules.includeSite)}</devtools-report-value>
</devtools-report>
${
inclusionRules.urlRules.length > 0 ? html`
<div class="device-bound-session-grid-wrapper">
<devtools-data-grid class="device-bound-session-url-rules-grid" striped inline>
<table>
<thead>
<tr>
<th id="should-include" weight="1" sortable>${i18nString(UIStrings.ruleType)}</th>
<th id="host-pattern" weight="2" sortable>${i18nString(UIStrings.ruleHostPattern)}</th>
<th id="path-prefix" weight="2" sortable>${i18nString(UIStrings.rulePathPrefix)}</th>
</tr>
</thead>
<tbody>
${inclusionRules.urlRules.map(rule => html`
<tr>
<td>${ruleTypeToString(rule.ruleType)}</td>
<td>${rule.hostPattern}</td>
<td>${rule.pathPrefix}</td>
</tr>
`)}
</tbody>
</table>
</devtools-data-grid>
</div>
` :
nothing}
<devtools-report-section-header>${i18nString(UIStrings.cookieCravings)}</devtools-report-section-header>
${
cookieCravings.length > 0 ? html`
<div class="device-bound-session-grid-wrapper">
<devtools-data-grid class="device-bound-session-cookie-cravings-grid" striped inline>
<table>
<thead>
<tr>
<th id="name" weight="2" sortable>${i18nString(UIStrings.name)}</th>
<th id="domain" weight="2" sortable>${i18n.i18n.lockedString('Domain')}</th>
<th id="path" weight="2" sortable>${i18n.i18n.lockedString('Path')}</th>
<th id="secure" type="boolean" align="center" weight="1" sortable>${
i18n.i18n.lockedString('Secure')}</th>
<th id="http-only" type="boolean" align="center" weight="1" sortable>${
i18n.i18n.lockedString('HttpOnly')}</th>
<th id="same-site" weight="1" sortable>${i18n.i18n.lockedString('SameSite')}</th>
</tr>
</thead>
<tbody>
${cookieCravings.map(craving => html`
<tr>
<td>${craving.name}</td>
<td>${craving.domain}</td>
<td>${craving.path}</td>
<td>${craving.secure}</td>
<td>${craving.httpOnly}</td>
<td>${craving.sameSite}</td>
</tr>
`)}
</tbody>
</table>
</devtools-data-grid>
</div>
` :
nothing}`;
}
const events = [...sessionAndEvents.eventsById.values()];
const eventsHtml = html`
<devtools-report-section-header>${i18nString(UIStrings.events)}</devtools-report-section-header>
${
events.length > 0 && onEventRowSelected ?
html`
<div class="device-bound-session-grid-wrapper">
<devtools-data-grid class="device-bound-session-events-grid" striped inline ${
Directives.ref((el?: Element) => {
if (!el || !(el instanceof HTMLElement)) {
return;
}
const grid = el as HTMLElement & {deselectRow(): void};
if (!selectedEvent) {
grid.deselectRow();
}
})}>
<table>
<thead>
<tr>
<th id="type" weight="1" sortable>${i18nString(UIStrings.type)}</th>
<th id="timestamp" weight="2" sortable>${i18nString(UIStrings.timestamp)}</th>
<th id="details" weight="2" sortable>${i18nString(UIStrings.result)}</th>
</tr>
</thead>
<tbody>${events.map(({event, timestamp}) => html`
<tr @select=${(): void => onEventRowSelected(event)}>
<td>${getEventTypeString(event)}</td>
<td>${timestamp.toLocaleString()}</td>
<td>${succeededToString(event.succeeded)}</td>
</tr>
`)}
</tbody>
</table>
</devtools-data-grid>
</div>
` :
html`<div class="device-bound-session-no-events-wrapper">${i18nString(UIStrings.noEvents)}</div>`}`;
const creationEventDetails =
selectedEvent?.creationEventDetails &&
html`
<devtools-report-key>${i18nString(UIStrings.fetchResult)}</devtools-report-key>
<devtools-report-value>${
fetchResultToString(selectedEvent.creationEventDetails.fetchResult)}</devtools-report-value>
${selectedEvent.creationEventDetails.newSession && html`
<devtools-report-key>${i18nString(UIStrings.updatedSessionConfig)}</devtools-report-key>
<devtools-report-value>${i18nString(UIStrings.yes)}</devtools-report-value>
`}
`;
const refreshEventDetails =
selectedEvent?.refreshEventDetails &&
html`
<devtools-report-key>${i18nString(UIStrings.refreshResult)}</devtools-report-key>
<devtools-report-value>${
refreshResultToString(selectedEvent.refreshEventDetails.refreshResult)}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.causedAnyRequestDeferrals)}</devtools-report-key>
<devtools-report-value>${
boolToString(!selectedEvent.refreshEventDetails.wasFullyProactiveRefresh)}</devtools-report-value>
${
selectedEvent.refreshEventDetails.fetchResult &&
html`
<devtools-report-key>${i18nString(UIStrings.fetchResult)}</devtools-report-key>
<devtools-report-value>${
fetchResultToString(selectedEvent.refreshEventDetails.fetchResult)}</devtools-report-value>
`}
${selectedEvent.refreshEventDetails.newSession && html`
<devtools-report-key>${i18nString(UIStrings.updatedSessionConfig)}</devtools-report-key>
<devtools-report-value>${i18nString(UIStrings.yes)}</devtools-report-value>
`}
`;
const challengeEventDetails =
selectedEvent?.challengeEventDetails &&
html`
<devtools-report-key>${i18nString(UIStrings.challengeResult)}</devtools-report-key>
<devtools-report-value>${
challengeResultToString(selectedEvent.challengeEventDetails.challengeResult)}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.challenge)}</devtools-report-key>
<devtools-report-value>${selectedEvent.challengeEventDetails.challenge}</devtools-report-value>
`;
const terminationEventDetails =
selectedEvent?.terminationEventDetails &&
html`
<devtools-report-key>${i18nString(UIStrings.deletionReason)}</devtools-report-key>
<devtools-report-value>${
deletionReasonToString(selectedEvent.terminationEventDetails.deletionReason)}</devtools-report-value>
`;
const eventDetailsContentHtml = selectedEvent ?
html`
<devtools-report>
<devtools-report-key>${i18nString(UIStrings.keySite)}</devtools-report-key>
<devtools-report-value>${selectedEvent.site}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.sessionId)}</devtools-report-key>
<devtools-report-value>${selectedEvent.sessionId}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.type)}</devtools-report-key>
<devtools-report-value>${getEventTypeString(selectedEvent)}</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.eventResult)}</devtools-report-key>
<devtools-report-value>${succeededToString(selectedEvent.succeeded)}</devtools-report-value>
${creationEventDetails}
${refreshEventDetails}
${challengeEventDetails}
${terminationEventDetails}
</devtools-report>
` :
html`<div class="device-bound-session-no-event-details">${i18nString(UIStrings.selectEventToViewDetails)}</div>`;
const eventDetailsHtml = html`
<devtools-report-section-header>${i18nString(UIStrings.eventDetails)}</devtools-report-section-header>
${eventDetailsContentHtml}
`;
render(
html`
<style>${UI.inspectorCommonStyles}</style>
<style>${deviceBoundSessionsViewStyles}</style>
${toolbarHtml}
<devtools-split-view sidebar-position="second">
<div slot="main" class="device-bound-session-view-wrapper">
${sessionDetailsHtml || nothing}
${eventsHtml}
</div>
<div slot="sidebar" class="device-bound-session-sidebar">
${eventDetailsHtml}
</div>
</devtools-split-view>`,
target);
};
export class DeviceBoundSessionsView extends UI.Widget.VBox {
#site?: string;
#sessionId?: string;
#model?: DeviceBoundSessionsModel;
#view: typeof DEFAULT_VIEW;
#defaultTitle?: string;
#defaultDescription?: string;
#selectedEvent?: Protocol.Network.DeviceBoundSessionEventOccurredEvent;
constructor(view: typeof DEFAULT_VIEW = DEFAULT_VIEW) {
super({jslog: `${VisualLogging.pane('device-bound-sessions')}`});
this.#view = view;
}
showSession(model: DeviceBoundSessionsModel, site: string, sessionId?: string): void {
this.#defaultTitle = undefined;
this.#defaultDescription = undefined;
this.#site = site;
this.#sessionId = sessionId;
this.#attachModel(model);
this.performUpdate();
}
showDefault(model: DeviceBoundSessionsModel, defaultTitle: string, defaultDescription: string): void {
this.#defaultTitle = defaultTitle;
this.#defaultDescription = defaultDescription;
this.#site = undefined;
this.#sessionId = undefined;
this.#attachModel(model);
this.performUpdate();
}
#attachModel(model: DeviceBoundSessionsModel): void {
if (this.#model) {
this.#model.removeEventListener(DeviceBoundSessionModelEvents.EVENT_OCCURRED, this.performUpdate, this);
this.#model.removeEventListener(DeviceBoundSessionModelEvents.CLEAR_EVENTS, this.performUpdate, this);
}
this.#model = model;
this.#model.addEventListener(DeviceBoundSessionModelEvents.EVENT_OCCURRED, this.performUpdate, this);
this.#model.addEventListener(DeviceBoundSessionModelEvents.CLEAR_EVENTS, this.performUpdate, this);
if (this.#selectedEvent) {
this.#selectedEvent = undefined;
}
}
override performUpdate(): void {
let sessionAndEvents: SessionAndEvents|undefined;
let preserveLogSetting: Common.Settings.Setting<boolean>|undefined;
if (this.#model) {
preserveLogSetting = this.#model.getPreserveLogSetting();
if (this.#site) {
sessionAndEvents = this.#model.getSession(this.#site, this.#sessionId);
}
}
this.#view(
{
sessionAndEvents,
preserveLogSetting,
defaultTitle: this.#defaultTitle,
defaultDescription: this.#defaultDescription,
selectedEvent: this.#selectedEvent,
onEventRowSelected: this.#onEventRowSelected.bind(this),
},
{}, this.contentElement);
}
#onEventRowSelected(selectedEvent?: Protocol.Network.DeviceBoundSessionEventOccurredEvent): void {
this.#selectedEvent = selectedEvent;
this.performUpdate();
}
}
function ruleTypeToString(ruleType: Protocol.Network.DeviceBoundSessionUrlRuleRuleType): string {
switch (ruleType) {
case Protocol.Network.DeviceBoundSessionUrlRuleRuleType.Exclude:
return i18nString(UIStrings.ruleTypeExclude);
case Protocol.Network.DeviceBoundSessionUrlRuleRuleType.Include:
return i18nString(UIStrings.ruleTypeInclude);
default:
return ruleType;
}
}
function getEventTypeString(event: Protocol.Network.DeviceBoundSessionEventOccurredEvent): string {
if (event.creationEventDetails) {
return i18nString(UIStrings.creation);
}
if (event.refreshEventDetails) {
return i18nString(UIStrings.refresh);
}
if (event.challengeEventDetails) {
return i18nString(UIStrings.challenge);
}
if (event.terminationEventDetails) {
return i18nString(UIStrings.termination);
}
return i18nString(UIStrings.unknown);
}
function fetchResultToString(fetchResult: Protocol.Network.DeviceBoundSessionFetchResult): string {
switch (fetchResult) {
case Protocol.Network.DeviceBoundSessionFetchResult.Success:
return i18nString(UIStrings.success);
case Protocol.Network.DeviceBoundSessionFetchResult.KeyError:
return i18nString(UIStrings.keyError);
case Protocol.Network.DeviceBoundSessionFetchResult.SigningError:
return i18nString(UIStrings.signingError);
case Protocol.Network.DeviceBoundSessionFetchResult.ServerRequestedTermination:
return i18nString(UIStrings.serverRequestedTermination);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidSessionId:
return i18nString(UIStrings.invalidSessionId);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidChallenge:
return i18nString(UIStrings.invalidChallenge);
case Protocol.Network.DeviceBoundSessionFetchResult.TooManyChallenges:
return i18nString(UIStrings.tooManyChallenges);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidFetcherUrl:
return i18nString(UIStrings.invalidFetcherUrl);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidRefreshUrl:
return i18nString(UIStrings.invalidRefreshUrl);
case Protocol.Network.DeviceBoundSessionFetchResult.TransientHttpError:
return i18nString(UIStrings.transientHttpError);
case Protocol.Network.DeviceBoundSessionFetchResult.ScopeOriginSameSiteMismatch:
return i18nString(UIStrings.scopeOriginSameSiteMismatch);
case Protocol.Network.DeviceBoundSessionFetchResult.RefreshUrlSameSiteMismatch:
return i18nString(UIStrings.refreshUrlSameSiteMismatch);
case Protocol.Network.DeviceBoundSessionFetchResult.MismatchedSessionId:
return i18nString(UIStrings.mismatchedSessionId);
case Protocol.Network.DeviceBoundSessionFetchResult.MissingScope:
return i18nString(UIStrings.missingScope);
case Protocol.Network.DeviceBoundSessionFetchResult.NoCredentials:
return i18nString(UIStrings.noCredentials);
case Protocol.Network.DeviceBoundSessionFetchResult.SubdomainRegistrationWellKnownUnavailable:
return i18nString(UIStrings.subdomainRegistrationWellKnownUnavailable);
case Protocol.Network.DeviceBoundSessionFetchResult.SubdomainRegistrationUnauthorized:
return i18nString(UIStrings.subdomainRegistrationUnauthorized);
case Protocol.Network.DeviceBoundSessionFetchResult.SubdomainRegistrationWellKnownMalformed:
return i18nString(UIStrings.subdomainRegistrationWellKnownMalformed);
case Protocol.Network.DeviceBoundSessionFetchResult.SessionProviderWellKnownUnavailable:
return i18nString(UIStrings.sessionProviderWellKnownUnavailable);
case Protocol.Network.DeviceBoundSessionFetchResult.RelyingPartyWellKnownUnavailable:
return i18nString(UIStrings.relyingPartyWellKnownUnavailable);
case Protocol.Network.DeviceBoundSessionFetchResult.FederatedKeyThumbprintMismatch:
return i18nString(UIStrings.federatedKeyThumbprintMismatch);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidFederatedSessionUrl:
return i18nString(UIStrings.invalidFederatedSessionUrl);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidFederatedKey:
return i18nString(UIStrings.invalidFederatedKey);
case Protocol.Network.DeviceBoundSessionFetchResult.TooManyRelyingOriginLabels:
return i18nString(UIStrings.tooManyRelyingOriginLabels);
case Protocol.Network.DeviceBoundSessionFetchResult.BoundCookieSetForbidden:
return i18nString(UIStrings.boundCookieSetForbidden);
case Protocol.Network.DeviceBoundSessionFetchResult.NetError:
return i18nString(UIStrings.netError);
case Protocol.Network.DeviceBoundSessionFetchResult.ProxyError:
return i18nString(UIStrings.proxyError);
case Protocol.Network.DeviceBoundSessionFetchResult.EmptySessionConfig:
return i18nString(UIStrings.emptySessionConfig);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsConfig:
return i18nString(UIStrings.invalidCredentialsConfig);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsType:
return i18nString(UIStrings.invalidCredentialsType);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsEmptyName:
return i18nString(UIStrings.invalidCredentialsEmptyName);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookie:
return i18nString(UIStrings.invalidCredentialsCookie);
case Protocol.Network.DeviceBoundSessionFetchResult.PersistentHttpError:
return i18nString(UIStrings.persistentHttpError);
case Protocol.Network.DeviceBoundSessionFetchResult.RegistrationAttemptedChallenge:
return i18nString(UIStrings.registrationAttemptedChallenge);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidScopeOrigin:
return i18nString(UIStrings.invalidScopeOrigin);
case Protocol.Network.DeviceBoundSessionFetchResult.ScopeOriginContainsPath:
return i18nString(UIStrings.scopeOriginContainsPath);
case Protocol.Network.DeviceBoundSessionFetchResult.RefreshInitiatorNotString:
return i18nString(UIStrings.refreshInitiatorNotString);
case Protocol.Network.DeviceBoundSessionFetchResult.RefreshInitiatorInvalidHostPattern:
return i18nString(UIStrings.refreshInitiatorInvalidHostPattern);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidScopeSpecification:
return i18nString(UIStrings.invalidScopeSpecification);
case Protocol.Network.DeviceBoundSessionFetchResult.MissingScopeSpecificationType:
return i18nString(UIStrings.missingScopeSpecificationType);
case Protocol.Network.DeviceBoundSessionFetchResult.EmptyScopeSpecificationDomain:
return i18nString(UIStrings.emptyScopeSpecificationDomain);
case Protocol.Network.DeviceBoundSessionFetchResult.EmptyScopeSpecificationPath:
return i18nString(UIStrings.emptyScopeSpecificationPath);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidScopeSpecificationType:
return i18nString(UIStrings.invalidScopeSpecificationType);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidScopeIncludeSite:
return i18nString(UIStrings.invalidScopeIncludeSite);
case Protocol.Network.DeviceBoundSessionFetchResult.MissingScopeIncludeSite:
return i18nString(UIStrings.missingScopeIncludeSite);
case Protocol.Network.DeviceBoundSessionFetchResult.FederatedNotAuthorizedByProvider:
return i18nString(UIStrings.federatedNotAuthorizedByProvider);
case Protocol.Network.DeviceBoundSessionFetchResult.FederatedNotAuthorizedByRelyingParty:
return i18nString(UIStrings.federatedNotAuthorizedByRelyingParty);
case Protocol.Network.DeviceBoundSessionFetchResult.SessionProviderWellKnownMalformed:
return i18nString(UIStrings.sessionProviderWellKnownMalformed);
case Protocol.Network.DeviceBoundSessionFetchResult.SessionProviderWellKnownHasProviderOrigin:
return i18nString(UIStrings.sessionProviderWellKnownHasProviderOrigin);
case Protocol.Network.DeviceBoundSessionFetchResult.RelyingPartyWellKnownMalformed:
return i18nString(UIStrings.relyingPartyWellKnownMalformed);
case Protocol.Network.DeviceBoundSessionFetchResult.RelyingPartyWellKnownHasRelyingOrigins:
return i18nString(UIStrings.relyingPartyWellKnownHasRelyingOrigins);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidFederatedSessionProviderSessionMissing:
return i18nString(UIStrings.invalidFederatedSessionProviderSessionMissing);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidFederatedSessionWrongProviderOrigin:
return i18nString(UIStrings.invalidFederatedSessionWrongProviderOrigin);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookieCreationTime:
return i18nString(UIStrings.invalidCredentialsCookieCreationTime);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookieName:
return i18nString(UIStrings.invalidCredentialsCookieName);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookieParsing:
return i18nString(UIStrings.invalidCredentialsCookieParsing);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookieUnpermittedAttribute:
return i18nString(UIStrings.invalidCredentialsCookieUnpermittedAttribute);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookieInvalidDomain:
return i18nString(UIStrings.invalidCredentialsCookieInvalidDomain);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidCredentialsCookiePrefix:
return i18nString(UIStrings.invalidCredentialsCookiePrefix);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidScopeRulePath:
return i18nString(UIStrings.invalidScopeRulePath);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidScopeRuleHostPattern:
return i18nString(UIStrings.invalidScopeRuleHostPattern);
case Protocol.Network.DeviceBoundSessionFetchResult.ScopeRuleOriginScopedHostPatternMismatch:
return i18nString(UIStrings.scopeRuleOriginScopedHostPatternMismatch);
case Protocol.Network.DeviceBoundSessionFetchResult.ScopeRuleSiteScopedHostPatternMismatch:
return i18nString(UIStrings.scopeRuleSiteScopedHostPatternMismatch);
case Protocol.Network.DeviceBoundSessionFetchResult.SigningQuotaExceeded:
return i18nString(UIStrings.signingQuotaExceeded);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidConfigJson:
return i18nString(UIStrings.invalidConfigJson);
case Protocol.Network.DeviceBoundSessionFetchResult.InvalidFederatedSessionProviderFailedToRestoreKey:
return i18nString(UIStrings.invalidFederatedSessionProviderFailedToRestoreKey);
case Protocol.Network.DeviceBoundSessionFetchResult.FailedToUnwrapKey:
return i18nString(UIStrings.failedToUnwrapKey);
case Protocol.Network.DeviceBoundSessionFetchResult.SessionDeletedDuringRefresh:
return i18nString(UIStrings.sessionDeletedDuringRefresh);
default:
return fetchResult;
}
}
function refreshResultToString(refreshResult: Protocol.Network.RefreshEventDetailsRefreshResult): string {
switch (refreshResult) {
case Protocol.Network.RefreshEventDetailsRefreshResult.Refreshed:
return i18nString(UIStrings.refreshed);
case Protocol.Network.RefreshEventDetailsRefreshResult.InitializedService:
return i18nString(UIStrings.initializedService);
case Protocol.Network.RefreshEventDetailsRefreshResult.Unreachable:
return i18nString(UIStrings.unreachable);
case Protocol.Network.RefreshEventDetailsRefreshResult.ServerError:
return i18nString(UIStrings.serverError);
case Protocol.Network.RefreshEventDetailsRefreshResult.RefreshQuotaExceeded:
return i18nString(UIStrings.refreshQuotaExceeded);
case Protocol.Network.RefreshEventDetailsRefreshResult.FatalError:
return i18nString(UIStrings.fatalError);
case Protocol.Network.RefreshEventDetailsRefreshResult.SigningQuotaExceeded:
return i18nString(UIStrings.signingQuotaExceeded);
default:
return refreshResult;
}
}
function challengeResultToString(challengeResult: Protocol.Network.ChallengeEventDetailsChallengeResult): string {
switch (challengeResult) {
case Protocol.Network.ChallengeEventDetailsChallengeResult.Success:
return i18nString(UIStrings.success);
case Protocol.Network.ChallengeEventDetailsChallengeResult.NoSessionId:
return i18nString(UIStrings.noSessionId);
case Protocol.Network.ChallengeEventDetailsChallengeResult.NoSessionMatch:
return i18nString(UIStrings.noSessionMatch);
case Protocol.Network.ChallengeEventDetailsChallengeResult.CantSetBoundCookie:
return i18nString(UIStrings.cantSetBoundCookie);
default:
return challengeResult;
}
}
function deletionReasonToString(deletionReason: Protocol.Network.TerminationEventDetailsDeletionReason): string {
switch (deletionReason) {
case Protocol.Network.TerminationEventDetailsDeletionReason.Expired:
return i18nString(UIStrings.expired);
case Protocol.Network.TerminationEventDetailsDeletionReason.FailedToRestoreKey:
return i18nString(UIStrings.failedToRestoreKey);
case Protocol.Network.TerminationEventDetailsDeletionReason.FailedToUnwrapKey:
return i18nString(UIStrings.failedToUnwrapKey);
case Protocol.Network.TerminationEventDetailsDeletionReason.StoragePartitionCleared:
return i18nString(UIStrings.storagePartitionCleared);
case Protocol.Network.TerminationEventDetailsDeletionReason.ClearBrowsingData:
return i18nString(UIStrings.clearBrowsingData);
case Protocol.Network.TerminationEventDetailsDeletionReason.ServerRequested:
return i18nString(UIStrings.serverRequestedTermination);
case Protocol.Network.TerminationEventDetailsDeletionReason.InvalidSessionParams:
return i18nString(UIStrings.invalidSessionParams);
case Protocol.Network.TerminationEventDetailsDeletionReason.RefreshFatalError:
return i18nString(UIStrings.refreshFatalError);
default:
return deletionReason;
}
}
function boolToString(bool: boolean): string {
return bool ? i18nString(UIStrings.yes) : i18nString(UIStrings.no);
}
function succeededToString(succeeded: boolean): string {
return succeeded ? i18nString(UIStrings.success) : i18nString(UIStrings.error);
}