chrome-devtools-frontend 1.0.1537860 → 1.0.1538523
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.template +3 -2
- package/eslint.config.mjs +151 -149
- package/front_end/core/host/AidaClient.ts +1 -0
- package/front_end/core/host/UserMetrics.ts +3 -1
- package/front_end/core/root/Runtime.ts +8 -0
- package/front_end/models/ai_code_generation/AiCodeGeneration.ts +151 -0
- package/front_end/models/ai_code_generation/ai_code_generation.ts +6 -0
- package/front_end/models/ai_code_generation/debug.ts +30 -0
- package/front_end/panels/application/PreloadingTreeElement.ts +10 -2
- package/front_end/panels/application/components/OriginTrialTreeView.ts +97 -129
- package/front_end/panels/application/components/originTrialTreeView.css +37 -7
- package/front_end/panels/application/preloading/components/PreloadingString.ts +13 -11
- package/front_end/panels/emulation/components/DeviceSizeInputElement.ts +1 -0
- package/front_end/panels/network/NetworkItemView.ts +1 -1
- package/front_end/panels/network/NetworkWaterfallColumn.ts +5 -6
- package/front_end/panels/network/RequestTimingView.ts +404 -348
- package/front_end/panels/network/networkTimingTable.css +22 -2
- package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +42 -3
- package/front_end/panels/timeline/components/networkRequestTooltip.css +19 -0
- package/front_end/ui/components/adorners/Adorner.ts +1 -0
- package/front_end/ui/components/icon_button/IconButton.ts +1 -0
- package/front_end/ui/components/settings/SettingCheckbox.ts +1 -0
- package/front_end/ui/legacy/Treeoutline.ts +15 -0
- package/front_end/ui/legacy/UIUtils.ts +3 -0
- package/front_end/ui/legacy/Widget.ts +6 -4
- package/front_end/ui/legacy/XLink.ts +1 -0
- package/front_end/ui/legacy/components/inline_editor/Swatches.ts +1 -0
- package/front_end/ui/legacy/components/perf_ui/BrickBreaker.ts +1 -0
- package/front_end/ui/legacy/popover.css +12 -11
- package/package.json +1 -1
- package/front_end/panels/application/components/badge.css +0 -33
|
@@ -342,6 +342,7 @@ export interface AidaRegisterClientEvent {
|
|
|
342
342
|
disable_user_content_logging: boolean;
|
|
343
343
|
do_conversation_client_event?: DoConversationClientEvent;
|
|
344
344
|
complete_code_client_event?: {user_acceptance: UserAcceptance}|{user_impression: UserImpression};
|
|
345
|
+
generate_code_client_event?: {user_acceptance: UserAcceptance}|{user_impression: UserImpression};
|
|
345
346
|
}
|
|
346
347
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
347
348
|
|
|
@@ -527,7 +527,9 @@ export enum Action {
|
|
|
527
527
|
InsightTeaserGenerationCompleted = 192,
|
|
528
528
|
InsightTeaserGenerationAborted = 193,
|
|
529
529
|
InsightTeaserGenerationErrored = 194,
|
|
530
|
-
|
|
530
|
+
AiCodeGenerationSuggestionDisplayed = 195,
|
|
531
|
+
AiCodeGenerationSuggestionAccepted = 196,
|
|
532
|
+
MAX_VALUE = 197,
|
|
531
533
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
532
534
|
}
|
|
533
535
|
|
|
@@ -422,6 +422,13 @@ export interface HostConfigAiCodeCompletion {
|
|
|
422
422
|
userTier: string;
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
export interface HostConfigAiCodeGeneration {
|
|
426
|
+
modelId: string;
|
|
427
|
+
temperature: number;
|
|
428
|
+
enabled: boolean;
|
|
429
|
+
userTier: string;
|
|
430
|
+
}
|
|
431
|
+
|
|
425
432
|
export interface HostConfigDeepLinksViaExtensibilityApi {
|
|
426
433
|
enabled: boolean;
|
|
427
434
|
}
|
|
@@ -556,6 +563,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
|
|
|
556
563
|
devToolsAiAssistanceFileAgent: HostConfigAiAssistanceFileAgent,
|
|
557
564
|
devToolsAiAssistancePerformanceAgent: HostConfigAiAssistancePerformanceAgent,
|
|
558
565
|
devToolsAiCodeCompletion: HostConfigAiCodeCompletion,
|
|
566
|
+
devToolsAiCodeGeneration: HostConfigAiCodeGeneration,
|
|
559
567
|
devToolsVeLogging: HostConfigVeLogging,
|
|
560
568
|
devToolsWellKnown: HostConfigWellKnown,
|
|
561
569
|
devToolsPrivacyUI: HostConfigPrivacyUI,
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Copyright 2025 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Host from '../../core/host/host.js';
|
|
6
|
+
import * as Root from '../../core/root/root.js';
|
|
7
|
+
|
|
8
|
+
import {debugLog} from './debug.js';
|
|
9
|
+
|
|
10
|
+
export const basePreamble =
|
|
11
|
+
`You are a highly skilled senior software engineer with deep expertise across multiple web technologies and programming languages, including JavaScript, TypeScript, HTML, and CSS.
|
|
12
|
+
Your role is to act as an expert pair programmer within the Chrome DevTools environment.
|
|
13
|
+
|
|
14
|
+
**Core Directives (Adhere to these strictly):**
|
|
15
|
+
|
|
16
|
+
1. **Language and Quality:**
|
|
17
|
+
* Generate code that is modern, efficient, and idiomatic for the inferred language (e.g., modern JavaScript/ES6+, semantic HTML5, efficient CSS).
|
|
18
|
+
* Where appropriate, include basic error handling (e.g., for API calls).
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
export const additionalContextForConsole = `
|
|
22
|
+
You are operating within the execution environment of the Chrome DevTools Console.
|
|
23
|
+
The console has direct access to the inspected page's \`window\` and \`document\`.
|
|
24
|
+
|
|
25
|
+
* **Utilize Console Utilities:** You have access to the Console Utilities API. You **should** use these helper functions and variables when they are the most direct way to accomplish the user's goal.
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
interface Options {
|
|
29
|
+
aidaClient: Host.AidaClient.AidaClient;
|
|
30
|
+
serverSideLoggingEnabled?: boolean;
|
|
31
|
+
confirmSideEffectForTest?: typeof Promise.withResolvers;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface RequestOptions {
|
|
35
|
+
temperature?: number;
|
|
36
|
+
modelId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The AiCodeGeneration class is responsible for fetching generated code suggestions
|
|
41
|
+
* from the AIDA backend.
|
|
42
|
+
*/
|
|
43
|
+
export class AiCodeGeneration {
|
|
44
|
+
readonly #sessionId: string = crypto.randomUUID();
|
|
45
|
+
readonly #aidaClient: Host.AidaClient.AidaClient;
|
|
46
|
+
readonly #serverSideLoggingEnabled: boolean;
|
|
47
|
+
|
|
48
|
+
constructor(opts: Options) {
|
|
49
|
+
this.#aidaClient = opts.aidaClient;
|
|
50
|
+
this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#buildRequest(
|
|
54
|
+
prompt: string,
|
|
55
|
+
preamble: string,
|
|
56
|
+
inferenceLanguage: Host.AidaClient.AidaInferenceLanguage = Host.AidaClient.AidaInferenceLanguage.JAVASCRIPT,
|
|
57
|
+
): Host.AidaClient.GenerateCodeRequest {
|
|
58
|
+
const userTier = Host.AidaClient.convertToUserTierEnum(this.#userTier);
|
|
59
|
+
function validTemperature(temperature: number|undefined): number|undefined {
|
|
60
|
+
return typeof temperature === 'number' && temperature >= 0 ? temperature : undefined;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
client: Host.AidaClient.CLIENT_NAME,
|
|
64
|
+
preamble,
|
|
65
|
+
current_message: {
|
|
66
|
+
parts: [{
|
|
67
|
+
text: prompt,
|
|
68
|
+
}],
|
|
69
|
+
role: Host.AidaClient.Role.USER,
|
|
70
|
+
},
|
|
71
|
+
use_case: Host.AidaClient.UseCase.CODE_GENERATION,
|
|
72
|
+
options: {
|
|
73
|
+
inference_language: inferenceLanguage,
|
|
74
|
+
temperature: validTemperature(this.#options.temperature),
|
|
75
|
+
model_id: this.#options.modelId || undefined,
|
|
76
|
+
},
|
|
77
|
+
metadata: {
|
|
78
|
+
disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
|
|
79
|
+
string_session_id: this.#sessionId,
|
|
80
|
+
user_tier: userTier,
|
|
81
|
+
client_version: Root.Runtime.getChromeVersion(),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get #userTier(): string|undefined {
|
|
87
|
+
return Root.Runtime.hostConfig.devToolsAiCodeGeneration?.userTier;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get #options(): RequestOptions {
|
|
91
|
+
const temperature = Root.Runtime.hostConfig.devToolsAiCodeGeneration?.temperature;
|
|
92
|
+
const modelId = Root.Runtime.hostConfig.devToolsAiCodeGeneration?.modelId;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
temperature,
|
|
96
|
+
modelId,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
registerUserImpression(rpcGlobalId: Host.AidaClient.RpcGlobalId, latency: number, sampleId?: number): void {
|
|
101
|
+
const seconds = Math.floor(latency / 1_000);
|
|
102
|
+
const remainingMs = latency % 1_000;
|
|
103
|
+
const nanos = Math.floor(remainingMs * 1_000_000);
|
|
104
|
+
|
|
105
|
+
void this.#aidaClient.registerClientEvent({
|
|
106
|
+
corresponding_aida_rpc_global_id: rpcGlobalId,
|
|
107
|
+
disable_user_content_logging: true,
|
|
108
|
+
generate_code_client_event: {
|
|
109
|
+
user_impression: {
|
|
110
|
+
sample: {
|
|
111
|
+
sample_id: sampleId,
|
|
112
|
+
},
|
|
113
|
+
latency: {
|
|
114
|
+
duration: {
|
|
115
|
+
seconds,
|
|
116
|
+
nanos,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
debugLog('Registered user impression with latency {seconds:', seconds, ', nanos:', nanos, '}');
|
|
123
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionDisplayed);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
registerUserAcceptance(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId?: number): void {
|
|
127
|
+
void this.#aidaClient.registerClientEvent({
|
|
128
|
+
corresponding_aida_rpc_global_id: rpcGlobalId,
|
|
129
|
+
disable_user_content_logging: true,
|
|
130
|
+
generate_code_client_event: {
|
|
131
|
+
user_acceptance: {
|
|
132
|
+
sample: {
|
|
133
|
+
sample_id: sampleId,
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
debugLog('Registered user acceptance');
|
|
139
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionAccepted);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async generateCode(prompt: string, preamble: string, inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage):
|
|
143
|
+
Promise<Host.AidaClient.GenerateCodeResponse|null> {
|
|
144
|
+
const request = this.#buildRequest(prompt, preamble, inferenceLanguage);
|
|
145
|
+
const response = await this.#aidaClient.generateCode(request);
|
|
146
|
+
|
|
147
|
+
debugLog({request, response});
|
|
148
|
+
|
|
149
|
+
return response;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Copyright 2025 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file Local debugging utilities.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function isDebugMode(): boolean {
|
|
10
|
+
return Boolean(localStorage.getItem('debugAiCodeGenerationEnabled'));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function debugLog(...log: unknown[]): void {
|
|
14
|
+
if (!isDebugMode()) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.log(...log);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setDebugAiCodeGenerationEnabled(enabled: boolean): void {
|
|
23
|
+
if (enabled) {
|
|
24
|
+
localStorage.setItem('debugAiCodeGenerationEnabled', 'true');
|
|
25
|
+
} else {
|
|
26
|
+
localStorage.removeItem('debugAiCodeGenerationEnabled');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// @ts-expect-error
|
|
30
|
+
globalThis.setDebugAiCodeGenerationEnabled = setDebugAiCodeGenerationEnabled;
|
|
@@ -8,7 +8,7 @@ import type * as SDK from '../../core/sdk/sdk.js';
|
|
|
8
8
|
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
|
|
9
9
|
|
|
10
10
|
import {ApplicationPanelTreeElement, ExpandableApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
|
|
11
|
-
import
|
|
11
|
+
import * as PreloadingHelper from './preloading/helper/helper.js';
|
|
12
12
|
import {PreloadingAttemptView, PreloadingRuleSetView, PreloadingSummaryView} from './preloading/PreloadingView.js';
|
|
13
13
|
import type {ResourcesPanel} from './ResourcesPanel.js';
|
|
14
14
|
|
|
@@ -125,7 +125,15 @@ export class PreloadingSummaryTreeElement extends ExpandableApplicationPanelTree
|
|
|
125
125
|
this.#attempt.initialize(model);
|
|
126
126
|
|
|
127
127
|
// Show the view if the model was initialized after selection.
|
|
128
|
-
if
|
|
128
|
+
// However, if the user last viewed this page and clicked into Rules or
|
|
129
|
+
// Speculations, we ensure that we instead show those pages.
|
|
130
|
+
if (this.#attempt.selected) {
|
|
131
|
+
const filter = new PreloadingHelper.PreloadingForward.AttemptViewWithFilter(null);
|
|
132
|
+
this.expandAndRevealAttempts(filter);
|
|
133
|
+
} else if (this.#ruleSet.selected) {
|
|
134
|
+
const filter = new PreloadingHelper.PreloadingForward.RuleSetView(null);
|
|
135
|
+
this.expandAndRevealRuleSet(filter);
|
|
136
|
+
} else if (this.#selected && !this.#view) {
|
|
129
137
|
this.onselect(false);
|
|
130
138
|
}
|
|
131
139
|
}
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
// Copyright 2021 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
|
-
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
6
4
|
|
|
7
5
|
import '../../../ui/components/icon_button/icon_button.js';
|
|
8
6
|
import '../../../ui/legacy/legacy.js';
|
|
7
|
+
import '../../../ui/components/adorners/adorners.js';
|
|
9
8
|
|
|
10
9
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
11
10
|
import * as Protocol from '../../../generated/protocol.js';
|
|
12
|
-
import * as Adorners from '../../../ui/components/adorners/adorners.js';
|
|
13
11
|
import type * as TreeOutline from '../../../ui/components/tree_outline/tree_outline.js';
|
|
14
12
|
import * as UI from '../../../ui/legacy/legacy.js';
|
|
15
|
-
import
|
|
13
|
+
import {Directives, html, type LitTemplate, nothing, render, type TemplateResult} from '../../../ui/lit/lit.js';
|
|
16
14
|
|
|
17
|
-
import badgeStyles from './badge.css.js';
|
|
18
15
|
import originTrialTokenRowsStyles from './originTrialTokenRows.css.js';
|
|
19
16
|
import originTrialTreeViewStyles from './originTrialTreeView.css.js';
|
|
20
17
|
|
|
21
|
-
const {
|
|
18
|
+
const {classMap} = Directives;
|
|
19
|
+
const {widgetConfig} = UI.Widget;
|
|
22
20
|
|
|
23
21
|
const UIStrings = {
|
|
24
22
|
/**
|
|
@@ -75,27 +73,6 @@ const UIStrings = {
|
|
|
75
73
|
const str_ = i18n.i18n.registerUIStrings('panels/application/components/OriginTrialTreeView.ts', UIStrings);
|
|
76
74
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
77
75
|
|
|
78
|
-
export interface BadgeData {
|
|
79
|
-
badgeContent: string;
|
|
80
|
-
style: 'error'|'success'|'secondary';
|
|
81
|
-
additionalClass?: string;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function createBadge(data: BadgeData): Adorners.Adorner.Adorner {
|
|
85
|
-
const adorner = new Adorners.Adorner.Adorner();
|
|
86
|
-
const adornerContent = document.createElement('span');
|
|
87
|
-
adornerContent.textContent = data.badgeContent;
|
|
88
|
-
adorner.data = {
|
|
89
|
-
name: 'badge',
|
|
90
|
-
content: adornerContent,
|
|
91
|
-
};
|
|
92
|
-
adorner.classList.add(`badge-${data.style}`);
|
|
93
|
-
if (data.additionalClass) {
|
|
94
|
-
adorner.classList.add(data.additionalClass);
|
|
95
|
-
}
|
|
96
|
-
return adorner;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
76
|
type TreeNode<DataType> = TreeOutline.TreeOutlineUtils.TreeNode<DataType>;
|
|
100
77
|
|
|
101
78
|
/**
|
|
@@ -107,72 +84,69 @@ type TreeNode<DataType> = TreeOutline.TreeOutlineUtils.TreeNode<DataType>;
|
|
|
107
84
|
**/
|
|
108
85
|
export type OriginTrialTreeNodeData = Protocol.Page.OriginTrial|Protocol.Page.OriginTrialTokenWithStatus|string;
|
|
109
86
|
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
badgeContent: i18nString(UIStrings.tokens, {PH1: originTrial.tokensWithStatus.length}),
|
|
113
|
-
style: 'secondary',
|
|
114
|
-
});
|
|
115
|
-
|
|
87
|
+
function renderOriginTrialTree(originTrial: Protocol.Page.OriginTrial): LitTemplate {
|
|
88
|
+
const success = originTrial.status === Protocol.Page.OriginTrialStatus.Enabled;
|
|
116
89
|
// clang-format off
|
|
117
90
|
return html`
|
|
118
91
|
<li role="treeitem">
|
|
119
92
|
${originTrial.trialName}
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
93
|
+
<devtools-adorner class="badge-${success ? 'success' : 'error'}">
|
|
94
|
+
${originTrial.status}
|
|
95
|
+
</devtools-adorner>
|
|
96
|
+
${originTrial.tokensWithStatus.length > 1 ? html`
|
|
97
|
+
<devtools-adorner class="badge-secondary">
|
|
98
|
+
${i18nString(UIStrings.tokens, {PH1: originTrial.tokensWithStatus.length})}
|
|
99
|
+
</devtools-adorner>`
|
|
100
|
+
: nothing}
|
|
126
101
|
<ul role="group" hidden>
|
|
127
102
|
${originTrial.tokensWithStatus.length > 1 ?
|
|
128
|
-
originTrial.tokensWithStatus.map(
|
|
129
|
-
|
|
103
|
+
originTrial.tokensWithStatus.map(renderTokenNode) :
|
|
104
|
+
renderTokenDetailsNodes(originTrial.tokensWithStatus[0])}
|
|
130
105
|
</ul>
|
|
131
106
|
</li>`;
|
|
132
107
|
// clang-format on
|
|
133
108
|
}
|
|
134
109
|
|
|
135
|
-
function
|
|
136
|
-
const
|
|
137
|
-
badgeContent: token.status,
|
|
138
|
-
style: token.status === Protocol.Page.OriginTrialTokenStatus.Success ? 'success' : 'error',
|
|
139
|
-
additionalClass: 'token-status-badge',
|
|
140
|
-
});
|
|
110
|
+
function renderTokenNode(token: Protocol.Page.OriginTrialTokenWithStatus): LitTemplate {
|
|
111
|
+
const success = token.status === Protocol.Page.OriginTrialTokenStatus.Success;
|
|
141
112
|
// Only display token status for convenience when the node is not expanded.
|
|
142
113
|
// clang-format off
|
|
143
114
|
return html`
|
|
144
115
|
<li role="treeitem">
|
|
145
|
-
${i18nString(UIStrings.token)}
|
|
116
|
+
${i18nString(UIStrings.token)}
|
|
117
|
+
<devtools-adorner class="token-status-badge badge-${success ? 'success' : 'error'}">
|
|
118
|
+
${token.status}
|
|
119
|
+
</devtools-adorner>
|
|
146
120
|
<ul role="group" hidden>
|
|
147
|
-
${
|
|
121
|
+
${renderTokenDetailsNodes(token)}
|
|
148
122
|
</ul>
|
|
149
123
|
</li>`;
|
|
150
124
|
}
|
|
151
125
|
|
|
152
126
|
interface TokenField {
|
|
153
127
|
name: string;
|
|
154
|
-
value:
|
|
128
|
+
value: {text: string, hasError?: boolean};
|
|
155
129
|
}
|
|
156
130
|
|
|
157
|
-
function renderTokenDetails(token: Protocol.Page.OriginTrialTokenWithStatus):
|
|
131
|
+
function renderTokenDetails(token: Protocol.Page.OriginTrialTokenWithStatus): TemplateResult {
|
|
158
132
|
return html`
|
|
159
133
|
<li role="treeitem">
|
|
160
|
-
<devtools-
|
|
161
|
-
</devtools-
|
|
134
|
+
<devtools-widget .widgetConfig=${widgetConfig(OriginTrialTokenRows, {data: token})}>
|
|
135
|
+
</devtools-widget>
|
|
162
136
|
</li>`;
|
|
163
137
|
}
|
|
164
138
|
|
|
165
|
-
function
|
|
166
|
-
|
|
139
|
+
function renderTokenDetailsNodes(token: Protocol.Page.OriginTrialTokenWithStatus):
|
|
140
|
+
TemplateResult {
|
|
167
141
|
// clang-format off
|
|
168
142
|
return html`
|
|
169
143
|
${renderTokenDetails(token)}
|
|
170
|
-
${
|
|
144
|
+
${renderRawTokenTextNode(token.rawTokenText)}
|
|
171
145
|
`;
|
|
172
146
|
// clang-format on
|
|
173
147
|
}
|
|
174
148
|
|
|
175
|
-
function
|
|
149
|
+
function renderRawTokenTextNode(tokenText: string): LitTemplate {
|
|
176
150
|
// clang-format off
|
|
177
151
|
return html`
|
|
178
152
|
<li role="treeitem">
|
|
@@ -192,8 +166,42 @@ export interface OriginTrialTokenRowsData {
|
|
|
192
166
|
node: TreeNode<OriginTrialTreeNodeData>;
|
|
193
167
|
}
|
|
194
168
|
|
|
195
|
-
|
|
196
|
-
|
|
169
|
+
interface RowsViewInput {
|
|
170
|
+
tokenWithStatus: Protocol.Page.OriginTrialTokenWithStatus;
|
|
171
|
+
parsedTokenDetails: TokenField[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
type RowsView = (input: RowsViewInput, output: undefined, target: HTMLElement) => void;
|
|
175
|
+
|
|
176
|
+
const ROWS_DEFAULT_VIEW: RowsView = (input, _output, target) => {
|
|
177
|
+
const success = input.tokenWithStatus.status === Protocol.Page.OriginTrialTokenStatus.Success;
|
|
178
|
+
// clang-format off
|
|
179
|
+
render(html`
|
|
180
|
+
<style>
|
|
181
|
+
${originTrialTokenRowsStyles}
|
|
182
|
+
${originTrialTreeViewStyles}
|
|
183
|
+
</style>
|
|
184
|
+
<div class="content">
|
|
185
|
+
<div class="key">${i18nString(UIStrings.status)}</div>
|
|
186
|
+
<div class="value">
|
|
187
|
+
<devtools-adorner class="badge-${success ? 'success' : 'error'}">
|
|
188
|
+
${input.tokenWithStatus.status}
|
|
189
|
+
</devtools-adorner>
|
|
190
|
+
</div>
|
|
191
|
+
${input.parsedTokenDetails.map((field: TokenField) => html`
|
|
192
|
+
<div class="key">${field.name}</div>
|
|
193
|
+
<div class="value">
|
|
194
|
+
<div class=${classMap({'error-text': Boolean(field.value.hasError)})}>
|
|
195
|
+
${field.value.text}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
`)}
|
|
199
|
+
</div>`, target);
|
|
200
|
+
// clang-format on
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export class OriginTrialTokenRows extends UI.Widget.Widget {
|
|
204
|
+
#view: RowsView;
|
|
197
205
|
#tokenWithStatus: Protocol.Page.OriginTrialTokenWithStatus|null = null;
|
|
198
206
|
#parsedTokenDetails: TokenField[] = [];
|
|
199
207
|
#dateFormatter: Intl.DateTimeFormat = new Intl.DateTimeFormat(
|
|
@@ -201,28 +209,20 @@ export class OriginTrialTokenRows extends HTMLElement {
|
|
|
201
209
|
{dateStyle: 'long', timeStyle: 'long'},
|
|
202
210
|
);
|
|
203
211
|
|
|
212
|
+
constructor(element?: HTMLElement, view: RowsView = ROWS_DEFAULT_VIEW) {
|
|
213
|
+
super(element, {useShadowDom: true});
|
|
214
|
+
this.#view = view;
|
|
215
|
+
}
|
|
216
|
+
|
|
204
217
|
set data(data: Protocol.Page.OriginTrialTokenWithStatus) {
|
|
205
218
|
this.#tokenWithStatus = data;
|
|
206
219
|
this.#setTokenFields();
|
|
207
220
|
}
|
|
208
221
|
|
|
209
222
|
connectedCallback(): void {
|
|
210
|
-
this
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
override cloneNode(): HTMLElement {
|
|
214
|
-
const clone = UI.UIUtils.cloneCustomElement(this);
|
|
215
|
-
if (this.#tokenWithStatus) {
|
|
216
|
-
clone.data = this.#tokenWithStatus;
|
|
217
|
-
}
|
|
218
|
-
return clone;
|
|
223
|
+
this.requestUpdate();
|
|
219
224
|
}
|
|
220
225
|
|
|
221
|
-
#renderTokenField = (fieldValue: string, hasError?: boolean): Lit.TemplateResult => html`
|
|
222
|
-
<div class=${ifDefined(hasError ? 'error-text' : undefined)}>
|
|
223
|
-
${fieldValue}
|
|
224
|
-
</div>`;
|
|
225
|
-
|
|
226
226
|
#setTokenFields(): void {
|
|
227
227
|
if (!this.#tokenWithStatus?.parsedToken) {
|
|
228
228
|
return;
|
|
@@ -230,27 +230,29 @@ export class OriginTrialTokenRows extends HTMLElement {
|
|
|
230
230
|
this.#parsedTokenDetails = [
|
|
231
231
|
{
|
|
232
232
|
name: i18nString(UIStrings.origin),
|
|
233
|
-
value:
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
value: {
|
|
234
|
+
text: this.#tokenWithStatus.parsedToken.origin,
|
|
235
|
+
hasError: this.#tokenWithStatus.status === Protocol.Page.OriginTrialTokenStatus.WrongOrigin,
|
|
236
|
+
},
|
|
236
237
|
},
|
|
237
238
|
{
|
|
238
239
|
name: i18nString(UIStrings.expiryTime),
|
|
239
|
-
value:
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
value: {
|
|
241
|
+
text: this.#dateFormatter.format(this.#tokenWithStatus.parsedToken.expiryTime * 1000),
|
|
242
|
+
hasError: this.#tokenWithStatus.status === Protocol.Page.OriginTrialTokenStatus.Expired
|
|
243
|
+
},
|
|
242
244
|
},
|
|
243
245
|
{
|
|
244
246
|
name: i18nString(UIStrings.usageRestriction),
|
|
245
|
-
value: this.#
|
|
247
|
+
value: {text: this.#tokenWithStatus.parsedToken.usageRestriction},
|
|
246
248
|
},
|
|
247
249
|
{
|
|
248
250
|
name: i18nString(UIStrings.isThirdParty),
|
|
249
|
-
value: this.#
|
|
251
|
+
value: {text: this.#tokenWithStatus.parsedToken.isThirdParty.toString()},
|
|
250
252
|
},
|
|
251
253
|
{
|
|
252
254
|
name: i18nString(UIStrings.matchSubDomains),
|
|
253
|
-
value: this.#
|
|
255
|
+
value: {text: this.#tokenWithStatus.parsedToken.matchSubDomains.toString()},
|
|
254
256
|
},
|
|
255
257
|
];
|
|
256
258
|
|
|
@@ -258,53 +260,27 @@ export class OriginTrialTokenRows extends HTMLElement {
|
|
|
258
260
|
this.#parsedTokenDetails = [
|
|
259
261
|
{
|
|
260
262
|
name: i18nString(UIStrings.trialName),
|
|
261
|
-
value: this.#
|
|
263
|
+
value: {text: this.#tokenWithStatus.parsedToken.trialName},
|
|
262
264
|
},
|
|
263
265
|
...this.#parsedTokenDetails,
|
|
264
266
|
];
|
|
265
267
|
}
|
|
268
|
+
this.requestUpdate();
|
|
266
269
|
}
|
|
267
270
|
|
|
268
|
-
|
|
271
|
+
override performUpdate(): void {
|
|
269
272
|
if (!this.#tokenWithStatus) {
|
|
270
273
|
return;
|
|
271
274
|
}
|
|
272
275
|
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
${createBadge({
|
|
279
|
-
badgeContent: this.#tokenWithStatus.status,
|
|
280
|
-
style: this.#tokenWithStatus.status === Protocol.Page.OriginTrialTokenStatus.Success ? 'success' : 'error',
|
|
281
|
-
})}`,
|
|
282
|
-
},
|
|
283
|
-
...this.#parsedTokenDetails,
|
|
284
|
-
];
|
|
285
|
-
|
|
286
|
-
const tokenDetailRows = tokenDetails.map((field: TokenField) => {
|
|
287
|
-
return html`
|
|
288
|
-
<div class="key">${field.name}</div>
|
|
289
|
-
<div class="value">${field.value}</div>
|
|
290
|
-
`;
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
Lit.render(
|
|
294
|
-
html`
|
|
295
|
-
<style>
|
|
296
|
-
${originTrialTokenRowsStyles}
|
|
297
|
-
</style>
|
|
298
|
-
<div class="content">
|
|
299
|
-
${tokenDetailRows}
|
|
300
|
-
</div>
|
|
301
|
-
`,
|
|
302
|
-
this.#shadow, {host: this});
|
|
276
|
+
const viewInput: RowsViewInput = {
|
|
277
|
+
tokenWithStatus: this.#tokenWithStatus,
|
|
278
|
+
parsedTokenDetails: this.#parsedTokenDetails,
|
|
279
|
+
};
|
|
280
|
+
this.#view(viewInput, undefined, this.contentElement);
|
|
303
281
|
}
|
|
304
282
|
}
|
|
305
283
|
|
|
306
|
-
customElements.define('devtools-resources-origin-trial-token-rows', OriginTrialTokenRows);
|
|
307
|
-
|
|
308
284
|
export interface OriginTrialTreeViewData {
|
|
309
285
|
trials: Protocol.Page.OriginTrial[];
|
|
310
286
|
}
|
|
@@ -314,8 +290,7 @@ type View = (input: OriginTrialTreeViewData, output: undefined, target: HTMLElem
|
|
|
314
290
|
const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
315
291
|
if (!input.trials.length) {
|
|
316
292
|
// clang-format off
|
|
317
|
-
|
|
318
|
-
<style>${originTrialTreeViewStyles}</style>
|
|
293
|
+
render(html`
|
|
319
294
|
<span class="status-badge">
|
|
320
295
|
<devtools-icon class="medium" name="clear"></devtools-icon>
|
|
321
296
|
<span>${i18nString(UIStrings.noTrialTokens)}</span>
|
|
@@ -325,13 +300,12 @@ const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
|
325
300
|
}
|
|
326
301
|
|
|
327
302
|
// clang-format off
|
|
328
|
-
|
|
329
|
-
<style>
|
|
330
|
-
${originTrialTreeViewStyles}
|
|
331
|
-
</style>
|
|
303
|
+
render(html`
|
|
304
|
+
<style>${originTrialTreeViewStyles}</style>
|
|
332
305
|
<devtools-tree .template=${html`
|
|
306
|
+
<style>${originTrialTreeViewStyles}</style>
|
|
333
307
|
<ul role="tree">
|
|
334
|
-
${input.trials.map(
|
|
308
|
+
${input.trials.map(renderOriginTrialTree)}
|
|
335
309
|
</ul>
|
|
336
310
|
`}>
|
|
337
311
|
</devtools-tree>
|
|
@@ -357,9 +331,3 @@ export class OriginTrialTreeView extends UI.Widget.Widget {
|
|
|
357
331
|
this.#view(this.#data, undefined, this.contentElement);
|
|
358
332
|
}
|
|
359
333
|
}
|
|
360
|
-
|
|
361
|
-
declare global {
|
|
362
|
-
interface HTMLElementTagNameMap {
|
|
363
|
-
'devtools-resources-origin-trial-token-rows': OriginTrialTokenRows;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
@@ -4,12 +4,42 @@
|
|
|
4
4
|
* found in the LICENSE file.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
:host {
|
|
8
|
+
.status-badge {
|
|
9
|
+
border-radius: 4px;
|
|
10
|
+
padding: 4px;
|
|
11
|
+
background: var(--sys-color-neutral-container);
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
& > devtools-icon {
|
|
14
|
+
vertical-align: sub;
|
|
15
|
+
}
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
|
|
18
|
+
.badge-error {
|
|
19
|
+
--override-adorner-text-color: var(--sys-color-error-bright);
|
|
20
|
+
--override-adorner-border-color: var(--sys-color-error-bright);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.badge-success {
|
|
24
|
+
--override-adorner-text-color: var(--sys-color-tertiary);
|
|
25
|
+
--override-adorner-border-color: var(--sys-color-tertiary);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.badge-secondary {
|
|
29
|
+
--override-adorner-text-color: var(--sys-color-token-subtle);
|
|
30
|
+
--override-adorner-border-color: var(--sys-color-token-subtle);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Use mono-space source code font to assist reading of adorner content */
|
|
34
|
+
devtools-adorner {
|
|
35
|
+
font-family: var(--source-code-font-family);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.token-status-badge {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[aria-expanded='false'] .token-status-badge {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
}
|
|
45
|
+
}
|