Commit efe9d933 authored by dpapad's avatar dpapad Committed by Commit Bot

Put chrome://usb-internals UI under a top-level WebComponent.

This is done so that the UI can be programmatically instantiated
from within a test, with
document.createElement('usb-internals-app');

in preparation of migrating to JS modules, where during tests,
test_loader.html is loaded instead of the original landing HTML
page.

Bug: 1139849
Change-Id: I919179150385e906665746afbfc6ce0aa2480498
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2486963
Commit-Queue: dpapad <dpapad@chromium.org>
Reviewed-by: default avatarRebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#819490}
parent 49fdeca1
...@@ -4,19 +4,21 @@ ...@@ -4,19 +4,21 @@
import("//third_party/closure_compiler/compile_js.gni") import("//third_party/closure_compiler/compile_js.gni")
import("//tools/grit/grit_rule.gni") import("//tools/grit/grit_rule.gni")
import("//tools/polymer/html_to_js.gni")
js_type_check("closure_compile") { js_type_check("closure_compile") {
deps = [ deps = [
":app",
":descriptor_panel", ":descriptor_panel",
":devices_page", ":devices_page",
":usb_internals",
] ]
} }
js_library("usb_internals") { js_library("app") {
deps = [ deps = [
":devices_page", ":devices_page",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings_js_library_for_compile", "//chrome/browser/ui/webui/usb_internals:mojo_bindings_js_library_for_compile",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:cr", "//ui/webui/resources/js:cr",
"//ui/webui/resources/js:util", "//ui/webui/resources/js:util",
"//ui/webui/resources/js/cr/ui:tabs", "//ui/webui/resources/js/cr/ui:tabs",
...@@ -28,6 +30,7 @@ js_library("devices_page") { ...@@ -28,6 +30,7 @@ js_library("devices_page") {
deps = [ deps = [
":descriptor_panel", ":descriptor_panel",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings_js_library_for_compile", "//chrome/browser/ui/webui/usb_internals:mojo_bindings_js_library_for_compile",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:cr", "//ui/webui/resources/js:cr",
] ]
} }
...@@ -50,5 +53,12 @@ grit("resources") { ...@@ -50,5 +53,12 @@ grit("resources") {
"-E", "-E",
"root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir), "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
] ]
deps = [ "//chrome/browser/ui/webui/usb_internals:mojo_bindings_js" ] deps = [
":web_components",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings_js",
]
}
html_to_js("web_components") {
js_files = [ "app.js" ]
} }
<link rel="stylesheet" href="chrome://resources/css/tabs.css">
<link rel="stylesheet" href="chrome://resources/css/tree.css">
<link rel="stylesheet" href="usb_internals.css">
<tabbox>
<tabs>
<tab>Test Devices</tab>
<tab>Devices</tab>
</tabs>
<tabpanels>
<tabpanel>
<!-- Test Devices -->
<h2>Test Devices</h2>
<p>
<table class="styled-table">
<thead>
<tr>
<th>Name</th>
<th>Serial number</th>
<th>Landing page</th>
<th>
</tr>
</thead>
<tbody id="test-device-list">
</tbody>
<template id="test-device-row">
<tr>
<td></td>
<td></td>
<td></td>
<td><button>Remove</button></td>
</tr>
</template>
</table>
</p>
<div class="page-section">
<strong>Add a test device:</strong>
<form id="add-test-device-form" action="">
<p>
<label>
Name: <input id="test-device-name" type="text" size="40">
</label>
</p>
<p>
<label>
Serial number:
<input id="test-device-serial" type="text" size="40">
</label>
</p>
<p>
<label>
Landing page:
<input id="test-device-landing-page" type="text" size="40">
</label>
</p>
<button type="submit">Add</button>
<span id="add-test-device-result"></span>
</form>
</div>
</tabpanel>
<tabpanel>
<!-- Devices -->
<h2>Devices</h2>
<table class="styled-table">
<thead>
<tr>
<th>Bus Number</th>
<th>Port Number</th>
<th>Vendor Id</th>
<th>Product Id</th>
<th>Manufacturer Name</th>
<th>Product Name</th>
<th>Serial Number</th>
<th>
</tr>
</thead>
<tbody id="device-list"></tbody>
<template id="device-row">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><button>Inspect</button></td>
</tr>
</template>
</table>
</tabpanel>
</tabpanels>
</tabbox>
<template id="tab-template">
<tab></tab>
</template>
<template id="device-tabpanel-template">
<tabpanel>
<tree class="tree-view"></tree>
<div class="descriptor-button">
<button class="device-descriptor-button">Get Device Descriptor</button>
</div>
<div class="device-descriptor-panel" hidden></div>
<div class="descriptor-button">
<button class="configuration-descriptor-button">
Get Configuration Descriptor
</button>
</div>
<div class="configuration-descriptor-panel" hidden></div>
<div class="descriptor-button">
<button class="string-descriptor-button">Get String Descriptor</button>
</div>
<div class="string-descriptor-panel" hidden>
String Descriptor Index:
<input id="index-input" type="number" min="1" list="indexes">
<datalist id="indexes"></datalist>
Language Code:
<input id="language-code-input" list="languages">
<datalist id="languages"></datalist>
<button>GET</button>
</div>
<div class="descriptor-button">
<button class="bos-descriptor-button">
Get WebUSB & Microsoft OS 2.0 Descriptors
</button>
</div>
<div class="bos-descriptor-panel" hidden></div>
<div class="descriptor-button">
<button class="testing-tool-button">Testing Tool Panel</button>
</div>
<div class="testing-tool-panel" hidden>
<select id="input-type">
<option label="Decimal with Dropdown Menu"></option>
<option label="Hex Bytes"></option>
</select>
<table class="styled-table">
<thead>
<tr>
<th>bmRequestType</th>
<th>bRequest</th>
<th>wValue</th>
<th>wIndex</th>
<th>wLength</th>
</tr>
</thead>
<tbody id="testing-tool">
<tr>
<td>
<select id="transfer-direction">
<option label="Host-to-Device" value="Host-to-Device">
</option>
<option label="Device-to-Host" value="Device-to-Host">
</option>
</select>
<select id="transfer-type">
<option label="Standard" value="STANDARD"></option>
<option label="Class" value="CLASS"></option>
<option label="Vendor" value="VENDOR"></option>
</select>
<select id="transfer-recipient">
<option label="Device" value="DEVICE"></option>
<option label="Interface" value="INTERFACE"></option>
<option label="Endpoint" value="ENDPOINT"></option>
<option label="Other" value="OTHER"></option>
</select>
</td>
<td><input id="query-request" type="number" placeholder="0"></td>
<td><input id="query-value" type="number" placeholder="0"></td>
<td><input id="query-index" type="number" placeholder="0"></td>
<td><input id="query-length" type="number" placeholder="0"></td>
<td><button>Send</button></td>
</tr>
<tr hidden>
<td>0x<input id="query-request-type" placeholder="00"></td>
<td>0x<input id="query-request" placeholder="00"></td>
<td>0x<input id="query-value" placeholder="0000"></td>
<td>0x<input id="query-index" placeholder="0000"></td>
<td>0x<input id="query-length" placeholder="0000"></td>
<td><button>Send</button></td>
</tr>
</tbody>
</table>
<div id="data-input-area">
Data (in Hex):
<textarea cols="31"></textarea>
</div>
</div>
</tabpanel>
</template>
<template id="descriptor-panel-template">
<descriptorpanel>
<tree class="raw-data-tree-view"></tree>
<div class="raw-data-byte-view"></div>
</descriptorpanel>
</template>
<template id="raw-data-byte-container-template">
<div></div>
</template>
<template id="raw-data-byte-template">
<span></span>
</template>
<template id="raw-data-tree-button">
<button>GET</button>
</template>
<template id="descriptor-panel-title">
<descriptorpaneltitle></descriptorpaneltitle>
</template>
...@@ -5,11 +5,34 @@ ...@@ -5,11 +5,34 @@
/** /**
* Javascript for usb_internals.html, served from chrome://usb-internals/. * Javascript for usb_internals.html, served from chrome://usb-internals/.
*/ */
cr.define('usb_internals', function() {
class UsbInternals {
constructor() {}
async initializeViews() { window.setupFn = window.setupFn || function() {
return Promise.resolve();
};
class UsbInternalsAppElement extends HTMLElement {
static get template() {
return `{__html_template__}`;
}
constructor() {
super();
this.attachShadow({mode: 'open'});
const template = document.createElement('template');
template.innerHTML = this.constructor.template || '';
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
/**
* @param {string} query
* @return {?Element}
*/
$(query) {
return this.shadowRoot.querySelector(query);
}
async connectedCallback() {
// window.setupFn() provides a hook for the test suite to perform setup // window.setupFn() provides a hook for the test suite to perform setup
// actions after the page is loaded but before any script is run. // actions after the page is loaded but before any script is run.
await window.setupFn(); await window.setupFn();
...@@ -24,28 +47,29 @@ cr.define('usb_internals', function() { ...@@ -24,28 +47,29 @@ cr.define('usb_internals', function() {
usbManager.$.bindNewPipeAndPassReceiver()); usbManager.$.bindNewPipeAndPassReceiver());
/** @private {devices_page.DevicesPage} */ /** @private {devices_page.DevicesPage} */
this.devicesPage_ = new devices_page.DevicesPage(usbManager); this.devicesPage_ = new devices_page.DevicesPage(
usbManager, assert(this.shadowRoot));
/** @private {device.mojom.UsbDeviceManagerTestRemote} */ /** @private {device.mojom.UsbDeviceManagerTestRemote} */
this.usbManagerTest_ = new device.mojom.UsbDeviceManagerTestRemote; this.usbManagerTest_ = new device.mojom.UsbDeviceManagerTestRemote;
await pageHandler.bindTestInterface( await pageHandler.bindTestInterface(
this.usbManagerTest_.$.bindNewPipeAndPassReceiver()); this.usbManagerTest_.$.bindNewPipeAndPassReceiver());
$('add-test-device-form').addEventListener('submit', (event) => { this.$('#add-test-device-form').addEventListener('submit', (event) => {
this.addTestDevice(event); this.addTestDevice(event);
}); });
this.refreshTestDeviceList(); this.refreshTestDeviceList();
cr.ui.decorate('tabbox', cr.ui.TabBox); cr.ui.decorate(assert(this.$('tabbox')), cr.ui.TabBox);
} }
async refreshTestDeviceList() { async refreshTestDeviceList() {
const response = await this.usbManagerTest_.getTestDevices(); const response = await this.usbManagerTest_.getTestDevices();
const tableBody = $('test-device-list'); const tableBody = this.$('#test-device-list');
tableBody.innerHTML = trustedTypes.emptyHTML; tableBody.innerHTML = trustedTypes.emptyHTML;
const rowTemplate = document.querySelector('#test-device-row'); const rowTemplate = this.$('#test-device-row');
const td = rowTemplate.content.querySelectorAll('td'); const td = rowTemplate.content.querySelectorAll('td');
for (const device of response.devices) { for (const device of response.devices) {
...@@ -69,28 +93,16 @@ cr.define('usb_internals', function() { ...@@ -69,28 +93,16 @@ cr.define('usb_internals', function() {
event.preventDefault(); event.preventDefault();
const response = await this.usbManagerTest_.addDeviceForTesting( const response = await this.usbManagerTest_.addDeviceForTesting(
$('test-device-name').value, $('test-device-serial').value, this.$('#test-device-name').value, this.$('#test-device-serial').value,
$('test-device-landing-page').value); this.$('#test-device-landing-page').value);
if (response.success) { if (response.success) {
this.refreshTestDeviceList(); this.refreshTestDeviceList();
} }
$('add-test-device-result').textContent = response.message; this.$('#add-test-device-result').textContent = response.message;
$('add-test-device-result').className = this.$('#add-test-device-result').className =
response.success ? 'action-success' : 'action-failure'; response.success ? 'action-success' : 'action-failure';
} }
} }
customElements.define('usb-internals-app', UsbInternalsAppElement);
return {
UsbInternals,
};
});
window.setupFn = window.setupFn || function() {
return Promise.resolve();
};
document.addEventListener('DOMContentLoaded', () => {
const usbInternalsPage = new usb_internals.UsbInternals();
usbInternalsPage.initializeViews();
});
...@@ -166,7 +166,9 @@ cr.define('descriptor_panel', function() { ...@@ -166,7 +166,9 @@ cr.define('descriptor_panel', function() {
this.stringDescriptorPanel_.stringDescriptorIndexes.add(index); this.stringDescriptorPanel_.stringDescriptorIndexes.add(index);
} }
const buttonTemplate = queryRequiredElement('#raw-data-tree-button'); const buttonTemplate = queryRequiredElement(
'#raw-data-tree-button',
/** @type {!DocumentFragment} */ (this.rootElement_.getRootNode()));
const button = document.importNode(buttonTemplate.content, true) const button = document.importNode(buttonTemplate.content, true)
.querySelector('button'); .querySelector('button');
item.labelElement.appendChild(button); item.labelElement.appendChild(button);
...@@ -200,7 +202,9 @@ cr.define('descriptor_panel', function() { ...@@ -200,7 +202,9 @@ cr.define('descriptor_panel', function() {
renderUrlDescriptorIndexItem_(rawData, offset, item, fieldLabel) { renderUrlDescriptorIndexItem_(rawData, offset, item, fieldLabel) {
const index = rawData[offset]; const index = rawData[offset];
if (index > 0) { if (index > 0) {
const buttonTemplate = queryRequiredElement('#raw-data-tree-button'); const buttonTemplate = queryRequiredElement(
'#raw-data-tree-button',
/** @type {!DocumentFragment} */ (this.rootElement_.getRootNode()));
const button = document.importNode(buttonTemplate.content, true) const button = document.importNode(buttonTemplate.content, true)
.querySelector('button'); .querySelector('button');
item.labelElement.appendChild(button); item.labelElement.appendChild(button);
...@@ -238,7 +242,9 @@ cr.define('descriptor_panel', function() { ...@@ -238,7 +242,9 @@ cr.define('descriptor_panel', function() {
const msOs20DescriptorSetLength = const msOs20DescriptorSetLength =
data.getUint16(MS_OS_20_SET_TOTAL_LENGTH_OFFSET, true); data.getUint16(MS_OS_20_SET_TOTAL_LENGTH_OFFSET, true);
const buttonTemplate = queryRequiredElement('#raw-data-tree-button'); const buttonTemplate = queryRequiredElement(
'#raw-data-tree-button',
/** @type {!DocumentFragment} */ (this.rootElement_.getRootNode()));
const button = document.importNode(buttonTemplate.content, true) const button = document.importNode(buttonTemplate.content, true)
.querySelector('button'); .querySelector('button');
item.labelElement.appendChild(button); item.labelElement.appendChild(button);
...@@ -277,7 +283,9 @@ cr.define('descriptor_panel', function() { ...@@ -277,7 +283,9 @@ cr.define('descriptor_panel', function() {
if (altEnumCode !== 0) { if (altEnumCode !== 0) {
const vendorCode = rawData[offset + MS_OS_20_VENDOR_CODE_ITEM_OFFSET]; const vendorCode = rawData[offset + MS_OS_20_VENDOR_CODE_ITEM_OFFSET];
const buttonTemplate = queryRequiredElement('#raw-data-tree-button'); const buttonTemplate = queryRequiredElement(
'#raw-data-tree-button',
/** @type {!DocumentFragment} */ (this.rootElement_.getRootNode()));
const button = document.importNode(buttonTemplate.content, true) const button = document.importNode(buttonTemplate.content, true)
.querySelector('button'); .querySelector('button');
item.labelElement.appendChild(button); item.labelElement.appendChild(button);
...@@ -2648,8 +2656,9 @@ cr.define('descriptor_panel', function() { ...@@ -2648,8 +2656,9 @@ cr.define('descriptor_panel', function() {
*/ */
function addNewDescriptorDisplayElement( function addNewDescriptorDisplayElement(
rootElement, descriptorPanelTitle = undefined) { rootElement, descriptorPanelTitle = undefined) {
const descriptorPanelTemplate = const descriptorPanelTemplate = queryRequiredElement(
queryRequiredElement('#descriptor-panel-template'); '#descriptor-panel-template',
/** @type {!DocumentFragment} */ (rootElement.getRootNode()));
const descriptorPanelClone = /** @type {!HTMLElement} */ const descriptorPanelClone = /** @type {!HTMLElement} */
(document.importNode(descriptorPanelTemplate.content, true)); (document.importNode(descriptorPanelTemplate.content, true));
...@@ -2665,8 +2674,9 @@ cr.define('descriptor_panel', function() { ...@@ -2665,8 +2674,9 @@ cr.define('descriptor_panel', function() {
rawDataTreeRoot.detail = {payload: {}, children: {}}; rawDataTreeRoot.detail = {payload: {}, children: {}};
if (descriptorPanelTitle) { if (descriptorPanelTitle) {
const descriptorPanelTitleTemplate = const descriptorPanelTitleTemplate = queryRequiredElement(
queryRequiredElement('#descriptor-panel-title'); '#descriptor-panel-title',
/** @type {!DocumentFragment} */ (rootElement.getRootNode()));
const clone = const clone =
document.importNode(descriptorPanelTitleTemplate.content, true) document.importNode(descriptorPanelTitleTemplate.content, true)
.querySelector('descriptorpaneltitle'); .querySelector('descriptorpaneltitle');
...@@ -2857,14 +2867,17 @@ cr.define('descriptor_panel', function() { ...@@ -2857,14 +2867,17 @@ cr.define('descriptor_panel', function() {
* @param {!Uint8Array} rawData * @param {!Uint8Array} rawData
*/ */
function renderRawDataBytes(rawDataByteElement, rawData) { function renderRawDataBytes(rawDataByteElement, rawData) {
const rawDataByteContainerTemplate = const rawDataByteContainerTemplate = queryRequiredElement(
queryRequiredElement('#raw-data-byte-container-template'); '#raw-data-byte-container-template',
/** @type {!DocumentFragment} */ (rawDataByteElement.getRootNode()));
const rawDataByteContainerClone = const rawDataByteContainerClone =
document.importNode(rawDataByteContainerTemplate.content, true); document.importNode(rawDataByteContainerTemplate.content, true);
const rawDataByteContainerElement = const rawDataByteContainerElement =
rawDataByteContainerClone.querySelector('div'); rawDataByteContainerClone.querySelector('div');
const rawDataByteTemplate = queryRequiredElement('#raw-data-byte-template'); const rawDataByteTemplate = queryRequiredElement(
'#raw-data-byte-template',
/** @type {!DocumentFragment} */ (rawDataByteElement.getRootNode()));
for (const value of rawData) { for (const value of rawData) {
const rawDataByteClone = const rawDataByteClone =
document.importNode(rawDataByteTemplate.content, true); document.importNode(rawDataByteTemplate.content, true);
......
...@@ -16,10 +16,12 @@ cr.define('devices_page', function() { ...@@ -16,10 +16,12 @@ cr.define('devices_page', function() {
class DevicesPage { class DevicesPage {
/** /**
* @param {!device.mojom.UsbDeviceManagerRemote} usbManager * @param {!device.mojom.UsbDeviceManagerRemote} usbManager
* @param {!ShadowRoot} root
*/ */
constructor(usbManager) { constructor(usbManager, root) {
/** @private {!device.mojom.UsbDeviceManagerRemote} */ /** @private {!device.mojom.UsbDeviceManagerRemote} */
this.usbManager_ = usbManager; this.usbManager_ = usbManager;
this.root = root;
this.renderDeviceList_(); this.renderDeviceList_();
} }
...@@ -33,10 +35,10 @@ cr.define('devices_page', function() { ...@@ -33,10 +35,10 @@ cr.define('devices_page', function() {
/** @type {!Array<!device.mojom.UsbDeviceInfo>} */ /** @type {!Array<!device.mojom.UsbDeviceInfo>} */
const devices = response.results; const devices = response.results;
const tableBody = $('device-list'); const tableBody = this.root.querySelector('#device-list');
tableBody.innerHTML = trustedTypes.emptyHTML; tableBody.innerHTML = trustedTypes.emptyHTML;
const rowTemplate = document.querySelector('#device-row'); const rowTemplate = this.root.querySelector('#device-row');
for (const device of devices) { for (const device of devices) {
/** @type {DocumentFragment|Node} */ /** @type {DocumentFragment|Node} */
...@@ -78,10 +80,10 @@ cr.define('devices_page', function() { ...@@ -78,10 +80,10 @@ cr.define('devices_page', function() {
switchToTab_(device) { switchToTab_(device) {
const tabId = device.guid; const tabId = device.guid;
if (null == $(tabId)) { if (null == this.root.getElementById(tabId)) {
const devicePage = new DevicePage(this.usbManager_, device); const devicePage = new DevicePage(this.usbManager_, device, this.root);
} }
$(tabId).selected = true; this.root.getElementById(tabId).selected = true;
} }
} }
...@@ -93,9 +95,11 @@ cr.define('devices_page', function() { ...@@ -93,9 +95,11 @@ cr.define('devices_page', function() {
/** /**
* @param {!device.mojom.UsbDeviceManagerRemote} usbManager * @param {!device.mojom.UsbDeviceManagerRemote} usbManager
* @param {!device.mojom.UsbDeviceInfo} device * @param {!device.mojom.UsbDeviceInfo} device
* @param {!ShadowRoot} root
*/ */
constructor(usbManager, device) { constructor(usbManager, device, root) {
this.usbManager_ = usbManager; this.usbManager_ = usbManager;
this.root = root;
this.renderTab_(device); this.renderTab_(device);
} }
...@@ -105,9 +109,9 @@ cr.define('devices_page', function() { ...@@ -105,9 +109,9 @@ cr.define('devices_page', function() {
* @private * @private
*/ */
renderTab_(device) { renderTab_(device) {
const tabs = queryRequiredElement('tabs'); const tabs = queryRequiredElement('tabs', this.root);
const tabTemplate = queryRequiredElement('#tab-template'); const tabTemplate = queryRequiredElement('#tab-template', this.root);
/** @type {DocumentFragment|Node} */ /** @type {DocumentFragment|Node} */
const tabClone = document.importNode(tabTemplate.content, true); const tabClone = document.importNode(tabTemplate.content, true);
...@@ -122,11 +126,11 @@ cr.define('devices_page', function() { ...@@ -122,11 +126,11 @@ cr.define('devices_page', function() {
tab.id = device.guid; tab.id = device.guid;
tabs.appendChild(tabClone); tabs.appendChild(tabClone);
cr.ui.decorate('tab', cr.ui.Tab); cr.ui.decorate(tab, cr.ui.Tab);
const tabPanels = queryRequiredElement('tabpanels'); const tabPanels = queryRequiredElement('tabpanels', this.root);
const tabPanelTemplate = const tabPanelTemplate =
queryRequiredElement('#device-tabpanel-template'); queryRequiredElement('#device-tabpanel-template', this.root);
/** @type {DocumentFragment|Node} */ /** @type {DocumentFragment|Node} */
const tabPanelClone = document.importNode(tabPanelTemplate.content, true); const tabPanelClone = document.importNode(tabPanelTemplate.content, true);
...@@ -368,8 +372,8 @@ cr.define('devices_page', function() { ...@@ -368,8 +372,8 @@ cr.define('devices_page', function() {
const button = queryRequiredElement(`.${panelType}-button`, tabPanel); const button = queryRequiredElement(`.${panelType}-button`, tabPanel);
const displayElement = const displayElement =
queryRequiredElement(`.${panelType}-panel`, tabPanel); queryRequiredElement(`.${panelType}-panel`, tabPanel);
const descriptorPanel = const descriptorPanel = new descriptor_panel.DescriptorPanel(
new descriptor_panel.DescriptorPanel(usbDevice, displayElement); usbDevice, /** @type {!HTMLElement} */ (displayElement));
switch (panelType) { switch (panelType) {
case 'string-descriptor': case 'string-descriptor':
descriptorPanel.initialStringDescriptorPanel(guid); descriptorPanel.initialStringDescriptorPanel(guid);
......
...@@ -24,13 +24,14 @@ ...@@ -24,13 +24,14 @@
flattenhtml="true" flattenhtml="true"
allowexternalscript="true" allowexternalscript="true"
type="BINDATA" /> type="BINDATA" />
<include name="IDR_USB_INTERNALS_JS"
file="usb_internals.js"
type="BINDATA" />
<include name="IDR_USB_INTERNALS_MOJOM_LITE_JS" <include name="IDR_USB_INTERNALS_MOJOM_LITE_JS"
file="${root_gen_dir}\chrome\browser\ui\webui\usb_internals\usb_internals.mojom-lite.js" file="${root_gen_dir}\chrome\browser\ui\webui\usb_internals\usb_internals.mojom-lite.js"
use_base_dir="false" use_base_dir="false"
type="BINDATA" /> type="BINDATA" />
<include name="IDR_USB_INTERNALS_APP_JS"
file="${root_gen_dir}\chrome\browser\resources\usb_internals\app.js"
use_base_dir="false"
type="BINDATA" />
</includes> </includes>
</release> </release>
</grit> </grit>
...@@ -5,9 +5,6 @@ ...@@ -5,9 +5,6 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>USB Internals</title> <title>USB Internals</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="chrome://resources/css/tabs.css">
<link rel="stylesheet" href="chrome://resources/css/tree.css">
<link rel="stylesheet" href="usb_internals.css">
<script src="chrome://resources/js/assert.js"></script> <script src="chrome://resources/js/assert.js"></script>
<script src="chrome://resources/js/cr.js"></script> <script src="chrome://resources/js/cr.js"></script>
...@@ -35,224 +32,8 @@ ...@@ -35,224 +32,8 @@
</head> </head>
<body> <body>
<tabbox> <usb-internals-app></usb-internals-app>
<tabs> <script src="app.js"></script>
<tab>Test Devices</tab>
<tab>Devices</tab>
</tabs>
<tabpanels>
<tabpanel>
<!-- Test Devices -->
<h2>Test Devices</h2>
<p>
<table class="styled-table">
<thead>
<tr>
<th>Name</th>
<th>Serial number</th>
<th>Landing page</th>
<th>
</tr>
</thead>
<tbody id="test-device-list">
</tbody>
<template id="test-device-row">
<tr>
<td></td>
<td></td>
<td></td>
<td><button>Remove</button></td>
</tr>
</template>
</table>
</p>
<div class="page-section">
<strong>Add a test device:</strong>
<form id="add-test-device-form" action="">
<p>
<label>
Name: <input id="test-device-name" type="text" size="40">
</label>
</p>
<p>
<label>
Serial number:
<input id="test-device-serial" type="text" size="40">
</label>
</p>
<p>
<label>
Landing page:
<input id="test-device-landing-page" type="text" size="40">
</label>
</p>
<button type="submit">Add</button>
<span id="add-test-device-result"></span>
</form>
</div>
</tabpanel>
<tabpanel>
<!-- Devices -->
<h2>Devices</h2>
<table class="styled-table">
<thead>
<tr>
<th>Bus Number</th>
<th>Port Number</th>
<th>Vendor Id</th>
<th>Product Id</th>
<th>Manufacturer Name</th>
<th>Product Name</th>
<th>Serial Number</th>
<th>
</tr>
</thead>
<tbody id="device-list"></tbody>
<template id="device-row">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><button>Inspect</button></td>
</tr>
</template>
</table>
</tabpanel>
</tabpanels>
</tabbox>
<template id="tab-template">
<tab></tab>
</template>
<template id="device-tabpanel-template">
<tabpanel>
<tree class="tree-view"></tree>
<div class="descriptor-button">
<button class="device-descriptor-button">Get Device Descriptor</button>
</div>
<div class="device-descriptor-panel" hidden></div>
<div class="descriptor-button">
<button class="configuration-descriptor-button">
Get Configuration Descriptor
</button>
</div>
<div class="configuration-descriptor-panel" hidden></div>
<div class="descriptor-button">
<button class="string-descriptor-button">Get String Descriptor</button>
</div>
<div class="string-descriptor-panel" hidden>
String Descriptor Index:
<input id="index-input" type="number" min="1" list="indexes">
<datalist id="indexes"></datalist>
Language Code:
<input id="language-code-input" list="languages">
<datalist id="languages"></datalist>
<button>GET</button>
</div>
<div class="descriptor-button">
<button class="bos-descriptor-button">
Get WebUSB & Microsoft OS 2.0 Descriptors
</button>
</div>
<div class="bos-descriptor-panel" hidden></div>
<div class="descriptor-button">
<button class="testing-tool-button">Testing Tool Panel</button>
</div>
<div class="testing-tool-panel" hidden>
<select id="input-type">
<option label="Decimal with Dropdown Menu"></option>
<option label="Hex Bytes"></option>
</select>
<table class="styled-table">
<thead>
<tr>
<th>bmRequestType</th>
<th>bRequest</th>
<th>wValue</th>
<th>wIndex</th>
<th>wLength</th>
</tr>
</thead>
<tbody id="testing-tool">
<tr>
<td>
<select id="transfer-direction">
<option label="Host-to-Device" value="Host-to-Device">
</option>
<option label="Device-to-Host" value="Device-to-Host">
</option>
</select>
<select id="transfer-type">
<option label="Standard" value="STANDARD"></option>
<option label="Class" value="CLASS"></option>
<option label="Vendor" value="VENDOR"></option>
</select>
<select id="transfer-recipient">
<option label="Device" value="DEVICE"></option>
<option label="Interface" value="INTERFACE"></option>
<option label="Endpoint" value="ENDPOINT"></option>
<option label="Other" value="OTHER"></option>
</select>
</td>
<td><input id="query-request" type="number" placeholder="0"></td>
<td><input id="query-value" type="number" placeholder="0"></td>
<td><input id="query-index" type="number" placeholder="0"></td>
<td><input id="query-length" type="number" placeholder="0"></td>
<td><button>Send</button></td>
</tr>
<tr hidden>
<td>0x<input id="query-request-type" placeholder="00"></td>
<td>0x<input id="query-request" placeholder="00"></td>
<td>0x<input id="query-value" placeholder="0000"></td>
<td>0x<input id="query-index" placeholder="0000"></td>
<td>0x<input id="query-length" placeholder="0000"></td>
<td><button>Send</button></td>
</tr>
</tbody>
</table>
<div id="data-input-area">
Data (in Hex):
<textarea cols="31"></textarea>
</div>
</div>
</tabpanel>
</template>
<template id="descriptor-panel-template">
<descriptorpanel>
<tree class="raw-data-tree-view"></tree>
<div class="raw-data-byte-view"></div>
</descriptorpanel>
</template>
<template id="raw-data-byte-container-template">
<div></div>
</template>
<template id="raw-data-byte-template">
<span></span>
</template>
<template id="raw-data-tree-button">
<button>GET</button>
</template>
<template id="descriptor-panel-title">
<descriptorpaneltitle></descriptorpaneltitle>
</template>
<script src="usb_internals.js"></script>
</body> </body>
</html> </html>
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "chrome/grit/browser_resources.h" #include "chrome/grit/browser_resources.h"
#include "chrome/grit/usb_internals_resources.h" #include "chrome/grit/usb_internals_resources.h"
#include "content/public/browser/web_ui_data_source.h" #include "content/public/browser/web_ui_data_source.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui) UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui)
: ui::MojoWebUIController(web_ui) { : ui::MojoWebUIController(web_ui) {
...@@ -21,8 +20,8 @@ UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui) ...@@ -21,8 +20,8 @@ UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui)
content::WebUIDataSource::Create(chrome::kChromeUIUsbInternalsHost); content::WebUIDataSource::Create(chrome::kChromeUIUsbInternalsHost);
static constexpr webui::ResourcePath kPaths[] = { static constexpr webui::ResourcePath kPaths[] = {
{"app.js", IDR_USB_INTERNALS_APP_JS},
{"usb_internals.css", IDR_USB_INTERNALS_CSS}, {"usb_internals.css", IDR_USB_INTERNALS_CSS},
{"usb_internals.js", IDR_USB_INTERNALS_JS},
{"usb_internals.mojom-lite.js", IDR_USB_INTERNALS_MOJOM_LITE_JS}, {"usb_internals.mojom-lite.js", IDR_USB_INTERNALS_MOJOM_LITE_JS},
{"descriptor_panel.js", IDR_USB_INTERNALS_DESCRIPTOR_PANEL_JS}, {"descriptor_panel.js", IDR_USB_INTERNALS_DESCRIPTOR_PANEL_JS},
{"devices_page.js", IDR_USB_INTERNALS_DEVICES_PAGE_JS}, {"devices_page.js", IDR_USB_INTERNALS_DEVICES_PAGE_JS},
...@@ -38,9 +37,7 @@ UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui) ...@@ -38,9 +37,7 @@ UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui)
webui::AddResourcePathsBulk(source, kPaths); webui::AddResourcePathsBulk(source, kPaths);
source->SetDefaultResource(IDR_USB_INTERNALS_HTML); source->SetDefaultResource(IDR_USB_INTERNALS_HTML);
source->OverrideContentSecurityPolicy( source->DisableTrustedTypesCSP();
network::mojom::CSPDirectiveName::TrustedTypes,
"trusted-types cr-ui-tree-js-static;");
content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source); content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
} }
......
...@@ -252,7 +252,11 @@ window.setupFn = () => { ...@@ -252,7 +252,11 @@ window.setupFn = () => {
suite('UsbInternalsUITest', function() { suite('UsbInternalsUITest', function() {
let app = null;
suiteSetup(function() { suiteSetup(function() {
app = document.querySelector('usb-internals-app');
// Before tests are run, make sure setup completes. // Before tests are run, make sure setup completes.
return setupResolver.promise.then(function() { return setupResolver.promise.then(function() {
return Promise.all([ return Promise.all([
...@@ -270,17 +274,17 @@ suite('UsbInternalsUITest', function() { ...@@ -270,17 +274,17 @@ suite('UsbInternalsUITest', function() {
const EXPECT_DEVICES_NUM = 2; const EXPECT_DEVICES_NUM = 2;
// Totally 2 tables: 'TestDevice' table and 'Device' table. // Totally 2 tables: 'TestDevice' table and 'Device' table.
const tables = document.querySelectorAll('table'); const tables = app.shadowRoot.querySelectorAll('table');
expectEquals(2, tables.length); expectEquals(2, tables.length);
// Only 2 tabs after loading page. // Only 2 tabs after loading page.
const tabs = document.querySelectorAll('tab'); const tabs = app.shadowRoot.querySelectorAll('tab');
expectEquals(2, tabs.length); expectEquals(2, tabs.length);
const tabPanels = document.querySelectorAll('tabpanel'); const tabPanels = app.shadowRoot.querySelectorAll('tabpanel');
expectEquals(2, tabPanels.length); expectEquals(2, tabPanels.length);
// The second is the devices table, which has 8 columns. // The second is the devices table, which has 8 columns.
const devicesTable = document.querySelectorAll('table')[1]; const devicesTable = app.shadowRoot.querySelectorAll('table')[1];
const columns = devicesTable.querySelector('thead') const columns = devicesTable.querySelector('thead')
.querySelector('tr') .querySelector('tr')
.querySelectorAll('th'); .querySelectorAll('th');
...@@ -292,29 +296,29 @@ suite('UsbInternalsUITest', function() { ...@@ -292,29 +296,29 @@ suite('UsbInternalsUITest', function() {
}); });
test('DeviceTabAdded', function() { test('DeviceTabAdded', function() {
const devicesTable = document.querySelector('#device-list'); const devicesTable = app.$('#device-list');
// Click the inspect button to open information about the first device. // Click the inspect button to open information about the first device.
// The device info is opened as a third tab panel. // The device info is opened as a third tab panel.
devicesTable.querySelectorAll('button')[0].click(); devicesTable.querySelectorAll('button')[0].click();
assertEquals(3, document.querySelectorAll('tab').length); assertEquals(3, app.shadowRoot.querySelectorAll('tab').length);
assertEquals(3, document.querySelectorAll('tabpanel').length); assertEquals(3, app.shadowRoot.querySelectorAll('tabpanel').length);
expectTrue(document.querySelectorAll('tabpanel')[2].selected); expectTrue(app.shadowRoot.querySelectorAll('tabpanel')[2].selected);
// Check that clicking the inspect button for another device will open a // Check that clicking the inspect button for another device will open a
// new tabpanel. // new tabpanel.
devicesTable.querySelectorAll('button')[1].click(); devicesTable.querySelectorAll('button')[1].click();
assertEquals(4, document.querySelectorAll('tab').length); assertEquals(4, app.shadowRoot.querySelectorAll('tab').length);
assertEquals(4, document.querySelectorAll('tabpanel').length); assertEquals(4, app.shadowRoot.querySelectorAll('tabpanel').length);
expectTrue(document.querySelectorAll('tabpanel')[3].selected); expectTrue(app.shadowRoot.querySelectorAll('tabpanel')[3].selected);
expectFalse(document.querySelectorAll('tabpanel')[2].selected); expectFalse(app.shadowRoot.querySelectorAll('tabpanel')[2].selected);
// Check that clicking the inspect button for the same device a second // Check that clicking the inspect button for the same device a second
// time will open the same tabpanel. // time will open the same tabpanel.
devicesTable.querySelectorAll('button')[0].click(); devicesTable.querySelectorAll('button')[0].click();
assertEquals(4, document.querySelectorAll('tab').length); assertEquals(4, app.shadowRoot.querySelectorAll('tab').length);
assertEquals(4, document.querySelectorAll('tabpanel').length); assertEquals(4, app.shadowRoot.querySelectorAll('tabpanel').length);
expectTrue(document.querySelectorAll('tabpanel')[2].selected); expectTrue(app.shadowRoot.querySelectorAll('tabpanel')[2].selected);
expectFalse(document.querySelectorAll('tabpanel')[3].selected); expectFalse(app.shadowRoot.querySelectorAll('tabpanel')[3].selected);
}); });
test('RenderDeviceInfoTree', function() { test('RenderDeviceInfoTree', function() {
...@@ -322,7 +326,7 @@ suite('UsbInternalsUITest', function() { ...@@ -322,7 +326,7 @@ suite('UsbInternalsUITest', function() {
// showing WebUSB information. Check the tree displays correct data. // showing WebUSB information. Check the tree displays correct data.
// The tab panel of the first device is opened in previous test as the // The tab panel of the first device is opened in previous test as the
// third tab panel. // third tab panel.
const deviceTab = document.querySelectorAll('tabpanel')[2]; const deviceTab = app.shadowRoot.querySelectorAll('tabpanel')[2];
const tree = deviceTab.querySelector('tree'); const tree = deviceTab.querySelector('tree');
const treeItems = tree.querySelectorAll('.tree-item'); const treeItems = tree.querySelectorAll('.tree-item');
assertEquals(11, treeItems.length); assertEquals(11, treeItems.length);
...@@ -347,7 +351,7 @@ suite('UsbInternalsUITest', function() { ...@@ -347,7 +351,7 @@ suite('UsbInternalsUITest', function() {
await deviceTabInitializedResolver.promise; await deviceTabInitializedResolver.promise;
// The tab panel of the first device is opened in previous test as the // The tab panel of the first device is opened in previous test as the
// third tab panel. This device has correct device descriptor. // third tab panel. This device has correct device descriptor.
const deviceTab = document.querySelectorAll('tabpanel')[2]; const deviceTab = app.shadowRoot.querySelectorAll('tabpanel')[2];
deviceTab.querySelector('.device-descriptor-button').click(); deviceTab.querySelector('.device-descriptor-button').click();
await deviceDescriptorRenderResolver.promise; await deviceDescriptorRenderResolver.promise;
...@@ -413,11 +417,11 @@ suite('UsbInternalsUITest', function() { ...@@ -413,11 +417,11 @@ suite('UsbInternalsUITest', function() {
test('RenderShortDeviceDescriptor', async function() { test('RenderShortDeviceDescriptor', async function() {
await deviceManagerGetDevicesResolver.promise; await deviceManagerGetDevicesResolver.promise;
const devicesTable = document.querySelector('#device-list'); const devicesTable = app.$('#device-list');
// Inspect the second device, which has short device descriptor. // Inspect the second device, which has short device descriptor.
devicesTable.querySelectorAll('button')[1].click(); devicesTable.querySelectorAll('button')[1].click();
// The fourth is the device tab (a third tab was opened in a previous test). // The fourth is the device tab (a third tab was opened in a previous test).
const deviceTab = document.querySelectorAll('tabpanel')[3]; const deviceTab = app.shadowRoot.querySelectorAll('tabpanel')[3];
await deviceTabInitializedResolver.promise; await deviceTabInitializedResolver.promise;
deviceDescriptorRenderResolver = new PromiseResolver(); deviceDescriptorRenderResolver = new PromiseResolver();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment