Updating XTBs based on .GRDs from branch master
[chromium-blink-merge.git] / media / test / data / eme_player_js / utils.js
blobac67d1cff9432598e31dcd99491b7ee40236db56
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Utils provide logging functions and other JS functions commonly used by the
6 // app and media players.
7 var Utils = new function() {
8   this.titleChanged = false;
9 };
11 // Adds options to document element.
12 Utils.addOptions = function(elementID, keyValueOptions, disabledOptions) {
13   disabledOptions = disabledOptions || [];
14   var selectElement = document.getElementById(elementID);
15   var keys = Object.keys(keyValueOptions);
16   for (var i = 0; i < keys.length; i++) {
17     var key = keys[i];
18     var option = new Option(key, keyValueOptions[key]);
19     option.title = keyValueOptions[key];
20     if (disabledOptions.indexOf(key) >= 0)
21       option.disabled = true;
22     selectElement.options.add(option);
23   }
26 Utils.convertToArray = function(input) {
27   if (Array.isArray(input))
28     return input;
29   return [input];
32 Utils.convertToUint8Array = function(msg) {
33   if (typeof msg == 'string') {
34     var ans = new Uint8Array(msg.length);
35     for (var i = 0; i < msg.length; i++) {
36       ans[i] = msg.charCodeAt(i);
37     }
38     return ans;
39   }
40   // Assume it is an ArrayBuffer or ArrayBufferView. If it already is a
41   // Uint8Array, this will just make a copy of the view.
42   return new Uint8Array(msg);
45 Utils.createJWKData = function(keyId, key) {
46   // JWK routines copied from third_party/WebKit/LayoutTests/media/
47   //   encrypted-media/encrypted-media-utils.js
49   // Encodes data (Uint8Array) into base64url string. There is no '=' padding,
50   // and the characters '-' and '_' must be used instead of '+' and '/',
51   // respectively.
52   function base64urlEncode(data) {
53     var result = btoa(String.fromCharCode.apply(null, data));
54     return result.replace(/=+$/g, '').replace(/\+/g, "-").replace(/\//g, "_");
55   }
57   // Creates a JWK from raw key ID and key.
58   function createJWK(keyId, key) {
59     var jwk = '{"kty":"oct","alg":"A128KW","kid":"';
60     jwk += base64urlEncode(keyId);
61     jwk += '","k":"';
62     jwk += base64urlEncode(key);
63     jwk += '"}';
64     return jwk;
65   }
67   // Creates a JWK Set from an array of JWK(s).
68   function createJWKSet() {
69     var jwkSet = '{"keys":[';
70     for (var i = 0; i < arguments.length; i++) {
71       if (i != 0)
72         jwkSet += ',';
73       jwkSet += arguments[i];
74     }
75     jwkSet += ']}';
76     return jwkSet;
77   }
79   return Utils.convertToUint8Array(createJWKSet(createJWK(keyId, key)));
82 Utils.extractFirstLicenseKeyId = function(message) {
83   // Decodes data (Uint8Array) from base64url string.
84   function base64urlDecode(data) {
85     return atob(data.replace(/\-/g, "+").replace(/\_/g, "/"));
86   }
88   function convertToString(data) {
89     return String.fromCharCode.apply(null, Utils.convertToUint8Array(data));
90   }
92   try {
93     var json = JSON.parse(convertToString(message));
94     // Decode the first element of 'kids', return it as an Uint8Array.
95     return Utils.convertToUint8Array(base64urlDecode(json.kids[0]));
96   } catch (error) {
97     // Not valid JSON, so return message untouched as Uint8Array.
98     return Utils.convertToUint8Array(message);
99   }
102 Utils.documentLog = function(log, success, time) {
103   if (!docLogs)
104     return;
105   time = time || Utils.getCurrentTimeString();
106   var timeLog = '<span style="color: green">' + time + '</span>';
107   var logColor = !success ? 'red' : 'black'; // default is true.
108   log = '<span style="color: "' + logColor + '>' + log + '</span>';
109   docLogs.innerHTML = timeLog + ' - ' + log + '<br>' + docLogs.innerHTML;
112 Utils.ensureOptionInList = function(listID, option) {
113   var selectElement = document.getElementById(listID);
114   for (var i = 0; i < selectElement.length; i++) {
115     if (selectElement.options[i].value == option) {
116       selectElement.value = option;
117       return;
118     }
119   }
120   // The list does not have the option, let's add it and select it.
121   var optionElement = new Option(option, option);
122   optionElement.title = option;
123   selectElement.options.add(optionElement);
124   selectElement.value = option;
127 Utils.failTest = function(msg, newTitle) {
128   var failMessage = 'FAIL: ';
129   var title = 'FAILED';
130   // Handle exception messages;
131   if (msg.message) {
132     title = msg.name || 'Error';
133     failMessage += title + ' ' + msg.message;
134   } else if (msg instanceof Event) {
135     // Handle failing events.
136     failMessage = msg.target + '.' + msg.type;
137     title = msg.type;
138   } else {
139     failMessage += msg;
140   }
141   // Force newTitle if passed.
142   title = newTitle || title;
143   // Log failure.
144   Utils.documentLog(failMessage, false);
145   console.log(failMessage, msg);
146   Utils.setResultInTitle(title);
149 Utils.getCurrentTimeString = function() {
150   var date = new Date();
151   var hours = ('0' + date.getHours()).slice(-2);
152   var minutes = ('0' + date.getMinutes()).slice(-2);
153   var secs = ('0' + date.getSeconds()).slice(-2);
154   var milliSecs = ('00' + date.getMilliseconds()).slice(-3);
155   return hours + ':' + minutes + ':' + secs + '.' + milliSecs;
158 Utils.getDefaultKey = function(forceInvalidResponse) {
159   if (forceInvalidResponse) {
160     Utils.timeLog('Forcing invalid key data.');
161     return new Uint8Array([0xAA]);
162   }
163   return KEY;
166 Utils.getHexString = function(uintArray) {
167   var hex_str = '';
168   for (var i = 0; i < uintArray.length; i++) {
169     var hex = uintArray[i].toString(16);
170     if (hex.length == 1)
171       hex = '0' + hex;
172     hex_str += hex;
173   }
174   return hex_str;
177 Utils.hasPrefix = function(msg, prefix) {
178   var message = String.fromCharCode.apply(null, msg);
179   return message.substring(0, prefix.length) == prefix;
182 Utils.installTitleEventHandler = function(element, event) {
183   element.addEventListener(event, function(e) {
184     Utils.setResultInTitle(e.type);
185   }, false);
188 Utils.isRenewalMessage = function(message) {
189   if (message.messageType != 'license-renewal')
190     return false;
192   if (!Utils.isRenewalMessagePrefixed(message.message)) {
193     Utils.failTest('license-renewal message doesn\'t contain expected header',
194                    PREFIXED_EME_RENEWAL_MISSING_HEADER);
195   }
196   return true;
199 // For the prefixed API renewal messages are determined by looking at the
200 // message and finding a known string.
201 Utils.isRenewalMessagePrefixed = function(msg) {
202   return Utils.hasPrefix(Utils.convertToUint8Array(msg),
203                          PREFIXED_EME_RENEWAL_MESSAGE_HEADER);
206 Utils.resetTitleChange = function() {
207   this.titleChanged = false;
208   document.title = '';
211 Utils.sendRequest = function(requestType, responseType, message, serverURL,
212                              onSuccessCallbackFn, forceInvalidResponse) {
213   var requestAttemptCount = 0;
214   var REQUEST_RETRY_DELAY_MS = 3000;
215   var REQUEST_TIMEOUT_MS = 1000;
217   function sendRequestAttempt() {
218     // No limit on the number of retries. This will retry on failures
219     // until the test framework stops the test.
220     requestAttemptCount++;
221     var xmlhttp = new XMLHttpRequest();
222     xmlhttp.responseType = responseType;
223     xmlhttp.open(requestType, serverURL, true);
224     xmlhttp.onerror = function(e) {
225       Utils.timeLog('Request status: ' + this.statusText);
226       Utils.timeLog('FAILED: License request XHR failed with network error.');
227       Utils.timeLog('Retrying request in ' + REQUEST_RETRY_DELAY_MS + 'ms');
228       setTimeout(sendRequestAttempt, REQUEST_RETRY_DELAY_MS);
229     };
230     xmlhttp.onload = function(e) {
231       if (this.status == 200) {
232         if (onSuccessCallbackFn)
233           onSuccessCallbackFn(this.response);
234       } else {
235         Utils.timeLog('Bad response status: ' + this.status);
236         Utils.timeLog('Bad response: ' + this.response);
237         Utils.timeLog('Retrying request in ' + REQUEST_RETRY_DELAY_MS + 'ms');
238         setTimeout(sendRequestAttempt, REQUEST_RETRY_DELAY_MS);
239       }
240     };
241     xmlhttp.timeout = REQUEST_TIMEOUT_MS;
242     xmlhttp.ontimeout = function(e) {
243       Utils.timeLog('Request timeout');
244       Utils.timeLog('Retrying request in ' + REQUEST_RETRY_DELAY_MS + 'ms');
245       setTimeout(sendRequestAttempt, REQUEST_RETRY_DELAY_MS);
246     }
247     Utils.timeLog('Attempt (' + requestAttemptCount +
248                   '): sending request to server: ' + serverURL);
249     xmlhttp.send(message);
250   }
252   if (forceInvalidResponse) {
253     Utils.timeLog('Not sending request - forcing an invalid response.');
254     return onSuccessCallbackFn([0xAA]);
255   }
256   sendRequestAttempt();
259 Utils.setResultInTitle = function(title) {
260   // If document title is 'ENDED', then update it with new title to possibly
261   // mark a test as failure.  Otherwise, keep the first title change in place.
262   if (!this.titleChanged || document.title.toUpperCase() == 'ENDED')
263     document.title = title.toUpperCase();
264   Utils.timeLog('Set document title to: ' + title + ', updated title: ' +
265                 document.title);
266   this.titleChanged = true;
269 Utils.timeLog = function(/**/) {
270   if (arguments.length == 0)
271     return;
272   var time = Utils.getCurrentTimeString();
273   // Log to document.
274   Utils.documentLog(arguments[0], time);
275   // Log to JS console.
276   var logString = time + ' - ';
277   for (var i = 0; i < arguments.length; i++) {
278     logString += ' ' + arguments[i];
279   }
280   console.log(logString);