Update web-platform-tests to revision 10168e9a5d44efbc6e7d416d1d454eb9c9f1396c

This commit is contained in:
Josh Matthews 2018-01-31 09:13:41 -05:00
parent c88dc51d03
commit 0e1caebaf4
791 changed files with 23381 additions and 5501 deletions

View file

@ -43,14 +43,29 @@ function makeVideo() {
});
}
function makeImage() {
return new Promise(resolve => {
var img = new Image();
img.onload = function() {
resolve(img);
};
img.src = "/images/pattern.png";
});
function makeMakeHTMLImage(src) {
return function() {
return new Promise(resolve => {
var img = new Image();
img.onload = function() {
resolve(img);
};
img.src = src;
});
}
}
function makeMakeSVGImage(src) {
return function() {
return new Promise((resolve, reject) => {
var image = document.createElementNS(NAMESPACES.svg, "image");
image.onload = () => resolve(image);
image.onerror = reject;
image.setAttribute("externalResourcesRequired", "true");
image.setAttributeNS(NAMESPACES.xlink, 'xlink:href', src);
document.body.appendChild(image);
});
}
}
function makeImageData() {
@ -100,7 +115,10 @@ function makeBlob() {
var imageSourceTypes = [
{ name: 'an HTMLCanvasElement', factory: makeCanvas },
{ name: 'an HTMLVideoElement', factory: makeVideo },
{ name: 'an HTMLImageElement', factory: makeImage },
{ name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.png") },
{ name: 'a vector HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.svg") },
{ name: 'a bitmap SVGImageElement', factory: makeMakeSVGImage("/images/pattern.png") },
{ name: 'a vector SVGImageElement', factory: makeMakeSVGImage("/images/pattern.svg") },
{ name: 'an OffscreenCanvas', factory: makeOffscreenCanvas },
{ name: 'an ImageData', factory: makeImageData },
{ name: 'an ImageBitmap', factory: makeImageBitmap },

View file

@ -5,7 +5,8 @@
<script src="/resources/testharnessreport.js"></script>
<script src="/common/canvas-tests.js"></script>
<script src="/common/media.js"></script>
<script src="common.js"></script>
<script src="/common/namespaces.js"></script>
<script src="common.sub.js"></script>
<link rel="stylesheet" href="/common/canvas-tests.css">
<body>
<script>

View file

@ -2,7 +2,8 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/media.js"></script>
<script src="common.js"></script>
<script src="/common/namespaces.js"></script>
<script src="common.sub.js"></script>
<script>
function makeOversizedCanvas() {
@ -29,17 +30,18 @@ function makeInvalidBlob() {
}
function makeBrokenImage() {
return new Promise(resolve => {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = "data:,x";
image.onload = reject;
image.onerror = () => resolve(image);
});
}
function makeAvailableButBrokenImage() {
function makeAvailableButBrokenImage(path) {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = "/images/broken.png";
image.src = path;
image.onload = () => resolve(image);
image.onerror = reject;
});
@ -99,6 +101,26 @@ promise_test( t => {
return promise_rejects(t, new TypeError(), createImageBitmap(null));
}, "createImageBitmap with null image source.");
promise_test( t => {
var context = document.createElement("canvas").getContext("2d");
return promise_rejects(t, new TypeError(), createImageBitmap(context));
}, "createImageBitmap with CanvasRenderingContext2D image source.");
promise_test( t => {
var context = document.createElement("canvas").getContext("webgl");
return promise_rejects(t, new TypeError(), createImageBitmap(context));
}, "createImageBitmap with WebGLRenderingContext image source.");
promise_test( t => {
var buffer = new Uint8Array();
return promise_rejects(t, new TypeError(), createImageBitmap(buffer));
}, "createImageBitmap with Uint8Array image source.");
promise_test( t => {
var buffer = new ArrayBuffer(8);
return promise_rejects(t, new TypeError(), createImageBitmap(buffer));
}, "createImageBitmap with ArrayBuffer image source.");
promise_test( t => {
return promise_rejects(t, "InvalidStateError",
createImageBitmap(new Image()));
@ -138,12 +160,26 @@ promise_test( t => {
}, "createImageBitmap with a broken image source.");
promise_test( t => {
return makeAvailableButBrokenImage().then(image => {
return makeAvailableButBrokenImage("/images/broken.png").then(image => {
return promise_rejects(t, "InvalidStateError",
createImageBitmap(image));
});
}, "createImageBitmap with an available but undecodable image source.");
promise_test( t => {
return makeAvailableButBrokenImage("/images/red-zeroheight.svg").then(image => {
return promise_rejects(t, "InvalidStateError",
createImageBitmap(image));
});
}, "createImageBitmap with an available but zero height image source.");
promise_test( t => {
return makeAvailableButBrokenImage("/images/red-zerowidth.svg").then(image => {
return promise_rejects(t, "InvalidStateError",
createImageBitmap(image));
});
}, "createImageBitmap with an available but zero width image source.");
promise_test( t => {
return makeImageBitmap().then(bitmap => {
bitmap.close()

View file

@ -0,0 +1,86 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>createImageBitmap: origin-clean flag</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/media.js"></script>
<script src="/common/namespaces.js"></script>
<div id=log></div>
<script>
const crossOriginImageUrl = "http://{{domains[www1]}}:{{ports[http][0]}}/images/red.png";
function assert_origin_unclean(bitmap) {
const context = document.createElement("canvas").getContext("2d");
context.drawImage(bitmap, 0, 0);
assert_throws("SecurityError", () => {
context.getImageData(0, 0, 1, 1);
});
}
function makeImage() {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = reject;
image.src = crossOriginImageUrl;
});
}
const arguments = [
{
name: "cross-origin HTMLImageElement",
factory: makeImage,
},
{
name: "cross-origin SVGImageElement",
factory: () => {
return new Promise((resolve, reject) => {
const image = document.createElementNS(NAMESPACES.svg, "image");
image.onload = () => resolve(image);
image.onerror = reject;
image.setAttribute("externalResourcesRequired", "true");
image.setAttributeNS(NAMESPACES.xlink, 'xlink:href', crossOriginImageUrl);
document.body.appendChild(image);
});
},
},
{
name: "cross-origin HTMLVideoElement",
factory: () => {
return new Promise((resolve, reject) => {
const video = document.createElement("video");
video.oncanplaythrough = () => resolve(video);
video.onerror = reject;
video.src = getVideoURI("http://{{domains[www1]}}:{{ports[http][0]}}/media/movie_300");
});
},
},
{
name: "unclean HTMLCanvasElement",
factory: () => {
return makeImage().then(image => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
return canvas;
});
},
},
{
name: "unclean ImageBitmap",
factory: () => {
return makeImage().then(createImageBitmap);
},
},
];
for (let { name, factory } of arguments) {
promise_test(function() {
return factory().then(createImageBitmap).then(assert_origin_unclean);
}, name);
}
</script>

View file

@ -0,0 +1,27 @@
<!doctype html>
<title>Acid3 numbered tests</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
var tests = undefined;
function gotMessage(e) {
var m = e.data;
if (tests === undefined && "num_tests" in m) {
tests = [];
for (var i = 0; i < m.num_tests; i++) {
tests.push(async_test("Test " + i));
}
} else if ("result" in m) {
var test = m.test;
var passed = m.result === "pass";
var message = m.message;
tests[test].step(function() {
assert_true(passed, message);
});
tests[test].done();
}
}
window.addEventListener("message", gotMessage, false);
</script>
<iframe src="test.html"></iframe>

View file

@ -3419,6 +3419,7 @@
}
];
window.parent.postMessage({num_tests: tests.length}, "*");
var log = '';
var delay = 10;
var score = 0, index = 0, retry = 0, errors = 0;
@ -3456,6 +3457,7 @@
} else {
fail("no error message");
}
window.parent.postMessage({test: index, result: "pass"}, "*");
} catch (e) {
var s;
if (e.message)
@ -3464,6 +3466,7 @@
s = e;
errors += 1;
log += "Test " + zeroPaddedIndex + " failed: " + s + "\n";
window.parent.postMessage({test: index, result: "fail", message: s}, "*");
};
retry = 0;
index += 1;

View file

@ -24,16 +24,25 @@ test(function() {
}, "Verify calling 'navigator.sendBeacon()' with a URL that is not a http(s) scheme throws an exception.");
// We'll validate that we can send one beacon that uses our entire Quota and then fail to send one that is just one char.
test(function () {
var destinationURL = "/fetch/api/resources/trickle.py?count=1&ms=1000";
var firstSuccess = navigator.sendBeacon(destinationURL, maxPayload);
assert_true(firstSuccess, "calling 'navigator.sendBeacon()' with our max payload size should succeed.");
promise_test(async () => {
function wait(ms) {
return new Promise(res => step_timeout(res, ms));
}
const url = '/fetch/api/resources/trickle.py?count=1&ms=0';
assert_true(navigator.sendBeacon(url, maxPayload),
"calling 'navigator.sendBeacon()' with our max payload size should succeed.");
// Now we'll send just one character.
var secondSuccess = navigator.sendBeacon(destinationURL, "1");
assert_false(secondSuccess, "calling 'navigator.sendBeacon()' with just one char should fail while our Quota is used up.");
assert_false(navigator.sendBeacon(url, '1'),
"calling 'navigator.sendBeacon()' with just one char should fail while our Quota is used up.");
}, "Verify calling 'navigator.sendBeacon()' with a small payload fails while Quota is completely utilized.");
for (let i = 0; i < 20; ++i) {
await wait(100);
if (navigator.sendBeacon(url, maxPayload)) {
return;
}
}
assert_unreached('The quota should recover after fetching.');
}, "Verify the behavior after the quota is exhausted.");
done();

View file

@ -9,6 +9,19 @@ the API to be loaded as needed.
The Chromium implementation is provided by
`../resources/chromium/web-bluetooth-test.js`.
The Chromium implementation is not included in stable Chrome builds since it
would add too much to the binary size. On Chromium infrastructure, it is run
using the `content_shell` executable.
In the future, Chromium `src/device/bluetooth` may be refactored into a Mojo
service. At this point, it would be possible to add the necessary testing hooks
into stable Chrome without substantially increasing the binary size, similar to
WebUSB.
These bluetooth tests are upstreamed here because other browsers can reuse them
by implementing the [Web Bluetooth Testing API], even if only on their internal
infrastructure.
[Web Bluetooth Testing API]: https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY/
# Generated gen-* files from generator.py
@ -45,4 +58,4 @@ Usage:
$ python generate.py
```
More details documented in `generate.py`.
More details documented in `generate.py`.

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Reject with SecurityError if requesting a blocklisted ' +
'service.';
const expected = new DOMException(
'requestDevice() called with a filter containing a blocklisted UUID. ' +
'https://goo.gl/4NeimX',
'SecurityError');
bluetooth_test(() => setUpPreconnectedDevice({
knownServiceUUIDs: ['human_interface_device']
})
.then(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({
filters: [{services: ['human_interface_device']}]
}),
expected, 'Requesting blocklisted service rejects.')),
test_desc);
</script>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Blocklisted UUID in optionalServices is removed and ' +
'access not granted.';
const expected = new DOMException('Origin is not allowed to access the ' +
'service. Tip: Add the service UUID to \'optionalServices\' in ' +
'requestDevice() options. https://goo.gl/HxfxSQ',
'SecurityError');
let device, fake_peripheral;
bluetooth_test(() => getDiscoveredHealthThermometerDevice({
filters: [{services: ['health_thermometer']}],
optionalServices: ['human_interface_device']
})
.then(_ => ({device, fake_peripheral} = _))
.then(() =>
fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS}))
.then(() => device.gatt.connect())
.then(() =>
fake_peripheral.setNextGATTDiscoveryResponse({code: HCI_SUCCESS}))
.then(() => Promise.all([
assert_promise_rejects_with_message(
device.gatt.getPrimaryService('human_interface_device'),
expected, 'Blocklisted service not accessible.'),
assert_promise_rejects_with_message(
device.gatt.getPrimaryServices('human_interface_device'),
expected, 'Blocklisted services not accessible.')])),
test_desc);
</script>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A device name between 29 and 248 bytes is valid.';
const DEVICE_NAME = 'a_device_name_that_is_longer_than_29_bytes_but_' +
'shorter_than_248_bytes';
bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
.then(() => requestDeviceWithTrustedClick({
filters: [{name: DEVICE_NAME}]
}))
.then(device => assert_equals(device.name, DEVICE_NAME)),
test_desc);
</script>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A filter must restrict the devices in some way.';
const expected = new TypeError();
bluetooth_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({filters: [{}]}),
expected),
test_desc);
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'An empty |filters| member should result in a TypeError';
const expected = new DOMException('Failed to execute \'requestDevice\' on ' +
'\'Bluetooth\': \'filters\' member must be non-empty to find any devices.',
new TypeError());
bluetooth_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({filters: []}),
expected),
test_desc);
</script>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'requestDevice with empty namePrefix. ' +
'Should reject with TypeError.';
const expected = new TypeError();
const test_specs = [{
filters: [{ namePrefix: ''}]
}, {
filters: [{ namePrefix: '', name: 'Name'}]
}, {
filters: [{ namePrefix: '', services: ['heart_rate']}]
}, {
filters: [{ namePrefix: '', name: 'Name', services: ['heart_rate']}]
}, {
filters: [{ namePrefix: ''}],
optionalServices: ['heart_rate']
}, {
filters: [{ namePrefix: '', name: 'Name'}],
optionalServices: ['heart_rate']
}, {
filters: [{ namePrefix: '', services: ['heart_rate']}],
optionalServices: ['heart_rate']
}, {
filters: [{ namePrefix: '', name: 'Name', services: ['heart_rate']}],
optionalServices: ['heart_rate']
}];
bluetooth_test(() => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises
.then(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick(args),
expected));
});
return test_promises;
}, test_desc);
</script>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Services member must contain at least one service.';
const expected = new TypeError();
bluetooth_test(() => {
let test_promises = Promise.resolve();
generateRequestDeviceArgsWithServices([]).forEach(args => {
test_promises = test_promises.then(() =>
assert_promise_rejects_with_message(
requestDeviceWithTrustedClick(args),
expected,
'Services member must contain at least one service'))
});
return test_promises;
}, test_desc);
</script>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = "RequestDeviceOptions should have exactly one of " +
"'filters' or 'acceptAllDevices:true'. Reject with TypeError if not.";
const expected = new DOMException(
"Failed to execute 'requestDevice' on 'Bluetooth': " +
"Either 'filters' should be present or " +
"'acceptAllDevices' should be true, but not both.",
new TypeError());
const test_specs = [
{},
{optionalServices: ['heart_rate']},
{filters: [], acceptAllDevices: true},
{filters: [], acceptAllDevices: true, optionalServices: ['heart_rate']}
];
bluetooth_test(() => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises
.then(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick(args),
expected))
});
return test_promises;
}, test_desc);
</script>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Unicode string with utf8 representation longer than 248 ' +
'bytes in \'name\' must throw TypeError.';
const expected = new DOMException(
"Failed to execute 'requestDevice' on 'Bluetooth': " +
"A device name can't be longer than 248 bytes.",
new TypeError());
// \u2764's UTF-8 respresentation is 3 bytes long.
// 83 chars * 3 bytes/char = 249 bytes
const unicode_name = '\u2764'.repeat(83);
bluetooth_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({filters: [{name: unicode_name}]}),
expected),
test_desc);
</script>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A device name longer than 248 must reject.';
const expected = new DOMException(
"Failed to execute 'requestDevice' on 'Bluetooth': A device " +
"name can't be longer than 248 bytes.",
new TypeError());
const name_too_long = 'a'.repeat(249);
bluetooth_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({filters: [{name: name_too_long}]}),
expected,
'Device name longer than 248'),
test_desc);
</script>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Unicode string with utf8 representation longer than 248 ' +
'bytes in \'namePrefix\' must throw NotFoundError.';
const expected = new DOMException(
"Failed to execute 'requestDevice' on 'Bluetooth': " +
"A device name can't be longer than 248 bytes.",
new TypeError());
// \u2764's UTF-8 respresentation is 3 bytes long.
// 83 chars * 3 bytes/char = 249 bytes
const unicode_name = '\u2764'.repeat(83);
bluetooth_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({filters: [{namePrefix: unicode_name}]}),
expected),
test_desc);
</script>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A device name prefix longer than 248 must reject.';
const expected = new DOMException(
"Failed to execute 'requestDevice' on 'Bluetooth': A device " +
"name can't be longer than 248 bytes.",
new TypeError());
const name_too_long = 'a'.repeat(249);
bluetooth_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({filters: [{namePrefix: name_too_long}]}),
expected,
'Device name longer than 248'),
test_desc);
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A unicode device name of 248 bytes is valid.';
// \u00A1's UTF-8 respresentation is 2 bytes long.
// 124 chars * 2 bytes/char = 248 bytes
const DEVICE_NAME = '\u00A1'.repeat(124);
bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
.then(() => requestDeviceWithTrustedClick({ filters: [{name: DEVICE_NAME}]}))
.then(device => assert_equals(device.name, DEVICE_NAME)),
test_desc);
</script>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A device name of 248 bytes is valid.';
const DEVICE_NAME = 'a'.repeat(248);
bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
.then(() => requestDeviceWithTrustedClick({ filters: [{name: DEVICE_NAME}]}))
.then(device => assert_equals(device.name, DEVICE_NAME)),
test_desc);
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A unicode device namePrefix of 248 bytes is valid.';
// \u00A1's UTF-8 respresentation is 2 bytes long.
// 124 chars * 2 bytes/char = 248 bytes
const DEVICE_NAME = '\u00A1'.repeat(124);
bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
.then(() => requestDeviceWithTrustedClick({
filters: [{namePrefix: DEVICE_NAME}]
}))
.then(device => assert_equals(device.name, DEVICE_NAME)),
test_desc);
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A device namePrefix of 248 bytes is valid.';
const DEVICE_NAME = 'a'.repeat(248);
bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
.then(() => requestDeviceWithTrustedClick({
filters: [{namePrefix: DEVICE_NAME}]
}))
.then(device => assert_equals(device.name, DEVICE_NAME)),
test_desc);
</script>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'requestDevice() requires an argument.';
const expected = new TypeError();
promise_test(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick(),
expected),
test_desc);
</script>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A name containing unicode characters whose utf8 length ' +
'is less than 30 must not throw an error.';
// \u2764's UTF-8 representation is 3 bytes long.
// 9 chars * 3 bytes/char = 27 bytes
const valid_unicode_name = '\u2764'.repeat(9);
bluetooth_test(() => setUpPreconnectedDevice({name: valid_unicode_name})
.then(() => requestDeviceWithTrustedClick({
filters: [{name: valid_unicode_name}]
}))
.then(device => assert_equals(device.name, valid_unicode_name)),
test_desc);
</script>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'A namePrefix containing unicode characters whose utf8 ' +
'length is less than 30 must not throw an error.';
// \u2764's UTF-8 representation is 3 bytes long.
// 9 chars * 3 bytes/char = 27 bytes
const valid_unicode_name = '\u2764'.repeat(9);
bluetooth_test(() => setUpPreconnectedDevice({name: valid_unicode_name})
.then(() => requestDeviceWithTrustedClick({
filters: [{namePrefix: valid_unicode_name}]
}))
.then(device => assert_equals(device.name, valid_unicode_name)),
test_desc);
</script>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Invalid optional service must reject the promise.';
const expected = new TypeError();
const test_specs = [{
optionalServices: ['wrong_service'],
filters: [{services: ['heart_rate']}]
}, {
optionalServices: ['wrong_service'],
filters: [{ services: ['heart_rate'], name: 'Name'}]
}, {
optionalServices: ['wrong_service'],
filters: [{ services: ['heart_rate'], namePrefix: 'Pre'}]
}, {
optionalServices: ['wrong_service'],
filters: [{ services: ['heart_rate'], name: 'Name', namePrefix: 'Pre'}]
}, {
optionalServices: ['wrong_service'],
filters: [{ name: 'Name'}]
}, {
optionalServices: ['wrong_service'],
filters: [{ name: 'Name', namePrefix: 'Pre'}]
}, {
optionalServices: ['wrong_service'],
filters: [{ namePrefix: 'Pre'}]
}];
bluetooth_test(() => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises =
test_promises.then(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick(args),
expected));
});
return test_promises;
}, test_desc);
</script>

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Invalid service must reject the promise.';
const expected = new TypeError();
bluetooth_test(() => {
let test_promises = Promise.resolve();
generateRequestDeviceArgsWithServices(['wrong_service']).forEach(args => {
test_promises = test_promises.then(() =>
assert_promise_rejects_with_message(
requestDeviceWithTrustedClick(args),
expected));
});
return test_promises;
}, test_desc);
</script>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Consumes a user gesture.';
const expected = new DOMException(
'Must be handling a user gesture to show a permission request.',
'SecurityError');
bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
.then(() => callWithTrustedClick(() => {
let first = navigator.bluetooth.requestDevice({
filters: [{services: ['heart_rate']}]});
let second = navigator.bluetooth.requestDevice({
filters: [{services: ['heart_rate']}]});
return Promise.all([
first.then(device => assert_equals(
device.constructor.name, 'BluetoothDevice')),
assert_promise_rejects_with_message(second,
expected, 'A request should consume a user gesture')
]);
})), test_desc);
</script>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Discover a device using alias, name, or UUID.';
bluetooth_test(() => getHealthThermometerDevice()
// Chrome will always close the previous chooser in the process of handling
// a user gesture for the next request, so these need to be done
// sequentially.
.then(() => requestDeviceWithTrustedClick({
filters: [{services: [health_thermometer.alias]}]
}))
.then(device => assert_equals(device.constructor.name, 'BluetoothDevice'))
.then(() => requestDeviceWithTrustedClick({
filters: [{services: [health_thermometer.name]}]
}))
.then(device => assert_equals(device.constructor.name, 'BluetoothDevice'))
.then(() => requestDeviceWithTrustedClick({
filters: [{services: [health_thermometer.uuid]}]
}))
.then(device => assert_equals(device.constructor.name, 'BluetoothDevice')),
test_desc);
</script>

View file

@ -0,0 +1,67 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Matches a filter if all present members match.';
let matching_services = [health_thermometer.uuid];
let matching_name = 'Health Thermometer';
let matching_namePrefix = 'Health';
let test_specs = [{
filters: [{
services: matching_services,
}]
}, {
filters: [{
services: matching_services,
name: matching_name,
}]
}, {
filters: [{
services: matching_services,
namePrefix: matching_namePrefix
}]
}, {
filters: [{
name: matching_name,
}],
optionalServices: matching_services
}, {
filters: [{
name: matching_name,
namePrefix: matching_namePrefix
}],
optionalServices: matching_services
}, {
filters: [{
namePrefix: matching_namePrefix
}],
optionalServices: matching_services
}, {
filters: [{
services: matching_services,
name: matching_name,
namePrefix: matching_namePrefix
}]
}];
bluetooth_test(() => setUpHealthThermometerDevice()
.then(() => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises
.then(() => requestDeviceWithTrustedClick(args))
.then(device => {
// We always have access to the services in matching_services
// because we include them in a filter or in optionalServices.
assert_equals(device.name, matching_name);
assert_true(device.name.startsWith(matching_namePrefix));
});
});
return test_promises;
}), test_desc);
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Reject with NotFoundError if Bluetooth is not supported.';
const expected = new DOMException('Bluetooth Low Energy not available.',
'NotFoundError');
bluetooth_test(() => navigator.bluetooth.test.setLESupported(false)
.then(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({acceptAllDevices: true}),
expected, 'Bluetooth Low Energy is not supported.')),
test_desc);
</script>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'An empty name device can be obtained by empty name filter.'
bluetooth_test(() => setUpPreconnectedDevice({name: ''})
.then(() => requestDeviceWithTrustedClick({filters: [{name: ''}]}))
.then(device => assert_equals(device.name, '')),
test_desc);
</script>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Requires a user gesture.';
const expected = new DOMException(
'Must be handling a user gesture to show a permission request.',
'SecurityError');
bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
.then(() => assert_promise_rejects_with_message(
navigator.bluetooth.requestDevice({filters:[{services:['heart_rate']}]}),
expected, 'User gesture is required')),
test_desc);
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Reject with NotFoundError if there is no BT radio present.';
const expected = new DOMException('Bluetooth adapter not available.',
'NotFoundError');
bluetooth_test(() => navigator.bluetooth.test.simulateCentral({state: 'absent'})
.then(() => assert_promise_rejects_with_message(
requestDeviceWithTrustedClick({
filters: [{services: ['generic_access']}]
}),
expected, 'Bluetooth adapter is not present.')),
test_desc);
</script>

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Concurrent requestDevice calls in iframes work.';
const iframes = [];
for (let i = 0; i < 5; i++) {
iframes.push(document.createElement('iframe'));
}
bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
// 1. Load the iframes.
.then(() => {
let promises = [];
for (let iframe of iframes) {
iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
document.body.appendChild(iframe);
promises.push(new Promise(resolve =>
iframe.addEventListener('load', resolve)));
}
return Promise.all(promises);
})
// 2. Request the device from the iframes.
.then(() => new Promise(async (resolve) => {
let numMessages = 0;
window.onmessage = messageEvent => {
assert_equals(messageEvent.data, 'Success');
if (++numMessages === iframes.length) {
resolve();
}
}
for (let iframe of iframes) {
await callWithTrustedClick(() => iframe.contentWindow.postMessage({
type: 'RequestDevice'
}, '*'));
}
})), test_desc);
</script>

View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<body>
<script>
'use strict';
const test_desc = 'Request device from a unique origin. ' +
'Should reject with SecurityError.';
const expected = 'SecurityError: requestDevice() called from cross-origin ' +
'iframe.';
let iframe = document.createElement('iframe');
bluetooth_test(() => getHealthThermometerDevice()
// 1. Load the iframe.
.then(() => new Promise(resolve => {
iframe.sandbox.add('allow-scripts');
iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
document.body.appendChild(iframe);
iframe.addEventListener('load', resolve);
}))
// 2. Request the device from the iframe.
.then(() => new Promise(resolve => {
callWithTrustedClick(() => {
iframe.contentWindow.postMessage({
type: 'RequestDevice'
}, '*');
});
window.onmessage = messageEvent => {
assert_equals(messageEvent.data, expected);
resolve();
}
})), test_desc);
</script>
</body>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Returned device should always be the same.';
let devices = [];
let push = device => devices.push(device);
bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
.then(() => requestDeviceWithTrustedClick({
filters: [{services: [heart_rate.alias]}]
}))
.then(push)
.then(() => requestDeviceWithTrustedClick({
filters: [{services: [heart_rate.name]}]
}))
.then(push)
.then(() => requestDeviceWithTrustedClick({
filters: [{services: [heart_rate.uuid]}]
}))
.then(push)
.then(() => {
assert_equals(devices[0], devices[1]);
assert_equals(devices[1], devices[2]);
}), test_desc);
</script>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Simple filter selects matching device.';
bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
.then(() => requestDeviceWithTrustedClick({
filters: [{services: ['health_thermometer']}]
}))
.then(device => assert_equals(device.name, 'Health Thermometer')),
test_desc);
</script>

View file

@ -218,22 +218,36 @@ var gatt_errors_tests = [{
'NotSupportedError')
}];
function callWithTrustedClick(callback) {
// Waits until the document has finished loading.
function waitForDocumentReady() {
return new Promise(resolve => {
let button = document.createElement('button');
button.textContent = 'click to continue test';
button.style.display = 'block';
button.style.fontSize = '20px';
button.style.padding = '10px';
button.onclick = () => {
document.body.removeChild(button);
resolve(callback());
};
document.body.appendChild(button);
test_driver.click(button);
if (document.readyState === 'complete') {
resolve();
}
window.addEventListener('load', () => {
resolve();
}, {once: true});
});
}
function callWithTrustedClick(callback) {
return waitForDocumentReady()
.then(() => new Promise(resolve => {
let button = document.createElement('button');
button.textContent = 'click to continue test';
button.style.display = 'block';
button.style.fontSize = '20px';
button.style.padding = '10px';
button.onclick = () => {
document.body.removeChild(button);
resolve(callback());
};
document.body.appendChild(button);
test_driver.click(button);
}));
}
// Calls requestDevice() in a context that's 'allowed to show a popup'.
function requestDeviceWithTrustedClick() {
let args = arguments;

View file

@ -1,17 +1,31 @@
<!DOCTYPE html>
<script>
let device;
let device, gatt;
function requestDeviceWithOptionsAndConnect(options) {
return navigator.bluetooth.requestDevice(options)
.then(device => device.gatt.connect());
}
window.onmessage = messageEvent => {
window.addEventListener('message', (messageEvent) => {
switch (messageEvent.data.type) {
case 'RequestDevice':
navigator.bluetooth.requestDevice({
filters: [{services: ['generic_access']}]
})
.then(device => {
if (device.constructor.name === 'BluetoothDevice') {
parent.postMessage('Success', '*');
} else {
parent.postMessage(
`FAIL: requestDevice in iframe returned ${device.name}`, '*');
}}).catch(err => parent.postMessage(`${err.name}: ${err.message}`,
'*'));
break;
case 'RequestAndConnect':
requestDeviceWithOptionsAndConnect(messageEvent.data.options)
.then(gatt => {
.then(_ => {
gatt = _;
device = gatt.device;
parent.postMessage('Connected', '*');
}).catch(err => {
@ -26,9 +40,18 @@ window.onmessage = messageEvent => {
parent.postMessage(`FAIL: ${err}`, '*');
});
break;
case 'GetService':
if (typeof gatt === 'undefined') {
parent.postMessage('FAIL: no GATT server', '*');
break;
}
gatt.getPrimaryService(messageEvent.data.options)
.then(() => parent.postMessage('ServiceReceived', '*'))
.catch(err => parent.postMessage(`FAIL: ${err}`, '*'));
break;
default:
parent.postMessage(`FAIL: Bad message type: ${messageEvent.data.type}`,
'*');
'*');
}
};
});
</script>

View file

@ -0,0 +1,19 @@
'use strict';
const test_desc = 'Serial Number String characteristic is blocklisted. ' +
'Should reject with SecurityError.';
const expected = new DOMException(
'getCharacteristic(s) called with blocklisted UUID. https://goo.gl/4NeimX',
'SecurityError');
bluetooth_test(() => getHIDDevice({
filters: [{services: ['device_information']}]
})
.then(({device}) => device.gatt.getPrimaryService('device_information'))
.then(service => assert_promise_rejects_with_message(
service.CALLS([
getCharacteristic('serial_number_string')|
getCharacteristics('serial_number_string')[UUID]
]),
expected,
'Serial Number String characteristic is blocklisted.')),
test_desc);

View file

@ -0,0 +1,15 @@
'use strict';
const test_desc = 'Request for absent characteristics with UUID. ' +
'Reject with NotFoundError.';
bluetooth_test(() => getEmptyHealthThermometerService()
.then(({service}) => assert_promise_rejects_with_message(
service.CALLS([
getCharacteristic('battery_level')|
getCharacteristics('battery_level')[UUID]
]),
new DOMException(
`No Characteristics matching UUID ${battery_level.uuid} found ` +
`in Service with UUID ${health_thermometer.uuid}.`,
'NotFoundError'))),
test_desc);

View file

@ -0,0 +1,24 @@
'use strict';
const test_desc = 'Garbage Collection ran during FUNCTION_NAME ' +
'call that fails. Should not crash';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let promise;
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => {
promise = assert_promise_rejects_with_message(
service.CALLS([
getCharacteristic('measurement_interval')|
getCharacteristics()|
getCharacteristics('measurement_interval')[UUID]
]), expected);
// Disconnect called to clear attributeInstanceMap and allow the object to
// get garbage collected.
service.device.gatt.disconnect();
})
.then(runGarbageCollection)
.then(() => promise),
test_desc);

View file

@ -0,0 +1,24 @@
'use strict';
const test_desc = 'Calls to FUNCTION_NAME should return the same object.';
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => Promise.all([
service.CALLS([
getCharacteristic('measurement_interval')|
getCharacteristics()|
getCharacteristics('measurement_interval')[UUID]]),
service.PREVIOUS_CALL]))
.then(([characteristics_first_call, characteristics_second_call]) => {
// Convert to arrays if necessary.
characteristics_first_call = [].concat(characteristics_first_call);
characteristics_second_call = [].concat(characteristics_second_call);
let first_call_set = new Set(characteristics_first_call);
assert_equals(characteristics_first_call.length, first_call_set.size);
let second_call_set = new Set(characteristics_second_call);
assert_equals(characteristics_second_call.length, second_call_set.size);
characteristics_first_call.forEach(characteristic => {
assert_true(second_call_set.has(characteristic));
});
}), test_desc);

View file

@ -0,0 +1,23 @@
'use strict';
const test_desc = 'Wrong Characteristic name. Reject with TypeError.';
const expected = new DOMException(
"Failed to execute 'FUNCTION_NAME' on " +
"'BluetoothRemoteGATTService': Invalid Characteristic name: " +
"'wrong_name'. " +
"It must be a valid UUID alias (e.g. 0x1234), " +
"UUID (lowercase hex characters e.g. " +
"'00001234-0000-1000-8000-00805f9b34fb'), " +
"or recognized standard name from " +
"https://www.bluetooth.com/specifications/gatt/characteristics" +
" e.g. 'aerobic_heart_rate_lower_limit'.",
'TypeError');
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => assert_promise_rejects_with_message(
service.CALLS([
getCharacteristic('wrong_name')|
getCharacteristics('wrong_name')
]),
expected,
'Wrong Characteristic name passed.')),
test_desc);

View file

@ -0,0 +1,36 @@
'use strict';
const test_desc = 'disconnect() and connect() called during ' +
'FUNCTION_NAME. Reject with NetworkError.';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let device;
bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
filters: [{services: [health_thermometer.name]}],
})
.then(_ => ({device} = _))
.then(() => device.gatt.getPrimaryService(health_thermometer.name))
.then(service => Promise.all([
// 1. Make a call to service.FUNCTION_NAME, while the service is still
// valid.
assert_promise_rejects_with_message(service.CALLS([
getCharacteristic(measurement_interval.name)|
getCharacteristics()|
getCharacteristics(measurement_interval.name)[UUID]
]), expected),
// 2. disconnect() and connect before the initial call completes.
// This is accomplished by making the calls without waiting for the
// earlier promises to resolve.
// connect() guarantees on OS-level connection, but disconnect()
// only disconnects the current instance.
// getHealthThermometerDeviceWithServicesDiscovered holds another
// connection in an iframe, so disconnect() and connect() are certain to
// reconnect. However, disconnect() will invalidate the service object so
// the subsequent calls made to it will fail, even after reconnecting.
device.gatt.disconnect(),
device.gatt.connect()
])),
test_desc);

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for service. Should return right service';
let device;
bluetooth_test(() => getHealthThermometerDevice({
filters: [{services: ['health_thermometer']}],
optionalServices: ['generic_access']
})
.then(_ => ({device} = _))
.then(() => Promise.all([
device.gatt.getPrimaryService(generic_access.alias),
device.gatt.getPrimaryService(generic_access.name),
device.gatt.getPrimaryService(generic_access.uuid)]))
.then(services => {
services.forEach(service => {
assert_equals(service.uuid, generic_access.uuid,
'Service UUID should be the same as requested UUID.');
assert_true(service.isPrimary,
'getPrimaryService should return a primary service.');
assert_equals(service.device, device,
'Service device should be the same as device.');
})
}), test_desc);
</script>

View file

@ -0,0 +1,80 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
"use strict";
const test_desc = 'Two iframes in the same origin should be able to access ' +
'each other\'s services';
const iframe1 = document.createElement('iframe');
const iframe2 = document.createElement('iframe');
function add_iframe(iframe) {
let promise = new Promise(resolve => iframe.addEventListener('load', resolve));
iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
document.body.appendChild(iframe);
return promise;
}
function send_message(iframe, command, arg, assert_func) {
let promise = new Promise((resolve, reject) => {
window.addEventListener('message', (messageEvent) => {
try {
assert_func(messageEvent.data);
} catch (e) {
reject(e);
}
resolve();
}, { once: true });
});
if (command === 'RequestAndConnect') {
arg = {filters: [{services: [arg]}]};
}
callWithTrustedClick(() => iframe.contentWindow.postMessage({
type: command,
options: arg,
}, '*'));
return promise;
}
bluetooth_test(() => getHealthThermometerDevice()
// 1. Add the first iframe.
.then(() => add_iframe(iframe1))
// 2. Connect with the first iframe, requesting the health thermometer
// service.
.then(() => send_message(iframe1, 'RequestAndConnect', 'health_thermometer',
msg => assert_equals(msg, 'Connected')))
// 3. Access the health thermometer service with the first iframe
// (successfully).
.then(() => send_message(iframe1, 'GetService', 'health_thermometer',
msg => assert_equals(msg, 'ServiceReceived')))
// 4. Access the generic access service with the first iframe
// (unsuccessfully).
.then(() => send_message(iframe1, 'GetService', 'generic_access', msg => {
let split_msg = msg.split(': ');
assert_equals(split_msg[0], 'FAIL');
assert_equals(split_msg[1], 'SecurityError');
}))
// 5. Add the second iframe.
.then(() => add_iframe(iframe2))
// 6. Connect with the second iframe, requesting the generic access service.
.then(() => send_message(iframe2, 'RequestAndConnect', 'generic_access',
msg => assert_equals(msg, 'Connected')))
// 7. Access the health thermometer service with the second iframe
// (successfully). Both iframes should have access to both services at this
// point since they have the same origin.
.then(() => send_message(iframe2, 'GetService', 'health_thermometer',
msg => assert_equals(msg, 'ServiceReceived')))
// 8. Access the generic access service with the second iframe
// (unsuccessfully).
.then(() => send_message(iframe2, 'GetService', 'generic_access',
msg => assert_equals(msg, 'ServiceReceived')))
// 9. Access the generic access service with the first iframe
// (successfully).
.then(() => send_message(iframe1, 'GetService', 'generic_access',
msg => assert_equals(msg, 'ServiceReceived'))),
test_desc);
</script>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for services. Does not return blocklisted service.';
const expected = new DOMException(
'Origin is not allowed to access the service. Tip: Add the service ' +
'UUID to \'optionalServices\' in requestDevice() options. ' +
'https://goo.gl/HxfxSQ', 'SecurityError');
bluetooth_test(() => getHIDDevice({
filters: [{services: ['device_information']}],
optionalServices: ['human_interface_device']
})
.then(({device}) => assert_promise_rejects_with_message(
device.gatt.getPrimaryServices('human_interface_device'),
expected)),
test_desc);
</script>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for services. Does not return blocklisted service.';
bluetooth_test(() => getHIDDevice({
filters: [{services: ['device_information']}],
optionalServices: ['generic_access', 'human_interface_device']
})
.then(({device}) => device.gatt.getPrimaryServices())
.then(services => {
assert_equals(services.length, 2);
let uuid_set = new Set(services.map(s => s.uuid));
assert_equals(uuid_set.size, 2);
assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
assert_true(uuid_set.has(BluetoothUUID.getService('device_information')));
assert_false(
uuid_set.has(BluetoothUUID.getService('human_interface_device')));
}), test_desc);
</script>

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Find correct services with UUID.';
let device, fake_peripheral;
bluetooth_test(() => getConnectedHealthThermometerDevice({
filters: [{services: ['health_thermometer']}]
})
.then(_ => ({device, fake_peripheral} = _))
.then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
.then(fake_service => Promise.all([
fake_service.addFakeCharacteristic({
uuid: 'temperature_measurement', properties: ['indicate']}),
fake_service.addFakeCharacteristic({
uuid: 'temperature_measurement', properties: ['indicate']})
]))
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({code:HCI_SUCCESS}))
.then(() => device.gatt.getPrimaryServices('health_thermometer'))
.then(services => Promise.all([
services[0].getCharacteristics(),
services[1].getCharacteristics()]))
.then(([characteristics1, characteristics2]) => {
if (characteristics1.length === 2)
assert_equals(characteristics2.length, 3);
else if (characteristics2.length === 2)
assert_equals(characteristics1.length, 3);
else
assert_unreached('Invalid lengths.');
}), test_desc);
</script>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for services. Should return right number of ' +
'services.';
bluetooth_test(() => getTwoHealthThermometerServicesDevice({
filters: [{services: ['health_thermometer']}]
})
.then(({device}) => Promise.all([
device.gatt.getPrimaryServices(health_thermometer.alias),
device.gatt.getPrimaryServices(health_thermometer.name),
device.gatt.getPrimaryServices(health_thermometer.uuid)]))
.then(services_arrays => services_arrays.forEach(services => {
assert_equals(services.length, 2);
services.forEach(service => {
assert_equals(service.uuid,
BluetoothUUID.getService('health_thermometer'));
assert_true(service.isPrimary);
});
})), test_desc);
</script>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Find all services in a device.';
bluetooth_test(() => getTwoHealthThermometerServicesDevice({
filters: [{services: ['health_thermometer']}],
optionalServices: ['generic_access']
})
.then(({device}) => device.gatt.getPrimaryServices())
.then(services => {
// Expect three service instances.
assert_equals(services.length, 3);
services.forEach(s => assert_true(s.isPrimary));
let uuid_set = new Set(services.map(s => s.uuid));
// Two of the expected services are 'health_thermometer', so
// only 2 unique UUIDs.
assert_equals(uuid_set.size, 2);
assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
assert_true(uuid_set.has(BluetoothUUID.getService('health_thermometer')));
}), test_desc);
</script>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for services in a device with no services. Reject ' +
'with NotFoundError.';
const expected = new DOMException('No Services found in device.',
'NotFoundError');
bluetooth_test(() => getEmptyHealthThermometerDevice()
.then(({device}) => assert_promise_rejects_with_message(
device.gatt.getPrimaryServices(),
expected)),
test_desc);
</script>

View file

@ -0,0 +1,26 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Serial Number String characteristic is blocklisted. ' +
'Should reject with SecurityError.';
const expected = new DOMException(
'getCharacteristic(s) called with blocklisted UUID. https://goo.gl/4NeimX',
'SecurityError');
bluetooth_test(() => getHIDDevice({
filters: [{services: ['device_information']}]
})
.then(({device}) => device.gatt.getPrimaryService('device_information'))
.then(service => assert_promise_rejects_with_message(
service.getCharacteristic('serial_number_string'),
expected,
'Serial Number String characteristic is blocklisted.')),
test_desc);
</script>

View file

@ -0,0 +1,22 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for absent characteristics with UUID. ' +
'Reject with NotFoundError.';
bluetooth_test(() => getEmptyHealthThermometerService()
.then(({service}) => assert_promise_rejects_with_message(
service.getCharacteristic('battery_level'),
new DOMException(
`No Characteristics matching UUID ${battery_level.uuid} found ` +
`in Service with UUID ${health_thermometer.uuid}.`,
'NotFoundError'))),
test_desc);
</script>

View file

@ -0,0 +1,30 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Garbage Collection ran during getCharacteristic ' +
'call that fails. Should not crash';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let promise;
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => {
promise = assert_promise_rejects_with_message(
service.getCharacteristic('measurement_interval'), expected);
// Disconnect called to clear attributeInstanceMap and allow the object to
// get garbage collected.
service.device.gatt.disconnect();
})
.then(runGarbageCollection)
.then(() => promise),
test_desc);
</script>

View file

@ -0,0 +1,31 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Calls to getCharacteristic should return the same object.';
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => Promise.all([
service.getCharacteristic('measurement_interval'),
service.getCharacteristic('measurement_interval')]))
.then(([characteristics_first_call, characteristics_second_call]) => {
// Convert to arrays if necessary.
characteristics_first_call = [].concat(characteristics_first_call);
characteristics_second_call = [].concat(characteristics_second_call);
let first_call_set = new Set(characteristics_first_call);
assert_equals(characteristics_first_call.length, first_call_set.size);
let second_call_set = new Set(characteristics_second_call);
assert_equals(characteristics_second_call.length, second_call_set.size);
characteristics_first_call.forEach(characteristic => {
assert_true(second_call_set.has(characteristic));
});
}), test_desc);
</script>

View file

@ -0,0 +1,30 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Wrong Characteristic name. Reject with TypeError.';
const expected = new DOMException(
"Failed to execute 'getCharacteristic' on " +
"'BluetoothRemoteGATTService': Invalid Characteristic name: " +
"'wrong_name'. " +
"It must be a valid UUID alias (e.g. 0x1234), " +
"UUID (lowercase hex characters e.g. " +
"'00001234-0000-1000-8000-00805f9b34fb'), " +
"or recognized standard name from " +
"https://www.bluetooth.com/specifications/gatt/characteristics" +
" e.g. 'aerobic_heart_rate_lower_limit'.",
'TypeError');
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => assert_promise_rejects_with_message(
service.getCharacteristic('wrong_name'),
expected,
'Wrong Characteristic name passed.')),
test_desc);
</script>

View file

@ -0,0 +1,42 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'disconnect() and connect() called during ' +
'getCharacteristic. Reject with NetworkError.';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let device;
bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
filters: [{services: [health_thermometer.name]}],
})
.then(_ => ({device} = _))
.then(() => device.gatt.getPrimaryService(health_thermometer.name))
.then(service => Promise.all([
// 1. Make a call to service.getCharacteristic, while the service is still
// valid.
assert_promise_rejects_with_message(service.getCharacteristic(measurement_interval.name), expected),
// 2. disconnect() and connect before the initial call completes.
// This is accomplished by making the calls without waiting for the
// earlier promises to resolve.
// connect() guarantees on OS-level connection, but disconnect()
// only disconnects the current instance.
// getHealthThermometerDeviceWithServicesDiscovered holds another
// connection in an iframe, so disconnect() and connect() are certain to
// reconnect. However, disconnect() will invalidate the service object so
// the subsequent calls made to it will fail, even after reconnecting.
device.gatt.disconnect(),
device.gatt.connect()
])),
test_desc);
</script>

View file

@ -0,0 +1,26 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Serial Number String characteristic is blocklisted. ' +
'Should reject with SecurityError.';
const expected = new DOMException(
'getCharacteristic(s) called with blocklisted UUID. https://goo.gl/4NeimX',
'SecurityError');
bluetooth_test(() => getHIDDevice({
filters: [{services: ['device_information']}]
})
.then(({device}) => device.gatt.getPrimaryService('device_information'))
.then(service => assert_promise_rejects_with_message(
service.getCharacteristics('serial_number_string'),
expected,
'Serial Number String characteristic is blocklisted.')),
test_desc);
</script>

View file

@ -0,0 +1,22 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Request for absent characteristics with UUID. ' +
'Reject with NotFoundError.';
bluetooth_test(() => getEmptyHealthThermometerService()
.then(({service}) => assert_promise_rejects_with_message(
service.getCharacteristics('battery_level'),
new DOMException(
`No Characteristics matching UUID ${battery_level.uuid} found ` +
`in Service with UUID ${health_thermometer.uuid}.`,
'NotFoundError'))),
test_desc);
</script>

View file

@ -0,0 +1,30 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Garbage Collection ran during getCharacteristics ' +
'call that fails. Should not crash';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let promise;
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => {
promise = assert_promise_rejects_with_message(
service.getCharacteristics('measurement_interval'), expected);
// Disconnect called to clear attributeInstanceMap and allow the object to
// get garbage collected.
service.device.gatt.disconnect();
})
.then(runGarbageCollection)
.then(() => promise),
test_desc);
</script>

View file

@ -0,0 +1,30 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Garbage Collection ran during getCharacteristics ' +
'call that fails. Should not crash';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let promise;
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => {
promise = assert_promise_rejects_with_message(
service.getCharacteristics(), expected);
// Disconnect called to clear attributeInstanceMap and allow the object to
// get garbage collected.
service.device.gatt.disconnect();
})
.then(runGarbageCollection)
.then(() => promise),
test_desc);
</script>

View file

@ -0,0 +1,31 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Calls to getCharacteristics should return the same object.';
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => Promise.all([
service.getCharacteristics('measurement_interval'),
service.getCharacteristics('measurement_interval')]))
.then(([characteristics_first_call, characteristics_second_call]) => {
// Convert to arrays if necessary.
characteristics_first_call = [].concat(characteristics_first_call);
characteristics_second_call = [].concat(characteristics_second_call);
let first_call_set = new Set(characteristics_first_call);
assert_equals(characteristics_first_call.length, first_call_set.size);
let second_call_set = new Set(characteristics_second_call);
assert_equals(characteristics_second_call.length, second_call_set.size);
characteristics_first_call.forEach(characteristic => {
assert_true(second_call_set.has(characteristic));
});
}), test_desc);
</script>

View file

@ -0,0 +1,31 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Calls to getCharacteristics should return the same object.';
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => Promise.all([
service.getCharacteristics(),
service.getCharacteristics()]))
.then(([characteristics_first_call, characteristics_second_call]) => {
// Convert to arrays if necessary.
characteristics_first_call = [].concat(characteristics_first_call);
characteristics_second_call = [].concat(characteristics_second_call);
let first_call_set = new Set(characteristics_first_call);
assert_equals(characteristics_first_call.length, first_call_set.size);
let second_call_set = new Set(characteristics_second_call);
assert_equals(characteristics_second_call.length, second_call_set.size);
characteristics_first_call.forEach(characteristic => {
assert_true(second_call_set.has(characteristic));
});
}), test_desc);
</script>

View file

@ -0,0 +1,30 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'Wrong Characteristic name. Reject with TypeError.';
const expected = new DOMException(
"Failed to execute 'getCharacteristics' on " +
"'BluetoothRemoteGATTService': Invalid Characteristic name: " +
"'wrong_name'. " +
"It must be a valid UUID alias (e.g. 0x1234), " +
"UUID (lowercase hex characters e.g. " +
"'00001234-0000-1000-8000-00805f9b34fb'), " +
"or recognized standard name from " +
"https://www.bluetooth.com/specifications/gatt/characteristics" +
" e.g. 'aerobic_heart_rate_lower_limit'.",
'TypeError');
bluetooth_test(() => getHealthThermometerService()
.then(({service}) => assert_promise_rejects_with_message(
service.getCharacteristics('wrong_name'),
expected,
'Wrong Characteristic name passed.')),
test_desc);
</script>

View file

@ -0,0 +1,42 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'disconnect() and connect() called during ' +
'getCharacteristics. Reject with NetworkError.';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let device;
bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
filters: [{services: [health_thermometer.name]}],
})
.then(_ => ({device} = _))
.then(() => device.gatt.getPrimaryService(health_thermometer.name))
.then(service => Promise.all([
// 1. Make a call to service.getCharacteristics, while the service is still
// valid.
assert_promise_rejects_with_message(service.getCharacteristics(measurement_interval.name), expected),
// 2. disconnect() and connect before the initial call completes.
// This is accomplished by making the calls without waiting for the
// earlier promises to resolve.
// connect() guarantees on OS-level connection, but disconnect()
// only disconnects the current instance.
// getHealthThermometerDeviceWithServicesDiscovered holds another
// connection in an iframe, so disconnect() and connect() are certain to
// reconnect. However, disconnect() will invalidate the service object so
// the subsequent calls made to it will fail, even after reconnecting.
device.gatt.disconnect(),
device.gatt.connect()
])),
test_desc);
</script>

View file

@ -0,0 +1,42 @@
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
const test_desc = 'disconnect() and connect() called during ' +
'getCharacteristics. Reject with NetworkError.';
const expected = new DOMException(
'GATT Server is disconnected. Cannot retrieve characteristics. ' +
'(Re)connect first with `device.gatt.connect`.',
'NetworkError');
let device;
bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
filters: [{services: [health_thermometer.name]}],
})
.then(_ => ({device} = _))
.then(() => device.gatt.getPrimaryService(health_thermometer.name))
.then(service => Promise.all([
// 1. Make a call to service.getCharacteristics, while the service is still
// valid.
assert_promise_rejects_with_message(service.getCharacteristics(), expected),
// 2. disconnect() and connect before the initial call completes.
// This is accomplished by making the calls without waiting for the
// earlier promises to resolve.
// connect() guarantees on OS-level connection, but disconnect()
// only disconnects the current instance.
// getHealthThermometerDeviceWithServicesDiscovered holds another
// connection in an iframe, so disconnect() and connect() are certain to
// reconnect. However, disconnect() will invalidate the service object so
// the subsequent calls made to it will fail, even after reconnecting.
device.gatt.disconnect(),
device.gatt.connect()
])),
test_desc);
</script>

View file

@ -0,0 +1,4 @@
var NAMESPACES = {
"svg": "http://www.w3.org/2000/svg",
"xlink": "http://www.w3.org/1999/xlink",
};

View file

@ -50,7 +50,9 @@
{ "name": "Wrong value of `csp` should not trigger sending Sec-Required-CSP Header - report-uri present",
"csp": "script-src 'unsafe-inline'; report-uri resources/dummy-report.php",
"expected": null },
// TODO(andypaicu): when `report-to` is implemented, add tests here.
{ "name": "Wrong value of `csp` should not trigger sending Sec-Required-CSP Header - report-to present",
"csp": "script-src 'unsafe-inline'; report-to resources/dummy-report.php",
"expected": null },
];
tests.forEach(test => {

View file

@ -2,6 +2,6 @@ Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: reporting-api-doesnt-send-reports-without-violation={{$id:uuid()}}; Path=/content-security-policy/reporting
Set-Cookie: reporting-api-doesnt-send-reports-without-violation={{$id:uuid()}}; Path=/content-security-policy/reporting-api
Report-To: { "url": "https://{{host}}:{{ports[https][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id}}", "group": "csp-group", "max-age": 10886400 }
Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'self'; report-to csp-group

View file

@ -2,6 +2,6 @@ Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: reporting-api-report-only-sends-reports-on-violation={{$id:uuid()}}; Path=/content-security-policy/reporting
Set-Cookie: reporting-api-report-only-sends-reports-on-violation={{$id:uuid()}}; Path=/content-security-policy/reporting-api
Report-To: { "url": "https://{{host}}:{{ports[https][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id}}", "group": "csp-group", "max-age": 10886400 }
Content-Security-Policy-Report-Only: script-src 'self' 'unsafe-inline'; img-src 'none'; report-to csp-group

View file

@ -2,6 +2,6 @@ Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: reporting-api-report-to-overrides-report-uri-1={{$id:uuid()}}; Path=/content-security-policy/reporting
Set-Cookie: reporting-api-report-to-overrides-report-uri-1={{$id:uuid()}}; Path=/content-security-policy/reporting-api
Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'none'; report-uri "/content-security-policy/support/report.py?op=put&reportID={{$id}}"; report-to csp-group
Report-To: { "url": "https://{{host}}:{{ports[https][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id:uuid()}}", "group": "csp-group", "max-age": 10886400 }

View file

@ -2,6 +2,6 @@ Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: reporting-api-report-to-overrides-report-uri-2={{$id:uuid()}}; Path=/content-security-policy/reporting
Set-Cookie: reporting-api-report-to-overrides-report-uri-2={{$id:uuid()}}; Path=/content-security-policy/reporting-api
Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'none'; report-to csp-group; report-uri "/content-security-policy/support/report.py?op=put&reportID={{$id}}"
Report-To: { "url": "https://{{host}}:{{ports[https][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id:uuid()}}", "group": "csp-group", "max-age": 10886400 }

View file

@ -2,6 +2,6 @@ Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: reporting-api-sends-reports-on-violation={{$id:uuid()}}; Path=/content-security-policy/reporting
Set-Cookie: reporting-api-sends-reports-on-violation={{$id:uuid()}}; Path=/content-security-policy/reporting-api
Report-To: { "url": "https://{{host}}:{{ports[https][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id}}", "group": "csp-group", "max-age": 10886400 }
Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'none'; report-to csp-group

View file

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test that reports using the report-api service are sent when there's a violation</title>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
</head>
<body>
<script>
async_test(function(t2) {
window.addEventListener("securitypolicyviolation", t2.step_func(function(e) {
assert_equals(e.blockedURI, "{{location[scheme]}}://{{location[host]}}/content-security-policy/support/fail.html");
assert_equals(e.violatedDirective, "frame-src");
t2.done();
}));
}, "Event is fired");
</script>
<iframe src="../support/fail.html"></iframe>
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-src%20%27none%27'></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Set-Cookie: reporting-api-works-on-frame-src={{$id:uuid()}}; Path=/content-security-policy/reporting-api
Report-To: { "url": "https://{{host}}:{{ports[https][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id}}", "group": "csp-group", "max-age": 10886400 }
Content-Security-Policy: script-src 'self' 'unsafe-inline'; frame-src 'none'; report-to csp-group

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>When multiple report-uri endpoints for multiple policies are specified, each gets a report</title>
<!-- CSP headers
Content-Security-Policy-Report-Only: img-src http://* https://*; default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}
Content-Security-Policy-Report-Only: img-src http://*; default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}
-->
</head>
<body>
<img src="ftp://blah.test" />
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20http%3A%2F%2F%2A%20https%3A%2F%2F%2A&testName=1-Violation%20report%20status%20OK'></script>
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20http%3A%2F%2F%2A&reportCookieName=multiple-report-policies-2&testName=2-Violation%20report%20status%20OK'></script>
</body>
</html>

View file

@ -0,0 +1,8 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: multiple-report-policies={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy-Report-Only: img-src http://* https://*; default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}
Set-Cookie: multiple-report-policies-2={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy-Report-Only: img-src http://*; default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Reporting and enforcing policies can be different</title>
<!-- CSP headers
Content-Security-Policy: img-src 'none'; style-src *; script-src 'self' 'unsafe-inline'
Content-Security-Policy-Report-Only: img-src *; style-src 'none'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}
-->
</head>
<body>
<script>
var img_test = async_test("The image should be blocked");
var sheet_test = async_test("The stylesheet should load");
<!-- This image should be blocked, but should not generate a report-->
var i = document.createElement('img');
i.onerror = img_test.step_func_done();
i.onload = img_test.unreached_func("Should not have loaded the img");
i.src = "../support/fail.png";
document.body.appendChild(i);
<!-- This font should be loaded but should generate a report-->
var s = document.createElement('link');
s.onerror = sheet_test.unreached_func("Should have loaded the font");
s.onload = sheet_test.step_func_done();
s.type = "text/css";
s.rel="stylesheet";
s.href = "../support/fonts.css";
document.body.appendChild(s);
</script>
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=style-src%20%27none%27'></script>
</body>
</html>

View file

@ -0,0 +1,7 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: report-and-enforce={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy: img-src 'none'; style-src *; script-src 'self' 'unsafe-inline'
Content-Security-Policy-Report-Only: img-src *; style-src 'none'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Data-uri images are reported correctly</title>
<!-- CSP headers
Content-Security-Policy: img-src 'none'; report-uri ../support/report.py?op=put&reportID={{$id}}
-->
</head>
<body>
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20%27none%27'></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: report-blocked-data-uri={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy: img-src 'none'; report-uri ../support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Cross-origin images are reported correctly</title>
<!-- CSP headers
Content-Security-Policy: script-src 'self' 'unsafe-inline'
Content-Security-Policy-Report-Only: img-src 'none'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID=$id
-->
</head>
<body>
<img src="http://{{domains[www1]}}:{{ports[http][0]}}/content-security-policy/support/pass.png">
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20%27none%27'></script>
</body>
</html>

View file

@ -0,0 +1,7 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: report-blocked-uri-cross-origin={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy: script-src 'self' 'unsafe-inline'
Content-Security-Policy-Report-Only: img-src 'none'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Blocked relative images are reported correctly</title>
<!-- CSP headers
Content-Security-Policy: script-src 'self' 'unsafe-inline'
Content-Security-Policy-Report-Only: img-src 'none'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}
-->
</head>
<body>
<img src="../support/pass.png">
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20%27none%27'></script>
</body>
</html>

View file

@ -0,0 +1,7 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: report-blocked-uri={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy: script-src 'self' 'unsafe-inline'
Content-Security-Policy-Report-Only: img-src 'none'; script-src 'self' 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Cookies are not sent on cross origin violation reports</title>
<!-- CSP headers
Content-Security-Policy: script-src 'unsafe-inline' 'self'; img-src 'none'; report-uri http://{{domains[www1]}}:{{ports[http][0]}}/content-security-policy/support/report.py?op=put&reportID=$id
-->
</head>
<body>
<script>
var test = async_test("Image should not load");
fetch(
"/cookies/resources/set-cookie.py?name=cspViolationReportCookie1&path=" + encodeURIComponent("{{domains[www1]}}:{{ports[http][0]}}/"),
{mode: 'no-cors', credentials: 'include'})
.then(() => {
// This image will generate a CSP violation report.
const img = new Image();
img.onerror = test.step_func_done();
img.onload = test.unreached_func("Should not have loaded the image");
img.src = "../support/fail.png";
document.body.appendChild(img);
});
</script>
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20%27none%27&noCookies=true'></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: report-cross-origin-no-cookies={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy: script-src 'unsafe-inline' 'self'; img-src 'none'; report-uri http://{{domains[www1]}}:{{ports[http][0]}}/content-security-policy/support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Test multiple violations cause multiple reports</title>
<!-- CSP headers
Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'self'; img-src 'none'; report-uri ../support/report.py?op=put&reportID={{$id}}
-->
</head>
<body>
<img src="../support/pass.png">
<img src="../support/pass2.png">
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=img-src%20%27none%27&reportCount=2'></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: report-multiple-violations-01={{$id:uuid()}}; Path=/content-security-policy/reporting/
Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'self'; img-src 'none'; report-uri ../support/report.py?op=put&reportID={{$id}}

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>This tests that multiple violations on a page trigger multiple reports
if and only if the violations are distinct.</title>
<!-- CSP headers
Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'self'; report-uri ../support/report.py?op=put&reportID={{$id}}
-->
</head>
<body>
<script>
for (var i = 0; i<5; i++)
setTimeout("alert('PASS: setTimeout #" + i + " executed.');", 0);
</script>
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27unsafe-inline%27%20%27self%27&reportCount=1'></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more