mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
a2e9431125
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
},
|
||||
];
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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']
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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'
|
@ -423,7 +423,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/xslt/xpath',
|
||||
'/dom/xul',
|
||||
'/gfx/2d',
|
||||
'/image/src',
|
||||
'/image',
|
||||
'/js/xpconnect/src',
|
||||
'/js/xpconnect/wrappers',
|
||||
'/layout/base',
|
||||
|
@ -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' }},
|
||||
|
@ -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
|
||||
|
76
dom/base/test/referrerHelper.js
Normal file
76
dom/base/test/referrerHelper.js
Normal 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();
|
||||
});
|
||||
}
|
@ -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;
|
||||
|
44
dom/base/test/test_bug1163743.html
Normal file
44
dom/base/test/test_bug1163743.html
Normal 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>
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
@ -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 attribute’s
|
||||
@ -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>
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -27,4 +27,3 @@ support-files = file_NestedFramesOuter.html
|
||||
[test_WebGLContextLost.html]
|
||||
disabled = bug 865844
|
||||
support-files = file_WebGLContextLost.html
|
||||
[test_DelayedBackgroundTransition.html]
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
2
dom/cache/QuotaClient.cpp
vendored
2
dom/cache/QuotaClient.cpp
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -142,7 +142,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/svg',
|
||||
'/dom/xul',
|
||||
'/gfx/gl',
|
||||
'/image/src',
|
||||
'/image',
|
||||
'/js/xpconnect/src',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
|
174
dom/canvas/test/captureStream_common.js
Normal file
174
dom/canvas/test/captureStream_common.js
Normal 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);
|
||||
};
|
@ -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>
|
||||
|
@ -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>
|
||||
|
BIN
dom/canvas/test/image_red_crossorigin_credentials.png
Normal file
BIN
dom/canvas/test/image_red_crossorigin_credentials.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 87 B |
@ -0,0 +1,2 @@
|
||||
Access-Control-Allow-Origin: http://mochi.test:8888
|
||||
Access-Control-Allow-Credentials: true
|
@ -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]
|
||||
|
111
dom/canvas/test/test_capture.html
Normal file
111
dom/canvas/test/test_capture.html
Normal 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>
|
||||
|
@ -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]
|
||||
|
200
dom/canvas/test/webgl-mochitest/test_capture.html
Normal file
200
dom/canvas/test/webgl-mochitest/test_capture.html
Normal 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>
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
341
dom/media/CanvasCaptureMediaStream.cpp
Normal file
341
dom/media/CanvasCaptureMediaStream.cpp
Normal 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
|
||||
|
108
dom/media/CanvasCaptureMediaStream.h
Normal file
108
dom/media/CanvasCaptureMediaStream.h
Normal 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_ */
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
187
dom/media/DecodedStream.cpp
Normal 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
99
dom/media/DecodedStream.h
Normal 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_
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
1367
dom/media/MediaFormatReader.cpp
Normal file
1367
dom/media/MediaFormatReader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
403
dom/media/MediaFormatReader.h
Normal file
403
dom/media/MediaFormatReader.h
Normal 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
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user