/* * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the media key. */ const CC = Components.Constructor; const TEST_HOST = "example.com"; const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/"; const USER_CONTEXTS = ["default", "personal"]; const TEST_EME_KEY = { initDataType: "keyids", initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"], "type":"persistent-license"}', kid: "LwVHf8JLtPrv2GUXFW2v_A", key: "97b9ddc459c8d5ff23c1f2754c95abe8", sessionType: "persistent-license", }; // // Support functions. // async function openTabInUserContext(uri, userContextId) { // Open the tab in the correct userContextId. let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId }); // Select tab and make sure its browser is focused. gBrowser.selectedTab = tab; tab.ownerGlobal.focus(); let browser = gBrowser.getBrowserForTab(tab); await BrowserTestUtils.browserLoaded(browser); return { tab, browser }; } function HexToBase64(hex) { var bin = ""; for (var i = 0; i < hex.length; i += 2) { bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); } return window .btoa(bin) .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); } function Base64ToHex(str) { var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/")); var res = ""; for (var i = 0; i < bin.length; i++) { res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2); } return res; } function ByteArrayToHex(array) { let bin = String.fromCharCode.apply(null, new Uint8Array(array)); let res = ""; for (let i = 0; i < bin.length; i++) { res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2); } return res; } function generateKeyObject(aKid, aKey) { let keyObj = { kty: "oct", kid: aKid, k: HexToBase64(aKey), }; return new TextEncoder().encode( JSON.stringify({ keys: [keyObj], }) ); } function generateKeyInfo(aData) { let keyInfo = { initDataType: aData.initDataType, initData: new TextEncoder().encode(aData.initData), sessionType: aData.sessionType, keyObj: generateKeyObject(aData.kid, aData.key), }; return keyInfo; } // Setup a EME key for the given browser, and return the sessionId. async function setupEMEKey(browser) { // Generate the key info. let keyInfo = generateKeyInfo(TEST_EME_KEY); // Setup the EME key. let result = await SpecialPowers.spawn( browser, [keyInfo], async function (aKeyInfo) { let access = await content.navigator.requestMediaKeySystemAccess( "org.w3.clearkey", [ { initDataTypes: [aKeyInfo.initDataType], videoCapabilities: [{ contentType: "video/webm" }], sessionTypes: ["persistent-license"], persistentState: "required", }, ] ); let mediaKeys = await access.createMediaKeys(); let session = mediaKeys.createSession(aKeyInfo.sessionType); let res = {}; // Insert the EME key. await new Promise(resolve => { session.addEventListener("message", function (event) { session .update(aKeyInfo.keyObj) .then(() => { resolve(); }) .catch(() => { ok(false, "Update the EME key fail."); resolve(); }); }); session.generateRequest(aKeyInfo.initDataType, aKeyInfo.initData); }); let map = session.keyStatuses; is(map.size, 1, "One EME key has been added."); if (map.size === 1) { res.keyId = map.keys().next().value; res.sessionId = session.sessionId; } // Close the session. session.close(); await session.closed; return res; } ); // Check the EME key ID. is( ByteArrayToHex(result.keyId), Base64ToHex(TEST_EME_KEY.kid), "The key Id is correct." ); return result.sessionId; } // Check whether the EME key has been cleared. async function checkEMEKey(browser, emeSessionId) { // Generate the key info. let keyInfo = generateKeyInfo(TEST_EME_KEY); keyInfo.sessionId = emeSessionId; await SpecialPowers.spawn(browser, [keyInfo], async function (aKeyInfo) { let access = await content.navigator.requestMediaKeySystemAccess( "org.w3.clearkey", [ { initDataTypes: [aKeyInfo.initDataType], videoCapabilities: [{ contentType: "video/webm" }], sessionTypes: ["persistent-license"], persistentState: "required", }, ] ); let mediaKeys = await access.createMediaKeys(); let session = mediaKeys.createSession(aKeyInfo.sessionType); // First, load the session with the sessionId. await session.load(aKeyInfo.sessionId); let map = session.keyStatuses; // Check that there is no media key here. is( map.size, 0, "No media key should be here after forgetThisSite() was called." ); }); } // // Test functions. // add_setup(async function () { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [ ["privacy.userContext.enabled", true], ["media.mediasource.enabled", true], ["media.mediasource.webm.enabled", true], ["media.clearkey.persistent-license.enabled", true], ], }); }); add_task(async function test_EME_forgetThisSite() { let tabs = []; let emeSessionIds = []; for (let userContextId of Object.keys(USER_CONTEXTS)) { // Open our tab in the given user context. tabs[userContextId] = await openTabInUserContext( TEST_URL + "empty_file.html", userContextId ); // Setup EME Key. emeSessionIds[userContextId] = await setupEMEKey( tabs[userContextId].browser ); // Close this tab. BrowserTestUtils.removeTab(tabs[userContextId].tab); } // Clear all EME data for a given domain with originAttributes pattern. let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].getService( Ci.mozIGeckoMediaPluginChromeService ); mps.forgetThisSite(TEST_HOST, JSON.stringify({})); // Open tabs again to check EME keys have been cleared. for (let userContextId of Object.keys(USER_CONTEXTS)) { // Open our tab in the given user context. tabs[userContextId] = await openTabInUserContext( TEST_URL + "empty_file.html", userContextId ); // Check whether EME Key has been cleared. await checkEMEKey( tabs[userContextId].browser, emeSessionIds[userContextId] ); // Close this tab. BrowserTestUtils.removeTab(tabs[userContextId].tab); } });