Skip to content

Commit 14cff69

Browse files
Samiya CaurDevtools-frontend LUCI CQ
authored andcommitted
Fix issues with syncing settings & AIDA state with code completion state
This CL fixes the following issues: - Even when there is "Verify it's you", the toolbar is visible and requests are being triggered - On disabling the feature in the Settings, on the same session, it still triggers code completions Bug: 442582444 Change-Id: If953b5e0b3abe78d660da449df4ef5a43ede9c51 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6904050 Reviewed-by: Ergün Erdoğmuş <ergunsh@chromium.org> Commit-Queue: Samiya Caur <samiyac@chromium.org>
1 parent a1940f9 commit 14cff69

12 files changed

Lines changed: 353 additions & 25 deletions

front_end/core/host/AidaClient.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,8 @@ export class HostConfigTracker extends Common.ObjectWrapper.ObjectWrapper<EventT
717717
const config =
718718
await new Promise<Root.Runtime.HostConfig>(resolve => InspectorFrontendHostInstance.getHostConfig(resolve));
719719
Object.assign(Root.Runtime.hostConfig, config);
720+
// TODO(crbug.com/442545623): Send `currentAidaAvailability` to the listeners as part of the event so that
721+
// `await AidaClient.checkAccessPreconditions()` does not need to be called again in the event handlers.
720722
this.dispatchEventToListeners(Events.AIDA_AVAILABILITY_CHANGED);
721723
}
722724
}

front_end/models/ai_code_completion/AiCodeCompletion.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,9 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
377377
clearTimeout(this.#renderingTimeout);
378378
this.#renderingTimeout = undefined;
379379
}
380+
this.#editor.dispatch({
381+
effects: TextEditor.Config.setAiAutoCompleteSuggestion.of(null),
382+
});
380383
}
381384
}
382385

front_end/panels/common/AiCodeCompletionDisclaimer.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import * as Host from '../../core/host/host.js';
56
import * as Root from '../../core/root/root.js';
67
import {renderElementIntoDOM} from '../../testing/DOMHelpers.js';
78
import {describeWithEnvironment, updateHostConfig} from '../../testing/EnvironmentHelpers.js';
@@ -50,4 +51,40 @@ describeWithEnvironment('AiCodeCompletionDisclaimer', () => {
5051
assert.isTrue(showViewStub.calledOnceWith('chrome-ai'));
5152
widget.detach();
5253
});
54+
55+
it('renders when AIDA becomes available', async () => {
56+
const checkAccessPreconditionsStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
57+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
58+
59+
const {view, widget} = await createDisclaimer();
60+
61+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
62+
63+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
64+
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
65+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
66+
67+
await view.nextInput;
68+
69+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
70+
widget.detach();
71+
});
72+
73+
it('does not render when AIDA becomes unavailable', async () => {
74+
const checkAccessPreconditionsStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
75+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
76+
77+
const {view, widget} = await createDisclaimer();
78+
79+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
80+
81+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
82+
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
83+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
84+
85+
await view.nextInput;
86+
87+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
88+
widget.detach();
89+
});
5390
});

front_end/panels/common/AiCodeCompletionDisclaimer.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import '../../ui/components/spinners/spinners.js';
66
import '../../ui/components/tooltips/tooltips.js';
77

8+
import * as Host from '../../core/host/host.js';
89
import * as i18n from '../../core/i18n/i18n.js';
910
import * as Root from '../../core/root/root.js';
1011
import * as UI from '../../ui/legacy/legacy.js';
@@ -47,6 +48,7 @@ const lockedString = i18n.i18n.lockedString;
4748
export interface ViewInput {
4849
disclaimerTooltipId?: string;
4950
noLogging: boolean;
51+
aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
5052
onManageInSettingsTooltipClick: () => void;
5153
}
5254

@@ -57,13 +59,12 @@ export interface ViewOutput {
5759

5860
export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
5961

60-
export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View =
61-
(input, output, target) => {
62-
if (!input.disclaimerTooltipId) {
63-
render(nothing, target);
64-
return;
65-
}
66-
// clang-format off
62+
export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, output, target) => {
63+
if (input.aidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE || !input.disclaimerTooltipId) {
64+
render(nothing, target);
65+
return;
66+
}
67+
// clang-format off
6768
render(
6869
html`
6970
<style>${styles}</style>
@@ -114,8 +115,8 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View =
114115
>${lockedString(UIStringsNotTranslate.manageInSettings)}</span></div></devtools-tooltip>
115116
</div>
116117
`, target);
117-
// clang-format on
118-
};
118+
// clang-format on
119+
};
119120

120121
const MINIMUM_LOADING_STATE_TIMEOUT = 1000;
121122

@@ -129,11 +130,15 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
129130
#loadingStartTime = 0;
130131
#spinnerLoadingTimeout: number|undefined;
131132

133+
#aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
134+
#boundOnAidaAvailabilityChange: () => Promise<void>;
135+
132136
constructor(element?: HTMLElement, view: View = DEFAULT_SUMMARY_TOOLBAR_VIEW) {
133137
super(element);
134138
this.markAsExternallyManaged();
135139
this.#noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
136140
Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
141+
this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
137142
this.#view = view;
138143
}
139144

@@ -169,6 +174,14 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
169174
}
170175
}
171176

177+
async #onAidaAvailabilityChange(): Promise<void> {
178+
const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
179+
if (currentAidaAvailability !== this.#aidaAvailability) {
180+
this.#aidaAvailability = currentAidaAvailability;
181+
this.requestUpdate();
182+
}
183+
}
184+
172185
#onManageInSettingsTooltipClick(): void {
173186
this.#viewOutput.hideTooltip?.();
174187
void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
@@ -179,8 +192,22 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
179192
{
180193
disclaimerTooltipId: this.#disclaimerTooltipId,
181194
noLogging: this.#noLogging,
195+
aidaAvailability: this.#aidaAvailability,
182196
onManageInSettingsTooltipClick: this.#onManageInSettingsTooltipClick.bind(this),
183197
},
184198
this.#viewOutput, this.contentElement);
185199
}
200+
201+
override wasShown(): void {
202+
super.wasShown();
203+
Host.AidaClient.HostConfigTracker.instance().addEventListener(
204+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
205+
void this.#onAidaAvailabilityChange();
206+
}
207+
208+
override willHide(): void {
209+
super.willHide();
210+
Host.AidaClient.HostConfigTracker.instance().removeEventListener(
211+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
212+
}
186213
}

front_end/panels/common/AiCodeCompletionSummaryToolbar.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import * as Host from '../../core/host/host.js';
56
import {renderElementIntoDOM} from '../../testing/DOMHelpers.js';
67
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
78
import {createViewFunctionStub} from '../../testing/ViewFunctionHelpers.js';
@@ -78,4 +79,40 @@ describeWithEnvironment('AiCodeCompletionSummaryToolbar', () => {
7879

7980
widget.detach();
8081
});
82+
83+
it('renders when AIDA becomes available', async () => {
84+
const checkAccessPreconditionsStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
85+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
86+
87+
const {view, widget} = await createToolbar();
88+
89+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
90+
91+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
92+
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
93+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
94+
95+
await view.nextInput;
96+
97+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
98+
widget.detach();
99+
});
100+
101+
it('does not render when AIDA becomes unavailable', async () => {
102+
const checkAccessPreconditionsStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
103+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
104+
105+
const {view, widget} = await createToolbar();
106+
107+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
108+
109+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
110+
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
111+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
112+
113+
await view.nextInput;
114+
115+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
116+
widget.detach();
117+
});
81118
});

front_end/panels/common/AiCodeCompletionSummaryToolbar.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import '../../ui/components/spinners/spinners.js';
66
import '../../ui/components/tooltips/tooltips.js';
77

8+
import * as Host from '../../core/host/host.js';
89
import * as i18n from '../../core/i18n/i18n.js';
910
import * as UI from '../../ui/legacy/legacy.js';
1011
import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
@@ -38,11 +39,16 @@ export interface ViewInput {
3839
citationsTooltipId: string;
3940
loading: boolean;
4041
hasTopBorder: boolean;
42+
aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
4143
}
4244

4345
export type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
4446

4547
export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, _output, target) => {
48+
if (input.aidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
49+
render(nothing, target);
50+
return;
51+
}
4652
const toolbarClasses = Directives.classMap({
4753
'ai-code-completion-summary-toolbar': true,
4854
'has-disclaimer': Boolean(input.disclaimerTooltipId),
@@ -101,15 +107,27 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
101107
#loading = false;
102108
#hasTopBorder = false;
103109

110+
#aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
111+
#boundOnAidaAvailabilityChange: () => Promise<void>;
112+
104113
constructor(props: AiCodeCompletionSummaryToolbarProps, view?: View) {
105114
super();
106115
this.#disclaimerTooltipId = props.disclaimerTooltipId;
107116
this.#citationsTooltipId = props.citationsTooltipId;
108117
this.#hasTopBorder = props.hasTopBorder ?? false;
118+
this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
109119
this.#view = view ?? DEFAULT_SUMMARY_TOOLBAR_VIEW;
110120
this.requestUpdate();
111121
}
112122

123+
async #onAidaAvailabilityChange(): Promise<void> {
124+
const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
125+
if (currentAidaAvailability !== this.#aidaAvailability) {
126+
this.#aidaAvailability = currentAidaAvailability;
127+
this.requestUpdate();
128+
}
129+
}
130+
113131
setLoading(loading: boolean): void {
114132
this.#loading = loading;
115133
this.requestUpdate();
@@ -133,7 +151,21 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
133151
citationsTooltipId: this.#citationsTooltipId,
134152
loading: this.#loading,
135153
hasTopBorder: this.#hasTopBorder,
154+
aidaAvailability: this.#aidaAvailability,
136155
},
137156
undefined, this.contentElement);
138157
}
158+
159+
override wasShown(): void {
160+
super.wasShown();
161+
Host.AidaClient.HostConfigTracker.instance().addEventListener(
162+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
163+
void this.#onAidaAvailabilityChange();
164+
}
165+
166+
override willHide(): void {
167+
super.willHide();
168+
Host.AidaClient.HostConfigTracker.instance().removeEventListener(
169+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
170+
}
139171
}

front_end/panels/common/AiCodeCompletionTeaser.test.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import * as FreDialog from './FreDialog.js';
1616

1717
describeWithEnvironment('AiCodeCompletionTeaser', () => {
1818
let showFreDialogStub: sinon.SinonStub<Parameters<typeof FreDialog.FreDialog.show>, Promise<boolean>>;
19+
let checkAccessPreconditionsStub: sinon.SinonStub;
1920

2021
beforeEach(() => {
2122
showFreDialogStub = sinon.stub(FreDialog.FreDialog, 'show');
22-
sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions')
23-
.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
23+
checkAccessPreconditionsStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
24+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
2425
});
2526

2627
async function createTeaser() {
@@ -93,4 +94,38 @@ describeWithEnvironment('AiCodeCompletionTeaser', () => {
9394
reminderItem.content.toString().includes('This data will not be used to improve Google’s AI models.')));
9495
widget.detach();
9596
});
97+
98+
it('renders when AIDA becomes available', async () => {
99+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
100+
101+
const {view, widget} = await createTeaser();
102+
103+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
104+
105+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
106+
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
107+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
108+
109+
await view.nextInput;
110+
111+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
112+
widget.detach();
113+
});
114+
115+
it('does not render when AIDA becomes unavailable', async () => {
116+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
117+
118+
const {view, widget} = await createTeaser();
119+
120+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.AVAILABLE);
121+
122+
checkAccessPreconditionsStub.resolves(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
123+
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
124+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
125+
126+
await view.nextInput;
127+
128+
assert.strictEqual(view.input.aidaAvailability, Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL);
129+
widget.detach();
130+
});
96131
});

0 commit comments

Comments
 (0)