chrome-devtools-frontend 1.0.1571573 → 1.0.1573331
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/front_end/Images/geminiInDevTools.png +0 -0
- package/front_end/Images/geminiInDevTools_2x.png +0 -0
- package/front_end/Images/src/database-off.svg +1 -0
- package/front_end/Images/src/lock-person.svg +1 -1
- package/front_end/core/common/Debouncer.ts +10 -1
- package/front_end/core/common/SettingRegistration.ts +1 -1
- package/front_end/core/host/AidaClient.ts +8 -0
- package/front_end/core/host/InspectorFrontendHostAPI.ts +0 -6
- package/front_end/core/host/UserMetrics.ts +0 -30
- package/front_end/core/root/ExperimentNames.ts +30 -0
- package/front_end/core/root/Runtime.ts +12 -27
- package/front_end/core/root/root.ts +2 -0
- package/front_end/core/sdk/CSSModel.ts +36 -13
- package/front_end/core/sdk/CSSProperty.ts +1 -1
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +9 -0
- package/front_end/core/sdk/CookieModel.ts +2 -1
- package/front_end/core/sdk/DOMModel.ts +1 -1
- package/front_end/core/sdk/DebuggerModel.ts +1 -1
- package/front_end/core/sdk/IsolateManager.ts +0 -7
- package/front_end/core/sdk/OverlayModel.ts +1 -1
- package/front_end/core/sdk/SourceMap.ts +1 -1
- package/front_end/devtools_compatibility.js +0 -6
- package/front_end/entrypoints/main/MainImpl.ts +34 -25
- package/front_end/entrypoints/main/main-meta.ts +1 -1
- package/front_end/generated/SupportedCSSProperties.js +4 -8
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +0 -180
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +7 -7
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +4 -4
- package/front_end/models/ai_assistance/ai_assistance.ts +0 -2
- package/front_end/models/breakpoints/BreakpointManager.ts +3 -3
- package/front_end/models/issues_manager/ContrastCheckTrigger.ts +2 -2
- package/front_end/models/source_map_scopes/NamesResolver.ts +1 -1
- package/front_end/models/trace/EventsSerializer.ts +5 -1
- package/front_end/models/trace/types/TraceEvents.ts +4 -0
- package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +1 -1
- package/front_end/panels/accessibility/AccessibilitySidebarView.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -63
- package/front_end/panels/ai_assistance/PatchWidget.ts +3 -4
- package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +1 -1
- package/front_end/panels/ai_assistance/aiAssistancePanel.css +0 -16
- package/front_end/panels/ai_assistance/ai_assistance.ts +0 -1
- package/front_end/panels/ai_assistance/components/ChatView.ts +123 -108
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +1 -145
- package/front_end/panels/application/AppManifestView.ts +5 -5
- package/front_end/panels/application/DeviceBoundSessionsModel.ts +55 -4
- package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +46 -1
- package/front_end/panels/application/FrameDetailsView.ts +10 -10
- package/front_end/panels/application/components/BackForwardCacheView.ts +6 -8
- package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +4 -3
- package/front_end/panels/application/components/ProtocolHandlersView.ts +5 -6
- package/front_end/panels/application/components/ReportsGrid.ts +8 -9
- package/front_end/panels/application/components/SharedStorageAccessGrid.ts +5 -4
- package/front_end/panels/application/components/TrustTokensView.ts +4 -6
- package/front_end/panels/application/preloading/PreloadingView.ts +9 -8
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +237 -230
- package/front_end/panels/application/preloading/components/PreloadingGrid.ts +3 -2
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +6 -10
- package/front_end/panels/application/resourcesSidebar.css +8 -0
- package/front_end/panels/autofill/AutofillView.ts +4 -3
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +1 -1
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +1 -1
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +35 -4
- package/front_end/panels/common/AiCodeGenerationUpgradeDialog.ts +2 -2
- package/front_end/panels/common/BadgeNotification.ts +18 -14
- package/front_end/panels/common/GdpSignUpDialog.ts +5 -6
- package/front_end/panels/common/GeminiRebrandPromoDialog.ts +192 -0
- package/front_end/panels/common/aiCodeGenerationTeaser.css +16 -1
- package/front_end/panels/common/common.ts +1 -0
- package/front_end/panels/common/geminiRebrandPromoDialog.css +47 -0
- package/front_end/panels/console/ConsoleInsightTeaser.ts +3 -3
- package/front_end/panels/console/ErrorStackParser.ts +11 -16
- package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +2 -2
- package/front_end/panels/css_overview/CSSOverviewModel.ts +1 -1
- package/front_end/panels/elements/ComputedStyleModel.ts +7 -54
- package/front_end/panels/elements/ElementsPanel.ts +33 -6
- package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +16 -8
- package/front_end/panels/elements/StylePropertyTreeElement.ts +67 -32
- package/front_end/panels/elements/StylesSidebarPane.ts +28 -11
- package/front_end/panels/elements/components/CSSHintDetailsView.ts +3 -2
- package/front_end/panels/elements/components/CSSPropertyDocsView.ts +3 -2
- package/front_end/panels/elements/elements-meta.ts +1 -1
- package/front_end/panels/explain/components/ConsoleInsight.ts +7 -7
- package/front_end/panels/issues/IssueView.ts +6 -6
- package/front_end/panels/network/RequestConditionsDrawer.ts +8 -7
- package/front_end/panels/network/RequestCookiesView.ts +3 -4
- package/front_end/panels/network/RequestTimingView.ts +3 -8
- package/front_end/panels/network/components/HeaderSectionRow.ts +3 -2
- package/front_end/panels/network/components/RequestHeaderSection.ts +3 -1
- package/front_end/panels/network/components/RequestHeadersView.ts +2 -2
- package/front_end/panels/profiler/HeapProfileView.ts +3 -3
- package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
- package/front_end/panels/profiler/IsolateSelector.ts +1 -2
- package/front_end/panels/profiler/profiler-meta.ts +3 -3
- package/front_end/panels/protocol_monitor/protocol_monitor-meta.ts +1 -1
- package/front_end/panels/recorder/RecorderController.ts +4 -9
- package/front_end/panels/recorder/components/CreateRecordingView.ts +6 -6
- package/front_end/panels/recorder/components/RecordingView.ts +0 -38
- package/front_end/panels/recorder/components/ReplaySection.ts +0 -14
- package/front_end/panels/recorder/components/StepEditor.ts +2 -40
- package/front_end/panels/recorder/recorderController.css +3 -3
- package/front_end/panels/security/CookieReportView.ts +17 -17
- package/front_end/panels/security/cookieControlsView.css +1 -1
- package/front_end/panels/security/cookieReportView.css +1 -1
- package/front_end/panels/settings/AISettingsTab.ts +8 -10
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +4 -12
- package/front_end/panels/settings/settings-meta.ts +1 -1
- package/front_end/panels/sources/BreakpointEditDialog.ts +4 -3
- package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +1 -1
- package/front_end/panels/sources/NavigatorView.ts +4 -4
- package/front_end/panels/sources/SourcesPanel.ts +5 -4
- package/front_end/panels/sources/components/HeadersView.ts +2 -2
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +3 -2
- package/front_end/panels/timeline/ThreadAppender.ts +2 -1
- package/front_end/panels/timeline/TimelineController.ts +3 -3
- package/front_end/panels/timeline/TimelinePanel.ts +6 -4
- package/front_end/panels/timeline/TimelineUIUtils.ts +2 -2
- package/front_end/panels/timeline/components/ExportTraceOptions.ts +4 -4
- package/front_end/panels/timeline/components/FieldSettingsDialog.ts +13 -6
- package/front_end/panels/timeline/components/LiveMetricsView.ts +13 -9
- package/front_end/panels/timeline/components/exportTraceOptions.css +1 -1
- package/front_end/panels/timeline/components/fieldSettingsDialog.css +1 -1
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -19
- package/front_end/panels/timeline/components/insights/InsightRenderer.ts +3 -4
- package/front_end/panels/timeline/components/insights/baseInsightComponent.css +0 -5
- package/front_end/panels/timeline/components/liveMetricsView.css +1 -1
- package/front_end/panels/whats_new/ReleaseNoteView.ts +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/buttons/button.css +4 -0
- package/front_end/ui/components/markdown_view/CodeBlock.ts +1 -1
- package/front_end/ui/components/panel_feedback/PanelFeedback.ts +1 -1
- package/front_end/ui/components/panel_feedback/PreviewToggle.ts +5 -5
- package/front_end/ui/components/panel_feedback/previewToggle.css +2 -2
- package/front_end/ui/components/text_editor/AiCodeGenerationParser.ts +27 -8
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +36 -6
- package/front_end/ui/components/text_editor/AutocompleteHistory.ts +33 -1
- package/front_end/ui/legacy/ActionRegistration.ts +1 -1
- package/front_end/ui/legacy/ContextMenu.ts +2 -2
- package/front_end/ui/legacy/EmptyWidget.ts +3 -3
- package/front_end/ui/legacy/LinkContextMenuProvider.ts +42 -0
- package/front_end/ui/legacy/ViewRegistration.ts +1 -1
- package/front_end/ui/legacy/XLink.ts +0 -27
- package/front_end/ui/legacy/components/color_picker/ContrastDetails.ts +1 -1
- package/front_end/ui/legacy/components/color_picker/ContrastOverlay.ts +4 -4
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +2 -2
- package/front_end/ui/legacy/components/perf_ui/perf_ui-meta.ts +1 -1
- package/front_end/ui/legacy/inspectorCommon.css +2 -1
- package/front_end/ui/legacy/legacy.ts +3 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- package/front_end/ui/visual_logging/LoggingDriver.ts +3 -0
- package/package.json +1 -1
- package/front_end/models/ai_assistance/ArtifactsManager.ts +0 -67
- package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +0 -183
- package/front_end/panels/ai_assistance/components/CollapsibleAssistanceContentWidget.ts +0 -89
- package/front_end/panels/ai_assistance/components/PerformanceAgentFlameChart.ts +0 -133
- package/front_end/panels/ai_assistance/components/artifactsViewer.css +0 -15
- package/front_end/panels/ai_assistance/components/collapsibleAssistanceContentWidget.css +0 -32
|
@@ -2,8 +2,6 @@
|
|
|
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
4
|
|
|
5
|
-
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
6
|
-
|
|
7
5
|
import '../../../ui/components/spinners/spinners.js';
|
|
8
6
|
|
|
9
7
|
import * as Host from '../../../core/host/host.js';
|
|
@@ -13,7 +11,7 @@ import type * as AiAssistanceModel from '../../../models/ai_assistance/ai_assist
|
|
|
13
11
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
14
12
|
import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
|
|
15
13
|
import * as UI from '../../../ui/legacy/legacy.js';
|
|
16
|
-
import
|
|
14
|
+
import {Directives, html, nothing, render} from '../../../ui/lit/lit.js';
|
|
17
15
|
import {PatchWidget} from '../PatchWidget.js';
|
|
18
16
|
|
|
19
17
|
import {ChatInput} from './ChatInput.js';
|
|
@@ -22,7 +20,7 @@ import chatViewStyles from './chatView.css.js';
|
|
|
22
20
|
|
|
23
21
|
export {ChatInput, type ImageInputData} from './ChatInput.js';
|
|
24
22
|
|
|
25
|
-
const {
|
|
23
|
+
const {ref, repeat, classMap} = Directives;
|
|
26
24
|
|
|
27
25
|
/*
|
|
28
26
|
* Strings that don't need to be translated at this time.
|
|
@@ -38,6 +36,12 @@ const lockedString = i18n.i18n.lockedString;
|
|
|
38
36
|
|
|
39
37
|
const SCROLL_ROUNDING_OFFSET = 1;
|
|
40
38
|
|
|
39
|
+
interface ViewOutput {
|
|
40
|
+
mainElement?: HTMLElement;
|
|
41
|
+
input?: UI.Widget.WidgetElement<ChatInput>;
|
|
42
|
+
}
|
|
43
|
+
type View = (input: ChatWidgetInput, output: ViewOutput, target: HTMLElement|ShadowRoot) => void;
|
|
44
|
+
|
|
41
45
|
export interface Props {
|
|
42
46
|
onTextSubmit:
|
|
43
47
|
(text: string, imageInput?: Host.AidaClient.Part,
|
|
@@ -64,18 +68,110 @@ export interface Props {
|
|
|
64
68
|
emptyStateSuggestions: AiAssistanceModel.AiAgent.ConversationSuggestion[];
|
|
65
69
|
inputPlaceholder: Platform.UIString.LocalizedString;
|
|
66
70
|
disclaimerText: Platform.UIString.LocalizedString;
|
|
67
|
-
isArtifactsSidebarOpen: boolean;
|
|
68
71
|
uploadImageInputEnabled?: boolean;
|
|
69
72
|
markdownRenderer: MarkdownLitRenderer;
|
|
70
73
|
additionalFloatyContext: UI.Floaty.FloatyContextSelection[];
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
interface ChatWidgetInput extends Props {
|
|
77
|
+
handleScroll: (ev: Event) => void;
|
|
78
|
+
handleSuggestionClick: (title: string) => void;
|
|
79
|
+
handleMessageContainerRef: (el: Element|undefined) => void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const DEFAULT_VIEW: View = (input, output, target) => {
|
|
83
|
+
const inputWidgetClasses = classMap({
|
|
84
|
+
'chat-input-widget': true,
|
|
85
|
+
sticky: !input.isReadOnly,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// clang-format off
|
|
89
|
+
render(html`
|
|
90
|
+
<style>${chatViewStyles}</style>
|
|
91
|
+
<div class="chat-ui">
|
|
92
|
+
<main @scroll=${input.handleScroll} ${ref(element => { output.mainElement = element as HTMLElement; } )}>
|
|
93
|
+
${input.messages.length > 0 ? html`
|
|
94
|
+
<div class="messages-container" ${ref(input.handleMessageContainerRef)}>
|
|
95
|
+
${repeat(input.messages, message =>
|
|
96
|
+
html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(ChatMessage, {
|
|
97
|
+
message,
|
|
98
|
+
isLoading: input.isLoading,
|
|
99
|
+
isReadOnly: input.isReadOnly,
|
|
100
|
+
canShowFeedbackForm: input.canShowFeedbackForm,
|
|
101
|
+
userInfo: input.userInfo,
|
|
102
|
+
markdownRenderer: input.markdownRenderer,
|
|
103
|
+
isLastMessage: input.messages.at(-1) === message,
|
|
104
|
+
onSuggestionClick: input.handleSuggestionClick,
|
|
105
|
+
onFeedbackSubmit: input.onFeedbackSubmit,
|
|
106
|
+
onCopyResponseClick: input.onCopyResponseClick,
|
|
107
|
+
})}></devtools-widget>`
|
|
108
|
+
)}
|
|
109
|
+
${input.isLoading ? nothing : html`<devtools-widget
|
|
110
|
+
.widgetConfig=${UI.Widget.widgetConfig(PatchWidget, {
|
|
111
|
+
changeSummary: input.changeSummary ?? '',
|
|
112
|
+
changeManager: input.changeManager,
|
|
113
|
+
})}
|
|
114
|
+
></devtools-widget>`}
|
|
115
|
+
</div>
|
|
116
|
+
` : html`
|
|
117
|
+
<div class="empty-state-container">
|
|
118
|
+
<div class="header">
|
|
119
|
+
<div class="icon">
|
|
120
|
+
<devtools-icon
|
|
121
|
+
name="smart-assistant"
|
|
122
|
+
></devtools-icon>
|
|
123
|
+
</div>
|
|
124
|
+
<h1>${lockedString(UIStringsNotTranslate.emptyStateText)}</h1>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="empty-state-content">
|
|
127
|
+
${input.emptyStateSuggestions.map(({title, jslogContext}) => {
|
|
128
|
+
return html`<devtools-button
|
|
129
|
+
class="suggestion"
|
|
130
|
+
@click=${() => input.handleSuggestionClick(title)}
|
|
131
|
+
.data=${
|
|
132
|
+
{
|
|
133
|
+
variant: Buttons.Button.Variant.OUTLINED,
|
|
134
|
+
size: Buttons.Button.Size.REGULAR,
|
|
135
|
+
title,
|
|
136
|
+
jslogContext: jslogContext ?? 'suggestion',
|
|
137
|
+
disabled: input.isTextInputDisabled,
|
|
138
|
+
} as Buttons.Button.ButtonData
|
|
139
|
+
}
|
|
140
|
+
>${title}</devtools-button>`;
|
|
141
|
+
})}
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
`}
|
|
145
|
+
<devtools-widget class=${inputWidgetClasses} .widgetConfig=${UI.Widget.widgetConfig(ChatInput, {
|
|
146
|
+
isLoading: input.isLoading,
|
|
147
|
+
blockedByCrossOrigin: input.blockedByCrossOrigin,
|
|
148
|
+
isTextInputDisabled: input.isTextInputDisabled,
|
|
149
|
+
inputPlaceholder: input.inputPlaceholder,
|
|
150
|
+
disclaimerText: input.disclaimerText,
|
|
151
|
+
selectedContext: input.selectedContext,
|
|
152
|
+
inspectElementToggled: input.inspectElementToggled,
|
|
153
|
+
multimodalInputEnabled: input.multimodalInputEnabled ?? false,
|
|
154
|
+
conversationType: input.conversationType,
|
|
155
|
+
uploadImageInputEnabled: input.uploadImageInputEnabled ?? false,
|
|
156
|
+
isReadOnly: input.isReadOnly,
|
|
157
|
+
additionalFloatyContext: input.additionalFloatyContext,
|
|
158
|
+
onContextClick: input.onContextClick,
|
|
159
|
+
onInspectElementClick: input.onInspectElementClick,
|
|
160
|
+
onTextSubmit: input.onTextSubmit,
|
|
161
|
+
onCancelClick: input.onCancelClick,
|
|
162
|
+
onNewConversation: input.onNewConversation,
|
|
163
|
+
})} ${ref(element => { output.input = element as UI.Widget.WidgetElement<ChatInput>; } )}></devtools-widget>
|
|
164
|
+
</main>
|
|
165
|
+
</div>
|
|
166
|
+
`, target);
|
|
167
|
+
// clang-format on
|
|
168
|
+
};
|
|
73
169
|
export class ChatView extends HTMLElement {
|
|
74
170
|
readonly #shadow = this.attachShadow({mode: 'open'});
|
|
75
171
|
#scrollTop?: number;
|
|
76
172
|
#props: Props;
|
|
77
173
|
#messagesContainerElement?: Element;
|
|
78
|
-
#
|
|
174
|
+
#output: ViewOutput = {};
|
|
79
175
|
#messagesContainerResizeObserver = new ResizeObserver(() => this.#handleMessagesContainerResize());
|
|
80
176
|
/**
|
|
81
177
|
* Indicates whether the chat scroll position should be pinned to the bottom.
|
|
@@ -94,11 +190,12 @@ export class ChatView extends HTMLElement {
|
|
|
94
190
|
* whether to pin the content to the bottom.
|
|
95
191
|
*/
|
|
96
192
|
#isProgrammaticScroll = false;
|
|
97
|
-
#
|
|
193
|
+
#view: View;
|
|
98
194
|
|
|
99
|
-
constructor(props: Props) {
|
|
195
|
+
constructor(props: Props, view = DEFAULT_VIEW) {
|
|
100
196
|
super();
|
|
101
197
|
this.#props = props;
|
|
198
|
+
this.#view = view;
|
|
102
199
|
}
|
|
103
200
|
|
|
104
201
|
set props(props: Props) {
|
|
@@ -132,7 +229,7 @@ export class ChatView extends HTMLElement {
|
|
|
132
229
|
return;
|
|
133
230
|
}
|
|
134
231
|
|
|
135
|
-
if (!this.#
|
|
232
|
+
if (!this.#output.mainElement) {
|
|
136
233
|
return;
|
|
137
234
|
}
|
|
138
235
|
|
|
@@ -140,11 +237,11 @@ export class ChatView extends HTMLElement {
|
|
|
140
237
|
}
|
|
141
238
|
|
|
142
239
|
scrollToBottom(): void {
|
|
143
|
-
if (!this.#
|
|
240
|
+
if (!this.#output.mainElement) {
|
|
144
241
|
return;
|
|
145
242
|
}
|
|
146
243
|
|
|
147
|
-
this.#setMainElementScrollTop(this.#
|
|
244
|
+
this.#setMainElementScrollTop(this.#output.mainElement.scrollHeight);
|
|
148
245
|
}
|
|
149
246
|
|
|
150
247
|
#handleMessagesContainerResize(): void {
|
|
@@ -152,26 +249,26 @@ export class ChatView extends HTMLElement {
|
|
|
152
249
|
return;
|
|
153
250
|
}
|
|
154
251
|
|
|
155
|
-
if (!this.#
|
|
252
|
+
if (!this.#output.mainElement) {
|
|
156
253
|
return;
|
|
157
254
|
}
|
|
158
255
|
|
|
159
256
|
if (this.#pinScrollToBottom) {
|
|
160
|
-
this.#setMainElementScrollTop(this.#
|
|
257
|
+
this.#setMainElementScrollTop(this.#output.mainElement.scrollHeight);
|
|
161
258
|
}
|
|
162
259
|
}
|
|
163
260
|
|
|
164
261
|
#setMainElementScrollTop(scrollTop: number): void {
|
|
165
|
-
if (!this.#
|
|
262
|
+
if (!this.#output.mainElement) {
|
|
166
263
|
return;
|
|
167
264
|
}
|
|
168
265
|
|
|
169
266
|
this.#scrollTop = scrollTop;
|
|
170
267
|
this.#isProgrammaticScroll = true;
|
|
171
|
-
this.#
|
|
268
|
+
this.#output.mainElement.scrollTop = scrollTop;
|
|
172
269
|
}
|
|
173
270
|
|
|
174
|
-
#handleMessageContainerRef(el: Element|undefined): void {
|
|
271
|
+
#handleMessageContainerRef = (el: Element|undefined): void => {
|
|
175
272
|
this.#messagesContainerElement = el;
|
|
176
273
|
|
|
177
274
|
if (el) {
|
|
@@ -180,7 +277,7 @@ export class ChatView extends HTMLElement {
|
|
|
180
277
|
this.#pinScrollToBottom = true;
|
|
181
278
|
this.#messagesContainerResizeObserver.disconnect();
|
|
182
279
|
}
|
|
183
|
-
}
|
|
280
|
+
};
|
|
184
281
|
|
|
185
282
|
#handleScroll = (ev: Event): void => {
|
|
186
283
|
if (!ev.target || !(ev.target instanceof HTMLElement)) {
|
|
@@ -201,103 +298,21 @@ export class ChatView extends HTMLElement {
|
|
|
201
298
|
};
|
|
202
299
|
|
|
203
300
|
#handleSuggestionClick = (suggestion: string): void => {
|
|
204
|
-
this.#
|
|
301
|
+
this.#output.input?.getWidget()?.setInputValue(suggestion);
|
|
205
302
|
this.#render();
|
|
206
303
|
this.focusTextInput();
|
|
207
304
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceDynamicSuggestionClicked);
|
|
208
305
|
};
|
|
209
306
|
|
|
210
307
|
#render(): void {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
<div class="chat-ui">
|
|
220
|
-
<main @scroll=${this.#handleScroll} ${ref(this.#mainElementRef)}>
|
|
221
|
-
${this.#props.messages.length > 0 ? html`
|
|
222
|
-
<div class="messages-container" ${ref(this.#handleMessageContainerRef)}>
|
|
223
|
-
${repeat(this.#props.messages, message =>
|
|
224
|
-
html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(ChatMessage, {
|
|
225
|
-
message,
|
|
226
|
-
isLoading: this.#props.isLoading,
|
|
227
|
-
isReadOnly: this.#props.isReadOnly,
|
|
228
|
-
canShowFeedbackForm: this.#props.canShowFeedbackForm,
|
|
229
|
-
userInfo: this.#props.userInfo,
|
|
230
|
-
markdownRenderer: this.#props.markdownRenderer,
|
|
231
|
-
isLastMessage: this.#props.messages.at(-1) === message,
|
|
232
|
-
onSuggestionClick: this.#handleSuggestionClick,
|
|
233
|
-
onFeedbackSubmit: this.#props.onFeedbackSubmit,
|
|
234
|
-
onCopyResponseClick: this.#props.onCopyResponseClick,
|
|
235
|
-
})}></devtools-widget>`
|
|
236
|
-
)}
|
|
237
|
-
${this.#props.isLoading ? Lit.nothing : html`<devtools-widget
|
|
238
|
-
.widgetConfig=${UI.Widget.widgetConfig(PatchWidget, {
|
|
239
|
-
changeSummary: this.#props.changeSummary ?? '',
|
|
240
|
-
changeManager: this.#props.changeManager,
|
|
241
|
-
})}
|
|
242
|
-
></devtools-widget>`}
|
|
243
|
-
</div>
|
|
244
|
-
` : html`
|
|
245
|
-
<div class="empty-state-container">
|
|
246
|
-
<div class="header">
|
|
247
|
-
<div class="icon">
|
|
248
|
-
<devtools-icon
|
|
249
|
-
name="smart-assistant"
|
|
250
|
-
></devtools-icon>
|
|
251
|
-
</div>
|
|
252
|
-
<h1>${lockedString(UIStringsNotTranslate.emptyStateText)}</h1>
|
|
253
|
-
</div>
|
|
254
|
-
<div class="empty-state-content">
|
|
255
|
-
${this.#props.emptyStateSuggestions.map(({title, jslogContext}) => {
|
|
256
|
-
return html`<devtools-button
|
|
257
|
-
class="suggestion"
|
|
258
|
-
@click=${() => this.#handleSuggestionClick(title)}
|
|
259
|
-
.data=${
|
|
260
|
-
{
|
|
261
|
-
variant: Buttons.Button.Variant.OUTLINED,
|
|
262
|
-
size: Buttons.Button.Size.REGULAR,
|
|
263
|
-
title,
|
|
264
|
-
jslogContext: jslogContext ?? 'suggestion',
|
|
265
|
-
disabled: this.#props.isTextInputDisabled,
|
|
266
|
-
} as Buttons.Button.ButtonData
|
|
267
|
-
}
|
|
268
|
-
>${title}</devtools-button>`;
|
|
269
|
-
})}
|
|
270
|
-
</div>
|
|
271
|
-
</div>
|
|
272
|
-
`}
|
|
273
|
-
<devtools-widget class=${inputWidgetClasses} .widgetConfig=${UI.Widget.widgetConfig(ChatInput, {
|
|
274
|
-
isLoading: this.#props.isLoading,
|
|
275
|
-
blockedByCrossOrigin: this.#props.blockedByCrossOrigin,
|
|
276
|
-
isTextInputDisabled: this.#props.isTextInputDisabled,
|
|
277
|
-
inputPlaceholder: this.#props.inputPlaceholder,
|
|
278
|
-
disclaimerText: this.#props.disclaimerText,
|
|
279
|
-
selectedContext: this.#props.selectedContext,
|
|
280
|
-
inspectElementToggled: this.#props.inspectElementToggled,
|
|
281
|
-
multimodalInputEnabled: this.#props.multimodalInputEnabled ?? false,
|
|
282
|
-
conversationType: this.#props.conversationType,
|
|
283
|
-
uploadImageInputEnabled: this.#props.uploadImageInputEnabled ?? false,
|
|
284
|
-
isReadOnly: this.#props.isReadOnly,
|
|
285
|
-
additionalFloatyContext: this.#props.additionalFloatyContext,
|
|
286
|
-
onContextClick: this.#props.onContextClick,
|
|
287
|
-
onInspectElementClick: this.#props.onInspectElementClick,
|
|
288
|
-
onTextSubmit: (
|
|
289
|
-
text: string, imageInput?: Host.AidaClient.Part,
|
|
290
|
-
multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
|
|
291
|
-
this.#props.onTextSubmit(text, imageInput, multimodalInputType);
|
|
292
|
-
this.#render();
|
|
293
|
-
},
|
|
294
|
-
onCancelClick: this.#props.onCancelClick,
|
|
295
|
-
onNewConversation: this.#props.onNewConversation,
|
|
296
|
-
})} ${ref(this.#inputRef)}></devtools-widget>
|
|
297
|
-
</main>
|
|
298
|
-
</div>
|
|
299
|
-
`, this.#shadow, {host: this});
|
|
300
|
-
// clang-format on
|
|
308
|
+
this.#view(
|
|
309
|
+
{
|
|
310
|
+
...this.#props,
|
|
311
|
+
handleScroll: this.#handleScroll,
|
|
312
|
+
handleSuggestionClick: this.#handleSuggestionClick,
|
|
313
|
+
handleMessageContainerRef: this.#handleMessageContainerRef,
|
|
314
|
+
},
|
|
315
|
+
this.#output, this.#shadow);
|
|
301
316
|
}
|
|
302
317
|
}
|
|
303
318
|
|
|
@@ -2,171 +2,27 @@
|
|
|
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
4
|
|
|
5
|
-
import '../../../models/trace/insights/insights.js';
|
|
6
|
-
import '../../../panels/timeline/components/components.js';
|
|
7
|
-
import './PerformanceAgentFlameChart.js';
|
|
8
|
-
import './CollapsibleAssistanceContentWidget.js';
|
|
9
|
-
|
|
10
5
|
import * as Common from '../../../core/common/common.js';
|
|
11
6
|
import * as SDK from '../../../core/sdk/sdk.js';
|
|
12
7
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
13
|
-
import * as GreenDev from '../../../models/greendev/greendev.js';
|
|
14
|
-
import * as Logs from '../../../models/logs/logs.js';
|
|
15
|
-
import * as NetworkTimeCalculator from '../../../models/network_time_calculator/network_time_calculator.js';
|
|
16
|
-
import * as Helpers from '../../../models/trace/helpers/helpers.js';
|
|
17
8
|
import * as Trace from '../../../models/trace/trace.js';
|
|
18
9
|
import type * as Marked from '../../../third_party/marked/marked.js';
|
|
19
|
-
import * as UI from '../../../ui/legacy/legacy.js';
|
|
20
10
|
import * as Lit from '../../../ui/lit/lit.js';
|
|
21
11
|
import * as PanelsCommon from '../../common/common.js';
|
|
22
|
-
import * as NetworkForward from '../../network/forward/forward.js';
|
|
23
|
-
import * as Network from '../../network/network.js';
|
|
24
|
-
import * as TimelineComponents from '../../timeline/components/components.js';
|
|
25
|
-
import * as Insights from '../../timeline/components/insights/insights.js';
|
|
26
|
-
import * as Timeline from '../../timeline/timeline.js';
|
|
27
12
|
|
|
28
13
|
import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
|
|
29
|
-
import type * as PerformanceAgentFlameChart from './PerformanceAgentFlameChart.js';
|
|
30
14
|
|
|
31
15
|
const {html} = Lit.StaticHtml;
|
|
32
16
|
const {ref, createRef} = Lit.Directives;
|
|
33
|
-
const {widgetConfig} = UI.Widget;
|
|
34
17
|
|
|
35
18
|
export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBlock {
|
|
36
|
-
#insightRenderer = new Insights.InsightRenderer.InsightRenderer();
|
|
37
|
-
|
|
38
19
|
constructor(
|
|
39
20
|
private mainFrameId = '',
|
|
40
|
-
private lookupEvent: (key: Trace.Types.File.SerializableKey) => Trace.Types.Events.Event | null = () => null
|
|
41
|
-
private parsedTrace: Trace.TraceModel.ParsedTrace|null = null) {
|
|
21
|
+
private lookupEvent: (key: Trace.Types.File.SerializableKey) => Trace.Types.Events.Event | null = () => null) {
|
|
42
22
|
super();
|
|
43
23
|
}
|
|
44
24
|
|
|
45
25
|
override templateForToken(token: Marked.Marked.MarkedToken): Lit.LitTemplate|null {
|
|
46
|
-
if (!this.parsedTrace) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// NOTE: The custom tag handling below (e.g., <ai-insight>, <network-request-widget>)
|
|
51
|
-
// is part of a prototype for the GreenDev project and is only rendered when the GreenDev
|
|
52
|
-
// feature is enabled.
|
|
53
|
-
if (token.type === 'html' && GreenDev.Prototypes.instance().isEnabled('inlineWidgets')) {
|
|
54
|
-
if (token.text.includes('<flame-chart-widget')) {
|
|
55
|
-
const startMatch = token.text.match(/start="?(\d+)"?/);
|
|
56
|
-
const endMatch = token.text.match(/end="?(\d+)"?/);
|
|
57
|
-
const start = startMatch ? Number(startMatch[1]) : this.parsedTrace.data.Meta.traceBounds.min;
|
|
58
|
-
const end = endMatch ? Number(endMatch[1]) : this.parsedTrace.data.Meta.traceBounds.max;
|
|
59
|
-
return html`<devtools-performance-agent-flame-chart .data=${{
|
|
60
|
-
parsedTrace: this.parsedTrace,
|
|
61
|
-
start,
|
|
62
|
-
end,
|
|
63
|
-
} as PerformanceAgentFlameChart.PerformanceAgentFlameChartData}
|
|
64
|
-
}></devtools-performance-agent-flame-chart>`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Flexible regex to match the tag name and a value a.
|
|
68
|
-
// match[1]: tagName (e.g., 'ai-insight', 'network-request-widget')
|
|
69
|
-
// match[2]: value (value needed to display the widget)
|
|
70
|
-
const regex = /<([a-z-]+)\s+value="([^"]+)">/;
|
|
71
|
-
const match = token.text.match(regex);
|
|
72
|
-
|
|
73
|
-
if (!match) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const tagName = match[1];
|
|
78
|
-
const value = match[2];
|
|
79
|
-
|
|
80
|
-
if (tagName === 'ai-insight' && value) {
|
|
81
|
-
const componentName = value;
|
|
82
|
-
|
|
83
|
-
const insightSet = this.parsedTrace.insights?.values().next().value;
|
|
84
|
-
const insightM = insightSet?.model[componentName as Trace.Insights.Types.InsightKeys];
|
|
85
|
-
|
|
86
|
-
if (!insightM) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return html`<devtools-collapsible-assistance-content-widget .data=${{
|
|
91
|
-
headerText: `Insight - ${componentName}`, onReveal: () => {
|
|
92
|
-
void UI.InspectorView.InspectorView.instance().showPanel('timeline').then(() => {
|
|
93
|
-
Timeline.TimelinePanel.TimelinePanel.instance().revealInsight(insightM);
|
|
94
|
-
});
|
|
95
|
-
},
|
|
96
|
-
}
|
|
97
|
-
}>
|
|
98
|
-
${this.#insightRenderer.renderInsightToWidgetElement(this.parsedTrace, insightSet, insightM, componentName, {
|
|
99
|
-
selected: true,
|
|
100
|
-
isAIAssistanceContext: true
|
|
101
|
-
})}
|
|
102
|
-
</devtools-collapsible-assistance-content-widget>`;
|
|
103
|
-
}
|
|
104
|
-
if (tagName === 'network-request-widget' && value) {
|
|
105
|
-
const rawTraceEvent =
|
|
106
|
-
Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().getRawTraceEvents().at(Number(value));
|
|
107
|
-
|
|
108
|
-
// Rendering RequestTimingView widget only works for fresh traces where the network log is in sync.
|
|
109
|
-
// If the trace is uploaded, we need to use the synthetic events and
|
|
110
|
-
// render the network request tooltip that uses the synthetic events.
|
|
111
|
-
if (rawTraceEvent && Trace.Types.Events.isSyntheticNetworkRequest(rawTraceEvent)) {
|
|
112
|
-
const rawTraceEventId = rawTraceEvent?.args?.data?.requestId;
|
|
113
|
-
const rawTraceEventUrl = rawTraceEvent?.args?.data?.url;
|
|
114
|
-
const networkRequest = rawTraceEvent ? Logs.NetworkLog.NetworkLog.instance()
|
|
115
|
-
.requestsForId(rawTraceEventId)
|
|
116
|
-
.find(r => r.url() === rawTraceEventUrl) :
|
|
117
|
-
null;
|
|
118
|
-
|
|
119
|
-
if (networkRequest) {
|
|
120
|
-
const calculator = new NetworkTimeCalculator.NetworkTimeCalculator(true);
|
|
121
|
-
return html`<devtools-collapsible-assistance-content-widget
|
|
122
|
-
.data=${{
|
|
123
|
-
headerText: `Network Request: ${
|
|
124
|
-
networkRequest.url().length > 80 ? networkRequest.url().slice(0, 80) + '...' : networkRequest.url()}`,
|
|
125
|
-
onReveal: () => {
|
|
126
|
-
void UI.InspectorView.InspectorView.instance().showPanel('network').then(() => {
|
|
127
|
-
void Common.Revealer.reveal(NetworkForward.UIRequestLocation.UIRequestLocation.tab(
|
|
128
|
-
networkRequest, NetworkForward.UIRequestLocation.UIRequestTabs.TIMING));
|
|
129
|
-
});
|
|
130
|
-
},
|
|
131
|
-
}
|
|
132
|
-
}>
|
|
133
|
-
<devtools-widget class="actions" .widgetConfig=${
|
|
134
|
-
UI.Widget.widgetConfig(Network.RequestTimingView.RequestTimingView, {
|
|
135
|
-
request: networkRequest,
|
|
136
|
-
calculator,
|
|
137
|
-
})}></devtools-widget>
|
|
138
|
-
</devtools-collapsible-assistance-content-widget>`;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const syntheticRequest =
|
|
143
|
-
Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().syntheticEventForRawEventIndex(
|
|
144
|
-
Number(value));
|
|
145
|
-
|
|
146
|
-
let networkTooltip = null;
|
|
147
|
-
if (syntheticRequest && Trace.Types.Events.isSyntheticNetworkRequest(syntheticRequest)) {
|
|
148
|
-
// clang-format off
|
|
149
|
-
networkTooltip = html`<devtools-widget .widgetConfig=${widgetConfig(TimelineComponents.NetworkRequestTooltip.NetworkRequestTooltip, {
|
|
150
|
-
networkRequest: syntheticRequest,
|
|
151
|
-
})}></devtools-widget>`;
|
|
152
|
-
// clang-format on
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return html`<devtools-collapsible-assistance-content-widget
|
|
156
|
-
.data=${{
|
|
157
|
-
headerText: 'Network Request', onReveal: () => {
|
|
158
|
-
// eslint-disable-next-line no-console
|
|
159
|
-
console.log('Reveal network request', value);
|
|
160
|
-
},
|
|
161
|
-
}
|
|
162
|
-
}>
|
|
163
|
-
${networkTooltip}
|
|
164
|
-
</devtools-collapsible-assistance-content-widget>`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
26
|
if (token.type === 'link' && token.href.startsWith('#')) {
|
|
171
27
|
if (token.href.startsWith('#node-')) {
|
|
172
28
|
const nodeId = Number(token.href.replace('#node-', '')) as Protocol.DOM.BackendNodeId;
|
|
@@ -644,7 +644,7 @@ function renderIdentity(identityData: IdentitySectionData, onCopy: () => void, o
|
|
|
644
644
|
jslog=${VisualLogging.action('help').track({hover: true})}>
|
|
645
645
|
</devtools-icon>
|
|
646
646
|
<devtools-link href="https://developer.chrome.com/blog/pwa-manifest-id/"
|
|
647
|
-
|
|
647
|
+
jslogcontext="learn-more"
|
|
648
648
|
${ref(setFocusOnSection(i18nString(UIStrings.identity), output))}>
|
|
649
649
|
${i18nString(UIStrings.learnMore)}
|
|
650
650
|
</devtools-link>
|
|
@@ -766,7 +766,7 @@ function renderIcons(
|
|
|
766
766
|
<div class="report-row">
|
|
767
767
|
${i18nTemplate(str_, UIStrings.needHelpReadOurS, {
|
|
768
768
|
PH1: html`
|
|
769
|
-
<devtools-link href="https://web.dev/maskable-icon/"
|
|
769
|
+
<devtools-link href="https://web.dev/maskable-icon/" jslogcontext="learn-more">
|
|
770
770
|
${i18nString(UIStrings.documentationOnMaskableIcons)}
|
|
771
771
|
</devtools-link>`,
|
|
772
772
|
})}
|
|
@@ -866,7 +866,7 @@ function renderWindowControlsSection(
|
|
|
866
866
|
PH2: html`<code>
|
|
867
867
|
<devtools-link
|
|
868
868
|
href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display_override"
|
|
869
|
-
|
|
869
|
+
jslogcontext="display-override"
|
|
870
870
|
${ref(setFocusOnSection(i18nString(UIStrings.windowControlsOverlay), output))}>
|
|
871
871
|
display-override
|
|
872
872
|
</devtools-link>
|
|
@@ -882,7 +882,7 @@ function renderWindowControlsSection(
|
|
|
882
882
|
${i18nTemplate(str_, UIStrings.wcoNotFound, {PH1: html`<code>
|
|
883
883
|
<devtools-link
|
|
884
884
|
href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display_override"
|
|
885
|
-
|
|
885
|
+
jslogcontext="display-override"
|
|
886
886
|
${ref(setFocusOnSection(i18nString(UIStrings.windowControlsOverlay), output))}>
|
|
887
887
|
display-override
|
|
888
888
|
</devtools-link>
|
|
@@ -891,7 +891,7 @@ function renderWindowControlsSection(
|
|
|
891
891
|
<div class="report-row">
|
|
892
892
|
${i18nTemplate(str_, UIStrings.wcoNeedHelpReadMore, {PH1: html`<devtools-link
|
|
893
893
|
href="https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay"
|
|
894
|
-
|
|
894
|
+
jslogcontext="customize-pwa-tittle-bar">
|
|
895
895
|
${i18nString(UIStrings.customizePwaTitleBar)}
|
|
896
896
|
</devtools-link>`})}
|
|
897
897
|
</div>
|
|
@@ -12,6 +12,8 @@ interface EventWithTimestamp {
|
|
|
12
12
|
}
|
|
13
13
|
export interface SessionAndEvents {
|
|
14
14
|
session?: Protocol.Network.DeviceBoundSession;
|
|
15
|
+
isSessionTerminated: boolean;
|
|
16
|
+
hasErrors: boolean;
|
|
15
17
|
eventsById: Map<string, EventWithTimestamp>;
|
|
16
18
|
}
|
|
17
19
|
type SessionIdToSessionMap = Map<string|undefined, SessionAndEvents>;
|
|
@@ -60,11 +62,21 @@ export class DeviceBoundSessionsModel extends Common.ObjectWrapper.ObjectWrapper
|
|
|
60
62
|
return;
|
|
61
63
|
}
|
|
62
64
|
const emptySessions = new Map<string, Array<string|undefined>>();
|
|
65
|
+
const noLongerFailedSessions = new Map<string, Array<string|undefined>>();
|
|
63
66
|
const emptySites = new Set<string>();
|
|
64
67
|
for (const [site, sessionIdToSessionMap] of [...this.#siteSessions]) {
|
|
65
68
|
let emptySessionsSiteEntry = emptySessions.get(site);
|
|
69
|
+
let noLongerFailedSessionsSiteEntry = noLongerFailedSessions.get(site);
|
|
66
70
|
for (const [sessionId, sessionAndEvents] of sessionIdToSessionMap) {
|
|
67
71
|
sessionAndEvents.eventsById.clear();
|
|
72
|
+
if (sessionAndEvents.hasErrors) {
|
|
73
|
+
sessionAndEvents.hasErrors = false;
|
|
74
|
+
if (!noLongerFailedSessionsSiteEntry) {
|
|
75
|
+
noLongerFailedSessionsSiteEntry = [];
|
|
76
|
+
noLongerFailedSessions.set(site, noLongerFailedSessionsSiteEntry);
|
|
77
|
+
}
|
|
78
|
+
noLongerFailedSessionsSiteEntry.push(sessionId);
|
|
79
|
+
}
|
|
68
80
|
if (sessionAndEvents.session) {
|
|
69
81
|
continue;
|
|
70
82
|
}
|
|
@@ -84,13 +96,30 @@ export class DeviceBoundSessionsModel extends Common.ObjectWrapper.ObjectWrapper
|
|
|
84
96
|
}
|
|
85
97
|
}
|
|
86
98
|
|
|
87
|
-
this.dispatchEventToListeners(
|
|
99
|
+
this.dispatchEventToListeners(
|
|
100
|
+
DeviceBoundSessionModelEvents.CLEAR_EVENTS, {emptySessions, emptySites, noLongerFailedSessions});
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
isSiteVisible(site: string): boolean {
|
|
91
104
|
return this.#visibleSites.has(site);
|
|
92
105
|
}
|
|
93
106
|
|
|
107
|
+
isSessionTerminated(site: string, sessionId?: string): boolean {
|
|
108
|
+
const session = this.getSession(site, sessionId);
|
|
109
|
+
if (session === undefined) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return session.isSessionTerminated;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
sessionHasErrors(site: string, sessionId?: string): boolean {
|
|
116
|
+
const session = this.getSession(site, sessionId);
|
|
117
|
+
if (session === undefined) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return session.hasErrors;
|
|
121
|
+
}
|
|
122
|
+
|
|
94
123
|
getSession(site: string, sessionId?: string): SessionAndEvents|undefined {
|
|
95
124
|
return this.#siteSessions.get(site)?.get(sessionId);
|
|
96
125
|
}
|
|
@@ -116,7 +145,12 @@ export class DeviceBoundSessionsModel extends Common.ObjectWrapper.ObjectWrapper
|
|
|
116
145
|
|
|
117
146
|
let sessionAndEvent = sessionIdToSessionMap.get(sessionId);
|
|
118
147
|
if (!sessionAndEvent) {
|
|
119
|
-
sessionAndEvent = {
|
|
148
|
+
sessionAndEvent = {
|
|
149
|
+
session: undefined,
|
|
150
|
+
isSessionTerminated: false,
|
|
151
|
+
hasErrors: false,
|
|
152
|
+
eventsById: new Map<string, EventWithTimestamp>()
|
|
153
|
+
};
|
|
120
154
|
sessionIdToSessionMap.set(sessionId, sessionAndEvent);
|
|
121
155
|
}
|
|
122
156
|
return sessionAndEvent;
|
|
@@ -145,6 +179,20 @@ export class DeviceBoundSessionsModel extends Common.ObjectWrapper.ObjectWrapper
|
|
|
145
179
|
sessionAndEvent.session.cachedChallenge = event.challengeEventDetails.challenge;
|
|
146
180
|
}
|
|
147
181
|
|
|
182
|
+
// Set the session's terminated status based on the event.
|
|
183
|
+
if (event.succeeded) {
|
|
184
|
+
if (event.terminationEventDetails) {
|
|
185
|
+
sessionAndEvent.isSessionTerminated = true;
|
|
186
|
+
} else if (event.creationEventDetails) {
|
|
187
|
+
sessionAndEvent.isSessionTerminated = false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Set that the session has errors if the latest event failed.
|
|
192
|
+
if (!event.succeeded) {
|
|
193
|
+
sessionAndEvent.hasErrors = true;
|
|
194
|
+
}
|
|
195
|
+
|
|
148
196
|
this.dispatchEventToListeners(
|
|
149
197
|
DeviceBoundSessionModelEvents.EVENT_OCCURRED,
|
|
150
198
|
{site: eventWithTimestamp.event.site, sessionId: eventWithTimestamp.event.sessionId});
|
|
@@ -164,6 +212,9 @@ export interface DeviceBoundSessionModelEventTypes {
|
|
|
164
212
|
[DeviceBoundSessionModelEvents.ADD_VISIBLE_SITE]: {site: string};
|
|
165
213
|
[DeviceBoundSessionModelEvents.CLEAR_VISIBLE_SITES]: void;
|
|
166
214
|
[DeviceBoundSessionModelEvents.EVENT_OCCURRED]: {site: string, sessionId?: string};
|
|
167
|
-
[DeviceBoundSessionModelEvents.CLEAR_EVENTS]:
|
|
168
|
-
|
|
215
|
+
[DeviceBoundSessionModelEvents.CLEAR_EVENTS]: {
|
|
216
|
+
emptySessions: Map<string, Array<string|undefined>>,
|
|
217
|
+
emptySites: Set<string>,
|
|
218
|
+
noLongerFailedSessions: Map<string, Array<string|undefined>>,
|
|
219
|
+
};
|
|
169
220
|
}
|