Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: third_party/WebKit/LayoutTests/usb/resources/webusb-test.js

Issue 2789723003: Migrate WebUSB LayoutTests into external/wpt (Closed)
Patch Set: Rebased Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 'use strict';
2
3 // This polyfil library implements the following WebIDL:
4 //
5 // partial interface USB {
6 // [SameObject] readonly attribute USBTest test;
7 // }
8 //
9 // interface USBTest {
10 // attribute EventHandler ondeviceclose;
11 // attribute FakeUSBDevice? chosenDevice;
12 // attribute FrozenArray<USBDeviceFilter>? lastFilters;
13 //
14 // Promise<void> initialize();
15 // Promise<void> attachToWindow(Window window);
16 // FakeUSBDevice addFakeDevice(FakeUSBDeviceInit deviceInit);
17 // void reset();
18 // };
19 //
20 // interface FakeUSBDevice {
21 // void disconnect();
22 // };
23 //
24 // dictionary FakeUSBDeviceInit {
25 // octet usbVersionMajor;
26 // octet usbVersionMinor;
27 // octet usbVersionSubminor;
28 // octet deviceClass;
29 // octet deviceSubclass;
30 // octet deviceProtocol;
31 // unsigned short vendorId;
32 // unsigned short productId;
33 // octet deviceVersionMajor;
34 // octet deviceVersionMinor;
35 // octet deviceVersionSubminor;
36 // DOMString? manufacturerName;
37 // DOMString? productName;
38 // DOMString? serialNumber;
39 // octet activeConfigurationValue = 0;
40 // sequence<FakeUSBConfigurationInit> configurations;
41 // };
42 //
43 // dictionary FakeUSBConfigurationInit {
44 // octet configurationValue;
45 // DOMString? configurationName;
46 // sequence<FakeUSBInterfaceInit> interfaces;
47 // };
48 //
49 // dictionary FakeUSBInterfaceInit {
50 // octet interfaceNumber;
51 // sequence<FakeUSBAlternateInterfaceInit> alternates;
52 // };
53 //
54 // dictionary FakeUSBAlternateInterfaceInit {
55 // octet alternateSetting;
56 // octet interfaceClass;
57 // octet interfaceSubclass;
58 // octet interfaceProtocol;
59 // DOMString? interfaceName;
60 // sequence<FakeUSBEndpointInit> endpoints;
61 // };
62 //
63 // dictionary FakeUSBEndpointInit {
64 // octet endpointNumber;
65 // USBDirection direction;
66 // USBEndpointType type;
67 // unsigned long packetSize;
68 // };
69
70 (() => {
71
72 // The global mojo object contains the Mojo JS binding modules loaded during
73 // initialization.
74 let mojo = null;
75
76 // These variables are logically members of the USBTest class but are defined
77 // here to hide them from being visible as fields of navigator.usb.test.
78 let g_initializePromise = null;
79 let g_chooserService = null;
80 let g_deviceManager = null;
81 let g_closeListener = null;
82 let g_nextGuid = 0;
83
84 function fakeDeviceInitToDeviceInfo(guid, init) {
85 let deviceInfo = {
86 guid: guid + "",
87 usb_version_major: init.usbVersionMajor,
88 usb_version_minor: init.usbVersionMinor,
89 usb_version_subminor: init.usbVersionSubminor,
90 class_code: init.deviceClass,
91 subclass_code: init.deviceSubclass,
92 protocol_code: init.deviceProtocol,
93 vendor_id: init.vendorId,
94 product_id: init.productId,
95 device_version_major: init.deviceVersionMajor,
96 device_version_minor: init.deviceVersionMinor,
97 device_version_subminor: init.deviceVersionSubminor,
98 manufacturer_name: init.manufacturerName,
99 product_name: init.productName,
100 serial_number: init.serialNumber,
101 active_configuration: init.activeConfigurationValue,
102 configurations: []
103 };
104 init.configurations.forEach(config => {
105 var configInfo = {
106 configuration_value: config.configurationValue,
107 configuration_name: config.configurationName,
108 interfaces: []
109 };
110 config.interfaces.forEach(iface => {
111 var interfaceInfo = {
112 interface_number: iface.interfaceNumber,
113 alternates: []
114 };
115 iface.alternates.forEach(alternate => {
116 var alternateInfo = {
117 alternate_setting: alternate.alternateSetting,
118 class_code: alternate.interfaceClass,
119 subclass_code: alternate.interfaceSubclass,
120 protocol_code: alternate.interfaceProtocol,
121 interface_name: alternate.interfaceName,
122 endpoints: []
123 };
124 alternate.endpoints.forEach(endpoint => {
125 var endpointInfo = {
126 endpoint_number: endpoint.endpointNumber,
127 packet_size: endpoint.packetSize,
128 };
129 switch (endpoint.direction) {
130 case "in":
131 endpointInfo.direction = mojo.device.TransferDirection.INBOUND;
132 break;
133 case "out":
134 endpointInfo.direction = mojo.device.TransferDirection.OUTBOUND;
135 break;
136 }
137 switch (endpoint.type) {
138 case "bulk":
139 endpointInfo.type = mojo.device.EndpointType.BULK;
140 break;
141 case "interrupt":
142 endpointInfo.type = mojo.device.EndpointType.INTERRUPT;
143 break;
144 case "isochronous":
145 endpointInfo.type = mojo.device.EndpointType.ISOCHRONOUS;
146 break;
147 }
148 alternateInfo.endpoints.push(endpointInfo);
149 });
150 interfaceInfo.alternates.push(alternateInfo);
151 });
152 configInfo.interfaces.push(interfaceInfo);
153 });
154 deviceInfo.configurations.push(configInfo);
155 });
156 return deviceInfo;
157 }
158
159 function convertMojoDeviceFilters(input) {
160 let output = [];
161 input.forEach(filter => {
162 output.push(convertMojoDeviceFilter(filter));
163 });
164 return output;
165 }
166
167 function convertMojoDeviceFilter(input) {
168 let output = {};
169 if (input.has_vendor_id)
170 output.vendorId = input.vendor_id;
171 if (input.has_product_id)
172 output.productId = input.product_id;
173 if (input.has_class_code)
174 output.classCode = input.class_code;
175 if (input.has_subclass_code)
176 output.subclassCode = input.subclass_code;
177 if (input.has_protocol_code)
178 output.protocolCode = input.protocol_code;
179 if (input.serial_number)
180 output.serialNumber = input.serial_number;
181 return output;
182 }
183
184 class FakeDevice {
185 constructor(deviceInit) {
186 this.info_ = deviceInit;
187 this.opened_ = false;
188 this.currentConfiguration_ = null;
189 this.claimedInterfaces_ = new Map();
190 }
191
192 getConfiguration() {
193 if (this.currentConfiguration_) {
194 return Promise.resolve({
195 value: this.currentConfiguration_.configuration_value });
196 } else {
197 return Promise.resolve({ value: 0 });
198 }
199 }
200
201 open() {
202 assert_false(this.opened_);
203 this.opened_ = true;
204 return Promise.resolve({ error: mojo.device.OpenDeviceError.OK });
205 }
206
207 close() {
208 assert_true(this.opened_);
209 this.opened_ = false;
210 return Promise.resolve();
211 }
212
213 setConfiguration(value) {
214 assert_true(this.opened_);
215
216 let selected_configuration = this.info_.configurations.find(
217 configuration => configuration.configurationValue == value);
218 // Blink should never request an invalid configuration.
219 assert_false(selected_configuration == undefined);
220 this.currentConfiguration_ = selected_configuration;
221 return Promise.resolve({ success: true });
222 }
223
224 claimInterface(interfaceNumber) {
225 assert_true(this.opened_);
226 assert_false(this.currentConfiguration_ == null, 'device configured');
227 assert_false(this.claimedInterfaces_.has(interfaceNumber),
228 'interface already claimed');
229
230 // Blink should never request an invalid interface.
231 assert_true(this.currentConfiguration_.interfaces.some(
232 iface => iface.interfaceNumber == interfaceNumber));
233 this.claimedInterfaces_.set(interfaceNumber, 0);
234 return Promise.resolve({ success: true });
235 }
236
237 releaseInterface(interfaceNumber) {
238 assert_true(this.opened_);
239 assert_false(this.currentConfiguration_ == null, 'device configured');
240 assert_true(this.claimedInterfaces_.has(interfaceNumber));
241 this.claimedInterfaces_.delete(interfaceNumber);
242 return Promise.resolve({ success: true });
243 }
244
245 setInterfaceAlternateSetting(interfaceNumber, alternateSetting) {
246 assert_true(this.opened_);
247 assert_false(this.currentConfiguration_ == null, 'device configured');
248 assert_true(this.claimedInterfaces_.has(interfaceNumber));
249
250 let iface = this.currentConfiguration_.interfaces.find(
251 iface => iface.interfaceNumber == interfaceNumber);
252 // Blink should never request an invalid interface or alternate.
253 assert_false(iface == undefined);
254 assert_true(iface.alternates.some(
255 x => x.alternateSetting == alternateSetting));
256 this.claimedInterfaces_.set(interfaceNumber, alternateSetting);
257 return Promise.resolve({ success: true });
258 }
259
260 reset() {
261 assert_true(this.opened_);
262 return Promise.resolve({ success: true });
263 }
264
265 clearHalt(endpoint) {
266 assert_true(this.opened_);
267 assert_false(this.currentConfiguration_ == null, 'device configured');
268 // TODO(reillyg): Assert that endpoint is valid.
269 return Promise.resolve({ success: true });
270 }
271
272 controlTransferIn(params, length, timeout) {
273 assert_true(this.opened_);
274 assert_false(this.currentConfiguration_ == null, 'device configured');
275 return Promise.resolve({
276 status: mojo.device.TransferStatus.OK,
277 data: [length >> 8, length & 0xff, params.request, params.value >> 8,
278 params.value & 0xff, params.index >> 8, params.index & 0xff]
279 });
280 }
281
282 controlTransferOut(params, data, timeout) {
283 assert_true(this.opened_);
284 assert_false(this.currentConfiguration_ == null, 'device configured');
285 return Promise.resolve({
286 status: mojo.device.TransferStatus.OK,
287 bytesWritten: data.byteLength
288 });
289 }
290
291 genericTransferIn(endpointNumber, length, timeout) {
292 assert_true(this.opened_);
293 assert_false(this.currentConfiguration_ == null, 'device configured');
294 // TODO(reillyg): Assert that endpoint is valid.
295 let data = new Array(length);
296 for (let i = 0; i < length; ++i)
297 data[i] = i & 0xff;
298 return Promise.resolve({
299 status: mojo.device.TransferStatus.OK,
300 data: data
301 });
302 }
303
304 genericTransferOut(endpointNumber, data, timeout) {
305 assert_true(this.opened_);
306 assert_false(this.currentConfiguration_ == null, 'device configured');
307 // TODO(reillyg): Assert that endpoint is valid.
308 return Promise.resolve({
309 status: mojo.device.TransferStatus.OK,
310 bytesWritten: data.byteLength
311 });
312 }
313
314 isochronousTransferIn(endpointNumber, packetLengths, timeout) {
315 assert_true(this.opened_);
316 assert_false(this.currentConfiguration_ == null, 'device configured');
317 // TODO(reillyg): Assert that endpoint is valid.
318 let data = new Array(packetLengths.reduce((a, b) => a + b, 0));
319 let dataOffset = 0;
320 let packets = new Array(packetLengths.length);
321 for (let i = 0; i < packetLengths.length; ++i) {
322 for (let j = 0; j < packetLengths[i]; ++j)
323 data[dataOffset++] = j & 0xff;
324 packets[i] = {
325 length: packetLengths[i],
326 transferred_length: packetLengths[i],
327 status: mojo.device.TransferStatus.OK
328 };
329 }
330 return Promise.resolve({ data: data, packets: packets });
331 }
332
333 isochronousTransferOut(endpointNumber, data, packetLengths, timeout) {
334 assert_true(this.opened_);
335 assert_false(this.currentConfiguration_ == null, 'device configured');
336 // TODO(reillyg): Assert that endpoint is valid.
337 let packets = new Array(packetLengths.length);
338 for (let i = 0; i < packetLengths.length; ++i) {
339 packets[i] = {
340 length: packetLengths[i],
341 transferred_length: packetLengths[i],
342 status: mojo.device.TransferStatus.OK
343 };
344 }
345 return Promise.resolve({ packets: packets });
346 }
347 }
348
349 class FakeDeviceManager {
350 constructor() {
351 this.bindingSet_ =
352 new mojo.bindings.BindingSet(mojo.deviceManager.DeviceManager);
353 this.devices_ = new Map();
354 this.devicesByGuid_ = new Map();
355 this.client_ = null;
356 this.nextGuid_ = 0;
357 }
358
359 addBinding(handle) {
360 this.bindingSet_.addBinding(this, handle);
361 }
362
363 addDevice(fakeDevice, info) {
364 let device = {
365 fakeDevice: fakeDevice,
366 guid: (this.nextGuid_++).toString(),
367 info: info,
368 bindingArray: []
369 };
370 this.devices_.set(fakeDevice, device);
371 this.devicesByGuid_.set(device.guid, device);
372 if (this.client_)
373 this.client_.onDeviceAdded(fakeDeviceInitToDeviceInfo(device.guid, info));
374 }
375
376 removeDevice(fakeDevice) {
377 let device = this.devices_.get(fakeDevice);
378 if (!device)
379 throw new Error('Cannot remove unknown device.');
380
381 for (var binding of device.bindingArray)
382 binding.close();
383 this.devices_.delete(device.fakeDevice);
384 this.devicesByGuid_.delete(device.guid);
385 if (this.client_) {
386 this.client_.onDeviceRemoved(
387 fakeDeviceInitToDeviceInfo(device.guid, device.info));
388 }
389 }
390
391 removeAllDevices() {
392 this.devices_.forEach(device => {
393 for (var binding of device.bindingArray)
394 binding.close();
395 this.client_.onDeviceRemoved(
396 fakeDeviceInitToDeviceInfo(device.guid, device.info));
397 });
398 this.devices_.clear();
399 this.devicesByGuid_.clear();
400 }
401
402 getDevices(options) {
403 let devices = [];
404 this.devices_.forEach(device => {
405 devices.push(fakeDeviceInitToDeviceInfo(device.guid, device.info));
406 });
407 return Promise.resolve({ results: devices });
408 }
409
410 getDevice(guid, request) {
411 let device = this.devicesByGuid_.get(guid);
412 if (device) {
413 let binding = new mojo.bindings.Binding(
414 mojo.device.Device, new FakeDevice(device.info), request);
415 binding.setConnectionErrorHandler(() => {
416 if (g_closeListener)
417 g_closeListener(device.fakeDevice);
418 });
419 device.bindingArray.push(binding);
420 } else {
421 request.close();
422 }
423 }
424
425 setClient(client) {
426 this.client_ = client;
427 }
428 }
429
430 class FakeChooserService {
431 constructor() {
432 this.bindingSet_ = new mojo.bindings.BindingSet(
433 mojo.chooserService.ChooserService);
434 this.chosenDevice_ = null;
435 this.lastFilters_ = null;
436 }
437
438 addBinding(handle) {
439 this.bindingSet_.addBinding(this, handle);
440 }
441
442 setChosenDevice(fakeDevice) {
443 this.chosenDevice_ = fakeDevice;
444 }
445
446 getPermission(deviceFilters) {
447 this.lastFilters_ = convertMojoDeviceFilters(deviceFilters);
448 let device = g_deviceManager.devices_.get(this.chosenDevice_);
449 if (device) {
450 return Promise.resolve({
451 result: fakeDeviceInitToDeviceInfo(device.guid, device.info)
452 });
453 } else {
454 return Promise.resolve({ result: null });
455 }
456 }
457 }
458
459 // Unlike FakeDevice this class is exported to callers of USBTest.addFakeDevice.
460 class FakeUSBDevice {
461 disconnect() {
462 setTimeout(() => g_deviceManager.removeDevice(this), 0);
463 }
464 }
465
466 class USBTest {
467 constructor() {}
468
469 initialize() {
470 if (!g_initializePromise) {
471 g_initializePromise = new Promise(resolve => {
472 window.define = gin.define; // Mojo modules expect this.
473
474 gin.define('WebUSB Test Mocks', [
475 'content/public/renderer/frame_interfaces',
476 'device/usb/public/interfaces/chooser_service.mojom',
477 'device/usb/public/interfaces/device_manager.mojom',
478 'device/usb/public/interfaces/device.mojom',
479 'mojo/public/js/bindings',
480 'mojo/public/js/core',
481 'mojo/public/js/router',
482 'mojo/public/js/support',
483 ], (frameInterfaces, chooserService, deviceManager, device,
484 bindings, core, router, support) => {
485 delete window.define; // Clean up.
486
487 mojo = {
488 frameInterfaces: frameInterfaces,
489 chooserService: chooserService,
490 deviceManager: deviceManager,
491 device: device,
492 bindings: bindings,
493 core: core,
494 router: router,
495 support: support
496 };
497
498 g_deviceManager = new FakeDeviceManager();
499 mojo.frameInterfaces.addInterfaceOverrideForTesting(
500 mojo.deviceManager.DeviceManager.name,
501 handle => g_deviceManager.addBinding(handle));
502
503 g_chooserService = new FakeChooserService();
504 mojo.frameInterfaces.addInterfaceOverrideForTesting(
505 mojo.chooserService.ChooserService.name,
506 handle => g_chooserService.addBinding(handle));
507
508 addEventListener('unload', () => {
509 mojo.frameInterfaces.clearInterfaceOverridesForTesting();
510 });
511
512 resolve();
513 });
514 });
515 }
516
517 return g_initializePromise;
518 }
519
520 attachToWindow(otherWindow) {
521 if (!g_deviceManager || !g_chooserService)
522 throw new Error('Call initialize() before attachToWindow().');
523
524 return new Promise(resolve => {
525 otherWindow.gin.define(
526 'WebUSB Test Frame Attach', [
527 'content/public/renderer/frame_interfaces'
528 ], frameInterfaces => {
529 frameInterfaces.addInterfaceOverrideForTesting(
530 mojo.deviceManager.DeviceManager.name,
531 handle => g_deviceManager.addBinding(handle));
532 frameInterfaces.addInterfaceOverrideForTesting(
533 mojo.chooserService.ChooserService.name,
534 handle => g_chooserService.addBinding(handle));
535 resolve();
536 });
537 });
538 }
539
540 addFakeDevice(deviceInit) {
541 if (!g_deviceManager)
542 throw new Error('Call initialize() before addFakeDevice().');
543
544 // |addDevice| and |removeDevice| are called in a setTimeout callback so
545 // that tests do not rely on the device being immediately available which
546 // may not be true for all implementations of this test API.
547 let fakeDevice = new FakeUSBDevice();
548 setTimeout(() => g_deviceManager.addDevice(fakeDevice, deviceInit), 0);
549 return fakeDevice;
550 }
551
552 set ondeviceclose(func) {
553 g_closeListener = func;
554 }
555
556 set chosenDevice(fakeDevice) {
557 if (!g_chooserService)
558 throw new Error('Call initialize() before setting chosenDevice.');
559
560 g_chooserService.setChosenDevice(fakeDevice);
561 }
562
563 get lastFilters() {
564 if (!g_chooserService)
565 throw new Error('Call initialize() before getting lastFilters.');
566
567 return g_chooserService.lastFilters_;
568 }
569
570 reset() {
571 if (!g_deviceManager || !g_chooserService)
572 throw new Error('Call initialize() before reset().');
573
574 g_deviceManager.removeAllDevices();
575 g_chooserService.setChosenDevice(null);
576 g_closeListener = null;
577 }
578 }
579
580 navigator.usb.test = new USBTest();
581
582 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698