chrome-devtools-frontend
Version:
Chrome DevTools UI
219 lines (197 loc) • 9.64 kB
text/typescript
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as i18n from '../../../core/i18n/i18n.js';
import * as Platform from '../../../core/platform/platform.js';
import * as SDK from '../../../core/sdk/sdk.js';
import type * as Protocol from '../../../generated/protocol.js';
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
import * as LegacyWrapper from '../../../ui/components/legacy_wrapper/legacy_wrapper.js';
import * as Coordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
import * as ReportView from '../../../ui/components/report_view/report_view.js';
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
const UIStrings = {
/**
*@description The origin of a URL (https://web.dev/same-site-same-origin/#origin).
*(for a lot of languages this does not need to be translated, please translate only where necessary)
*/
origin: 'Origin',
/**
*@description Site (https://web.dev/same-site-same-origin/#site) for the URL the user sees in the omnibox.
*/
topLevelSite: 'Top-level site',
/**
*@description Text to show in the top-level site row, in case the value is opaque (https://html.spec.whatwg.org/#concept-origin-opaque).
*/
opaque: '(opaque)',
/**
*@description Whether the storage corresponds to an opaque key (similar to https://html.spec.whatwg.org/#concept-origin-opaque).
*/
isOpaque: 'Is opaque',
/**
*@description Whether the storage corresponds to a third-party origin (https://web.dev/learn/privacy/third-parties/).
*/
isThirdParty: 'Is third-party',
/**
*@description Text indicating that the condition holds.
*/
yes: 'Yes',
/**
*@description Text indicating that the condition does not hold.
*/
no: 'No',
/**
*@description Text indicating that the storage corresponds to a third-party origin because top-level site is opaque.
*/
yesBecauseTopLevelIsOpaque: 'Yes, because the top-level site is opaque',
/**
*@description Text indicating that the storage corresponds to a third-party origin because the storage key is opaque.
*/
yesBecauseKeyIsOpaque: 'Yes, because the storage key is opaque',
/**
*@description Text indicating that the storage corresponds to a third-party origin because the origin doesn't match the top-level site.
*/
yesBecauseOriginNotInTopLevelSite: 'Yes, because the origin is outside of the top-level site',
/**
*@description Text indicating that the storage corresponds to a third-party origin because the was a third-party origin in the ancestry chain.
*/
yesBecauseAncestorChainHasCrossSite: 'Yes, because the ancestry chain contains a third-party origin',
/**
*@description Text when something is loading.
*/
loading: 'Loading…',
/**
*@description The storage bucket name (https://wicg.github.io/storage-buckets/explainer#bucket-names)
*/
bucketName: 'Bucket name',
/**
*@description Text indicating that the storage is persistent (https://wicg.github.io/storage-buckets/explainer#storage-policy-persistence)
*/
persistent: 'Is persistent',
/**
*@description The storage durability policy (https://wicg.github.io/storage-buckets/explainer#storage-policy-durability)
*/
durability: 'Durability',
/**
*@description The storage quota (https://wicg.github.io/storage-buckets/explainer#storage-policy-quota)
*/
quota: 'Quota',
/**
*@description The storage expiration (https://wicg.github.io/storage-buckets/explainer#storage-policy-expiration)
*/
expiration: 'Expiration',
/**
*@description Text indicating that no value is set
*/
none: 'None',
};
const str_ = i18n.i18n.registerUIStrings('panels/application/components/StorageMetadataView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();
export class StorageMetadataView extends LegacyWrapper.LegacyWrapper.WrappableComponent {
static readonly litTagName = LitHtml.literal`devtools-storage-metadata-view`;
readonly #shadow = this.attachShadow({mode: 'open'});
#storageKey: SDK.StorageKeyManager.StorageKey|null = null;
#storageBucket: Protocol.Storage.StorageBucketInfo|null = null;
getShadow(): ShadowRoot {
return this.#shadow;
}
setStorageKey(storageKey: string): void {
this.#storageKey = SDK.StorageKeyManager.parseStorageKey(storageKey);
void this.render();
}
setStorageBucket(storageBucket: Protocol.Storage.StorageBucketInfo): void {
this.#storageBucket = storageBucket;
this.setStorageKey(storageBucket.bucket.storageKey);
}
override render(): Promise<void> {
return coordinator.write('StorageMetadataView render', async () => {
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
LitHtml.render(LitHtml.html`
<${ReportView.ReportView.Report.litTagName} .data=${{reportTitle: this.getTitle() || i18nString(UIStrings.loading)} as ReportView.ReportView.ReportData}>
${await this.renderReportContent()}
</${ReportView.ReportView.Report.litTagName}>`, this.#shadow, {host: this});
// clang-format on
});
}
getTitle(): string|undefined {
return this.#storageKey?.origin;
}
key(content: string|LitHtml.TemplateResult): LitHtml.TemplateResult {
return LitHtml.html`<${ReportView.ReportView.ReportKey.litTagName}>${content}</${
ReportView.ReportView.ReportKey.litTagName}>`;
}
value(content: string|LitHtml.TemplateResult): LitHtml.TemplateResult {
return LitHtml.html`<${ReportView.ReportView.ReportValue.litTagName}>${content}</${
ReportView.ReportView.ReportValue.litTagName}>`;
}
async renderReportContent(): Promise<LitHtml.LitTemplate> {
if (!this.#storageKey) {
return LitHtml.nothing;
}
const origin = this.#storageKey.origin;
const ancestorChainHasCrossSite =
Boolean(this.#storageKey.components.get(SDK.StorageKeyManager.StorageKeyComponent.ANCESTOR_CHAIN_BIT));
const hasNonce = Boolean(this.#storageKey.components.get(SDK.StorageKeyManager.StorageKeyComponent.NONCE_HIGH));
const topLevelSiteIsOpaque = Boolean(
this.#storageKey.components.get(SDK.StorageKeyManager.StorageKeyComponent.TOP_LEVEL_SITE_OPAQUE_NONCE_HIGH));
const topLevelSite = this.#storageKey.components.get(SDK.StorageKeyManager.StorageKeyComponent.TOP_LEVEL_SITE);
const thirdPartyReason = ancestorChainHasCrossSite ? i18nString(UIStrings.yesBecauseAncestorChainHasCrossSite) :
hasNonce ? i18nString(UIStrings.yesBecauseKeyIsOpaque) :
topLevelSiteIsOpaque ? i18nString(UIStrings.yesBecauseTopLevelIsOpaque) :
(topLevelSite && origin !== topLevelSite) ? i18nString(UIStrings.yesBecauseOriginNotInTopLevelSite) :
null;
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
return LitHtml.html`
${this.key(i18nString(UIStrings.origin))}
${this.value(LitHtml.html`<div class="text-ellipsis" title=${origin}>${origin}</div>`)}
${(topLevelSite || topLevelSiteIsOpaque) ? this.key(i18nString(UIStrings.topLevelSite)) : LitHtml.nothing}
${topLevelSite ? this.value(topLevelSite) : LitHtml.nothing}
${topLevelSiteIsOpaque ? this.value(i18nString(UIStrings.opaque)) : LitHtml.nothing}
${thirdPartyReason ? LitHtml.html`${this.key(i18nString(UIStrings.isThirdParty))}${this.value(thirdPartyReason)}` : LitHtml.nothing}
${hasNonce || topLevelSiteIsOpaque ?
this.key(i18nString(UIStrings.isOpaque)) : LitHtml.nothing}
${hasNonce ? this.value(i18nString(UIStrings.yes)) : LitHtml.nothing}
${topLevelSiteIsOpaque ?
this.value(i18nString(UIStrings.yesBecauseTopLevelIsOpaque)) : LitHtml.nothing}
${this.#storageBucket ? this.#renderStorageBucketInfo() : LitHtml.nothing}`;
// clang-format on
}
#renderStorageBucketInfo(): LitHtml.LitTemplate {
if (!this.#storageBucket) {
throw new Error('Should not call #renderStorageBucketInfo if #bucket is null.');
}
const {bucket: {name}, persistent, durability, quota} = this.#storageBucket;
// clang-format off
return LitHtml.html`
${this.key(i18nString(UIStrings.bucketName))}
${this.value(name || 'default')}
${this.key(i18nString(UIStrings.persistent))}
${this.value(persistent ? i18nString(UIStrings.yes) : i18nString(UIStrings.no))}
${this.key(i18nString(UIStrings.durability))}
${this.value(durability)}
${this.key(i18nString(UIStrings.quota))}
${this.value(Platform.NumberUtilities.bytesToString(quota))}
${this.key(i18nString(UIStrings.expiration))}
${this.value(this.#getExpirationString())}`;
}
#getExpirationString(): string {
if (!this.#storageBucket) {
throw new Error('Should not call #getExpirationString if #bucket is null.');
}
const {expiration} = this.#storageBucket;
if (expiration === 0) {
return i18nString(UIStrings.none);
}
return (new Date(expiration * 1000)).toLocaleString();
}
}
ComponentHelpers.CustomElements.defineComponent('devtools-storage-metadata-view', StorageMetadataView);
declare global {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface HTMLElementTagNameMap {
'devtools-storage-metadata-view': StorageMetadataView;
}
}