mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-30 01:07:01 +08:00
Allow deletion of Trust Tokens
This CL adds a new column to the Trust Tokens table in the Application panel. The column contains a delete button for each row, that can be used to delete all Trust Tokens issued by the corresponding issuer. Screenshot: https://imgur.com/a/gkfXcQ7 R=jacktfranklin@chromium.org, sigurds@chromium.org Bug: chromium:1126824 Change-Id: I816221ed130a9c00f6ea2ee69f5eec5734464d7d Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2748085 Commit-Queue: Simon Zünd <szuend@chromium.org> Reviewed-by: Sigurd Schneider <sigurds@chromium.org> Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
This commit is contained in:
@@ -111,6 +111,7 @@ grd_files_release_sources = [
|
||||
"front_end/Images/toolbarResizerVertical.png",
|
||||
"front_end/Images/touchCursor.png",
|
||||
"front_end/Images/touchCursor_2x.png",
|
||||
"front_end/Images/trash_bin_icon.svg",
|
||||
"front_end/Images/treeoutlineTriangles.svg",
|
||||
"front_end/Images/warning_icon.svg",
|
||||
"front_end/Images/whatsnew.avif",
|
||||
|
||||
@@ -102,6 +102,7 @@ devtools_image_files = [
|
||||
"toolbarResizerVertical.png",
|
||||
"touchCursor_2x.png",
|
||||
"touchCursor.png",
|
||||
"trash_bin_icon.svg",
|
||||
"treeoutlineTriangles.svg",
|
||||
"warning_icon.svg",
|
||||
"whatsnew.avif",
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
"smallIcons.svg": "10719c45b1004a4c4dd236e590deded1",
|
||||
"sources_panel_icon.svg": "48769389e8622535a65ccfcb755e74a1",
|
||||
"switcherIcon.svg": "796f5ed707e44fc587858a0c76c448eb",
|
||||
"trash_bin_icon.svg": "de378e55632db9bbc3da1a3b7dc32acb",
|
||||
"treeoutlineTriangles.svg": "aa6c8f8df9ae7c50f14397131808e764",
|
||||
"warning_icon.svg": "4d6d7934fb97ecb318fb8b9122b1a750"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2.91 4.233" height="16" width="11"><path d="M.265 1.323h2.38L2.25 3.969H.661M2.381.53h-.529V.264h-.794v.264H.53a.27.27 0 00-.264.265v.264H2.646V.794A.27.27 0 002.38.529"/></svg>
|
||||
|
After Width: | Height: | Size: 230 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2.91 4.233" height="16" width="11"><path d="M.265 1.323h2.38L2.25 3.969H.661M2.381.53h-.529V.264h-.794v.264H.53a.27.27 0 0 0-.264.265v.264h2.38V.794A.27.27 0 0 0 2.38.529"/></svg>
|
||||
|
After Width: | Height: | Size: 232 B |
@@ -7469,6 +7469,9 @@
|
||||
"resources/TrustTokensView.ts | allStoredTrustTokensAvailableIn": {
|
||||
"message": "All stored Trust Tokens available in this browser instance."
|
||||
},
|
||||
"resources/TrustTokensView.ts | deleteTrustTokens": {
|
||||
"message": "Delete all stored Trust Tokens issued by {PH1}."
|
||||
},
|
||||
"resources/TrustTokensView.ts | issuer": {
|
||||
"message": "Issuer"
|
||||
},
|
||||
|
||||
@@ -7469,6 +7469,9 @@
|
||||
"resources/TrustTokensView.ts | allStoredTrustTokensAvailableIn": {
|
||||
"message": "Âĺl̂ śt̂ór̂éd̂ T́r̂úŝt́ T̂ók̂én̂ś âv́âíl̂áb̂ĺê ín̂ t́ĥíŝ b́r̂óŵśêŕ îńŝt́âńĉé."
|
||||
},
|
||||
"resources/TrustTokensView.ts | deleteTrustTokens": {
|
||||
"message": "D̂él̂ét̂é âĺl̂ śt̂ór̂éd̂ T́r̂úŝt́ T̂ók̂én̂ś îśŝúêd́ b̂ý {PH1}."
|
||||
},
|
||||
"resources/TrustTokensView.ts | issuer": {
|
||||
"message": "Îśŝúêŕ"
|
||||
},
|
||||
|
||||
@@ -33,6 +33,13 @@ const UIStrings = {
|
||||
* @description Text shown instead of a table when the table would be empty.
|
||||
*/
|
||||
noTrustTokensStored: 'No Trust Tokens are currently stored.',
|
||||
/**
|
||||
* @description Each row in the Trust Token table has a delete button. This is the text shown
|
||||
* when hovering over this button. The placeholder is a normal URL, indicating the site which
|
||||
* provided the Trust Tokens that will be deleted when the button is clicked.
|
||||
* @example {https://google.com} PH1
|
||||
*/
|
||||
deleteTrustTokens: 'Delete all stored Trust Tokens issued by {PH1}.',
|
||||
};
|
||||
const str_ = i18n.i18n.registerUIStrings('resources/TrustTokensView.ts', UIStrings);
|
||||
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
||||
@@ -78,7 +85,12 @@ class TrustTokensViewWidgetWrapper extends UI.ThrottledWidget.ThrottledWidget {
|
||||
return;
|
||||
}
|
||||
const {tokens} = await mainTarget.storageAgent().invoke_getTrustTokens();
|
||||
this.trustTokensView.data = {tokens};
|
||||
this.trustTokensView.data = {
|
||||
tokens,
|
||||
deleteClickHandler: (issuer: string): void => {
|
||||
mainTarget.storageAgent().invoke_clearTrustTokens({issuerOrigin: issuer});
|
||||
},
|
||||
};
|
||||
|
||||
this.update();
|
||||
}
|
||||
@@ -86,11 +98,13 @@ class TrustTokensViewWidgetWrapper extends UI.ThrottledWidget.ThrottledWidget {
|
||||
|
||||
export interface TrustTokensViewData {
|
||||
tokens: Protocol.Storage.TrustTokens[];
|
||||
deleteClickHandler: (issuerOrigin: string) => void;
|
||||
}
|
||||
|
||||
export class TrustTokensView extends HTMLElement {
|
||||
private readonly shadow = this.attachShadow({mode: 'open'});
|
||||
private tokens: Protocol.Storage.TrustTokens[] = [];
|
||||
private deleteClickHandler: (issuerOrigin: string) => void = () => {};
|
||||
|
||||
connectedCallback(): void {
|
||||
this.render();
|
||||
@@ -98,6 +112,7 @@ export class TrustTokensView extends HTMLElement {
|
||||
|
||||
set data(data: TrustTokensViewData) {
|
||||
this.tokens = data.tokens;
|
||||
this.deleteClickHandler = data.deleteClickHandler;
|
||||
this.render();
|
||||
}
|
||||
|
||||
@@ -150,7 +165,7 @@ export class TrustTokensView extends HTMLElement {
|
||||
{
|
||||
id: 'issuer',
|
||||
title: i18nString(UIStrings.issuer),
|
||||
widthWeighting: 2,
|
||||
widthWeighting: 10,
|
||||
hideable: false,
|
||||
visible: true,
|
||||
sortable: true,
|
||||
@@ -158,11 +173,19 @@ export class TrustTokensView extends HTMLElement {
|
||||
{
|
||||
id: 'count',
|
||||
title: i18nString(UIStrings.storedTokenCount),
|
||||
widthWeighting: 1,
|
||||
widthWeighting: 5,
|
||||
hideable: false,
|
||||
visible: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
id: 'delete-button',
|
||||
title: '',
|
||||
widthWeighting: 1,
|
||||
hideable: false,
|
||||
visible: true,
|
||||
sortable: false,
|
||||
},
|
||||
],
|
||||
rows: this.buildRowsFromTokens(),
|
||||
initialSort: {
|
||||
@@ -181,11 +204,58 @@ export class TrustTokensView extends HTMLElement {
|
||||
const tokens = this.tokens.filter(token => token.count > 0);
|
||||
return tokens.map(token => ({
|
||||
cells: [
|
||||
{
|
||||
columnId: 'delete-button',
|
||||
value: removeTrailingSlash(token.issuerOrigin),
|
||||
renderer: this.deleteButtonRenderer.bind(this),
|
||||
},
|
||||
{columnId: 'issuer', value: removeTrailingSlash(token.issuerOrigin)},
|
||||
{columnId: 'count', value: token.count},
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
private deleteButtonRenderer(issuer: Components.DataGridUtils.CellValue): LitHtml.TemplateResult {
|
||||
// clang-format off
|
||||
return LitHtml.html`
|
||||
<style>
|
||||
.delete-button {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete-button:hover devtools-icon {
|
||||
--icon-color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.delete-button:focus devtools-icon {
|
||||
--icon-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<!-- Wrap the button in a container, otherwise we can't center it inside the column. -->
|
||||
<span class="button-container">
|
||||
<button class="delete-button"
|
||||
title=${i18nString(UIStrings.deleteTrustTokens, {PH1: issuer as string})}
|
||||
@click=${(): void => this.deleteClickHandler(issuer as string)}>
|
||||
<devtools-icon .data=${
|
||||
{iconName: 'trash_bin_icon', color: 'var(--color-text-secondary)', width: '9px', height: '14px'} as
|
||||
Components.Icon.IconWithName}>
|
||||
</devtools-icon>
|
||||
</button>
|
||||
</span>`;
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
|
||||
function removeTrailingSlash(s: string): string {
|
||||
|
||||
@@ -6,17 +6,18 @@ import * as Coordinator from '../../../../front_end/render_coordinator/render_co
|
||||
import * as Resources from '../../../../front_end/resources/resources.js';
|
||||
import * as Components from '../../../../front_end/ui/components/components.js';
|
||||
import {assertElement, assertShadowRoot, getElementWithinComponent, renderElementIntoDOM} from '../helpers/DOMHelpers.js';
|
||||
import {getValuesOfAllBodyRows} from '../ui/components/DataGridHelpers.js';
|
||||
import {getCellByIndexes, getValuesOfAllBodyRows} from '../ui/components/DataGridHelpers.js';
|
||||
|
||||
const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();
|
||||
|
||||
const {assert} = chai;
|
||||
|
||||
async function renderTrustTokensView(tokens: Protocol.Storage.TrustTokens[]):
|
||||
Promise<Resources.TrustTokensView.TrustTokensView> {
|
||||
async function renderTrustTokensView(
|
||||
tokens: Protocol.Storage.TrustTokens[],
|
||||
deleteClickHandler: (issuer: string) => void = () => {}): Promise<Resources.TrustTokensView.TrustTokensView> {
|
||||
const component = new Resources.TrustTokensView.TrustTokensView();
|
||||
renderElementIntoDOM(component);
|
||||
component.data = {tokens};
|
||||
component.data = {tokens, deleteClickHandler};
|
||||
|
||||
// The data-grid's renderer is scheduled, so we need to wait until the coordinator
|
||||
// is done before we can test against it.
|
||||
@@ -43,8 +44,8 @@ describe('TrustTokensView', () => {
|
||||
const dataGridShadowRoot = getInternalDataGridShadowRoot(component);
|
||||
const rowValues = getValuesOfAllBodyRows(dataGridShadowRoot);
|
||||
assert.deepEqual(rowValues, [
|
||||
['bar.org', '7'],
|
||||
['foo.com', '42'],
|
||||
['bar.org', '7', ''],
|
||||
['foo.com', '42', ''],
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -56,7 +57,7 @@ describe('TrustTokensView', () => {
|
||||
|
||||
const dataGridShadowRoot = getInternalDataGridShadowRoot(component);
|
||||
const rowValues = getValuesOfAllBodyRows(dataGridShadowRoot);
|
||||
assert.deepEqual(rowValues, [['foo.com', '42']]);
|
||||
assert.deepEqual(rowValues, [['foo.com', '42', '']]);
|
||||
});
|
||||
|
||||
it('removes trailing slashes from issuer origins', async () => {
|
||||
@@ -68,8 +69,8 @@ describe('TrustTokensView', () => {
|
||||
const dataGridShadowRoot = getInternalDataGridShadowRoot(component);
|
||||
const rowValues = getValuesOfAllBodyRows(dataGridShadowRoot);
|
||||
assert.deepEqual(rowValues, [
|
||||
['example.com', '20'],
|
||||
['sub.domain.org', '14'],
|
||||
['example.com', '20', ''],
|
||||
['sub.domain.org', '14', ''],
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -83,4 +84,30 @@ describe('TrustTokensView', () => {
|
||||
const noTrustTokensElement = component.shadowRoot.querySelector('div.no-tt-message');
|
||||
assertElement(noTrustTokensElement, HTMLDivElement);
|
||||
});
|
||||
|
||||
it('calls the delete handler with the right issuer when the delete button is clicked in a row', async () => {
|
||||
// Create a Promise that resolves with the issuer for which the delete button was clicked.
|
||||
let resolveDeleteButtonPromise: (issuer: string) => void;
|
||||
const deleteButtonClicked: Promise<string> = new Promise(resolve => {
|
||||
resolveDeleteButtonPromise = resolve;
|
||||
});
|
||||
|
||||
const component = await renderTrustTokensView(
|
||||
[
|
||||
{issuerOrigin: 'bar.org', count: 42},
|
||||
{issuerOrigin: 'foo.com', count: 7},
|
||||
],
|
||||
(issuer: string) => {
|
||||
resolveDeleteButtonPromise(issuer);
|
||||
});
|
||||
|
||||
const dataGridShadowRoot = getInternalDataGridShadowRoot(component);
|
||||
const deleteCell = getCellByIndexes(dataGridShadowRoot, {column: 2, row: 1});
|
||||
const button = deleteCell.querySelector('button');
|
||||
assertElement(button, HTMLButtonElement);
|
||||
button.click();
|
||||
|
||||
const actualIssuer = await deleteButtonClicked;
|
||||
assert.strictEqual(actualIssuer, 'bar.org');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user