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

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

Issue 2789723003: Migrate WebUSB LayoutTests into external/wpt (Closed)
Patch Set: Add README.md and more comments explaining the polyfill Created 3 years, 5 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 polyfill library implements the WebUSB Test API as specified here:
4 // https://wicg.github.io/webusb/test/
5
6 (() => {
7
8 // These variables are logically members of the USBTest class but are defined
9 // here to hide them from being visible as fields of navigator.usb.test.
10 let internal = {
11 intialized: false,
12
13 deviceManager: null,
14 deviceManagerInterceptor: null,
15 deviceManagerCrossFrameProxy: null,
16
17 chooser: null,
18 chooserInterceptor: null,
19 chooserCrossFrameProxy: null,
20 };
21
22 function fakeDeviceInitToDeviceInfo(guid, init) {
23 let deviceInfo = {
24 guid: guid + "",
25 usbVersionMajor: init.usbVersionMajor,
26 usbVersionMinor: init.usbVersionMinor,
27 usbVersionSubminor: init.usbVersionSubminor,
28 classCode: init.deviceClass,
29 subclassCode: init.deviceSubclass,
30 protocolCode: init.deviceProtocol,
31 vendorId: init.vendorId,
32 productId: init.productId,
33 deviceVersionMajor: init.deviceVersionMajor,
34 deviceVersionMinor: init.deviceVersionMinor,
35 deviceVersionSubminor: init.deviceVersionSubminor,
36 manufacturerName: init.manufacturerName,
37 productName: init.productName,
38 serialNumber: init.serialNumber,
39 activeConfiguration: init.activeConfigurationValue,
40 configurations: []
41 };
42 init.configurations.forEach(config => {
43 var configInfo = {
44 configurationValue: config.configurationValue,
45 configurationName: config.configurationName,
46 interfaces: []
47 };
48 config.interfaces.forEach(iface => {
49 var interfaceInfo = {
50 interfaceNumber: iface.interfaceNumber,
51 alternates: []
52 };
53 iface.alternates.forEach(alternate => {
54 var alternateInfo = {
55 alternateSetting: alternate.alternateSetting,
56 classCode: alternate.interfaceClass,
57 subclassCode: alternate.interfaceSubclass,
58 protocolCode: alternate.interfaceProtocol,
59 interfaceName: alternate.interfaceName,
60 endpoints: []
61 };
62 alternate.endpoints.forEach(endpoint => {
63 var endpointInfo = {
64 endpointNumber: endpoint.endpointNumber,
65 packetSize: endpoint.packetSize,
66 };
67 switch (endpoint.direction) {
68 case "in":
69 endpointInfo.direction = device.mojom.UsbTransferDirection.INBOUND;
70 break;
71 case "out":
72 endpointInfo.direction = device.mojom.UsbTransferDirection.OUTBOUND;
73 break;
74 }
75 switch (endpoint.type) {
76 case "bulk":
77 endpointInfo.type = device.mojom.UsbTransferType.BULK;
78 break;
79 case "interrupt":
80 endpointInfo.type = device.mojom.UsbTransferType.INTERRUPT;
81 break;
82 case "isochronous":
83 endpointInfo.type = device.mojom.UsbTransferType.ISOCHRONOUS;
84 break;
85 }
86 alternateInfo.endpoints.push(endpointInfo);
87 });
88 interfaceInfo.alternates.push(alternateInfo);
89 });
90 configInfo.interfaces.push(interfaceInfo);
91 });
92 deviceInfo.configurations.push(configInfo);
93 });
94 return deviceInfo;
95 }
96
97 function convertMojoDeviceFilters(input) {
98 let output = [];
99 input.forEach(filter => {
100 output.push(convertMojoDeviceFilter(filter));
101 });
102 return output;
103 }
104
105 function convertMojoDeviceFilter(input) {
106 let output = {};
107 if (input.hasVendorId)
108 output.vendorId = input.vendorId;
109 if (input.hasProductId)
110 output.productId = input.productId;
111 if (input.hasClassCode)
112 output.classCode = input.classCode;
113 if (input.hasSubclassCode)
114 output.subclassCode = input.subclassCode;
115 if (input.hasProtocolCode)
116 output.protocolCode = input.protocolCode;
117 if (input.serialNumber)
118 output.serialNumber = input.serialNumber;
119 return output;
120 }
121
122 class FakeDevice {
123 constructor(deviceInit) {
124 this.info_ = deviceInit;
125 this.opened_ = false;
126 this.currentConfiguration_ = null;
127 this.claimedInterfaces_ = new Map();
128 }
129
130 getConfiguration() {
131 if (this.currentConfiguration_) {
132 return Promise.resolve({
133 value: this.currentConfiguration_.configurationValue });
134 } else {
135 return Promise.resolve({ value: 0 });
136 }
137 }
138
139 open() {
140 assert_false(this.opened_);
141 this.opened_ = true;
142 return Promise.resolve({ error: device.mojom.UsbOpenDeviceError.OK });
143 }
144
145 close() {
146 assert_true(this.opened_);
147 this.opened_ = false;
148 return Promise.resolve();
149 }
150
151 setConfiguration(value) {
152 assert_true(this.opened_);
153
154 let selectedConfiguration = this.info_.configurations.find(
155 configuration => configuration.configurationValue == value);
156 // Blink should never request an invalid configuration.
157 assert_not_equals(selectedConfiguration, undefined);
158 this.currentConfiguration_ = selectedConfiguration;
159 return Promise.resolve({ success: true });
160 }
161
162 claimInterface(interfaceNumber) {
163 assert_true(this.opened_);
164 assert_false(this.currentConfiguration_ == null, 'device configured');
165 assert_false(this.claimedInterfaces_.has(interfaceNumber),
166 'interface already claimed');
167
168 // Blink should never request an invalid interface.
169 assert_true(this.currentConfiguration_.interfaces.some(
170 iface => iface.interfaceNumber == interfaceNumber));
171 this.claimedInterfaces_.set(interfaceNumber, 0);
172 return Promise.resolve({ success: true });
173 }
174
175 releaseInterface(interfaceNumber) {
176 assert_true(this.opened_);
177 assert_false(this.currentConfiguration_ == null, 'device configured');
178 assert_true(this.claimedInterfaces_.has(interfaceNumber));
179 this.claimedInterfaces_.delete(interfaceNumber);
180 return Promise.resolve({ success: true });
181 }
182
183 setInterfaceAlternateSetting(interfaceNumber, alternateSetting) {
184 assert_true(this.opened_);
185 assert_false(this.currentConfiguration_ == null, 'device configured');
186 assert_true(this.claimedInterfaces_.has(interfaceNumber));
187
188 let iface = this.currentConfiguration_.interfaces.find(
189 iface => iface.interfaceNumber == interfaceNumber);
190 // Blink should never request an invalid interface or alternate.
191 assert_false(iface == undefined);
192 assert_true(iface.alternates.some(
193 x => x.alternateSetting == alternateSetting));
194 this.claimedInterfaces_.set(interfaceNumber, alternateSetting);
195 return Promise.resolve({ success: true });
196 }
197
198 reset() {
199 assert_true(this.opened_);
200 return Promise.resolve({ success: true });
201 }
202
203 clearHalt(endpoint) {
204 assert_true(this.opened_);
205 assert_false(this.currentConfiguration_ == null, 'device configured');
206 // TODO(reillyg): Assert that endpoint is valid.
207 return Promise.resolve({ success: true });
208 }
209
210 controlTransferIn(params, length, timeout) {
211 assert_true(this.opened_);
212 assert_false(this.currentConfiguration_ == null, 'device configured');
213 return Promise.resolve({
214 status: device.mojom.UsbTransferStatus.OK,
215 data: [length >> 8, length & 0xff, params.request, params.value >> 8,
216 params.value & 0xff, params.index >> 8, params.index & 0xff]
217 });
218 }
219
220 controlTransferOut(params, data, timeout) {
221 assert_true(this.opened_);
222 assert_false(this.currentConfiguration_ == null, 'device configured');
223 return Promise.resolve({
224 status: device.mojom.UsbTransferStatus.OK,
225 bytesWritten: data.byteLength
226 });
227 }
228
229 genericTransferIn(endpointNumber, length, timeout) {
230 assert_true(this.opened_);
231 assert_false(this.currentConfiguration_ == null, 'device configured');
232 // TODO(reillyg): Assert that endpoint is valid.
233 let data = new Array(length);
234 for (let i = 0; i < length; ++i)
235 data[i] = i & 0xff;
236 return Promise.resolve({
237 status: device.mojom.UsbTransferStatus.OK,
238 data: data
239 });
240 }
241
242 genericTransferOut(endpointNumber, data, timeout) {
243 assert_true(this.opened_);
244 assert_false(this.currentConfiguration_ == null, 'device configured');
245 // TODO(reillyg): Assert that endpoint is valid.
246 return Promise.resolve({
247 status: device.mojom.UsbTransferStatus.OK,
248 bytesWritten: data.byteLength
249 });
250 }
251
252 isochronousTransferIn(endpointNumber, packetLengths, timeout) {
253 assert_true(this.opened_);
254 assert_false(this.currentConfiguration_ == null, 'device configured');
255 // TODO(reillyg): Assert that endpoint is valid.
256 let data = new Array(packetLengths.reduce((a, b) => a + b, 0));
257 let dataOffset = 0;
258 let packets = new Array(packetLengths.length);
259 for (let i = 0; i < packetLengths.length; ++i) {
260 for (let j = 0; j < packetLengths[i]; ++j)
261 data[dataOffset++] = j & 0xff;
262 packets[i] = {
263 length: packetLengths[i],
264 transferredLength: packetLengths[i],
265 status: device.mojom.UsbTransferStatus.OK
266 };
267 }
268 return Promise.resolve({ data: data, packets: packets });
269 }
270
271 isochronousTransferOut(endpointNumber, data, packetLengths, timeout) {
272 assert_true(this.opened_);
273 assert_false(this.currentConfiguration_ == null, 'device configured');
274 // TODO(reillyg): Assert that endpoint is valid.
275 let packets = new Array(packetLengths.length);
276 for (let i = 0; i < packetLengths.length; ++i) {
277 packets[i] = {
278 length: packetLengths[i],
279 transferredLength: packetLengths[i],
280 status: device.mojom.UsbTransferStatus.OK
281 };
282 }
283 return Promise.resolve({ packets: packets });
284 }
285 }
286
287 class FakeDeviceManager {
288 constructor() {
289 this.bindingSet_ = new mojo.BindingSet(device.mojom.UsbDeviceManager);
290 this.devices_ = new Map();
291 this.devicesByGuid_ = new Map();
292 this.client_ = null;
293 this.nextGuid_ = 0;
294 }
295
296 addBinding(handle) {
297 this.bindingSet_.addBinding(this, handle);
298 }
299
300 addDevice(fakeDevice, info) {
301 let device = {
302 fakeDevice: fakeDevice,
303 guid: (this.nextGuid_++).toString(),
304 info: info,
305 bindingArray: []
306 };
307 this.devices_.set(fakeDevice, device);
308 this.devicesByGuid_.set(device.guid, device);
309 if (this.client_)
310 this.client_.onDeviceAdded(fakeDeviceInitToDeviceInfo(device.guid, info));
311 }
312
313 removeDevice(fakeDevice) {
314 let device = this.devices_.get(fakeDevice);
315 if (!device)
316 throw new Error('Cannot remove unknown device.');
317
318 for (var binding of device.bindingArray)
319 binding.close();
320 this.devices_.delete(device.fakeDevice);
321 this.devicesByGuid_.delete(device.guid);
322 if (this.client_) {
323 this.client_.onDeviceRemoved(
324 fakeDeviceInitToDeviceInfo(device.guid, device.info));
325 }
326 }
327
328 removeAllDevices() {
329 this.devices_.forEach(device => {
330 for (var binding of device.bindingArray)
331 binding.close();
332 this.client_.onDeviceRemoved(
333 fakeDeviceInitToDeviceInfo(device.guid, device.info));
334 });
335 this.devices_.clear();
336 this.devicesByGuid_.clear();
337 }
338
339 getDevices(options) {
340 let devices = [];
341 this.devices_.forEach(device => {
342 devices.push(fakeDeviceInitToDeviceInfo(device.guid, device.info));
343 });
344 return Promise.resolve({ results: devices });
345 }
346
347 getDevice(guid, request) {
348 let device = this.devicesByGuid_.get(guid);
349 if (device) {
350 let binding = new mojo.Binding(
351 window.device.mojom.UsbDevice, new FakeDevice(device.info), request);
352 binding.setConnectionErrorHandler(() => {
353 if (device.fakeDevice.onclose)
354 device.fakeDevice.onclose();
355 });
356 device.bindingArray.push(binding);
357 } else {
358 request.close();
359 }
360 }
361
362 setClient(client) {
363 this.client_ = client;
364 }
365 }
366
367 class FakeChooserService {
368 constructor() {
369 this.bindingSet_ = new mojo.BindingSet(device.mojom.UsbChooserService);
370 this.chosenDevice_ = null;
371 this.lastFilters_ = null;
372 }
373
374 addBinding(handle) {
375 this.bindingSet_.addBinding(this, handle);
376 }
377
378 setChosenDevice(fakeDevice) {
379 this.chosenDevice_ = fakeDevice;
380 }
381
382 getPermission(deviceFilters) {
383 this.lastFilters_ = convertMojoDeviceFilters(deviceFilters);
384 let device = internal.deviceManager.devices_.get(this.chosenDevice_);
385 if (device) {
386 return Promise.resolve({
387 result: fakeDeviceInitToDeviceInfo(device.guid, device.info)
388 });
389 } else {
390 return Promise.resolve({ result: null });
391 }
392 }
393 }
394
395 // Unlike FakeDevice this class is exported to callers of USBTest.addFakeDevice.
396 class FakeUSBDevice {
397 constructor() {
398 this.onclose = null;
399 }
400
401 disconnect() {
402 setTimeout(() => internal.deviceManager.removeDevice(this), 0);
403 }
404 }
405
406 // A helper for forwarding MojoHandle instances from one frame to another.
407 class CrossFrameHandleProxy {
408 constructor(callback) {
409 let {handle0, handle1} = Mojo.createMessagePipe();
410 this.sender_ = handle0;
411 this.receiver_ = handle1;
412 this.receiver_.watch({readable: true}, () => {
413 let message = this.receiver_.readMessage();
414 assert_equals(message.buffer.byteLength, 0);
415 assert_equals(message.handles.length, 1);
416 callback(message.handles[0]);
417 });
418 }
419
420 forwardHandle(handle) {
421 this.sender_.writeMessage(new ArrayBuffer, [handle]);
422 }
423 }
424
425 class USBTest {
426 constructor() {}
427
428 initialize() {
429 if (internal.initialized)
430 return Promise.resolve();
431
432 internal.deviceManager = new FakeDeviceManager();
433 internal.deviceManagerInterceptor =
434 new MojoInterfaceInterceptor(device.mojom.UsbDeviceManager.name);
435 internal.deviceManagerInterceptor.oninterfacerequest =
436 e => internal.deviceManager.addBinding(e.handle);
437 internal.deviceManagerInterceptor.start();
438 internal.deviceManagerCrossFrameProxy = new CrossFrameHandleProxy(
439 handle => internal.deviceManager.addBinding(handle));
440
441 internal.chooser = new FakeChooserService();
442 internal.chooserInterceptor =
443 new MojoInterfaceInterceptor(device.mojom.UsbChooserService.name);
444 internal.chooserInterceptor.oninterfacerequest =
445 e => internal.chooser.addBinding(e.handle);
446 internal.chooserInterceptor.start();
447 internal.chooserCrossFrameProxy = new CrossFrameHandleProxy(
448 handle => internal.chooser.addBinding(handle));
449
450 internal.initialized = true;
451 return Promise.resolve();
452 }
453
454 attachToWindow(otherWindow) {
455 if (!internal.initialized)
456 throw new Error('Call initialize() before attachToWindow().');
457
458 otherWindow.deviceManagerInterceptor =
459 new otherWindow.MojoInterfaceInterceptor(
460 device.mojom.UsbDeviceManager.name);
461 otherWindow.deviceManagerInterceptor.oninterfacerequest =
462 e => internal.deviceManagerCrossFrameProxy.forwardHandle(e.handle);
463 otherWindow.deviceManagerInterceptor.start();
464
465 otherWindow.chooserInterceptor =
466 new otherWindow.MojoInterfaceInterceptor(
467 device.mojom.UsbChooserService.name);
468 otherWindow.chooserInterceptor.oninterfacerequest =
469 e => internal.chooserCrossFrameProxy.forwardHandle(e.handle);
470 otherWindow.chooserInterceptor.start();
471 return Promise.resolve();
472 }
473
474 addFakeDevice(deviceInit) {
475 if (!internal.initialized)
476 throw new Error('Call initialize() before addFakeDevice().');
477
478 // |addDevice| and |removeDevice| are called in a setTimeout callback so
479 // that tests do not rely on the device being immediately available which
480 // may not be true for all implementations of this test API.
481 let fakeDevice = new FakeUSBDevice();
482 setTimeout(
483 () => internal.deviceManager.addDevice(fakeDevice, deviceInit), 0);
484 return fakeDevice;
485 }
486
487 set chosenDevice(fakeDevice) {
488 if (!internal.initialized)
489 throw new Error('Call initialize() before setting chosenDevice.');
490
491 internal.chooser.setChosenDevice(fakeDevice);
492 }
493
494 get lastFilters() {
495 if (!internal.initialized)
496 throw new Error('Call initialize() before getting lastFilters.');
497
498 return internal.chooser.lastFilters_;
499 }
500
501 reset() {
502 if (!internal.initialized)
503 throw new Error('Call initialize() before reset().');
504
505 // Reset the mocks in a setTimeout callback so that tests do not rely on
506 // the fact that this polyfill can do this synchronously.
507 return new Promise(resolve => {
508 setTimeout(() => {
509 internal.deviceManager.removeAllDevices();
510 internal.chooser.setChosenDevice(null);
511 resolve();
512 }, 0);
513 });
514 }
515 }
516
517 navigator.usb.test = new USBTest();
518
519 })();
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/usb/resources/webusb-test.js ('k') | third_party/WebKit/LayoutTests/usb/test-polyfil.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698