[CrOS Bluetooth] Sanitize Bluetooth device names
This CL updates getDeviceName for in bluetooth_utils.ts and marks it as unsafe. This is because we allow users to enter any string as the device name. We make sure to use loadTimeData.getStringF when displaying the device name in HTML so the HTML is not rendered. Fixed: b/298724102 Test: Deployed to DUT, added test and ran CQ Change-Id: Icddf7ad76fff13bcf4511f118a6500b0009e275f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5238635 Commit-Queue: Theo Johnson-kanu <tjohnsonkanu@google.com> Reviewed-by: Chad Duffin <chadduffin@chromium.org> Cr-Commit-Position: refs/heads/main@{#1257566}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
e027c63ebf
commit
473d45cf37
ash/webui/common/resources/bluetooth
chrome
browser
resources
ash
settings
os_bluetooth_page
os_bluetooth_change_device_name_dialog.tsos_bluetooth_device_detail_subpage.htmlos_bluetooth_device_detail_subpage.tsos_bluetooth_forget_device_dialog.tsos_bluetooth_summary.tsos_paired_bluetooth_list_item.htmlos_paired_bluetooth_list_item.tsos_saved_devices_list_item.htmlos_saved_devices_list_item.ts
os_settings_menu
test
data
webui
settings
@@ -7,16 +7,20 @@ import {BluetoothDeviceProperties, PairedBluetoothDeviceProperties} from 'chrome
|
|||||||
|
|
||||||
import {BatteryType} from './bluetooth_types.js';
|
import {BatteryType} from './bluetooth_types.js';
|
||||||
|
|
||||||
export function getDeviceName(device: PairedBluetoothDeviceProperties | null): string {
|
|
||||||
if (!device) {
|
/**
|
||||||
|
* WARNING: The returned string may contain malicious HTML and should not be
|
||||||
|
* used for Polymer bindings in CSS code. For additional information see
|
||||||
|
* b/298724102.
|
||||||
|
*/
|
||||||
|
export function getDeviceNameUnsafe(device: PairedBluetoothDeviceProperties|
|
||||||
|
null): string {
|
||||||
|
if (!device || (!device.nickname && !device.deviceProperties?.publicName)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device.nickname) {
|
return device.nickname ||
|
||||||
return device.nickname;
|
mojoString16ToString(device.deviceProperties.publicName);
|
||||||
}
|
|
||||||
|
|
||||||
return mojoString16ToString(device.deviceProperties.publicName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
15
chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_change_device_name_dialog.ts
15
chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_change_device_name_dialog.ts
@@ -11,7 +11,7 @@ import '../settings_shared.css.js';
|
|||||||
import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
|
import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
|
||||||
import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
||||||
|
|
||||||
import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
||||||
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
||||||
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
||||||
@@ -52,6 +52,11 @@ export class SettingsBluetoothChangeDeviceNameDialogElement extends
|
|||||||
value: MAX_INPUT_LENGTH,
|
value: MAX_INPUT_LENGTH,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WARNING: This string may contain malicious HTML and should not be used
|
||||||
|
* for Polymer bindings in CSS code. For additional information see
|
||||||
|
* b/298724102.
|
||||||
|
*/
|
||||||
deviceName_: {
|
deviceName_: {
|
||||||
type: String,
|
type: String,
|
||||||
value: '',
|
value: '',
|
||||||
@@ -71,7 +76,7 @@ export class SettingsBluetoothChangeDeviceNameDialogElement extends
|
|||||||
private isInputInvalid_: boolean;
|
private isInputInvalid_: boolean;
|
||||||
|
|
||||||
private onDeviceChanged_(): void {
|
private onDeviceChanged_(): void {
|
||||||
this.deviceName_ = getDeviceName(this.device);
|
this.deviceName_ = getDeviceNameUnsafe(this.device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCancelClick_(): void {
|
private onCancelClick_(): void {
|
||||||
@@ -118,7 +123,7 @@ export class SettingsBluetoothChangeDeviceNameDialogElement extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isDoneDisabled_(): boolean {
|
private isDoneDisabled_(): boolean {
|
||||||
if (this.deviceName_ === getDeviceName(this.device)) {
|
if (this.deviceName_ === getDeviceNameUnsafe(this.device)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +133,10 @@ export class SettingsBluetoothChangeDeviceNameDialogElement extends
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNameForTest(): string|null {
|
||||||
|
return getDeviceNameUnsafe(this.device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -104,7 +104,7 @@
|
|||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
$i18n{bluetoothDeviceDetailName}
|
$i18n{bluetoothDeviceDetailName}
|
||||||
<div class="secondary" id="bluetoothDeviceNameLabel">
|
<div class="secondary" id="bluetoothDeviceNameLabel">
|
||||||
[[getDeviceName_(device_.*)]]
|
[[getDeviceNameUnsafe_(device_.*)]]
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<cr-button id="changeNameBtn"
|
<cr-button id="changeNameBtn"
|
||||||
|
@@ -17,7 +17,7 @@ import 'chrome://resources/ash/common/bluetooth/bluetooth_device_battery_info.js
|
|||||||
|
|
||||||
import {BluetoothUiSurface, recordBluetoothUiSurfaceMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
|
import {BluetoothUiSurface, recordBluetoothUiSurfaceMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
|
||||||
import {BatteryType} from 'chrome://resources/ash/common/bluetooth/bluetooth_types.js';
|
import {BatteryType} from 'chrome://resources/ash/common/bluetooth/bluetooth_types.js';
|
||||||
import {getBatteryPercentage, getDeviceName, hasAnyDetailedBatteryInfo, hasDefaultImage, hasTrueWirelessImages} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getBatteryPercentage, getDeviceNameUnsafe, hasAnyDetailedBatteryInfo, hasDefaultImage, hasTrueWirelessImages} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
||||||
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
||||||
import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
|
import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
|
||||||
@@ -211,11 +211,8 @@ export class SettingsBluetoothDeviceDetailSubpageElement extends
|
|||||||
this.i18n('bluetoothDeviceDetailDisconnected');
|
this.i18n('bluetoothDeviceDetailDisconnected');
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDeviceName_(): string {
|
private getDeviceNameUnsafe_(): string {
|
||||||
if (!this.device_) {
|
return getDeviceNameUnsafe(this.device_);
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return getDeviceName(this.device_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowConnectDisconnectBtn_(): boolean {
|
private shouldShowConnectDisconnectBtn_(): boolean {
|
||||||
@@ -235,7 +232,7 @@ export class SettingsBluetoothDeviceDetailSubpageElement extends
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(this.parentNode as OsSettingsSubpageElement).pageTitle =
|
(this.parentNode as OsSettingsSubpageElement).pageTitle =
|
||||||
getDeviceName(this.device_);
|
getDeviceNameUnsafe(this.device_);
|
||||||
|
|
||||||
// Special case a where user is still on detail page and has
|
// Special case a where user is still on detail page and has
|
||||||
// tried to connect to device but failed. The current |pageState_|
|
// tried to connect to device but failed. The current |pageState_|
|
||||||
@@ -299,9 +296,9 @@ export class SettingsBluetoothDeviceDetailSubpageElement extends
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDeviceDetailChangeDeviceNameBtnA11yLabel',
|
'bluetoothDeviceDetailChangeDeviceNameBtnA11yLabel',
|
||||||
this.getDeviceName_());
|
getDeviceNameUnsafe(this.device_));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMultipleBatteryInfoA11yLabel_(): string {
|
private getMultipleBatteryInfoA11yLabel_(): string {
|
||||||
@@ -363,20 +360,22 @@ export class SettingsBluetoothDeviceDetailSubpageElement extends
|
|||||||
|
|
||||||
switch (this.pageState_) {
|
switch (this.pageState_) {
|
||||||
case PageState.CONNECTING:
|
case PageState.CONNECTING:
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDeviceDetailConnectingA11yLabel', this.getDeviceName_());
|
'bluetoothDeviceDetailConnectingA11yLabel',
|
||||||
|
getDeviceNameUnsafe(this.device_));
|
||||||
case PageState.CONNECTED:
|
case PageState.CONNECTED:
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDeviceDetailConnectedA11yLabel', this.getDeviceName_());
|
'bluetoothDeviceDetailConnectedA11yLabel',
|
||||||
|
getDeviceNameUnsafe(this.device_));
|
||||||
case PageState.CONNECTION_FAILED:
|
case PageState.CONNECTION_FAILED:
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDeviceDetailConnectionFailureA11yLabel',
|
'bluetoothDeviceDetailConnectionFailureA11yLabel',
|
||||||
this.getDeviceName_());
|
getDeviceNameUnsafe(this.device_));
|
||||||
case PageState.DISCONNECTED:
|
case PageState.DISCONNECTED:
|
||||||
case PageState.DISCONNECTING:
|
case PageState.DISCONNECTING:
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDeviceDetailDisconnectedA11yLabel',
|
'bluetoothDeviceDetailDisconnectedA11yLabel',
|
||||||
this.getDeviceName_());
|
getDeviceNameUnsafe(this.device_));
|
||||||
default:
|
default:
|
||||||
assertNotReached();
|
assertNotReached();
|
||||||
}
|
}
|
||||||
@@ -525,8 +524,9 @@ export class SettingsBluetoothDeviceDetailSubpageElement extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getForgetA11yLabel_(): string {
|
private getForgetA11yLabel_(): string {
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDeviceDetailForgetA11yLabel', this.getDeviceName_());
|
'bluetoothDeviceDetailForgetA11yLabel',
|
||||||
|
getDeviceNameUnsafe(this.device_));
|
||||||
}
|
}
|
||||||
|
|
||||||
private onForgetButtonClicked_(): void {
|
private onForgetButtonClicked_(): void {
|
||||||
|
@@ -10,7 +10,7 @@ import '../settings_shared.css.js';
|
|||||||
import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
|
import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
|
||||||
import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
||||||
|
|
||||||
import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
|
||||||
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
||||||
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||||
@@ -45,15 +45,11 @@ class SettingsBluetoothForgetDeviceDialogElement extends
|
|||||||
private device_: PairedBluetoothDeviceProperties;
|
private device_: PairedBluetoothDeviceProperties;
|
||||||
|
|
||||||
private getForgetDeviceDialogBodyText_(): string {
|
private getForgetDeviceDialogBodyText_(): string {
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'bluetoothDevicesDialogLabel', this.getDeviceName_(),
|
'bluetoothDevicesDialogLabel', getDeviceNameUnsafe(this.device_),
|
||||||
loadTimeData.getString('primaryUserEmail'));
|
loadTimeData.getString('primaryUserEmail'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDeviceName_(): string {
|
|
||||||
return getDeviceName(this.device_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onForgetClick_(event: Event): void {
|
private onForgetClick_(event: Event): void {
|
||||||
const fireEvent = new CustomEvent(
|
const fireEvent = new CustomEvent(
|
||||||
'forget-bluetooth-device', {bubbles: true, composed: true});
|
'forget-bluetooth-device', {bubbles: true, composed: true});
|
||||||
|
@@ -12,7 +12,7 @@ import '../settings_shared.css.js';
|
|||||||
import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
|
import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
|
||||||
import 'chrome://resources/ash/common/cr_elements/icons.html.js';
|
import 'chrome://resources/ash/common/cr_elements/icons.html.js';
|
||||||
|
|
||||||
import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
||||||
import {getInstance as getAnnouncerInstance} from 'chrome://resources/ash/common/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
|
import {getInstance as getAnnouncerInstance} from 'chrome://resources/ash/common/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
|
||||||
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
||||||
@@ -175,31 +175,32 @@ export class SettingsBluetoothSummaryElement extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isA11yLabel = labelType === LabelType.A11Y;
|
const isA11yLabel = labelType === LabelType.A11Y;
|
||||||
const firstConnectedDeviceName = getDeviceName(connectedDevices[0]);
|
const firstConnectedDeviceName = getDeviceNameUnsafe(connectedDevices[0]);
|
||||||
|
|
||||||
if (connectedDevices.length === 1) {
|
if (connectedDevices.length === 1) {
|
||||||
return isA11yLabel ? this.i18n(
|
return isA11yLabel ? loadTimeData.getStringF(
|
||||||
'bluetoothSummaryPageConnectedA11yOneDevice',
|
'bluetoothSummaryPageConnectedA11yOneDevice',
|
||||||
firstConnectedDeviceName) :
|
firstConnectedDeviceName) :
|
||||||
firstConnectedDeviceName;
|
firstConnectedDeviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectedDevices.length === 2) {
|
if (connectedDevices.length === 2) {
|
||||||
const secondConnectedDeviceName = getDeviceName(connectedDevices[1]);
|
const secondConnectedDeviceName =
|
||||||
|
getDeviceNameUnsafe(connectedDevices[1]);
|
||||||
return isA11yLabel ?
|
return isA11yLabel ?
|
||||||
this.i18n(
|
loadTimeData.getStringF(
|
||||||
'bluetoothSummaryPageConnectedA11yTwoDevices',
|
'bluetoothSummaryPageConnectedA11yTwoDevices',
|
||||||
firstConnectedDeviceName, secondConnectedDeviceName) :
|
firstConnectedDeviceName, secondConnectedDeviceName) :
|
||||||
this.i18n(
|
loadTimeData.getStringF(
|
||||||
'bluetoothSummaryPageTwoDevicesDescription',
|
'bluetoothSummaryPageTwoDevicesDescription',
|
||||||
firstConnectedDeviceName, secondConnectedDeviceName);
|
firstConnectedDeviceName, secondConnectedDeviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isA11yLabel ?
|
return isA11yLabel ?
|
||||||
this.i18n(
|
loadTimeData.getStringF(
|
||||||
'bluetoothSummaryPageConnectedA11yTwoOrMoreDevices',
|
'bluetoothSummaryPageConnectedA11yTwoOrMoreDevices',
|
||||||
firstConnectedDeviceName, connectedDevices.length - 1) :
|
firstConnectedDeviceName, connectedDevices.length - 1) :
|
||||||
this.i18n(
|
loadTimeData.getStringF(
|
||||||
'bluetoothSummaryPageTwoOrMoreDevicesDescription',
|
'bluetoothSummaryPageTwoOrMoreDevicesDescription',
|
||||||
firstConnectedDeviceName, connectedDevices.length - 1);
|
firstConnectedDeviceName, connectedDevices.length - 1);
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
on-click="onSelected_">
|
on-click="onSelected_">
|
||||||
<bluetooth-icon device="[[device.deviceProperties]]"></bluetooth-icon>
|
<bluetooth-icon device="[[device.deviceProperties]]"></bluetooth-icon>
|
||||||
<div class="middle" aria-hidden="true">
|
<div class="middle" aria-hidden="true">
|
||||||
<div id="deviceName">[[getDeviceName_(device)]]</div>
|
<div id="deviceName">[[getDeviceNameUnsafe_(device)]]</div>
|
||||||
<template is="dom-if"
|
<template is="dom-if"
|
||||||
if="[[shouldShowBatteryInfo_(device)]]" restamp>
|
if="[[shouldShowBatteryInfo_(device)]]" restamp>
|
||||||
<bluetooth-device-battery-info
|
<bluetooth-device-battery-info
|
||||||
|
@@ -16,10 +16,11 @@ import 'chrome://resources/ash/common/bluetooth/bluetooth_icon.js';
|
|||||||
import 'chrome://resources/ash/common/bluetooth/bluetooth_device_battery_info.js';
|
import 'chrome://resources/ash/common/bluetooth/bluetooth_device_battery_info.js';
|
||||||
|
|
||||||
import {BatteryType} from 'chrome://resources/ash/common/bluetooth/bluetooth_types.js';
|
import {BatteryType} from 'chrome://resources/ash/common/bluetooth/bluetooth_types.js';
|
||||||
import {getBatteryPercentage, getDeviceName, hasAnyDetailedBatteryInfo} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getBatteryPercentage, getDeviceNameUnsafe, hasAnyDetailedBatteryInfo} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {FocusRowMixin} from 'chrome://resources/ash/common/cr_elements/focus_row_mixin.js';
|
import {FocusRowMixin} from 'chrome://resources/ash/common/cr_elements/focus_row_mixin.js';
|
||||||
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
||||||
import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
|
import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
|
||||||
|
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||||
import {DeviceConnectionState, DeviceType, PairedBluetoothDeviceProperties} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
|
import {DeviceConnectionState, DeviceType, PairedBluetoothDeviceProperties} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
|
||||||
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||||
|
|
||||||
@@ -101,8 +102,9 @@ export class SettingsPairedBluetoothListItemElement extends
|
|||||||
Router.getInstance().navigateTo(routes.BLUETOOTH_DEVICE_DETAIL, params);
|
Router.getInstance().navigateTo(routes.BLUETOOTH_DEVICE_DETAIL, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDeviceName_(device: PairedBluetoothDeviceProperties): string {
|
private getDeviceNameUnsafe_(device: PairedBluetoothDeviceProperties):
|
||||||
return getDeviceName(device);
|
string {
|
||||||
|
return getDeviceNameUnsafe(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowBatteryInfo_(device: PairedBluetoothDeviceProperties):
|
private shouldShowBatteryInfo_(device: PairedBluetoothDeviceProperties):
|
||||||
@@ -159,9 +161,9 @@ export class SettingsPairedBluetoothListItemElement extends
|
|||||||
private getAriaLabel_(device: PairedBluetoothDeviceProperties): string {
|
private getAriaLabel_(device: PairedBluetoothDeviceProperties): string {
|
||||||
// Start with the base information of the device name and location within
|
// Start with the base information of the device name and location within
|
||||||
// the list of devices with the same connection state.
|
// the list of devices with the same connection state.
|
||||||
let a11yLabel = this.i18n(
|
let a11yLabel = loadTimeData.getStringF(
|
||||||
'bluetoothA11yDeviceName', this.itemIndex + 1, this.listSize,
|
'bluetoothA11yDeviceName', this.itemIndex + 1, this.listSize,
|
||||||
this.getDeviceName_(device));
|
this.getDeviceNameUnsafe_(device));
|
||||||
|
|
||||||
// Include the connection status.
|
// Include the connection status.
|
||||||
a11yLabel +=
|
a11yLabel +=
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="middle" aria-hidden="true">
|
<div class="middle" aria-hidden="true">
|
||||||
<div id="deviceName">[[getDeviceName_(device)]]</div>
|
<div id="deviceName">[[getDeviceNameUnsafe_(device)]]</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<cr-icon-button class="icon-more-vert"
|
<cr-icon-button class="icon-more-vert"
|
||||||
|
@@ -16,8 +16,8 @@ import 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.
|
|||||||
import {FastPairSavedDevicesUiEvent, recordSavedDevicesUiEventMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
|
import {FastPairSavedDevicesUiEvent, recordSavedDevicesUiEventMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
|
||||||
import {CrActionMenuElement} from 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
|
import {CrActionMenuElement} from 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
|
||||||
import {FocusRowMixin} from 'chrome://resources/ash/common/cr_elements/focus_row_mixin.js';
|
import {FocusRowMixin} from 'chrome://resources/ash/common/cr_elements/focus_row_mixin.js';
|
||||||
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
|
||||||
import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
|
import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
|
||||||
|
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||||
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||||
|
|
||||||
import {getTemplate} from './os_saved_devices_list_item.html.js';
|
import {getTemplate} from './os_saved_devices_list_item.html.js';
|
||||||
@@ -30,7 +30,7 @@ interface SettingsSavedDevicesListItemElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SettingsSavedDevicesListItemElementBase =
|
const SettingsSavedDevicesListItemElementBase =
|
||||||
FocusRowMixin(WebUiListenerMixin(I18nMixin(PolymerElement)));
|
FocusRowMixin(WebUiListenerMixin(PolymerElement));
|
||||||
|
|
||||||
class SettingsSavedDevicesListItemElement extends
|
class SettingsSavedDevicesListItemElement extends
|
||||||
SettingsSavedDevicesListItemElementBase {
|
SettingsSavedDevicesListItemElementBase {
|
||||||
@@ -69,7 +69,7 @@ class SettingsSavedDevicesListItemElement extends
|
|||||||
listSize: number;
|
listSize: number;
|
||||||
private shouldShowRemoveSavedDeviceDialog_: boolean;
|
private shouldShowRemoveSavedDeviceDialog_: boolean;
|
||||||
|
|
||||||
private getDeviceName_(device: FastPairSavedDevice): string {
|
private getDeviceNameUnsafe_(device: FastPairSavedDevice): string {
|
||||||
return device.name;
|
return device.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +95,16 @@ class SettingsSavedDevicesListItemElement extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getAriaLabel_(device: FastPairSavedDevice): string {
|
private getAriaLabel_(device: FastPairSavedDevice): string {
|
||||||
const deviceName = this.getDeviceName_(device);
|
const deviceName = this.getDeviceNameUnsafe_(device);
|
||||||
return this.i18n(
|
return loadTimeData.getStringF(
|
||||||
'savedDeviceItemA11yLabel', this.itemIndex + 1, this.listSize,
|
'savedDeviceItemA11yLabel', this.itemIndex + 1, this.listSize,
|
||||||
deviceName);
|
deviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSubpageButtonA11yLabel_(device: FastPairSavedDevice): string {
|
private getSubpageButtonA11yLabel_(device: FastPairSavedDevice): string {
|
||||||
const deviceName = this.getDeviceName_(device);
|
const deviceName = this.getDeviceNameUnsafe_(device);
|
||||||
return this.i18n('savedDeviceItemButtonA11yLabel', deviceName);
|
return loadTimeData.getStringF(
|
||||||
|
'savedDeviceItemButtonA11yLabel', deviceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,13 +15,13 @@ import '../settings_shared.css.js';
|
|||||||
import '../os_settings_icons.html.js';
|
import '../os_settings_icons.html.js';
|
||||||
import './menu_item.js';
|
import './menu_item.js';
|
||||||
|
|
||||||
import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
||||||
|
import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
||||||
|
import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
|
||||||
import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
|
import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
|
||||||
import {NetworkListenerBehavior, NetworkListenerBehaviorInterface} from 'chrome://resources/ash/common/network/network_listener_behavior.js';
|
import {NetworkListenerBehavior, NetworkListenerBehaviorInterface} from 'chrome://resources/ash/common/network/network_listener_behavior.js';
|
||||||
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
|
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
|
||||||
import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
|
|
||||||
import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
|
|
||||||
import {BluetoothSystemProperties, BluetoothSystemState, DeviceConnectionState, PairedBluetoothDeviceProperties, SystemPropertiesObserverReceiver as BluetoothPropertiesObserverReceiver} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
|
import {BluetoothSystemProperties, BluetoothSystemState, DeviceConnectionState, PairedBluetoothDeviceProperties, SystemPropertiesObserverReceiver as BluetoothPropertiesObserverReceiver} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
|
||||||
import {CrosNetworkConfigInterface, FilterType, NO_LIMIT} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
|
import {CrosNetworkConfigInterface, FilterType, NO_LIMIT} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
|
||||||
import {NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
|
import {NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
|
||||||
@@ -664,7 +664,7 @@ export class OsSettingsMenuElement extends OsSettingsMenuElementBase {
|
|||||||
|
|
||||||
if (connectedDevices.length === 1) {
|
if (connectedDevices.length === 1) {
|
||||||
const device = castExists(connectedDevices[0]);
|
const device = castExists(connectedDevices[0]);
|
||||||
this.bluetoothMenuItemDescription_ = getDeviceName(device);
|
this.bluetoothMenuItemDescription_ = getDeviceNameUnsafe(device);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ import 'chrome://os-settings/lazy_load.js';
|
|||||||
|
|
||||||
import {SettingsBluetoothChangeDeviceNameDialogElement} from 'chrome://os-settings/lazy_load.js';
|
import {SettingsBluetoothChangeDeviceNameDialogElement} from 'chrome://os-settings/lazy_load.js';
|
||||||
import {CrInputElement} from 'chrome://os-settings/os_settings.js';
|
import {CrInputElement} from 'chrome://os-settings/os_settings.js';
|
||||||
import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
|
||||||
import {setBluetoothConfigForTesting} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
import {setBluetoothConfigForTesting} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
|
||||||
import {DeviceConnectionState} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
|
import {DeviceConnectionState} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
|
||||||
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||||
@@ -18,6 +18,7 @@ suite('<os-settings-bluetooth-change-device-name-dialog>', () => {
|
|||||||
let bluetoothDeviceChangeNameDialog:
|
let bluetoothDeviceChangeNameDialog:
|
||||||
SettingsBluetoothChangeDeviceNameDialogElement;
|
SettingsBluetoothChangeDeviceNameDialogElement;
|
||||||
let bluetoothConfig: FakeBluetoothConfig;
|
let bluetoothConfig: FakeBluetoothConfig;
|
||||||
|
const deviceId = '12//345&6789';
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
bluetoothConfig = new FakeBluetoothConfig();
|
bluetoothConfig = new FakeBluetoothConfig();
|
||||||
@@ -61,7 +62,7 @@ suite('<os-settings-bluetooth-change-device-name-dialog>', () => {
|
|||||||
|
|
||||||
test('Input is sanitized', async () => {
|
test('Input is sanitized', async () => {
|
||||||
const device1 = createDefaultBluetoothDevice(
|
const device1 = createDefaultBluetoothDevice(
|
||||||
/*id=*/ '12//345&6789',
|
/*id=*/ deviceId,
|
||||||
/*publicName=*/ 'BeatsX',
|
/*publicName=*/ 'BeatsX',
|
||||||
/*connectionState=*/
|
/*connectionState=*/
|
||||||
DeviceConnectionState.kConnected,
|
DeviceConnectionState.kConnected,
|
||||||
@@ -109,8 +110,10 @@ suite('<os-settings-bluetooth-change-device-name-dialog>', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Device name is changed', async () => {
|
test('Device name is changed', async () => {
|
||||||
const id = '12//345&6789';
|
const initialNickname = 'device1';
|
||||||
const nickname = 'Nickname';
|
const newNickname = 'nickname';
|
||||||
|
const htmlNickname = '<a>html</a>';
|
||||||
|
|
||||||
const getDoneBtn = () => {
|
const getDoneBtn = () => {
|
||||||
const doneButton = bluetoothDeviceChangeNameDialog.shadowRoot!
|
const doneButton = bluetoothDeviceChangeNameDialog.shadowRoot!
|
||||||
.querySelector<HTMLButtonElement>('#done');
|
.querySelector<HTMLButtonElement>('#done');
|
||||||
@@ -119,11 +122,11 @@ suite('<os-settings-bluetooth-change-device-name-dialog>', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const device = createDefaultBluetoothDevice(
|
const device = createDefaultBluetoothDevice(
|
||||||
id,
|
deviceId,
|
||||||
/*publicName=*/ 'BeatsX',
|
/*publicName=*/ 'BeatsX',
|
||||||
/*connectionState=*/
|
/*connectionState=*/
|
||||||
DeviceConnectionState.kConnected,
|
DeviceConnectionState.kConnected,
|
||||||
/*opt_nickname=*/ 'device1');
|
/*opt_nickname=*/ initialNickname);
|
||||||
|
|
||||||
bluetoothDeviceChangeNameDialog.set('device', {...device});
|
bluetoothDeviceChangeNameDialog.set('device', {...device});
|
||||||
bluetoothConfig.appendToPairedDeviceList([device]);
|
bluetoothConfig.appendToPairedDeviceList([device]);
|
||||||
@@ -132,18 +135,27 @@ suite('<os-settings-bluetooth-change-device-name-dialog>', () => {
|
|||||||
const input = bluetoothDeviceChangeNameDialog.shadowRoot!
|
const input = bluetoothDeviceChangeNameDialog.shadowRoot!
|
||||||
.querySelector<CrInputElement>('#changeNameInput');
|
.querySelector<CrInputElement>('#changeNameInput');
|
||||||
assertTrue(!!input);
|
assertTrue(!!input);
|
||||||
assertEquals('device1', input.value);
|
assertEquals(initialNickname, input.value);
|
||||||
assertTrue(getDoneBtn().disabled);
|
assertTrue(getDoneBtn().disabled);
|
||||||
|
|
||||||
input.value = nickname;
|
input.value = newNickname;
|
||||||
await flushTasks();
|
await flushTasks();
|
||||||
assertFalse(getDoneBtn().disabled);
|
assertFalse(getDoneBtn().disabled);
|
||||||
|
|
||||||
getDoneBtn().click();
|
getDoneBtn().click();
|
||||||
await flushTasks();
|
await flushTasks();
|
||||||
|
assertEquals(
|
||||||
|
newNickname,
|
||||||
|
getDeviceNameUnsafe(bluetoothConfig.getPairedDeviceById(deviceId)));
|
||||||
|
|
||||||
const newName = getDeviceName(bluetoothConfig.getPairedDeviceById(id));
|
input.value = htmlNickname;
|
||||||
|
await flushTasks();
|
||||||
|
assertFalse(getDoneBtn().disabled);
|
||||||
|
|
||||||
assertEquals(nickname, newName);
|
getDoneBtn().click();
|
||||||
|
await flushTasks();
|
||||||
|
assertEquals(
|
||||||
|
htmlNickname,
|
||||||
|
getDeviceNameUnsafe(bluetoothConfig.getPairedDeviceById(deviceId)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -195,6 +195,32 @@ suite('<os-settings-paired-bluetooth-list-item>', () => {
|
|||||||
getItemA11yLabel());
|
getItemA11yLabel());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Device name is set correctly', async () => {
|
||||||
|
const getDeviceName = () => {
|
||||||
|
const deviceName =
|
||||||
|
pairedBluetoothListItem.shadowRoot!.querySelector<HTMLElement>(
|
||||||
|
'#deviceName');
|
||||||
|
assertTrue(!!deviceName);
|
||||||
|
return deviceName;
|
||||||
|
};
|
||||||
|
|
||||||
|
const publicName = 'BeatsX';
|
||||||
|
const nameWithHtml = '<a>testname</a>';
|
||||||
|
const device = createDefaultBluetoothDevice(
|
||||||
|
/*id=*/ '123456789', /*publicName=*/ publicName,
|
||||||
|
/*connectionState=*/
|
||||||
|
DeviceConnectionState.kConnected);
|
||||||
|
pairedBluetoothListItem.set('device', device);
|
||||||
|
await flushTasks();
|
||||||
|
assertEquals(publicName, getDeviceName().innerText);
|
||||||
|
|
||||||
|
device.nickname = nameWithHtml;
|
||||||
|
pairedBluetoothListItem.set('device', {...device});
|
||||||
|
await flushTasks();
|
||||||
|
assertTrue(!!getDeviceName());
|
||||||
|
assertEquals(nameWithHtml, getDeviceName().innerText);
|
||||||
|
});
|
||||||
|
|
||||||
test('Battery percentage out of bounds', async () => {
|
test('Battery percentage out of bounds', async () => {
|
||||||
const device = createDefaultBluetoothDevice(
|
const device = createDefaultBluetoothDevice(
|
||||||
/*id=*/ '123456789', /*publicName=*/ 'BeatsX',
|
/*id=*/ '123456789', /*publicName=*/ 'BeatsX',
|
||||||
|
@@ -177,4 +177,23 @@ suite('<os-settings-saved-devices-list>', () => {
|
|||||||
assertTrue(isVisible(
|
assertTrue(isVisible(
|
||||||
getListItems()[1]!.shadowRoot!.querySelector('#deviceImage')));
|
getListItems()[1]!.shadowRoot!.querySelector('#deviceImage')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Device names are set properly', async () => {
|
||||||
|
const getDeviceName = () => {
|
||||||
|
return getListItems()[0]!.shadowRoot!
|
||||||
|
.querySelector<HTMLElement>('#deviceName')!.innerText;
|
||||||
|
};
|
||||||
|
const deviceName = 'deviceName';
|
||||||
|
const device = {name: deviceName, imageUrl: 'fakeUrl', accountKey: '1'};
|
||||||
|
|
||||||
|
savedDevicesList.set('devices', [device]);
|
||||||
|
await flushTasks();
|
||||||
|
assertEquals(deviceName, getDeviceName());
|
||||||
|
|
||||||
|
const nameWithHtml = '<a>test</a>';
|
||||||
|
device.name = nameWithHtml;
|
||||||
|
savedDevicesList.set('devices', [{...device}]);
|
||||||
|
await flushTasks();
|
||||||
|
assertEquals(nameWithHtml, getDeviceName());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user