Update web-platform-tests to revision 346d5b51a122f7bb1c7747064499ef281a0200f7

This commit is contained in:
Ms2ger 2016-06-24 10:13:49 +02:00
parent 581c8ba1c8
commit 79b1e6c40c
1728 changed files with 20243 additions and 5349 deletions

View file

@ -10,3 +10,4 @@ node_modules
scratch
testharness_runner.html
webdriver/.idea
.vscode/

View file

@ -5,7 +5,7 @@
<script src=/resources/testharnessreport.js></script>
<script>
test( function() {
test(function() {
var closedRange = IDBKeyRange.bound(5, 20);
assert_true(!!closedRange.includes, "IDBKeyRange has a .includes");
assert_true(closedRange.includes(7), "in range");
@ -17,17 +17,45 @@
"invalid key");
}, "IDBKeyRange.includes() with a closed range");
test( function() {
test(function() {
var openRange = IDBKeyRange.bound(5, 20, true, true);
assert_false(openRange.includes(5) || openRange.includes(20),
"boundary points");
}, "IDBKeyRange.includes() with an open range");
test( function() {
test(function() {
var range = IDBKeyRange.only(42);
assert_true(range.includes(42), "in range");
assert_false(range.includes(1), "below range");
assert_false(range.includes(9000), "above range");
}, "IDBKeyRange.includes() with an only range");
test(function() {
var range = IDBKeyRange.lowerBound(5);
assert_false(range.includes(4), 'value before closed lower bound');
assert_true(range.includes(5), 'value at closed lower bound');
assert_true(range.includes(6), 'value after closed lower bound');
}, "IDBKeyRange.includes() with an closed lower-bounded range");
test(function() {
var range = IDBKeyRange.lowerBound(5, true);
assert_false(range.includes(4), 'value before open lower bound');
assert_false(range.includes(5), 'value at open lower bound');
assert_true(range.includes(6), 'value after open lower bound');
}, "IDBKeyRange.includes() with an open lower-bounded range");
test(function() {
var range = IDBKeyRange.upperBound(5);
assert_true(range.includes(4), 'value before closed upper bound');
assert_true(range.includes(5), 'value at closed upper bound');
assert_false(range.includes(6), 'value after closed upper bound');
}, "IDBKeyRange.includes() with an closed upper-bounded range");
test(function() {
var range = IDBKeyRange.upperBound(5, true);
assert_true(range.includes(4), 'value before open upper bound');
assert_false(range.includes(5), 'value at open upper bound');
assert_false(range.includes(6), 'value after open upper bound');
}, "IDBKeyRange.includes() with an open upper-bounded range");
</script>

View file

@ -4,31 +4,10 @@
<link rel="help" href="http://w3c.github.io/IndexedDB/#dom-idbobjectstore-createindex">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support.js"></script>
<script>
function indexeddb_test(description, upgrade_func, open_func = null) {
async_test(function(t) {
var dbname = document.location + "-" + t.name;
var del = indexedDB.deleteDatabase(dbname);
del.onerror = t.unreached_func("deleteDatabase should succeed");
var open = indexedDB.open(dbname, 1);
open.onerror = t.unreached_func("open should succeed");
open.onupgradeneeded = t.step_func(function() {
var db = open.result;
var tx = open.transaction;
upgrade_func(t, db, tx);
});
if (open_func) {
open.onsuccess = t.step_func(function() {
var db = open.result;
open_func(t, db);
});
}
}, description);
}
indexeddb_test(
"InvalidStateError(Incorrect mode) vs. TransactionInactiveError",
function(t, db, txn) {
var store = db.createObjectStore("s");
},
@ -41,12 +20,12 @@ indexeddb_test(
}, "Mode check should precede state check of the transaction");
t.done();
};
}
},
"InvalidStateError(Incorrect mode) vs. TransactionInactiveError"
);
var gDeletedObjectStore;
indexeddb_test(
"InvalidStateError(Deleted ObjectStore) vs. TransactionInactiveError",
function(t, db, txn) {
gDeletedObjectStore = db.createObjectStore("s");
db.deleteObjectStore("s");
@ -56,11 +35,12 @@ indexeddb_test(
}, "Deletion check should precede transaction-state check");
t.done();
};
}
},
null,
"InvalidStateError(Deleted ObjectStore) vs. TransactionInactiveError"
);
indexeddb_test(
"TransactionInactiveError vs. ConstraintError",
function(t, db, txn) {
var store = db.createObjectStore("s");
store.createIndex("index", "foo");
@ -70,11 +50,12 @@ indexeddb_test(
}, "Transaction-state check should precede index name check");
t.done();
};
}
},
null,
"TransactionInactiveError vs. ConstraintError"
);
indexeddb_test(
"ConstraintError vs. SyntaxError",
function(t, db) {
var store = db.createObjectStore("s");
store.createIndex("index", "foo");
@ -86,11 +67,12 @@ indexeddb_test(
["invalid key path 1", "invalid key path 2"]);
}, "Index name check should precede syntax check of the key path");
t.done();
}
},
null,
"ConstraintError vs. SyntaxError"
);
indexeddb_test(
"SyntaxError vs. InvalidAccessError",
function(t, db) {
var store = db.createObjectStore("s");
assert_throws("SyntaxError", function() {
@ -99,7 +81,9 @@ indexeddb_test(
{ multiEntry: true });
}, "Syntax check should precede multiEntry check of the key path");
t.done();
}
},
null,
"SyntaxError vs. InvalidAccessError"
);
</script>

View file

@ -2,27 +2,9 @@
<title>IndexedDB: IDBTransaction.objectStoreNames attribute</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support.js"></script>
<script>
function indexeddb_test(upgrade_func, open_func, description) {
async_test(function(t) {
var dbname = document.location + '-' + t.name;
var del = indexedDB.deleteDatabase(dbname);
del.onerror = t.unreached_func('deleteDatabase should succeed');
var open = indexedDB.open(dbname, 1);
open.onerror = t.unreached_func('open should succeed');
open.onupgradeneeded = t.step_func(function() {
var db = open.result;
var tx = open.transaction;
upgrade_func(t, db, tx);
});
open.onsuccess = t.step_func(function() {
var db = open.result;
open_func(t, db);
});
}, description);
}
function with_stores_test(store_names, open_func, description) {
indexeddb_test(function(t, db, tx) {
store_names.forEach(function(name) {

View file

@ -101,3 +101,23 @@ function createdb_for_multiple_tests(dbname, version) {
function assert_key_equals(actual, expected, description) {
assert_equals(indexedDB.cmp(actual, expected), 0, description);
}
function indexeddb_test(upgrade_func, open_func, description) {
async_test(function(t) {
var dbname = document.location + '-' + t.name;
var del = indexedDB.deleteDatabase(dbname);
del.onerror = t.unreached_func('deleteDatabase should succeed');
var open = indexedDB.open(dbname, 1);
open.onerror = t.unreached_func('open should succeed');
open.onupgradeneeded = t.step_func(function() {
var db = open.result;
var tx = open.transaction;
upgrade_func(t, db, tx);
});
open.onsuccess = t.step_func(function() {
var db = open.result;
if (open_func)
open_func(t, db);
});
}, description);
}

View file

@ -0,0 +1,89 @@
<!DOCTYPE html>
<title>IndexedDB: Commit ordering of empty transactions</title>
<script src='../../resources/testharness.js'></script>
<script src='../../resources/testharnessreport.js'></script>
<script src='support.js'></script>
<script>
// Call with a test object and array of expected values. Returns a
// function to call with each actual value. Once the expected number
// of values is seen, asserts that the value orders match and completes
// the test.
function expect(t, expected) {
var results = [];
return result => {
results.push(result);
if (results.length === expected.length) {
assert_array_equals(results, expected);
t.done();
}
};
}
indexeddb_test(
(t, db) => {
db.createObjectStore('store');
},
(t, db) => {
var saw = expect(t, ['rq1.onsuccess',
'rq2.onsuccess',
'tx1.oncomplete',
'tx2.oncomplete']);
var tx1 = db.transaction('store', 'readwrite');
tx1.onabort = t.unreached_func('transaction should commit');
tx1.oncomplete = t.step_func(() => saw('tx1.oncomplete'));
var store = tx1.objectStore('store');
var rq1 = store.put('a', 1);
rq1.onerror = t.unreached_func('put should succeed');
rq1.onsuccess = t.step_func(() => {
saw('rq1.onsuccess');
var tx2 = db.transaction('store', 'readonly');
tx2.onabort = t.unreached_func('transaction should commit');
tx2.oncomplete = t.step_func(() => saw('tx2.oncomplete'));
var rq2 = store.put('b', 2);
rq2.onsuccess = t.step_func(() => saw('rq2.onsuccess'));
rq2.onerror = t.unreached_func('request should succeed');
});
},
'Transactions without requests complete in the expected order');
indexeddb_test(
(t, db) => {
db.createObjectStore('store');
},
(t, db) => {
var saw = expect(t, ['rq1.onsuccess',
'rq2.onsuccess',
'tx1.oncomplete',
'tx2.oncomplete',
'tx3.oncomplete']);
var tx1 = db.transaction('store', 'readwrite');
tx1.onabort = t.unreached_func('transaction should commit');
tx1.oncomplete = t.step_func(() => saw('tx1.oncomplete'));
var store = tx1.objectStore('store');
var rq1 = store.put('a', 1);
rq1.onerror = t.unreached_func('put should succeed');
rq1.onsuccess = t.step_func(() => {
saw('rq1.onsuccess');
var tx2 = db.transaction('store', 'readonly');
tx2.onabort = t.unreached_func('transaction should commit');
tx2.oncomplete = t.step_func(() => saw('tx2.oncomplete'));
var tx3 = db.transaction('store', 'readonly');
tx3.onabort = t.unreached_func('transaction should commit');
tx3.oncomplete = t.step_func(() => saw('tx3.oncomplete'));
var rq2 = store.put('b', 2);
rq2.onsuccess = t.step_func(() => saw('rq2.onsuccess'));
rq2.onerror = t.unreached_func('request should succeed');
});
},
'Multiple transactions without requests complete in the expected order');
</script>

View file

@ -0,0 +1,154 @@
function run_test() {
var subtle = crypto.subtle; // Change to test prefixed implementations
var sourceData = {
empty: new Uint8Array(0),
short: new Uint8Array([21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255]),
medium: new Uint8Array([182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140, 184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178, 227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107, 129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7, 131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37, 171, 172, 161, 147])
};
sourceData.long = new Uint8Array(1024 * sourceData.medium.byteLength);
for (var i=0; i<1024; i++) {
sourceData.long.set(sourceData.medium, i * sourceData.medium.byteLength);
}
var digestedData = {
"sha-1": {
empty: new Uint8Array([218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]),
short: new Uint8Array([201, 19, 24, 205, 242, 57, 106, 1, 94, 63, 78, 106, 134, 160, 186, 101, 184, 99, 89, 68]),
medium: new Uint8Array([229, 65, 6, 8, 112, 235, 22, 191, 51, 182, 142, 81, 245, 19, 82, 104, 147, 152, 103, 41]),
long: new Uint8Array([48, 152, 181, 0, 55, 236, 208, 46, 189, 101, 118, 83, 178, 191, 160, 30, 238, 39, 162, 234])
},
"sha-256": {
empty: new Uint8Array([227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85]),
short: new Uint8Array([162, 131, 17, 134, 152, 71, 146, 199, 211, 45, 89, 200, 151, 64, 104, 127, 25, 173, 220, 27, 149, 158, 113, 161, 204, 83, 138, 59, 126, 216, 67, 242]),
medium: new Uint8Array([83, 83, 103, 135, 126, 240, 20, 215, 252, 113, 126, 92, 183, 132, 62, 89, 182, 26, 238, 98, 199, 2, 156, 236, 126, 198, 193, 47, 217, 36, 224, 228]),
long: new Uint8Array([20, 205, 234, 157, 199, 95, 90, 98, 116, 217, 252, 30, 100, 0, 153, 18, 241, 220, 211, 6, 180, 143, 232, 233, 207, 18, 45, 230, 113, 87, 23, 129])
},
"sha-384": {
empty: new Uint8Array([56, 176, 96, 167, 81, 172, 150, 56, 76, 217, 50, 126, 177, 177, 227, 106, 33, 253, 183, 17, 20, 190, 7, 67, 76, 12, 199, 191, 99, 246, 225, 218, 39, 78, 222, 191, 231, 111, 101, 251, 213, 26, 210, 241, 72, 152, 185, 91]),
short: new Uint8Array([107, 245, 234, 101, 36, 209, 205, 220, 67, 247, 207, 59, 86, 238, 5, 146, 39, 64, 74, 47, 83, 143, 2, 42, 61, 183, 68, 122, 120, 44, 6, 193, 237, 5, 232, 171, 79, 94, 220, 23, 243, 113, 20, 64, 223, 233, 119, 49]),
medium: new Uint8Array([203, 194, 197, 136, 254, 91, 37, 249, 22, 218, 40, 180, 228, 122, 72, 74, 230, 252, 31, 228, 144, 45, 213, 201, 147, 154, 107, 253, 3, 74, 179, 180, 139, 57, 8, 116, 54, 1, 31, 106, 153, 135, 157, 39, 149, 64, 233, 119]),
long: new Uint8Array([73, 244, 253, 179, 152, 25, 104, 249, 125, 87, 55, 15, 133, 52, 80, 103, 205, 82, 150, 169, 125, 209, 161, 142, 6, 145, 30, 117, 110, 150, 8, 73, 37, 41, 135, 14, 26, 209, 48, 153, 141, 87, 203, 251, 183, 193, 208, 158])
},
"sha-512": {
empty: new Uint8Array([207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128, 7, 214, 32, 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233, 206, 71, 208, 209, 60, 93, 133, 242, 176, 255, 131, 24, 210, 135, 126, 236, 47, 99, 185, 49, 189, 71, 65, 122, 129, 165, 56, 50, 122, 249, 39, 218, 62]),
short: new Uint8Array([55, 82, 72, 190, 95, 243, 75, 231, 76, 171, 79, 241, 195, 188, 141, 198, 139, 213, 248, 223, 244, 2, 62, 152, 248, 123, 134, 92, 255, 44, 114, 66, 146, 223, 24, 148, 67, 166, 79, 244, 19, 74, 101, 205, 70, 53, 185, 212, 245, 220, 13, 63, 182, 117, 40, 0, 42, 99, 172, 242, 108, 157, 165, 117]),
medium: new Uint8Array([185, 16, 159, 131, 158, 142, 164, 60, 137, 15, 41, 60, 225, 29, 198, 226, 121, 141, 30, 36, 49, 241, 228, 185, 25, 227, 178, 12, 79, 54, 48, 59, 163, 156, 145, 109, 179, 6, 196, 90, 59, 101, 118, 31, 245, 190, 133, 50, 142, 234, 244, 44, 56, 48, 241, 217, 94, 122, 65, 22, 91, 125, 45, 54]),
long: new Uint8Array([75, 2, 202, 246, 80, 39, 96, 48, 234, 86, 23, 229, 151, 197, 213, 63, 217, 218, 166, 139, 120, 191, 230, 11, 34, 170, 184, 211, 106, 76, 42, 58, 255, 219, 113, 35, 79, 73, 39, 103, 55, 197, 117, 221, 247, 77, 20, 5, 76, 189, 111, 219, 152, 253, 13, 220, 188, 180, 111, 145, 173, 118, 182, 238])
},
}
// Try every combination of hash with source data size. Variations tested are
// hash name in upper, lower, or mixed case, and upper-case version with the
// source buffer altered after call.
Object.keys(sourceData).forEach(function(size) {
Object.keys(digestedData).forEach(function(alg) {
var upCase = alg.toUpperCase();
var downCase = alg.toLowerCase();
var mixedCase = upCase.substr(0, 1) + downCase.substr(1);
promise_test(function(test) {
var promise = subtle.digest({name: upCase}, sourceData[size])
.then(function(result) {
assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
}, function(err) {
assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
});
return promise;
}, upCase + " with " + size + " source data");
promise_test(function(test) {
var promise = subtle.digest({name: mixedCase}, sourceData[size])
.then(function(result) {
assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
}, function(err) {
assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);mixedCase
});
return promise;
}, downCase + " with " + size + " source data");
promise_test(function(test) {
var promise = subtle.digest({name: mixedCase}, sourceData[size])
.then(function(result) {
assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
}, function(err) {
assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
});
return promise;
}, mixedCase + " with " + size + " source data");
promise_test(function(test) {
var copiedBuffer = copyBuffer(sourceData[size]);
var promise = subtle.digest({name: upCase}, copiedBuffer)
.then(function(result) {
assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
}, function(err) {
assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
});
copiedBuffer[0] = 255 - copiedBuffer;
return promise;
}, upCase + " with " + size + " source data and altered buffer after call");
});
});
// Call digest() with bad algorithm names to get an error
var badNames = ["AES-GCM", "RSA-OAEP", "PBKDF2", "AES-KW"];
Object.keys(sourceData).forEach(function(size) {
badNames.forEach(function(badName) {
promise_test(function(test) {
var promise = subtle.digest({name: badName}, sourceData[size])
.then(function(result) {
assert_unreached("digest() should not have worked for " + alg + ":" + size);
}, function(err) {
assert_equals(err.message, "OperationError", "Bad algorithm name should cause OperationError")
});
return promise;
}, badName + " with " + size);
});
});
done();
// Returns a copy of the sourceBuffer it is sent.
function copyBuffer(sourceBuffer) {
var source = new Uint8Array(sourceBuffer);
var copy = new Uint8Array(sourceBuffer.byteLength)
for (var i=0; i<source.byteLength; i++) {
copy[i] = source[i];
}
return copy;
}
function equalBuffers(a, b) {
if (a.byteLength !== b.byteLength) {
return false;
}
var aBytes = new Uint8Array(a);
var bBytes = new Uint8Array(b);
for (var i=0; i<a.byteLength; i++) {
if (aBytes[i] !== bBytes[i]) {
return false;
}
}
return true;
}
return;
}

View file

@ -0,0 +1,4 @@
importScripts("/resources/testharness.js");
importScripts("digest.js");
run_test();

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>WebCryptoAPI: digest()</title>
<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
<link rel="help" href="https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-SubtleCrypto-method-digest">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="digest.js"></script>
<h1>encrypt Tests for digest method</h1>
<div id="log"></div>
<script>
run_test();
</script>

View file

@ -0,0 +1,323 @@
function run_test() {
var subtle = self.crypto.subtle; // Change to test prefixed implementations
// When are all these tests really done? When all the promises they use have resolved.
var all_promises = [];
// Source file aes_XXX_vectors.js provides the getTestVectors method
// for the AES-XXX algorithm that drives these tests.
var vectors = getTestVectors();
var passingVectors = vectors.passing;
var failingVectors = vectors.failing;
var decryptionFailingVectors = vectors.decryptionFailing;
// Check for successful encryption.
passingVectors.forEach(function(vector) {
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
.then(function(result) {
assert_true(equalBuffers(result, vector.result), "Should return expected result");
}, function(err) {
assert_unreached("encrypt error for test " + vector.name + ": " + err.message);
});
}, vector.name);
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: " + vector.name);
});
all_promises.push(promise);
});
// Check for successful encryption even if the buffer is changed after calling encrypt.
passingVectors.forEach(function(vector) {
var plaintext = copyBuffer(vector.plaintext);
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
var operation = subtle.encrypt(vector.algorithm, vector.key, plaintext)
.then(function(result) {
assert_true(equalBuffers(result, vector.result), "Should return expected result");
}, function(err) {
assert_unreached("encrypt error for test " + vector.name + ": " + err.message);
});
plaintext[0] = 255 - plaintext[0];
return operation;
}, vector.name + " with altered plaintext");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: " + vector.name + " with altered plaintext");
});
all_promises.push(promise);
});
// Check for successful decryption.
passingVectors.forEach(function(vector) {
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.key, vector.result)
.then(function(result) {
assert_true(equalBuffers(result, vector.plaintext), "Should return expected result");
}, function(err) {
assert_unreached("decrypt error for test " + vector.name + ": " + err.message);
});
}, vector.name + " decryption");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step for decryption: " + vector.name);
});
all_promises.push(promise);
});
// Check for successful decryption even if ciphertext is altered.
passingVectors.forEach(function(vector) {
var ciphertext = copyBuffer(vector.result);
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
var operation = subtle.decrypt(vector.algorithm, vector.key, ciphertext)
.then(function(result) {
assert_true(equalBuffers(result, vector.plaintext), "Should return expected result");
}, function(err) {
assert_unreached("decrypt error for test " + vector.name + ": " + err.message);
});
ciphertext[0] = 255 - ciphertext[0];
return operation;
}, vector.name + " decryption with altered ciphertext");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step for decryption: " + vector.name + " with altered ciphertext");
});
all_promises.push(promise);
});
// Everything that succeeded should fail if no "encrypt" usage.
passingVectors.forEach(function(vector) {
// Don't want to overwrite key being used for success tests!
var badVector = Object.assign({}, vector);
badVector.key = null;
var promise = importVectorKey(badVector, ["decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
.then(function(result) {
assert_unreached("should have thrown exception for test " + vector.name);
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message)
});
}, vector.name + " without encrypt usage");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: " + vector.name + " without encrypt usage");
});
all_promises.push(promise);
});
// Encryption should fail if algorithm of key doesn't match algorithm of function call.
passingVectors.forEach(function(vector) {
var algorithm = Object.assign({}, vector.algorithm);
if (algorithm.name === "AES-CBC") {
algorithm.name = "AES-CTR";
algorithm.counter = new Uint8Array(16);
algorithm.length = 64;
} else {
algorithm.name = "AES-CBC";
algorithm.iv = new Uint8Array(16); // Need syntactically valid parameter to get to error being checked.
}
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.encrypt(algorithm, vector.key, vector.plaintext)
.then(function(result) {
assert_unreached("encrypt succeeded despite mismatch " + vector.name + ": " + err.message);
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Mismatch should cause InvalidAccessError instead of " + err.message);
});
}, vector.name + " with mismatched key and algorithm");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: " + vector.name + " with mismatched key and algorithm");
});
all_promises.push(promise);
});
// Everything that succeeded decrypting should fail if no "decrypt" usage.
passingVectors.forEach(function(vector) {
// Don't want to overwrite key being used for success tests!
var badVector = Object.assign({}, vector);
badVector.key = null;
var promise = importVectorKey(badVector, ["encrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.key, vector.result)
.then(function(result) {
assert_unreached("should have thrown exception for test " + vector.name);
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message)
});
}, vector.name + " without decrypt usage");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: " + vector.name + " without decrypt usage");
});
all_promises.push(promise);
});
// Check for OperationError due to data lengths.
failingVectors.forEach(function(vector) {
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
.then(function(result) {
assert_unreached("should have thrown exception for test " + vector.name);
}, function(err) {
assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message)
});
}, vector.name);
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: " + vector.name);
});
all_promises.push(promise);
});
// Check for OperationError due to data lengths for decryption, too.
failingVectors.forEach(function(vector) {
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.key, vector.result)
.then(function(result) {
assert_unreached("should have thrown exception for test " + vector.name);
}, function(err) {
assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message)
});
}, vector.name + " decryption");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: decryption " + vector.name);
});
all_promises.push(promise);
});
// Check for decryption failing for algorithm-specific reasons (such as bad
// padding for AES-CBC).
decryptionFailingVectors.forEach(function(vector) {
var promise = importVectorKey(vector, ["encrypt", "decrypt"])
.then(function(vector) {
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.key, vector.result)
.then(function(result) {
assert_unreached("should have thrown exception for test " + vector.name);
}, function(err) {
assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message)
});
}, vector.name);
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importKey failed for " + vector.name);
}, "importKey step: decryption " + vector.name);
});
all_promises.push(promise);
});
Promise.all(all_promises)
.then(function() {done();})
.catch(function() {done();})
// A test vector has all needed fields for encryption, EXCEPT that the
// key field may be null. This function replaces that null with the Correct
// CryptoKey object.
//
// Returns a Promise that yields an updated vector on success.
function importVectorKey(vector, usages) {
if (vector.key !== null) {
return new Promise(function(resolve, reject) {
resolve(vector);
});
} else {
return subtle.importKey("raw", vector.keyBuffer, {name: vector.algorithm.name}, false, usages)
.then(function(key) {
vector.key = key;
return vector;
});
}
}
// Returns a copy of the sourceBuffer it is sent.
function copyBuffer(sourceBuffer) {
var source = new Uint8Array(sourceBuffer);
var copy = new Uint8Array(sourceBuffer.byteLength)
for (var i=0; i<source.byteLength; i++) {
copy[i] = source[i];
}
return copy;
}
function equalBuffers(a, b) {
if (a.byteLength !== b.byteLength) {
return false;
}
var aBytes = new Uint8Array(a);
var bBytes = new Uint8Array(b);
for (var i=0; i<a.byteLength; i++) {
if (aBytes[i] !== bBytes[i]) {
return false;
}
}
return true;
}
return;
}

View file

@ -0,0 +1,5 @@
importScripts("/resources/testharness.js");
importScripts("aes_cbc_vectors.js");
importScripts("aes.js");
run_test();

View file

@ -0,0 +1,261 @@
// aes_cbc_vectors.js
// The following function returns an array of test vectors
// for the subtleCrypto encrypt method.
//
// Each test vector has the following fields:
// name - a unique name for this vector
// keyBuffer - an arrayBuffer with the key data in raw form
// key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it!
// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt
// plaintext - the text to encrypt
// result - the expected result (usually just ciphertext, sometimes with added authentication)
function getTestVectors() {
// Before we can really start, we need to fill a bunch of buffers with data
var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115,
112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110,
32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32,
74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80,
73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114,
109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114,
121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32,
111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105,
110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97,
116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97,
115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115,
105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110,
101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32,
118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110,
44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112,
116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99,
114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100,
105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105,
116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97,
110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112,
108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111,
32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110,
100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116,
104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97,
116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115,
115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102,
111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112,
101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32,
65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111,
109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101,
114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110,
116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111,
99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111,
100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32,
97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102,
105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32,
97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116,
121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105,
99, 97, 116, 105, 111, 110, 115, 46]);
// We want some random key bytes of various sizes.
// These were randomly generated from a script.
var keyBytes = {
128: new Uint8Array([222, 192, 212, 252, 191, 60, 71,
65, 200, 146, 218, 189, 28, 212, 192, 78]),
192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208,
61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]),
256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236,
233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116,
216, 0, 11, 191, 48])
}
// AES-CBC needs a 16 byte (128 bit) IV.
var iv = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39,
167, 108, 39, 162, 132]);
// Results. These were created using the Python cryptography module.
// AES-CBC produces ciphertext
var ciphertext = {
128: new Uint8Array([35, 127, 3, 254, 231, 8, 114, 231, 143, 174, 193,
72, 221, 189, 1, 189, 119, 203, 150, 227, 56, 30, 244, 236, 226, 175,
234, 23, 167, 175, 211, 124, 203, 228, 97, 223, 156, 77, 88, 174,
166, 187, 186, 225, 176, 92, 250, 177, 225, 41, 135, 124, 215, 86,
198, 134, 124, 49, 154, 60, 224, 93, 165, 12, 190, 245, 241, 164,
247, 220, 227, 69, 242, 105, 208, 108, 222, 193, 223, 0, 226, 217,
39, 160, 78, 147, 191, 38, 153, 232, 206, 221, 254, 25, 185, 249, 7,
181, 215, 104, 98, 163, 194, 161, 103, 161, 237, 167, 10, 242, 37,
80, 2, 255, 173, 96, 20, 106, 170, 110, 80, 38, 136, 127, 16, 85,
244, 78, 172, 56, 106, 3, 115, 130, 58, 186, 129, 236, 255, 251,
178, 112, 24, 159, 82, 252, 1, 178, 132, 92, 40, 125, 18, 135, 116,
64, 178, 31, 174, 87, 114, 114, 218, 78, 111, 0, 239, 252, 79, 63,
119, 58, 118, 78, 55, 249, 36, 130, 225, 205, 13, 76, 97, 214, 250,
174, 232, 67, 103, 211, 178, 206, 32, 129, 188, 243, 100, 71, 63,
154, 159, 200, 125, 34, 138, 39, 73, 130, 75, 97, 203, 204, 111,
244, 75, 186, 181, 43, 207, 175, 146, 98, 207, 27, 23, 90, 144, 161,
19, 235, 199, 93, 98, 238, 72, 134, 157, 220, 207, 66, 167, 236, 94,
57, 0, 3, 202, 250, 55, 26, 163, 20, 133, 191, 67, 20, 63, 150, 203,
87, 216, 44, 57, 188, 236, 64, 80, 111, 68, 26, 12, 10, 163, 82, 3,
191, 19, 71, 186, 196, 177, 84, 244, 7, 78, 41, 172, 203, 27, 225,
231, 108, 206, 141, 221, 253, 204, 220, 134, 20, 130, 54, 113, 81,
127, 197, 27, 101, 121, 159, 223, 193, 115, 190, 12, 153, 174, 231,
196, 92, 142, 156, 61, 189, 3, 18, 153, 206, 190, 58, 255, 154, 115,
66, 23, 107, 94, 220, 156, 220, 228, 241, 66, 6, 184, 44, 238, 249,
51, 240, 109, 142, 208, 189, 11, 117, 70, 170, 217, 170, 216, 66,
231, 18, 175, 121, 221, 16, 29, 139, 55, 103, 91, 239, 111, 29, 108,
94, 179, 138, 134, 73, 130, 29, 69, 182, 192, 249, 150, 165, 79, 47,
91, 203, 226, 63, 87, 52, 60, 172, 191, 190, 179, 171, 155, 205, 88,
172, 111, 59, 40, 198, 250, 209, 148, 177, 115, 200, 40, 43, 165,
167, 67, 116, 64, 159, 240, 81, 253, 235, 137, 132, 49, 223, 214,
172, 53, 7, 47, 184, 223, 120, 59, 51, 33, 124, 147, 221, 27, 60,
16, 254, 24, 115, 115, 214, 75, 73, 97, 136, 214, 209, 177, 106, 71,
254, 211, 94, 57, 104, 170, 168, 35, 37, 93, 203, 199, 38, 28, 84]),
192: new Uint8Array([131, 160, 2, 14, 214, 229, 41, 230, 47, 99, 83,
193, 62, 133, 172, 195, 127, 61, 247, 80, 71, 167, 37, 184, 230,
207, 168, 163, 139, 145, 18, 225, 205, 134, 87, 138, 80, 247, 166,
176, 177, 18, 71, 88, 193, 56, 45, 96, 36, 78, 134, 212, 9, 250, 217,
24, 207, 215, 111, 72, 114, 203, 27, 188, 122, 34, 212, 191, 88, 72,
22, 194, 224, 217, 236, 201, 191, 236, 214, 231, 90, 244, 100, 153,
211, 35, 182, 205, 128, 84, 79, 161, 53, 166, 236, 196, 181, 163,
140, 255, 80, 59, 49, 71, 170, 118, 14, 100, 40, 105, 184, 187, 41,
198, 180, 135, 69, 211, 69, 74, 132, 243, 76, 144, 102, 90, 155,
243, 125, 140, 190, 20, 9, 232, 188, 198, 221, 148, 13, 53, 155, 91,
34, 235, 24, 121, 109, 48, 242, 142, 8, 160, 223, 242, 163, 98, 198,
131, 164, 160, 79, 27, 210, 216, 192, 228, 27, 4, 254, 222, 195, 14,
77, 72, 225, 151, 114, 38, 130, 143, 6, 17, 138, 229, 193, 114, 169,
2, 108, 225, 35, 37, 232, 200, 167, 147, 251, 210, 138, 243, 44, 48,
12, 84, 192, 169, 108, 0, 113, 77, 160, 218, 96, 4, 138, 171, 207,
20, 189, 146, 255, 206, 68, 160, 87, 127, 3, 83, 182, 203, 116, 59,
24, 186, 79, 68, 220, 161, 85, 227, 29, 118, 134, 128, 187, 29, 128,
121, 120, 64, 211, 30, 255, 52, 187, 185, 216, 151, 30, 10, 165,
203, 148, 39, 224, 14, 173, 199, 57, 0, 194, 79, 115, 206, 159, 43,
13, 36, 169, 97, 144, 32, 0, 207, 230, 16, 162, 156, 166, 34, 150,
12, 93, 141, 164, 181, 194, 10, 47, 139, 82, 75, 42, 23, 224, 3, 92,
151, 154, 249, 170, 57, 141, 113, 32, 52, 158, 218, 49, 242, 134,
65, 69, 203, 71, 19, 133, 125, 117, 1, 207, 210, 224, 130, 45, 37,
42, 181, 139, 34, 85, 8, 67, 165, 249, 180, 89, 3, 60, 152, 1, 231,
49, 1, 124, 243, 81, 44, 72, 232, 239, 129, 75, 108, 4, 169, 132,
73, 183, 21, 29, 46, 94, 138, 83, 190, 131, 146, 65, 104, 107, 251,
218, 95, 227, 94, 145, 70, 0, 2, 252, 59, 188, 58, 150, 203, 148,
100, 219, 36, 182, 81, 237, 138, 160, 83, 151, 119, 11, 216, 122,
134, 189, 246, 251, 192, 41, 158, 125, 247, 190, 32, 173, 104, 9,
58, 223, 97, 212, 48, 62, 3, 112, 21, 74, 206, 87, 182, 110, 197,
67, 68, 155, 189, 223, 136, 2, 239, 137, 151, 138, 252, 162, 141,
255, 209, 25, 4, 146, 24, 221, 43, 148, 120, 26, 228, 208, 200, 198,
192, 4, 96, 70, 227, 237, 104, 17, 67, 9, 211]),
256: new Uint8Array([41, 213, 121, 140, 181, 227, 200, 97, 100, 133, 58,
227, 106, 115, 25, 63, 77, 51, 26, 57, 238, 140, 99, 63, 71, 211,
128, 84, 115, 26, 236, 52, 103, 81, 145, 14, 101, 161, 181, 58, 135,
193, 56, 167, 214, 220, 5, 52, 85, 222, 183, 27, 101, 134, 86, 155,
64, 148, 124, 212, 219, 251, 65, 42, 32, 44, 128, 2, 50, 128, 221,
22, 238, 56, 189, 83, 28, 122, 121, 157, 215, 135, 151, 128, 233,
193, 65, 190, 86, 148, 191, 140, 196, 120, 8, 172, 100, 166, 254,
41, 245, 75, 56, 6, 166, 244, 178, 111, 234, 23, 4, 107, 6, 22, 132,
187, 230, 17, 71, 172, 113, 238, 73, 4, 180, 90, 103, 77, 37, 51,
118, 112, 129, 238, 199, 7, 222, 122, 173, 30, 232, 178, 233, 234,
144, 98, 14, 234, 112, 77, 68, 62, 62, 159, 230, 101, 98, 43, 2,
204, 69, 156, 86, 104, 128, 34, 128, 7, 173, 90, 120, 33, 104, 59,
45, 251, 93, 51, 240, 232, 60, 94, 189, 134, 90, 20, 184, 122, 29,
225, 85, 213, 38, 116, 159, 80, 69, 106, 168, 236, 201, 69, 140, 98,
240, 45, 160, 133, 225, 106, 45, 245, 212, 160, 176, 128, 27, 114,
153, 182, 144, 145, 214, 72, 196, 138, 183, 87, 61, 245, 150, 56,
82, 158, 224, 50, 114, 125, 122, 172, 161, 129, 234, 70, 63, 245,
136, 30, 136, 9, 128, 220, 229, 157, 222, 195, 149, 189, 70, 8, 71,
40, 195, 93, 27, 7, 234, 164, 175, 102, 201, 149, 115, 248, 179,
125, 66, 122, 194, 26, 61, 218, 198, 181, 152, 140, 199, 48, 148,
31, 14, 241, 197, 3, 70, 128, 239, 32, 86, 15, 215, 86, 245, 190,
95, 141, 41, 111, 0, 232, 28, 152, 67, 87, 197, 255, 118, 13, 251,
71, 84, 22, 231, 134, 188, 175, 115, 138, 37, 199, 5, 238, 199, 2,
99, 203, 75, 62, 231, 21, 150, 239, 94, 201, 185, 219, 58, 210, 228,
151, 131, 76, 148, 104, 60, 74, 82, 6, 168, 49, 251, 182, 3, 232,
173, 210, 201, 19, 101, 166, 7, 94, 11, 194, 211, 146, 229, 75, 241,
15, 50, 187, 36, 175, 78, 227, 98, 224, 3, 95, 209, 93, 126, 112,
178, 29, 18, 108, 241, 232, 79, 210, 41, 2, 238, 208, 190, 171, 134,
147, 188, 191, 229, 122, 32, 209, 166, 118, 129, 223, 130, 214, 195,
89, 67, 94, 218, 155, 185, 0, 144, 255, 132, 213, 25, 59, 83, 242,
57, 69, 148, 109, 133, 61, 163, 30, 214, 254, 54, 169, 3, 217, 77,
66, 123, 193, 204, 199, 109, 123, 49, 186, 223, 229, 8, 230, 164,
171, 196, 145, 225, 10, 111, 248, 111, 164, 216, 54, 225, 253])
};
// Replace the last block of each ciphertext with bad padding below for decryption errors
var badPadding = {
128: {
"zeroPadChar": new Uint8Array([238, 27, 248, 169, 218, 138, 164, 86, 207, 102, 36, 223, 6, 166, 77, 14]),
"bigPadChar": new Uint8Array([91, 67, 119, 104, 252, 238, 175, 144, 17, 75, 12, 163, 212, 52, 46, 51]),
"inconsistentPadChars": new Uint8Array([135, 101, 112, 208, 3, 106, 226, 20, 25, 219, 79, 94, 58, 212, 242, 192])
},
192: {
"zeroPadChar": new Uint8Array([22, 158, 50, 15, 168, 47, 19, 194, 182, 133, 184, 65, 36, 43, 177, 254]),
"bigPadChar": new Uint8Array([207, 110, 28, 160, 165, 213, 48, 213, 163, 242, 15, 78, 96, 117, 106, 87]),
"inconsistentPadChars": new Uint8Array([143, 227, 12, 112, 216, 207, 136, 167, 78, 137, 93, 30, 50, 75, 102, 101])
},
256: {
"zeroPadChar": new Uint8Array([1, 253, 141, 214, 30, 193, 254, 68, 140, 200, 157, 110, 200, 89, 177, 129]),
"bigPadChar": new Uint8Array([88, 7, 110, 221, 74, 34, 97, 109, 99, 25, 189, 222, 94, 90, 27, 60]),
"inconsistentPadChars": new Uint8Array([152, 54, 60, 148, 59, 136, 193, 21, 77, 140, 170, 67, 120, 74, 106, 62])
}
};
var keyLengths = [128, 192, 256];
// All the scenarios that should succeed, if the key has "encrypt" usage
var passing = [];
keyLengths.forEach(function(keyLength) {
passing.push({
name: "AES-CBC " + keyLength.toString() + "-bit key",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CBC", iv: iv},
plaintext: plaintext,
result: ciphertext[keyLength]
});
});
// Scenarios that should fail because of a bad iv length, causing an OperationError
var failing = [];
keyLengths.forEach(function(keyLength) {
var shortIv = iv.slice(0, 8);
failing.push({
name: "AES-CBC " + keyLength.toString() + "-bit key, 64-bit IV",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CBC", iv: shortIv},
plaintext: plaintext,
result: ciphertext[keyLength]
});
var longIv = new Uint8Array(24);
longIv.set(iv, 0);
longIv.set(iv.slice(0, 8), 16);
failing.push({
name: "AES-CBC " + keyLength.toString() + "-bit key, 192-bit IV",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CBC", iv: longIv},
plaintext: plaintext,
result: ciphertext[keyLength]
});
});
// Scenarios that should fail decryption because of bad padding
var decryptionFailing = [];
keyLengths.forEach(function(keyLength) {
["zeroPadChar", "bigPadChar", "inconsistentPadChars"].forEach(function(paddingProblem) {
var badCiphertext = new Uint8Array(ciphertext[keyLength].byteLength);
badCiphertext.set(ciphertext[keyLength].slice(0, ciphertext[keyLength].byteLength - 16));
badCiphertext.set(badPadding[keyLength][paddingProblem]);
decryptionFailing.push({
name: "AES-CBC " + keyLength.toString() + "-bit key, " + paddingProblem,
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CBC", iv: iv},
plaintext: plaintext,
result: badCiphertext
});
});
});
return {passing: passing, failing: failing, decryptionFailing: decryptionFailing};
}

View file

@ -0,0 +1,5 @@
importScripts("/resources/testharness.js");
importScripts("aes_ctr_vectors.js");
importScripts("aes.js");
run_test();

View file

@ -0,0 +1,123 @@
// aes_ctr_vectors.js
// The following function returns an array of test vectors
// for the subtleCrypto encrypt method.
//
// Each test vector has the following fields:
// name - a unique name for this vector
// keyBuffer - an arrayBuffer with the key data in raw form
// key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it!
// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt
// plaintext - the text to encrypt
// result - the expected result (usually just ciphertext, sometimes with added authentication)
function getTestVectors() {
// Before we can really start, we need to fill a bunch of buffers with data
var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115,
112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110,
32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32,
74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80,
73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114,
109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114,
121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32,
111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105,
110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97,
116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97,
115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115,
105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110,
101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32,
118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110,
44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112,
116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99,
114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100,
105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105,
116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97,
110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112,
108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111,
32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110,
100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116,
104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97,
116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115,
115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102,
111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112,
101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32,
65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111,
109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101,
114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110,
116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111,
99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111,
100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32,
97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102,
105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32,
97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116,
121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105,
99, 97, 116, 105, 111, 110, 115, 46]);
// We want some random key bytes of various sizes.
// These were randomly generated from a script.
var keyBytes = {
128: new Uint8Array([222, 192, 212, 252, 191, 60, 71,
65, 200, 146, 218, 189, 28, 212, 192, 78]),
192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208,
61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]),
256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236,
233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116,
216, 0, 11, 191, 48])
}
// AES-CTR needs a 16 byte (128 bit) counter.
var counter = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39,
167, 108, 39, 162, 132]);
// Results. These were created using the Python cryptography module.
// AES-CTR produces ciphertext
var ciphertext = {
128: new Uint8Array([233, 17, 117, 253, 164, 245, 234, 87, 197, 43, 13, 0, 11, 190, 152, 175, 104, 192, 165, 144, 88, 174, 237, 138, 181, 183, 6, 53, 3, 161, 206, 71, 13, 121, 218, 209, 116, 249, 10, 170, 250, 165, 68, 157, 132, 141, 200, 178, 197, 87, 209, 231, 250, 75, 154, 65, 162, 251, 30, 159, 234, 20, 20, 181, 147, 218, 180, 12, 4, 241, 75, 79, 129, 64, 15, 228, 60, 147, 153, 1, 129, 176, 150, 161, 85, 97, 22, 154, 234, 23, 127, 16, 4, 22, 226, 11, 104, 16, 176, 14, 225, 176, 79, 239, 103, 243, 190, 222, 40, 186, 244, 212, 29, 57, 125, 175, 21, 17, 233, 2, 13, 119, 102, 233, 230, 4, 16, 222, 56, 225, 67, 45, 191, 250, 15, 153, 45, 193, 240, 212, 117, 101, 68, 232, 199, 101, 175, 125, 247, 6, 249, 14, 0, 157, 185, 56, 76, 51, 228, 77, 234, 84, 60, 42, 119, 187, 213, 32, 34, 222, 65, 231, 215, 26, 73, 141, 231, 254, 185, 118, 14, 180, 126, 80, 51, 102, 200, 141, 204, 45, 26, 56, 119, 136, 222, 45, 143, 120, 231, 44, 43, 221, 136, 21, 188, 138, 84, 232, 208, 238, 226, 117, 104, 60, 165, 4, 18, 144, 240, 49, 173, 90, 68, 84, 239, 161, 124, 196, 144, 119, 24, 243, 239, 75, 117, 254, 219, 209, 53, 131, 37, 79, 68, 26, 21, 168, 163, 50, 59, 18, 244, 11, 143, 190, 188, 129, 108, 249, 180, 104, 216, 215, 165, 160, 251, 84, 132, 152, 195, 154, 110, 216, 70, 21, 248, 148, 146, 152, 56, 174, 248, 227, 1, 102, 15, 118, 182, 50, 73, 63, 35, 112, 159, 237, 253, 94, 16, 127, 120, 38, 127, 51, 27, 96, 163, 140, 20, 111, 151, 16, 72, 74, 74, 205, 239, 241, 16, 179, 183, 116, 95, 248, 58, 168, 203, 93, 233, 225, 91, 17, 226, 10, 120, 85, 114, 4, 31, 40, 82, 161, 152, 17, 86, 237, 207, 7, 228, 110, 182, 65, 68, 68, 156, 206, 116, 185, 204, 148, 22, 58, 111, 218, 138, 225, 146, 25, 114, 29, 96, 183, 87, 181, 181, 236, 113, 141, 171, 213, 9, 84, 182, 230, 163, 147, 246, 86, 246, 52, 111, 64, 34, 157, 12, 80, 224, 28, 21, 112, 31, 42, 79, 229, 210, 90, 23, 78, 223, 155, 144, 238, 12, 14, 191, 158, 6, 181, 254, 0, 85, 134, 56, 161, 234, 55, 129, 64, 59, 12, 146, 6, 217, 232, 20, 214, 167, 159, 183, 165, 96, 96, 225, 199, 23, 106, 243, 108, 106, 26, 214, 53, 152, 26, 155, 253, 128, 7, 216, 207, 109, 159, 147, 240, 232, 226, 43, 147, 169, 162, 204, 215, 9, 10, 177, 223, 99, 206, 163, 240, 64]),
192: new Uint8Array([98, 123, 235, 65, 14, 86, 80, 133, 88, 104, 244, 125, 165, 185, 163, 4, 3, 230, 62, 58, 113, 222, 46, 210, 17, 155, 95, 19, 125, 125, 70, 234, 105, 54, 23, 246, 114, 9, 237, 191, 9, 194, 34, 254, 156, 11, 50, 216, 80, 178, 185, 221, 132, 154, 27, 85, 82, 49, 241, 123, 23, 106, 119, 134, 203, 0, 151, 66, 149, 218, 124, 247, 227, 233, 236, 184, 88, 234, 174, 250, 83, 168, 33, 15, 122, 26, 96, 213, 210, 4, 52, 92, 20, 12, 64, 12, 209, 197, 69, 100, 15, 56, 60, 63, 241, 52, 18, 189, 93, 146, 47, 60, 33, 200, 218, 243, 43, 169, 17, 108, 19, 199, 174, 33, 107, 186, 57, 95, 167, 138, 180, 187, 53, 113, 208, 148, 190, 48, 167, 53, 209, 52, 153, 184, 231, 63, 168, 54, 179, 238, 93, 130, 125, 3, 149, 119, 60, 25, 142, 150, 183, 193, 29, 18, 3, 219, 235, 219, 26, 116, 217, 196, 108, 6, 96, 103, 212, 48, 227, 91, 124, 77, 181, 169, 18, 111, 123, 83, 26, 169, 230, 88, 103, 185, 153, 93, 143, 152, 142, 231, 41, 226, 226, 156, 179, 206, 212, 67, 18, 193, 187, 53, 252, 214, 15, 228, 246, 131, 170, 101, 134, 212, 100, 170, 146, 47, 57, 125, 50, 230, 51, 246, 74, 175, 129, 196, 178, 206, 176, 52, 153, 39, 77, 24, 186, 99, 137, 83, 105, 111, 168, 35, 176, 24, 29, 170, 223, 74, 160, 138, 247, 12, 102, 233, 136, 59, 172, 228, 242, 84, 13, 34, 155, 80, 80, 87, 180, 143, 129, 61, 213, 54, 41, 8, 183, 102, 126, 179, 127, 77, 55, 176, 152, 41, 131, 85, 86, 225, 87, 216, 139, 226, 196, 195, 210, 34, 33, 161, 249, 153, 205, 197, 128, 41, 28, 121, 6, 159, 25, 211, 168, 137, 26, 217, 249, 113, 81, 141, 18, 1, 250, 228, 68, 238, 74, 54, 99, 167, 236, 176, 199, 148, 161, 143, 156, 51, 189, 204, 59, 240, 151, 170, 85, 63, 23, 38, 152, 199, 12, 81, 217, 244, 178, 231, 249, 159, 224, 107, 214, 58, 127, 116, 143, 219, 155, 80, 55, 213, 171, 80, 127, 235, 20, 247, 12, 104, 228, 147, 202, 124, 143, 110, 223, 76, 221, 154, 175, 143, 185, 237, 222, 189, 104, 218, 72, 244, 55, 253, 138, 183, 92, 231, 68, 176, 239, 171, 100, 10, 63, 61, 194, 228, 15, 133, 216, 45, 60, 135, 203, 142, 127, 153, 172, 223, 213, 230, 220, 189, 223, 234, 156, 134, 238, 220, 251, 104, 209, 117, 175, 47, 46, 148, 6, 61, 216, 215, 39, 30, 116, 212, 45, 112, 202, 227, 198, 98, 253, 97, 177, 120, 74, 238, 68, 99, 240, 96, 43, 88, 166]),
256: new Uint8Array([55, 82, 154, 67, 47, 80, 186, 78, 83, 56, 95, 130, 102, 236, 61, 236, 204, 236, 234, 222, 122, 226, 147, 149, 233, 41, 16, 118, 201, 91, 185, 162, 79, 71, 146, 252, 221, 110, 165, 137, 75, 129, 94, 219, 93, 94, 64, 34, 250, 190, 5, 90, 6, 177, 167, 224, 25, 121, 85, 91, 87, 152, 56, 100, 191, 35, 1, 156, 177, 179, 127, 253, 173, 176, 87, 247, 40, 207, 178, 175, 10, 51, 209, 70, 52, 76, 251, 160, 172, 203, 77, 191, 97, 58, 123, 238, 82, 60, 166, 214, 134, 14, 71, 74, 156, 15, 77, 6, 141, 76, 10, 205, 148, 204, 85, 203, 242, 30, 66, 133, 202, 21, 17, 108, 151, 2, 15, 44, 51, 180, 88, 80, 8, 248, 254, 151, 201, 226, 156, 6, 39, 197, 212, 124, 72, 217, 75, 232, 139, 155, 22, 199, 242, 223, 116, 10, 141, 42, 7, 85, 99, 5, 184, 43, 145, 159, 122, 135, 202, 46, 209, 157, 178, 114, 98, 194, 119, 194, 19, 242, 167, 236, 162, 94, 90, 106, 219, 234, 67, 11, 162, 225, 6, 17, 152, 23, 16, 84, 40, 90, 255, 158, 8, 105, 198, 56, 220, 213, 36, 203, 241, 242, 85, 218, 103, 90, 202, 214, 215, 134, 121, 169, 149, 139, 122, 143, 155, 178, 29, 217, 197, 128, 173, 25, 111, 154, 14, 76, 106, 101, 0, 215, 187, 33, 223, 116, 205, 89, 52, 206, 60, 77, 141, 31, 57, 211, 74, 42, 219, 88, 210, 36, 196, 128, 151, 136, 124, 222, 157, 59, 225, 70, 163, 234, 59, 173, 228, 198, 134, 76, 249, 228, 69, 181, 196, 194, 179, 239, 78, 43, 143, 94, 234, 10, 177, 192, 185, 171, 231, 164, 254, 91, 44, 11, 29, 148, 223, 107, 18, 149, 61, 50, 115, 38, 14, 128, 189, 9, 77, 236, 151, 163, 23, 122, 156, 236, 11, 80, 66, 190, 24, 4, 4, 12, 148, 57, 64, 59, 143, 114, 247, 66, 111, 167, 86, 173, 98, 102, 207, 44, 134, 89, 231, 64, 50, 157, 208, 210, 79, 159, 133, 73, 118, 98, 202, 215, 57, 247, 29, 97, 116, 1, 28, 119, 248, 243, 31, 180, 66, 38, 40, 141, 251, 134, 129, 126, 241, 113, 22, 50, 28, 113, 187, 158, 217, 125, 182, 233, 144, 246, 32, 88, 88, 15, 0, 102, 131, 67, 31, 34, 150, 98, 241, 213, 227, 205, 175, 254, 3, 53, 70, 124, 167, 38, 53, 104, 140, 147, 158, 200, 179, 45, 100, 101, 246, 81, 166, 53, 247, 60, 10, 78, 127, 10, 173, 176, 232, 31, 91, 203, 250, 236, 38, 113, 172, 151, 253, 194, 253, 50, 242, 76, 148, 23, 117, 195, 122, 104, 16, 212, 177, 113, 188, 138, 186, 144, 168, 102, 3])
};
var keyLengths = [128, 192, 256];
// All the scenarios that should succeed, if the key has "encrypt" usage
var passing = [];
keyLengths.forEach(function(keyLength) {
passing.push({
name: "AES-CTR " + keyLength.toString() + "-bit key",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CTR", counter: counter, length: 64},
plaintext: plaintext,
result: ciphertext[keyLength]
});
});
// Scenarios that should fail because of a bad length parameter, causing an OperationError
var failing = [];
keyLengths.forEach(function(keyLength) {
failing.push({
name: "AES-CTR " + keyLength.toString() + "-bit key, 0-bit counter",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CTR", counter: counter, length: 0},
plaintext: plaintext,
result: ciphertext[keyLength]
});
failing.push({
name: "AES-CTR " + keyLength.toString() + "-bit key, 129-bit counter",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-CTR", counter: counter, length: 129},
plaintext: plaintext,
result: ciphertext[keyLength]
});
});
return {passing: passing, failing: failing, decryptionFailing: []};
}

View file

@ -0,0 +1,5 @@
importScripts("/resources/testharness.js");
importScripts("aes_gcm_vectors.js");
importScripts("aes.js");
run_test();

View file

@ -0,0 +1,262 @@
// aes_gcm_vectors.js
// The following function returns an array of test vectors
// for the subtleCrypto encrypt method.
//
// Each test vector has the following fields:
// name - a unique name for this vector
// keyBuffer - an arrayBuffer with the key data in raw form
// key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it!
// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt
// plaintext - the text to encrypt
// result - the expected result (usually just ciphertext, sometimes with added authentication)
function getTestVectors() {
// Before we can really start, we need to fill a bunch of buffers with data
var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115,
112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110,
32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32,
74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80,
73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114,
109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114,
121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32,
111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105,
110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97,
116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97,
115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115,
105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110,
101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32,
118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110,
44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112,
116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99,
114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100,
105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105,
116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97,
110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112,
108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111,
32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110,
100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116,
104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97,
116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115,
115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102,
111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112,
101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32,
65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111,
109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101,
114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110,
116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111,
99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111,
100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32,
97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102,
105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32,
97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116,
121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105,
99, 97, 116, 105, 111, 110, 115, 46]);
// We want some random key bytes of various sizes.
// These were randomly generated from a script.
var keyBytes = {
128: new Uint8Array([222, 192, 212, 252, 191, 60, 71,
65, 200, 146, 218, 189, 28, 212, 192, 78]),
192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208,
61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]),
256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236,
233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116,
216, 0, 11, 191, 48])
}
// AES-GCM needs an IV of no more than 2^64 - 1 bytes. Well, 32 bytes is okay then.
var iv = new Uint8Array([58, 146, 115, 42, 166, 234, 57,
191, 57, 134, 224, 199, 63, 169, 32, 0, 32, 33, 117, 56,
94, 248, 173, 234, 194, 200, 115, 53, 235, 146, 141, 212]);
// Authenticated encryption via AES-GCM requires additional data that
// will be checked. We use the ASCII encoded Editorial Note
// following the Abstract of the Web Cryptography API recommendation.
var additionalData = new Uint8Array([84, 104, 101, 114, 101,
32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116, 104, 101,
114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32,
110, 111, 116, 101, 115, 32, 105, 110, 32, 116, 104, 101,
32, 100, 111, 99, 117, 109, 101, 110, 116, 46]);
// The length of the tag defaults to 16 bytes (128 bit).
var tag = {
128: new Uint8Array([194, 226, 198, 253, 239, 28,
197, 240, 123, 216, 176, 151, 239, 200, 184, 183]),
192: new Uint8Array([183, 57, 32, 144, 164, 76, 121, 77, 58,
86, 62, 132, 53, 130, 96, 225]),
256: new Uint8Array([188, 239, 241, 48, 159, 21, 213, 0, 241,
42, 85, 76, 194, 28, 49, 60])
};
var tag_with_empty_ad = {
128: new Uint8Array([222, 51, 11, 23, 36, 222, 250, 248, 27, 98, 30, 81, 150, 35, 220, 198]),
192: new Uint8Array([243, 11, 130, 112, 169, 239, 114, 238, 185, 219, 93, 1, 95, 108, 184, 183]),
256: new Uint8Array([244, 186, 86, 203, 154, 37, 191, 248, 246, 57, 139, 130, 224, 47, 217, 238])
};
// Results. These were created using the Python cryptography module.
// AES-GCM produces ciphertext and a tag.
var ciphertext = {
128: new Uint8Array([180, 241, 40, 183, 105,
52, 147, 238, 224, 175, 175, 236, 168, 244, 241, 121, 9,
202, 225, 237, 56, 216, 253, 254, 186, 102, 111, 207, 228,
190, 130, 177, 159, 246, 6, 53, 249, 113, 228, 254, 81,
126, 253, 191, 100, 43, 251, 147, 107, 91, 166, 231, 201,
241, 180, 214, 112, 47, 123, 164, 186, 134, 54, 65, 22,
181, 201, 82, 236, 59, 52, 139, 172, 39, 41, 89, 123, 62,
102, 167, 82, 150, 250, 93, 96, 169, 135, 89, 245, 255,
164, 192, 169, 159, 25, 16, 139, 145, 76, 4, 144, 131,
148, 197, 204, 46, 23, 110, 193, 228, 127, 120, 242, 24,
54, 240, 181, 162, 98, 244, 249, 68, 134, 122, 126, 151,
38, 108, 116, 68, 150, 109, 38, 194, 21, 159, 140, 205,
183, 35, 97, 151, 186, 120, 145, 22, 235, 22, 210, 223,
187, 143, 162, 183, 93, 196, 104, 51, 96, 53, 234, 250,
184, 76, 237, 157, 37, 203, 226, 87, 222, 75, 240, 95, 218,
222, 64, 81, 165, 75, 201, 216, 190, 13, 116, 217, 69, 66,
47, 161, 68, 247, 74, 253, 157, 181, 162, 121, 53, 32, 91,
124, 230, 105, 224, 17, 187, 50, 61, 77, 103, 79, 71, 57,
163, 116, 234, 149, 27, 105, 24, 31, 159, 3, 128, 130, 42,
94, 125, 200, 142, 251, 148, 201, 17, 149, 232, 84, 50, 17,
18, 203, 186, 226, 164, 227, 202, 76, 65, 16, 163, 224,
132, 52, 31, 101, 129, 72, 171, 159, 42, 177, 253, 98, 86,
201, 95, 117, 62, 12, 205, 78, 36, 126, 196, 121, 89, 185,
37, 161, 66, 181, 117, 186, 71, 124, 132, 110, 120, 27,
246, 163, 18, 13, 90, 200, 127, 82, 209, 241, 170, 73, 247,
137, 96, 244, 254, 251, 119, 71, 156, 27, 107, 53, 33, 45,
22, 0, 144, 48, 32, 11, 116, 21, 125, 246, 217, 171, 158,
224, 142, 234, 141, 242, 168, 89, 154, 66, 227, 161, 182,
96, 1, 88, 78, 12, 7, 239, 30, 206, 31, 89, 111, 107, 42,
37, 241, 148, 232, 1, 8, 251, 117, 146, 183, 9, 48, 39, 94,
59, 70, 230, 26, 165, 97, 156, 140, 141, 31, 62, 10, 206,
55, 48, 207, 0, 197, 202, 197, 108, 133, 175, 80, 4, 16,
154, 223, 255, 4, 196, 188, 178, 240, 29, 13, 120, 5, 225,
202, 3, 35, 225, 158, 92, 152, 73, 205, 107, 157, 224, 245,
99, 194, 171, 156, 245, 247, 183, 165, 40, 62, 200, 110,
29, 151, 206, 100, 175, 88, 36, 242, 90, 4, 82, 73, 250,
140, 245, 217, 9, 153, 35, 242, 206, 78, 197, 121, 115, 15,
80, 128, 101, 191, 240, 91, 151, 249, 62, 62, 244, 18, 3,
17, 135, 222, 210, 93, 149, 123]),
192: new Uint8Array([126, 160, 166, 112, 227, 212, 106,
186, 175, 70, 24, 28, 86, 149, 31, 154, 156, 190, 244, 132, 44, 61, 149,
242, 105, 67, 17, 136, 7, 146, 153, 170, 200, 214, 142, 205, 170, 225,
85, 44, 241, 159, 255, 234, 10, 13, 37, 48, 255, 21, 141, 176, 60, 117,
73, 130, 247, 204, 144, 102, 167, 89, 203, 235, 229, 129, 122, 253, 124,
179, 115, 118, 163, 157, 67, 141, 122, 146, 209, 11, 112, 5, 230, 117,
123, 184, 243, 99, 83, 10, 31, 166, 96, 1, 121, 44, 10, 241, 24, 43,
184, 187, 25, 239, 246, 176, 108, 230, 127, 25, 42, 67, 202, 140, 179,
104, 159, 75, 103, 43, 248, 98, 166, 179, 67, 0, 163, 227, 84, 40, 129,
227, 198, 205, 7, 156, 16, 185, 24, 166, 59, 218, 197, 114, 74, 34, 126,
22, 226, 226, 85, 212, 69, 83, 163, 185, 68, 109, 182, 54, 209, 237, 96,
184, 32, 53, 127, 175, 13, 146, 141, 115, 164, 184, 98, 245, 174, 223,
46, 32, 167, 39, 103, 19, 210, 80, 131, 254, 103, 249, 247, 29, 120, 31,
105, 241, 103, 169, 249, 93, 153, 74, 56, 53, 239, 157, 132, 236, 169,
246, 242, 24, 113, 97, 128, 238, 152, 148, 31, 84, 8, 52, 105, 198, 116,
103, 132, 48, 199, 23, 90, 24, 29, 63, 41, 117, 191, 57, 31, 209, 128,
60, 119, 175, 84, 141, 177, 165, 169, 195, 35, 163, 105, 146, 157, 209,
93, 149, 105, 160, 93, 231, 78, 201, 92, 235, 200, 89, 37, 50, 181, 30,
213, 242, 59, 156, 219, 19, 158, 17, 224, 81, 108, 52, 87, 248, 101, 23,
39, 107, 67, 151, 103, 230, 126, 202, 184, 118, 226, 18, 29, 93, 37, 208,
40, 82, 113, 35, 157, 145, 152, 50, 253, 140, 47, 141, 192, 1, 148, 114,
40, 10, 112, 79, 227, 16, 105, 247, 31, 49, 102, 195, 75, 183, 172, 254,
188, 42, 89, 77, 38, 104, 1, 180, 106, 61, 71, 70, 35, 160, 103, 101,
244, 26, 226, 37, 159, 155, 4, 107, 222, 219, 136, 37, 24, 246, 44, 23,
44, 248, 132, 108, 59, 179, 99, 145, 132, 82, 53, 203, 111, 150, 55,
123, 51, 214, 165, 108, 124, 179, 131, 174, 139, 224, 114, 96, 218, 181,
243, 128, 198, 98, 115, 92, 95, 165, 23, 229, 108, 146, 14, 244, 162,
37, 85, 201, 33, 44, 92, 106, 112, 185, 16, 189, 42, 114, 109, 59, 124,
131, 16, 211, 31, 97, 29, 135, 61, 150, 75, 250, 207, 129, 38, 205, 187,
186, 55, 207, 232, 24, 48, 232, 49, 226, 16, 12, 27, 70, 31, 124, 128,
218, 100, 91, 200, 184, 78, 252, 100, 235, 62, 43, 69, 214, 163, 65, 14,
44, 180]),
256: new Uint8Array([8, 97, 235, 113, 70, 32, 135, 131,
210, 209, 124, 160, 255, 182, 9, 29, 125, 193, 27, 240, 129, 46, 2, 137,
169, 142, 61, 7, 145, 54, 170, 207, 159, 111, 39, 95, 87, 63, 162, 27,
6, 18, 219, 215, 116, 34, 90, 57, 114, 244, 102, 145, 67, 6, 51, 152,
247, 165, 242, 116, 100, 219, 177, 72, 177, 17, 110, 67, 93, 219, 100,
217, 20, 207, 89, 154, 45, 37, 105, 83, 67, 162, 140, 235, 129, 40, 177,
202, 174, 54, 148, 55, 156, 193, 232, 249, 134, 163, 195, 51, 114, 116,
65, 38, 73, 99, 96, 249, 224, 69, 17, 119, 186, 188, 181, 43, 78, 156,
76, 138, 226, 63, 5, 248, 9, 94, 26, 1, 2, 235, 39, 174, 74, 47, 183,
22, 40, 47, 47, 13, 100, 119, 12, 67, 178, 184, 56, 167, 238, 143, 13,
44, 208, 185, 151, 108, 6, 17, 52, 122, 182, 210, 207, 42, 219, 37, 74,
94, 126, 36, 249, 37, 32, 4, 218, 44, 238, 69, 56, 219, 31, 77, 173, 46,
187, 103, 36, 112, 213, 252, 40, 87, 164, 240, 163, 159, 32, 129, 125,
178, 108, 47, 28, 31, 36, 42, 115, 36, 14, 145, 195, 156, 191, 46, 163,
249, 181, 31, 90, 73, 30, 72, 57, 223, 63, 60, 79, 140, 14, 117, 31,
145, 222, 156, 121, 237, 32, 145, 143, 96, 12, 254, 35, 21, 21, 59, 168,
171, 154, 217, 0, 59, 202, 175, 103, 214, 192, 175, 26, 18, 43, 54, 176,
222, 75, 22, 7, 122, 253, 224, 145, 61, 42, 208, 73, 237, 84, 141, 209,
213, 228, 46, 244, 59, 9, 68, 6, 35, 88, 189, 10, 62, 9, 85, 28, 44, 82,
19, 153, 160, 178, 240, 56, 160, 244, 201, 173, 77, 61, 20, 227, 30,
180, 167, 16, 105, 185, 193, 95, 207, 41, 23, 134, 78, 198, 182, 93, 24,
89, 247, 231, 75, 233, 194, 137, 242, 114, 194, 190, 130, 138, 238, 94,
137, 193, 194, 115, 137, 190, 207, 169, 83, 155, 14, 210, 160, 129, 195,
161, 234, 221, 255, 114, 67, 98, 12, 93, 41, 65, 183, 244, 103, 247,
101, 82, 246, 125, 87, 125, 78, 21, 186, 102, 205, 20, 40, 32, 201, 174,
15, 52, 240, 217, 180, 162, 108, 6, 211, 41, 18, 135, 232, 184, 18, 188,
169, 157, 190, 76, 166, 75, 176, 127, 39, 251, 22, 203, 153, 80, 49,
241, 124, 137, 151, 123, 204, 43, 159, 190, 177, 196, 18, 117, 169, 46,
152, 251, 45, 25, 164, 27, 145, 214, 228, 55, 15, 2, 131, 216, 80, 255,
204, 175, 100, 59, 145, 15, 103, 40, 33, 45, 255, 200, 254, 172, 138,
20, 58, 87, 182, 192, 148, 219, 41, 88, 230, 229, 70, 249])
};
var keyLengths = [128, 192, 256];
var tagLengths = [32, 64, 96, 104, 112, 120, 128];
// All the scenarios that should succeed, if the key has "encrypt" usage
var passing = [];
keyLengths.forEach(function(keyLength) {
tagLengths.forEach(function(tagLength) {
var byteCount = tagLength / 8;
var result = new Uint8Array(ciphertext[keyLength].byteLength + byteCount);
result.set(ciphertext[keyLength], 0);
result.set(tag[keyLength].slice(0, byteCount), ciphertext[keyLength].byteLength);
passing.push({
name: "AES-GCM " + keyLength.toString() + "-bit key, " + tagLength.toString() + "-bit tag",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-GCM", iv: iv, additionalData: additionalData, tagLength: tagLength},
plaintext: plaintext,
result: result
});
var noadresult = new Uint8Array(ciphertext[keyLength].byteLength + byteCount);
noadresult.set(ciphertext[keyLength], 0);
noadresult.set(tag_with_empty_ad[keyLength].slice(0, byteCount), ciphertext[keyLength].byteLength);
passing.push({
name: "AES-GCM " + keyLength.toString() + "-bit key, no additional data, " + tagLength.toString() + "-bit tag",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-GCM", iv: iv, tagLength: tagLength},
plaintext: plaintext,
result: noadresult
});
});
});
// Scenarios that should fail because of a bad tag length, causing an OperationError
var failing = [];
keyLengths.forEach(function(keyLength) {
// First, make some tests for bad tag lengths
[24, 48, 72, 95, 129, 256].forEach(function(badTagLength) {
failing.push({
name: "AES-GCM " + keyLength.toString() + "-bit key, illegal tag length " + badTagLength.toString() + "-bits",
keyBuffer: keyBytes[keyLength],
key: null,
algorithm: {name: "AES-GCM", iv: iv, additionalData: additionalData, tagLength: badTagLength},
plaintext: plaintext,
result: ciphertext[keyLength]
});
});
});
return {passing: passing, failing: failing, decryptionFailing: []};
}

View file

@ -0,0 +1,376 @@
function run_test() {
var subtle = self.crypto.subtle; // Change to test prefixed implementations
// When are all these tests really done? When all the promises they use have resolved.
var all_promises = [];
// Source file rsa_vectors.js provides the getTestVectors method
// for the RSA-OAEP algorithm that drives these tests.
var vectors = getTestVectors();
var passingVectors = vectors.passing;
var failingVectors = vectors.failing;
// Test decryption, first, because encryption tests rely on that working
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
// Get a one byte longer plaintext to encrypt
if (!("ciphertext" in vector)) {
return;
}
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.privateKey, vector.ciphertext)
.then(function(plaintext) {
assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works"));
}, function(err) {
assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'");
});
}, vector.name + " decryption");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " decryption");
});
all_promises.push(promise);
});
// Test decryption with an altered buffer
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
// Get a one byte longer plaintext to encrypt
if (!("ciphertext" in vector)) {
return;
}
promise_test(function(test) {
var ciphertext = copyBuffer(vector.ciphertext);
var operation = subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext)
.then(function(plaintext) {
assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works"));
}, function(err) {
assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'");
});
ciphertext[0] = 255 - ciphertext[0];
return operation;
}, vector.name + " decryption with altered ciphertext");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " decryption with altered ciphertext");
});
all_promises.push(promise);
});
// Check for failures due to using publicKey to decrypt.
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.publicKey, vector.ciphertext)
.then(function(plaintext) {
assert_unreached("Should have thrown error for using publicKey to decrypt in " + vector.name + ": " + err.message + "'");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message);
});
}, vector.name + " using publicKey to decrypt");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " using publicKey to decrypt");
});
all_promises.push(promise);
});
// Check for failures due to no "decrypt" usage.
passingVectors.forEach(function(originalVector) {
var vector = Object.assign({}, originalVector);
var promise = importVectorKeys(vector, ["encrypt"], ["unwrapKey"])
.then(function(vectors) {
// Get a one byte longer plaintext to encrypt
promise_test(function(test) {
return subtle.decrypt(vector.algorithm, vector.publicKey, vector.ciphertext)
.then(function(plaintext) {
assert_unreached("Should have thrown error for no decrypt usage in " + vector.name + ": " + err.message + "'");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message);
});
}, vector.name + " no decrypt usage");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " no decrypt usage");
});
all_promises.push(promise);
});
// Check for successful encryption even if plaintext is altered after call.
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
promise_test(function(test) {
var plaintext = copyBuffer(vector.plaintext);
var operation = subtle.encrypt(vector.algorithm, vector.publicKey, plaintext)
.then(function(ciphertext) {
assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length");
// Can we get the original plaintext back via decrypt?
return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext)
.then(function(result) {
assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext");
return ciphertext;
}, function(err) {
assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'");
});
})
.then(function(priorCiphertext) {
// Will a second encrypt give us different ciphertext, as it should?
return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext)
.then(function(ciphertext) {
assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results")
}, function(err) {
assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'");
});
}, function(err) {
assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'");
});
plaintext[0] = 255 - plaintext[0];
return operation;
}, vector.name + " with altered plaintext");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " with altered plaintext");
});
all_promises.push(promise);
});
// Check for successful encryption.
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext)
.then(function(ciphertext) {
assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length");
// Can we get the original plaintext back via decrypt?
return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext)
.then(function(result) {
assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext");
return ciphertext;
}, function(err) {
assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'");
});
})
.then(function(priorCiphertext) {
// Will a second encrypt give us different ciphertext, as it should?
return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext)
.then(function(ciphertext) {
assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results")
}, function(err) {
assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'");
});
}, function(err) {
assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'");
});
}, vector.name);
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name);
});
all_promises.push(promise);
});
// Check for failures due to too long plaintext.
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
// Get a one byte longer plaintext to encrypt
var plaintext = new Uint8Array(vector.plaintext.byteLength + 1);
plaintext.set(plaintext, 0);
plaintext.set(new Uint8Array([32]), vector.plaintext.byteLength);
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.publicKey, plaintext)
.then(function(ciphertext) {
assert_unreached("Should have thrown error for too long plaintext in " + vector.name + ": " + err.message + "'");
}, function(err) {
assert_equals(err.name, "OperationError", "Should throw OperationError instead of " + err.message);
});
}, vector.name + " too long plaintext");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " too long plaintext");
});
all_promises.push(promise);
});
// Check for failures due to using privateKey to encrypt.
passingVectors.forEach(function(vector) {
var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"])
.then(function(vectors) {
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.privateKey, vector.plaintext)
.then(function(ciphertext) {
assert_unreached("Should have thrown error for using privateKey to encrypt in " + vector.name + ": " + err.message + "'");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message);
});
}, vector.name + " using privateKey to encrypt");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " using privateKey to encrypt");
});
all_promises.push(promise);
});
// Check for failures due to no "encrypt usage".
passingVectors.forEach(function(originalVector) {
var vector = Object.assign({}, originalVector);
var promise = importVectorKeys(vector, [], ["decrypt"])
.then(function(vectors) {
// Get a one byte longer plaintext to encrypt
promise_test(function(test) {
return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext)
.then(function(ciphertext) {
assert_unreached("Should have thrown error for no encrypt usage in " + vector.name + ": " + err.message + "'");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message);
});
}, vector.name + " no encrypt usage");
}, function(err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested encryption
promise_test(function(test) {
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
}, "importVectorKeys step: " + vector.name + " no encrypt usage");
});
all_promises.push(promise);
});
Promise.all(all_promises)
.then(function() {done();})
.catch(function() {done();})
// A test vector has all needed fields for encryption, EXCEPT that the
// key field may be null. This function replaces that null with the Correct
// CryptoKey object.
//
// Returns a Promise that yields an updated vector on success.
function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
var publicPromise, privatePromise;
if (vector.publicKey !== null) {
publicPromise = new Promise(function(resolve, reject) {
resolve(vector);
});
} else {
publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, publicKeyUsages)
.then(function(key) {
vector.publicKey = key;
return vector;
}); // Returns a copy of the sourceBuffer it is sent.
function copyBuffer(sourceBuffer) {
var source = new Uint8Array(sourceBuffer);
var copy = new Uint8Array(sourceBuffer.byteLength)
for (var i=0; i<source.byteLength; i++) {
copy[i] = source[i];
}
return copy;
}
}
if (vector.privateKey !== null) {
privatePromise = new Promise(function(resolve, reject) {
resolve(vector);
});
} else {
privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, privateKeyUsages)
.then(function(key) {
vector.privateKey = key;
return vector;
});
}
return Promise.all([publicPromise, privatePromise]);
}
// Returns a copy of the sourceBuffer it is sent.
function copyBuffer(sourceBuffer) {
var source = new Uint8Array(sourceBuffer);
var copy = new Uint8Array(sourceBuffer.byteLength)
for (var i=0; i<source.byteLength; i++) {
copy[i] = source[i];
}
return copy;
}
function equalBuffers(a, b) {
if (a.byteLength !== b.byteLength) {
return false;
}
var aBytes = new Uint8Array(a);
var bBytes = new Uint8Array(b);
for (var i=0; i<a.byteLength; i++) {
if (aBytes[i] !== bBytes[i]) {
return false;
}
}
return true;
}
return;
}

View file

@ -0,0 +1,5 @@
importScripts("/resources/testharness.js");
importScripts("rsa_vectors.js");
importScripts("rsa.js");
run_test();

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>WebCryptoAPI: encrypt() Using AES-CBC</title>
<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
<link rel="help" href="https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#SubtleCrypto-method-encrypt">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="aes_cbc_vectors.js"></script>
<script src="aes.js"></script>
<h1>encrypt Tests for AES-CBC</h1>
<div id="log"></div>
<script>
run_test();
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>WebCryptoAPI: encrypt() Using AES-CTR</title>
<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
<link rel="help" href="https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#SubtleCrypto-method-encrypt">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="aes_ctr_vectors.js"></script>
<script src="aes.js"></script>
<h1>encrypt Tests for AES-CTR</h1>
<div id="log"></div>
<script>
run_test();
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>WebCryptoAPI: encrypt() Using AES-GCM</title>
<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
<link rel="help" href="https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#SubtleCrypto-method-encrypt">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="aes_gcm_vectors.js"></script>
<script src="aes.js"></script>
<h1>encrypt Tests for AES-GCM</h1>
<div id="log"></div>
<script>
run_test();
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>WebCryptoAPI: encrypt() Using RSA-OAEP</title>
<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
<link rel="help" href="https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#SubtleCrypto-method-encrypt">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="rsa_vectors.js"></script>
<script src="rsa.js"></script>
<h1>encrypt Tests for RSA-OAEP</h1>
<div id="log"></div>
<script>
run_test();
</script>

View file

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html>
<head>
<link rel="help" href="https://xhr.spec.whatwg.org/#handler-xhr-onprogress" data-tested-assertations="../.." />
<link rel="help" href="https://xhr.spec.whatwg.org/#event-xhr-progress" data-tested-assertations="../.." />
<link rel="help" href="https://xhr.spec.whatwg.org/#the-send()-method" data-tested-assertations="following::dt[@id="dom-xmlhttprequest-send-bodyinit"]/following::dd[1]/p[2] following::ol[1]/li[9]//li[1] following::ol[1]/li[9]//li[2]" />
<link rel="help" href="https://fetch.spec.whatwg.org/#http-fetch" data-tested-assertations="following::ol[1]/li[6]/dl/dd[1]//dd[3]" />
<link rel="help" href="https://fetch.spec.whatwg.org/#concept-http-redirect-fetch" data-tested-assertations="following::li[16]" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>XMLHttpRequest: The send() method: POSTing to URL that redirects</title>
</head>
<body>
<div id="log"></div>
<script type="text/javascript">
function testRedirectPost(code, shouldResendPost) {
var test = async_test(document.title + " (" + code + ")");
var actual = [];
// We check upload.onprogress with a boolean because it *might* fire more than once
var progressFiredReadyState1 = false;
var expectedHeaders, expectedEvents;
// 307 redirects should resend the POST data, and events and headers will be a little different..
if(shouldResendPost) {
expectedHeaders = {
"X-Request-Content-Length": "11988",
"X-Request-Content-Type": "text/plain;charset=UTF-8",
"X-Request-Method": "POST",
"X-Request-Query": "NO",
"Content-Length": "11988"
}
expectedEvents = [
"xhr onreadystatechange 1",
"xhr loadstart 1",
"upload loadstart 1",
"upload loadend 1",
"xhr onreadystatechange 2",
"xhr onreadystatechange 3",
"xhr onreadystatechange 4",
"xhr load 4",
"xhr loadend 4"
];
} else {
// setting the right expectations for POST resent as GET without request body
expectedHeaders = {
"X-Request-Content-Length": "NO",
"X-Request-Content-Type": "NO",
"X-Request-Method": "GET",
"X-Request-Query": "NO"
}
expectedEvents = [
"xhr onreadystatechange 1",
"xhr loadstart 1",
"upload loadstart 1",
"upload loadend 1",
"xhr onreadystatechange 2",
/* we expect no onreadystatechange readyState=3 event because there is no loading content */
"xhr onreadystatechange 4",
"xhr load 4",
"xhr loadend 4"
];
}
test.step(function()
{
var xhr = new XMLHttpRequest();
xhr.upload.onloadstart = test.step_func(function(e) {
actual.push("upload loadstart " + xhr.readyState);
});
xhr.upload.onprogress = test.step_func(function(e) {
// events every 50ms, one final when uploading is done
if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
assert_equals(xhr.status, 200, "JS never gets to see the 30x status code");
}
progressFiredReadyState1 = xhr.readyState === xhr.OPENED;
});
xhr.upload.onloadend = test.step_func(function() {
actual.push("upload loadend " + xhr.readyState);
});
xhr.onloadstart = test.step_func(function() {
actual.push("xhr loadstart " + xhr.readyState);
});
xhr.onreadystatechange = test.step_func(function() {
if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
assert_equals(xhr.status, 200, "JS never gets to see the 30x status code");
}
actual.push("xhr onreadystatechange " + xhr.readyState);
});
xhr.onload = test.step_func(function(e)
{
actual.push("xhr load " + xhr.readyState);
});
xhr.onloadend = test.step_func(function(e)
{
actual.push("xhr loadend " + xhr.readyState);
assert_true(progressFiredReadyState1, "One progress event should fire on xhr.upload when readyState is 1");
// Headers will tell us if data was sent when expected
for(var header in expectedHeaders) {
assert_equals(xhr.getResponseHeader(header), expectedHeaders[header], header);
}
assert_array_equals(actual, expectedEvents, "events firing in expected order and states");
test.done();
});
xhr.open("POST", "./resources/redirect.py?location=content.py&code=" + code, true);
xhr.send((new Array(1000)).join("Test Message"));
});
}
testRedirectPost(301, false);
testRedirectPost(302, false);
testRedirectPost(303, false);
testRedirectPost(307, true);
</script>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<script>
var x = 0;
</script>
<!-- This script's URI is:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'data:text/plain,aaa', false);
xhr.send();
x=1;
-->
<script defer src="data:application/javascript,var%20x%20=%200;%20var%20xhr%20=%20new%20XMLHttpRequest();%20xhr.open('GET',%20'data:text/plain,aaa',%20false);%20xhr.send();%20x=1"></script>
<!-- This script's URI is:
parent.postMessage(x, '*');
-->
<script defer src="data:application/javascript,parent.postMessage(x, '*');"></script>

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<title>Check that a sync XHR in a defer script blocks later defer scripts from running</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<!--
We run the test in a subframe, because something in the testharness stuff
interferes with defer scripts -->
<script>
var t = async_test();
onmessage = t.step_func_done(function(e) {
assert_equals(e.data, 1);
});
</script>
<iframe src="xmlhttprequest-sync-block-defer-scripts-subframe.html"></iframe>

View file

@ -0,0 +1,22 @@
<!doctype html>
<meta charset=utf-8>
<title>Check that while a sync XHR is in flight async script loads don't complete and run script</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<body>
<script>
var scriptRan = false;
var onloadFired = false;
test(function() {
var s = document.createElement("script");
s.src = "data:application/javascript,scriptRan = true;";
s.onload = function() { onloadFired = true; }
document.body.appendChild(s);
var xhr = new XMLHttpRequest();
xhr.open("GET", "data:,", false);
xhr.send();
assert_false(scriptRan, "Script should not have run");
assert_false(onloadFired, "load event for <script> should not have fired");
});
</script>
</body>

View file

@ -0,0 +1,80 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Battery Test: iframe has a different Navigator object</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
iframe {
display: none;
}
</style>
<div id="log"></div>
<iframe id="blank" src="about:blank"></iframe>
<iframe id="frame"></iframe>
<script>
promise_test(function(t) {
var iframe = document.querySelector('#blank');
var originalPromise = navigator.getBattery();
return originalPromise.then(function(originalManager) {
var promise = iframe.contentWindow.navigator.getBattery();
assert_true(originalManager instanceof BatteryManager);
assert_not_equals(iframe.contentWindow.navigator,
navigator,
'navigator objects shall be different');
assert_not_equals(promise,
originalPromise,
'battery promises in different navigators shall be different');
assert_equals(iframe.contentWindow.navigator.getBattery(),
promise,
'battery promises in same navigator shall be same');
return promise;
}).then(function(manager) {
assert_equals(manager.__proto__,
iframe.contentWindow.BatteryManager.prototype);
assert_true(manager instanceof iframe.contentWindow.BatteryManager);
});
}, 'iframe has a different Navigator object thus getting another battery promise');
async_test(function (t) {
var iframe = document.querySelector('#blank');
var originalNavigator = iframe.contentWindow.navigator;
var originalPromise = iframe.contentWindow.navigator.getBattery();
iframe.onload = t.step_func(function() {
assert_equals(iframe.contentWindow.navigator,
originalNavigator,
'navigator objects shall be same');
assert_equals(iframe.contentWindow.navigator.getBattery(),
originalPromise,
'battery status promises shall be same');
t.done();
});
iframe.src = 'support-iframe.html';
}, 'setting src of an iframe with initial about:blank makes same Navigator object and battery promise');
async_test(function (t) {
var iframe = document.querySelector('#frame');
iframe.src = 'support-iframe-initial.html';
iframe.onload = t.step_func(function() {
var originalNavigator = iframe.contentWindow.navigator;
var originalPromise = iframe.contentWindow.navigator.getBattery();
iframe.onload = t.step_func(function() {
assert_not_equals(iframe.contentWindow.navigator,
originalNavigator,
'navigator objects shall be changed');
assert_not_equals(iframe.contentWindow.navigator.getBattery(),
originalPromise,
'battery status promises shall be different');
t.done();
});
iframe.src = 'support-iframe.html';
});
}, 'setting src of an iframe with initial frame makes its Navigator object vary thus getting another battery promise');
</script>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Battery Test: window.open() makes a different Navigator object</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#note {
background-color: #fef1b5;
border: solid 1px #cdab2d;
padding: 5px;
margin: 15px;
display: block;
}
</style>
<div id="note">
Allow pop-up windows before running the tests.
</div>
<div id="log"></div>
<script>
async_test(function (t) {
var win = window.open('support-window-open.html');
window.onmessage = t.step_func(function(e) {
assert_array_equals(e.data, [false, false, true]);
win.close();
t.done();
});
}, 'window.open() makes a different Navigator object thus getting another battery promise');
</script>

View file

@ -1,26 +1,10 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Battery Test: each Navigator object has a battery promise for BatteryManager</title>
<title>Battery Test: navigator.getBattery() always return same battery promise</title>
<link rel="author" title="YuichiNukiyama" href="https://github.com/YuichiNukiyama">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#note {
background-color: #fef1b5;
border: solid 1px #cdab2d;
padding: 5px;
margin: 15px;
display: block;
}
iframe {
display: none;
}
</style>
<div id="note">
Allow pop-up windows before running the tests.
</div>
<div id="log"></div>
<iframe src="about:blank"></iframe>
<script>
promise_test(function () {
return navigator.getBattery().then(function (result) {
@ -32,57 +16,4 @@ promise_test(function () {
test(function () {
assert_equals(navigator.getBattery(), navigator.getBattery());
}, 'navigator.getBattery() shall always return the same promise');
promise_test(function(t) {
var iframe = document.querySelector('iframe');
var originalPromise = navigator.getBattery();
return originalPromise.then(function(originalManager) {
var promise = iframe.contentWindow.navigator.getBattery();
assert_true(originalManager instanceof BatteryManager);
assert_not_equals(iframe.contentWindow.navigator,
navigator,
'navigator objects shall be different');
assert_not_equals(promise,
originalPromise,
'battery promises in different navigators shall be different');
assert_equals(iframe.contentWindow.navigator.getBattery(),
promise,
'battery promises in same navigator shall be same');
return promise;
}).then(function(manager) {
assert_equals(manager.__proto__,
iframe.contentWindow.BatteryManager.prototype);
assert_true(manager instanceof iframe.contentWindow.BatteryManager);
});
}, 'iframe has a different Navigator object thus getting another battery promise');
async_test(function (t) {
var iframe = document.querySelector('iframe');
var originalNavigator = iframe.contentWindow.navigator;
var originalPromise = iframe.contentWindow.navigator.getBattery();
iframe.onload = t.step_func(function() {
assert_not_equals(iframe.contentWindow.navigator,
originalNavigator,
'navigator objects shall be changed');
assert_not_equals(iframe.contentWindow.navigator.getBattery(),
originalPromise,
'battery status promises shall be different');
t.done();
});
iframe.src = 'support-iframe.html';
}, 'setting iframe\'s src makes its Navigator object vary thus getting another battery promise');
async_test(function (t) {
var win = window.open('support-window-open.html');
window.onmessage = t.step_func(function(e) {
assert_array_equals(e.data, [false, false, true]);
win.close();
t.done();
});
}, 'window.open() makes a different Navigator object thus getting another battery promise');
</script>

View file

@ -0,0 +1,5 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<div>
Hello!
</div>

View file

@ -1944,9 +1944,12 @@
"html/elements/source/src/userinfo-username-contains-pile-of-poo-novalid.html": "Bad value \u201chttp://\ud83d\udca9:foo@example.com\u201d for attribute \u201csrc\u201d on element \u201csource\u201d: Bad URL: Illegal character in user or password: \u201c\ud83d\udca9\u201d is not allowed.",
"html/elements/span/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
"html/elements/strong/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
"html/elements/style/scoped-as-div-child-novalid.html": "Element \u201cstyle\u201d not allowed in this context. (The parent was element \u201cdiv\u201d.) Suppressing further errors from this subtree.",
"html/elements/style/scoped-as-p-child-novalid.html": "Element \u201cstyle\u201d not allowed in this context. (The parent was element \u201cp\u201d.) Suppressing further errors from this subtree.",
"html/elements/style/scoped-in-head-novalid.html": "Attribute \u201cscoped\u201d not allowed on element \u201cstyle\u201d in this context.",
"html/elements/style/scoped-model-novalid.html": "Element \u201cstyle\u201d not allowed in this context. (The parent was element \u201cdiv\u201d.) Suppressing further errors from this subtree.",
"html/elements/style/scoped-multiple-adjacent-novalid.html": "Element \u201cstyle\u201d not allowed in this context. (The parent was element \u201cdiv\u201d.) Suppressing further errors from this subtree.",
"html/elements/style/scoped-novalid.html": "Element \u201cstyle\u201d not allowed in this context. (The parent was element \u201cdiv\u201d.) Suppressing further errors from this subtree.",
"html/elements/sub/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
"html/elements/sup/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
"html/elements/table/model-input-child-hidden-novalid.html": "Start tag \u201cinput\u201d seen in \u201ctable\u201d.",

View file

@ -67,7 +67,7 @@ async_test("getResponseHeader: Combined testing of cors response headers")
}
if (client.readyState > 1)
{
assert_equals(client.getResponseHeader("x-custom-header"), "test", 'x-custom-header')
assert_equals(client.getResponseHeader("x-custom-header"), "test, test", 'x-custom-header')
assert_equals(client.getResponseHeader("x-custom-header-empty"), "", 'x-custom-header-empty')
assert_equals(client.getResponseHeader("set-cookie"), null)
assert_equals(client.getResponseHeader("set-cookie2"), null)
@ -86,7 +86,7 @@ test(function() {
var client = new XMLHttpRequest()
client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false)
client.send(null)
assert_equals(client.getResponseHeader("x-custom-header"), "test", 'x-custom-header')
assert_equals(client.getResponseHeader("x-custom-header"), "test, test", 'x-custom-header')
assert_equals(client.getResponseHeader("x-nonexposed"), null, 'x-nonexposed')
}, "getResponse: don't expose x-nonexposed")

View file

@ -161,7 +161,15 @@
// 8. Let observedAttributesIterable be Get(constructor, "observedAttributes").
// Rethrow any exceptions.
// See step 12 for rethrow tests.
test(() => {
class C {
static get observedAttributes() { throw_rethrown_error(); }
attributeChangedCallback() {}
}
assert_rethrown(() => {
customElements.define('test-define-observedattributes-rethrow', C);
});
}, 'If constructor.observedAttributes throws, should rethrow');
// 10. Let prototype be Get(constructor, "prototype"). Rethrow any exceptions.
function assert_rethrown(func, description) {
@ -208,42 +216,38 @@
// 17. If attributeChangedCallback is not undefined, and IsCallable(attributeChangedCallback) is false,
// then throw a TypeError exception.
[
'observedAttributes', // See step 8 above.
'connectedCallback',
'disconnectedCallback',
'attributeChangedCallback',
].forEach(name => {
test(() => {
].forEach(name => {
test(() => {
class C {
get [name]() { throw_rethrown_error(); }
}
assert_rethrown(() => {
customElements.define('test-define-constructor-rethrow-prototype-' + name, C);
assert_rethrown(() => {
customElements.define(`test-define-${name.toLowerCase()}-rethrow`, C);
});
}, `If constructor.prototype.${name} throws, should rethrow`);
});
[
'connectedCallback',
'disconnectedCallback',
'attributeChangedCallback',
].forEach(name => {
test(() => {
class c {};
c.prototype[name] = undefined;
customElements.define('test-define-constructor-prototype-' + name, c);
}, `If constructor.prototype.${name} is undefined, should success`);
[
[ 'null', null ],
[ 'object', {} ],
].forEach(value => {
test(() => {
class c {};
c.prototype[name] = value[1];
assert_throws(expectTypeError, () => {
customElements.define('test-define-constructor-prototype-' + name, c);
});
}, `If constructor.prototype.${name} is ${value[0]}, should throw a TypeError`);
})
{ name: 'undefined', value: undefined, success: true },
{ name: 'function', value: function () { }, success: true },
{ name: 'null', value: null, success: false },
{ name: 'object', value: {}, success: false },
{ name: 'integer', value: 1, success: false },
].forEach(data => {
test(() => {
class C { };
C.prototype[name] = data.value;
if (data.success) {
customElements.define(`test-define-${name.toLowerCase()}-${data.name}`, C);
} else {
assert_throws(expectTypeError, () => {
customElements.define(`test-define-${name.toLowerCase()}-${data.name}`, C);
});
}
}, `If constructor.prototype.${name} is ${data.name}, should ${data.success ? 'succeed' : 'throw a TypeError'}`);
});
});
})();
</script>

View file

@ -0,0 +1,113 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>EventListenerOptions.passive</title>
<link rel="author" title="Rick Byers" href="mailto:rbyers@chromium.org">
<link rel="help" href="https://dom.spec.whatwg.org/#dom-addeventlisteneroptions-passive">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
test(function() {
var supportsPassive = false;
var query_options = {
get passive() {
supportsPassive = true;
return false;
},
get dummy() {
assert_unreached("dummy value getter invoked");
return false;
}
};
document.addEventListener('test_event', null, query_options);
assert_true(supportsPassive, "addEventListener doesn't support the passive option");
supportsPassive = false;
document.removeEventListener('test_event', null, query_options);
assert_false(supportsPassive, "removeEventListener supports the passive option when it should not");
}, "Supports passive option on addEventListener only");
function testPassiveValue(optionsValue, expectedDefaultPrevented) {
var defaultPrevented = undefined;
var handler = function handler(e) {
assert_false(e.defaultPrevented, "Event prematurely marked defaultPrevented");
e.preventDefault();
defaultPrevented = e.defaultPrevented;
}
document.addEventListener('test', handler, optionsValue);
var uncanceled = document.body.dispatchEvent(new Event('test', {bubbles: true, cancelable: true}));
assert_equals(defaultPrevented, expectedDefaultPrevented, "Incorrect defaultPrevented for options: " + JSON.stringify(optionsValue));
assert_equals(uncanceled, !expectedDefaultPrevented, "Incorrect return value from dispatchEvent");
document.removeEventListener('test', handler, optionsValue);
}
test(function() {
testPassiveValue(undefined, true);
testPassiveValue({}, true);
testPassiveValue({passive: false}, true);
testPassiveValue({passive: true}, false);
testPassiveValue({passive: 0}, true);
testPassiveValue({passive: 1}, false);
}, "preventDefault should be ignored if-and-only-if the passive option is true");
function testPassiveWithOtherHandlers(optionsValue, expectedDefaultPrevented) {
var handlerInvoked1 = false;
var dummyHandler1 = function() {
handlerInvoked1 = true;
};
var handlerInvoked2 = false;
var dummyHandler2 = function() {
handlerInvoked2 = true;
};
document.addEventListener('test', dummyHandler1, {passive:true});
document.addEventListener('test', dummyHandler2);
testPassiveValue(optionsValue, expectedDefaultPrevented);
assert_true(handlerInvoked1, "Extra passive handler not invoked");
assert_true(handlerInvoked2, "Extra non-passive handler not invoked");
document.removeEventListener('test', dummyHandler1);
document.removeEventListener('test', dummyHandler2);
}
test(function() {
testPassiveWithOtherHandlers({}, true);
testPassiveWithOtherHandlers({passive: false}, true);
testPassiveWithOtherHandlers({passive: true}, false);
}, "passive behavior of one listener should be unaffeted by the presence of other listeners");
function testOptionEquivalence(optionValue1, optionValue2, expectedEquality) {
var invocationCount = 0;
var handler = function handler(e) {
invocationCount++;
}
document.addEventListener('test', handler, optionValue1);
document.addEventListener('test', handler, optionValue2);
document.body.dispatchEvent(new Event('test', {bubbles: true}));
assert_equals(invocationCount, expectedEquality ? 1 : 2, "equivalence of options " +
JSON.stringify(optionValue1) + " and " + JSON.stringify(optionValue2));
document.removeEventListener('test', handler, optionValue1);
document.removeEventListener('test', handler, optionValue2);
}
test(function() {
// Sanity check options that should be treated as distinct handlers
testOptionEquivalence({capture:true}, {capture:false, passive:false}, false);
testOptionEquivalence({capture:true}, {passive:true}, false);
// Option values that should be treated as equivalent
testOptionEquivalence({}, {passive:false}, true);
testOptionEquivalence({passive:true}, {passive:false}, true);
testOptionEquivalence(undefined, {passive:true}, true);
testOptionEquivalence({capture: true, passive: false}, {capture: true, passive: true}, true);
}, "Equivalence of option values");
</script>

View file

@ -0,0 +1,19 @@
<!doctype html>
<title>CustomEvent</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
test(function() {
var type = "foo";
var target = document.createElement("div");
target.addEventListener(type, this.step_func(function(evt) {
assert_equals(evt.type, type);
}), true);
var fooEvent = document.createEvent("CustomEvent");
fooEvent.initEvent(type, true, true);
target.dispatchEvent(fooEvent);
});
</script>

View file

@ -32,7 +32,7 @@ test(function() {
ev.preventDefault();
assert_equals(ev.cancelable, true, "cancelable (after)");
assert_equals(ev.defaultPrevented, true, "defaultPrevented");
}, "preventDefault() should change defaultPrevented if cancelable is false.");
}, "preventDefault() should change defaultPrevented if cancelable is true.");
test(function() {
ev.initEvent("foo", true, true);
assert_equals(ev.bubbles, true, "bubbles");

View file

@ -3,7 +3,7 @@
<title></title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe src="{{location[scheme]}}://{{domains[www1]}}:{{ports[http][0]}}{{location[path]}}/../EventListener-incumbent-global-subframe.sub.html"></iframe>
<iframe src="{{location[scheme]}}://{{domains[www1]}}:{{ports[http][0]}}{{location[path]}}/../EventListener-incumbent-global-subframe-1.sub.html"></iframe>
<script>
var t = async_test("Check the incumbent global EventListeners are called with");

View file

@ -0,0 +1,20 @@
<!doctype html>
<meta charset=utf-8>
<title></title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe src="{{location[scheme]}}://{{domains[www1]}}:{{ports[http][0]}}{{location[path]}}/../EventListener-incumbent-global-subframe-2.sub.html"></iframe>
<script>
var t = async_test("Check the incumbent global EventListeners are called with");
onload = t.step_func(function() {
onmessage = t.step_func_done(function(e) {
var d = e.data;
assert_equals(d.actual, d.expected, d.reason);
});
frames[0].postMessage("start", "*");
});
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<iframe src="{{location[scheme]}}://{{domains[www2]}}:{{ports[http][0]}}{{location[path]}}/../EventListener-incumbent-global-subsubframe.sub.html"></iframe>
<script>
document.domain = "{{host}}";
onmessage = function(e) {
if (e.data == "start") {
frames[0].document.body.addEventListener("click", frames[0].getTheListener());
frames[0].postMessage("sendclick", "*");
} else {
parent.postMessage(e.data, "*");
}
}
</script>

View file

@ -1,5 +1,8 @@
<!DOCTYPE html>
<script>
function getTheListener() {
return postMessage.bind(this, "respond", "*", undefined)
}
document.domain = "{{host}}";
onmessage = function (e) {
if (e.data == "sendclick") {

View file

@ -16,9 +16,9 @@ function testCaptureValue(captureValue, expectedValue) {
handlerPhase = e.eventPhase;
}
document.addEventListener('test', handler, captureValue);
document.body.dispatchEvent(new Event('test', {'bubbles': true}));
document.body.dispatchEvent(new Event('test', {bubbles: true}));
document.removeEventListener('test', handler, captureValue);
document.body.dispatchEvent(new Event('test', {'bubbles': true}));
document.body.dispatchEvent(new Event('test', {bubbles: true}));
assert_equals(handlerPhase, expectedValue, "Incorrect event phase for value: " + JSON.stringify(captureValue));
}
@ -72,7 +72,7 @@ function testOptionEquality(addOptionValue, removeOptionValue, expectedEquality)
}
document.addEventListener('test', handler, addOptionValue);
document.removeEventListener('test', handler, removeOptionValue);
document.body.dispatchEvent(new Event('test', {'bubbles': true}));
document.body.dispatchEvent(new Event('test', {bubbles: true}));
assert_equals(!handlerInvoked, expectedEquality, "equivalence of options " +
JSON.stringify(addOptionValue) + " and " + JSON.stringify(removeOptionValue));
if (handlerInvoked)

View file

@ -117,7 +117,8 @@ var nodeNuked = [
"isSupported",
"getFeature",
"getUserData",
"setUserData"
"setUserData",
"rootNode",
]
nodeNuked.forEach(isNukedFromNode)

View file

@ -212,7 +212,6 @@ interface Node : EventTarget {
readonly attribute boolean isConnected;
readonly attribute Document? ownerDocument;
readonly attribute Node rootNode;
readonly attribute Node? parentNode;
readonly attribute Element? parentElement;
boolean hasChildNodes();

View file

@ -0,0 +1,95 @@
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Node.prototype.isConnected</title>
<link rel=help href="https://dom.spec.whatwg.org/#dom-node-isconnected">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
"use strict";
test(function() {
var nodes = [document.createElement("div"),
document.createElement("div"),
document.createElement("div")];
checkNodes([], nodes);
// Append nodes[0].
document.body.appendChild(nodes[0]);
checkNodes([nodes[0]],
[nodes[1], nodes[2]]);
// Append nodes[1] and nodes[2] together.
nodes[1].appendChild(nodes[2]);
checkNodes([nodes[0]],
[nodes[1], nodes[2]]);
nodes[0].appendChild(nodes[1]);
checkNodes(nodes, []);
// Remove nodes[2].
nodes[2].remove();
checkNodes([nodes[0], nodes[1]],
[nodes[2]]);
// Remove nodes[0] and nodes[1] together.
nodes[0].remove();
checkNodes([], nodes);
}, "Test with ordinary child nodes");
test(function() {
var nodes = [document.createElement("iframe"),
document.createElement("iframe"),
document.createElement("iframe"),
document.createElement("iframe"),
document.createElement("div")];
var frames = [nodes[0],
nodes[1],
nodes[2],
nodes[3]];
checkNodes([], nodes);
// Since we cannot append anything to the contentWindow of an iframe before it
// is appended to the main DOM tree, we append the iframes one after another.
document.body.appendChild(nodes[0]);
checkNodes([nodes[0]],
[nodes[1], nodes[2], nodes[3], nodes[4]]);
frames[0].contentDocument.body.appendChild(nodes[1]);
checkNodes([nodes[0], nodes[1]],
[nodes[2], nodes[3], nodes[4]]);
frames[1].contentDocument.body.appendChild(nodes[2]);
checkNodes([nodes[0], nodes[1], nodes[2]],
[nodes[3], nodes[4]]);
frames[2].contentDocument.body.appendChild(nodes[3]);
checkNodes([nodes[0], nodes[1], nodes[2], nodes[3]],
[nodes[4]]);
frames[3].contentDocument.body.appendChild(nodes[4]);
checkNodes(nodes, []);
frames[3].remove();
// Since node[4] is still under the doument of frame[3], it's still connected.
checkNodes([nodes[0], nodes[1], nodes[2], nodes[4]],
[nodes[3]]);
frames[0].remove();
// Since node[1] and node[2] are still under the doument of frame[0], they are
// still connected.
checkNodes([nodes[1], nodes[2], nodes[4]],
[nodes[0], nodes[3]]);
}, "Test with iframes");
// This helper function is used to check whether nodes should be connected.
function checkNodes(aConnectedNodes, aDisconnectedNodes) {
aConnectedNodes.forEach(node => assert_true(node.isConnected));
aDisconnectedNodes.forEach(node => assert_false(node.isConnected));
}
</script>
</body>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="support/style.css" type="text/css"?>
<?xml-stylesheet href="data:text/css,&#x41;&amp;&apos;" type="text/css"?>
<?xml-stylesheet href="data:text/css,&#65;&amp;&apos;" type="text/css"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ProcessingInstruction numeric escapes</title>
<link rel="help" href="https://dom.spec.whatwg.org/#dom-processinginstruction-target"/>
<link rel="help" href="https://dom.spec.whatwg.org/#dom-characterdata-data"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"/>
<script>
<![CDATA[
test(function() {
var pienc = document.firstChild.nextSibling;
assert_true(pienc instanceof ProcessingInstruction)
assert_equals(pienc.target, "xml-stylesheet")
assert_equals(pienc.data, 'href="data:text/css,&#x41;&amp;&apos;" type="text/css"')
assert_equals(pienc.sheet.href, "data:text/css,A&'");
pienc = pienc.nextSibling;
assert_true(pienc instanceof ProcessingInstruction)
assert_equals(pienc.target, "xml-stylesheet")
assert_equals(pienc.data, 'href="data:text/css,&#65;&amp;&apos;" type="text/css"')
assert_equals(pienc.sheet.href, "data:text/css,A&'");
})
]]>
</script>
</body>
</html>

View file

@ -1,82 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>Node.prototype.rootNode</title>
<link rel="help" href="https://dom.spec.whatwg.org/#dom-node-rootnode">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
test(function () {
var element = document.createElement('div');
assert_equals(element.rootNode, element, 'rootNode on an element without a parent must return the element itself');
var text = document.createTextNode('');
assert_equals(text.rootNode, text, 'rootNode on a text node without a parent must return the text node itself');
var processingInstruction = document.createProcessingInstruction('target', 'data');
assert_equals(processingInstruction.rootNode, processingInstruction, 'rootNode on a processing instruction node without a parent must return the processing instruction node itself');
assert_equals(document.rootNode, document, 'rootNode on a document node must return the document itself');
}, 'rootNode attribute must return the context object when it does not have any parent');
test(function () {
var parent = document.createElement('div');
var element = document.createElement('div');
parent.appendChild(element);
assert_equals(element.rootNode, parent, 'rootNode on an element with a single ancestor must return the parent node');
var text = document.createTextNode('');
parent.appendChild(text);
assert_equals(text.rootNode, parent, 'rootNode on a text node with a single ancestor must return the parent node');
var processingInstruction = document.createProcessingInstruction('target', 'data');
parent.appendChild(processingInstruction)
assert_equals(processingInstruction.rootNode, parent, 'rootNode on a processing instruction node with a single ancestor must return the parent node');
}, 'rootNode attribute must return the parent node of the context object when the context object has a single ancestor not in a document');
test(function () {
var parent = document.createElement('div');
document.body.appendChild(parent);
var element = document.createElement('div');
parent.appendChild(element);
assert_equals(element.rootNode, document, 'rootNode on an element inside a document must return the document');
var text = document.createTextNode('');
parent.appendChild(text);
assert_equals(text.rootNode, document, 'rootNode on a text node inside a document must return the document');
var processingInstruction = document.createProcessingInstruction('target', 'data');
parent.appendChild(processingInstruction)
assert_equals(processingInstruction.rootNode, document, 'rootNode on a processing instruction node inside a document must return the document');
}, 'rootNode attribute must return the document when a node is in document');
test(function () {
var fragment = document.createDocumentFragment();
var parent = document.createElement('div');
fragment.appendChild(parent);
var element = document.createElement('div');
parent.appendChild(element);
assert_equals(element.rootNode, fragment, 'rootNode on an element inside a document fragment must return the fragment');
var text = document.createTextNode('');
parent.appendChild(text);
assert_equals(text.rootNode, fragment, 'rootNode on a text node inside a document fragment must return the fragment');
var processingInstruction = document.createProcessingInstruction('target', 'data');
parent.appendChild(processingInstruction)
assert_equals(processingInstruction.rootNode, fragment,
'rootNode on a processing instruction node inside a document fragment must return the fragment');
}, 'rootNode attribute must return a document fragment when a node is in the fragment');
</script>
</body>
</html>

View file

@ -45,7 +45,7 @@ var badStrings = [
badStrings.forEach(function(t) {
test(function() {
var encoded = new TextEncoder('utf-8').encode(t.input);
var encoded = new TextEncoder().encode(t.input);
assert_array_equals([].slice.call(encoded), t.expected);
assert_equals(new TextDecoder('utf-8').decode(encoded), t.decoded);
}, 'Invalid surrogates encoded into UTF-8: ' + t.name);

View file

@ -7,6 +7,9 @@
var string = '\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF';
var octets = {
'utf-8': [0x00,0x31,0x32,0x33,0x41,0x42,0x43,0x61,0x62,0x63,0xc2,0x80,
0xc3,0xbf,0xc4,0x80,0xe1,0x80,0x80,0xef,0xbf,0xbd,0xf0,0x90,
0x80,0x80,0xf4,0x8f,0xbf,0xbf],
'utf-16le': [0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x41,0x00,0x42,0x00,
0x43,0x00,0x61,0x00,0x62,0x00,0x63,0x00,0x80,0x00,0xFF,0x00,
0x00,0x01,0x00,0x10,0xFD,0xFF,0x00,0xD8,0x00,0xDC,0xFF,0xDB,
@ -17,11 +20,10 @@ var octets = {
0xDF,0xFF]
};
utf_encodings.forEach(function (encoding) {
Object.keys(octets).forEach(function(encoding) {
for (var len = 1; len <= 5; ++len) {
test(function() {
var encoded = octets[encoding] ||
new TextEncoder(encoding).encode(string);
var encoded = octets[encoding];
var out = '';
var decoder = new TextDecoder(encoding);

View file

@ -37,18 +37,16 @@ var bad = [
}
];
var encoding = 'utf-8';
bad.forEach(function(t) {
test(function() {
var encoded = new TextEncoder(encoding).encode(t.input);
var decoded = new TextDecoder(encoding).decode(encoded);
var encoded = new TextEncoder().encode(t.input);
var decoded = new TextDecoder().decode(encoded);
assert_equals(decoded, t.expected);
}, 'USVString handling: ' + t.name);
});
test(function() {
assert_equals(new TextEncoder(encoding).encode().length, 0, 'Should default to empty string');
assert_equals(new TextEncoder().encode().length, 0, 'Should default to empty string');
}, 'USVString default');
</script>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test asynchronous creation of MediaKeys and MediaKeySession while running garbage collection</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
// Run garbage collection often.
setInterval(asyncGC, 0);
var initDataType;
var initData;
var mediaKeySession;
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
return mediaKeySession.generateRequest(initDataType, initData);
}).then(function() {
return mediaKeySession.close();
}).then(function(result) {
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Test asynchronous creation of MediaKeys and MediaKeySession while running garbage collection.');
</script>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test asynchronous setServerCertificate while running garbage collection</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// Run garbage collection continuously.
setInterval(asyncGC, 0);
promise_test(function(test)
{
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var cert = new Uint8Array(200);
return mediaKeys.setServerCertificate(cert);
}).then(function(result) {
assert_false(result);
});
}, 'Test asynchronous setServerCertificate while running garbage collection.');
</script>
</body>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test support of different initDataTypes.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
function checkInitDataType(initDataType)
{
return isInitDataTypeSupported(initDataType).then(function(result) {
// If |initDataType| is not supported, simply succeed.
if (!result)
return Promise.resolve('Not supported');
var options = [ { initDataTypes: [initDataType] } ];
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', options)
.then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var mediaKeySession = mediaKeys.createSession();
var initData = getInitData(initDataType);
return mediaKeySession.generateRequest(initDataType, initData);
});
});
}
promise_test(function()
{
return checkInitDataType('webm');
}, 'Clear key support for "webm".');
promise_test(function()
{
return checkInitDataType('cenc');
}, 'Clear key support for "cenc".');
promise_test(function()
{
return checkInitDataType('keyids');
}, 'Clear key support for "keyids".');
</script>
</body>
</html>

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Invalid Clear Key License.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
var initDataType;
var initData;
var invalidLicense = new Uint8Array(
[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]);
function handleMessage(event) {
event.target.update(invalidLicense).then(function(event) {
assert_unreached('Error: update() succeeded unexpectedly.');
test.done();
}).catch(function(error) {
assert_equals(error.name, 'InvalidAccessError');
test.done();
});
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var keySession = mediaKeys.createSession();
keySession.addEventListener('message', handleMessage, false);
keySession.generateRequest(initDataType, initData);
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Invalid Clear Key License.');
</script>
</body>
</html>

View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test Clear Key handling of non-ASCII responses for update().</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// This test passes |response| to update() as a JSON Web Key Set.
// CDMs other than Clear Key won't expect |response| in this format.
async_test(function(test)
{
var initDataType;
var mediaKeySession;
function processMessage(event)
{
// |jwkSet| includes some non-ASCII characters.
var jwkSet = '{"keys":[{'
+ '"kty":"oct\uDC00\uD800",'
+ '"k":"MDEyMzQ1Njc4OTAxMjM0NQ",'
+ '"kid":"MDEyMzQ1Njc4OTAxMjM0NQ"'
+ '\xff\xfe}]';
mediaKeySession.update(stringToUint8Array(jwkSet)).then(function() {
forceTestFailureFromPromise(test, 'Error: update() succeeded');
}, function(error) {
assert_equals(error.name, 'InvalidAccessError');
test.done();
});
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, processMessage, test);
return mediaKeySession.generateRequest(initDataType, getInitData(initDataType));
});
}, 'Clear Key update() with non-ASCII response.');
</script>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Verify v2 events</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// Currently Clear Key only generates aynchronous "message" and
// "keychange" events.
async_test(function(test)
{
var initDataType;
var initData;
var mediaKeySession;
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
function processMessage(event)
{
assert_true(event instanceof window.MediaKeyMessageEvent);
assert_equals(event.target, mediaKeySession);
assert_equals(event.type, 'message');
assert_equals(event.messageType, 'license-request');
var keyId = extractSingleKeyIdFromMessage(event.message);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
waitForEventAndRunStep('keystatuseschange', mediaKeySession, test.step_func(processKeyStatusesChange), test);
mediaKeySession.update(jwkSet).catch(test.step_func(function(error) {
forceTestFailureFromPromise(test, error);
}));
}
function processKeyStatusesChange(event)
{
assert_true(event instanceof Event);
assert_equals(event.target, mediaKeySession);
assert_equals(event.type, 'keystatuseschange');
test.done();
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(test.step_func(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, test.step_func(processMessage), test);
return mediaKeySession.generateRequest(initDataType, initData);
})).catch(test.step_func(function(error) {
forceTestFailureFromPromise(test, error);
}));
}, 'Verify v2 events.');
</script>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test handling of invalid initData for generateRequest().</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// Create a session and call generateRequest() with |initDataType|
// and |initData|. generateRequest() should fail with an
// InvalidAccessError. Returns a promise that resolves successfully
// if the error happened, rejects otherwise.
function test_session(initDataType, initData)
{
return isInitDataTypeSupported(initDataType).then(function(result) {
// If |initDataType| is not supported, simply succeed.
if (!result)
return Promise.resolve('Not supported');
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var mediaKeySession = mediaKeys.createSession();
return mediaKeySession.generateRequest(initDataType, initData);
}).then(function() {
assert_unreached('generateRequest() succeeded');
}, function(error) {
assert_equals(error.name, 'InvalidAccessError');
return Promise.resolve('success');
});
})
}
promise_test(function()
{
var initData = new Uint8Array(70000);
return test_session('webm', initData);
}, 'generateRequest() with webm initData longer than 64Kb characters.');
promise_test(function()
{
var initData = new Uint8Array(70000);
return test_session('cenc', initData);
}, 'generateRequest() with cenc initData longer than 64Kb characters.');
promise_test(function()
{
var initData = new Uint8Array(70000);
return test_session('keyids', initData);
}, 'generateRequest() with keyids initData longer than 64Kb characters.');
promise_test(function()
{
// Invalid 'pssh' box as the size specified is larger than what
// is provided.
var initData = new Uint8Array([
0x00, 0x00, 0xff, 0xff, // size = huge
0x70, 0x73, 0x73, 0x68, // 'pssh'
0x00, // version = 0
0x00, 0x00, 0x00, // flags
0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // Common SystemID
0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B,
0x00, 0x00, 0x00, 0x00 // datasize
]);
return test_session('cenc', initData);
}, 'generateRequest() with invalid pssh data.');
promise_test(function()
{
// Invalid data as type = 'psss'.
var initData = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // size = 0
0x70, 0x73, 0x73, 0x73, // 'psss'
0x00, // version = 0
0x00, 0x00, 0x00, // flags
0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // Common SystemID
0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B,
0x00, 0x00, 0x00, 0x00 // datasize
]);
return test_session('cenc', initData);
}, 'generateRequest() with non pssh data.');
promise_test(function()
{
// Valid key ID size must be at least 1 character for keyids.
var keyId = new Uint8Array(0);
var initData = stringToUint8Array(createKeyIDs(keyId));
return test_session('keyids', initData);
}, 'generateRequest() with too short key ID.');
promise_test(function()
{
// Valid key ID size must be less than 512 characters for keyids.
var keyId = new Uint8Array(600);
var initData = stringToUint8Array(createKeyIDs(keyId));
return test_session('keyids', initData);
}, 'generateRequest() with too long key ID.');
</script>
</body>
</html>

View file

@ -0,0 +1,120 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Verify MediaKeySession.keyStatuses with multiple sessions</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
var mediaKeySession1;
var mediaKeySession2;
var initDataType;
var initData;
// Even though key ids are uint8, using printable values so that
// they can be verified easily.
var key1 = stringToUint8Array('123');
var key2 = stringToUint8Array('4567890');
var rawKey1 = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
var rawKey2 = new Uint8Array([0x3c, 0xae, 0xe4, 0xfc, 0x2a, 0x12, 0xef, 0x68,
0x7b, 0xd2, 0x14, 0x68, 0xf1, 0x62, 0xdd, 0xeb]);
function processMessage1(event)
{
// This should only be called for session1.
assert_equals(event.target, mediaKeySession1);
// No keys added yet.
verifyKeyStatuses(mediaKeySession1.keyStatuses, { expected: [], unexpected: [key1, key2] });
// Add key1 to session1.
var jwkSet = stringToUint8Array(createJWKSet(createJWK(key1, rawKey1)));
mediaKeySession1.update(jwkSet).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function processKeyStatusesChange1(event)
{
// This should only be called for session1.
assert_equals(event.target, mediaKeySession1);
// Check that keyStatuses contains the expected key1 only.
dumpKeyStatuses(mediaKeySession1.keyStatuses);
verifyKeyStatuses(mediaKeySession1.keyStatuses, { expected: [key1], unexpected: [key2] });
// Now trigger a message event on session2.
mediaKeySession2.generateRequest(initDataType, initData).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function processMessage2(event)
{
// This should only be called for session2.
assert_equals(event.target, mediaKeySession2);
// session2 has no keys added yet.
verifyKeyStatuses(mediaKeySession2.keyStatuses, { expected: [], unexpected: [key1, key2] });
// session1 should still have 1 key.
verifyKeyStatuses(mediaKeySession1.keyStatuses, { expected: [key1], unexpected: [key2] });
// Add key2 to session2.
var jwkSet = stringToUint8Array(createJWKSet(createJWK(key2, rawKey2)));
mediaKeySession2.update(jwkSet).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function processKeyStatusesChange2(event)
{
// This should only be called for session2.
assert_equals(event.target, mediaKeySession2);
// Check that keyStatuses contains the expected key2 only.
dumpKeyStatuses(mediaKeySession2.keyStatuses);
verifyKeyStatuses(mediaKeySession2.keyStatuses, { expected: [key2], unexpected: [key1] });
// session1 should still have 1 key.
verifyKeyStatuses(mediaKeySession1.keyStatuses, { expected: [key1], unexpected: [key2] });
test.done();
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession1 = mediaKeys.createSession();
mediaKeySession2 = mediaKeys.createSession();
// There should be no keys defined on either session.
verifyKeyStatuses(mediaKeySession1.keyStatuses, { expected: [], unexpected: [key1, key2] });
verifyKeyStatuses(mediaKeySession2.keyStatuses, { expected: [], unexpected: [key1, key2] });
// Bind all the event handlers now.
waitForEventAndRunStep('message', mediaKeySession1, processMessage1, test);
waitForEventAndRunStep('message', mediaKeySession2, processMessage2, test);
waitForEventAndRunStep('keystatuseschange', mediaKeySession1, processKeyStatusesChange1, test);
waitForEventAndRunStep('keystatuseschange', mediaKeySession2, processKeyStatusesChange2, test);
// Generate a request on session1.
return mediaKeySession1.generateRequest(initDataType, initData);
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Verify MediaKeySession.keyStatuses with multiple sessions.');
</script>
</body>
</html>

View file

@ -0,0 +1,86 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Verify MediaKeySession.keyStatuses with multiple updates</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
var initDataType;
var initData;
var mediaKeySession;
var firstEvent;
// Even though key ids are uint8, using printable values so that
// they can be verified easily.
var key1 = stringToUint8Array('123');
var key2 = stringToUint8Array('4567890');
var rawKey1 = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
var rawKey2 = new Uint8Array([0x3c, 0xae, 0xe4, 0xfc, 0x2a, 0x12, 0xef, 0x68,
0x7b, 0xd2, 0x14, 0x68, 0xf1, 0x62, 0xdd, 0xeb]);
function processMessage(event)
{
// No keys added yet.
verifyKeyStatuses(mediaKeySession.keyStatuses, { expected: [], unexpected: [key1, key2] });
// Add key1 to the session.
firstEvent = true;
var jwkSet = stringToUint8Array(createJWKSet(createJWK(key1, rawKey1)));
mediaKeySession.update(jwkSet).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function processKeyStatusesChange(event)
{
if (firstEvent) {
// Verify that the session only contains key1.
dumpKeyStatuses(mediaKeySession.keyStatuses);
verifyKeyStatuses(mediaKeySession.keyStatuses, { expected: [key1], unexpected: [key2] });
// Now add key2 to the session.
firstEvent = false;
var jwkSet = stringToUint8Array(createJWKSet(createJWK(key2, rawKey2)));
mediaKeySession.update(jwkSet).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
} else {
// Verify that the session now contains key1 and key2.
dumpKeyStatuses(mediaKeySession.keyStatuses);
verifyKeyStatuses(mediaKeySession.keyStatuses, { expected: [key1, key2] });
test.done();
}
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
// There should be no keys defined yet.
assert_equals(mediaKeySession.keyStatuses.size, 0);
waitForEventAndRunStep('message', mediaKeySession, processMessage, test);
waitForEventAndRunStep('keystatuseschange', mediaKeySession, processKeyStatusesChange, test);
return mediaKeySession.generateRequest(initDataType, initData);
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Verify MediaKeySession.keyStatuses with multiple updates.');
</script>
</body>
</html>

View file

@ -0,0 +1,124 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Verify MediaKeySession.keyStatuses</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
var mediaKeySession;
var initDataType;
var initData;
// Even though key ids are uint8, using printable values so that
// they can be verified easily.
var key1String = '123';
var key2String = '4567890';
var key1 = stringToUint8Array(key1String);
var key2 = stringToUint8Array(key2String);
var rawKey1 = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
var rawKey2 = new Uint8Array([0x3c, 0xae, 0xe4, 0xfc, 0x2a, 0x12, 0xef, 0x68,
0x7b, 0xd2, 0x14, 0x68, 0xf1, 0x62, 0xdd, 0xeb]);
function processMessage(event)
{
// No keys added yet.
assert_equals(mediaKeySession.keyStatuses.size, 0);
waitForEventAndRunStep('keystatuseschange', mediaKeySession, processKeyStatusesChange, test);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(key1, rawKey1), createJWK(key2, rawKey2)));
mediaKeySession.update(jwkSet).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function processKeyStatusesChange(event)
{
// Two keys added, so both should show up in |keyStatuses|.
assert_equals(mediaKeySession.keyStatuses.size, 2);
dumpKeyStatuses(mediaKeySession.keyStatuses);
// Check |keyStatuses| for 2 entries.
var result = [];
for (var entry of mediaKeySession.keyStatuses) {
result.push({ key: arrayBufferAsString(entry[0]), value: entry[1] });
}
assert_object_equals(result,
[{ key: key1String, value: 'usable'}, { key: key2String, value: 'usable'}],
'keyStatuses fails');
// |keyStatuses| must contain both keys.
result = [];
for (var key of mediaKeySession.keyStatuses.keys()) {
result.push(arrayBufferAsString(key));
}
assert_array_equals(result,
[key1String, key2String],
'keyStatuses.keys() fails');
// Both values in |mediaKeySession| should be 'usable'.
result = [];
for (var value of mediaKeySession.keyStatuses.values()) {
result.push(value);
}
assert_array_equals(result,
['usable', 'usable'],
'keyStatuses.values() fails');
// Check |keyStatuses.entries()|.
result = [];
for (var entry of mediaKeySession.keyStatuses.entries()) {
result.push({ key: arrayBufferAsString(entry[0]), value: entry[1] });
}
assert_object_equals(result,
[{ key: key1String, value: 'usable'}, { key: key2String, value: 'usable'}],
'keyStatuses.entries() fails');
// forEach() should return both entries.
result = [];
mediaKeySession.keyStatuses.forEach(function(value, key, map) {
result.push({ key: arrayBufferAsString(key), value: value });
});
assert_object_equals(result,
[{ key: key1String, value: 'usable'}, { key: key2String, value: 'usable'}],
'keyStatuses.forEach() fails');
assert_true(mediaKeySession.keyStatuses.has(key1));
assert_true(mediaKeySession.keyStatuses.has(key2));
assert_false(mediaKeySession.keyStatuses.has(stringToUint8Array('123456')));
assert_equals(mediaKeySession.keyStatuses.get(key1), 'usable');
assert_equals(mediaKeySession.keyStatuses.get(key2), 'usable');
assert_equals(mediaKeySession.keyStatuses.get(stringToUint8Array('123456')), undefined);
test.done();
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
// There should be no keys defined yet.
assert_equals(mediaKeySession.keyStatuses.size, 0);
waitForEventAndRunStep('message', mediaKeySession, processMessage, test);
return mediaKeySession.generateRequest(initDataType, initData);
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Verify MediaKeySession.keyStatuses.');
</script>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test MediaKeys lifetime when adding a session</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// MediaKeySessions remain as long as:
// JavaScript has a reference to it
// OR (MediaKeys is around
// AND the session has not received a close() event)
// In the tests below, we do not close any session nor keep a
// Javascript reference to any session, so MediaKeySessions remain
// as long as the associated MediaKeys object is around.
// For this test, create a MediaKeySession and verify lifetime.
async_test(function(test)
{
var initDataType;
var initData;
var mediaKeys;
var startingActiveDOMObjectCount = window.internals.activeDOMObjectCount(document);
function numActiveDOMObjectsCreated()
{
return window.internals.activeDOMObjectCount(document) - startingActiveDOMObjectCount;
}
// Create a MediaKeys object with a session.
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
assert_equals(access.keySystem, 'org.w3.clearkey');
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
// Verify MediaKeys is an ActiveDOMObject.
// In non-Oilpan, numActiveDOMObjectsCreate() == 1.
// In Oilpan, numActiveDOMObjectsCreate() <= 4.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 1 MediaKeySystemAccessInitializer (navigator.requestMediaKeySystemAccess() use above),
// 1 MediaKeySystemAccessInitializer (isInitDataSupported() (via getSupportedInitDataType())))
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 4, 'MediaKeys.create()');
var mediaKeySession = mediaKeys.createSession();
return mediaKeySession.generateRequest(initDataType, initData);
}).then(function() {
// Should be 1 MediaKeys + 1 MediaKeySession.
// In non-Oilpan, numActiveDOMObjectsCreate() == 2.
// In Oilpan, numActiveDOMObjectsCreate() <= 6.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 2 MediaKeySystemAccessInitializer,
// 1 ContentDecryptionModuleResultPromise and
// 1 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 6, 'MediaKeys.createSession()');
// Run gc(), should not affect MediaKeys object nor the
// session since we still have a reference to it.
// When enabling oilpan GC, the in-active
// ScriptPromiseResolvers will be destroyed.
return createGCPromise();
}).then(function(result) {
assert_equals(typeof mediaKeys.createSession, 'function');
// MediaKeys + MediaKeySessions should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 3, 'After gc()');
// Drop reference to the MediaKeys object and run gc()
// again. Object should be collected this time. Since
// MediaKeySessions remain alive as long as MediaKeys is
// around, it is possible that gc() checks the
// MediaKeySession object first, and doesn't collect it
// since MediaKeys hasn't been collected yet. Thus run gc()
// twice, to ensure that the unreferenced MediaKeySession
// object get collected.
mediaKeys = null;
return createGCPromise();
}).then(function(result) {
return createGCPromise();
}).then(function(result) {
// No MediaKeySessions should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 0, 1, 'After final gc()');
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'MediaKeys lifetime with session');
</script>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test MediaKeys lifetime</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
// Create a MediaKeys object and free immediately.
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
// Do nothing with the created object
}).then(function(result) {
// No way to verify that MediaKeys object is actually
// collected, but make sure it doesn't crash.
return createGCPromise();
}).then(function(result) {
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Creating and destroying MediaKeys does not crash');
async_test(function(test)
{
var mediaKeys;
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
return createGCPromise();
}).then(function(result) {
// Check that the object still exists.
assert_equals(typeof mediaKeys.createSession, 'function');
mediaKeys = null;
// Now that the reference is dropped, it should be
// collected. No way to verify that it is actually
// collected, but make sure it doesn't crash.
return createGCPromise();
}).then(function(result) {
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'MediaKeys is not collected as long as we have a reference');
</script>
</body>
</html>

View file

@ -0,0 +1,157 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test MediaKeySession lifetime without release()</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// Since MediaKeySession (and MediaKeys) are ActiveDOMObjects,
// we can determine when they are garbage collected.
// MediaKeySessions remain as long as:
// JavaScript has a reference to it
// OR (MediaKeys is around
// AND the session has not received a close() event)
async_test(function(test)
{
var mediaKeys;
var mediaKeySession1;
var mediaKeySession2;
var mediaKeySession3;
var initDataType;
var initData;
var startingActiveDOMObjectCount = window.internals.activeDOMObjectCount(document);
function numActiveDOMObjectsCreated()
{
return window.internals.activeDOMObjectCount(document) - startingActiveDOMObjectCount;
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
assert_equals(access.keySystem, 'org.w3.clearkey');
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
assert_equals(typeof mediaKeys.createSession, 'function');
// Verify MediaKeys is an ActiveDOMObject.
// In non-Oilpan, numActiveDOMObjectsCreate() == 1.
// In Oilpan, numActiveDOMObjectsCreate() <= 4.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 1 MediaKeySystemAccessInitializer (navigator.requestMediaKeySystemAccess() use above),
// 1 MediaKeySystemAccessInitializer (isInitDataSupported() (via getSupportedInitDataType())))
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 4, 'MediaKeys.create()');
// Create 3 sessions.
mediaKeySession1 = mediaKeys.createSession();
return mediaKeySession1.generateRequest(initDataType, initData);
}).then(function() {
assert_true(mediaKeySession1.sessionId && mediaKeySession1.sessionId.length > 0);
// Should be 1 MediaKeys + 1 MediaKeySession.
// In non-Oilpan, numActiveDOMObjectsCreate() == 2.
// In Oilpan, numActiveDOMObjectsCreate() <= 6.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 2 MediaKeySystemAccessInitializer,
// 1 ContentDecryptionModuleResultPromise and
// 1 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 6, 'MediaKeys.createSession(1)');
mediaKeySession2 = mediaKeys.createSession();
return mediaKeySession2.generateRequest(initDataType, initData);
}).then(function() {
assert_true(mediaKeySession2.sessionId && mediaKeySession2.sessionId.length > 0);
// Should be 1 MediaKeys + 2 MediaKeySessions.
// In non-Oilpan, numActiveDOMObjectsCreate() == 3.
// In Oilpan, numActiveDOMObjectsCreate() <= 8.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 2 MediaKeySystemAccessInitializers,
// 2 ContentDecryptionModuleResultPromise and
// 2 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 3, 8, 'mediaKeys.createSession(2)');
mediaKeySession3 = mediaKeys.createSession();
return mediaKeySession3.generateRequest(initDataType, initData);
}).then(function() {
assert_true(mediaKeySession3.sessionId && mediaKeySession3.sessionId.length > 0);
// Should be 1 MediaKeys + 3 MediaKeySessions.
// In non-Oilpan, numActiveDOMObjectsCreate() == 4.
// In Oilpan, numActiveDOMObjectsCreate() <= 10.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 2 MediaKeySystemAccessInitializers,
// 3 ContentDecryptionModuleResultPromise and
// 3 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 4, 10, 'mediaKeys.createSession(3)');
// Run gc(). All sessions should remain as we have a
// reference to each one. However, running gc()
// asynchronously should free up the last PromiseResolver.
return createGCPromise();
}).then(function(result) {
// Only MediaKeys + 3 MediaKeySessions should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 4, 5, 'After gc()');
// Now drop references to 2 of the sessions. Even though we
// don't have a reference, MediaKeys is still around (and
// the sessions aren't closed), so the objects won't be
// collected.
mediaKeySession1 = null;
mediaKeySession2 = null;
return createGCPromise();
}).then(function(result) {
return createGCPromise();
}).then(function(result) {
// MediaKeys + 3 MediaKeySessions should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 4, 5, 'After second gc()');
// Now drop the reference to MediaKeys. It and the 2
// unreferenced sessions should be collected. Since
// MediaKeySessions remain alive as long as MediaKeys is
// around, it is possible that gc() checks one or both
// MediaKeySession objects first, and doesn't collect them
// since MediaKeys hasn't been collected yet. Thus run gc()
// twice, to ensure that the unreferenced MediaKeySession
// objects get collected.
mediaKeys = null;
return createGCPromise();
}).then(function(result) {
return createGCPromise();
}).then(function(result) {
// Only 1 MediaKeySessions should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 2, 'After mediaKeys = null');
// Drop the reference to the last session. It should get
// collected now since MediaKeys is gone.
mediaKeySession3 = null;
return createGCPromise();
}).then(function(result) {
// No MediaKeySessions should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 0, 1, 'After final gc()');
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'MediaKeySession lifetime without release()');
</script>
</body>
</html>

View file

@ -0,0 +1,129 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test MediaKeySession lifetime after release() without references</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// Since MediaKeySession (and MediaKeys) are ActiveDOMObjects,
// we can determine when they are garbage collected.
// MediaKeySessions remain as long as:
// JavaScript has a reference to it
// OR (MediaKeys is around
// AND the session has not received a close() event)
async_test(function(test)
{
var initDataType;
var initData;
var startingActiveDOMObjectCount = window.internals.activeDOMObjectCount(document);
function numActiveDOMObjectsCreated()
{
return window.internals.activeDOMObjectCount(document) - startingActiveDOMObjectCount;
}
// Create 2 sessions.
var mediaKeys;
var mediaKeySession1;
var mediaKeySession2;
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
// Verify MediaKeys is an ActiveDOMObject.
// In non-Oilpan, numActiveDOMObjectsCreate() == 1.
// In Oilpan, numActiveDOMObjectsCreate() <= 4.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 1 MediaKeySystemAccessInitializer (navigator.requestMediaKeySystemAccess() use above),
// 1 MediaKeySystemAccessInitializer (isInitDataSupported() (via getSupportedInitDataType())))
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 4, 'MediaKeys.create()');
mediaKeySession1 = mediaKeys.createSession();
return mediaKeySession1.generateRequest(initDataType, initData);
}).then(function() {
assert_true(mediaKeySession1.sessionId && mediaKeySession1.sessionId.length > 0);
// Should be 1 MediaKeys + 1 MediaKeySession.
// In non-Oilpan, numActiveDOMObjectsCreate() == 2.
// In Oilpan, numActiveDOMObjectsCreate() <= 6.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 2 MediaKeySystemAccessInitializer,
// 1 ContentDecryptionModuleResultPromise and
// 1 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 6, 'MediaKeys.createSession(1)');
mediaKeySession2 = mediaKeys.createSession();
return mediaKeySession2.generateRequest(initDataType, initData);
}).then(function() {
assert_true(mediaKeySession2.sessionId && mediaKeySession2.sessionId.length > 0);
// Should be 1 MediaKeys + 2 MediaKeySessions.
// In non-Oilpan, numActiveDOMObjectsCreate() == 3.
// In Oilpan, numActiveDOMObjectsCreate() <= 8.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 2 MediaKeySystemAccessInitializers,
// 2 ContentDecryptionModuleResultPromise and
// 2 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 3, 8, 'mediaKeys.createSession(2)');
}).then(function(result) {
// Run gc(). All sessions should remain as we have a
// reference to each one.
return createGCPromise();
}).then(function(result) {
// Should be just 1 MediaKeys + 2 MediaKeySessions.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 3, 4, 'After gc()');
// Close the sessions. Once the close() event is received,
// they should get garbage collected as there are no JS
// references to them.
var promise = mediaKeySession1.close();
mediaKeySession1 = null;
return promise;
}).then(function(result) {
// Give time so that the close event can be processed by
// MediaKeySession.
return delayToAllowEventProcessingPromise();
}).then(function(result) {
return createGCPromise();
}).then(function(result) {
// Only MediaKeys + mediaKeySession2 should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 3, 'mediaKeySession1 not collected');
var promise = mediaKeySession2.close();
mediaKeySession2 = null;
return promise;
}).then(function(result) {
// Provide time for the mediaKeySession2 close event to be
// handled.
return delayToAllowEventProcessingPromise();
}).then(function(result) {
return createGCPromise();
}).then(function(result) {
// Only MediaKeys should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 2, 'mediaKeySession2 not collected');
assert_not_equals(mediaKeys, null);
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'MediaKeySession lifetime after release() without references');
</script>
</body>
</html>

View file

@ -0,0 +1,117 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>MediaKeySession lifetime after release()</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// Since MediaKeySession (and MediaKeys) are ActiveDOMObjects,
// we can determine when they are garbage collected.
// MediaKeySessions remain as long as:
// JavaScript has a reference to it
// OR (MediaKeys is around
// AND the session has not received a close() event)
async_test(function(test)
{
var mediaKeys;
var mediaKeySession1;
var mediaKeySession2;
var initDataType;
var initData;
var startingActiveDOMObjectCount = window.internals.activeDOMObjectCount(document);
function numActiveDOMObjectsCreated()
{
return window.internals.activeDOMObjectCount(document) - startingActiveDOMObjectCount;
}
// Create 2 sessions.
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
// Verify MediaKeys is an ActiveDOMObject.
// In non-Oilpan, numActiveDOMObjectsCreate() == 1.
// In Oilpan, numActiveDOMObjectsCreate() <= 4.
// (1 MediaKeys,
// 1 MediaKeysInitializer and
// 1 MediaKeySystemAccessInitializer (navigator.requestMediaKeySystemAccess() use above),
// 1 MediaKeySystemAccessInitializer (isInitDataSupported() (via getSupportedInitDataType())))
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 4, 'MediaKeys.create()');
mediaKeySession1 = mediaKeys.createSession();
return mediaKeySession1.generateRequest(initDataType, initData);
}).then(function() {
// Should be 1 MediaKeys + 1 MediaKeySession.
// In non-Oilpan, numActiveDOMObjectsCreate() == 2.
// In Oilpan, numActiveDOMObjectsCreate() <= 6.
// (1 MediaKeys,
// 1 MediaKeysInitializer,
// 2 MediaKeySystemAccessInitializers,
// 1 ContentDecryptionModuleResultPromise and
// 1 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 6, 'MediaKeys.createSession(1)');
mediaKeySession2 = mediaKeys.createSession();
return mediaKeySession2.generateRequest(initDataType, initData);
}).then(function() {
// Should be 1 MediaKeys + 2 MediaKeySessions.
// In non-Oilpan, numActiveDOMObjectsCreate() == 3.
// In Oilpan, numActiveDOMObjectsCreate() <= 8.
// (1 MediaKeys,
// 1 MediaKeysInitializer,
// 2 MediaKeySystemAccessInitializers,
// 2 ContentDecryptionModuleResultPromise and
// 2 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 3, 8, 'mediaKeys.createSession(2)');
// Close the sessions. Once completed, only the JS
// reference to them keeps them around.
return mediaKeySession1.close();
}).then(function(result) {
return mediaKeySession2.close();
}).then(function(result) {
// Since both sessions have been closed, dropping the
// reference to them from JS will result in the session
// being garbage-collected.
// Should be 1 MediaKeys + 2 MediaKeySessions.
// In non-Oilpan, numActiveDOMObjectsCreate() == 3.
// In Oilpan, numActiveDOMObjectsCreate() <= 10.
// (1 MediaKeys,
// 1 MediaKeysInitializer,
// 2 MediaKeySystemAccessInitializers,
// 4 ContentDecryptionModuleResultPromise and
// 2 MediaKeySession).
assert_between_inclusive(numActiveDOMObjectsCreated(), 3, 10, 'after close');
mediaKeySession1 = null;
return createGCPromise();
}).then(function() {
// Only MediaKeys + mediaKeySession2 should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 3, 'mediaKeySession1 not collected');
mediaKeySession2 = null;
return createGCPromise();
}).then(function() {
// Only MediaKeys should remain.
// In non-Oilpan, there is also something from createGCPromise().
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 2, 'mediaKeySession2 not collected');
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'MediaKeySession lifetime after release()');
</script>
</body>
</html>

View file

@ -0,0 +1,130 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test multiple MediaKeys lifetimes</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// For this test, create several MediaKeys and verify lifetime.
async_test(function(test)
{
var mediaKeys;
var startingActiveDOMObjectCount = window.internals.activeDOMObjectCount(document);
function numActiveDOMObjectsCreated()
{
return window.internals.activeDOMObjectCount(document) - startingActiveDOMObjectCount;
}
// Create a MediaKeys object. Returns a promise that resolves
// with the new MediaKeys object.
function createMediaKeys()
{
return getSupportedInitDataType().then(function(type) {
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
return mediaKeys;
});
}
// Create a few MediaKeys objects. Only keep a reference to the
// last one created.
createMediaKeys().then(function(result) {
// Should be 1 MediaKeys.
// In non-Oilpan, numActiveDOMObjectsCreate() == 1.
// In Oilpan, numActiveDOMObjectsCreated() <= 4.
// (1 MediaKeysInitializer,
// 1 MediaKeySystemAccessInitializer (navigator.requestMediaKeySystemAccess() use above),
// 1 MediaKeySystemAccessInitializer (isInitDataSupported() (via getSupportedInitDataType())) and
// 1 ContentDecryptionModuleResultPromise).
assert_between_inclusive(numActiveDOMObjectsCreated(), 1, 4);
return createMediaKeys();
}).then(function(result) {
// Should be 2 MediaKeys.
// In non-Oilpan, numActiveDOMObjectsCreate() == 2.
// In Oilpan, numActiveDOMObjectsCreate() <= 8.
// (2 MediaKeysInitializer,
// 4 MediaKeySystemAccessInitializer and
// 2 ContentDecryptionModuleResultPromise).
assert_between_inclusive(numActiveDOMObjectsCreated(), 2, 8);
return createMediaKeys();
}).then(function(result) {
// Should be 3 MediaKeys.
// In non-Oilpan, numActiveDOMObjectsCreate() == 3.
// In Oilpan, numActiveDOMObjectsCreate() <= 12.
// (3 MediaKeysInitializer,
// 6 MediaKeySystemAccessInitializer and
// 3 ContentDecryptionModuleResultPromise).
assert_between_inclusive(numActiveDOMObjectsCreated(), 3, 12);
return createMediaKeys();
}).then(function(result) {
// Should be 4 MediaKeys.
// In non-Oilpan, numActiveDOMObjectsCreate() == 4.
// In Oilpan, numActiveDOMObjectsCreate() <= 16.
// (4 MediaKeysInitializer,
// 8 MediaKeySystemAccessInitializer and
// 4 ContentDecryptionModuleResultPromise).
assert_between_inclusive(numActiveDOMObjectsCreated(), 4, 16);
return createMediaKeys();
}).then(function(result) {
// Should be 5 MediaKeys.
// In non-Oilpan, numActiveDOMObjectsCreate() == 5.
// In Oilpan, numActiveDOMObjectsCreate() <= 20.
// (5 MediaKeysInitializer,
// 10 MediaKeySystemAccessInitializer and
// 5 ContentDecryptionModuleResultPromise).
assert_between_inclusive(numActiveDOMObjectsCreated(), 5, 20);
// |mediaKeys| refers to the most recently created MediaKeys
// object.
mediaKeys = result;
// In order for the MediaKey objects to be garbage
// collected, it needs time to process any pending events.
return delayToAllowEventProcessingPromise();
}).then(function(result) {
// In non-Oilpan, numActiveDOMObjectsCreated() == 5
// (5 MediaKeySession objects).
// In Oilpan, numActiveDOMObjectsCreated() <= 23
// (5 MediaKeysInitializer,
// 12 MediaKeySystemAccessInitializer,
// 5 ContentDecryptionModuleResultPromise and
// 1 DOMTimer (in delayToAllowEventProcessingPromise))
assert_between_inclusive(numActiveDOMObjectsCreated(), 5, 23);
// As we only have a reference (|mediaKeys|) to the last
// created MediaKeys object, the other 4 MediaKeys objects
// are available to be garbage collected.
return createGCPromise();
}).then(function(result) {
// Should be 1 MediaKeys and DOMTimer.
assert_less_than_equal(numActiveDOMObjectsCreated(), 2);
assert_equals(typeof mediaKeys.createSession, 'function');
// Release the last MediaKeys object created.
mediaKeys = null;
// Run gc() again to reclaim the remaining MediaKeys object.
return createGCPromise();
}).then(function(result) {
// Should be just a DOMTimer.
assert_less_than_equal(numActiveDOMObjectsCreated(), 1);
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Multiple MediaKeys lifetime');
</script>
</body>
</html>

View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Reloading during encrypted media playback</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var video = document.getElementById('testVideo');
var mediaKeySession = null;
var hasSessionUpdateSucceeded = false;
var encryptedEventCount = 0;
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
function onEncrypted(event)
{
assert_equals(event.target, video);
assert_true(event instanceof window.MediaEncryptedEvent);
assert_equals(event.type, 'encrypted');
// The same decryption key is used by both the audio and
// the video streams so only create a session once. To
// avoid issues when comparing the expected.txt file
// (which logs the events in the order they occur), create
// the session on the second event. This also ensures we
// see both events.
if (++encryptedEventCount != 2)
return;
mediaKeySession = video.mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, onMessage, test);
mediaKeySession.generateRequest(event.initDataType, event.initData).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onMessage(event)
{
assert_true(event instanceof window.MediaKeyMessageEvent);
assert_equals(event.target, mediaKeySession);
assert_equals(event.type, 'message');
assert_equals(event.messageType, 'license-request');
var keyId = extractSingleKeyIdFromMessage(event.message);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
mediaKeySession.update(jwkSet).then(function(result) {
hasSessionUpdateSucceeded = true;
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onPlaying(event)
{
// Not using waitForEventAndRunStep() to avoid too many
// EVENT(onTimeUpdate) logs.
video.addEventListener('timeupdate', onTimeUpdate, true);
}
function onTimeUpdate(event)
{
if (event.target.currentTime < 0.2 || !hasSessionUpdateSucceeded)
return;
// Reload the page to catch any possible teardown issues.
if (location.hash == '#x') {
test.done();
return;
}
location.hash += 'x';
location.reload();
}
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
waitForEventAndRunStep('playing', video, onPlaying, test);
video.src = '../content/test-encrypted.webm';
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
video.play();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Reloading during encrypted media playback.');
</script>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test MediaKeySession not callable immediately after CreateSession().</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// After creation, the MediaKeySession object is not
// callable, and we should get a InvalidStateError.
promise_test(function()
{
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var mediaKeySession = mediaKeys.createSession();
var arbitraryResponse = new Uint8Array([0x00, 0x11]);
return mediaKeySession.update(arbitraryResponse).then(function(result) {
assert_unreached('update() succeeded unexpectedly.');
}).catch(function(error) {
assert_equals(error.name, 'InvalidStateError');
});
});
}, 'Update() immediately after CreateSession().');
promise_test(function()
{
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var mediaKeySession = mediaKeys.createSession();
return mediaKeySession.close().then(function(result) {
assert_unreached('close() succeeded unexpectedly.');
}).catch(function(error) {
assert_equals(error.name, 'InvalidStateError');
});
});
}, 'Close() immediately after CreateSession().');
promise_test(function()
{
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
var mediaKeySession = mediaKeys.createSession();
return mediaKeySession.remove().then(function(result) {
assert_unreached('remove() succeeded unexpectedly.');
}).catch(function(error) {
assert_equals(error.name, 'InvalidStateError');
});
});
}, 'Remove() immediately after CreateSession().');
</script>
</body>
</html>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>onencrypted</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo" controls></video>
<div id="log"></div>
<script>
var expectedInitData = stringToUint8Array('0123456789012345');
// Will get 2 identical events, one for audio, one for video.
var expectedEvents = 2;
async_test(function(test)
{
var video = document.getElementById('testVideo');
var onEncrypted = function(event)
{
assert_equals(event.target, video);
assert_true(event instanceof window.MediaEncryptedEvent);
assert_equals(event.type, 'encrypted');
assert_equals(event.initDataType, 'webm');
assert_array_equals(new Uint8Array(event.initData), expectedInitData);
if (--expectedEvents == 0)
test.done();
};
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
video.src = '../content/test-encrypted.webm';
}, 'encrypted fired on encrypted media file.');
</script>
</body>
</html>

View file

@ -0,0 +1,121 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Multiple playbacks alternating between encrypted and clear sources.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var video = document.getElementById('testVideo');
var isUpdatePromiseResolved = false;
var encryptedEventCount = 0;
var playbackCount = 0;
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
function onEncrypted(event)
{
assert_equals(event.target, video);
assert_true(event instanceof window.MediaEncryptedEvent);
assert_equals(event.type, 'encrypted');
// The same decryption key is used by both the audio and
// the video streams so only create a session once. To
// avoid issues when comparing the expected.txt file
// (which logs the events in the order they occur), create
// the session on the second event. This also ensures we
// see both events.
if (++encryptedEventCount != 2)
return;
assert_false(video.mediaKeys === null, "video.mediaKeys is null.");
var mediaKeySession = video.mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, onMessage, test);
mediaKeySession.generateRequest(event.initDataType, event.initData).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onMessage(event)
{
assert_true(event instanceof window.MediaKeyMessageEvent);
assert_equals(event.type, 'message');
assert_equals(event.messageType, 'license-request');
var keyId = extractSingleKeyIdFromMessage(event.message);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
event.target.update(jwkSet).then(function(result) {
isUpdatePromiseResolved = true;
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onPlaying(event)
{
// Not using waitForEventAndRunStep() to avoid too many
// EVENT(onTimeUpdate) logs.
video.addEventListener('timeupdate', onTimeUpdate, true);
}
function onTimeUpdate(event)
{
if (event.target.currentTime < 0.2 || !isUpdatePromiseResolved)
return;
video.removeEventListener('timeupdate', onTimeUpdate, true);
if (playbackCount > 2) {
test.done();
return;
}
playbackCount++;
resetSrc().then(function(){
startPlayback();
});
}
function resetSrc() {
encryptedEventCount = 0;
video.removeAttribute('src');
video.load();
return video.setMediaKeys(null);
}
function startPlayback() {
if (playbackCount % 2) {
video.src = '../content/test-vp8-vorbis-webvtt.webm';
video.play();
return;
}
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
video.src = '../content/test-encrypted.webm';
assert_false(video.mediaKeys === null, "video.mediaKeys is null.");
video.play();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
waitForEventAndRunStep('playing', video, onPlaying, test);
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
startPlayback();
}, 'Multiple playbacks alternating between encrypted and clear sources.');
</script>
</body>
</html>

View file

@ -0,0 +1,139 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Clear Key Playback with Multiple Sessions</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var video = document.getElementById('testVideo');
var audioMediaKeySession = null;
var videoMediaKeySession = null;
var audioInitDataType = null;
var videoInitDataType = null;
var audioInitData = null;
var videoInitData = null;
var audioKeyProvided = false;
var videoKeyProvided = false;
// The 2 streams use different key ids and different keys.
var audioKeyId = '1234567890123456';
var audioKey = new Uint8Array([0x30, 0x30, 0x62, 0xF1, 0x68, 0x14, 0xD2, 0x7B,
0x68, 0xEF, 0x12, 0x2A, 0xFC, 0xE4, 0xAE, 0x0A]);
var videoKeyId = '0123456789012345';
var videoKey = new Uint8Array([0x7A, 0x7A, 0x62, 0xF1, 0x68, 0x14, 0xD2, 0x7B,
0x68, 0xEF, 0x12, 0x2A, 0xFC, 0xE4, 0xAE, 0x0A]);
function onEncrypted(event)
{
var keyId = String.fromCharCode.apply(null, new Uint8Array(event.initData));
// To avoid issues when comparing the expected.txt file
// (which logs the events in the order they occur), save
// the initData and make the calls to generateRequest()
// only after both "onencrypted" events are received.
// This prevents a "message" event from occurring before
// both "onencrypted" events are received.
var mediaKeySession = video.mediaKeys.createSession();
if (keyId == videoKeyId) {
assert_equals(videoMediaKeySession, null);
videoMediaKeySession = mediaKeySession;
videoInitDataType = event.initDataType;
videoInitData = event.initData;
// Return if audio "onencrypted" event not yet received.
if (audioMediaKeySession == null)
return;
} else {
assert_equals(keyId, audioKeyId);
assert_equals(audioMediaKeySession, null);
audioMediaKeySession = mediaKeySession;
audioInitDataType = event.initDataType;
audioInitData = event.initData;
// Return if video "onencrypted" event not yet received.
if (videoMediaKeySession == null)
return;
}
// Both sessions have been created.
assert_not_equals(videoMediaKeySession, null);
assert_not_equals(audioMediaKeySession, null);
var promises = [];
waitForEventAndRunStep('message', videoMediaKeySession, onMessage, test);
promises.push(videoMediaKeySession.generateRequest(videoInitDataType, videoInitData));
waitForEventAndRunStep('message', audioMediaKeySession, onMessage, test);
promises.push(audioMediaKeySession.generateRequest(audioInitDataType, audioInitData));
Promise.all(promises).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onMessage(event)
{
assert_equals(event.messageType, 'license-request');
var keyId = extractSingleKeyIdFromMessage(event.message);
if (event.target == videoMediaKeySession) {
assert_equals(String.fromCharCode.apply(null, keyId), videoKeyId);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, videoKey)));
videoMediaKeySession.update(jwkSet).then(function(result) {
videoKeyProvided = true;
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
} else {
assert_equals(event.target, audioMediaKeySession);
assert_equals(String.fromCharCode.apply(null, keyId), audioKeyId);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, audioKey)));
audioMediaKeySession.update(jwkSet).then(function(result) {
audioKeyProvided = true;
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
}
function onPlaying(event)
{
// Video should not start playing until both keys have been
// provided.
assert_true(videoKeyProvided);
assert_true(audioKeyProvided);
// Not using waitForEventAndRunStep() to avoid too many
// EVENT(onTimeUpdate) logs.
video.addEventListener('timeupdate', onTimeUpdate, true);
}
function onTimeUpdate(event)
{
if (event.target.currentTime < 0.2)
return;
test.done();
}
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
waitForEventAndRunStep('playing', video, onPlaying, test);
video.src = '../content/test-encrypted-different-av-keys.webm';
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
video.play();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Playback using Clear Key key system with multiple sessions.');
</script>
</body>
</html>

View file

@ -0,0 +1,92 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Clear Key Playback</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var video = document.getElementById('testVideo');
var isUpdatePromiseResolved = false;
var encryptedEventCount = 0;
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
function onEncrypted(event)
{
assert_equals(event.target, video);
assert_true(event instanceof window.MediaEncryptedEvent);
assert_equals(event.type, 'encrypted');
// The same decryption key is used by both the audio and
// the video streams so only create a session once. To
// avoid issues when comparing the expected.txt file
// (which logs the events in the order they occur), create
// the session on the second event. This also ensures we
// see both events.
if (++encryptedEventCount != 2)
return;
var mediaKeySession = video.mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, onMessage, test);
mediaKeySession.generateRequest(event.initDataType, event.initData).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onMessage(event)
{
assert_true(event instanceof window.MediaKeyMessageEvent);
assert_equals(event.type, 'message');
assert_equals(event.messageType, 'license-request');
var keyId = extractSingleKeyIdFromMessage(event.message);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
event.target.update(jwkSet).then(function(result) {
isUpdatePromiseResolved = true;
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onPlaying(event)
{
// Not using waitForEventAndRunStep() to avoid too many
// EVENT(onTimeUpdate) logs.
video.addEventListener('timeupdate', onTimeUpdate, true);
}
function onTimeUpdate(event)
{
if (event.target.currentTime < 0.2 || !isUpdatePromiseResolved)
return;
test.done();
}
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
waitForEventAndRunStep('playing', video, onPlaying, test);
video.src = '../content/test-encrypted.webm';
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
video.play();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Playback using Clear Key key system, calling setMediaKeys() after setting src attribute.');
</script>
</body>
</html>

View file

@ -0,0 +1,92 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Clear Key Playback</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var video = document.getElementById('testVideo');
var isUpdatePromiseResolved = false;
var encryptedEventCount = 0;
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
function onEncrypted(event)
{
assert_equals(event.target, video);
assert_true(event instanceof window.MediaEncryptedEvent);
assert_equals(event.type, 'encrypted');
// The same decryption key is used by both the audio and
// the video streams so only create a session once. To
// avoid issues when comparing the expected.txt file
// (which logs the events in the order they occur), create
// the session on the second event. This also ensures we
// see both events.
if (++encryptedEventCount != 2)
return;
var mediaKeySession = video.mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, onMessage, test);
mediaKeySession.generateRequest(event.initDataType, event.initData).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onMessage(event)
{
assert_true(event instanceof window.MediaKeyMessageEvent);
assert_equals(event.type, 'message');
assert_equals(event.messageType, 'license-request');
var keyId = extractSingleKeyIdFromMessage(event.message);
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
event.target.update(jwkSet).then(function(result) {
isUpdatePromiseResolved = true;
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}
function onPlaying(event)
{
// Not using waitForEventAndRunStep() to avoid too many
// EVENT(onTimeUpdate) logs.
video.addEventListener('timeupdate', onTimeUpdate, true);
}
function onTimeUpdate(event)
{
if (event.target.currentTime < 0.2 || !isUpdatePromiseResolved)
return;
test.done();
}
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
waitForEventAndRunStep('playing', video, onPlaying, test);
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
video.src = '../content/test-encrypted.webm';
video.play();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Playback using Clear Key key system, calling setMediaKeys() before setting src attribute.');
</script>
</body>
</html>

View file

@ -0,0 +1,86 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Clear Key Play Two Videos At Same Time</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<video id="secondVideo"></video>
<div id="log"></div>
<script>
// As this code doesn't wait for the 'message' event to simplify
// the code, specify the key ID and key used by the encrypted
// content.
var keyId = stringToUint8Array('0123456789012345');
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
promise_test(function(test)
{
var promises = [
play_video_as_promise(document.getElementById('testVideo'), '../content/test-encrypted.webm'),
play_video_as_promise(document.getElementById('secondVideo'), '../content/test-encrypted.webm')
];
return Promise.all(promises);
}, 'Play two videos at the same time.');
function play_video_as_promise(video, content)
{
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
video.src = content;
video.play();
return wait_for_encrypted_message(video);
}).then(function(result) {
return wait_for_timeupdate_message(video);
});
};
function wait_for_encrypted_message(video)
{
var encryptedEventCount = 0;
return new Promise(function(resolve) {
video.addEventListener('encrypted', function listener(e) {
// The same decryption key is used by both the audio
// and the video streams so only create a session once.
// Create the session on the second event. This also
// ensures we see both events.
if (++encryptedEventCount != 2)
return;
video.removeEventListener('encrypted', listener);
var mediaKeySession = video.mediaKeys.createSession();
mediaKeySession.generateRequest(e.initDataType, e.initData).then(function(result) {
// Don't bother waiting for the 'message' event.
// Just call update() since we know the keyId
// needed.
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
return mediaKeySession.update(jwkSet);
}).then(function(result) {
resolve(result);
});
});
});
};
function wait_for_timeupdate_message(video)
{
return new Promise(function(resolve) {
video.addEventListener('timeupdate', function listener(e) {
if (e.target.currentTime < 0.2)
return;
video.removeEventListener('timeupdate', listener);
resolve(e);
});
});
};
</script>
</body>
</html>

View file

@ -0,0 +1,319 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test navigator.requestMediaKeySystemAccess()</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
function expect_error(keySystem, configurations, expectedError, testName) {
promise_test(function(test) {
return navigator.requestMediaKeySystemAccess(keySystem, configurations).then(function(a) {
assert_unreached('Unexpected requestMediaKeySystemAccess() success.');
}, function(e) {
assert_equals(e.name, expectedError);
});
}, testName);
}
function assert_subset(actual, expected, path) {
if (typeof expected == 'string') {
assert_equals(actual, expected, path);
} else {
if (expected.hasOwnProperty('length'))
assert_equals(actual.length, expected.length, path + '.length');
for (property in expected)
assert_subset(actual[property], expected[property], path + '.' + property);
}
}
function expect_config(keySystem, configurations, expectedConfiguration, testName) {
promise_test(function(test) {
return navigator.requestMediaKeySystemAccess(keySystem, configurations).then(function(a) {
assert_subset(a.getConfiguration(), expectedConfiguration, 'getConfiguration()');
});
}, testName);
}
// Tests for keySystem.
expect_error('', [{}], 'InvalidAccessError', 'Empty keySystem');
expect_error('com.example.unsupported', [{}], 'NotSupportedError', 'Unsupported keySystem');
expect_error('org.w3.clearkey.', [{}], 'NotSupportedError', 'keySystem ends with "."');
expect_error('org.w3.ClearKey', [{}], 'NotSupportedError', 'Capitalized keySystem');
expect_error('org.w3.clearke\u028F', [{}], 'NotSupportedError', 'Non-ASCII keySystem');
// Parent of Clear Key not supported.
expect_error('org', [{}], 'NotSupportedError', 'Parent of Clear Key (org)');
expect_error('org.', [{}], 'NotSupportedError', 'Parent of Clear Key (org.)');
expect_error('org.w3', [{}], 'NotSupportedError', 'Parent of Clear Key (org.w3)');
expect_error('org.w3.', [{}], 'NotSupportedError', 'Parent of Clear Key (org.w3.)');
// Child of Clear Key not supported.
expect_error('org.w3.clearkey.foo', [{}], 'NotSupportedError', 'Child of Clear Key');
// Prefixed Clear Key not supported.
expect_error('webkit-org.w3.clearkey', [{}], 'NotSupportedError', 'Prefixed Clear Key');
// Incomplete names.
expect_error('org.w3.learkey', [{}], 'NotSupportedError', 'Incomplete name org.w3.learkey');
expect_error('org.w3.clearke', [{}], 'NotSupportedError', 'Incomplete name org.w3.clearke');
expect_error('or.w3.clearkey', [{}], 'NotSupportedError', 'Incomplete name or.w3.clearkey');
// Spaces in key system name not supported.
expect_error(' org.w3.clearkey', [{}], 'NotSupportedError', 'Leading space in key system name');
expect_error('org.w3. clearkey', [{}], 'NotSupportedError', 'Extra space in key system name');
expect_error('org.w3.clearkey ', [{}], 'NotSupportedError', 'Trailing space in key system name');
// Extra dots in key systems names not supported.
expect_error('.org.w3.clearkey', [{}], 'NotSupportedError', 'Leading dot in key systems name');
expect_error('org.w3.clearkey.', [{}], 'NotSupportedError', 'Trailing dot in key systems name');
expect_error('org.w3..clearkey', [{}], 'NotSupportedError', 'Double dot in key systems name');
expect_error('org.w3.clear.key', [{}], 'NotSupportedError', 'Extra dot in key systems name');
// Key system name is case sensitive.
expect_error('org.w3.Clearkey', [{}], 'NotSupportedError', 'Key system name is case sensitive');
expect_error('Org.w3.clearkey', [{}], 'NotSupportedError', 'Key system name is case sensitive');
// Tests for trivial configurations.
expect_error('org.w3.clearkey', [], 'InvalidAccessError', 'Empty supportedConfigurations');
expect_config('org.w3.clearkey', [{}], {}, 'Empty configuration');
// Various combinations of supportedConfigurations.
// TODO(jrummell): Specifying contentType without codecs is
// deprecated, so this test should fail. http://crbug.com/605661.
expect_config('org.w3.clearkey', [{
initDataTypes: ['webm'],
audioCapabilities: [{contentType: 'audio/webm'}],
videoCapabilities: [{contentType: 'video/webm'}],
}], {
initDataTypes: ['webm'],
audioCapabilities: [{contentType: 'audio/webm'}],
videoCapabilities: [{contentType: 'video/webm'}],
}, 'Basic supported configuration');
// TODO(jrummell): Specifying contentType without codecs is
// deprecated, so this test should fail. http://crbug.com/605661.
expect_config('org.w3.clearkey', [{
initDataTypes: ['fakeidt', 'webm'],
audioCapabilities: [{contentType: 'audio/fake'}, {contentType: 'audio/webm'}],
videoCapabilities: [{contentType: 'video/fake'}, {contentType: 'video/webm'}],
}], {
initDataTypes: ['webm'],
audioCapabilities: [{contentType: 'audio/webm'}],
videoCapabilities: [{contentType: 'video/webm'}],
}, 'Partially supported configuration');
expect_config('org.w3.clearkey', [{
audioCapabilities: [{contentType: 'audio/webm; codecs=vorbis'}],
}], {
audioCapabilities: [{contentType: 'audio/webm; codecs=vorbis'}],
}, 'Supported audio codec');
expect_config('org.w3.clearkey', [{
audioCapabilities: [{contentType: 'audio/webm; codecs="vorbis"'}],
}], {
audioCapabilities: [{contentType: 'audio/webm; codecs="vorbis"'}],
}, 'ContentType formatting must be preserved');
expect_error('org.w3.clearkey', [{
audioCapabilities: [{contentType: 'audio/webm; codecs=fake'}],
}], 'NotSupportedError', 'Unsupported audio codec');
expect_error('org.w3.clearkey', [{
audioCapabilities: [
{contentType: 'audio/webm; codecs=mp4a'},
{contentType: 'audio/webm; codecs=mp4a.40.2'}
],
}], 'NotSupportedError', 'Mismatched audio container/codec');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs=vp8'}],
}], {
videoCapabilities: [{contentType: 'video/webm; codecs=vp8'}],
}, 'Supported video codec');
expect_error('org.w3.clearkey', [{
audioCapabilities: [{contentType: 'video/webm; codecs=vp8'}],
}], 'NotSupportedError', 'Video codec specified in audio field');
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'audio/webm; codecs=vorbis'}],
}], 'NotSupportedError', 'Audio codec specified in video field');
expect_error('org.w3.clearkey', [{
audioCapabilities: [{contentType: 'video/webm; codecs=fake'}],
}], 'NotSupportedError', 'Unsupported video codec');
expect_error('org.w3.clearkey', [{
audioCapabilities: [
{contentType: 'audio/webm; codecs=avc1'},
{contentType: 'audio/webm; codecs=avc1.42e01e'}
],
}], 'NotSupportedError', 'Mismatched video container/codec');
expect_config('org.w3.clearkey', [
{initDataTypes: ['fakeidt']},
{initDataTypes: ['webm']}
], {initDataTypes: ['webm']}, 'Two configurations, one supported');
expect_config('org.w3.clearkey', [
{initDataTypes: ['webm']},
{}
], {initDataTypes: ['webm']}, 'Two configurations, both supported');
// Audio MIME type does not support video codecs.
expect_error('org.w3.clearkey', [{
audioCapabilities: [
{contentType: 'audio/webm; codecs="vp8,vorbis"'},
{contentType: 'audio/webm; codecs="vorbis, vp8"'},
{contentType: 'audio/webm; codecs="vp8"'}
],
}], 'NotSupportedError', 'Audio MIME type does not support video codecs.');
// Video MIME type does not support audio codecs.
expect_error('org.w3.clearkey', [{
videoCapabilities: [
{contentType: 'video/webm; codecs="vp8,vorbis"'},
{contentType: 'video/webm; codecs="vorbis, vp8"'},
{contentType: 'video/webm; codecs="vorbis"'}
],
}], 'NotSupportedError', 'Video MIME type does not support audio codecs.');
// WebM does not support AVC1/AAC.
expect_error('org.w3.clearkey', [{
audioCapabilities: [
{contentType: 'audio/webm; codecs="aac"'},
{contentType: 'audio/webm; codecs="avc1"'},
{contentType: 'audio/webm; codecs="vp8,aac"'}
],
}], 'NotSupportedError', 'WebM audio does not support AVC1/AAC.');
expect_error('org.w3.clearkey', [{
videoCapabilities: [
{contentType: 'video/webm; codecs="aac"'},
{contentType: 'video/webm; codecs="avc1"'},
{contentType: 'video/webm; codecs="vp8,aac"'}
],
}], 'NotSupportedError', 'WebM video does not support AVC1/AAC.');
// Extra space is allowed in contentType.
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: ' video/webm; codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: ' video/webm; codecs="vp8"'}],
}, 'Leading space in contentType');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm ; codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: 'video/webm ; codecs="vp8"'}],
}, 'Space before ; in contentType');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: 'video/webm; codecs="vp8"'}],
}, 'Extra spaces after ; in contentType');
// TODO(jrummell): contentType should allow white space at the
// end of the string. http://crbug.com/487392
// expect_config('org.w3.clearkey', [{
// videoCapabilities: [{contentType: 'video/webm; codecs="vp8" '}],
// }], {
// videoCapabilities: [{contentType: 'video/webm; codecs="vp8" '}],
// }, 'Trailing space in contentType');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs=" vp8"'}],
}], {
videoCapabilities: [{contentType: 'video/webm; codecs=" vp8"'}],
}, 'Space at start of codecs parameter');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs="vp8 "'}],
}], {
videoCapabilities: [{contentType: 'video/webm; codecs="vp8 "'}],
}, 'Space at end of codecs parameter');
// contentType is not case sensitive (except the codec names).
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'Video/webm; codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: 'Video/webm; codecs="vp8"'}],
}, 'Video/webm');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/Webm; codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: 'video/Webm; codecs="vp8"'}],
}, 'video/Webm');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; Codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: 'video/webm; Codecs="vp8"'}],
}, 'Codecs=');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'VIDEO/WEBM; codecs="vp8"'}],
}], {
videoCapabilities: [{contentType: 'VIDEO/WEBM; codecs="vp8"'}],
}, 'VIDEO/WEBM');
expect_config('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; CODECS="vp8"'}],
}], {
videoCapabilities: [{contentType: 'video/webm; CODECS="vp8"'}],
}, 'CODECS=');
// Unrecognized attributes are not allowed.
// TODO(jrummell): Unrecognized attributes are ignored currently.
// http://crbug.com/449690
// expect_error('org.w3.clearkey', [{
// videoCapabilities: [{contentType: 'video/webm; foo="bar"'}],
// }], 'NotSupportedError', 'Unrecognized foo');
// expect_error('org.w3.clearkey', [{
// videoCapabilities: [{contentType: 'video/webm; foo="bar"; codecs="vp8"'}],
// }], 'NotSupportedError', 'Unrecognized foo with codecs');
// Invalid contentTypes.
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'fake'}],
}], 'NotSupportedError', 'contentType fake');
expect_error('org.w3.clearkey', [{
audioCapabilities: [{contentType: 'audio/fake'}],
}], 'NotSupportedError', 'contentType audio/fake');
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/fake'}],
}], 'NotSupportedError', 'contentType video/fake');
// The actual codec names are case sensitive.
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs="Vp8"'}],
}], 'NotSupportedError', 'codecs Vp8');
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs="VP8"'}],
}], 'NotSupportedError', 'codecs VP8');
// Extra comma is not allowed in codecs.
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs=",vp8"'}],
}], 'NotSupportedError', 'Leading , in codecs');
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs="vp8,"'}],
}], 'NotSupportedError', 'Trailing , in codecs');
expect_error('org.w3.clearkey', [{
videoCapabilities: [{contentType: 'video/webm; codecs=",vp8,"'}],
}], 'NotSupportedError', 'Leading and trailing , in codecs');
</script>
</body>
</html>

View file

@ -0,0 +1,60 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Reset src after setMediaKeys()</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var mediaKeys;
var encryptedEventIndex = 0;
var video = document.getElementById('testVideo');
assert_not_equals(video, null);
var onEncrypted = function(event)
{
++encryptedEventIndex;
assert_not_equals(video.mediaKeys, null);
assert_true(video.mediaKeys === mediaKeys);
// This event is fired once for the audio stream and once
// for the video stream each time .src is set.
if (encryptedEventIndex == 2) {
// Finished first video; set src to a different video.
video.src = '../content/test-encrypted-different-av-keys.webm';
} else if (encryptedEventIndex == 4) {
// Finished second video.
test.done();
}
};
// Create a MediaKeys object and assign it to video.
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}])
.then(function(access) {
assert_equals(access.keySystem, 'org.w3.clearkey');
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
assert_not_equals(mediaKeys, null);
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
assert_not_equals(video.mediaKeys, null, 'not set initially');
assert_true(video.mediaKeys === mediaKeys);
// Set src to a video.
waitForEventAndRunStep('encrypted', video, onEncrypted, test);
video.src = '../content/test-encrypted.webm';
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Reset src after setMediaKeys().');
</script>
</body>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test MediaKeySession closed event</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(test)
{
var initDataType;
var initData;
var mediaKeySession;
getSupportedInitDataType().then(function(type) {
initDataType = type;
initData = getInitData(initDataType);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
return mediaKeySession.generateRequest(initDataType, initData);
}).then(function() {
// Wait for the session to be closed.
mediaKeySession.closed.then(function(result) {
assert_equals(result, undefined);
// Now that the session is closed, verify that the
// closed attribute immediately returns a fulfilled
// promise.
return mediaKeySession.closed;
}).then(function(result) {
assert_equals(result, undefined);
test.done();
});
// release() should result in the closed promise being
// fulfilled.
return mediaKeySession.close();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Test MediaKeySession closed event.');
</script>
</body>
</html>

View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>setMediaKeys() again after playback</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
promise_test(function(test)
{
var video = document.getElementById('testVideo');
var keyId = stringToUint8Array('0123456789012345');
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
var content = '../content/test-encrypted.webm';
var duration = 0.2;
return createMediaKeys(keyId, rawKey).then(function(mediaKeys) {
return video.setMediaKeys(mediaKeys);
}).then(function() {
return playVideoAndWaitForTimeupdate(video, content, duration);
}).then(function() {
// Now create a second MediaKeys.
return createMediaKeys(keyId, rawKey);
}).then(function(mediaKeys) {
// video is currently playing, so should not be able to
// change MediaKeys now.
assert_false(video.ended);
return video.setMediaKeys(mediaKeys);
}).then(function() {
assert_unreached('Able to change MediaKeys while playing.');
}, function(error) {
// Error expected.
assert_equals(error.name, 'InvalidStateError');
return Promise.resolve('success');
});
}, 'setMediaKeys() again after playback');
</script>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>setMediaKeys() again after resetting src</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
promise_test(function(test)
{
var video = document.getElementById('testVideo');
var keyId = stringToUint8Array('0123456789012345');
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
var content = '../content/test-encrypted.webm';
var duration = 0.2;
return createMediaKeys(keyId, rawKey).then(function(mediaKeys) {
return video.setMediaKeys(mediaKeys);
}).then(function() {
return playVideoAndWaitForTimeupdate(video, content, duration);
}).then(function() {
// Now create a second MediaKeys and repeat.
return createMediaKeys(keyId, rawKey);
}).then(function(mediaKeys) {
// MediaKeys is use by previous video, so clear .src
// so that MediaKeys can be assigned.
video.src = '';
return video.setMediaKeys(mediaKeys);
}).then(function() {
return playVideoAndWaitForTimeupdate(video, content, duration);
});
}, 'setMediaKeys() again after resetting src');
</script>
</body>
</html>

View file

@ -0,0 +1,105 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Set MediaKeys multiple times in parallel</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="video"></video>
<div id="log"></div>
<script>
// Wait for an 'encrypted' event as a promise.
function wait_for_encrypted_event(video)
{
return new Promise(function(resolve) {
video.addEventListener('encrypted', function listener(e) {
video.removeEventListener('encrypted', listener);
resolve();
});
});
};
// Return a promise that calls setMediaKeys() and returns 1 if
// resolved, 0 if rejected. If |must_succeed| is true, then
// setMediaKeys() should not fail.
function setMediaKeys_as_count(video, mediaKeys, must_succeed)
{
return video.setMediaKeys(mediaKeys).then(function() {
return 1;
}, function() {
assert_false(must_succeed);
return 0;
});
};
// Return the sum of the results from |promises|. Each promise
// must return a number.
function count_promise_results(promises)
{
var count = 0;
var result = Promise.resolve(null);
promises.forEach(function(promise) {
result = result.then(function() {
return promise;
}).then(function(i) {
count += i;
});
});
return result.then(function() { return count; });
};
promise_test(function(test)
{
var video = document.getElementById('video');
var access;
var mediaKeys1;
var mediaKeys2;
var mediaKeys3;
var mediaKeys4;
var mediaKeys5;
// Start a video now so that it is waiting for MediaKeys
// in order to continue.
video.src = '../content/test-encrypted.webm';
video.play();
return wait_for_encrypted_event(video).then(function() {
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(result) {
access = result;
return access.createMediaKeys();
}).then(function(result) {
mediaKeys1 = result;
return access.createMediaKeys();
}).then(function(result) {
mediaKeys2 = result;
return access.createMediaKeys();
}).then(function(result) {
mediaKeys3 = result;
return access.createMediaKeys();
}).then(function(result) {
mediaKeys4 = result;
return access.createMediaKeys();
}).then(function(result) {
mediaKeys5 = result;
// Create 5 calls to setMediaKeys(). The first one must
// succeed, the others are optional.
var p1 = setMediaKeys_as_count(video, mediaKeys1, true);
var p2 = setMediaKeys_as_count(video, mediaKeys2, false);
var p3 = setMediaKeys_as_count(video, mediaKeys3, false);
var p4 = setMediaKeys_as_count(video, mediaKeys4, false);
var p5 = setMediaKeys_as_count(video, mediaKeys5, false);
return count_promise_results([p1, p2, p3, p4, p5]);
}).then(function(count) {
// At least one of the setMediaKeys() calls should have
// succeeded.
assert_greater_than(count, 0);
});
}, 'Set MediaKeys multiple times in parallel.');
</script>
</body>
</html>

View file

@ -0,0 +1,79 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>setMediaKeys() multiple times with different MediaKeys.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="video"></video>
<div id="log"></div>
<script>
promise_test(function(test)
{
var video = document.getElementById('video');
var keySystemAccess;
var mediaKeys1;
var mediaKeys2;
assert_equals(video.mediaKeys, null);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
keySystemAccess = access;
// Create a mediaKeys.
return keySystemAccess.createMediaKeys();
}).then(function(result) {
mediaKeys1 = result;
assert_not_equals(mediaKeys1, null);
// Create a second mediaKeys.
return keySystemAccess.createMediaKeys();
}).then(function(result) {
mediaKeys2 = result;
assert_not_equals(mediaKeys2, null);
// Set mediaKeys1 on video.
return video.setMediaKeys(mediaKeys1);
}).then(function() {
assert_true(video.mediaKeys === mediaKeys1);
// Set mediaKeys2 on video (switching MediaKeys).
return video.setMediaKeys(mediaKeys2);
}).then(function() {
assert_true(video.mediaKeys === mediaKeys2);
// Clear mediaKeys from video.
return video.setMediaKeys(null);
}).then(function() {
assert_equals(video.mediaKeys, null);
// Set mediaKeys1 on video again.
return video.setMediaKeys(mediaKeys1);
}).then(function() {
assert_true(video.mediaKeys === mediaKeys1);
// Load the media element to create the WebMediaPlayer.
video.src = '../content/test-encrypted.webm';
// Set mediaKeys2 on video (switching MediaKeys) not
// supported after WebMediaPlayer is created.
return video.setMediaKeys(mediaKeys2);
}).then(function() {
assert_unreached('Switching mediaKeys after setting src should have failed.');
}, function(error) {
assert_true(video.mediaKeys === mediaKeys1);
assert_equals(error.name, 'InvalidStateError');
assert_not_equals(error.message, '');
// Return something so the promise resolves properly.
return Promise.resolve();
}).then(function() {
// Set null mediaKeys on video (clearing MediaKeys) not
// supported after WebMediaPlayer is created.
return video.setMediaKeys(null);
}).then(function() {
assert_unreached('Clearing mediaKeys after setting src should have failed.');
}, function(error) {
assert_true(video.mediaKeys === mediaKeys1);
assert_equals(error.name, 'InvalidStateError');
assert_not_equals(error.message, '');
return Promise.resolve();
});
}, 'setMediaKeys() multiple times with different MediaKeys.');
</script>
</body>
</html>

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>setMediaKeys() multiple times with the same MediaKeys.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="video"></video>
<div id="log"></div>
<script>
promise_test(function(test)
{
var video = document.getElementById('video');
var mediaKeys;
assert_equals(video.mediaKeys, null);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
assert_not_equals(mediaKeys, null);
// Set mediaKeys on video should work.
return video.setMediaKeys(mediaKeys);
}).then(function() {
assert_true(video.mediaKeys === mediaKeys);
// Set mediaKeys on video again should return a resolved
// promise.
return video.setMediaKeys(mediaKeys);
}).then(function() {
assert_true(video.mediaKeys === mediaKeys);
// Load the media element to create the WebMediaPlayer.
video.src = '../content/test-encrypted.webm';
// Set mediaKeys again on video should still return a
// resolved promise.
return video.setMediaKeys(mediaKeys);
}).then(function() {
assert_true(video.mediaKeys === mediaKeys);
});
}, 'setMediaKeys() multiple times with the same MediaKeys.');
</script>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>setMediaKeys() on multiple video elements.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="video1"></video>
<video id="video2"></video>
<div id="log"></div>
<script>
promise_test(function(test)
{
var video1 = document.getElementById('video1');
var video2 = document.getElementById('video2');
var mediaKeys;
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
// Assignment to video1 should work.
return video1.setMediaKeys(mediaKeys);
}).then(function() {
// Assignment to video2 should fail.
return video2.setMediaKeys(mediaKeys);
}).then(function() {
assert_unreached('Second setMediaKeys should have failed.');
}, function(error) {
assert_equals(error.name, 'QuotaExceededError');
assert_not_equals(error.message, '');
// Return something so the promise resolves properly.
return Promise.resolve();
}).then(function() {
// Now clear it from video1.
return video1.setMediaKeys(null);
}).then(function() {
// Should be assignable to video2.
return video2.setMediaKeys(mediaKeys);
});
}, 'setMediaKeys() on multiple video elements.');
</script>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>setMediaKeys</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="video"></video>
<div id="log"></div>
<script>
async_test(function(test)
{
var mediaKeys;
var video = document.getElementById('video');
assert_not_equals(video, null);
// Test MediaKeys assignment.
assert_equals(video.mediaKeys, null);
assert_equals(typeof video.setMediaKeys, 'function');
// Try setting mediaKeys to null.
video.setMediaKeys(null).then(function(result) {
assert_equals(video.mediaKeys, null);
// Try setting mediaKeys to the wrong type of object.
return video.setMediaKeys(new Date());
}).then(function(result) {
assert_unreached('setMediaKeys did not fail when setting to Date()');
}, function(error) {
// TypeError expected.
assert_equals(error.name, 'TypeError');
// Create a MediaKeys object and assign it to video.
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
assert_equals(access.keySystem, 'org.w3.clearkey');
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
assert_not_equals(mediaKeys, null);
assert_equals(typeof mediaKeys.createSession, 'function');
return video.setMediaKeys(mediaKeys);
}).then(function(result) {
assert_not_equals(video.mediaKeys, null);
assert_true(video.mediaKeys === mediaKeys);
test.done();
}).catch(function(error) {
forceTestFailureFromPromise(test, error);
});
}, 'Setting MediaKeys on a video object.');
</script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Unique origin is unable to create MediaKeys</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// When the sandbox attribute is present on an iframe, it will
// treat the content as being from a unique origin. So try to
// call createMediaKeys() inside an iframe and it should fail.
function load_iframe(src, sandbox) {
return new Promise(function(resolve) {
var iframe = document.createElement('iframe');
iframe.onload = function() { resolve(iframe); };
iframe.sandbox = sandbox;
iframe.src = src;
document.documentElement.appendChild(iframe);
});
}
function wait_for_message() {
return new Promise(function(resolve) {
self.addEventListener('message', function listener(e) {
resolve(e.data);
self.removeEventListener('message', listener);
});
});
}
promise_test(function(test) {
var script = 'data:text/html,' +
'<script>' +
' window.onmessage = function(e) {' +
' navigator.requestMediaKeySystemAccess(\'org.w3.clearkey\', [{}]).then(function(access) {' +
' return access.createMediaKeys();' +
' }).then(function(mediaKeys) {' +
' window.parent.postMessage({result: \'allowed\'}, \'*\');' +
' }, function(error) {' +
' window.parent.postMessage({result: \'failed\'}, \'*\');' +
' });' +
' };' +
'<\/script>';
// Verify that this page can create a MediaKeys first.
navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
// Success, so now create the iframe and try there.
return load_iframe(script, 'allow-scripts')
}).then(function(iframe) {
iframe.contentWindow.postMessage({}, '*');
return wait_for_message();
}).then(function(message) {
assert_equals(message.result, 'failed');
});
}, 'Unique origin is unable to create MediaKeys');
</script>
</body>
</html>

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Test handling of invalid responses for update().</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
// This test passes |response| to update() as a JSON Web Key Set.
// CDMs other than Clear Key won't expect |response| in this format.
async_test(function(test)
{
var initDataType;
var mediaKeySession;
function repeat(pattern, count) {
var result = '';
while (count > 1) {
if (count & 1) result += pattern;
count >>= 1;
pattern += pattern;
}
return result + pattern;
}
function createReallyLongJWKSet()
{
// This is just a standard JWKSet with a lot of
// extra items added to the end. Key ID and key
// doesn't really matter.
var jwkSet = '{"keys":[{'
+ '"kty":"oct",'
+ '"k":"MDEyMzQ1Njc4OTAxMjM0NQ",'
+ '"kid":"MDEyMzQ1Njc4OTAxMjM0NQ"'
+ '}]';
return jwkSet + repeat(',"test":"unknown"', 4000) + '}';
}
function processMessage(event)
{
var jwkSet = createReallyLongJWKSet();
assert_greater_than(jwkSet.length, 65536);
var jwkSetArray = stringToUint8Array(jwkSet);
mediaKeySession.update(jwkSetArray).then(function() {
forceTestFailureFromPromise(test, 'Error: update() succeeded');
}, function(error) {
assert_equals(error.name, 'InvalidAccessError');
test.done();
});
}
getSupportedInitDataType().then(function(type) {
initDataType = type;
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]);
}).then(function(access) {
return access.createMediaKeys();
}).then(function(mediaKeys) {
mediaKeySession = mediaKeys.createSession();
waitForEventAndRunStep('message', mediaKeySession, processMessage, test);
return mediaKeySession.generateRequest(initDataType, getInitData(initDataType));
});
}, 'update() with response longer than 64Kb characters.');
</script>
</body>
</html>

View file

@ -0,0 +1,300 @@
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
var consoleDiv = null;
function consoleWrite(text)
{
if (!consoleDiv && document.body) {
consoleDiv = document.createElement('div');
document.body.appendChild(consoleDiv);
}
var span = document.createElement('span');
span.appendChild(document.createTextNode(text));
span.appendChild(document.createElement('br'));
consoleDiv.appendChild(span);
}
// Returns a promise that is fulfilled with true if |initDataType| is supported,
// or false if not.
function isInitDataTypeSupported(initDataType)
{
return navigator.requestMediaKeySystemAccess(
"org.w3.clearkey", [{ initDataTypes : [initDataType] }])
.then(function() { return(true); }, function() { return(false); });
}
// Returns a promise that is fulfilled with an initDataType that is supported,
// rejected if none are supported.
function getSupportedInitDataType()
{
var configuration = [{ initDataTypes : [ 'webm', 'cenc', 'keyids' ] }];
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', configuration)
.then(function(access) {
var initDataTypes = access.getConfiguration().initDataTypes;
assert_greater_than(initDataTypes.length, 0);
return Promise.resolve(initDataTypes[0]);
}, function(error) {
return Promise.reject('No supported initDataType.');
});
}
function getInitData(initDataType)
{
if (initDataType == 'webm') {
return new Uint8Array([
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
]);
}
if (initDataType == 'cenc') {
return new Uint8Array([
0x00, 0x00, 0x00, 0x00, // size = 0
0x70, 0x73, 0x73, 0x68, // 'pssh'
0x01, // version = 1
0x00, 0x00, 0x00, // flags
0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // Common SystemID
0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B,
0x00, 0x00, 0x00, 0x01, // key count
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // key
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x00, 0x00, 0x00 // datasize
]);
}
if (initDataType == 'keyids') {
var keyId = new Uint8Array([
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
]);
return stringToUint8Array(createKeyIDs(keyId));
}
throw 'initDataType ' + initDataType + ' not supported.';
}
function waitForEventAndRunStep(eventName, element, func, stepTest)
{
var eventCallback = function(event) {
if (func)
func(event);
}
if (stepTest)
eventCallback = stepTest.step_func(eventCallback);
element.addEventListener(eventName, eventCallback, true);
}
// Copied from LayoutTests/resources/js-test.js.
// See it for details of why this is necessary.
function asyncGC(callback)
{
GCController.collectAll();
setTimeout(callback, 0);
}
function createGCPromise()
{
// Run gc() as a promise.
return new Promise(
function(resolve, reject) {
asyncGC(resolve);
});
}
function delayToAllowEventProcessingPromise()
{
return new Promise(
function(resolve, reject) {
setTimeout(resolve, 0);
});
}
function stringToUint8Array(str)
{
var result = new Uint8Array(str.length);
for(var i = 0; i < str.length; i++) {
result[i] = str.charCodeAt(i);
}
return result;
}
function arrayBufferAsString(buffer)
{
// MediaKeySession.keyStatuses iterators return an ArrayBuffer,
// so convert it into a printable string.
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
function dumpKeyStatuses(keyStatuses)
{
consoleWrite("for (var entry of keyStatuses)");
for (var entry of keyStatuses) {
consoleWrite(arrayBufferAsString(entry[0]) + ", " + entry[1]);
}
consoleWrite("for (var key of keyStatuses.keys())");
for (var key of keyStatuses.keys()) {
consoleWrite(arrayBufferAsString(key));
}
consoleWrite("for (var value of keyStatuses.values())");
for (var value of keyStatuses.values()) {
consoleWrite(value);
}
consoleWrite("for (var entry of keyStatuses.entries())");
for (var entry of keyStatuses.entries()) {
consoleWrite(arrayBufferAsString(entry[0]) + ", " + entry[1]);
}
consoleWrite("keyStatuses.forEach()");
keyStatuses.forEach(function(value, key, map) {
consoleWrite(arrayBufferAsString(key) + ", " + value);
});
}
// Verify that |keyStatuses| contains just the keys in |keys.expected|
// and none of the keys in |keys.unexpected|. All keys should have status
// 'usable'. Example call: verifyKeyStatuses(mediaKeySession.keyStatuses,
// { expected: [key1], unexpected: [key2] });
function verifyKeyStatuses(keyStatuses, keys)
{
var expected = keys.expected || [];
var unexpected = keys.unexpected || [];
// |keyStatuses| should have same size as number of |keys.expected|.
assert_equals(keyStatuses.size, expected.length);
// All |keys.expected| should be found.
expected.map(function(key) {
assert_true(keyStatuses.has(key));
assert_equals(keyStatuses.get(key), 'usable');
});
// All |keys.unexpected| should not be found.
unexpected.map(function(key) {
assert_false(keyStatuses.has(key));
assert_equals(keyStatuses.get(key), undefined);
});
}
// Encodes |data| into base64url string. There is no '=' padding, and the
// characters '-' and '_' must be used instead of '+' and '/', respectively.
function base64urlEncode(data)
{
var result = btoa(String.fromCharCode.apply(null, data));
return result.replace(/=+$/g, '').replace(/\+/g, "-").replace(/\//g, "_");
}
// Decode |encoded| using base64url decoding.
function base64urlDecode(encoded)
{
return atob(encoded.replace(/\-/g, "+").replace(/\_/g, "/"));
}
// For Clear Key, the License Format is a JSON Web Key (JWK) Set, which contains
// a set of cryptographic keys represented by JSON. These helper functions help
// wrap raw keys into a JWK set.
// See:
// https://w3c.github.io/encrypted-media/#clear-key-license-format
// http://tools.ietf.org/html/draft-ietf-jose-json-web-key
//
// Creates a JWK from raw key ID and key.
// |keyId| and |key| are expected to be ArrayBufferViews, not base64-encoded.
function createJWK(keyId, key)
{
var jwk = '{"kty":"oct","alg":"A128KW","kid":"';
jwk += base64urlEncode(keyId);
jwk += '","k":"';
jwk += base64urlEncode(key);
jwk += '"}';
return jwk;
}
// Creates a JWK Set from multiple JWKs.
function createJWKSet()
{
var jwkSet = '{"keys":[';
for (var i = 0; i < arguments.length; i++) {
if (i != 0)
jwkSet += ',';
jwkSet += arguments[i];
}
jwkSet += ']}';
return jwkSet;
}
// Clear Key can also support Key IDs Initialization Data.
// ref: http://w3c.github.io/encrypted-media/keyids-format.html
// Each parameter is expected to be a key id in an Uint8Array.
function createKeyIDs()
{
var keyIds = '{"kids":["';
for (var i = 0; i < arguments.length; i++) {
if (i != 0)
keyIds += '","';
keyIds += base64urlEncode(arguments[i]);
}
keyIds += '"]}';
return keyIds;
}
function forceTestFailureFromPromise(test, error, message)
{
// Promises convert exceptions into rejected Promises. Since there is
// currently no way to report a failed test in the test harness, errors
// are reported using force_timeout().
if (message)
consoleWrite(message + ': ' + error.message);
else if (error)
consoleWrite(error);
test.force_timeout();
test.done();
}
function extractSingleKeyIdFromMessage(message)
{
var json = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(message)));
// Decode the first element of 'kids'.
assert_equals(1, json.kids.length);
var decoded_key = base64urlDecode(json.kids[0]);
// Convert to an Uint8Array and return it.
return stringToUint8Array(decoded_key);
}
// Create a MediaKeys object for Clear Key with 1 session. KeyId and key
// required for the video are already known and provided. Returns a promise
// that resolves to the MediaKeys object created.
function createMediaKeys(keyId, key)
{
var mediaKeys;
var mediaKeySession;
var request = stringToUint8Array(createKeyIDs(keyId));
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, key)));
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
return access.createMediaKeys();
}).then(function(result) {
mediaKeys = result;
mediaKeySession = mediaKeys.createSession();
return mediaKeySession.generateRequest('keyids', request);
}).then(function() {
return mediaKeySession.update(jwkSet);
}).then(function() {
return Promise.resolve(mediaKeys);
});
}
// Play the specified |content| on |video|. Returns a promise that is resolved
// after the video plays for |duration| seconds.
function playVideoAndWaitForTimeupdate(video, content, duration)
{
video.src = content;
video.play();
return new Promise(function(resolve) {
video.addEventListener('timeupdate', function listener(event) {
if (event.target.currentTime < duration)
return;
video.removeEventListener('timeupdate', listener);
resolve('success');
});
});
}

View file

@ -0,0 +1,164 @@
<!DOCTYPE html>
<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
<html>
<head>
<title>Waiting for a key.</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="testVideo"></video>
<div id="log"></div>
<script>
// For debugging timeouts, keep track of the number of the
// various events received.
var debugEncryptedEventCount = 0;
var debugWaitingForKeyEventCount = 0;
var debugTimeUpdateEventCount = 0;
var debugMessage = '';
promise_test(function(test)
{
var video = document.getElementById('testVideo');
var initData;
var initDataType;
var mediaKeySession;
test.timeout = function()
{
var message = 'timeout. message = ' + debugMessage
+ ', encrypted: ' + debugEncryptedEventCount
+ ', waitingforkey: ' + debugWaitingForKeyEventCount
+ ', timeupdate: ' + debugTimeUpdateEventCount;
test.force_timeout();
test.timeout_id = null;
test.set_status(2, message);
test.done();
};
// As this code doesn't wait for the 'message' event to avoid
// race conditions with 'waitingforkey', specify the key ID and
// key used by the encrypted content.
var keyId = stringToUint8Array('0123456789012345');
var rawKey = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
return navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{}]).then(function(access) {
debugMessage = 'createMediaKeys()';
return access.createMediaKeys();
}).then(function(mediaKeys) {
debugMessage = 'setMediaKeys()';
return video.setMediaKeys(mediaKeys);
}).then(function() {
video.src = '../content/test-encrypted.webm';
video.play();
debugMessage = 'wait_for_encrypted_event()';
return wait_for_encrypted_event(video);
}).then(function(e) {
// Received the 'encrypted' event(s), so keep a copy of
// the initdata for use when creating the session later.
initData = e.initData;
initDataType = e.initDataType;
// Wait until the video indicates that it needs a key to
// continue.
debugMessage = 'wait_for_waitingforkey_event()';
return wait_for_waitingforkey_event(video);
}).then(function() {
// Make sure the video is NOT paused and not progressing
// before a key is provided. This requires the video
// to NOT have a clear lead.
assert_false(video.paused);
assert_equals(video.currentTime, 0);
// Create a session.
mediaKeySession = video.mediaKeys.createSession();
debugMessage = 'generateRequest()';
return mediaKeySession.generateRequest(initDataType, initData);
}).then(function() {
// generateRequest() will cause a 'message' event to
// occur specifying the keyId that is needed, but we
// ignore it since we already know what keyId is needed.
// Add the key needed to decrypt.
var jwkSet = stringToUint8Array(createJWKSet(createJWK(keyId, rawKey)));
debugMessage = 'update()';
return mediaKeySession.update(jwkSet);
}).then(function() {
// Video should start playing now that it can decrypt the
// streams, so wait until a little bit of the video has
// played.
debugMessage = 'wait_for_timeupdate_event()';
return wait_for_timeupdate_event(video);
});
// Typical test duration is 6 seconds on release builds
// (12 seconds on debug). Since the test is timing out anyway,
// make the duration 5 seconds so that the timeout function
// is actually called (instead of simply aborting the test).
}, 'Waiting for a key.', { timeout: 5000 });
// Wait for a pair of 'encrypted' events. Promise resolved on
// second event.
function wait_for_encrypted_event(video)
{
var encryptedEventCount = 0;
return new Promise(function(resolve) {
video.addEventListener('encrypted', function listener(e) {
assert_equals(e.target, video);
assert_true(e instanceof window.MediaEncryptedEvent);
assert_equals(e.type, 'encrypted');
// The same decryption key is used by both the audio
// and the video streams so wait for the second event
// to ensure we see both events.
++debugEncryptedEventCount;
if (++encryptedEventCount != 2)
return;
video.removeEventListener('encrypted', listener);
resolve(e);
});
});
};
// Wait for a 'waitingforkey' event. Promise resolved when the
// event is received.
function wait_for_waitingforkey_event(video)
{
var waitingForKeyEventCount = 0;
return new Promise(function(resolve) {
video.addEventListener('waitingforkey', function listener(e) {
assert_equals(e.target, video);
assert_equals(e.type, 'waitingforkey');
++debugWaitingForKeyEventCount;
++waitingForKeyEventCount;
// TODO(jrummell): waitingforkey event should only
// occur once. http://crbug.com/461903
// assert_equals(waitingForKeyEventCount, 1, 'Multiple waitingforkey events');
video.removeEventListener('waitingforkey', listener);
resolve(e);
});
});
};
// Wait for a 'timeupdate' event. Promise resolved if |video| has
// played for more than 0.2 seconds.
function wait_for_timeupdate_event(video)
{
return new Promise(function(resolve) {
video.addEventListener('timeupdate', function listener(e) {
assert_equals(e.target, video);
++debugTimeUpdateEventCount;
if (video.currentTime < 0.2)
return;
video.removeEventListener('timeupdate', listener);
resolve(e);
});
});
};
</script>
</body>
</html>

View file

@ -0,0 +1 @@
@ddorwin

View file

@ -32,7 +32,17 @@ function checkKoUrl(url, method, desc) {
var blob2 = new Blob(["Blob's data"], { "type" : "text/plain" });
checkKoUrl("blob:http://{{domains[www]}}:{{ports[http][0]}}/", "GET",
"Fetching [GET] blob:http://{{domains[www]}}:{{ports[http][0]}}/ is KO");
checkKoUrl(URL.createObjectURL(blob2), "POST",
"Fetching [POST] URL.createObjectURL(blob) is KO");
var invalidRequestMethods = [
"POST",
"OPTIONS",
"HEAD",
"PUT",
"DELETE",
"INVALID",
];
invalidRequestMethods.forEach(function(method) {
checkKoUrl(URL.createObjectURL(blob2), method, "Fetching [" + method + "] URL.createObjectURL(blob) is KO");
});
done();

View file

@ -32,7 +32,7 @@ var origin = "http://{{host}}:{{ports[http][0]}}";
corsPreflightReferrer("Referrer policy: no-referrer", corsUrl, "no-referrer", "");
corsPreflightReferrer("Referrer policy: \"\"", corsUrl, "", "");
corsPreflightReferrer("Referrer policy: origin-only", corsUrl, "origin-only", origin);
corsPreflightReferrer("Referrer policy: origin", corsUrl, "origin", origin);
corsPreflightReferrer("Referrer policy: origin-when-cross-origin", corsUrl, "origin-when-cross-origin", origin);
corsPreflightReferrer("Referrer policy: unsafe-url", corsUrl, "unsafe-url", location.toString());

View file

@ -27,7 +27,7 @@ function corsPreflight(desc, corsUrl, method, allowed, headers) {
urlParameters += "&control_request_headers"
//Make the server allow the headers
urlParameters += "&allow_headers="
urlParameters += headers.join("%2C%20");
urlParameters += headers.map(function (x) { return x[0]; }).join("%2C%20");
}
promise_test(function(test) {
test.add_cleanup(function() {
@ -40,8 +40,8 @@ function corsPreflight(desc, corsUrl, method, allowed, headers) {
var actualHeaders = resp.headers.get("x-control-request-headers").split(",");
for (var i in actualHeaders)
actualHeaders[i] = actualHeaders[i].trim();
for (var header in headers)
assert_in_array(header, actualHeaders, "Preflight asked permission for header: " + header);
for (var header of headers)
assert_in_array(header[0], actualHeaders, "Preflight asked permission for header: " + header);
}
});
}, desc);
@ -67,16 +67,13 @@ corsPreflight("CORS [PATCH], server refuses", corsUrl, "PATCH", false);
corsPreflight("CORS [NEW], server allows", corsUrl, "NEW", true);
corsPreflight("CORS [NEW], server refuses", corsUrl, "NEW", false);
corsPreflight("CORS [GET] [x-test-header: allowed], server allows", corsUrl, "GET", true, {"x-test-header1": "allowed"});
corsPreflight("CORS [GET] [x-test-header: refused], server refuses", corsUrl, "GET", false, {"x-test-header1": "refused"});
corsPreflight("CORS [GET] [x-test-header: allowed], server allows", corsUrl, "GET", true, [["x-test-header1", "allowed"]]);
corsPreflight("CORS [GET] [x-test-header: refused], server refuses", corsUrl, "GET", false, [["x-test-header1", "refused"]]);
var headers = {"x-test-header1": "allowedOrRefused",
"x-test-header2": "allowedOrRefused",
"x-test-header3": "allowedOrRefused",
};
var headers = [["x-test-header1", "allowedOrRefused"],
["x-test-header2", "allowedOrRefused"],
["x-test-header3", "allowedOrRefused"]];
corsPreflight("CORS [GET] [several headers], server allows", corsUrl, "GET", true, headers);
corsPreflight("CORS [GET] [several headers], server refuses", corsUrl, "GET", false, headers);
corsPreflight("CORS [PUT] [several headers], server allows", corsUrl, "PUT", true, headers);
corsPreflight("CORS [PUT] [several headers], server refuses", corsUrl, "PUT", false, headers);
done();

View file

@ -0,0 +1,23 @@
<!doctype html>
<meta charset="utf-8">
<title>Fetch: handling different schemes in redirects</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>
<script>
// All non-HTTP(S) schemes cannot survive redirects
var url = "../resources/redirect.py?location=",
tests = [
fetch(url + "mailto:a@a.com"),
fetch(url + "data:,HI"),
fetch(url + "facetime:a@a.org"),
fetch(url + "about:blank"),
fetch(url + "about:unicorn"),
fetch(url + "blob:djfksfjs")
];
tests.forEach(function(f) {
promise_test(function(t) {
return promise_rejects(t, new TypeError(), f)
})
})
</script>

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