Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-05-18 13:52:33 +02:00
commit a2e9431125
641 changed files with 7738 additions and 3232 deletions

View File

@ -740,13 +740,9 @@ pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.OomScoreAdjust", 200);
pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.cgroup", "apps");
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderKB", 7168);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderKB", 8192);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.cgroup", "apps/bg_perceivable");
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.OomScoreAdjust", 534);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderKB", 8192);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.cgroup", "apps/bg_non_interactive");
pref("hal.processPriorityManager.gonk.BACKGROUND.OomScoreAdjust", 667);
pref("hal.processPriorityManager.gonk.BACKGROUND.KillUnderKB", 20480);
pref("hal.processPriorityManager.gonk.BACKGROUND.cgroup", "apps/bg_non_interactive");

View File

@ -439,6 +439,10 @@
@RESPATH@/components/TCPPresentationServer.js
#ifdef MOZ_SECUREELEMENT
@RESPATH@/components/ACEService.js
@RESPATH@/components/ACEService.manifest
@RESPATH@/components/GPAccessRulesManager.js
@RESPATH@/components/GPAccessRulesManager.manifest
@RESPATH@/components/SecureElement.js
@RESPATH@/components/SecureElement.manifest
@RESPATH@/components/UiccConnector.js
@ -511,6 +515,8 @@
@RESPATH@/components/RILSystemMessengerHelper.manifest
@RESPATH@/components/TelephonyAudioService.js
@RESPATH@/components/TelephonyAudioService.manifest
@RESPATH@/components/USSDReceivedWrapper.js
@RESPATH@/components/USSDReceivedWrapper.manifest
#ifndef DISABLE_MOZ_RIL_GEOLOC
@RESPATH@/components/TelephonyService.js
@RESPATH@/components/TelephonyService.manifest

View File

@ -49,19 +49,19 @@ let _referrerTests = [
rel: "noreferrer",
result: "" // rel=noreferrer trumps meta-referrer
},
// 3. Origin-when-crossorigin policy - this depends on the triggering
// 3. Origin-when-cross-origin policy - this depends on the triggering
// principal. We expect full referrer for same-origin requests,
// and origin referrer for cross-origin requests.
{
fromScheme: "https://",
toScheme: "https://",
policy: "origin-when-crossorigin",
policy: "origin-when-cross-origin",
result: "https://test1.example.com/browser" // same origin
},
{
fromScheme: "http://",
toScheme: "https://",
policy: "origin-when-crossorigin",
policy: "origin-when-cross-origin",
result: "http://test1.example.com" // cross origin
},
];

View File

@ -600,6 +600,10 @@ IsOnFullDomainWhitelist(nsIURI* aURI)
NS_LITERAL_CSTRING("mw.nikkei.com"),
NS_LITERAL_CSTRING("www.nhk.or.jp"),
NS_LITERAL_CSTRING("www.tokyo-sports.co.jp"),
NS_LITERAL_CSTRING("www.bellemaison.jp"),
NS_LITERAL_CSTRING("www.kuronekoyamato.co.jp"),
NS_LITERAL_CSTRING("s.tsite.jp"),
NS_LITERAL_CSTRING("formassist.jp"), // for orico.jp
};
static const size_t sNumFullDomainsOnWhitelist =
MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist);

View File

@ -69,7 +69,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'../shistory/src',
'/docshell/shistory',
'/dom/base',
'/layout/base',
'/layout/generic',

View File

@ -13,12 +13,12 @@ SOURCES += [
]
LOCAL_INCLUDES += [
'../base',
'../shistory/src/',
'/docshell/base',
'/docshell/shistory',
'/uriloader/base',
'/uriloader/exthandler',
'/uriloader/prefetch',
]
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
LOCAL_INCLUDES += ['/uriloader/exthandler/mac']

View File

@ -4,5 +4,33 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['public', 'src']
XPIDL_SOURCES += [
'nsIBFCacheEntry.idl',
'nsISHContainer.idl',
'nsISHEntry.idl',
'nsISHistory.idl',
'nsISHistoryInternal.idl',
'nsISHistoryListener.idl',
'nsISHTransaction.idl',
]
XPIDL_MODULE = 'shistory'
EXPORTS += [
'nsSHEntryShared.h',
]
UNIFIED_SOURCES += [
'nsSHEntry.cpp',
'nsSHEntryShared.cpp',
'nsSHistory.cpp',
'nsSHTransaction.cpp',
]
FAIL_ON_WARNINGS = True
LOCAL_INCLUDES += [
'/docshell/base',
]
FINAL_LIBRARY = 'xul'

View File

@ -1,18 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIBFCacheEntry.idl',
'nsISHContainer.idl',
'nsISHEntry.idl',
'nsISHistory.idl',
'nsISHistoryInternal.idl',
'nsISHistoryListener.idl',
'nsISHTransaction.idl',
]
XPIDL_MODULE = 'shistory'

View File

@ -1,24 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'nsSHEntryShared.h',
]
UNIFIED_SOURCES += [
'nsSHEntry.cpp',
'nsSHEntryShared.cpp',
'nsSHistory.cpp',
'nsSHTransaction.cpp',
]
FAIL_ON_WARNINGS = True
LOCAL_INCLUDES += [
'/docshell/base',
]
FINAL_LIBRARY = 'xul'

View File

@ -423,7 +423,7 @@ LOCAL_INCLUDES += [
'/dom/xslt/xpath',
'/dom/xul',
'/gfx/2d',
'/image/src',
'/image',
'/js/xpconnect/src',
'/js/xpconnect/wrappers',
'/layout/base',

View File

@ -44,7 +44,7 @@ var testData = {
'crossorigin': 'origin',
'downgrade': 'origin' }},
'origin-when-crossorigin': { 'csp': "script-src * 'unsafe-inline'; referrer origin-when-crossorigin",
'origin-when-cross-origin': { 'csp': "script-src * 'unsafe-inline'; referrer origin-when-cross-origin",
'expected': { 'sameorigin': 'full',
'crossorigin': 'origin',
'downgrade': 'origin' }},

View File

@ -242,6 +242,7 @@ support-files =
w3element_traversal.svg
wholeTexty-helper.xml
file_nonascii_blob_url.html
referrerHelper.js
[test_anonymousContent_api.html]
[test_anonymousContent_append_after_reflow.html]
@ -613,6 +614,7 @@ skip-if = buildapp == 'b2g'
[test_bug704320.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g (Needs multiple window.open support) android(times out, bug 1100609) e10s(randomly fails, bug 1100362)
[test_bug704320_policyset.html]
support-files = referrerHelper.js
[test_bug704320_preload.html]
[test_bug707142.html]
[test_bug708620.html]
@ -665,6 +667,8 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
[test_bug1075702.html]
[test_bug1101364.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
[test_bug1163743.html]
support-files = referrerHelper.js
[test_caretPositionFromPoint.html]
[test_classList.html]
# This test fails on the Mac for some reason

View File

@ -0,0 +1,76 @@
/**
* Listen for notifications from the child.
* These are sent in case of error, or when the loads we await have completed.
*/
window.addEventListener("message", function(event) {
if (event.data == "childLoadComplete") {
// all loads happen, continue the test.
advance();
} else if (event.data == "childOverload") {
// too many loads happened in a test frame, abort.
ok(false, "Too many load handlers called in test.");
SimpleTest.finish();
} else if (event.data.indexOf("fail-") == 0) {
// something else failed in the test frame, abort.
ok(false, "Child failed the test with error " + event.data.substr(5));
SimpleTest.finish();
}});
/**
* helper to perform an XHR.
* Used by resetCounter() and checkResults().
*/
function doXHR(url, onSuccess, onFail) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status == 200) {
onSuccess(xhr);
} else {
onFail(xhr);
}
};
xhr.open('GET', url, true);
xhr.send(null);
}
/**
* This triggers state-resetting on the counter server.
*/
function resetCounter() {
doXHR('/tests/dom/base/test/bug704320_counter.sjs?reset',
advance,
function(xhr) {
ok(false, "Need to be able to reset the request counter");
SimpleTest.finish();
});
}
/**
* Grabs the results via XHR and passes to checker.
*/
function checkResults(testname, expected) {
doXHR('/tests/dom/base/test/bug704320_counter.sjs?results',
function(xhr) {
var results = JSON.parse(xhr.responseText);
info(xhr.responseText);
ok('img' in results,
testname + " test: some image loads required in results object.");
is(results['img'].count, 2,
testname + " Test: Expected 2 loads for image requests.");
expected.forEach(function (ref) {
ok(results['img'].referrers.indexOf(ref) >= 0,
testname + " Test: Expected " + ref + " referrer policy in test, results were " +
JSON.stringify(results['img'].referrers) +".");
});
advance();
},
function(xhr) {
ok(false, "Can't get results from the counter server.");
SimpleTest.finish();
});
}

View File

@ -5,7 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1091883
-->
<head>
<meta charset="utf-8">
<meta name="referrer" content="origin-when-crossorigin">
<meta name="referrer" content="origin-when-cross-origin">
<title>Test for Bug 1091883</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
@ -26,7 +26,7 @@ var numOrigins = origins.length;
// includes a "frame" that includes a "subframe"; and then this test
// navigates this "subframe" to the "target". Both the referrer and
// the triggering principal are this test, i.e., "http://mochi.test:8888".
// Since the referrer policy is origin-when-crossorigin, we expect to have
// Since the referrer policy is origin-when-cross-origin, we expect to have
// a full referrer if and only if the target is also "http://mochi.test:8888";
// in all other cases, the referrer needs to be the origin alone.
var numTests = numOrigins * numOrigins * numOrigins;

View File

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<!--
This checks if the origin-when-crossorigin policy works.
https://bugzilla.mozilla.org/show_bug.cgi?id=1163743
-->
<head>
<meta charset="utf-8">
<title>Test policies for Bug 1163743</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="referrerHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
var advance = function() { tests.next(); };
/**
* testing legacy support for origin-when-crossorigin (1163743)
*/
var tests = (function() {
var iframe = document.getElementById("testframe");
const sjs = "/tests/dom/base/test/bug704320.sjs?action=generate-policy-test";
// origin when crossorigin (trimming whitespace)
yield resetCounter();
yield iframe.src = sjs + "&policy=" + escape(' origin-when-crossorigin');
yield checkResults("origin-when-cross-origin", ["origin", "full"]);
// complete. Be sure to yield so we don't call this twice.
yield SimpleTest.finish();
})();
</script>
</head>
<body onload="tests.next();">
<iframe id="testframe"></iframe>
</body>
</html>

View File

@ -26,25 +26,25 @@ var testIframeUrls = [
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=no-referrer',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=unsafe-url',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=origin',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-crossorigin',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-cross-origin',
// HTTP to HTTPS
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=no-referrer-when-downgrade',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=no-referrer',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=unsafe-url',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin-when-crossorigin',
'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin-when-cross-origin',
// HTTPS to HTTP
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=no-referrer-when-downgrade',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=no-referrer',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=unsafe-url',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=origin',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=origin-when-crossorigin',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=origin-when-cross-origin',
// HTTPS to HTTPS
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=no-referrer-when-downgrade',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=no-referrer',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=unsafe-url',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=origin',
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-crossorigin'
'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-cross-origin'
];
var expectedResults = {
@ -58,14 +58,14 @@ var expectedResults = {
'no-referrer': '',
'unsafe-url': '',
'origin': '',
'origin-when-crossorigin': '',
'origin-when-cross-origin': '',
'no-referrer-when-downgrade': ''
},
'http-to-https': {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=unsafe-url',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com',
'origin-when-cross-origin': 'http://example.com',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=no-referrer-when-downgrade'
},
// Encrypted and not same-origin
@ -73,7 +73,7 @@ var expectedResults = {
'no-referrer': '',
'unsafe-url': '',
'origin': '',
'origin-when-crossorigin': '',
'origin-when-cross-origin': '',
'no-referrer-when-downgrade': ''
},
// Encrypted
@ -81,7 +81,7 @@ var expectedResults = {
'no-referrer': '',
'unsafe-url': '',
'origin': '',
'origin-when-crossorigin': '',
'origin-when-cross-origin': '',
'no-referrer-when-downgrade': ''
}
},
@ -91,28 +91,28 @@ var expectedResults = {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=unsafe-url&type=form',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-crossorigin&type=form',
'origin-when-cross-origin': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-cross-origin&type=form',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=no-referrer-when-downgrade&type=form'
},
'http-to-https': {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=unsafe-url&type=form',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com',
'origin-when-cross-origin': 'http://example.com',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=no-referrer-when-downgrade&type=form'
},
'https-to-http': {
'no-referrer': '',
'unsafe-url': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=http&policy=unsafe-url&type=form',
'origin': 'https://example.com',
'origin-when-crossorigin': 'https://example.com',
'origin-when-cross-origin': 'https://example.com',
'no-referrer-when-downgrade': ''
},
'https-to-https': {
'no-referrer': '',
'unsafe-url': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=unsafe-url&type=form',
'origin': 'https://example.com',
'origin-when-crossorigin': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-crossorigin&type=form',
'origin-when-cross-origin': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-cross-origin&type=form',
'no-referrer-when-downgrade': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=no-referrer-when-downgrade&type=form'
}
},
@ -122,28 +122,28 @@ var expectedResults = {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=unsafe-url&type=window.location',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-crossorigin&type=window.location',
'origin-when-cross-origin': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-cross-origin&type=window.location',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=http&policy=no-referrer-when-downgrade&type=window.location'
},
'http-to-https': {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=unsafe-url&type=window.location',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com',
'origin-when-cross-origin': 'http://example.com',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=no-referrer-when-downgrade&type=window.location'
},
'https-to-http': {
'no-referrer': '',
'unsafe-url': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=http&policy=unsafe-url&type=window.location',
'origin': 'https://example.com',
'origin-when-crossorigin': 'https://example.com',
'origin-when-cross-origin': 'https://example.com',
'no-referrer-when-downgrade': ''
},
'https-to-https': {
'no-referrer': '',
'unsafe-url': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=unsafe-url&type=window.location',
'origin': 'https://example.com',
'origin-when-crossorigin': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-crossorigin&type=window.location',
'origin-when-cross-origin': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-cross-origin&type=window.location',
'no-referrer-when-downgrade': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-2nd-level-iframe&scheme-from=https&scheme-to=https&policy=no-referrer-when-downgrade&type=window.location'
}
},
@ -152,28 +152,28 @@ var expectedResults = {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=unsafe-url',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-crossorigin',
'origin-when-cross-origin': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=origin-when-cross-origin',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=http&policy=no-referrer-when-downgrade'
},
'http-to-https': {
'no-referrer': '',
'unsafe-url': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=unsafe-url',
'origin': 'http://example.com',
'origin-when-crossorigin': 'http://example.com',
'origin-when-cross-origin': 'http://example.com',
'no-referrer-when-downgrade': 'http://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=no-referrer-when-downgrade'
},
'https-to-http': {
'no-referrer': '',
'unsafe-url': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=http&policy=unsafe-url',
'origin': 'https://example.com',
'origin-when-crossorigin': 'https://example.com',
'origin-when-cross-origin': 'https://example.com',
'no-referrer-when-downgrade': ''
},
'https-to-https': {
'no-referrer': '',
'unsafe-url': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=unsafe-url',
'origin': 'https://example.com',
'origin-when-crossorigin': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-crossorigin',
'origin-when-cross-origin': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=origin-when-cross-origin',
'no-referrer-when-downgrade': 'https://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=https&scheme-to=https&policy=no-referrer-when-downgrade'
}
}

View File

@ -9,6 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=704320
<meta charset="utf-8">
<title>Test policies for Bug 704320</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="referrerHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript;version=1.7">
@ -16,24 +17,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=704320
SimpleTest.waitForExplicitFinish();
var advance = function() { tests.next(); };
/**
* Listen for notifications from the child.
* These are sent in case of error, or when the loads we await have completed.
*/
window.addEventListener("message", function(event) {
if (event.data == "childLoadComplete") {
// all loads happen, continue the test.
advance();
} else if (event.data == "childOverload") {
// too many loads happened in a test frame, abort.
ok(false, "Too many load handlers called in test.");
SimpleTest.finish();
} else if (event.data.indexOf("fail-") == 0) {
// something else failed in the test frame, abort.
ok(false, "Child failed the test with error " + event.data.substr(5));
SimpleTest.finish();
}});
/**
* This is the main test routine -- serialized by use of a generator.
* It resets the counter, then performs two tests in sequence using
@ -82,8 +65,8 @@ var tests = (function() {
// origin when cross-origin (trimming whitespace)
yield resetCounter();
yield iframe.src = sjs + "&policy=" + escape(' origin-when-crossorigin');
yield checkResults("origin-when-crossorigin", ["origin", "full"]);
yield iframe.src = sjs + "&policy=" + escape(' origin-when-cross-origin');
yield checkResults("origin-when-cross-origin", ["origin", "full"]);
// according to the spec section 4.1:
// "If the meta element lacks a content attribute, or if that attributes
@ -109,65 +92,6 @@ var tests = (function() {
yield SimpleTest.finish();
})();
// Helper functions below.
/**
* helper to perform an XHR.
* Used by resetCounter() and checkResults().
*/
function doXHR(url, onSuccess, onFail) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status == 200) {
onSuccess(xhr);
} else {
onFail(xhr);
}
};
xhr.open('GET', url, true);
xhr.send(null);
}
/**
* This triggers state-resetting on the counter server.
*/
function resetCounter() {
doXHR('/tests/dom/base/test/bug704320_counter.sjs?reset',
advance,
function(xhr) {
ok(false, "Need to be able to reset the request counter");
SimpleTest.finish();
});
}
/**
* Grabs the results via XHR and passes to checker.
*/
function checkResults(testname, expected) {
doXHR('/tests/dom/base/test/bug704320_counter.sjs?results',
function(xhr) {
var results = JSON.parse(xhr.responseText);
info(xhr.responseText);
ok('img' in results,
testname + " test: some image loads required in results object.");
is(results['img'].count, 2,
testname + " Test: Expected 2 loads for image requests.");
expected.forEach(function (ref) {
ok(results['img'].referrers.indexOf(ref) >= 0,
testname + " Test: Expected " + ref + " referrer policy in test, results were " +
JSON.stringify(results['img'].referrers) +".");
});
advance();
},
function(xhr) {
ok(false, "Can't get results from the counter server.");
SimpleTest.finish();
});
}
</script>
</head>

View File

@ -2286,6 +2286,11 @@ BluetoothServiceBluedroid::AdapterStateChangedNotification(bool aState)
BluetoothA2dpManager::DeinitA2dpInterface
};
// Set discoverable cache to default value after state becomes BT_STATE_OFF.
if (sAdapterDiscoverable) {
sAdapterDiscoverable = false;
}
// Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
nsRefPtr<ProfileDeinitResultHandler> res =
new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
@ -2369,11 +2374,16 @@ BluetoothServiceBluedroid::AdapterPropertiesNotification(
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", sAdapterBdName);
} else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
sAdapterDiscoverable =
(p.mScanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
BT_APPEND_NAMED_VALUE(propertiesArray, "Discoverable",
sAdapterDiscoverable);
// If BT is not enabled, Bluetooth scan mode should be non-discoverable
// by defalut. 'AdapterStateChangedNotification' would set the default
// properties to bluetooth backend once Bluetooth is enabled.
if (IsEnabled()) {
sAdapterDiscoverable =
(p.mScanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
BT_APPEND_NAMED_VALUE(propertiesArray, "Discoverable",
sAdapterDiscoverable);
}
} else if (p.mType == PROPERTY_ADAPTER_BONDED_DEVICES) {
// We have to cache addresses of bonded devices. Unlike BlueZ,
// Bluedroid would not send another PROPERTY_ADAPTER_BONDED_DEVICES
@ -2429,7 +2439,10 @@ BluetoothServiceBluedroid::AdapterPropertiesNotification(
} else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
BluetoothScanMode newMode = p.mScanMode;
if (newMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
// If BT is not enabled, Bluetooth scan mode should be non-discoverable
// by defalut. 'AdapterStateChangedNotification' would set the default
// properties to bluetooth backend once Bluetooth is enabled.
if (newMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE && IsEnabled()) {
propertyValue = sAdapterDiscoverable = true;
} else {
propertyValue = sAdapterDiscoverable = false;
@ -2477,7 +2490,6 @@ BluetoothServiceBluedroid::AdapterPropertiesNotification(
BluetoothValue(props)));
// Send reply for SetProperty
if (!sSetPropertyRunnableArray.IsEmpty()) {
DispatchBluetoothReply(sSetPropertyRunnableArray[0],
BluetoothValue(true), EmptyString());

View File

@ -53,8 +53,7 @@ const browserElementTestHelpers = {
['dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2],
['dom.ipc.processPriorityManager.FOREGROUND.LRUPoolLevels', 2],
['dom.ipc.processPriorityManager.testMode', true],
['dom.ipc.processPriorityManager.enabled', true],
['dom.ipc.processCount', 3]
['dom.ipc.processPriorityManager.enabled', true]
);
},
@ -229,43 +228,6 @@ function expectPriorityWithLRUSet(childID, expectedPriority, expectedLRU) {
});
}
// Returns a promise which is resolved or rejected the next time the process
// childID delays its priority change. We resolve if the priority matches
// expectedPriority, and we reject otherwise.
function expectPriorityDelay(childID, expectedPriority) {
return new Promise(function(resolve, reject) {
var observed = false;
browserElementTestHelpers.addProcessPriorityObserver(
'process-priority-delayed',
function(subject, topic, data) {
if (observed) {
return;
}
var [id, priority] = data.split(":");
if (id != childID) {
return;
}
// Make sure we run the is() calls in this observer only once, otherwise
// we'll expect /every/ priority change to match expectedPriority.
observed = true;
is(priority, expectedPriority,
'Expected delayed priority change of childID ' + childID +
' to ' + expectedPriority);
if (priority == expectedPriority) {
resolve();
} else {
reject();
}
}
);
});
}
// Returns a promise which is resolved the first time the given iframe fires
// the mozbrowser##eventName event.
function expectMozbrowserEvent(iframe, eventName) {

View File

@ -27,4 +27,3 @@ support-files = file_NestedFramesOuter.html
[test_WebGLContextLost.html]
disabled = bug 865844
support-files = file_WebGLContextLost.html
[test_DelayedBackgroundTransition.html]

View File

@ -18,17 +18,6 @@ browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -62,7 +51,7 @@ function runTest() {
// service. This is controled by the media.useAudioChannelService pref.
addEventListener('testready', function() {
SpecialPowers.pushPrefEnv({set: [['media.useAudioChannelService', true]]},
setupTest);
runTest);
});
</script>

View File

@ -19,17 +19,6 @@ browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -58,7 +47,7 @@ function runTest() {
document.body.appendChild(iframe);
}
addEventListener('testready', setupTest);
addEventListener('testready', runTest);
</script>
</body>

View File

@ -20,17 +20,6 @@ browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
SpecialPowers.addPermission("embed-apps", true, document);
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe1 = document.createElement('iframe');
iframe1.setAttribute('mozbrowser', true);
@ -79,7 +68,7 @@ function runTest() {
document.body.appendChild(iframe1);
}
addEventListener('testready', setupTest);
addEventListener('testready', runTest);
</script>
</body>

View File

@ -1,61 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test that a process won't transit to BACKGROUND priority unless
there is at least one FOREGROUND process.
-->
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
function insertForegroundFrame() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.setAttribute('id', 'foreground');
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend');
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
iframe.src = browserElementTestHelpers.emptyPage;
var childID = null;
Promise.all([
expectProcessCreated('FOREGROUND').then(function(chid) {
childID = chid;
}),
expectMozbrowserEvent(iframe, 'loadend')
]).then(function() {
var p = expectPriorityDelay(childID, 'BACKGROUND');
iframe.setVisible(false);
return p;
}).then(function() {
var p = expectPriorityChange(childID, 'BACKGROUND');
insertForegroundFrame();
return p;
}).then(function() {
var p = expectPriorityChange(childID, 'FOREGROUND');
iframe.setVisible(true);
return p;
}).then(SimpleTest.finish);
document.body.appendChild(iframe);
}
addEventListener('testready', runTest);
</script>
</body>
</html>

View File

@ -33,17 +33,6 @@ addEventListener('unload', function() {
isInBrowserElement: true });
});
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -73,7 +62,7 @@ addEventListener('testready', function() {
// set the timeout to a large value.
SpecialPowers.pushPrefEnv(
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 99999]]},
setupTest);
runTest);
});
</script>

View File

@ -19,17 +19,6 @@ browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -59,7 +48,7 @@ function runTest() {
document.body.appendChild(iframe);
}
addEventListener('testready', setupTest);
addEventListener('testready', runTest);
</script>
</body>

View File

@ -19,17 +19,6 @@ browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -64,7 +53,7 @@ function runTest() {
document.body.appendChild(iframe);
}
addEventListener('testready', setupTest);
addEventListener('testready', runTest);
</script>
</body>
</html>

View File

@ -32,17 +32,6 @@ addEventListener('unload', function() {
isInBrowserElement: true });
});
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
// Set up the following hierarchy of frames:
//
@ -80,7 +69,7 @@ function runTest() {
document.body.appendChild(iframe);
}
addEventListener('testready', setupTest);
addEventListener('testready', runTest);
</script>
</body>

View File

@ -18,17 +18,6 @@ browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -57,7 +46,7 @@ function runTest() {
document.body.appendChild(iframe);
}
addEventListener('testready', setupTest);
addEventListener('testready', runTest);
</script>
</body>
</html>

View File

@ -19,17 +19,6 @@ browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
// ProcessPriorityManager requires at least one process in foreground
// so that other processes can transit freely between foreground and
// background.
function setupTest() {
var foreground = document.createElement('iframe');
foreground.setAttribute('mozbrowser', true);
foreground.src = browserElementTestHelpers.emptyPage;
expectMozbrowserEvent(foreground, 'loadend').then(runTest);
document.body.appendChild(foreground);
}
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
@ -102,7 +91,7 @@ addEventListener('testready', function() {
// shouldn't hurt things, and anyway this setting mirrors what we do on B2G,
// which is what we're trying to test!
SpecialPowers.pushPrefEnv({set: [["webgl.force-enabled", true]]},
setupTest);
runTest);
});
</script>

View File

@ -110,7 +110,7 @@ public:
InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
const nsACString& aOrigin, UsageInfo* aUsageInfo) override
{
return NS_OK;
return GetUsageForOrigin(aPersistenceType, aGroup, aOrigin, aUsageInfo);
}
virtual nsresult

View File

@ -310,6 +310,8 @@ public:
bool IsPremultAlpha() const { return mOptions.premultipliedAlpha; }
bool IsPreservingDrawingBuffer() const { return mOptions.preserveDrawingBuffer; }
bool PresentScreenBuffer();
// a number that increments every time we have an event that causes

View File

@ -142,7 +142,7 @@ LOCAL_INCLUDES += [
'/dom/svg',
'/dom/xul',
'/gfx/gl',
'/image/src',
'/image',
'/js/xpconnect/src',
'/layout/generic',
'/layout/style',

View File

@ -0,0 +1,174 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/*
* Util base class to help test a captured canvas element. Initializes the
* output canvas (used for testing the color of video elements), and optionally
* overrides the default element |width| and |height|.
*/
function CaptureStreamTestHelper(width, height) {
this.cout = document.createElement('canvas');
if (width) {
this.elemWidth = width;
}
if (height) {
this.elemHeight = height;
}
this.cout.width = this.elemWidth;
this.cout.height = this.elemHeight;
document.body.appendChild(this.cout);
}
CaptureStreamTestHelper.prototype = {
/* Predefined colors for use in the methods below. */
black: { data: [0, 0, 0, 255], name: "black" },
green: { data: [0, 255, 0, 255], name: "green" },
red: { data: [255, 0, 0, 255], name: "red" },
/* Default element size for createAndAppendElement() */
elemWidth: 100,
elemHeight: 100,
/* Request a frame from the stream played by |video|. */
requestFrame: function (video) {
info("Requesting frame from " + video.id);
video.mozSrcObject.requestFrame();
},
/* Tests the top left pixel of |video| against |refData|. Format [R,G,B,A]. */
testPixel: function (video, refData, threshold) {
var ctxout = this.cout.getContext('2d');
ctxout.drawImage(video, 0, 0);
var pixel = ctxout.getImageData(0, 0, 1, 1).data;
return pixel.every((val, i) => Math.abs(val - refData[i]) <= threshold);
},
/*
* Returns a promise that resolves when the pixel matches. Use |threshold|
* for fuzzy matching the color on each channel, in the range [0,255].
*/
waitForPixel: function (video, refColor, threshold, infoString) {
return new Promise(resolve => {
info("Testing " + video.id + " against [" + refColor.data.join(',') + "]");
CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout);
video.ontimeupdate = () => {
if (this.testPixel(video, refColor.data, threshold)) {
ok(true, video.id + " " + infoString);
video.ontimeupdate = null;
resolve();
}
};
});
},
/*
* Returns a promise that resolves after |timeout| ms of playback or when a
* pixel of |video| becomes the color |refData|. The test is failed if the
* timeout is not reached.
*/
waitForPixelToTimeout: function (video, refColor, threshold, timeout, infoString) {
return new Promise(resolve => {
info("Waiting for " + video.id + " to time out after " + timeout +
"ms against [" + refColor.data.join(',') + "] - " + refColor.name);
CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout);
var startTime = video.currentTime;
video.ontimeupdate = () => {
if (this.testPixel(video, refColor.data, threshold)) {
ok(false, video.id + " " + infoString);
video.ontimeupdate = null;
resolve();
} else if (video.currentTime > startTime + (timeout / 1000.0)) {
ok(true, video.id + " " + infoString);
video.ontimeupdate = null;
resolve();
}
};
});
},
/* Create an element of type |type| with id |id| and append it to the body. */
createAndAppendElement: function (type, id) {
var e = document.createElement(type);
e.id = id;
e.width = this.elemWidth;
e.height = this.elemHeight;
if (type === 'video') {
e.autoplay = true;
}
document.body.appendChild(e);
return e;
},
}
/* Sub class holding 2D-Canvas specific helpers. */
function CaptureStreamTestHelper2D(width, height) {
CaptureStreamTestHelper.call(this, width, height);
}
CaptureStreamTestHelper2D.prototype = Object.create(CaptureStreamTestHelper.prototype);
CaptureStreamTestHelper2D.prototype.constructor = CaptureStreamTestHelper2D;
/* Clear all drawn content on |canvas|. */
CaptureStreamTestHelper2D.prototype.clear = function(canvas) {
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
};
/* Draw the color |color| to the source canvas |canvas|. Format [R,G,B,A]. */
CaptureStreamTestHelper2D.prototype.drawColor = function(canvas, color) {
var ctx = canvas.getContext('2d');
var rgba = color.data.slice(); // Copy to not overwrite the original array
info("Drawing color " + rgba.join(','));
rgba[3] = rgba[3] / 255.0; // Convert opacity to double in range [0,1]
ctx.fillStyle = "rgba(" + rgba.join(',') + ")";
// Only fill top left corner to test that output is not flipped or rotated.
ctx.fillRect(0, 0, canvas.width / 2, canvas.height / 2);
};
/* Test that the given 2d canvas is NOT origin-clean. */
CaptureStreamTestHelper2D.prototype.testNotClean = function(canvas) {
var ctx = canvas.getContext('2d');
var error = "OK";
try {
var data = ctx.getImageData(0, 0, 1, 1);
} catch(e) {
error = e.name;
}
is(error, "SecurityError",
"Canvas '" + canvas.id + "' should not be origin-clean");
};
/* Sub class holding WebGL specific helpers. */
function CaptureStreamTestHelperWebGL(width, height) {
CaptureStreamTestHelper.call(this, width, height);
}
CaptureStreamTestHelperWebGL.prototype = Object.create(CaptureStreamTestHelper.prototype);
CaptureStreamTestHelperWebGL.prototype.constructor = CaptureStreamTestHelperWebGL;
/* Set the (uniform) color location for future draw calls. */
CaptureStreamTestHelperWebGL.prototype.setFragmentColorLocation = function(colorLocation) {
this.colorLocation = colorLocation;
};
/* Clear the given WebGL context with |color|. */
CaptureStreamTestHelperWebGL.prototype.clearColor = function(canvas, color) {
info("WebGL: clearColor(" + color.name + ")");
var gl = canvas.getContext('webgl');
var conv = color.data.map(i => i / 255.0);
gl.clearColor(conv[0], conv[1], conv[2], conv[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
};
/* Set an already setFragmentColorLocation() to |color| and drawArrays() */
CaptureStreamTestHelperWebGL.prototype.drawColor = function(canvas, color) {
info("WebGL: drawArrays(" + color.name + ")");
var gl = canvas.getContext('webgl');
var conv = color.data.map(i => i / 255.0);
gl.uniform4f(this.colorLocation, conv[0], conv[1], conv[2], conv[3]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
};

View File

@ -73,6 +73,17 @@ function testImage(url, crossOriginAttribute, expected_error) {
"drawImage then get image data on " + url +
" with crossOrigin=" + this.crossOrigin);
try {
c.captureStream(0);
actual_error = OK;
} catch (e) {
actual_error = e.name;
}
verifyError(actual_error, expected_error,
"drawImage then capture stream on " + url +
" with crossOrigin=" + this.crossOrigin);
// Now test patterns
c = document.createElement("canvas");
c.width = this.width;
@ -91,6 +102,17 @@ function testImage(url, crossOriginAttribute, expected_error) {
"createPattern+fill then get image data on " + url +
" with crossOrigin=" + this.crossOrigin);
try {
c.captureStream(0);
actual_error = OK;
} catch (e) {
actual_error = e.name;
}
verifyError(actual_error, expected_error,
"createPattern+fill then capture stream on " + url +
" with crossOrigin=" + this.crossOrigin);
testDone();
};
@ -130,56 +152,64 @@ const attrValues = [
[ "foobar", "anonymous" ]
];
for (var imgIdx = 0; imgIdx < imageFiles.length; ++imgIdx) {
for (var hostnameIdx = 0; hostnameIdx < hostnames.length; ++hostnameIdx) {
var hostnameData = hostnames[hostnameIdx];
var url = "http://" + hostnameData[0] + testPath + imageFiles[imgIdx][0];
for (var attrValIdx = 0; attrValIdx < attrValues.length; ++attrValIdx) {
var attrValData = attrValues[attrValIdx];
// Now compute the expected result
var expected_error;
if (hostnameData[1] == "same-origin") {
// Same-origin; these should all Just Work
expected_error = OK;
} else {
// Cross-origin
is(hostnameData[1], "cross-origin",
"what sort of host is " + hostnameData[0]);
var CORSMode = attrValData[1];
if (CORSMode == "none") {
// Doesn't matter what headers the server sends; we're not
// using CORS on our end.
expected_error = "SecurityError";
function beginTest() {
for (var imgIdx = 0; imgIdx < imageFiles.length; ++imgIdx) {
for (var hostnameIdx = 0; hostnameIdx < hostnames.length; ++hostnameIdx) {
var hostnameData = hostnames[hostnameIdx];
var url = "http://" + hostnameData[0] + testPath + imageFiles[imgIdx][0];
for (var attrValIdx = 0; attrValIdx < attrValues.length; ++attrValIdx) {
var attrValData = attrValues[attrValIdx];
// Now compute the expected result
var expected_error;
if (hostnameData[1] == "same-origin") {
// Same-origin; these should all Just Work
expected_error = OK;
} else {
// Check whether the server will let us talk to them
var CORSHeaders = imageFiles[imgIdx][1];
// We're going to look for CORS headers from the server
if (CORSHeaders == "none") {
// No CORS headers from server; load will fail.
expected_error = BAD_URI_ERR;
} else if (CORSHeaders == "allow-all-anon") {
// Server only allows anonymous requests
if (CORSMode == "anonymous") {
expected_error = OK;
} else {
is(CORSMode, "use-credentials",
"What other CORS modes are there?");
// A load with credentials against a server that only
// allows anonymous loads will fail.
expected_error = BAD_URI_ERR;
}
// Cross-origin
is(hostnameData[1], "cross-origin",
"what sort of host is " + hostnameData[0]);
var CORSMode = attrValData[1];
if (CORSMode == "none") {
// Doesn't matter what headers the server sends; we're not
// using CORS on our end.
expected_error = "SecurityError";
} else {
is(CORSHeaders, "allow-single-server-creds",
"What other CORS headers could there be?");
// Our server should allow both anonymous and non-anonymous requests
expected_error = OK;
// Check whether the server will let us talk to them
var CORSHeaders = imageFiles[imgIdx][1];
// We're going to look for CORS headers from the server
if (CORSHeaders == "none") {
// No CORS headers from server; load will fail.
expected_error = BAD_URI_ERR;
} else if (CORSHeaders == "allow-all-anon") {
// Server only allows anonymous requests
if (CORSMode == "anonymous") {
expected_error = OK;
} else {
is(CORSMode, "use-credentials",
"What other CORS modes are there?");
// A load with credentials against a server that only
// allows anonymous loads will fail.
expected_error = BAD_URI_ERR;
}
} else {
is(CORSHeaders, "allow-single-server-creds",
"What other CORS headers could there be?");
// Our server should allow both anonymous and non-anonymous requests
expected_error = OK;
}
}
}
testImage(url, attrValData[0], expected_error);
}
testImage(url, attrValData[0], expected_error);
}
}
}
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
</script>
</pre>
</body>

View File

@ -28,11 +28,7 @@ function createCanvas(width, height) {
return c;
}
function testCanvasDrawImage(v) {
var c = createCanvas(v.width, v.height);
var ctx = c.getContext("2d");
ctx.drawImage(v, 0, 0);
function checkGetImageData(ctx, v) {
try {
var data = ctx.getImageData(0, 0, 1, 1);
ok(true, "drawImage '" + v.src + "' then getImageData with crossOrigin='" + v.crossOrigin + "' worked");
@ -42,22 +38,54 @@ function testCanvasDrawImage(v) {
}
}
function checkGetImageDataTainted(ctx, v) {
try {
var data = ctx.getImageData(0, 0, 1, 1);
ok(false, "changing the CORS mode should not allow reading data from remote videos");
} catch (error) {
ok(error.name === "SecurityError", "changing the CORS mode, drawImage '" + v.src + "' then getImageData with crossOrigin='" + v.crossOrigin + "' failed");
}
}
function checkCaptureStream(c, v) {
try {
var stream = c.captureStream(0);
ok(true, "drawImage '" + v.src + "' then captureStream with crossOrigin='" + v.crossOrigin + "' worked");
} catch(error) {
ok(!v.crossOrigin && error.name === "SecurityError", "drawImage '" + v.src + "' then captureStream with crossOrigin='" + v.crossOrigin + "' failed");
v.tainted = true;
}
}
function checkCaptureStreamTainted(c, v) {
try {
var stream = c.captureStream(0);
ok(false, "changing the CORS mode should not allow capturing a stream from remote videos");
} catch (error) {
ok(error.name === "SecurityError", "changing the CORS mode, drawImage '" + v.src + "' then captureStream with crossOrigin='" + v.crossOrigin + "' failed");
}
}
function testCanvasDrawImage(v) {
var c = createCanvas(v.width, v.height);
var ctx = c.getContext("2d");
ctx.drawImage(v, 0, 0);
checkGetImageData(ctx, v);
checkCaptureStream(c, v);
}
function testCanvasCreatePattern(v) {
var c = createCanvas(v.width, v.height);
var ctx = c.getContext("2d");
ctx.fillStyle = ctx.createPattern(v, "");
ctx.fillRect(0, 0, c.width, c.height);
try {
var data = ctx.getImageData(0, 0, 1, 1);
ok(true, "createPattern '" + v.src + "' then getImageData with crossOrigin='" + v.crossOrigin + "' worked");
} catch(error) {
ok(!v.crossOrigin && error.name === "SecurityError", "createPattern '" + v.src + "' then getImageData with crossOrigin='" + v.crossOrigin + "' failed");
v.tainted = true;
}
checkGetImageData(ctx, v);
checkCaptureStream(c, v);
}
function testWebGL(v) {
function testWebGL(gl, v) {
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
@ -75,12 +103,8 @@ function testTaintedCanvas(v) {
var ctx = c.getContext("2d");
ctx.drawImage(v, 0, 0);
try {
var data = ctx.getImageData(0, 0, 1, 1);
ok(false, "changing the CORS mode should not allow reading data from remote videos");
} catch (error) {
ok(error.name === "SecurityError", "changing the CORS mode, drawImage '" + v.src + "' then getImageData with crossOrigin='" + v.crossOrigin + "' failed");
}
checkGetImageDataTainted(ctx, v);
checkCaptureStreamTainted(c, v);
}
function vidDataSuccess(e) {
@ -88,8 +112,8 @@ function vidDataSuccess(e) {
testCanvasDrawImage(e.target);
testCanvasCreatePattern(e.target);
if (gl) {
testWebGL(e.target);
if (document.gl) {
testWebGL(document.gl, e.target);
}
// If we change the CORS mode after loading the file without CORS it should still throw a security error
if (e.target.tainted) {
@ -121,7 +145,7 @@ function startTest(test, token) {
v.crossOrigin = test.cors;
}
v.token = token;
manager.started(token);
document.manager.started(token);
v.autoplay = true;
v.preload = "auto";
v.style.display = "none";
@ -140,53 +164,59 @@ function startTest(test, token) {
function doneTest(e) {
var v = e.target;
v.parentNode.removeChild(v);
manager.finished(v.token);
document.manager.finished(v.token);
}
var videoFile = getPlayableVideo(gSmallTests);
if (!videoFile) {
SimpleTest.finish();
}
videoFile = "?name=tests/dom/media/test/" + videoFile.name + "&type=" + videoFile.type;
function beginTest() {
var videoFile = getPlayableVideo(gSmallTests);
if (!videoFile) {
SimpleTest.finish();
}
videoFile = "?name=tests/dom/media/test/" + videoFile.name + "&type=" + videoFile.type;
var gl;
try {
gl = createCanvas(16, 16).getContext("experimental-webgl");
} catch (ex) {
// Mac OS X 10.5 doesn't support WebGL, so we won't run the WebGL tests
document.manager = new MediaTestManager;
var corsTests = [];
const host = "http://example.com/tests/dom/canvas/test/crossorigin/video.sjs";
const serverAttrValues = [
[ "&cors=none", "none" ],
[ "&cors=anonymous", "anonymous" ],
[ "&cors=use-credentials", "use-credentials" ]
];
const clientAttrValues = [
[ "missing-value-default", "none" ],
[ "", "anonymous" ],
[ "just-crossOrigin-without-value", "anonymous" ],
[ "anonymous", "anonymous" ],
[ "use-credentials", "use-credentials" ],
[ "foobar", "anonymous" ]
];
// Build the video file test array
for (var i = 0; i < serverAttrValues.length; i++) {
for (var n = 0; n < clientAttrValues.length; n++) {
corsTests.push({
name: host + videoFile + serverAttrValues[i][0],
nameIntent: serverAttrValues[i][1],
cors: clientAttrValues[n][0],
corsIntent: clientAttrValues[n][1]
});
}
}
try {
document.gl = createCanvas(16, 16).getContext("experimental-webgl");
} catch (ex) {
// Mac OS X 10.5 doesn't support WebGL, so we won't run the WebGL tests
}
document.manager.runTests(corsTests, startTest);
}
var manager = new MediaTestManager;
var corsTests = [];
const host = "http://example.com/tests/dom/canvas/test/crossorigin/video.sjs";
const serverAttrValues = [
[ "&cors=none", "none" ],
[ "&cors=anonymous", "anonymous" ],
[ "&cors=use-credentials", "use-credentials" ]
];
const clientAttrValues = [
[ "missing-value-default", "none" ],
[ "", "anonymous" ],
[ "just-crossOrigin-without-value", "anonymous" ],
[ "anonymous", "anonymous" ],
[ "use-credentials", "use-credentials" ],
[ "foobar", "anonymous" ]
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
// Build the video file test array
for (var i = 0; i < serverAttrValues.length; i++) {
for (var n = 0; n < clientAttrValues.length; n++) {
corsTests.push({
name: host + videoFile + serverAttrValues[i][0],
nameIntent: serverAttrValues[i][1],
cors: clientAttrValues[n][0],
corsIntent: clientAttrValues[n][1]
});
}
}
manager.runTests(corsTests, startTest);
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
</script>
</pre>

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

View File

@ -0,0 +1,2 @@
Access-Control-Allow-Origin: http://mochi.test:8888
Access-Control-Allow-Credentials: true

View File

@ -14,6 +14,8 @@ support-files =
image_green.png
image_red-16x16.png
image_red.png
image_red_crossorigin_credentials.png
image_red_crossorigin_credentials.png^headers^
image_redtransparent.png
image_rgrg-256x256.png
image_rrgg-256x256.png
@ -224,6 +226,8 @@ skip-if = (toolkit == 'gonk' && !debug) || os == 'win' #specialpowers.wrap
[test_hitregion_event.html]
skip-if = os == "android" || appname == "b2g"
[test_canvas_strokeStyle_getter.html]
[test_capture.html]
support-files = captureStream_common.js
[test_drawImageIncomplete.html]
[test_drawImage_document_domain.html]
[test_drawImage_edge_cases.html]

View File

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Canvas2D test: CaptureStream()</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="captureStream_common.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body>
<script>
var c; // Canvas element captured by streams.
var h; // CaptureStreamTestHelper holding utility test functions.
var vauto; // Video element with captureStream stream in automatic mode.
var vmanual; // Video element with captureStream stream in manual (fps 0) mode.
var vrate; // Video element with captureStream stream with fixed frame rate.
function checkDrawColorInitialRed() {
info("Checking that all video elements become red after first drawColor(red).");
h.drawColor(c, h.red);
vauto.mozSrcObject = c.captureStream();
vmanual.mozSrcObject = c.captureStream(0);
vrate.mozSrcObject = c.captureStream(10);
ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "vauto hould not be drawn to before stable state");
ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "vrate Should not be drawn to before stable state");
ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "vmanual Should not be drawn to before stable state");
return Promise.resolve()
.then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically"))
.then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically"))
.then(() => h.waitForPixel(vmanual, h.red, 0, "should become red when we get" +
" to stable state (first frame)"));
}
function checkDrawColorGreen() {
info("Checking that drawColor(green) propagates properly to video elements.");
h.drawColor(c, h.green);
return Promise.resolve()
.then(() => h.waitForPixel(vauto, h.green, 0, "should become green automatically"))
.then(() => h.waitForPixel(vrate, h.green, 0, "should become green automatically"))
.then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red"))
.then(() => h.requestFrame(vmanual))
.then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after requstFrame()"));
}
function checkRequestFrameOrderGuarantee() {
info("Checking that requestFrame() immediately before and after drawColor() " +
"calls results in the expected frame seen in the stream.");
return Promise.resolve()
.then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green"))
.then(() => h.drawColor(c, h.red)) // 1. Draw canvas red
.then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame
.then(() => h.drawColor(c, h.green)) // 3. Immediately draw canvas green
.then(() => h.waitForPixel(vmanual, h.red, 0, "should become red after call order test"))
.then(() => h.waitForPixelToTimeout(vmanual, h.green, 0, 500, "should not become green after call order test"));
}
function checkDrawImageNotCleanRed() {
info("Checking that drawImage with not origin-clean image renders streams useless.");
var ctx = c.getContext('2d');
var notCleanRed = new Image();
return new Promise((resolve, reject) => {
notCleanRed.onload = resolve;
notCleanRed.onerror = () => reject(new Error("Failed to load tainted image."));
notCleanRed.src = "http://example.com/tests/dom/canvas/test/image_red_crossorigin_credentials.png";
document.body.appendChild(notCleanRed);
})
.then(() => ctx.drawImage(notCleanRed, 0, 0, c.width, c.height))
.then(() => h.testNotClean(c))
.then(() => h.waitForPixelToTimeout(vauto, h.red, 0, 1000, "should not become red"))
.then(() => h.waitForPixelToTimeout(vrate, h.red, 0, 0, "should not become red"))
.then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green"))
.then(() => h.requestFrame(vmanual))
.then(() => h.waitForPixelToTimeout(vmanual, h.red, 0, 1000, "should not become red"));
}
function finish() {
ok(true, 'Test complete.');
SimpleTest.finish();
}
function beginTest() {
h = new CaptureStreamTestHelper2D();
c = h.createAndAppendElement('canvas', 'c');
vauto = h.createAndAppendElement('video', 'vauto');
vmanual = h.createAndAppendElement('video', 'vmanual');
vrate = h.createAndAppendElement('video', 'vrate');
Promise.resolve()
.then(checkDrawColorInitialRed)
.then(checkDrawColorGreen)
.then(checkRequestFrameOrderGuarantee)
.then(checkDrawColorGreen) // Restore video elements to green.
.then(checkDrawImageNotCleanRed)
.then(finish);
}
SimpleTest.waitForExplicitFinish();
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
</script>

View File

@ -9,6 +9,8 @@ support-files =
[webgl-mochitest/test_backbuffer_channels.html]
fail-if = (os == 'b2g')
[webgl-mochitest/test_depth_readpixels.html]
[webgl-mochitest/test_capture.html]
support-files = captureStream_common.js
[webgl-mochitest/test_draw.html]
[webgl-mochitest/test_fb_param.html]
[webgl-mochitest/test_fb_param_crash.html]

View File

@ -0,0 +1,200 @@
<!DOCTYPE HTML>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>WebGL test: CaptureStream()</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="../captureStream_common.js">
<script src="driver-info.js"></script>
<script src="webgl-util.js"></script>
<script id="vs" type="x-shader/x-vertex">
attribute vec2 aVertCoord;
void main(void) {
gl_Position = vec4(aVertCoord, 0.0, 1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 uColor;
void main(void) {
gl_FragColor = uColor;
}
</script>
<body>
<script>
// Globals. Initialized during beginTest().
var c; // Canvas element captured by streams.
var gl; // WebGLContext of |c|.
var h; // CaptureStreamTestHelper holding utility test functions.
var vauto; // Video element with captureStream stream in automatic mode.
var vmanual; // Video element with captureStream stream in manual (fps 0) mode.
var vrate; // Video element with captureStream stream with fixed frame rate.
/* Fails the test if there was a GL error */
function checkGLError(info) {
var error = gl.getError();
// Comparing strings for sake of log output in hex format.
is("0x" + error.toString(16), "0x0", "WebGL error [" + info + "]");
}
function checkClearColorInitialRed() {
info("Checking that clearing to red works for first frame.");
h.clearColor(c, h.red);
vauto.mozSrcObject = c.captureStream();
vmanual.mozSrcObject = c.captureStream(0);
vrate.mozSrcObject = c.captureStream(10);
ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "Should not be drawn to before stable state");
ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "Should not be drawn to before stable state");
ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "Should not be drawn to before stable state");
return Promise.resolve()
.then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically"))
.then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically"))
.then(() => h.waitForPixel(vmanual, h.red, 0, "should become red when we get to stable state (first frame)"))
}
function checkDrawColorGreen() {
info("Checking that drawColor() results in green frames.");
h.drawColor(c, h.green);
checkGLError('after DrawColor');
return Promise.resolve()
.then(() => h.waitForPixel(vauto, h.green, 0, "should become green automatically"))
.then(() => h.waitForPixel(vrate, h.green, 0, "should become green automatically"))
.then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red"))
.then(() => h.requestFrame(vmanual))
.then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after requstFrame()"))
}
function checkClearColorRed() {
info("Checking that clearing to red works.");
h.clearColor(c, h.red);
return Promise.resolve()
.then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically"))
.then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically"))
.then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green"))
.then(() => h.requestFrame(vmanual))
.then(() => h.waitForPixel(vmanual, h.red, 0, "should become red after requestFrame()"))
}
function checkRequestFrameOrderGuarantee() {
info("Checking that requestFrame() immediately before and after draw " +
"calls results in the expected frame seen in the stream.");
return Promise.resolve()
.then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red"))
.then(() => h.drawColor(c, h.green)) // 1. Draw canvas green
.then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame
.then(() => h.clearColor(c, h.red)) // 3. Immediately clear to red
.then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after call order test"))
.then(() => h.waitForPixelToTimeout(vmanual, h.red, 0, 500, "should not become red after call order test"));
}
function checkCapturingForbidden() {
info("Checking that capturing a WebGL context with " +
"`preservDrawingBuffer: false` is forbidden.");
var c2 = h.createAndAppendElement("canvas", "c2");
var gl2 = WebGLUtil.getWebGL("c2", false, { preserveDrawingBuffer: false });
var checkThrows = function(f, expected, fName) {
try {
f();
ok(false, fName + " should throw when not preserving drawing buffer");
} catch(e) {
is(e.name, expected, fName + " forbidden when not preserving drawing buffer");
}
};
checkThrows(() => c2.captureStream(), "NS_ERROR_FAILURE", "captureStream()");
checkThrows(() => c2.captureStream(0), "NS_ERROR_FAILURE", "captureStream(0)");
checkThrows(() => c2.captureStream(10), "NS_ERROR_FAILURE", "captureStream(10)");
}
function finish() {
ok(true, 'Test complete.');
SimpleTest.finish();
}
function beginTest() {
h = new CaptureStreamTestHelperWebGL();
c = h.createAndAppendElement('canvas', 'c');
vauto = h.createAndAppendElement('video', 'vauto');
vmanual = h.createAndAppendElement('video', 'vmanual');
vrate = h.createAndAppendElement('video', 'vrate');
gl = WebGLUtil.getWebGL('c', false, { preserveDrawingBuffer: true });
if (!gl) {
todo(false, 'WebGL is unavailable.');
finish();
return;
}
function errorFunc(str) {
ok(false, 'Error: ' + str);
}
WebGLUtil.setErrorFunc(errorFunc);
WebGLUtil.setWarningFunc(errorFunc);
gl.disable(gl.DEPTH_TEST);
prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs');
if (!prog) {
ok(false, 'Program linking should succeed.');
return;
}
// Setup vertex coordinates for drawing a rectangle across the whole canvas.
prog.aVertCoord = gl.getAttribLocation(prog, "aVertCoord");
ok(prog.aVertCoord >= 0, '`aVertCoord` should be valid.');
var vertCoordArr = new Float32Array([
-1, -1,
1, -1,
-1, 1,
1, 1,
]);
var vertCoordBuff = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff);
gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW);
gl.useProgram(prog);
gl.enableVertexAttribArray(prog.aVertCoord);
gl.vertexAttribPointer(prog.aVertCoord, 2, gl.FLOAT, false, 0, 0);
// Setup the helper with a pointer to how to change fragment color.
var uColorLocation = gl.getUniformLocation(prog, "uColor");
h.setFragmentColorLocation(uColorLocation);
checkGLError('after setup');
// Run tests.
Promise.resolve()
.then(checkClearColorInitialRed)
.then(checkDrawColorGreen)
.then(checkClearColorRed)
.then(checkRequestFrameOrderGuarantee)
.then(checkCapturingForbidden)
.then(finish);
}
SimpleTest.waitForExplicitFinish();
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
</script>

View File

@ -34,19 +34,19 @@ WebGLUtil = (function() {
// ---------------------------------------------------------------------------
// WebGL helpers
function getWebGL(canvasId, requireConformant) {
function getWebGL(canvasId, requireConformant, attributes) {
// `requireConformant` will default to falsey if it is not supplied.
var canvas = document.getElementById(canvasId);
var gl = null;
try {
gl = canvas.getContext('webgl');
gl = canvas.getContext('webgl', attributes);
} catch(e) {}
if (!gl && !requireConformant) {
try {
gl = canvas.getContext('experimental-webgl');
gl = canvas.getContext('experimental-webgl', attributes);
} catch(e) {}
}

View File

@ -13,6 +13,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/CanvasCaptureMediaStream.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/HTMLCanvasElementBinding.h"
@ -405,6 +406,53 @@ HTMLCanvasElement::GetMozPrintCallback() const
return mPrintCallback;
}
already_AddRefed<CanvasCaptureMediaStream>
HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
ErrorResult& aRv)
{
if (IsWriteOnly()) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
if (!mCurrentContext) {
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
return nullptr;
}
if (mCurrentContextType != CanvasContextType::Canvas2D) {
WebGLContext* gl = static_cast<WebGLContext*>(mCurrentContext.get());
if (!gl->IsPreservingDrawingBuffer()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
}
nsRefPtr<CanvasCaptureMediaStream> stream =
CanvasCaptureMediaStream::CreateSourceStream(window, this);
if (!stream) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<nsIPrincipal> principal = NodePrincipal();
stream->CombineWithPrincipal(principal);
TrackID videoTrackId = 1;
nsresult rv = stream->Init(aFrameRate, videoTrackId);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
return stream.forget();
}
nsresult
HTMLCanvasElement::ExtractData(nsAString& aType,
const nsAString& aOptions,

View File

@ -29,7 +29,7 @@ class SourceSurface;
}
namespace dom {
class CanvasCaptureMediaStream;
class File;
class FileCallback;
class HTMLCanvasPrintState;
@ -126,6 +126,9 @@ public:
PrintCallback* GetMozPrintCallback() const;
void SetMozPrintCallback(PrintCallback* aCallback);
already_AddRefed<CanvasCaptureMediaStream>
CaptureStream(const Optional<double>& aFrameRate, ErrorResult& aRv);
/**
* Get the size in pixels of this canvas element
*/

View File

@ -1484,10 +1484,12 @@ HTMLMediaElement::Seek(double aTime,
// Clamp the seek target to inside the seekable ranges.
nsRefPtr<dom::TimeRanges> seekable = new dom::TimeRanges();
if (NS_FAILED(mDecoder->GetSeekable(seekable))) {
media::TimeIntervals seekableIntervals = mDecoder->GetSeekable();
if (seekableIntervals.IsInvalid()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
seekableIntervals.ToTimeRanges(seekable);
uint32_t length = 0;
seekable->GetLength(&length);
if (!length) {
@ -1601,9 +1603,8 @@ HTMLMediaElement::Seekable() const
{
nsRefPtr<TimeRanges> ranges = new TimeRanges();
if (mDecoder && mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mDecoder->GetSeekable(ranges);
mDecoder->GetSeekable().ToTimeRanges(ranges);
}
ranges->Normalize();
return ranges.forget();
}
@ -4169,12 +4170,12 @@ HTMLMediaElement::Buffered() const
nsRefPtr<TimeRanges> ranges = new TimeRanges();
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING) {
if (mDecoder) {
// If GetBuffered fails we ignore the error result and just return the
// time ranges we found up till the error.
mDecoder->GetBuffered(ranges);
media::TimeIntervals buffered = mDecoder->GetBuffered();
if (!buffered.IsInvalid()) {
buffered.ToTimeRanges(ranges);
}
}
}
ranges->Normalize();
return ranges.forget();
}

View File

@ -482,6 +482,60 @@ Icc.prototype = {
aCallback.notifySuccessWithBoolean(aResponse.result);
});
},
iccOpenChannel: function(aAid, aCallback) {
this._radioInterface.sendWorkerMessage("iccOpenChannel",
{ aid: aAid },
(aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
return;
}
aCallback.notifyOpenChannelSuccess(aResponse.channel);
});
},
iccExchangeAPDU: function(aChannel, aCla, aIns, aP1, aP2, aP3, aData, aCallback) {
if (!aData) {
if (DEBUG) debug('data is not set , aP3 : ' + aP3);
}
let apdu = {
cla: aCla,
command: aIns,
p1: aP1,
p2: aP2,
p3: aP3,
data: aData
};
this._radioInterface.sendWorkerMessage("iccExchangeAPDU",
{ channel: aChannel, apdu: apdu },
(aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
return;
}
aCallback.notifyExchangeAPDUResponse(aResponse.sw1,
aResponse.sw2,
aResponse.simResponse);
});
},
iccCloseChannel: function(aChannel, aCallback) {
this._radioInterface.sendWorkerMessage("iccCloseChannel",
{ channel: aChannel },
(aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
return;
}
aCallback.notifyCloseChannelSuccess();
});
}
};

View File

@ -6,51 +6,12 @@
interface nsIDOMDOMRequest;
interface nsIDOMWindow;
interface nsIIccInfo;
interface nsIIccListener;
[scriptable, uuid(6136acab-b50e-494a-a86d-df392a032897)]
interface nsIIccChannelCallback : nsISupports
{
/**
* Callback function to notify on successfully opening a logical channel.
*
* @param channel
* The Channel Number/Handle that is successfully opened.
*/
void notifyOpenChannelSuccess(in long channel);
/**
* Callback function to notify on successfully closing the logical channel.
*
*/
void notifyCloseChannelSuccess();
/**
* Callback function to notify the status of 'iccExchangeAPDU' command.
*
* @param sw1
* Response's First Status Byte
* @param sw2
* Response's Second Status Byte
* @param data
* Response's data
*/
void notifyExchangeAPDUResponse(in octet sw1,
in octet sw2,
in DOMString data);
/**
* Callback function to notify error
*
*/
void notifyError(in DOMString error);
};
/**
* XPCOM component (in the content process) that provides the ICC information.
*/
[scriptable, uuid(7dd6e186-b007-11e4-9b7e-7717d7863cb8)]
[scriptable, uuid(2fbacfc4-f52d-11e4-9667-33b72f279d14)]
interface nsIIccProvider : nsISupports
{
/**
@ -91,32 +52,4 @@ interface nsIIccProvider : nsISupports
in unsigned long contactType,
in jsval contact,
in DOMString pin2);
/**
* Secure Card Icc communication channel
*/
void iccOpenChannel(in unsigned long clientId,
in DOMString aid,
in nsIIccChannelCallback callback);
/**
* Exchange Command APDU (C-APDU) with SIM on the given logical channel.
* Note that 'P3' parameter could be Le/Lc depending on command APDU case.
* For Case 1 scenario (when only command header is present), the value
* of 'P3' should be set to '-1' explicitly.
* Refer to 3G TS 31.101 , 10.2 'Command APDU Structure' for all the cases.
*/
void iccExchangeAPDU(in unsigned long clientId,
in long channel,
in octet cla,
in octet ins,
in octet p1,
in octet p2,
in short p3,
in DOMString data,
in nsIIccChannelCallback callback);
void iccCloseChannel(in unsigned long clientId,
in long channel,
in nsIIccChannelCallback callback);
};

View File

@ -63,6 +63,44 @@ interface nsIIccCallback : nsISupports
void notifyCardLockError(in DOMString aErrorMsg, in long aRetryCount);
};
[scriptable, uuid(6136acab-b50e-494a-a86d-df392a032897)]
interface nsIIccChannelCallback : nsISupports
{
/**
* Callback function to notify on successfully opening a logical channel.
*
* @param channel
* The Channel Number/Handle that is successfully opened.
*/
void notifyOpenChannelSuccess(in long channel);
/**
* Callback function to notify on successfully closing the logical channel.
*
*/
void notifyCloseChannelSuccess();
/**
* Callback function to notify the status of 'iccExchangeAPDU' command.
*
* @param sw1
* Response's First Status Byte
* @param sw2
* Response's Second Status Byte
* @param data
* Response's data
*/
void notifyExchangeAPDUResponse(in octet sw1,
in octet sw2,
in DOMString data);
/**
* Callback function to notify error
*
*/
void notifyError(in DOMString error);
};
%{C++
#define ICC_SERVICE_CID \
{ 0xbab0277a, 0x900e, 0x11e4, { 0x80, 0xc7, 0xdb, 0xd7, 0xad, 0x05, 0x24, 0x01 } }
@ -97,7 +135,7 @@ NS_CreateIccService();
/**
* XPCOM component that provides the access to the selected ICC.
*/
[scriptable, uuid(20a99186-e4cb-11e4-a5f9-938abcf7c826)]
[scriptable, uuid(6ad6b686-f52d-11e4-942d-db2884bd9242)]
interface nsIIcc : nsISupports
{
/**
@ -331,4 +369,61 @@ interface nsIIcc : nsISupports
*/
void getServiceStateEnabled(in unsigned long aService,
in nsIIccCallback aCallback);
/**
* Open Secure Card Icc communication channel
*
* @param aAid
* Card Application Id in this UICC.
* @param aCallback
* An instance of nsIIccChannelCallback.
* nsIIccChannelCallback::notifyOpenChannelSuccess() if success.
* nsIIccChannelCallback::notifyError(), otherwise.
*/
void iccOpenChannel(in DOMString aAid,
in nsIIccChannelCallback aCallback);
/**
* Exchange Command APDU (C-APDU) with UICC on the given logical channel.
* Note that 'P3' parameter could be Le/Lc depending on command APDU case.
* For Case 1 scenario (when only command header is present), the value
* of 'P3' should be set to '-1' explicitly.
* Refer to 3G TS 31.101 , 10.2 'Command APDU Structure' for all the cases.
*
* @param aChannel
* given logical channel.
* @param aCla
* APDU class.
* @param aIns
* Instruction code.
* @param aP1, aP2, aP3
* P1, P2, P3 parameters in APDU.
* @param aData
* The hex data to be sent by this PDU.
* @param aCallback
* An instance of nsIIccChannelCallback.
* nsIIccChannelCallback::notifyExchangeAPDUResponse() if success.
* nsIIccChannelCallback::notifyError(), otherwise.
*/
void iccExchangeAPDU(in long aChannel,
in octet aCla,
in octet aIns,
in octet aP1,
in octet aP2,
in short aP3,
in DOMString aData,
in nsIIccChannelCallback aCallback);
/**
* Close Secure Card Icc communication channel
*
* @param aChannel
* Channel to be closed.
* @param aCallback
* An instance of nsIIccChannelCallback.
* nsIIccChannelCallback::notifyCloseChannelSuccess() if success.
* nsIIccChannelCallback::notifyError(), otherwise.
*/
void iccCloseChannel(in long aChannel,
in nsIIccChannelCallback aCallback);
};

View File

@ -265,6 +265,26 @@ IccChild::GetServiceStateEnabled(uint32_t aService,
? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
IccChild::IccOpenChannel(const nsAString& aAid, nsIIccChannelCallback* aCallback)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
IccChild::IccExchangeAPDU(int32_t aChannel, uint8_t aCla, uint8_t aIns, uint8_t aP1,
uint8_t aP2, int16_t aP3, const nsAString & aData,
nsIIccChannelCallback* aCallback)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
IccChild::IccCloseChannel(int32_t aChannel, nsIIccChannelCallback* aCallback)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/**
* PIccRequestChild Implementation.
*/

View File

@ -187,9 +187,8 @@
"Command insertHorizontalRule, value \"id\": input event, canceled":true,
"Command insertHorizontalRule, value \"id\": beforeinput event, uncanceled":true,
"Command insertHorizontalRule, value \"id\": input event, uncanceled":true,
"Command insertHTML, value \"\": execCommand() must not throw, canceled":true,
"Command insertHTML, value \"\": beforeinput event, canceled":true,
"Command insertHTML, value \"\": execCommand() must not throw, uncanceled":true,
"Command insertHTML, value \"\": input event, canceled":true,
"Command insertHTML, value \"\": beforeinput event, uncanceled":true,
"Command insertHTML, value \"\": input event, uncanceled":true,
"Command insertHTML, value \"quasit\": beforeinput event, canceled":true,

View File

@ -3801,9 +3801,6 @@
"[[\"inserthtml\",\"<!--abc-->\"]] \"<p><br>{}</p>\" compare innerHTML":true,
"[[\"inserthtml\",\"<!--abc-->\"]] \"<p><!--foo--><span><br></span>{}<!--bar--></p>\" compare innerHTML":true,
"[[\"inserthtml\",\"<!--abc-->\"]] \"<p><span><!--foo--><br><!--bar--></span>{}</p>\" compare innerHTML":true,
"[[\"inserthtml\",\"\"]] \"foo[bar]baz\": execCommand(\"inserthtml\", false, \"\") return value":true,
"[[\"inserthtml\",\"\"]] \"foo[bar]baz\" compare innerHTML":true,
"[[\"inserthtml\",\"\\u0000\"]] \"foo[bar]baz\" compare innerHTML":true,
"[[\"stylewithcss\",\"true\"],[\"inserthtml\",\"<b>\"]] \"foo[bar]baz\" compare innerHTML":true,
"[[\"stylewithcss\",\"false\"],[\"inserthtml\",\"<b>\"]] \"foo[bar]baz\" compare innerHTML":true,
"[[\"defaultparagraphseparator\",\"div\"],[\"inserthtml\",\"<p>abc\"]] \"<p>foo[bar]baz\": execCommand(\"defaultparagraphseparator\", false, \"div\") return value":true,

View File

@ -2282,6 +2282,12 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
mAppInfo.ID.Assign(ID);
mAppInfo.vendor.Assign(vendor);
return true;
}
bool
ContentChild::RecvAppInit()
{
if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
return true;
}

View File

@ -345,6 +345,7 @@ public:
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
const nsCString& name, const nsCString& UAName,
const nsCString& ID, const nsCString& vendor) override;
virtual bool RecvAppInit() override;
virtual bool RecvLastPrivateDocShellDestroyed() override;

View File

@ -2426,6 +2426,18 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
// must come after the Open() call above.
ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
if (gAppData) {
nsCString version(gAppData->version);
nsCString buildID(gAppData->buildID);
nsCString name(gAppData->name);
nsCString UAName(gAppData->UAName);
nsCString ID(gAppData->ID);
nsCString vendor(gAppData->vendor);
// Sending all information to content process.
unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
}
if (aSetupOffMainThreadCompositing) {
// NB: internally, this will send an IPC message to the child
// process to get it to create the CompositorChild. This
@ -2457,15 +2469,8 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
}
if (gAppData) {
nsCString version(gAppData->version);
nsCString buildID(gAppData->buildID);
nsCString name(gAppData->name);
nsCString UAName(gAppData->UAName);
nsCString ID(gAppData->ID);
nsCString vendor(gAppData->vendor);
// Sending all information to content process.
unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
unused << SendAppInit();
}
nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();

View File

@ -60,6 +60,7 @@ using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
using nsIWidget::TouchPointerState from "nsIWidget.h";
using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
using struct mozilla::OwningSerializedStructuredCloneBuffer from "ipc/IPCMessageUtils.h";
using nsEventStatus from "mozilla/EventForwards.h";
namespace mozilla {
namespace dom {
@ -594,8 +595,14 @@ child:
RealMouseButtonEvent(WidgetMouseEvent event);
RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding);
MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
RealTouchEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
RealTouchMoveEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
RealTouchEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId,
nsEventStatus aApzResponse);
RealTouchMoveEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId,
nsEventStatus aApzResponse);
RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction, uint32_t aDropEffect);
/**

View File

@ -565,6 +565,7 @@ child:
AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
nsCString ID, nsCString vendor);
AppInit();
// Notify child that last-pb-context-exited notification was observed
LastPrivateDocShellDestroyed();

View File

@ -38,8 +38,6 @@
#undef LOG
#endif
#include <utility>
// Use LOGP inside a ParticularProcessPriorityManager method; use LOG
// everywhere else. LOGP prints out information about the particular process
// priority manager.
@ -204,26 +202,6 @@ public:
*/
void Unfreeze();
/**
* Return the number of processes that have
* PROCESS_PRIORITY_FOREGROUND priority.
*/
uint32_t NumberOfForegroundProcesses();
/**
* Register a priority change to be performed at later time.
*/
void ScheduleDelayedSetPriority(
ParticularProcessPriorityManager* aParticularManager,
hal::ProcessPriority aPriority);
/**
* Perform the registered priority change unless
* aLastParticularManager is the same as the registered one.
*/
void PerformDelayedSetPriority(
ParticularProcessPriorityManager* aLastParticularManager);
private:
static bool sPrefListenersRegistered;
static bool sInitialized;
@ -243,6 +221,7 @@ private:
void ObserveContentParentCreated(nsISupports* aContentParent);
void ObserveContentParentDestroyed(nsISupports* aSubject);
void ObserveScreenStateChanged(const char16_t* aData);
nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> >
mParticularManagers;
@ -258,10 +237,6 @@ private:
/** Contains a pseudo-LRU list of foreground processes */
ProcessLRUPool mForegroundLRUPool;
/** Contains the delayed priority change request */
std::pair<nsRefPtr<ParticularProcessPriorityManager>, hal::ProcessPriority>
mDelayedSetPriority;
};
/**
@ -451,7 +426,6 @@ ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
RegisterWakeLockObserver(this);
mDelayedSetPriority = std::make_pair(nullptr, PROCESS_PRIORITY_UNKNOWN);
}
ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl()
@ -473,6 +447,7 @@ ProcessPriorityManagerImpl::Init()
if (os) {
os->AddObserver(this, "ipc:content-created", /* ownsWeak */ false);
os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ false);
os->AddObserver(this, "screen-state-changed", /* ownsWeak */ false);
}
}
@ -487,6 +462,8 @@ ProcessPriorityManagerImpl::Observe(
ObserveContentParentCreated(aSubject);
} else if (topic.EqualsLiteral("ipc:content-shutdown")) {
ObserveContentParentDestroyed(aSubject);
} else if (topic.EqualsLiteral("screen-state-changed")) {
ObserveScreenStateChanged(aData);
} else {
MOZ_ASSERT(false);
}
@ -568,10 +545,40 @@ ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
if (mHighPriorityChildIDs.Contains(childID)) {
mHighPriorityChildIDs.RemoveEntry(childID);
}
}
}
if (mDelayedSetPriority.first == pppm) {
mDelayedSetPriority = std::make_pair(nullptr, PROCESS_PRIORITY_UNKNOWN);
}
static PLDHashOperator
FreezeParticularProcessPriorityManagers(
const uint64_t& aKey,
nsRefPtr<ParticularProcessPriorityManager> aValue,
void* aUserData)
{
aValue->Freeze();
return PL_DHASH_NEXT;
}
static PLDHashOperator
UnfreezeParticularProcessPriorityManagers(
const uint64_t& aKey,
nsRefPtr<ParticularProcessPriorityManager> aValue,
void* aUserData)
{
aValue->Unfreeze();
return PL_DHASH_NEXT;
}
void
ProcessPriorityManagerImpl::ObserveScreenStateChanged(const char16_t* aData)
{
if (NS_LITERAL_STRING("on").Equals(aData)) {
sFrozen = false;
mParticularManagers.EnumerateRead(
&UnfreezeParticularProcessPriorityManagers, nullptr);
} else {
sFrozen = true;
mParticularManagers.EnumerateRead(
&FreezeParticularProcessPriorityManagers, nullptr);
}
}
@ -634,91 +641,6 @@ ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
}
}
static PLDHashOperator
FreezeParticularProcessPriorityManagers(
const uint64_t& aKey,
nsRefPtr<ParticularProcessPriorityManager> aValue,
void* aUserData)
{
aValue->Freeze();
return PL_DHASH_NEXT;
}
void
ProcessPriorityManagerImpl::Freeze()
{
sFrozen = true;
mParticularManagers.EnumerateRead(&FreezeParticularProcessPriorityManagers,
nullptr);
}
static PLDHashOperator
UnfreezeParticularProcessPriorityManagers(
const uint64_t& aKey,
nsRefPtr<ParticularProcessPriorityManager> aValue,
void* aUserData)
{
aValue->Unfreeze();
return PL_DHASH_NEXT;
}
void
ProcessPriorityManagerImpl::Unfreeze()
{
sFrozen = false;
mParticularManagers.EnumerateRead(&UnfreezeParticularProcessPriorityManagers,
nullptr);
}
static PLDHashOperator
CountNumberOfForegroundProcesses(
const uint64_t& aKey,
nsRefPtr<ParticularProcessPriorityManager> aValue,
void* aUserData)
{
uint32_t* accumulator = static_cast<uint32_t*>(aUserData);
if (aValue->CurrentPriority() == PROCESS_PRIORITY_FOREGROUND ||
aValue->CurrentPriority() == PROCESS_PRIORITY_FOREGROUND_HIGH) {
(*accumulator)++;
}
return PL_DHASH_NEXT;
}
uint32_t
ProcessPriorityManagerImpl::NumberOfForegroundProcesses()
{
uint32_t accumulator = 0;
mParticularManagers.EnumerateRead(&CountNumberOfForegroundProcesses,
&accumulator);
return accumulator;
}
void
ProcessPriorityManagerImpl::ScheduleDelayedSetPriority(
ParticularProcessPriorityManager* aParticularManager,
ProcessPriority aPriority)
{
mDelayedSetPriority = std::make_pair(aParticularManager, aPriority);
}
void
ProcessPriorityManagerImpl::PerformDelayedSetPriority(
ParticularProcessPriorityManager* aLastParticularManager)
{
nsRefPtr<ParticularProcessPriorityManager> pppm = mDelayedSetPriority.first;
ProcessPriority priority = mDelayedSetPriority.second;
mDelayedSetPriority = std::make_pair(nullptr, PROCESS_PRIORITY_UNKNOWN);
if (pppm == aLastParticularManager) {
return;
}
if (pppm && priority != PROCESS_PRIORITY_UNKNOWN) {
pppm->SetPriorityNow(priority);
}
}
NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
nsIObserver,
nsITimerCallback,
@ -932,6 +854,10 @@ ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubj
nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
NS_ENSURE_TRUE_VOID(fl);
if (mFrozen) {
return; // Ignore visibility changes when the screen is off
}
TabParent* tp = TabParent::GetFrom(fl);
if (!tp) {
return;
@ -1086,9 +1012,7 @@ ParticularProcessPriorityManager::ComputePriority()
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
}
return HasAppType("homescreen") ?
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
PROCESS_PRIORITY_BACKGROUND;
return PROCESS_PRIORITY_BACKGROUND;
}
void
@ -1125,20 +1049,6 @@ ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
ProcessPriority oldPriority = mPriority;
if (oldPriority == PROCESS_PRIORITY_FOREGROUND &&
aPriority < PROCESS_PRIORITY_FOREGROUND &&
ProcessPriorityManagerImpl::GetSingleton()->
NumberOfForegroundProcesses() == 1) {
LOGP("Attempting to demote the last foreground process is delayed.");
ProcessPriorityManagerImpl::GetSingleton()->
ScheduleDelayedSetPriority(this, aPriority);
FireTestOnlyObserverNotification("process-priority-delayed",
ProcessPriorityToString(aPriority));
return;
}
mPriority = aPriority;
hal::SetProcessPriority(Pid(), mPriority);
@ -1155,12 +1065,6 @@ ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
FireTestOnlyObserverNotification("process-priority-set",
ProcessPriorityToString(mPriority));
if (aPriority >= PROCESS_PRIORITY_FOREGROUND) {
LOGP("More than one foreground processes. Run delayed priority change");
ProcessPriorityManagerImpl::GetSingleton()->
PerformDelayedSetPriority(this);
}
}
void
@ -1173,7 +1077,6 @@ void
ParticularProcessPriorityManager::Unfreeze()
{
mFrozen = false;
ResetPriorityNow();
}
void
@ -1481,24 +1384,4 @@ ProcessPriorityManager::AnyProcessHasHighPriority()
}
}
/* static */ void
ProcessPriorityManager::Freeze()
{
ProcessPriorityManagerImpl* singleton =
ProcessPriorityManagerImpl::GetSingleton();
if (singleton) {
singleton->Freeze();
}
}
/* static */ void
ProcessPriorityManager::Unfreeze()
{
ProcessPriorityManagerImpl* singleton =
ProcessPriorityManagerImpl::GetSingleton();
if (singleton) {
singleton->Unfreeze();
}
}
} // namespace mozilla

View File

@ -74,17 +74,6 @@ public:
*/
static bool AnyProcessHasHighPriority();
/**
* Prevents processes from changing priority until unfrozen.
*/
static void Freeze();
/**
* Allow process' priorities to change again. This will immediately adjust
* processes whose priority change did not happen because of the freeze.
*/
static void Unfreeze();
private:
ProcessPriorityManager();
DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManager);

View File

@ -88,6 +88,7 @@
#include "nsNetUtil.h"
#include "nsIPermissionManager.h"
#include "nsIScriptError.h"
#include "mozilla/EventForwards.h"
#define BROWSER_ELEMENT_CHILD_SCRIPT \
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
@ -2379,7 +2380,8 @@ TabChild::GetPresShellResolution() const
bool
TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse)
{
TABC_LOG("Receiving touch event of type %d\n", aEvent.message);
@ -2407,17 +2409,17 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
return true;
}
mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId,
nsEventStatus_eIgnore);
mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId, aApzResponse);
return true;
}
bool
TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse)
{
return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId);
return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
}
bool

View File

@ -363,10 +363,12 @@ public:
const uint64_t& aInputBlockId) override;
virtual bool RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse) override;
virtual bool RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse) override;
virtual bool RecvKeyEvent(const nsString& aType,
const int32_t& aKeyCode,
const int32_t& aCharCode,

View File

@ -96,7 +96,14 @@ struct ParamTraits<mozilla::dom::AudioChannelState>
mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
{ };
template <>
struct ParamTraits<nsEventStatus>
: public ContiguousEnumSerializer<nsEventStatus,
nsEventStatus_eIgnore,
nsEventStatus_eSentinel>
{ };
}
#endif

View File

@ -1309,7 +1309,7 @@ bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
ScrollableLayerGuid guid;
uint64_t blockId;
ApzAwareEventRoutingToChild(&guid, &blockId);
ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
event.refPoint += GetChildProcessOffset();
return PBrowserParent::SendMouseWheelEvent(event, guid, blockId);
}
@ -1624,7 +1624,8 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
ScrollableLayerGuid guid;
uint64_t blockId;
ApzAwareEventRoutingToChild(&guid, &blockId);
nsEventStatus apzResponse;
ApzAwareEventRoutingToChild(&guid, &blockId, &apzResponse);
if (mIsDestroyed) {
return false;
@ -1636,8 +1637,8 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
}
return (event.message == NS_TOUCH_MOVE) ?
PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId) :
PBrowserParent::SendRealTouchEvent(event, guid, blockId);
PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId, apzResponse) :
PBrowserParent::SendRealTouchEvent(event, guid, blockId, apzResponse);
}
bool
@ -2727,7 +2728,8 @@ TabParent::GetWidget() const
void
TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId)
uint64_t* aOutInputBlockId,
nsEventStatus* aOutApzResponse)
{
if (gfxPrefs::AsyncPanZoomEnabled()) {
if (aOutTargetGuid) {
@ -2747,6 +2749,9 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
if (aOutInputBlockId) {
*aOutInputBlockId = InputAPZContext::GetInputBlockId();
}
if (aOutApzResponse) {
*aOutApzResponse = InputAPZContext::GetApzResponse();
}
// Let the widget know that the event will be sent to the child process,
// which will (hopefully) send a confirmation notice back to APZ.

View File

@ -502,18 +502,17 @@ private:
// Update state prior to routing an APZ-aware event to the child process.
// |aOutTargetGuid| will contain the identifier
// of the APZC instance that handled the event. aOutTargetGuid may be
// null.
// of the APZC instance that handled the event. aOutTargetGuid may be null.
// |aOutInputBlockId| will contain the identifier of the input block
// that this event was added to, if there was one. aOutInputBlockId may
// be null.
// that this event was added to, if there was one. aOutInputBlockId may be null.
// |aOutApzResponse| will contain the response that the APZ gave when processing
// the input block; this is used for generating appropriate pointercancel events.
void ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
// When true, we've initiated normal shutdown and notified our
// managing PContent.
uint64_t* aOutInputBlockId,
nsEventStatus* aOutApzResponse);
// When true, we've initiated normal shutdown and notified our managing PContent.
bool mMarkedDestroying;
// When true, the TabParent is invalid and we should not send IPC messages
// anymore.
// When true, the TabParent is invalid and we should not send IPC messages anymore.
bool mIsDestroyed;
// Whether we have already sent a FileDescriptor for the app package.
bool mAppPackageFileDescriptorSent;

View File

@ -0,0 +1,341 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CanvasCaptureMediaStream.h"
#include "DOMMediaStream.h"
#include "gfxPlatform.h"
#include "ImageContainer.h"
#include "MediaStreamGraph.h"
#include "mozilla/Mutex.h"
#include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "nsIAppShell.h"
#include "nsWidgetsCID.h"
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
using namespace mozilla::layers;
using namespace mozilla::gfx;
namespace mozilla {
namespace dom {
class OutputStreamDriver::StreamListener : public MediaStreamListener
{
public:
explicit StreamListener(OutputStreamDriver* aDriver,
SourceMediaStream* aSourceStream)
: mSourceStream(aSourceStream)
, mMutex("CanvasCaptureMediaStream::OSD::StreamListener")
, mDriver(aDriver)
{
MOZ_ASSERT(mDriver);
MOZ_ASSERT(mSourceStream);
}
void Forget() {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
mDriver = nullptr;
}
virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
{
// Called on the MediaStreamGraph thread.
MutexAutoLock lock(mMutex);
if (mDriver) {
mDriver->NotifyPull(aDesiredTime);
} else {
// The DOM stream is dead, let's end it
mSourceStream->EndAllTrackAndFinish();
}
}
protected:
~StreamListener() { }
private:
nsRefPtr<SourceMediaStream> mSourceStream;
// The below members are protected by mMutex.
Mutex mMutex;
// This is a raw pointer to avoid a reference cycle with OutputStreamDriver.
// Accessed on main and MediaStreamGraph threads, set on main thread.
OutputStreamDriver* mDriver;
};
OutputStreamDriver::OutputStreamDriver(CanvasCaptureMediaStream* aDOMStream,
const TrackID& aTrackId)
: mDOMStream(aDOMStream)
, mSourceStream(nullptr)
, mStarted(false)
, mStreamListener(nullptr)
, mTrackId(aTrackId)
, mMutex("CanvasCaptureMediaStream::OutputStreamDriver")
, mImage(nullptr)
{
MOZ_ASSERT(mDOMStream);
}
OutputStreamDriver::~OutputStreamDriver()
{
if (mStreamListener) {
// MediaStreamGraph will keep the listener alive until it can finish the
// stream on the next NotifyPull().
mStreamListener->Forget();
}
}
nsresult
OutputStreamDriver::Start()
{
if (mStarted) {
return NS_ERROR_ALREADY_INITIALIZED;
}
MOZ_ASSERT(mDOMStream);
mDOMStream->CreateDOMTrack(mTrackId, MediaSegment::VIDEO);
mSourceStream = mDOMStream->GetStream()->AsSourceStream();
MOZ_ASSERT(mSourceStream);
mStreamListener = new StreamListener(this, mSourceStream);
mSourceStream->AddListener(mStreamListener);
mSourceStream->AddTrack(mTrackId, 0, new VideoSegment());
mSourceStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
mSourceStream->SetPullEnabled(true);
// Run StartInternal() in stable state to allow it to directly capture a frame
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &OutputStreamDriver::StartInternal);
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
appShell->RunInStableState(runnable);
mStarted = true;
return NS_OK;
}
void
OutputStreamDriver::ForgetDOMStream()
{
if (mStreamListener) {
mStreamListener->Forget();
}
mDOMStream = nullptr;
}
void
OutputStreamDriver::AppendToTrack(StreamTime aDuration)
{
MOZ_ASSERT(mSourceStream);
MutexAutoLock lock(mMutex);
nsRefPtr<Image> image = mImage;
IntSize size = image ? image->GetSize() : IntSize(0, 0);
VideoSegment segment;
segment.AppendFrame(image.forget(), aDuration, size);
mSourceStream->AppendToTrack(mTrackId, &segment);
}
void
OutputStreamDriver::NotifyPull(StreamTime aDesiredTime)
{
StreamTime delta = aDesiredTime - mSourceStream->GetEndOfAppendedData(mTrackId);
if (delta > 0) {
// nullptr images are allowed
AppendToTrack(delta);
}
}
void
OutputStreamDriver::SetImage(Image* aImage)
{
MutexAutoLock lock(mMutex);
mImage = aImage;
}
// ----------------------------------------------------------------------
class TimerDriver : public OutputStreamDriver
, public nsITimerCallback
{
public:
explicit TimerDriver(CanvasCaptureMediaStream* aDOMStream,
const double& aFPS,
const TrackID& aTrackId)
: OutputStreamDriver(aDOMStream, aTrackId)
, mFPS(aFPS)
, mTimer(nullptr)
{
}
void ForgetDOMStream() override
{
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
OutputStreamDriver::ForgetDOMStream();
}
nsresult
TakeSnapshot()
{
// mDOMStream can't be killed while we're on main thread
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(DOMStream());
if (!DOMStream()->Canvas()) {
// DOMStream's canvas pointer was garbage collected. We can abort now.
return NS_ERROR_NOT_AVAILABLE;
}
MOZ_ASSERT(DOMStream()->Canvas());
if (DOMStream()->Canvas()->IsWriteOnly()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
// Pass `nullptr` to force alpha-premult.
RefPtr<SourceSurface> snapshot = DOMStream()->Canvas()->GetSurfaceSnapshot(nullptr);
if (!snapshot) {
return NS_ERROR_FAILURE;
}
RefPtr<SourceSurface> opt = gfxPlatform::GetPlatform()
->ScreenReferenceDrawTarget()->OptimizeSourceSurface(snapshot);
if (!opt) {
return NS_ERROR_FAILURE;
}
CairoImage::Data imageData;
imageData.mSize = opt->GetSize();
imageData.mSourceSurface = opt;
RefPtr<CairoImage> image = new layers::CairoImage();
image->SetData(imageData);
SetImage(image);
return NS_OK;
}
NS_IMETHODIMP
Notify(nsITimer* aTimer)
{
nsresult rv = TakeSnapshot();
if (NS_FAILED(rv)) {
aTimer->Cancel();
}
return rv;
}
virtual void RequestFrame() override
{
TakeSnapshot();
}
NS_DECL_ISUPPORTS_INHERITED
protected:
virtual ~TimerDriver() {}
virtual void StartInternal() override
{
// Always capture at least one frame.
DebugOnly<nsresult> rv = TakeSnapshot();
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (mFPS == 0.0) {
return;
}
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (!mTimer) {
return;
}
mTimer->InitWithCallback(this, int(1000 / mFPS), nsITimer::TYPE_REPEATING_SLACK);
}
private:
const double mFPS;
nsCOMPtr<nsITimer> mTimer;
};
NS_IMPL_ADDREF_INHERITED(TimerDriver, OutputStreamDriver)
NS_IMPL_RELEASE_INHERITED(TimerDriver, OutputStreamDriver)
NS_IMPL_QUERY_INTERFACE(TimerDriver, nsITimerCallback)
// ----------------------------------------------------------------------
NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream, DOMMediaStream,
mCanvas)
NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
CanvasCaptureMediaStream::CanvasCaptureMediaStream(HTMLCanvasElement* aCanvas)
: mCanvas(aCanvas)
, mOutputStreamDriver(nullptr)
{
}
CanvasCaptureMediaStream::~CanvasCaptureMediaStream()
{
if (mOutputStreamDriver) {
mOutputStreamDriver->ForgetDOMStream();
}
}
JSObject*
CanvasCaptureMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return dom::CanvasCaptureMediaStreamBinding::Wrap(aCx, this, aGivenProto);
}
void
CanvasCaptureMediaStream::RequestFrame()
{
if (mOutputStreamDriver) {
mOutputStreamDriver->RequestFrame();
}
}
nsresult
CanvasCaptureMediaStream::Init(const dom::Optional<double>& aFPS,
const TrackID& aTrackId)
{
if (!aFPS.WasPassed()) {
// TODO (Bug 1152298): Implement a real AutoDriver.
// We use a 30FPS TimerDriver for now.
mOutputStreamDriver = new TimerDriver(this, 30.0, aTrackId);
} else if (aFPS.Value() < 0) {
return NS_ERROR_ILLEGAL_VALUE;
} else {
// Cap frame rate to 60 FPS for sanity
double fps = std::min(60.0, aFPS.Value());
mOutputStreamDriver = new TimerDriver(this, fps, aTrackId);
}
return mOutputStreamDriver->Start();
}
already_AddRefed<CanvasCaptureMediaStream>
CanvasCaptureMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
HTMLCanvasElement* aCanvas)
{
nsRefPtr<CanvasCaptureMediaStream> stream = new CanvasCaptureMediaStream(aCanvas);
stream->InitSourceStream(aWindow);
return stream.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,108 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_CanvasCaptureMediaStream_h_
#define mozilla_dom_CanvasCaptureMediaStream_h_
namespace mozilla {
class DOMMediaStream;
class MediaStreamListener;
class SourceMediaStream;
namespace layers {
class Image;
}
namespace dom {
class CanvasCaptureMediaStream;
class HTMLCanvasElement;
class OutputStreamDriver
{
public:
OutputStreamDriver(CanvasCaptureMediaStream* aDOMStream,
const TrackID& aTrackId);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OutputStreamDriver);
nsresult Start();
virtual void ForgetDOMStream();
virtual void RequestFrame() { }
CanvasCaptureMediaStream* DOMStream() const { return mDOMStream; }
protected:
virtual ~OutputStreamDriver();
class StreamListener;
/*
* Appends mImage to video track for the desired duration.
*/
void AppendToTrack(StreamTime aDuration);
void NotifyPull(StreamTime aDesiredTime);
/*
* Sub classes can SetImage() to update the image being appended to the
* output stream. It will be appended on the next NotifyPull from MSG.
*/
void SetImage(layers::Image* aImage);
/*
* Called in main thread stable state to initialize sub classes.
*/
virtual void StartInternal() = 0;
private:
// This is a raw pointer to avoid a reference cycle between OutputStreamDriver
// and CanvasCaptureMediaStream. ForgetDOMStream() will be called by
// ~CanvasCaptureMediaStream() to make sure we don't do anything illegal.
CanvasCaptureMediaStream* mDOMStream;
nsRefPtr<SourceMediaStream> mSourceStream;
bool mStarted;
nsRefPtr<StreamListener> mStreamListener;
const TrackID mTrackId;
// The below members are protected by mMutex.
Mutex mMutex;
nsRefPtr<layers::Image> mImage;
};
class CanvasCaptureMediaStream: public DOMMediaStream
{
public:
explicit CanvasCaptureMediaStream(HTMLCanvasElement* aCanvas);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
nsresult Init(const dom::Optional<double>& aFPS, const TrackID& aTrackId);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// WebIDL
HTMLCanvasElement* Canvas() const { return mCanvas; }
void RequestFrame();
/**
* Create a CanvasCaptureMediaStream whose underlying stream is a SourceMediaStream.
*/
static already_AddRefed<CanvasCaptureMediaStream>
CreateSourceStream(nsIDOMWindow* aWindow,
HTMLCanvasElement* aCanvas);
protected:
~CanvasCaptureMediaStream();
private:
nsRefPtr<HTMLCanvasElement> mCanvas;
nsRefPtr<OutputStreamDriver> mOutputStreamDriver;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_CanvasCaptureMediaStream_h_ */

View File

@ -14,6 +14,7 @@
#include "mozilla/dom/AudioTrackList.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "MediaStreamGraph.h"
#include "AudioStreamTrack.h"
#include "VideoStreamTrack.h"
@ -666,3 +667,4 @@ DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
stream->InitTrackUnionStream(aWindow, aGraph);
return stream.forget();
}

View File

@ -36,6 +36,7 @@ class MediaStreamGraph;
namespace dom {
class AudioNode;
class HTMLCanvasElement;
class MediaStreamTrack;
class AudioStreamTrack;
class VideoStreamTrack;

187
dom/media/DecodedStream.cpp Normal file
View File

@ -0,0 +1,187 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DecodedStream.h"
#include "MediaStreamGraph.h"
#include "MediaDecoder.h"
namespace mozilla {
class DecodedStreamGraphListener : public MediaStreamListener {
typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
public:
DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData)
: mData(aData)
, mMutex("DecodedStreamGraphListener::mMutex")
, mStream(aStream)
, mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime()))
, mStreamFinishedOnMainThread(false) {}
void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) override
{
MutexAutoLock lock(mMutex);
if (mStream) {
mLastOutputTime = mStream->StreamTimeToMicroseconds(mStream->GraphTimeToStreamTime(aCurrentTime));
}
}
void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
{
if (event == EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
}
void DoNotifyFinished()
{
MutexAutoLock lock(mMutex);
mStreamFinishedOnMainThread = true;
}
int64_t GetLastOutputTime()
{
MutexAutoLock lock(mMutex);
return mLastOutputTime;
}
void Forget()
{
MOZ_ASSERT(NS_IsMainThread());
mData = nullptr;
MutexAutoLock lock(mMutex);
mStream = nullptr;
}
bool IsFinishedOnMainThread()
{
MutexAutoLock lock(mMutex);
return mStreamFinishedOnMainThread;
}
private:
// Main thread only
DecodedStreamData* mData;
Mutex mMutex;
// Members below are protected by mMutex.
nsRefPtr<MediaStream> mStream;
int64_t mLastOutputTime; // microseconds
bool mStreamFinishedOnMainThread;
};
DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime,
SourceMediaStream* aStream)
: mAudioFramesWritten(0)
, mInitialTime(aInitialTime)
, mNextVideoTime(-1)
, mNextAudioTime(-1)
, mDecoder(aDecoder)
, mStreamInitialized(false)
, mHaveSentFinish(false)
, mHaveSentFinishAudio(false)
, mHaveSentFinishVideo(false)
, mStream(aStream)
, mHaveBlockedForPlayState(false)
, mHaveBlockedForStateMachineNotPlaying(false)
, mEOSVideoCompensation(false)
{
mListener = new DecodedStreamGraphListener(mStream, this);
mStream->AddListener(mListener);
}
DecodedStreamData::~DecodedStreamData()
{
mListener->Forget();
mStream->Destroy();
}
bool
DecodedStreamData::IsFinished() const
{
return mListener->IsFinishedOnMainThread();
}
int64_t
DecodedStreamData::GetClock() const
{
return mInitialTime + mListener->GetLastOutputTime();
}
class OutputStreamListener : public MediaStreamListener {
typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
public:
OutputStreamListener(MediaDecoder* aDecoder, MediaStream* aStream)
: mDecoder(aDecoder), mStream(aStream) {}
void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
{
if (event == EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &OutputStreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget());
}
}
void Forget()
{
MOZ_ASSERT(NS_IsMainThread());
mDecoder = nullptr;
}
private:
void DoNotifyFinished()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mDecoder) {
return;
}
// Remove the finished stream so it won't block the decoded stream.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
auto& streams = mDecoder->OutputStreams();
// Don't read |mDecoder| in the loop since removing the element will lead
// to ~OutputStreamData() which will call Forget() to reset |mDecoder|.
for (int32_t i = streams.Length() - 1; i >= 0; --i) {
auto& os = streams[i];
MediaStream* p = os.mStream.get();
if (p == mStream.get()) {
if (os.mPort) {
os.mPort->Destroy();
os.mPort = nullptr;
}
streams.RemoveElementAt(i);
break;
}
}
}
// Main thread only
MediaDecoder* mDecoder;
nsRefPtr<MediaStream> mStream;
};
OutputStreamData::OutputStreamData()
{
//
}
OutputStreamData::~OutputStreamData()
{
mListener->Forget();
}
void
OutputStreamData::Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream)
{
mStream = aStream;
mListener = new OutputStreamListener(aDecoder, aStream);
aStream->AddListener(mListener);
}
} // namespace mozilla

99
dom/media/DecodedStream.h Normal file
View File

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DecodedStream_h_
#define DecodedStream_h_
#include "nsRefPtr.h"
#include "mozilla/gfx/Point.h"
namespace mozilla {
class MediaDecoder;
class MediaInputPort;
class SourceMediaStream;
class ProcessedMediaStream;
class DecodedStreamGraphListener;
class OutputStreamData;
class OutputStreamListener;
namespace layers {
class Image;
}
/*
* All MediaStream-related data is protected by the decoder's monitor.
* We have at most one DecodedStreamDaata per MediaDecoder. Its stream
* is used as the input for each ProcessedMediaStream created by calls to
* captureStream(UntilEnded). Seeking creates a new source stream, as does
* replaying after the input as ended. In the latter case, the new source is
* not connected to streams created by captureStreamUntilEnded.
*/
class DecodedStreamData {
public:
DecodedStreamData(MediaDecoder* aDecoder, int64_t aInitialTime,
SourceMediaStream* aStream);
~DecodedStreamData();
bool IsFinished() const;
int64_t GetClock() const;
/* The following group of fields are protected by the decoder's monitor
* and can be read or written on any thread.
*/
// Count of audio frames written to the stream
int64_t mAudioFramesWritten;
// Saved value of aInitialTime. Timestamp of the first audio and/or
// video packet written.
const int64_t mInitialTime; // microseconds
// mNextVideoTime is the end timestamp for the last packet sent to the stream.
// Therefore video packets starting at or after this time need to be copied
// to the output stream.
int64_t mNextVideoTime; // microseconds
int64_t mNextAudioTime; // microseconds
MediaDecoder* mDecoder;
// The last video image sent to the stream. Useful if we need to replicate
// the image.
nsRefPtr<layers::Image> mLastVideoImage;
gfx::IntSize mLastVideoImageDisplaySize;
// This is set to true when the stream is initialized (audio and
// video tracks added).
bool mStreamInitialized;
bool mHaveSentFinish;
bool mHaveSentFinishAudio;
bool mHaveSentFinishVideo;
// The decoder is responsible for calling Destroy() on this stream.
const nsRefPtr<SourceMediaStream> mStream;
nsRefPtr<DecodedStreamGraphListener> mListener;
// True when we've explicitly blocked this stream because we're
// not in PLAY_STATE_PLAYING. Used on the main thread only.
bool mHaveBlockedForPlayState;
// We also have an explicit blocker on the stream when
// mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
bool mHaveBlockedForStateMachineNotPlaying;
// True if we need to send a compensation video frame to ensure the
// StreamTime going forward.
bool mEOSVideoCompensation;
};
class OutputStreamData {
public:
// Compiler-generated default constructor needs the complete definition
// of OutputStreamListener when constructing OutputStreamData. Provide our
// own default constructor for forward declaration of OutputStreamListener
// to work.
OutputStreamData();
~OutputStreamData();
void Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream);
nsRefPtr<ProcessedMediaStream> mStream;
// mPort connects DecodedStreamData::mStream to our mStream.
nsRefPtr<MediaInputPort> mPort;
nsRefPtr<OutputStreamListener> mListener;
};
} // namespace mozilla
#endif // DecodedStream_h_

View File

@ -71,7 +71,9 @@
#ifdef MOZ_FMP4
#include "MP4Reader.h"
#include "MP4Decoder.h"
#include "MP4Demuxer.h"
#endif
#include "MediaFormatReader.h"
namespace mozilla
{
@ -659,7 +661,9 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
}
#ifdef MOZ_FMP4
if (IsMP4SupportedType(aType)) {
decoderReader = new MP4Reader(aDecoder);
decoderReader = Preferences::GetBool("media.format-reader.mp4", true) ?
static_cast<MediaDecoderReader*>(new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource()))) :
static_cast<MediaDecoderReader*>(new MP4Reader(aDecoder));
} else
#endif
#ifdef MOZ_GSTREAMER

View File

@ -1081,9 +1081,9 @@ void AudioCallbackDriver::CompleteAudioContextOperations(AsyncCubebOperation aOp
for (uint32_t i = 0; i < array.Length(); i++) {
StreamAndPromiseForOperation& s = array[i];
if ((aOperation == AsyncCubebOperation::INIT &&
s.mOperation == AudioContextOperation::Resume) ||
s.mOperation == dom::AudioContextOperation::Resume) ||
(aOperation == AsyncCubebOperation::SHUTDOWN &&
s.mOperation != AudioContextOperation::Resume)) {
s.mOperation != dom::AudioContextOperation::Resume)) {
GraphImpl()->AudioContextOperationCompleted(s.mStream,
s.mPromise,

View File

@ -118,7 +118,7 @@ public:
bool Contains(const SelfType& aOther) const
{
return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
(aOther.mEnd + aOther.mFuzz <= mEnd - mFuzz);
(aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
}
bool ContainsStrict(const SelfType& aOther) const
@ -127,6 +127,13 @@ public:
}
bool Intersects(const SelfType& aOther) const
{
return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
(aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
}
// Same as Intersects, but including the boundaries.
bool Touches(const SelfType& aOther) const
{
return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
(aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
@ -139,7 +146,17 @@ public:
return mEnd <= aOther.mStart && aOther.mStart - mEnd <= mFuzz + aOther.mFuzz;
}
SelfType Union(const SelfType& aOther) const
bool RightOf(const SelfType& aOther) const
{
return aOther.mEnd - aOther.mFuzz <= mStart + mFuzz;
}
bool LeftOf(const SelfType& aOther) const
{
return mEnd - mFuzz <= aOther.mStart + aOther.mFuzz;
}
SelfType Span(const SelfType& aOther) const
{
SelfType result(*this);
if (aOther.mStart < mStart) {
@ -176,6 +193,11 @@ public:
return mStart == mEnd;
}
void SetFuzz(const T& aFuzz)
{
mFuzz = aFuzz;
}
T mStart;
T mEnd;
T mFuzz;
@ -183,6 +205,8 @@ public:
private:
};
// An IntervalSet in a collection of Intervals. The IntervalSet is always
// normalized.
template<typename T>
class IntervalSet
{
@ -205,18 +229,22 @@ public:
}
IntervalSet(SelfType&& aOther)
: mIntervals(Move(aOther.mIntervals))
{
mIntervals.MoveElementsFrom(Move(aOther.mIntervals));
}
explicit IntervalSet(const ElemType& aOther)
{
mIntervals.AppendElement(aOther);
if (!aOther.IsEmpty()) {
mIntervals.AppendElement(aOther);
}
}
explicit IntervalSet(ElemType&& aOther)
{
mIntervals.AppendElement(Move(aOther));
if (!aOther.IsEmpty()) {
mIntervals.AppendElement(Move(aOther));
}
}
SelfType& operator= (const SelfType& aOther)
@ -247,19 +275,55 @@ public:
return *this;
}
// + and += operator will append the provided interval or intervalset.
// Note that the result is not normalized. Call Normalize() as required.
// Alternatively, use Union()
SelfType& Add(const SelfType& aIntervals)
{
mIntervals.AppendElements(aIntervals.mIntervals);
Normalize();
return *this;
}
SelfType& Add(const ElemType& aInterval)
{
mIntervals.AppendElement(aInterval);
if (aInterval.IsEmpty()) {
return *this;
}
if (mIntervals.IsEmpty()) {
mIntervals.AppendElement(aInterval);
return *this;
}
// Most of our actual usage is adding an interval that will be outside the
// range. We can speed up normalization here.
if (aInterval.RightOf(mIntervals.LastElement()) &&
!aInterval.Touches(mIntervals.LastElement())) {
mIntervals.AppendElement(aInterval);
return *this;
}
ContainerType normalized;
ElemType current(aInterval);
bool inserted = false;
IndexType i = 0;
for (; i < mIntervals.Length(); i++) {
ElemType& interval = mIntervals[i];
if (current.Touches(interval)) {
current = current.Span(interval);
} else if (current.LeftOf(interval)) {
normalized.AppendElement(Move(current));
inserted = true;
break;
} else {
normalized.AppendElement(Move(interval));
}
}
if (!inserted) {
normalized.AppendElement(Move(current));
}
for (; i < mIntervals.Length(); i++) {
normalized.AppendElement(Move(mIntervals[i]));
}
mIntervals.Clear();
mIntervals.MoveElementsFrom(Move(normalized));
return *this;
}
@ -299,18 +363,15 @@ public:
}
// Mutate this IntervalSet to be the union of this and aOther.
// Resulting IntervalSet is normalized.
SelfType& Union(const SelfType& aOther)
{
Add(aOther);
Normalize();
return *this;
}
SelfType& Union(const ElemType& aInterval)
{
Add(aInterval);
Normalize();
return *this;
}
@ -331,8 +392,8 @@ public:
j++;
}
}
mIntervals = intersection;
mIntervals.Clear();
mIntervals.MoveElementsFrom(Move(intersection));
return *this;
}
@ -417,7 +478,20 @@ public:
}
}
bool Contains(const T& aX) {
bool Contains(const ElemType& aInterval) const {
for (const auto& interval : mIntervals) {
if (aInterval.LeftOf(interval)) {
// Will never succeed.
return false;
}
if (interval.Contains(aInterval)) {
return true;
}
}
return false;
}
bool Contains(const T& aX) const {
for (const auto& interval : mIntervals) {
if (interval.Contains(aX)) {
return true;
@ -426,7 +500,7 @@ public:
return false;
}
bool ContainsStrict(const T& aX) {
bool ContainsStrict(const T& aX) const {
for (const auto& interval : mIntervals) {
if (interval.ContainsStrict(aX)) {
return true;
@ -435,45 +509,25 @@ public:
return false;
}
void Normalize()
// Shift all values by aOffset.
void Shift(const T& aOffset)
{
if (mIntervals.Length() >= 2) {
ContainerType normalized;
mIntervals.Sort(CompareIntervals());
// This merges the intervals.
ElemType current(mIntervals[0]);
for (IndexType i = 1; i < mIntervals.Length(); i++) {
if (current.Contains(mIntervals[i])) {
continue;
}
if (current.Intersects(mIntervals[i])) {
current = current.Union(mIntervals[i]);
} else {
normalized.AppendElement(current);
current = mIntervals[i];
}
}
normalized.AppendElement(current);
mIntervals = normalized;
for (auto& interval : mIntervals) {
interval.mStart = interval.mStart + aOffset;
interval.mEnd = interval.mEnd + aOffset;
}
}
// Shift all values by aOffset.
void Shift(T aOffset)
{
void SetFuzz(const T& aFuzz) {
for (auto& interval : mIntervals) {
interval.mStart += aOffset;
interval.mEnd += aOffset;
interval.SetFuzz(aFuzz);
}
Normalize();
}
static const IndexType NoIndex = IndexType(-1);
IndexType Find(T aValue) const
IndexType Find(const T& aValue) const
{
for (IndexType i = 0; i < mIntervals.Length(); i++) {
if (mIntervals[i].Contains(aValue)) {
@ -487,6 +541,31 @@ protected:
ContainerType mIntervals;
private:
void Normalize()
{
if (mIntervals.Length() >= 2) {
ContainerType normalized;
mIntervals.Sort(CompareIntervals());
// This merges the intervals.
ElemType current(mIntervals[0]);
for (IndexType i = 1; i < mIntervals.Length(); i++) {
ElemType& interval = mIntervals[i];
if (current.Touches(interval)) {
current = current.Span(interval);
} else {
normalized.AppendElement(Move(current));
current = Move(interval);
}
}
normalized.AppendElement(Move(current));
mIntervals.Clear();
mIntervals.MoveElementsFrom(Move(normalized));
}
}
struct CompareIntervals
{
bool Equals(const ElemType& aT1, const ElemType& aT2) const

View File

@ -12,7 +12,6 @@
#include "nsTArray.h"
#include "VideoUtils.h"
#include "MediaDecoderStateMachine.h"
#include "mozilla/dom/TimeRanges.h"
#include "ImageContainer.h"
#include "MediaResource.h"
#include "nsError.h"
@ -303,138 +302,6 @@ void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream)
aStream->mStream->ChangeExplicitBlockerCount(-1);
}
MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime,
SourceMediaStream* aStream)
: mAudioFramesWritten(0),
mInitialTime(aInitialTime),
mNextVideoTime(-1),
mNextAudioTime(-1),
mDecoder(aDecoder),
mStreamInitialized(false),
mHaveSentFinish(false),
mHaveSentFinishAudio(false),
mHaveSentFinishVideo(false),
mStream(aStream),
mHaveBlockedForPlayState(false),
mHaveBlockedForStateMachineNotPlaying(false),
mEOSVideoCompensation(false)
{
mListener = new DecodedStreamGraphListener(mStream, this);
mStream->AddListener(mListener);
}
MediaDecoder::DecodedStreamData::~DecodedStreamData()
{
mListener->Forget();
mStream->Destroy();
}
MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream,
DecodedStreamData* aData)
: mData(aData),
mMutex("MediaDecoder::DecodedStreamData::mMutex"),
mStream(aStream),
mLastOutputTime(aStream->
StreamTimeToMicroseconds(aStream->GetCurrentTime())),
mStreamFinishedOnMainThread(false)
{
}
void
MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph,
GraphTime aCurrentTime)
{
MutexAutoLock lock(mMutex);
if (mStream) {
mLastOutputTime = mStream->
StreamTimeToMicroseconds(mStream->GraphTimeToStreamTime(aCurrentTime));
}
}
void
MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished()
{
MutexAutoLock lock(mMutex);
mStreamFinishedOnMainThread = true;
}
void
MediaDecoder::DecodedStreamGraphListener::NotifyEvent(MediaStreamGraph* aGraph,
MediaStreamListener::MediaStreamGraphEvent event)
{
if (event == EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
}
class MediaDecoder::OutputStreamListener : public MediaStreamListener {
public:
OutputStreamListener(MediaDecoder* aDecoder, MediaStream* aStream)
: mDecoder(aDecoder), mStream(aStream) {}
virtual void NotifyEvent(
MediaStreamGraph* aGraph,
MediaStreamListener::MediaStreamGraphEvent event) override {
if (event == EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &OutputStreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget());
}
}
void Forget() {
MOZ_ASSERT(NS_IsMainThread());
mDecoder = nullptr;
}
private:
void DoNotifyFinished() {
MOZ_ASSERT(NS_IsMainThread());
if (!mDecoder) {
return;
}
// Remove the finished stream so it won't block the decoded stream.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
auto& streams = mDecoder->OutputStreams();
// Don't read |mDecoder| in the loop since removing the element will lead
// to ~OutputStreamData() which will call Forget() to reset |mDecoder|.
for (int32_t i = streams.Length() - 1; i >= 0; --i) {
auto& os = streams[i];
MediaStream* p = os.mStream.get();
if (p == mStream.get()) {
if (os.mPort) {
os.mPort->Destroy();
os.mPort = nullptr;
}
streams.RemoveElementAt(i);
break;
}
}
}
// Main thread only
MediaDecoder* mDecoder;
nsRefPtr<MediaStream> mStream;
};
void
MediaDecoder::OutputStreamData::Init(MediaDecoder* aDecoder,
ProcessedMediaStream* aStream)
{
mStream = aStream;
mListener = new OutputStreamListener(aDecoder, aStream);
aStream->AddListener(mListener);
}
MediaDecoder::OutputStreamData::~OutputStreamData()
{
mListener->Forget();
}
void MediaDecoder::UpdateDecodedStream()
{
MOZ_ASSERT(NS_IsMainThread());
@ -1424,22 +1291,21 @@ bool MediaDecoder::IsMediaSeekable()
return mMediaSeekable;
}
nsresult MediaDecoder::GetSeekable(dom::TimeRanges* aSeekable)
media::TimeIntervals MediaDecoder::GetSeekable()
{
double initialTime = 0.0;
// We can seek in buffered range if the media is seekable. Also, we can seek
// in unbuffered ranges if the transport level is seekable (local file or the
// server supports range requests, etc.)
if (!IsMediaSeekable()) {
return NS_OK;
return media::TimeIntervals();
} else if (!IsTransportSeekable()) {
return GetBuffered(aSeekable);
return GetBuffered();
} else {
double end = IsInfinite() ? std::numeric_limits<double>::infinity()
: initialTime + GetDuration();
aSeekable->Add(initialTime, end);
return NS_OK;
return media::TimeIntervals(
media::TimeInterval(media::TimeUnit::FromMicroseconds(0),
IsInfinite() ?
media::TimeUnit::FromInfinity() :
media::TimeUnit::FromSeconds(GetDuration())));
}
}
@ -1585,9 +1451,9 @@ void MediaDecoder::Invalidate()
// Constructs the time ranges representing what segments of the media
// are buffered and playable.
nsresult MediaDecoder::GetBuffered(dom::TimeRanges* aBuffered) {
NS_ENSURE_TRUE(mDecoderStateMachine && !mShuttingDown, NS_ERROR_FAILURE);
return mDecoderStateMachine->GetBuffered(aBuffered);
media::TimeIntervals MediaDecoder::GetBuffered() {
NS_ENSURE_TRUE(mDecoderStateMachine && !mShuttingDown, media::TimeIntervals::Invalid());
return mDecoderStateMachine->GetBuffered();
}
size_t MediaDecoder::SizeOfVideoQueue() {

View File

@ -192,31 +192,23 @@ destroying the MediaDecoder object.
#include "MediaPromise.h"
#include "MediaResource.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/ReentrantMonitor.h"
#include "MediaDecoderOwner.h"
#include "MediaStreamGraph.h"
#include "AbstractMediaDecoder.h"
#include "DecodedStream.h"
#include "StateMirroring.h"
#include "StateWatching.h"
#include "necko-config.h"
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "TimeUnits.h"
class nsIStreamListener;
class nsIPrincipal;
namespace mozilla {
namespace dom {
class TimeRanges;
}
}
namespace mozilla {
namespace layers {
class Image;
} //namespace layers
class VideoFrameContainer;
class MediaDecoderStateMachine;
@ -284,7 +276,6 @@ public:
};
typedef MediaPromise<SeekResolveValue, bool /* aIgnored */, /* IsExclusive = */ true> SeekPromise;
class DecodedStreamGraphListener;
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
@ -404,113 +395,6 @@ public:
// replaying after the input as ended. In the latter case, the new source is
// not connected to streams created by captureStreamUntilEnded.
struct DecodedStreamData {
typedef gfx::IntSize IntSize;
DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime, SourceMediaStream* aStream);
~DecodedStreamData();
// microseconds
bool IsFinished() const {
return mListener->IsFinishedOnMainThread();
}
int64_t GetClock() const {
return mInitialTime + mListener->GetLastOutputTime();
}
// The following group of fields are protected by the decoder's monitor
// and can be read or written on any thread.
// Count of audio frames written to the stream
int64_t mAudioFramesWritten;
// Saved value of aInitialTime. Timestamp of the first audio and/or
// video packet written.
const int64_t mInitialTime; // microseconds
// mNextVideoTime is the end timestamp for the last packet sent to the stream.
// Therefore video packets starting at or after this time need to be copied
// to the output stream.
int64_t mNextVideoTime; // microseconds
int64_t mNextAudioTime; // microseconds
MediaDecoder* mDecoder;
// The last video image sent to the stream. Useful if we need to replicate
// the image.
nsRefPtr<layers::Image> mLastVideoImage;
IntSize mLastVideoImageDisplaySize;
// This is set to true when the stream is initialized (audio and
// video tracks added).
bool mStreamInitialized;
bool mHaveSentFinish;
bool mHaveSentFinishAudio;
bool mHaveSentFinishVideo;
// The decoder is responsible for calling Destroy() on this stream.
// Can be read from any thread.
const nsRefPtr<SourceMediaStream> mStream;
// Can be read from any thread.
nsRefPtr<DecodedStreamGraphListener> mListener;
// True when we've explicitly blocked this stream because we're
// not in PLAY_STATE_PLAYING. Used on the main thread only.
bool mHaveBlockedForPlayState;
// We also have an explicit blocker on the stream when
// mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
bool mHaveBlockedForStateMachineNotPlaying;
// True if we need to send a compensation video frame to ensure the
// StreamTime going forward.
bool mEOSVideoCompensation;
};
class DecodedStreamGraphListener : public MediaStreamListener {
public:
DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) override;
virtual void NotifyEvent(MediaStreamGraph* aGraph,
MediaStreamListener::MediaStreamGraphEvent event) override;
void DoNotifyFinished();
int64_t GetLastOutputTime() // microseconds
{
MutexAutoLock lock(mMutex);
return mLastOutputTime;
}
void Forget()
{
NS_ASSERTION(NS_IsMainThread(), "Main thread only");
mData = nullptr;
MutexAutoLock lock(mMutex);
mStream = nullptr;
}
bool IsFinishedOnMainThread()
{
MutexAutoLock lock(mMutex);
return mStreamFinishedOnMainThread;
}
private:
// Main thread only
DecodedStreamData* mData;
Mutex mMutex;
// Protected by mMutex
nsRefPtr<MediaStream> mStream;
// Protected by mMutex
int64_t mLastOutputTime; // microseconds
// Protected by mMutex
bool mStreamFinishedOnMainThread;
};
class OutputStreamListener;
struct OutputStreamData {
void Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream);
~OutputStreamData();
nsRefPtr<ProcessedMediaStream> mStream;
// mPort connects mDecodedStream->mStream to our mStream.
nsRefPtr<MediaInputPort> mPort;
nsRefPtr<OutputStreamListener> mListener;
};
/**
* Connects mDecodedStream->mStream to aStream->mStream.
*/
@ -637,7 +521,7 @@ public:
virtual bool IsTransportSeekable() override;
// Return the time ranges that can be seeked into.
virtual nsresult GetSeekable(dom::TimeRanges* aSeekable);
virtual media::TimeIntervals GetSeekable();
// Set the end time of the media resource. When playback reaches
// this point the media pauses. aTime is in seconds.
@ -695,7 +579,7 @@ public:
// Constructs the time ranges representing what segments of the media
// are buffered and playable.
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered);
virtual media::TimeIntervals GetBuffered();
// Returns the size, in bytes, of the heap memory used by the currently
// queued decoded video and audio data.

View File

@ -147,8 +147,8 @@ MediaDecoderReader::SetStartTime(int64_t aStartTime)
mStartTime = aStartTime;
}
nsresult
MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered)
media::TimeIntervals
MediaDecoderReader::GetBuffered()
{
AutoPinned<MediaResource> stream(mDecoder->GetResource());
int64_t durationUs = 0;
@ -156,8 +156,7 @@ MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered)
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
durationUs = mDecoder->GetMediaDuration();
}
GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
return NS_OK;
return GetEstimatedBufferedTimeRanges(stream, durationUs);
}
int64_t

View File

@ -12,13 +12,10 @@
#include "MediaPromise.h"
#include "MediaQueue.h"
#include "AudioCompactor.h"
#include "TimeUnits.h"
namespace mozilla {
namespace dom {
class TimeRanges;
}
class MediaDecoderReader;
class SharedDecoderManager;
@ -226,7 +223,7 @@ public:
// The OggReader relies on this base implementation not performing I/O,
// since in FirefoxOS we can't do I/O on the main thread, where this is
// called.
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered);
virtual media::TimeIntervals GetBuffered();
virtual int64_t ComputeStartTime(const VideoData* aVideo, const AudioData* aAudio);

View File

@ -21,7 +21,7 @@
#include "mozilla/MathAlgorithms.h"
#include "mozilla/mozalloc.h"
#include "VideoUtils.h"
#include "mozilla/dom/TimeRanges.h"
#include "TimeUnits.h"
#include "nsDeque.h"
#include "AudioSegment.h"
#include "VideoSegment.h"
@ -38,6 +38,7 @@
#include "gfx2DGlue.h"
#include "nsPrintfCString.h"
#include "DOMMediaStream.h"
#include "DecodedStream.h"
#include <algorithm>
@ -1712,17 +1713,13 @@ void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
//
// Make sure to only do this if we have a start time, otherwise the reader
// doesn't know how to compute GetBuffered.
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
if (mDecoder->IsInfinite() && (mStartTime != -1) &&
NS_SUCCEEDED(mDecoder->GetBuffered(buffered)))
{
uint32_t length = 0;
buffered->GetLength(&length);
if (length) {
double end = 0;
buffered->End(length - 1, &end);
media::TimeIntervals buffered{mDecoder->GetBuffered()};
if (mDecoder->IsInfinite() && (mStartTime != -1) && !buffered.IsInvalid()) {
bool exists;
media::TimeUnit end{buffered.GetEnd(&exists)};
if (exists) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mEndTime = std::max<int64_t>(mEndTime, end * USECS_PER_S);
mEndTime = std::max<int64_t>(mEndTime, end.ToMicroseconds());
}
}
}
@ -2148,9 +2145,10 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
return false;
}
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
nsresult rv = mReader->GetBuffered(buffered.get());
NS_ENSURE_SUCCESS(rv, false);
media::TimeIntervals buffered{mReader->GetBuffered()};
if (buffered.IsInvalid()) {
return false;
}
int64_t endOfDecodedVideoData = INT64_MAX;
if (HasVideo() && !VideoQueue().AtEndOfStream()) {
@ -2164,10 +2162,13 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
endOfDecodedAudioData = mDecodedAudioEndTime;
}
int64_t endOfDecodedData = std::min(endOfDecodedVideoData, endOfDecodedAudioData);
return endOfDecodedData != INT64_MAX &&
!buffered->Contains(static_cast<double>(endOfDecodedData) / USECS_PER_S,
static_cast<double>(std::min(endOfDecodedData + aUsecs, GetDuration())) / USECS_PER_S);
if (GetDuration() < endOfDecodedData) {
// Our duration is not up to date. No point buffering.
return false;
}
media::TimeInterval interval(media::TimeUnit::FromMicroseconds(endOfDecodedData),
media::TimeUnit::FromMicroseconds(std::min(endOfDecodedData + aUsecs, GetDuration())));
return endOfDecodedData != INT64_MAX && !buffered.Contains(interval);
}
void

View File

@ -98,6 +98,7 @@ namespace mozilla {
class AudioSegment;
class MediaTaskQueue;
class AudioSink;
class DecodedStreamData;
/*
The state machine class. This manages the decoding and seeking in the
@ -117,10 +118,9 @@ class MediaDecoderStateMachine
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine)
public:
typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
typedef MediaDecoder::DecodedStreamData DecodedStreamData;
MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderReader* aReader,
bool aRealTime = false);
MediaDecoderReader* aReader,
bool aRealTime = false);
nsresult Init(MediaDecoderStateMachine* aCloneDonor);
@ -277,16 +277,16 @@ public:
return mState == DECODER_STATE_SEEKING;
}
nsresult GetBuffered(dom::TimeRanges* aBuffered) {
media::TimeIntervals GetBuffered() {
// It's possible for JS to query .buffered before we've determined the start
// time from metadata, in which case the reader isn't ready to be asked this
// question.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mStartTime < 0) {
return NS_OK;
return media::TimeIntervals();
}
return mReader->GetBuffered(aBuffered);
return mReader->GetBuffered();
}
size_t SizeOfVideoQueue() {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,403 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(MediaFormatReader_h_)
#define MediaFormatReader_h_
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/Monitor.h"
#include "MediaDataDemuxer.h"
#include "MediaDecoderReader.h"
#include "MediaTaskQueue.h"
#include "PlatformDecoderModule.h"
namespace mozilla {
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
#define READER_DORMANT_HEURISTIC
#else
#undef READER_DORMANT_HEURISTIC
#endif
class MediaFormatReader final : public MediaDecoderReader
{
typedef TrackInfo::TrackType TrackType;
public:
explicit MediaFormatReader(AbstractMediaDecoder* aDecoder,
MediaDataDemuxer* aDemuxer);
virtual ~MediaFormatReader();
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
virtual size_t SizeOfVideoQueueInFrames() override;
virtual size_t SizeOfAudioQueueInFrames() override;
virtual nsRefPtr<VideoDataPromise>
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) override;
virtual nsRefPtr<AudioDataPromise> RequestAudioData() override;
bool HasVideo() override
{
return mInfo.HasVideo();
}
bool HasAudio() override
{
return mInfo.HasAudio();
}
virtual nsRefPtr<MetadataPromise> AsyncReadMetadata() override;
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) override
{
// Unused as we provide AsyncReadMetadataAPI.
// However we must implement it as it's pure virtual.
return NS_OK;
}
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) override;
virtual nsRefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aUnused) override;
virtual bool IsMediaSeekable() override
{
return mSeekable;
}
virtual int64_t GetEvictionOffset(double aTime) override;
virtual void NotifyDataArrived(const char* aBuffer,
uint32_t aLength,
int64_t aOffset) override;
virtual media::TimeIntervals GetBuffered() override;
// For Media Resource Management
virtual void SetIdle() override;
virtual bool IsDormantNeeded() override;
virtual void ReleaseMediaResources() override;
virtual void SetSharedDecoderManager(SharedDecoderManager* aManager)
override;
virtual nsresult ResetDecode() override;
virtual nsRefPtr<ShutdownPromise> Shutdown() override;
virtual bool IsAsync() const override { return true; }
virtual bool VideoIsHardwareAccelerated() const override;
virtual void DisableHardwareAcceleration() override;
virtual bool IsWaitForDataSupported() override { return true; }
virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) override;
virtual bool IsWaitingOnCDMResource() override;
private:
bool InitDemuxer();
void NotifyDemuxer(uint32_t aLength, int64_t aOffset);
void ReturnOutput(MediaData* aData, TrackType aTrack);
bool EnsureDecodersSetup();
// Enqueues a task to call Update(aTrack) on the decoder task queue.
// Lock for corresponding track must be held.
void ScheduleUpdate(TrackType aTrack);
void Update(TrackType aTrack);
// Handle actions should more data be received.
// Returns true if no more action is required.
bool UpdateReceivedNewData(TrackType aTrack);
// Called when new samples need to be demuxed.
void RequestDemuxSamples(TrackType aTrack);
// Decode any pending already demuxed samples.
void DecodeDemuxedSamples(TrackType aTrack,
AbstractMediaDecoder::AutoNotifyDecoded& aA);
void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
void NotifyInputExhausted(TrackType aTrack);
void NotifyDrainComplete(TrackType aTrack);
void NotifyError(TrackType aTrack);
void NotifyWaitingForData(TrackType aTrack);
void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
// Initializes mLayersBackendType if possible.
void InitLayersBackendType();
// DecoderCallback proxies the MediaDataDecoderCallback calls to these
// functions.
void Output(TrackType aType, MediaData* aSample);
void InputExhausted(TrackType aTrack);
void Error(TrackType aTrack);
void Flush(TrackType aTrack);
void DrainComplete(TrackType aTrack);
bool IsSupportedAudioMimeType(const nsACString& aMimeType);
bool IsSupportedVideoMimeType(const nsACString& aMimeType);
bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
size_t SizeOfQueue(TrackType aTrack);
nsRefPtr<MediaDataDemuxer> mDemuxer;
nsRefPtr<PlatformDecoderModule> mPlatform;
class DecoderCallback : public MediaDataDecoderCallback {
public:
DecoderCallback(MediaFormatReader* aReader, TrackType aType)
: mReader(aReader)
, mType(aType)
{
}
virtual void Output(MediaData* aSample) override {
mReader->Output(mType, aSample);
}
virtual void InputExhausted() override {
mReader->InputExhausted(mType);
}
virtual void Error() override {
mReader->Error(mType);
}
virtual void DrainComplete() override {
mReader->DrainComplete(mType);
}
virtual void ReleaseMediaResources() override {
mReader->ReleaseMediaResources();
}
virtual bool OnReaderTaskQueue() override {
return mReader->OnTaskQueue();
}
private:
MediaFormatReader* mReader;
TrackType mType;
};
struct DecoderData {
DecoderData(MediaFormatReader* aOwner,
MediaData::Type aType,
uint32_t aDecodeAhead)
: mOwner(aOwner)
, mType(aType)
, mDecodeAhead(aDecodeAhead)
, mUpdateScheduled(false)
, mDemuxEOS(false)
, mDemuxEOSServiced(false)
, mWaitingForData(false)
, mReceivedNewData(false)
, mDiscontinuity(true)
, mOutputRequested(false)
, mInputExhausted(false)
, mError(false)
, mDrainComplete(false)
, mNumSamplesInput(0)
, mNumSamplesOutput(0)
, mSizeOfQueue(0)
, mMonitor(aType == MediaData::AUDIO_DATA ? "audio decoder data"
: "video decoder data")
{}
MediaFormatReader* mOwner;
// Disambiguate Audio vs Video.
MediaData::Type mType;
nsRefPtr<MediaTrackDemuxer> mTrackDemuxer;
// The platform decoder.
nsRefPtr<MediaDataDecoder> mDecoder;
// TaskQueue on which decoder can choose to decode.
// Only non-null up until the decoder is created.
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
// Callback that receives output and error notifications from the decoder.
nsAutoPtr<DecoderCallback> mCallback;
// Only accessed from reader's task queue.
uint32_t mDecodeAhead;
bool mUpdateScheduled;
bool mDemuxEOS;
bool mDemuxEOSServiced;
bool mWaitingForData;
bool mReceivedNewData;
bool mDiscontinuity;
// Queued demux samples waiting to be decoded.
nsTArray<nsRefPtr<MediaRawData>> mQueuedSamples;
MediaPromiseConsumerHolder<MediaTrackDemuxer::SamplesPromise> mDemuxRequest;
MediaPromiseHolder<WaitForDataPromise> mWaitingPromise;
bool HasWaitingPromise()
{
MOZ_ASSERT(mOwner->OnTaskQueue());
return !mWaitingPromise.IsEmpty();
}
// MediaDataDecoder handler's variables.
bool mOutputRequested;
bool mInputExhausted;
bool mError;
bool mDrainComplete;
// Decoded samples returned my mDecoder awaiting being returned to
// state machine upon request.
nsTArray<nsRefPtr<MediaData>> mOutput;
uint64_t mNumSamplesInput;
uint64_t mNumSamplesOutput;
// These get overriden in the templated concrete class.
// Indicate if we have a pending promise for decoded frame.
virtual bool HasPromise() = 0;
virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
const char* aMethodName) = 0;
void ResetState()
{
MOZ_ASSERT(mOwner->OnTaskQueue());
mDemuxEOS = false;
mDemuxEOSServiced = false;
mWaitingForData = false;
mReceivedNewData = false;
mDiscontinuity = true;
mQueuedSamples.Clear();
mOutputRequested = false;
mInputExhausted = false;
mDrainComplete = false;
mOutput.Clear();
mNumSamplesInput = 0;
mNumSamplesOutput = 0;
}
// Used by the MDSM for logging purposes.
Atomic<size_t> mSizeOfQueue;
// Monitor that protects all non-threadsafe state; the primitives
// that follow.
Monitor mMonitor;
media::TimeIntervals mTimeRanges;
};
template<typename PromiseType>
struct DecoderDataWithPromise : public DecoderData {
DecoderDataWithPromise(MediaFormatReader* aOwner,
MediaData::Type aType,
uint32_t aDecodeAhead) :
DecoderData(aOwner, aType, aDecodeAhead)
{}
MediaPromiseHolder<PromiseType> mPromise;
bool HasPromise() override
{
MOZ_ASSERT(mOwner->OnTaskQueue());
return !mPromise.IsEmpty();
}
void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
const char* aMethodName) override
{
MOZ_ASSERT(mOwner->OnTaskQueue());
mPromise.Reject(aReason, aMethodName);
}
};
DecoderDataWithPromise<AudioDataPromise> mAudio;
DecoderDataWithPromise<VideoDataPromise> mVideo;
// Returns true when the decoder for this track needs input.
// aDecoder.mMonitor must be locked.
bool NeedInput(DecoderData& aDecoder);
DecoderData& GetDecoderData(TrackType aTrack);
// Demuxer objects.
void OnDemuxerInitDone(nsresult);
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
MediaPromiseConsumerHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
void OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure);
void DoDemuxVideo();
void OnVideoDemuxCompleted(nsRefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
void OnVideoDemuxFailed(DemuxerFailureReason aFailure)
{
OnDemuxFailed(TrackType::kVideoTrack, aFailure);
}
void DoDemuxAudio();
void OnAudioDemuxCompleted(nsRefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
void OnAudioDemuxFailed(DemuxerFailureReason aFailure)
{
OnDemuxFailed(TrackType::kAudioTrack, aFailure);
}
void SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold);
MediaPromiseConsumerHolder<MediaTrackDemuxer::SkipAccessPointPromise> mSkipRequest;
void OnVideoSkipCompleted(uint32_t aSkipped);
void OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure);
// The last number of decoded output frames that we've reported to
// MediaDecoder::NotifyDecoded(). We diff the number of output video
// frames every time that DecodeVideoData() is called, and report the
// delta there.
uint64_t mLastReportedNumDecodedFrames;
layers::LayersBackend mLayersBackendType;
// Metadata objects
// True if we've read the streams' metadata.
bool mInitDone;
MediaPromiseHolder<MetadataPromise> mMetadataPromise;
// Accessed from multiple thread, in particular the MediaDecoderStateMachine,
// however the value doesn't change after reading the metadata.
bool mSeekable;
bool IsEncrypted()
{
return mIsEncrypted;
}
// Accessed from multiple thread, in particular the MediaDecoderStateMachine,
// however the value doesn't currently change after reading the metadata.
// TODO handle change of encryption half-way. The above assumption will then
// become incorrect.
bool mIsEncrypted;
// Seeking objects.
bool IsSeeking() const { return mPendingSeekTime.isSome(); }
void AttemptSeek();
void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
void DoVideoSeek();
void OnVideoSeekCompleted(media::TimeUnit aTime);
void OnVideoSeekFailed(DemuxerFailureReason aFailure)
{
OnSeekFailed(TrackType::kVideoTrack, aFailure);
}
void DoAudioSeek();
void OnAudioSeekCompleted(media::TimeUnit aTime);
void OnAudioSeekFailed(DemuxerFailureReason aFailure)
{
OnSeekFailed(TrackType::kAudioTrack, aFailure);
}
// Temporary seek information while we wait for the data
Maybe<media::TimeUnit> mPendingSeekTime;
MediaPromiseConsumerHolder<MediaTrackDemuxer::SeekPromise> mVideoSeekRequest;
MediaPromiseConsumerHolder<MediaTrackDemuxer::SeekPromise> mAudioSeekRequest;
MediaPromiseHolder<SeekPromise> mSeekPromise;
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mCDMProxy;
#endif
nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
// Main thread objects
nsRefPtr<MediaDataDemuxer> mMainThreadDemuxer;
nsRefPtr<MediaTrackDemuxer> mAudioTrackDemuxer;
nsRefPtr<MediaTrackDemuxer> mVideoTrackDemuxer;
#if defined(READER_DORMANT_HEURISTIC)
const bool mDormantEnabled;
#endif
};
} // namespace mozilla
#endif

View File

@ -8,7 +8,6 @@
#define TIME_UNITS_H
#include "Intervals.h"
#include "VideoUtils.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/dom/TimeRanges.h"
@ -16,6 +15,15 @@
namespace mozilla {
namespace media {
// Number of microseconds per second. 1e6.
static const int64_t USECS_PER_S = 1000000;
// Number of microseconds per millisecond.
static const int64_t USECS_PER_MS = 1000;
// Number of nanoseconds per second. 1e9.
static const int64_t NSECS_PER_S = 1000000000;
struct Microseconds {
Microseconds()
: mValue(0)
@ -61,11 +69,16 @@ struct Microseconds {
int64_t mValue;
};
// TimeUnit at present uses a CheckedInt64 as storage.
// INT64_MAX has the special meaning of being +oo.
class TimeUnit final {
public:
static TimeUnit FromSeconds(double aValue) {
MOZ_ASSERT(!IsNaN(aValue));
if (mozilla::IsInfinite<double>(aValue)) {
return FromInfinity();
}
double val = aValue * USECS_PER_S;
if (val >= double(INT64_MAX)) {
return FromMicroseconds(INT64_MAX);
@ -84,14 +97,33 @@ public:
return TimeUnit(aValue.mValue);
}
static TimeUnit FromNanoseconds(int64_t aValue) {
return TimeUnit(aValue / 1000);
}
static TimeUnit FromInfinity() {
return TimeUnit(INT64_MAX);
}
int64_t ToMicroseconds() const {
return mValue.value();
}
int64_t ToNanoseconds() const {
return mValue.value() * 1000;
}
double ToSeconds() const {
if (IsInfinite()) {
return PositiveInfinity<double>();
}
return double(mValue.value()) / USECS_PER_S;
}
bool IsInfinite() const {
return mValue.value() == INT64_MAX;
}
bool operator == (const TimeUnit& aOther) const {
MOZ_ASSERT(IsValid() && aOther.IsValid());
return mValue.value() == aOther.mValue.value();
@ -111,9 +143,16 @@ public:
return !(*this >= aOther);
}
TimeUnit operator + (const TimeUnit& aOther) const {
if (IsInfinite() || aOther.IsInfinite()) {
return FromInfinity();
}
return TimeUnit(mValue + aOther.mValue);
}
TimeUnit operator - (const TimeUnit& aOther) const {
if (IsInfinite() && !aOther.IsInfinite()) {
return FromInfinity();
}
MOZ_ASSERT(!IsInfinite() && !aOther.IsInfinite());
return TimeUnit(mValue - aOther.mValue);
}
@ -175,6 +214,17 @@ public:
: BaseType(Move(aOther))
{}
static TimeIntervals Invalid()
{
return TimeIntervals(TimeInterval(TimeUnit::FromMicroseconds(INT64_MIN),
TimeUnit::FromMicroseconds(INT64_MIN)));
}
bool IsInvalid()
{
return Length() == 1 && Start(0).ToMicroseconds() == INT64_MIN &&
End(0).ToMicroseconds() == INT64_MIN;
}
TimeIntervals() = default;
// Make TimeIntervals interchangeable with dom::TimeRanges.

View File

@ -4,7 +4,7 @@
#include "VideoUtils.h"
#include "MediaResource.h"
#include "mozilla/dom/TimeRanges.h"
#include "TimeUnits.h"
#include "nsMathUtils.h"
#include "nsSize.h"
#include "VorbisUtils.h"
@ -70,18 +70,20 @@ static int64_t BytesToTime(int64_t offset, int64_t length, int64_t durationUs) {
return int64_t(double(durationUs) * r);
}
void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
int64_t aDurationUsecs,
mozilla::dom::TimeRanges* aOutBuffered)
media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
int64_t aDurationUsecs)
{
media::TimeIntervals buffered;
// Nothing to cache if the media takes 0us to play.
if (aDurationUsecs <= 0 || !aStream || !aOutBuffered)
return;
if (aDurationUsecs <= 0 || !aStream)
return buffered;
// Special case completely cached files. This also handles local files.
if (aStream->IsDataCachedToEndOfResource(0)) {
aOutBuffered->Add(0, double(aDurationUsecs) / USECS_PER_S);
return;
buffered +=
media::TimeInterval(media::TimeUnit::FromMicroseconds(0),
media::TimeUnit::FromMicroseconds(aDurationUsecs));
return buffered;
}
int64_t totalBytes = aStream->GetLength();
@ -90,7 +92,7 @@ void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
// buffered. This will put us in a state of eternally-low-on-undecoded-data
// which is not great, but about the best we can do.
if (totalBytes <= 0)
return;
return buffered;
int64_t startOffset = aStream->GetNextCachedData(0);
while (startOffset >= 0) {
@ -102,12 +104,14 @@ void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
int64_t startUs = BytesToTime(startOffset, totalBytes, aDurationUsecs);
int64_t endUs = BytesToTime(endOffset, totalBytes, aDurationUsecs);
if (startUs != endUs) {
aOutBuffered->Add(double(startUs) / USECS_PER_S,
double(endUs) / USECS_PER_S);
buffered +=
media::TimeInterval(media::TimeUnit::FromMicroseconds(startUs),
media::TimeUnit::FromMicroseconds(endUs));
}
startOffset = aStream->GetNextCachedData(endOffset);
}
return;
return buffered;
}
int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,

View File

@ -23,6 +23,7 @@
#include "prtime.h"
#include "AudioSampleFormat.h"
#include "mozilla/RefPtr.h"
#include "TimeUnits.h"
using mozilla::CheckedInt64;
using mozilla::CheckedUint64;
@ -115,19 +116,14 @@ void DeleteOnMainThread(nsAutoPtr<T>& aObject) {
class MediaResource;
namespace dom {
class TimeRanges;
}
// Estimates the buffered ranges of a MediaResource using a simple
// (byteOffset/length)*duration method. Probably inaccurate, but won't
// do file I/O, and can be used when we don't have detailed knowledge
// of the byte->time mapping of a resource. aDurationUsecs is the duration
// of the media in microseconds. Estimated buffered ranges are stored in
// aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
int64_t aDurationUsecs,
mozilla::dom::TimeRanges* aOutBuffered);
media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
int64_t aDurationUsecs);
// Converts from number of audio frames (aFrames) to microseconds, given
// the specified audio rate (aRate). Stores result in aOutUsecs. Returns true

View File

@ -4,7 +4,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Preferences.h"
#include "mozilla/dom/TimeRanges.h"
#include "MediaResource.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "AndroidMediaPluginHost.h"

Some files were not shown because too many files have changed in this diff Show More