michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: var Cc = SpecialPowers.Cc; michael@0: var Ci = SpecialPowers.Ci; michael@0: var Cr = SpecialPowers.Cr; michael@0: michael@0: // Specifies whether we are using fake streams to run this automation michael@0: var FAKE_ENABLED = true; michael@0: michael@0: michael@0: /** michael@0: * Create the necessary HTML elements for head and body as used by Mochitests michael@0: * michael@0: * @param {object} meta michael@0: * Meta information of the test michael@0: * @param {string} meta.title michael@0: * Description of the test michael@0: * @param {string} [meta.bug] michael@0: * Bug the test was created for michael@0: * @param {boolean} [meta.visible=false] michael@0: * Visibility of the media elements michael@0: */ michael@0: function createHTML(meta) { michael@0: var test = document.getElementById('test'); michael@0: michael@0: // Create the head content michael@0: var elem = document.createElement('meta'); michael@0: elem.setAttribute('charset', 'utf-8'); michael@0: document.head.appendChild(elem); michael@0: michael@0: var title = document.createElement('title'); michael@0: title.textContent = meta.title; michael@0: document.head.appendChild(title); michael@0: michael@0: // Create the body content michael@0: var anchor = document.createElement('a'); michael@0: anchor.setAttribute('target', '_blank'); michael@0: michael@0: if (meta.bug) { michael@0: anchor.setAttribute('href', 'https://bugzilla.mozilla.org/show_bug.cgi?id=' + meta.bug); michael@0: } michael@0: michael@0: anchor.textContent = meta.title; michael@0: document.body.insertBefore(anchor, test); michael@0: michael@0: var display = document.createElement('p'); michael@0: display.setAttribute('id', 'display'); michael@0: document.body.insertBefore(display, test); michael@0: michael@0: var content = document.createElement('div'); michael@0: content.setAttribute('id', 'content'); michael@0: content.style.display = meta.visible ? 'block' : "none"; michael@0: document.body.appendChild(content); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Create the HTML element if it doesn't exist yet and attach michael@0: * it to the content node. michael@0: * michael@0: * @param {string} type michael@0: * Type of media element to create ('audio' or 'video') michael@0: * @param {string} label michael@0: * Description to use for the element michael@0: * @return {HTMLMediaElement} The created HTML media element michael@0: */ michael@0: function createMediaElement(type, label) { michael@0: var id = label + '_' + type; michael@0: var element = document.getElementById(id); michael@0: michael@0: // Sanity check that we haven't created the element already michael@0: if (element) michael@0: return element; michael@0: michael@0: element = document.createElement(type === 'audio' ? 'audio' : 'video'); michael@0: element.setAttribute('id', id); michael@0: element.setAttribute('height', 100); michael@0: element.setAttribute('width', 150); michael@0: element.setAttribute('controls', 'controls'); michael@0: document.getElementById('content').appendChild(element); michael@0: michael@0: return element; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Wrapper function for mozGetUserMedia to allow a singular area of control michael@0: * for determining whether we run this with fake devices or not. michael@0: * michael@0: * @param {Dictionary} constraints michael@0: * The constraints for this mozGetUserMedia callback michael@0: * @param {Function} onSuccess michael@0: * The success callback if the stream is successfully retrieved michael@0: * @param {Function} onError michael@0: * The error callback if the stream fails to be retrieved michael@0: */ michael@0: function getUserMedia(constraints, onSuccess, onError) { michael@0: constraints["fake"] = FAKE_ENABLED; michael@0: michael@0: info("Call getUserMedia for " + JSON.stringify(constraints)); michael@0: navigator.mozGetUserMedia(constraints, onSuccess, onError); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Setup any Mochitest for WebRTC by enabling the preference for michael@0: * peer connections. As by bug 797979 it will also enable mozGetUserMedia() michael@0: * and disable the mozGetUserMedia() permission checking. michael@0: * michael@0: * @param {Function} aCallback michael@0: * Test method to execute after initialization michael@0: */ michael@0: function runTest(aCallback) { michael@0: if (window.SimpleTest) { michael@0: // Running as a Mochitest. michael@0: SimpleTest.waitForExplicitFinish(); michael@0: SpecialPowers.pushPrefEnv({'set': [ michael@0: ['dom.messageChannel.enabled', true], michael@0: ['media.peerconnection.enabled', true], michael@0: ['media.peerconnection.identity.enabled', true], michael@0: ['media.peerconnection.identity.timeout', 12000], michael@0: ['media.navigator.permission.disabled', true]] michael@0: }, function () { michael@0: try { michael@0: aCallback(); michael@0: } michael@0: catch (err) { michael@0: generateErrorCallback()(err); michael@0: } michael@0: }); michael@0: } else { michael@0: // Steeplechase, let it call the callback. michael@0: window.run_test = function(is_initiator) { michael@0: var options = {is_local: is_initiator, michael@0: is_remote: !is_initiator}; michael@0: aCallback(options); michael@0: }; michael@0: // Also load the steeplechase test code. michael@0: var s = document.createElement("script"); michael@0: s.src = "/test.js"; michael@0: document.head.appendChild(s); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Checks that the media stream tracks have the expected amount of tracks michael@0: * with the correct kind and id based on the type and constraints given. michael@0: * michael@0: * @param {Object} constraints specifies whether the stream should have michael@0: * audio, video, or both michael@0: * @param {String} type the type of media stream tracks being checked michael@0: * @param {sequence} mediaStreamTracks the media stream michael@0: * tracks being checked michael@0: */ michael@0: function checkMediaStreamTracksByType(constraints, type, mediaStreamTracks) { michael@0: if(constraints[type]) { michael@0: is(mediaStreamTracks.length, 1, 'One ' + type + ' track shall be present'); michael@0: michael@0: if(mediaStreamTracks.length) { michael@0: is(mediaStreamTracks[0].kind, type, 'Track kind should be ' + type); michael@0: ok(mediaStreamTracks[0].id, 'Track id should be defined'); michael@0: } michael@0: } else { michael@0: is(mediaStreamTracks.length, 0, 'No ' + type + ' tracks shall be present'); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Check that the given media stream contains the expected media stream michael@0: * tracks given the associated audio & video constraints provided. michael@0: * michael@0: * @param {Object} constraints specifies whether the stream should have michael@0: * audio, video, or both michael@0: * @param {MediaStream} mediaStream the media stream being checked michael@0: */ michael@0: function checkMediaStreamTracks(constraints, mediaStream) { michael@0: checkMediaStreamTracksByType(constraints, 'audio', michael@0: mediaStream.getAudioTracks()); michael@0: checkMediaStreamTracksByType(constraints, 'video', michael@0: mediaStream.getVideoTracks()); michael@0: } michael@0: michael@0: /** michael@0: * Utility methods michael@0: */ michael@0: michael@0: /** michael@0: * Returns the contents of a blob as text michael@0: * michael@0: * @param {Blob} blob michael@0: The blob to retrieve the contents from michael@0: * @param {Function} onSuccess michael@0: Callback with the blobs content as parameter michael@0: */ michael@0: function getBlobContent(blob, onSuccess) { michael@0: var reader = new FileReader(); michael@0: michael@0: // Listen for 'onloadend' which will always be called after a success or failure michael@0: reader.onloadend = function (event) { michael@0: onSuccess(event.target.result); michael@0: }; michael@0: michael@0: reader.readAsText(blob); michael@0: } michael@0: michael@0: /** michael@0: * Generates a callback function fired only under unexpected circumstances michael@0: * while running the tests. The generated function kills off the test as well michael@0: * gracefully. michael@0: * michael@0: * @param {String} [message] michael@0: * An optional message to show if no object gets passed into the michael@0: * generated callback method. michael@0: */ michael@0: function generateErrorCallback(message) { michael@0: var stack = new Error().stack.split("\n"); michael@0: stack.shift(); // Don't include this instantiation frame michael@0: michael@0: /** michael@0: * @param {object} aObj michael@0: * The object fired back from the callback michael@0: */ michael@0: return function (aObj) { michael@0: if (aObj) { michael@0: if (aObj.name && aObj.message) { michael@0: ok(false, "Unexpected callback for '" + aObj.name + michael@0: "' with message = '" + aObj.message + "' at " + michael@0: JSON.stringify(stack)); michael@0: } else { michael@0: ok(false, "Unexpected callback with = '" + aObj + michael@0: "' at: " + JSON.stringify(stack)); michael@0: } michael@0: } else { michael@0: ok(false, "Unexpected callback with message = '" + message + michael@0: "' at: " + JSON.stringify(stack)); michael@0: } michael@0: SimpleTest.finish(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Generates a callback function fired only for unexpected events happening. michael@0: * michael@0: * @param {String} description michael@0: Description of the object for which the event has been fired michael@0: * @param {String} eventName michael@0: Name of the unexpected event michael@0: */ michael@0: function unexpectedEventAndFinish(message, eventName) { michael@0: var stack = new Error().stack.split("\n"); michael@0: stack.shift(); // Don't include this instantiation frame michael@0: michael@0: return function () { michael@0: ok(false, "Unexpected event '" + eventName + "' fired with message = '" + michael@0: message + "' at: " + JSON.stringify(stack)); michael@0: SimpleTest.finish(); michael@0: } michael@0: }