|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 var Cc = SpecialPowers.Cc; |
|
6 var Ci = SpecialPowers.Ci; |
|
7 var Cr = SpecialPowers.Cr; |
|
8 |
|
9 // Specifies whether we are using fake streams to run this automation |
|
10 var FAKE_ENABLED = true; |
|
11 |
|
12 |
|
13 /** |
|
14 * Create the necessary HTML elements for head and body as used by Mochitests |
|
15 * |
|
16 * @param {object} meta |
|
17 * Meta information of the test |
|
18 * @param {string} meta.title |
|
19 * Description of the test |
|
20 * @param {string} [meta.bug] |
|
21 * Bug the test was created for |
|
22 * @param {boolean} [meta.visible=false] |
|
23 * Visibility of the media elements |
|
24 */ |
|
25 function createHTML(meta) { |
|
26 var test = document.getElementById('test'); |
|
27 |
|
28 // Create the head content |
|
29 var elem = document.createElement('meta'); |
|
30 elem.setAttribute('charset', 'utf-8'); |
|
31 document.head.appendChild(elem); |
|
32 |
|
33 var title = document.createElement('title'); |
|
34 title.textContent = meta.title; |
|
35 document.head.appendChild(title); |
|
36 |
|
37 // Create the body content |
|
38 var anchor = document.createElement('a'); |
|
39 anchor.setAttribute('target', '_blank'); |
|
40 |
|
41 if (meta.bug) { |
|
42 anchor.setAttribute('href', 'https://bugzilla.mozilla.org/show_bug.cgi?id=' + meta.bug); |
|
43 } |
|
44 |
|
45 anchor.textContent = meta.title; |
|
46 document.body.insertBefore(anchor, test); |
|
47 |
|
48 var display = document.createElement('p'); |
|
49 display.setAttribute('id', 'display'); |
|
50 document.body.insertBefore(display, test); |
|
51 |
|
52 var content = document.createElement('div'); |
|
53 content.setAttribute('id', 'content'); |
|
54 content.style.display = meta.visible ? 'block' : "none"; |
|
55 document.body.appendChild(content); |
|
56 } |
|
57 |
|
58 |
|
59 /** |
|
60 * Create the HTML element if it doesn't exist yet and attach |
|
61 * it to the content node. |
|
62 * |
|
63 * @param {string} type |
|
64 * Type of media element to create ('audio' or 'video') |
|
65 * @param {string} label |
|
66 * Description to use for the element |
|
67 * @return {HTMLMediaElement} The created HTML media element |
|
68 */ |
|
69 function createMediaElement(type, label) { |
|
70 var id = label + '_' + type; |
|
71 var element = document.getElementById(id); |
|
72 |
|
73 // Sanity check that we haven't created the element already |
|
74 if (element) |
|
75 return element; |
|
76 |
|
77 element = document.createElement(type === 'audio' ? 'audio' : 'video'); |
|
78 element.setAttribute('id', id); |
|
79 element.setAttribute('height', 100); |
|
80 element.setAttribute('width', 150); |
|
81 element.setAttribute('controls', 'controls'); |
|
82 document.getElementById('content').appendChild(element); |
|
83 |
|
84 return element; |
|
85 } |
|
86 |
|
87 |
|
88 /** |
|
89 * Wrapper function for mozGetUserMedia to allow a singular area of control |
|
90 * for determining whether we run this with fake devices or not. |
|
91 * |
|
92 * @param {Dictionary} constraints |
|
93 * The constraints for this mozGetUserMedia callback |
|
94 * @param {Function} onSuccess |
|
95 * The success callback if the stream is successfully retrieved |
|
96 * @param {Function} onError |
|
97 * The error callback if the stream fails to be retrieved |
|
98 */ |
|
99 function getUserMedia(constraints, onSuccess, onError) { |
|
100 constraints["fake"] = FAKE_ENABLED; |
|
101 |
|
102 info("Call getUserMedia for " + JSON.stringify(constraints)); |
|
103 navigator.mozGetUserMedia(constraints, onSuccess, onError); |
|
104 } |
|
105 |
|
106 |
|
107 /** |
|
108 * Setup any Mochitest for WebRTC by enabling the preference for |
|
109 * peer connections. As by bug 797979 it will also enable mozGetUserMedia() |
|
110 * and disable the mozGetUserMedia() permission checking. |
|
111 * |
|
112 * @param {Function} aCallback |
|
113 * Test method to execute after initialization |
|
114 */ |
|
115 function runTest(aCallback) { |
|
116 if (window.SimpleTest) { |
|
117 // Running as a Mochitest. |
|
118 SimpleTest.waitForExplicitFinish(); |
|
119 SpecialPowers.pushPrefEnv({'set': [ |
|
120 ['dom.messageChannel.enabled', true], |
|
121 ['media.peerconnection.enabled', true], |
|
122 ['media.peerconnection.identity.enabled', true], |
|
123 ['media.peerconnection.identity.timeout', 12000], |
|
124 ['media.navigator.permission.disabled', true]] |
|
125 }, function () { |
|
126 try { |
|
127 aCallback(); |
|
128 } |
|
129 catch (err) { |
|
130 generateErrorCallback()(err); |
|
131 } |
|
132 }); |
|
133 } else { |
|
134 // Steeplechase, let it call the callback. |
|
135 window.run_test = function(is_initiator) { |
|
136 var options = {is_local: is_initiator, |
|
137 is_remote: !is_initiator}; |
|
138 aCallback(options); |
|
139 }; |
|
140 // Also load the steeplechase test code. |
|
141 var s = document.createElement("script"); |
|
142 s.src = "/test.js"; |
|
143 document.head.appendChild(s); |
|
144 } |
|
145 } |
|
146 |
|
147 /** |
|
148 * Checks that the media stream tracks have the expected amount of tracks |
|
149 * with the correct kind and id based on the type and constraints given. |
|
150 * |
|
151 * @param {Object} constraints specifies whether the stream should have |
|
152 * audio, video, or both |
|
153 * @param {String} type the type of media stream tracks being checked |
|
154 * @param {sequence<MediaStreamTrack>} mediaStreamTracks the media stream |
|
155 * tracks being checked |
|
156 */ |
|
157 function checkMediaStreamTracksByType(constraints, type, mediaStreamTracks) { |
|
158 if(constraints[type]) { |
|
159 is(mediaStreamTracks.length, 1, 'One ' + type + ' track shall be present'); |
|
160 |
|
161 if(mediaStreamTracks.length) { |
|
162 is(mediaStreamTracks[0].kind, type, 'Track kind should be ' + type); |
|
163 ok(mediaStreamTracks[0].id, 'Track id should be defined'); |
|
164 } |
|
165 } else { |
|
166 is(mediaStreamTracks.length, 0, 'No ' + type + ' tracks shall be present'); |
|
167 } |
|
168 } |
|
169 |
|
170 /** |
|
171 * Check that the given media stream contains the expected media stream |
|
172 * tracks given the associated audio & video constraints provided. |
|
173 * |
|
174 * @param {Object} constraints specifies whether the stream should have |
|
175 * audio, video, or both |
|
176 * @param {MediaStream} mediaStream the media stream being checked |
|
177 */ |
|
178 function checkMediaStreamTracks(constraints, mediaStream) { |
|
179 checkMediaStreamTracksByType(constraints, 'audio', |
|
180 mediaStream.getAudioTracks()); |
|
181 checkMediaStreamTracksByType(constraints, 'video', |
|
182 mediaStream.getVideoTracks()); |
|
183 } |
|
184 |
|
185 /** |
|
186 * Utility methods |
|
187 */ |
|
188 |
|
189 /** |
|
190 * Returns the contents of a blob as text |
|
191 * |
|
192 * @param {Blob} blob |
|
193 The blob to retrieve the contents from |
|
194 * @param {Function} onSuccess |
|
195 Callback with the blobs content as parameter |
|
196 */ |
|
197 function getBlobContent(blob, onSuccess) { |
|
198 var reader = new FileReader(); |
|
199 |
|
200 // Listen for 'onloadend' which will always be called after a success or failure |
|
201 reader.onloadend = function (event) { |
|
202 onSuccess(event.target.result); |
|
203 }; |
|
204 |
|
205 reader.readAsText(blob); |
|
206 } |
|
207 |
|
208 /** |
|
209 * Generates a callback function fired only under unexpected circumstances |
|
210 * while running the tests. The generated function kills off the test as well |
|
211 * gracefully. |
|
212 * |
|
213 * @param {String} [message] |
|
214 * An optional message to show if no object gets passed into the |
|
215 * generated callback method. |
|
216 */ |
|
217 function generateErrorCallback(message) { |
|
218 var stack = new Error().stack.split("\n"); |
|
219 stack.shift(); // Don't include this instantiation frame |
|
220 |
|
221 /** |
|
222 * @param {object} aObj |
|
223 * The object fired back from the callback |
|
224 */ |
|
225 return function (aObj) { |
|
226 if (aObj) { |
|
227 if (aObj.name && aObj.message) { |
|
228 ok(false, "Unexpected callback for '" + aObj.name + |
|
229 "' with message = '" + aObj.message + "' at " + |
|
230 JSON.stringify(stack)); |
|
231 } else { |
|
232 ok(false, "Unexpected callback with = '" + aObj + |
|
233 "' at: " + JSON.stringify(stack)); |
|
234 } |
|
235 } else { |
|
236 ok(false, "Unexpected callback with message = '" + message + |
|
237 "' at: " + JSON.stringify(stack)); |
|
238 } |
|
239 SimpleTest.finish(); |
|
240 } |
|
241 } |
|
242 |
|
243 /** |
|
244 * Generates a callback function fired only for unexpected events happening. |
|
245 * |
|
246 * @param {String} description |
|
247 Description of the object for which the event has been fired |
|
248 * @param {String} eventName |
|
249 Name of the unexpected event |
|
250 */ |
|
251 function unexpectedEventAndFinish(message, eventName) { |
|
252 var stack = new Error().stack.split("\n"); |
|
253 stack.shift(); // Don't include this instantiation frame |
|
254 |
|
255 return function () { |
|
256 ok(false, "Unexpected event '" + eventName + "' fired with message = '" + |
|
257 message + "' at: " + JSON.stringify(stack)); |
|
258 SimpleTest.finish(); |
|
259 } |
|
260 } |