0

Add Observer for OS Location Permission and Getter for OS Microphone Permission

There is no observer available for the microphone permission yet.

Bug: b/388580704
Change-Id: Id2c1dc112f12c93e408d378414f57062b1d542ab
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6358436
Reviewed-by: Alex Gough <ajgo@chromium.org>
Reviewed-by: Trevor Perrier <perrier@chromium.org>
Reviewed-by: Dan Harrington <harringtond@chromium.org>
Commit-Queue: Nick Birnie <birnie@google.com>
Cr-Commit-Position: refs/heads/main@{#1433833}
This commit is contained in:
Nick Birnie 2025-03-17 15:57:39 -07:00 committed by Chromium LUCI CQ
parent 27da9ec0ea
commit 5aed270e6d
9 changed files with 138 additions and 12 deletions
chrome
browser
test/data/webui/glic/test_client
tools/metrics/histograms/metadata/glic

@ -185,6 +185,7 @@ struct WebClientInitialState {
bool microphone_permission_enabled;
bool location_permission_enabled;
bool tab_context_permission_enabled;
bool os_location_permission_enabled;
mojo_base.mojom.Version chrome_version;
// Whether the glic panel can currently be attached to a browser window.
bool can_attach;
@ -413,6 +414,9 @@ interface WebClientHandler {
// Attempts to open the OS permission settings page. No return value.
OpenOsPermissionSettingsMenu(content_settings.mojom.ContentSettingsType type);
// Get the status of the OS Microphone permission currently granted to Chrome.
GetOsMicrophonePermissionStatus() => (bool enabled);
};
// Data sent from the browser to the web client with panel opening information.
@ -547,6 +551,10 @@ interface WebClient {
// client or by user action in the glic settings.
NotifyTabContextPermissionStateChanged(bool enabled);
// The OS Location permission granted to Chrome has changed by user action in
// the OS settings menu.
NotifyOsLocationPermissionStateChanged(bool enabled);
// The focused tab has changed.
NotifyFocusedTabChanged(FocusedTabData focused_tab_data);
};

@ -234,6 +234,11 @@ class GlicWebClientHandler : public glic::mojom::WebClientHandler,
browser_attach_observation_ = ObserveBrowserForAttachment(profile_, this);
system_permission_settings_observation_ =
system_permission_settings::Observe(base::BindRepeating(
&GlicWebClientHandler::OnOsPermissionSettingChanged,
base::Unretained(this)));
auto state = glic::mojom::WebClientInitialState::New();
state->chrome_version = version_info::GetVersion();
state->microphone_permission_enabled =
@ -242,6 +247,8 @@ class GlicWebClientHandler : public glic::mojom::WebClientHandler,
pref_service_->GetBoolean(prefs::kGlicGeolocationEnabled);
state->tab_context_permission_enabled =
pref_service_->GetBoolean(prefs::kGlicTabContextEnabled);
state->os_location_permission_enabled =
system_permission_settings::IsAllowed(ContentSettingsType::GEOLOCATION);
state->panel_state =
glic_service_->window_controller().GetPanelState().Clone();
@ -463,13 +470,22 @@ class GlicWebClientHandler : public glic::mojom::WebClientHandler,
}
void OpenOsPermissionSettingsMenu(ContentSettingsType type) override {
if (type == ContentSettingsType::MEDIASTREAM_MIC ||
type == ContentSettingsType::GEOLOCATION) {
system_permission_settings::OpenSystemSettings(
page_handler_->webui_contents(), type);
} else {
NOTIMPLEMENTED();
if (type != ContentSettingsType::MEDIASTREAM_MIC &&
type != ContentSettingsType::GEOLOCATION) {
// This will terminate the render process.
mojo::ReportBadMessage(
"OpenOsPermissionSettingsMenu received for unsupported "
"OS permission.");
return;
}
system_permission_settings::OpenSystemSettings(
page_handler_->webui_contents(), type);
}
void GetOsMicrophonePermissionStatus(
GetOsMicrophonePermissionStatusCallback callback) override {
std::move(callback).Run(system_permission_settings::IsAllowed(
ContentSettingsType::MEDIASTREAM_MIC));
}
// GlicWindowController::StateObserver implementation.
@ -509,6 +525,14 @@ class GlicWebClientHandler : public glic::mojom::WebClientHandler,
}
}
void OnOsPermissionSettingChanged(ContentSettingsType content_type,
bool is_blocked) {
// Ignore other content types.
if (content_type == ContentSettingsType::GEOLOCATION) {
web_client_->NotifyOsLocationPermissionStateChanged(!is_blocked);
}
}
private:
void Uninstall() {
SetAudioDucking(false, base::DoNothing());
@ -566,6 +590,8 @@ class GlicWebClientHandler : public glic::mojom::WebClientHandler,
mojo::Remote<glic::mojom::WebClient> web_client_;
std::unique_ptr<BrowserAttachObservation> browser_attach_observation_;
std::unique_ptr<GlicAnnotationManager> annotation_manager_;
std::unique_ptr<system_permission_settings::ScopedObservation>
system_permission_settings_observation_;
};
GlicPageHandler::GlicPageHandler(

@ -313,6 +313,9 @@ export declare interface GlicBrowserHost {
/** Returns the state of the tab context permission. */
getTabContextPermissionState?(): ObservableValue<boolean>;
/** Returns the state of the OS granted location permission. */
getOsLocationPermissionState?(): ObservableValue<boolean>;
/**
* Set the state of the microphone permission in settings. Returns a promise
* that resolves when the browser has stored the new pref value.
@ -393,6 +396,11 @@ export declare interface GlicBrowserHost {
* Supports `media` for microphone ad `geolocation` for location.
*/
openOsPermissionSettingsMenu?(permission: string): void;
/**
* Get the status of the OS Microphone permission currently granted to Chrome.
*/
getOsMicrophonePermissionStatus?(): Promise<boolean>;
}
/** Holds optional parameters for `GlicBrowserHost#resizeWindow`. */

@ -119,6 +119,12 @@ class WebClientMessageHandler implements WebClientMessageHandlerInterface {
this.host.getTabContextPermissionState().assignAndSignal(payload.enabled);
}
glicWebClientNotifyOsLocationPermissionStateChanged(payload: {
enabled: boolean,
}) {
this.host.getOsLocationPermissionState().assignAndSignal(payload.enabled);
}
glicWebClientNotifyFocusedTabChanged(payload: {
focusedTabDataPrivate: FocusedTabDataPrivate,
}) {
@ -155,6 +161,8 @@ class GlicBrowserHostImpl implements GlicBrowserHost {
private permissionStateLocation = ObservableValueImpl.withNoValue<boolean>();
private permissionStateTabContext =
ObservableValueImpl.withNoValue<boolean>();
private permissionStateOsLocation =
ObservableValueImpl.withNoValue<boolean>();
panelActiveValue = ObservableValueImpl.withNoValue<boolean>();
private fitWindow = false;
private metrics: GlicBrowserHostMetricsImpl;
@ -202,6 +210,8 @@ class GlicBrowserHostImpl implements GlicBrowserHost {
state.locationPermissionEnabled);
this.permissionStateTabContext.assignAndSignal(
state.tabContextPermissionEnabled);
this.permissionStateOsLocation.assignAndSignal(
state.osLocationPermissionEnabled);
this.canAttachPanelValue.assignAndSignal(state.canAttach);
this.chromeVersion = state.chromeVersion;
this.panelActiveValue.assignAndSignal(state.panelIsActive);
@ -356,6 +366,10 @@ class GlicBrowserHostImpl implements GlicBrowserHost {
return this.permissionStateTabContext;
}
getOsLocationPermissionState(): ObservableValueImpl<boolean> {
return this.permissionStateOsLocation;
}
setMicrophonePermissionState(enabled: boolean): Promise<void> {
return this.sender.requestWithResponse(
'glicBrowserSetMicrophonePermissionState', {enabled});
@ -417,6 +431,12 @@ class GlicBrowserHostImpl implements GlicBrowserHost {
this.sender.requestNoResponse(
'glicBrowserOpenOsPermissionSettingsMenu', {permission});
}
async getOsMicrophonePermissionStatus(): Promise<boolean> {
return (await this.sender.requestWithResponse(
'glicBrowserGetOsMicrophonePermissionStatus', undefined))
.enabled;
}
}
class GlicBrowserHostMetricsImpl implements GlicBrowserHostMetrics {

@ -145,6 +145,13 @@ class WebClientImpl implements WebClientInterface {
});
}
notifyOsLocationPermissionStateChanged(enabled: boolean): void {
this.sender.requestNoResponse(
'glicWebClientNotifyOsLocationPermissionStateChanged', {
enabled: enabled,
});
}
notifyFocusedTabChanged(focusedTabData: (FocusedTabDataMojo)): void {
const extras = new ResponseExtras();
this.sender.requestNoResponse(
@ -459,6 +466,9 @@ class HostMessageHandler implements HostMessageHandlerInterface {
}
glicBrowserOpenOsPermissionSettingsMenu(request: {permission: string}) {
// Warning: calling openOsPermissionSettingsMenu with unsupported content
// setting type will terminate the render process (bad mojo message). Update
// GlicWebClientHandler:OpenOsPermissionSettingsMenu with any new types.
switch (request.permission) {
case 'media':
return this.handler.openOsPermissionSettingsMenu(
@ -469,6 +479,10 @@ class HostMessageHandler implements HostMessageHandlerInterface {
}
return Promise.resolve();
}
glicBrowserGetOsMicrophonePermissionStatus(): Promise<{enabled: boolean}> {
return this.handler.getOsMicrophonePermissionStatus();
}
}
class OneShotTimer {

@ -165,6 +165,11 @@ export declare interface HostRequestTypes {
},
};
glicBrowserOpenOsPermissionSettingsMenu: {request: {permission: string}};
glicBrowserGetOsMicrophonePermissionStatus: {
response: {
enabled: boolean,
},
};
}
// Types of requests to the GlicWebClient.
@ -211,6 +216,11 @@ export declare interface WebClientRequestTypes {
enabled: boolean,
},
};
glicWebClientNotifyOsLocationPermissionStateChanged: {
request: {
enabled: boolean,
},
};
glicWebClientNotifyFocusedTabChanged: {
request: {
focusedTabDataPrivate: FocusedTabDataPrivate,
@ -265,6 +275,7 @@ type HostRequestEnumNamesType = {
ScrollTo: 0,
SetSyntheticExperimentState: 0,
OpenOsPermissionSettingsMenu: 0,
GetOsMicrophonePermissionStatus: 0,
};
return apiRequestTypes;
// LINT.ThenChange(//tools/metrics/histograms/metadata/glic/histograms.xml:ApiRequestType)

@ -151,8 +151,16 @@ found in the LICENSE file.
<label for="tabContextSwitch">Tab Context:</label>
<input type="checkbox" id="tabContextSwitch" disabled />
</div>
<div class="permission-switch">
<label for="osGeolocationPermissionSwitch">OS Geolocation:</label>
<input type="checkbox" id="osGeolocationPermissionSwitch" disabled />
</div>
</div>
<div>
<button id="getOsMicrophonePermissionButton">Get OS Microphone Permission</button>
<span id="osMicrophonePermissionResult"></span>
</div>
<div>
<select id="permissionSelect">
<option value="geolocation">Geolocation</option>
<option value="microphone">Microphone</option>
@ -165,6 +173,8 @@ found in the LICENSE file.
<button id="testPermissionSwitch">Simulate External Permission
Update</button>
<button id="openSettings">Open Glic Settings</button>
<button id="openOsLocationSettings">Open OS Location Settings</button>
<button id="openOsMicrophoneSettings">Open OS Microphone Settings</button>
</div>
</div>
<div class="section">
@ -253,9 +263,12 @@ found in the LICENSE file.
<button id="getlocation">Get Location</button> <br />
<div id="location"></div>
<div id="locationStatus"></div>
<div id="locationErrorUI" style="display: none;">
<div id="locationOsErrorUI" style="display: none;">
<button id="openOsLocationSettingsButton">Open OS Settings</button>
</div>
<div id="locationGlicErrorUI" style="display: none;">
<button id="openGlicLocationSettingsButton">Open Glic Settings</button>
</div>
</div>
<div class="section">
<h1>Test Link Out Behavior</h1>

@ -48,8 +48,12 @@ interface PageElementTypes {
getlocation: HTMLButtonElement;
location: HTMLElement;
locationStatus: HTMLDivElement;
locationErrorUI: HTMLDivElement;
locationOsErrorUI: HTMLDivElement;
locationGlicErrorUI: HTMLDivElement;
openOsLocationSettingsButton: HTMLButtonElement;
openOsMicrophoneSettings: HTMLButtonElement;
openOsLocationSettings: HTMLButtonElement;
openGlicLocationSettingsButton: HTMLButtonElement;
permissionSelect: HTMLSelectElement;
enabledSelect: HTMLSelectElement;
closebn: HTMLButtonElement;
@ -97,6 +101,9 @@ interface PageElementTypes {
osDenialUI: HTMLDivElement;
openLocalSettingsButton: HTMLButtonElement;
openOsSettingsButton: HTMLButtonElement;
osGeolocationPermissionSwitch: HTMLInputElement;
getOsMicrophonePermissionButton: HTMLButtonElement;
osMicrophonePermissionResult: HTMLSpanElement;
}
const $: PageElementTypes = new Proxy({}, {
@ -151,6 +158,7 @@ class WebClient implements GlicWebClient {
microphone: this.browser.getMicrophonePermissionState!(),
geolocation: this.browser.getLocationPermissionState!(),
tabContext: this.browser.getTabContextPermissionState!(),
osGeolocation: this.browser.getOsLocationPermissionState!(),
};
for (const permission of Object.keys(permissionStates) as
PermissionSwitchName[]) {
@ -350,11 +358,13 @@ async function updateSizingMode(inSizingTest: boolean) {
// Permissions:
type PermissionSwitchName = 'microphone'|'geolocation'|'tabContext';
type PermissionSwitchName =
'microphone'|'geolocation'|'tabContext'|'osGeolocation';
const permissionSwitches: Record<PermissionSwitchName, HTMLInputElement> = {
microphone: $.microphoneSwitch,
geolocation: $.geolocationSwitch,
tabContext: $.tabContextSwitch,
osGeolocation: $.osGeolocationPermissionSwitch,
};
// Update a permission switch display state.
@ -545,9 +555,10 @@ $.getlocation.addEventListener('click', async () => {
if (error instanceof GeolocationPositionError) {
if (error.code === 1) {
$.locationStatus.innerText = `Permission Denied.`;
const locPermissionStatus = permissionSwitches['geolocation'].checked;
if (locPermissionStatus) {
$.locationErrorUI.style.display = 'block';
if (!permissionSwitches['osGeolocation'].checked) {
$.locationOsErrorUI.style.display = 'block';
} else if (!permissionSwitches['geolocation'].checked) {
$.locationGlicErrorUI.style.display = 'block';
}
}
}
@ -815,6 +826,20 @@ window.addEventListener('load', () => {
$.openOsLocationSettingsButton.addEventListener('click', () => {
getBrowser()!.openOsPermissionSettingsMenu!('geolocation');
});
$.openOsLocationSettings.addEventListener('click', () => {
getBrowser()!.openOsPermissionSettingsMenu!('geolocation');
});
$.openOsMicrophoneSettings.addEventListener('click', () => {
getBrowser()!.openOsPermissionSettingsMenu!('media');
});
$.openGlicLocationSettingsButton.addEventListener('click', () => {
getBrowser()!.openGlicSettingsPage!();
});
$.getOsMicrophonePermissionButton.addEventListener('click', async () => {
const permission = await getBrowser()!.getOsMicrophonePermissionStatus!();
$.osMicrophonePermissionResult.textContent =
`OS Microphone Permission: ${permission}`;
});
});
function readStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {

@ -61,6 +61,7 @@ chromium-metrics-reviews@google.com.
<variant name="CreateTab"/>
<variant name="DetachPanel"/>
<variant name="GetContextFromFocusedTab"/>
<variant name="GetOsMicrophonePermissionStatus"/>
<variant name="GetUserProfileInfo"/>
<variant name="OnResponseRated"/>
<variant name="OnResponseStarted"/>