utils.js (11240B)
1 function testnamePrefix( qualifier, keysystem ) { 2 return ( qualifier || '' ) + ( keysystem === 'org.w3.clearkey' ? keysystem : 'drm' ); 3 } 4 5 function getInitData(initDataType) { 6 7 // FIXME: This is messed up, because here we are hard coding the key ids for the different content 8 // that we use for clearkey testing: webm and mp4. For keyids we return the mp4 one 9 // 10 // The content used with the DRM today servers has a different key id altogether 11 12 if (initDataType == 'webm') { 13 return new Uint8Array([ 14 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 15 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F 16 ]); 17 } 18 19 if (initDataType == 'cenc') { 20 return new Uint8Array([ 21 0x00, 0x00, 0x00, 0x34, // size 22 0x70, 0x73, 0x73, 0x68, // 'pssh' 23 0x01, // version = 1 24 0x00, 0x00, 0x00, // flags 25 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // Common SystemID 26 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B, 27 0x00, 0x00, 0x00, 0x01, // key count 28 0x00, 0x00, 0x00, 0x00, 0x03, 0xd2, 0xfc, 0x41, // key id 29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 0x00, 0x00, 0x00, 0x00 // datasize 31 ]); 32 } 33 if (initDataType == 'keyids') { 34 var keyId = new Uint8Array([ 35 0x00, 0x00, 0x00, 0x00, 0x03, 0xd2, 0xfc, 0x41, 36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 37 ]); 38 return stringToUint8Array(createKeyIDs(keyId)); 39 } 40 throw 'initDataType ' + initDataType + ' not supported.'; 41 } 42 43 function stringToUint8Array(str) 44 { 45 var result = new Uint8Array(str.length); 46 for(var i = 0; i < str.length; i++) { 47 result[i] = str.charCodeAt(i); 48 } 49 return result; 50 } 51 // Encodes |data| into base64url string. There is no '=' padding, and the 52 // characters '-' and '_' must be used instead of '+' and '/', respectively. 53 function base64urlEncode(data) { 54 var result = btoa(String.fromCharCode.apply(null, data)); 55 return result.replace(/=+$/g, '').replace(/\+/g, "-").replace(/\//g, "_"); 56 } 57 // Decode |encoded| using base64url decoding. 58 function base64urlDecode(encoded) { 59 return atob(encoded.replace(/\-/g, "+").replace(/\_/g, "/")); 60 } 61 // Decode |encoded| using base64 to a Uint8Array 62 function base64DecodeToUnit8Array(encoded) { 63 return new Uint8Array( atob( encoded ).split('').map( function(c){return c.charCodeAt(0);} ) ); 64 } 65 // Clear Key can also support Key IDs Initialization Data. 66 // ref: http://w3c.github.io/encrypted-media/keyids-format.html 67 // Each parameter is expected to be a key id in an Uint8Array. 68 function createKeyIDs() { 69 var keyIds = '{"kids":["'; 70 for (var i = 0; i < arguments.length; i++) { 71 if (i != 0) keyIds += '","'; 72 keyIds += base64urlEncode(arguments[i]); 73 } 74 keyIds += '"]}'; 75 return keyIds; 76 } 77 78 function getSupportedKeySystem() { 79 var userAgent = navigator.userAgent.toLowerCase(); 80 var keysystem = undefined; 81 if (userAgent.indexOf('edge') > -1 ) { 82 keysystem = 'com.microsoft.playready'; 83 } else if (userAgent.indexOf('chrome') > -1) { 84 keysystem = 'com.widevine.alpha'; 85 } else if (userAgent.indexOf('firefox') > -1) { 86 if (userAgent.includes("win")) { 87 keysystem = 'com.microsoft.playready.recommendation'; 88 } else { 89 keysystem = 'com.widevine.alpha'; 90 } 91 } 92 return keysystem; 93 } 94 95 function waitForEventAndRunStep(eventName, element, func, stepTest) 96 { 97 var eventCallback = function(event) { 98 if (func) 99 func(event); 100 } 101 102 element.addEventListener(eventName, stepTest.step_func(eventCallback), true); 103 } 104 105 function waitForEvent(eventName, element) { 106 return new Promise(function(resolve) { 107 element.addEventListener(eventName, resolve, true); 108 }) 109 } 110 111 var consoleDiv = null; 112 113 function consoleWrite(text) 114 { 115 if (!consoleDiv && document.body) { 116 consoleDiv = document.createElement('div'); 117 document.body.appendChild(consoleDiv); 118 } 119 var span = document.createElement('span'); 120 span.appendChild(document.createTextNode(text)); 121 span.appendChild(document.createElement('br')); 122 consoleDiv.appendChild(span); 123 } 124 125 function forceTestFailureFromPromise(test, error, message) 126 { 127 test.step_func(assert_unreached)(message ? message + ': ' + error.message : error); 128 } 129 130 // Returns an array of audioCapabilities that includes entries for a set of 131 // codecs that should cover all user agents. 132 function getPossibleAudioCapabilities() 133 { 134 return [ 135 { contentType: 'audio/mp4; codecs="mp4a.40.2"' }, 136 { contentType: 'audio/webm; codecs="opus"' }, 137 ]; 138 } 139 140 // Returns a trivial MediaKeySystemConfiguration that should be accepted, 141 // possibly as a subset of the specified capabilities, by all user agents. 142 function getSimpleConfiguration() 143 { 144 return [ { 145 initDataTypes : [ 'webm', 'cenc', 'keyids' ], 146 audioCapabilities: getPossibleAudioCapabilities() 147 } ]; 148 } 149 150 // Returns a MediaKeySystemConfiguration for |initDataType| that should be 151 // accepted, possibly as a subset of the specified capabilities, by all 152 // user agents. 153 function getSimpleConfigurationForInitDataType(initDataType) 154 { 155 return [ { 156 initDataTypes: [ initDataType ], 157 audioCapabilities: getPossibleAudioCapabilities() 158 } ]; 159 } 160 161 // Returns a promise that is fulfilled with true if |initDataType| is supported, 162 // by keysystem or false if not. 163 function isInitDataTypeSupported(keysystem,initDataType) 164 { 165 return navigator.requestMediaKeySystemAccess( 166 keysystem, getSimpleConfigurationForInitDataType(initDataType)) 167 .then(function() { return true; }, function() { return false; }); 168 } 169 170 function getSupportedInitDataTypes( keysystem ) 171 { 172 return [ 'cenc', 'keyids', 'webm' ].filter( isInitDataTypeSupported.bind( null, keysystem ) ); 173 } 174 175 function arrayBufferAsString(buffer) 176 { 177 var array = []; 178 Array.prototype.push.apply( array, new Uint8Array( buffer ) ); 179 return '0x' + array.map( function( x ) { return x < 16 ? '0'+x.toString(16) : x.toString(16); } ).join(''); 180 } 181 182 function dumpKeyStatuses(keyStatuses,short) 183 { 184 var userAgent = navigator.userAgent.toLowerCase(); 185 if (userAgent.indexOf('edge') === -1) { 186 if (!short) { consoleWrite("for (var entry of keyStatuses)"); } 187 for (var entry of keyStatuses) { 188 consoleWrite(arrayBufferAsString(entry[0]) + ": " + entry[1]); 189 } 190 if (!short) { 191 consoleWrite("for (var keyId of keyStatuses.keys())"); 192 for (var keyId of keyStatuses.keys()) { 193 consoleWrite(arrayBufferAsString(keyId)); 194 } 195 consoleWrite("for (var status of keyStatuses.values())"); 196 for (var status of keyStatuses.values()) { 197 consoleWrite(status); 198 } 199 consoleWrite("for (var entry of keyStatuses.entries())"); 200 for (var entry of keyStatuses.entries()) { 201 consoleWrite(arrayBufferAsString(entry[0]) + ": " + entry[1]); 202 } 203 consoleWrite("keyStatuses.forEach()"); 204 keyStatuses.forEach(function(status, keyId) { 205 consoleWrite(arrayBufferAsString(keyId) + ": " + status); 206 }); 207 } 208 } else { 209 if (!short) { consoleWrite("keyStatuses.forEach()"); } 210 keyStatuses.forEach(function(keyId, status) { 211 consoleWrite(arrayBufferAsString(keyId) + ": " + status); 212 }); 213 } 214 } 215 216 // Verify that |keyStatuses| contains just the keys in |keys.expected| 217 // and none of the keys in |keys.unexpected|. All keys should have status 218 // 'usable'. Example call: verifyKeyStatuses(mediaKeySession.keyStatuses, 219 // { expected: [key1], unexpected: [key2] }); 220 function verifyKeyStatuses(keyStatuses, keys) 221 { 222 var expected = keys.expected || []; 223 var unexpected = keys.unexpected || []; 224 225 // |keyStatuses| should have same size as number of |keys.expected|. 226 assert_equals(keyStatuses.size, expected.length, "keystatuses should have expected size"); 227 228 // All |keys.expected| should be found. 229 expected.map(function(key) { 230 assert_true(keyStatuses.has(key), "keystatuses should have the expected keys"); 231 assert_equals(keyStatuses.get(key), 'usable', "keystatus value should be 'usable'"); 232 }); 233 234 // All |keys.unexpected| should not be found. 235 unexpected.map(function(key) { 236 assert_false(keyStatuses.has(key), "keystatuses should not have unexpected keys"); 237 assert_equals(keyStatuses.get(key), undefined, "keystatus for unexpected key should be undefined"); 238 }); 239 } 240 241 // This function checks that calling |testCase.func| returns a 242 // rejected Promise with the error.name equal to 243 // |testCase.exception|. 244 function test_exception(testCase /*...*/) { 245 var func = testCase.func; 246 var exception = testCase.exception; 247 var args = Array.prototype.slice.call(arguments, 1); 248 249 // This should really be rewritten in terms of the promise_rejects_* 250 // testharness utility functions, but that needs the async test involved 251 // passed in, and we don't have that here. 252 return func.apply(null, args).then( 253 function (result) { 254 assert_unreached(format_value(func)); 255 }, 256 function (error) { 257 assert_not_equals(error.message, "", format_value(func)); 258 // `exception` is a string name for the error. We can differentiate 259 // JS Errors from DOMExceptions by checking whether 260 // window[exception] exists. If it does, expectedError is the name 261 // of a JS Error subclass and window[exception] is the constructor 262 // for that subclass. Otherwise it's a name for a DOMException. 263 if (window[exception]) { 264 assert_throws_js(window[exception], 265 () => { throw error; }, 266 format_value(func)); 267 } else { 268 assert_throws_dom(exception, 269 () => { throw error; }, 270 format_value(func)); 271 } 272 } 273 ); 274 } 275 276 // Check that the events sequence (array of strings) matches the pattern (array of either strings, or 277 // arrays of strings, with the latter representing a possibly repeating sub-sequence) 278 function checkEventSequence(events,pattern) { 279 function th(i) { return i + (i < 4 ? ["th", "st", "nd", "rd"][i] : "th"); } 280 var i = 0, j=0, k=0; 281 while(i < events.length && j < pattern.length) { 282 if (!Array.isArray(pattern[j])) { 283 assert_equals(events[i], pattern[j], "Expected " + th(i+1) + " event to be '" + pattern[j] + "'"); 284 ++i; 285 ++j; 286 } else { 287 assert_equals(events[i], pattern[j][k], "Expected " + th(i+1) + " event to be '" + pattern[j][k] + "'"); 288 ++i; 289 k = (k+1)%pattern[j].length; 290 if (k === 0 && events[i] !== pattern[j][0]) { 291 ++j; 292 } 293 } 294 } 295 assert_equals(i,events.length,"Received more events than expected"); 296 assert_equals(j,pattern.length,"Expected more events than received"); 297 }