mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 00:55:37 +00:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
8d03d43e97
@ -63,17 +63,16 @@ add_task(function* test() {
|
||||
});
|
||||
}
|
||||
|
||||
// make sure we have received a message
|
||||
yield ContentTask.spawn(receiver.browser, channelName,
|
||||
function* (name) {
|
||||
yield content.window.testPromise.then(function() {});
|
||||
}
|
||||
);
|
||||
|
||||
// Since sender1 sends before sender2, if the title is exactly
|
||||
// sender2's message, sender1's message must've been blocked
|
||||
is(receiver.browser.contentDocument.title, sender2.message,
|
||||
"should only receive messages from the same user context");
|
||||
yield ContentTask.spawn(receiver.browser, sender2.message,
|
||||
function* (message) {
|
||||
yield content.window.testPromise.then(function() {
|
||||
is(content.document.title, message,
|
||||
"should only receive messages from the same user context");
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
gBrowser.removeTab(sender1.tab);
|
||||
gBrowser.removeTab(sender2.tab);
|
||||
|
@ -35,7 +35,6 @@ support-files =
|
||||
[browser_ext_contextMenus_urlPatterns.js]
|
||||
[browser_ext_currentWindow.js]
|
||||
[browser_ext_getViews.js]
|
||||
[browser_ext_history.js]
|
||||
[browser_ext_incognito_popup.js]
|
||||
[browser_ext_lastError.js]
|
||||
[browser_ext_optionsPage_privileges.js]
|
||||
|
@ -1,3 +1,7 @@
|
||||
{
|
||||
"extends": "../../../../../testing/xpcshell/xpcshell.eslintrc",
|
||||
|
||||
"globals": {
|
||||
"browser": false,
|
||||
},
|
||||
}
|
||||
|
@ -2,12 +2,24 @@
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
/* exported createHttpServer */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
|
||||
"resource://gre/modules/Extension.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData",
|
||||
"resource://gre/modules/Extension.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
|
||||
"resource://gre/modules/ExtensionManagement.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestUtils",
|
||||
"resource://testing-common/ExtensionXPCShellUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
|
||||
"resource://testing-common/httpd.js");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
@ -15,36 +27,29 @@ XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
/* exported normalizeManifest */
|
||||
ExtensionTestUtils.init(this);
|
||||
|
||||
let BASE_MANIFEST = {
|
||||
"applications": {"gecko": {"id": "test@web.ext"}},
|
||||
|
||||
"manifest_version": 2,
|
||||
/**
|
||||
* Creates a new HttpServer for testing, and begins listening on the
|
||||
* specified port. Automatically shuts down the server when the test
|
||||
* unit ends.
|
||||
*
|
||||
* @param {integer} [port]
|
||||
* The port to listen on. If omitted, listen on a random
|
||||
* port. The latter is the preferred behavior.
|
||||
*
|
||||
* @returns {HttpServer}
|
||||
*/
|
||||
function createHttpServer(port = -1) {
|
||||
let server = new HttpServer();
|
||||
server.start(port);
|
||||
|
||||
"name": "name",
|
||||
"version": "0",
|
||||
};
|
||||
do_register_cleanup(() => {
|
||||
return new Promise(resolve => {
|
||||
server.stop(resolve);
|
||||
});
|
||||
});
|
||||
|
||||
function* normalizeManifest(manifest, baseManifest = BASE_MANIFEST) {
|
||||
const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
yield Management.lazyInit();
|
||||
|
||||
let errors = [];
|
||||
let context = {
|
||||
url: null,
|
||||
|
||||
logError: error => {
|
||||
errors.push(error);
|
||||
},
|
||||
|
||||
preprocessors: {},
|
||||
};
|
||||
|
||||
manifest = Object.assign({}, baseManifest, manifest);
|
||||
|
||||
let normalized = Schemas.normalize(manifest, "manifest.WebExtensionManifest", context);
|
||||
normalized.errors = errors;
|
||||
|
||||
return normalized;
|
||||
return server;
|
||||
}
|
||||
|
@ -1,16 +1,5 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebExtension test</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
function backgroundScript() {
|
||||
@ -276,13 +265,13 @@ function backgroundScript() {
|
||||
return browser.bookmarks.search("Menu Item");
|
||||
}).then(results => {
|
||||
browser.test.assertEq(1, results.length, "Expected number of results returned for menu item search");
|
||||
checkBookmark({title: "Menu Item", url: "http://menu.org/", index: 3, parentId: bookmarkGuids.menuGuid}, results[0]);
|
||||
checkBookmark({title: "Menu Item", url: "http://menu.org/", index: 0, parentId: bookmarkGuids.menuGuid}, results[0]);
|
||||
|
||||
// finds toolbar items
|
||||
return browser.bookmarks.search("Toolbar Item");
|
||||
}).then(results => {
|
||||
browser.test.assertEq(1, results.length, "Expected number of results returned for toolbar item search");
|
||||
checkBookmark({title: "Toolbar Item", url: "http://toolbar.org/", index: 2, parentId: bookmarkGuids.toolbarGuid}, results[0]);
|
||||
checkBookmark({title: "Toolbar Item", url: "http://toolbar.org/", index: 0, parentId: bookmarkGuids.toolbarGuid}, results[0]);
|
||||
|
||||
// finds folders
|
||||
return browser.bookmarks.search("Mozilla Folder");
|
||||
@ -389,7 +378,7 @@ function backgroundScript() {
|
||||
return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.menuGuid});
|
||||
}).then(result => {
|
||||
browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
|
||||
browser.test.assertEq(childCount + 1, result.index, "Bookmark has the expected index");
|
||||
browser.test.assertEq(childCount, result.index, "Bookmark has the expected index");
|
||||
|
||||
return browser.bookmarks.move(corporationBookmark.id, {index: 1});
|
||||
}).then(result => {
|
||||
@ -490,8 +479,3 @@ add_task(function* test_contentscript() {
|
||||
yield extension.awaitFinish("bookmarks");
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -85,14 +85,14 @@ add_task(function* test_delete() {
|
||||
}
|
||||
|
||||
yield PlacesUtils.history.insertMany(visits);
|
||||
is(yield PlacesTestUtils.visitsInDB(visits[0].url), 5, "5 visits for uri found in history database");
|
||||
equal((yield PlacesTestUtils.visitsInDB(visits[0].url)), 5, "5 visits for uri found in history database");
|
||||
|
||||
let testUrl = visits[2].url;
|
||||
ok(yield PlacesTestUtils.isPageInDB(testUrl), "expected url found in history database");
|
||||
ok((yield PlacesTestUtils.isPageInDB(testUrl)), "expected url found in history database");
|
||||
|
||||
extension.sendMessage("delete-url", testUrl);
|
||||
yield extension.awaitMessage("url-deleted");
|
||||
is(yield PlacesTestUtils.isPageInDB(testUrl), false, "expected url not found in history database");
|
||||
equal((yield PlacesTestUtils.isPageInDB(testUrl)), false, "expected url not found in history database");
|
||||
|
||||
// delete 3 of the 5 visits for url 1
|
||||
let filter = {
|
||||
@ -103,10 +103,10 @@ add_task(function* test_delete() {
|
||||
extension.sendMessage("delete-range", filter);
|
||||
let removedUrls = yield extension.awaitMessage("range-deleted");
|
||||
ok(!removedUrls.includes(visits[0].url), `${visits[0].url} not received by onVisitRemoved`);
|
||||
ok(yield PlacesTestUtils.isPageInDB(visits[0].url), "expected uri found in history database");
|
||||
is(yield PlacesTestUtils.visitsInDB(visits[0].url), 2, "2 visits for uri found in history database");
|
||||
ok(yield PlacesTestUtils.isPageInDB(visits[1].url), "expected uri found in history database");
|
||||
is(yield PlacesTestUtils.visitsInDB(visits[1].url), 1, "1 visit for uri found in history database");
|
||||
ok((yield PlacesTestUtils.isPageInDB(visits[0].url)), "expected uri found in history database");
|
||||
equal((yield PlacesTestUtils.visitsInDB(visits[0].url)), 2, "2 visits for uri found in history database");
|
||||
ok((yield PlacesTestUtils.isPageInDB(visits[1].url)), "expected uri found in history database");
|
||||
equal((yield PlacesTestUtils.visitsInDB(visits[1].url)), 1, "1 visit for uri found in history database");
|
||||
|
||||
// delete the rest of the visits for url 1, and the visit for url 2
|
||||
filter.startTime = visits[0].visits[0].date;
|
||||
@ -115,18 +115,18 @@ add_task(function* test_delete() {
|
||||
extension.sendMessage("delete-range", filter);
|
||||
yield extension.awaitMessage("range-deleted");
|
||||
|
||||
is(yield PlacesTestUtils.isPageInDB(visits[0].url), false, "expected uri not found in history database");
|
||||
is(yield PlacesTestUtils.visitsInDB(visits[0].url), 0, "0 visits for uri found in history database");
|
||||
is(yield PlacesTestUtils.isPageInDB(visits[1].url), false, "expected uri not found in history database");
|
||||
is(yield PlacesTestUtils.visitsInDB(visits[1].url), 0, "0 visits for uri found in history database");
|
||||
equal((yield PlacesTestUtils.isPageInDB(visits[0].url)), false, "expected uri not found in history database");
|
||||
equal((yield PlacesTestUtils.visitsInDB(visits[0].url)), 0, "0 visits for uri found in history database");
|
||||
equal((yield PlacesTestUtils.isPageInDB(visits[1].url)), false, "expected uri not found in history database");
|
||||
equal((yield PlacesTestUtils.visitsInDB(visits[1].url)), 0, "0 visits for uri found in history database");
|
||||
|
||||
ok(yield PlacesTestUtils.isPageInDB(visits[3].url), "expected uri found in history database");
|
||||
ok((yield PlacesTestUtils.isPageInDB(visits[3].url)), "expected uri found in history database");
|
||||
|
||||
extension.sendMessage("delete-all");
|
||||
[historyClearedCount, removedUrls] = yield extension.awaitMessage("history-cleared");
|
||||
is(PlacesUtils.history.hasHistoryEntries, false, "history is empty");
|
||||
is(historyClearedCount, 2, "onVisitRemoved called for each clearing of history");
|
||||
is(removedUrls.length, 3, "onVisitRemoved called the expected number of times");
|
||||
equal(PlacesUtils.history.hasHistoryEntries, false, "history is empty");
|
||||
equal(historyClearedCount, 2, "onVisitRemoved called for each clearing of history");
|
||||
equal(removedUrls.length, 3, "onVisitRemoved called the expected number of times");
|
||||
for (let i = 1; i < 3; ++i) {
|
||||
let url = visits[i].url;
|
||||
ok(removedUrls.includes(url), `${url} received by onVisitRemoved`);
|
||||
@ -215,9 +215,9 @@ add_task(function* test_search() {
|
||||
|
||||
function checkResult(results, url, expectedCount) {
|
||||
let result = findResult(url, results);
|
||||
isnot(result, null, `history.search result was found for ${url}`);
|
||||
is(result.visitCount, expectedCount, `history.search reports ${expectedCount} visit(s)`);
|
||||
is(result.title, `test visit for ${url}`, "title for search result is correct");
|
||||
notEqual(result, null, `history.search result was found for ${url}`);
|
||||
equal(result.visitCount, expectedCount, `history.search reports ${expectedCount} visit(s)`);
|
||||
equal(result.title, `test visit for ${url}`, "title for search result is correct");
|
||||
}
|
||||
|
||||
yield extension.startup();
|
||||
@ -229,21 +229,21 @@ add_task(function* test_search() {
|
||||
extension.sendMessage("check-history");
|
||||
|
||||
let results = yield extension.awaitMessage("empty-search");
|
||||
is(results.length, 3, "history.search with empty text returned 3 results");
|
||||
equal(results.length, 3, "history.search with empty text returned 3 results");
|
||||
checkResult(results, SINGLE_VISIT_URL, 1);
|
||||
checkResult(results, DOUBLE_VISIT_URL, 2);
|
||||
checkResult(results, MOZILLA_VISIT_URL, 1);
|
||||
|
||||
results = yield extension.awaitMessage("text-search");
|
||||
is(results.length, 1, "history.search with specific text returned 1 result");
|
||||
equal(results.length, 1, "history.search with specific text returned 1 result");
|
||||
checkResult(results, MOZILLA_VISIT_URL, 1);
|
||||
|
||||
results = yield extension.awaitMessage("max-results-search");
|
||||
is(results.length, 1, "history.search with maxResults returned 1 result");
|
||||
equal(results.length, 1, "history.search with maxResults returned 1 result");
|
||||
checkResult(results, DOUBLE_VISIT_URL, 2);
|
||||
|
||||
results = yield extension.awaitMessage("date-range-search");
|
||||
is(results.length, 2, "history.search with a date range returned 2 result");
|
||||
equal(results.length, 2, "history.search with a date range returned 2 result");
|
||||
checkResult(results, DOUBLE_VISIT_URL, 2);
|
||||
checkResult(results, SINGLE_VISIT_URL, 1);
|
||||
|
||||
@ -300,11 +300,11 @@ add_task(function* test_add_url() {
|
||||
];
|
||||
|
||||
function* checkUrl(results) {
|
||||
ok(yield PlacesTestUtils.isPageInDB(results.details.url), `${results.details.url} found in history database`);
|
||||
ok((yield PlacesTestUtils.isPageInDB(results.details.url)), `${results.details.url} found in history database`);
|
||||
ok(PlacesUtils.isValidGuid(results.result.id), "URL was added with a valid id");
|
||||
is(results.result.title, results.details.title, "URL was added with the correct title");
|
||||
equal(results.result.title, results.details.title, "URL was added with the correct title");
|
||||
if (results.details.visitTime) {
|
||||
is(results.result.lastVisitTime,
|
||||
equal(results.result.lastVisitTime,
|
||||
Number(ExtensionUtils.normalizeTime(results.details.visitTime)),
|
||||
"URL was added with the correct date");
|
||||
}
|
||||
@ -458,12 +458,12 @@ add_task(function* test_on_visited() {
|
||||
function checkOnVisitedData(index, expected) {
|
||||
let onVisited = onVisitedData[index];
|
||||
ok(PlacesUtils.isValidGuid(onVisited.id), "onVisited received a valid id");
|
||||
is(onVisited.url, expected.url, "onVisited received the expected url");
|
||||
equal(onVisited.url, expected.url, "onVisited received the expected url");
|
||||
// Title will be blank until bug 1287928 lands
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1287928
|
||||
is(onVisited.title, "", "onVisited received a blank title");
|
||||
is(onVisited.lastVisitTime, expected.time, "onVisited received the expected time");
|
||||
is(onVisited.visitCount, expected.visitCount, "onVisited received the expected visitCount");
|
||||
equal(onVisited.title, "", "onVisited received a blank title");
|
||||
equal(onVisited.lastVisitTime, expected.time, "onVisited received the expected time");
|
||||
equal(onVisited.visitCount, expected.visitCount, "onVisited received the expected visitCount");
|
||||
}
|
||||
|
||||
let expected = {
|
@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
add_task(function* test_manifest_commands() {
|
||||
let normalized = yield normalizeManifest({
|
||||
let normalized = yield ExtensionTestUtils.normalizeManifest({
|
||||
"commands": {
|
||||
"toggle-feature": {
|
||||
"suggested_key": {"default": "Shifty+Y"},
|
||||
|
@ -3,4 +3,6 @@ head = head.js
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
|
||||
[test_ext_bookmarks.js]
|
||||
[test_ext_history.js]
|
||||
[test_ext_manifest_commands.js]
|
||||
|
@ -108,37 +108,9 @@ this.ContentLinkHandler = {
|
||||
}
|
||||
sizeHistogramTypes.add(sizesType);
|
||||
|
||||
if (uri.scheme == 'blob') {
|
||||
// Blob URLs don't work cross process, work around this by sending as a data uri
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: uri,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE,
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
let listener = {
|
||||
encoded: "",
|
||||
bis: null,
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
this.bis = Components.classes["@mozilla.org/binaryinputstream;1"]
|
||||
.createInstance(Components.interfaces.nsIBinaryInputStream);
|
||||
},
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
let spec = "data:" + channel.contentType + ";base64," + this.encoded;
|
||||
chromeGlobal.sendAsyncMessage(
|
||||
"Link:SetIcon",
|
||||
{url: spec, loadingPrincipal: link.ownerDocument.nodePrincipal});
|
||||
},
|
||||
onDataAvailable: function(request, context, inputStream, offset, count) {
|
||||
this.bis.setInputStream(inputStream);
|
||||
this.encoded += btoa(this.bis.readBytes(this.bis.available()));
|
||||
}
|
||||
}
|
||||
channel.asyncOpen2(listener);
|
||||
} else {
|
||||
chromeGlobal.sendAsyncMessage(
|
||||
"Link:SetIcon",
|
||||
{url: uri.spec, loadingPrincipal: link.ownerDocument.nodePrincipal});
|
||||
}
|
||||
chromeGlobal.sendAsyncMessage(
|
||||
"Link:SetIcon",
|
||||
{url: uri.spec, loadingPrincipal: link.ownerDocument.nodePrincipal});
|
||||
iconAdded = true;
|
||||
break;
|
||||
case "search":
|
||||
|
@ -185,7 +185,6 @@ mt = check_prog('_MT', depends_win()(lambda: ('mt.exe',)), what='mt',
|
||||
# utility".
|
||||
@depends_win(mt)
|
||||
@checking('whether MT is really Microsoft Manifest Tool', lambda x: bool(x))
|
||||
@imports('re')
|
||||
@imports('subprocess')
|
||||
def valid_mt(path):
|
||||
try:
|
||||
@ -193,21 +192,13 @@ def valid_mt(path):
|
||||
out = '\n'.join(l for l in out
|
||||
if 'Microsoft (R) Manifest Tool' in l)
|
||||
if out:
|
||||
m = re.search(r'(?<=[^!-~])[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+',
|
||||
out)
|
||||
if not m:
|
||||
raise FatalCheckError(
|
||||
'Unknown version of the Microsoft Manifest Tool')
|
||||
return namespace(
|
||||
path=path,
|
||||
version=Version(m.group(0)),
|
||||
)
|
||||
return path
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
raise FatalCheckError('%s is not Microsoft Manifest Tool')
|
||||
|
||||
|
||||
set_config('MT', depends_if(valid_mt)(lambda x: os.path.basename(x.path)))
|
||||
set_config('MT', depends_if(valid_mt)(lambda x: os.path.basename(x)))
|
||||
set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x)))
|
||||
|
||||
|
||||
|
@ -2425,7 +2425,8 @@ nsDocument::FillStyleSet(StyleSetHandle aStyleSet)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NS_ERROR("stylo: nsStyleSheetService doesn't handle ServoStyleSheets yet");
|
||||
NS_WARNING("stylo: Not yet checking nsStyleSheetService for Servo-backed "
|
||||
"documents. See bug 1290224");
|
||||
}
|
||||
|
||||
AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
|
||||
@ -13350,7 +13351,8 @@ nsIDocument::FlushUserFontSet()
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
NS_ERROR("stylo: ServoStyleSets cannot handle @font-face rules yet");
|
||||
NS_WARNING("stylo: ServoStyleSets cannot handle @font-face rules yet. "
|
||||
"See bug 1290237.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -13439,6 +13441,10 @@ nsIDocument::UpdateStyleBackendType()
|
||||
{
|
||||
MOZ_ASSERT(mStyleBackendType == StyleBackendType(0),
|
||||
"no need to call UpdateStyleBackendType now");
|
||||
|
||||
// Assume Gecko by default.
|
||||
mStyleBackendType = StyleBackendType::Gecko;
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
// XXX For now we use a Servo-backed style set only for (X)HTML documents
|
||||
// in content docshells. This should let us avoid implementing XUL-specific
|
||||
@ -13447,15 +13453,11 @@ nsIDocument::UpdateStyleBackendType()
|
||||
// document before we have a pres shell (i.e. before we make the decision
|
||||
// here about whether to use a Gecko- or Servo-backed style system), so
|
||||
// we avoid Servo-backed style sets for SVG documents.
|
||||
NS_ASSERTION(mDocumentContainer, "stylo: calling UpdateStyleBackendType "
|
||||
"before we have a docshell");
|
||||
mStyleBackendType =
|
||||
nsLayoutUtils::SupportsServoStyleBackend(this) &&
|
||||
mDocumentContainer ?
|
||||
StyleBackendType::Servo :
|
||||
StyleBackendType::Gecko;
|
||||
#else
|
||||
mStyleBackendType = StyleBackendType::Gecko;
|
||||
if (!mDocumentContainer) {
|
||||
NS_WARNING("stylo: No docshell yet, assuming Gecko style system");
|
||||
} else if (nsLayoutUtils::SupportsServoStyleBackend(this)) {
|
||||
mStyleBackendType = StyleBackendType::Servo;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,8 @@ NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsMimeTypeArray,
|
||||
mWindow,
|
||||
mMimeTypes)
|
||||
mMimeTypes,
|
||||
mCTPMimeTypes)
|
||||
|
||||
nsMimeTypeArray::nsMimeTypeArray(nsPIDOMWindowInner* aWindow)
|
||||
: mWindow(aWindow)
|
||||
@ -57,6 +58,7 @@ void
|
||||
nsMimeTypeArray::Refresh()
|
||||
{
|
||||
mMimeTypes.Clear();
|
||||
mCTPMimeTypes.Clear();
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
@ -133,6 +135,10 @@ nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound)
|
||||
aFound = true;
|
||||
return mimeType;
|
||||
}
|
||||
nsMimeType* hiddenType = FindMimeType(mCTPMimeTypes, lowerName);
|
||||
if (hiddenType) {
|
||||
nsPluginArray::NotifyHiddenPluginTouched(hiddenType->GetEnabledPlugin());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -180,6 +186,7 @@ nsMimeTypeArray::EnsurePluginMimeTypes()
|
||||
}
|
||||
|
||||
pluginArray->GetMimeTypes(mMimeTypes);
|
||||
pluginArray->GetCTPMimeTypes(mCTPMimeTypes);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsMimeType, AddRef)
|
||||
|
@ -48,6 +48,7 @@ protected:
|
||||
// mMimeTypes contains MIME types handled by plugins or by an OS
|
||||
// PreferredApplicationHandler.
|
||||
nsTArray<RefPtr<nsMimeType> > mMimeTypes;
|
||||
nsTArray<RefPtr<nsMimeType> > mCTPMimeTypes;
|
||||
};
|
||||
|
||||
class nsMimeType final : public nsWrapperCache
|
||||
|
@ -114,6 +114,24 @@ nsPluginArray::GetMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes)
|
||||
aMimeTypes.Sort();
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginArray::GetCTPMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes)
|
||||
{
|
||||
aMimeTypes.Clear();
|
||||
|
||||
if (!AllowPlugins()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnsurePlugins();
|
||||
|
||||
GetPluginMimeTypes(mCTPPlugins, aMimeTypes);
|
||||
|
||||
// Alphabetize the enumeration order of non-hidden MIME types to reduce
|
||||
// fingerprintable entropy based on plugins' installation file times.
|
||||
aMimeTypes.Sort();
|
||||
}
|
||||
|
||||
nsPluginElement*
|
||||
nsPluginArray::Item(uint32_t aIndex)
|
||||
{
|
||||
@ -236,21 +254,26 @@ nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound)
|
||||
if (!aFound) {
|
||||
nsPluginElement* hiddenPlugin = FindPlugin(mCTPPlugins, aName);
|
||||
if (hiddenPlugin) {
|
||||
HiddenPluginEventInit init;
|
||||
init.mTag = hiddenPlugin->PluginTag();
|
||||
nsCOMPtr<nsIDocument> doc = hiddenPlugin->GetParentObject()->GetDoc();
|
||||
RefPtr<HiddenPluginEvent> event =
|
||||
HiddenPluginEvent::Constructor(doc, NS_LITERAL_STRING("HiddenPlugin"), init);
|
||||
event->SetTarget(doc);
|
||||
event->SetTrusted(true);
|
||||
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
|
||||
bool dummy;
|
||||
doc->DispatchEvent(event, &dummy);
|
||||
NotifyHiddenPluginTouched(hiddenPlugin);
|
||||
}
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
|
||||
void nsPluginArray::NotifyHiddenPluginTouched(nsPluginElement* aHiddenElement)
|
||||
{
|
||||
HiddenPluginEventInit init;
|
||||
init.mTag = aHiddenElement->PluginTag();
|
||||
nsCOMPtr<nsIDocument> doc = aHiddenElement->GetParentObject()->GetDoc();
|
||||
RefPtr<HiddenPluginEvent> event =
|
||||
HiddenPluginEvent::Constructor(doc, NS_LITERAL_STRING("HiddenPlugin"), init);
|
||||
event->SetTarget(doc);
|
||||
event->SetTrusted(true);
|
||||
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
|
||||
bool dummy;
|
||||
doc->DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsPluginArray::Length()
|
||||
{
|
||||
|
@ -41,6 +41,9 @@ public:
|
||||
void Invalidate();
|
||||
|
||||
void GetMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes);
|
||||
void GetCTPMimeTypes(nsTArray<RefPtr<nsMimeType>>& aMimeTypes);
|
||||
|
||||
static void NotifyHiddenPluginTouched(nsPluginElement* aElement);
|
||||
|
||||
// PluginArray WebIDL methods
|
||||
|
||||
|
@ -346,16 +346,10 @@ WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
|
||||
return JS::Int32Value(mBoundVertexArray->mAttribs[index].stride);
|
||||
|
||||
case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
|
||||
if (!mBoundVertexArray->mAttribs[index].enabled)
|
||||
return JS::Int32Value(4);
|
||||
|
||||
return JS::Int32Value(mBoundVertexArray->mAttribs[index].size);
|
||||
|
||||
case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
|
||||
if (!mBoundVertexArray->mAttribs[index].enabled)
|
||||
return JS::NumberValue(uint32_t(LOCAL_GL_FLOAT));
|
||||
|
||||
return JS::NumberValue(uint32_t(mBoundVertexArray->mAttribs[index].type));
|
||||
return JS::Int32Value(mBoundVertexArray->mAttribs[index].type);
|
||||
|
||||
case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER:
|
||||
if (IsWebGL2())
|
||||
|
@ -262,6 +262,9 @@ WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarge
|
||||
bytes = view.DataAllowShared();
|
||||
byteCount = view.LengthAllowShared();
|
||||
}
|
||||
} else if (isSubImage) {
|
||||
mContext->ErrorInvalidValue("%s: `pixels` must not be null.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isClientData = true;
|
||||
|
9
dom/canvas/crashtests/1284356-1.html
Normal file
9
dom/canvas/crashtests/1284356-1.html
Normal file
@ -0,0 +1,9 @@
|
||||
<canvas id='i0'></canvas>
|
||||
<script>
|
||||
var c=document.getElementById('i0').getContext('2d');
|
||||
c.lineWidth=194.622602174;
|
||||
c.miterLimit=270.273509738;
|
||||
c.transform(0,0,0,0,0,0);
|
||||
c.globalCompositeOperation='soft-light';
|
||||
c.strokeText('a',200,273,722);
|
||||
</script>
|
8
dom/canvas/crashtests/1284578-1.html
Normal file
8
dom/canvas/crashtests/1284578-1.html
Normal file
@ -0,0 +1,8 @@
|
||||
<canvas id='i0'></canvas>
|
||||
<script>
|
||||
var c=document.getElementById('i0').getContext('2d');
|
||||
c.bezierCurveTo(157,351,351,44,946,701);
|
||||
c.quadraticCurveTo(260,-9007199254740991,945,145);
|
||||
c.translate(-9007199254740991,239);
|
||||
c.isPointInPath(988,439);
|
||||
</script>
|
8
dom/canvas/crashtests/1287652-1.html
Normal file
8
dom/canvas/crashtests/1287652-1.html
Normal file
@ -0,0 +1,8 @@
|
||||
<canvas id='i0'></canvas>
|
||||
<script>
|
||||
var c=document.getElementById('i0').getContext('2d');
|
||||
var g=c.createLinearGradient(59,9,38.89,-75.51);
|
||||
c.fillStyle=g;
|
||||
c.globalAlpha=0.62;
|
||||
c.fillText('a',0,24,30);
|
||||
</script>
|
@ -29,6 +29,9 @@ skip-if(azureCairo) load 1229983-1.html
|
||||
load 1229932-1.html
|
||||
load 1244850-1.html
|
||||
load 1246775-1.html
|
||||
load 1284356-1.html
|
||||
load 1284578-1.html
|
||||
skip-if(d2d) load 1287515-1.html
|
||||
load 1287652-1.html
|
||||
load 1288872-1.html
|
||||
|
||||
|
@ -5838,7 +5838,6 @@ skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.
|
||||
[generated/test_2_conformance__textures__misc__tex-image-with-invalid-data.html]
|
||||
skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
|
||||
[generated/test_2_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
|
||||
[generated/test_2_conformance__textures__misc__tex-sub-image-2d.html]
|
||||
skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
|
||||
@ -6722,7 +6721,6 @@ skip-if = (os == 'android')
|
||||
skip-if = (os == 'android')
|
||||
[generated/test_conformance__textures__misc__tex-input-validation.html]
|
||||
[generated/test_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
[generated/test_conformance__textures__misc__tex-sub-image-2d.html]
|
||||
[generated/test_conformance__textures__misc__texparameter-test.html]
|
||||
[generated/test_conformance__textures__misc__texture-active-bind-2.html]
|
||||
|
@ -148,8 +148,6 @@ fail-if = (os == 'mac')
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_2_conformance2__glsl3__forbidden-operators.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
[generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_2_conformance__rendering__negative-one-index.html]
|
||||
@ -160,8 +158,6 @@ fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_conformance__attribs__gl-vertexattribpointer.html]
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
[generated/test_2_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
|
||||
fail-if = (os == 'android') || (os == 'linux')
|
||||
[generated/test_conformance__ogles__GL__gl_FragCoord__gl_FragCoord_001_to_003.html]
|
||||
|
@ -2365,22 +2365,6 @@ HTMLInputElement::GetValueIfStepped(int32_t aStep,
|
||||
value += step * Decimal(aStep);
|
||||
}
|
||||
|
||||
// For date inputs, the value can hold a string that is not a day. We do not
|
||||
// want to round it, as it might result in a step mismatch. Instead we want to
|
||||
// clamp to the next valid value.
|
||||
if (mType == NS_FORM_INPUT_DATE &&
|
||||
NS_floorModulo(Decimal(value - GetStepBase()), GetStepScaleFactor()) != Decimal(0)) {
|
||||
MOZ_ASSERT(GetStep() > Decimal(0));
|
||||
Decimal validStep = EuclidLCM<Decimal>(GetStep().floor(),
|
||||
GetStepScaleFactor().floor());
|
||||
if (aStep > 0) {
|
||||
value -= NS_floorModulo(value - GetStepBase(), validStep);
|
||||
value += validStep;
|
||||
} else if (aStep < 0) {
|
||||
value -= NS_floorModulo(value - GetStepBase(), validStep);
|
||||
}
|
||||
}
|
||||
|
||||
if (value < minimum) {
|
||||
value = minimum;
|
||||
deltaFromStep = NS_floorModulo(value - stepBase, step);
|
||||
@ -6903,6 +6887,11 @@ HTMLInputElement::GetStep() const
|
||||
step = GetDefaultStep();
|
||||
}
|
||||
|
||||
// For input type=date, we round the step value to have a rounded day.
|
||||
if (mType == NS_FORM_INPUT_DATE) {
|
||||
step = std::max(step.round(), Decimal(1));
|
||||
}
|
||||
|
||||
return step * GetStepScaleFactor();
|
||||
}
|
||||
|
||||
@ -7506,16 +7495,6 @@ HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
|
||||
Decimal step = GetStep();
|
||||
MOZ_ASSERT(step != kStepAny && step > Decimal(0));
|
||||
|
||||
// In case this is a date and the step is not an integer, we don't want to
|
||||
// display the dates corresponding to the truncated timestamps of valueLow
|
||||
// and valueHigh because they might suffer from a step mismatch as well.
|
||||
// Instead we want the timestamps to correspond to a rounded day. That is,
|
||||
// we want a multiple of the step scale factor (1 day) as well as of step.
|
||||
if (mType == NS_FORM_INPUT_DATE) {
|
||||
step = EuclidLCM<Decimal>(step.floor(),
|
||||
GetStepScaleFactor().floor());
|
||||
}
|
||||
|
||||
Decimal stepBase = GetStepBase();
|
||||
|
||||
Decimal valueLow = value - NS_floorModulo(value - stepBase, step);
|
||||
|
@ -3083,66 +3083,100 @@ HTMLMediaElement::ReportTelemetry()
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_UNLOAD_STATE, state);
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_UNLOAD_STATE = %d", this, state));
|
||||
|
||||
FrameStatisticsData data;
|
||||
|
||||
if (HTMLVideoElement* vid = HTMLVideoElement::FromContentOrNull(this)) {
|
||||
RefPtr<VideoPlaybackQuality> quality = vid->GetVideoPlaybackQuality();
|
||||
uint32_t totalFrames = quality->TotalVideoFrames();
|
||||
if (totalFrames) {
|
||||
uint32_t droppedFrames = quality->DroppedVideoFrames();
|
||||
MOZ_ASSERT(droppedFrames <= totalFrames);
|
||||
// Dropped frames <= total frames, so 'percentage' cannot be higher than
|
||||
// 100 and therefore can fit in a uint32_t (that Telemetry takes).
|
||||
uint32_t percentage = 100 * droppedFrames / totalFrames;
|
||||
LOG(LogLevel::Debug,
|
||||
("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
|
||||
percentage);
|
||||
FrameStatistics* stats = vid->GetFrameStatistics();
|
||||
if (stats) {
|
||||
data = stats->GetFrameStatisticsData();
|
||||
if (data.mParsedFrames) {
|
||||
MOZ_ASSERT(data.mDroppedFrames <= data.mParsedFrames);
|
||||
// Dropped frames <= total frames, so 'percentage' cannot be higher than
|
||||
// 100 and therefore can fit in a uint32_t (that Telemetry takes).
|
||||
uint32_t percentage = 100 * data.mDroppedFrames / data.mParsedFrames;
|
||||
LOG(LogLevel::Debug,
|
||||
("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
|
||||
percentage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double playTime = mPlayTime.Total();
|
||||
double hiddenPlayTime = mHiddenPlayTime.Total();
|
||||
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(playTime));
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, playTime));
|
||||
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(hiddenPlayTime));
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, hiddenPlayTime));
|
||||
|
||||
if (playTime > 0.0 &&
|
||||
mMediaInfo.HasVideo() &&
|
||||
if (mMediaInfo.HasVideo() &&
|
||||
mMediaInfo.mVideo.mImage.height > 0) {
|
||||
// We have actually played some valid video -> Report hidden/total ratio.
|
||||
uint32_t hiddenPercentage = uint32_t(hiddenPlayTime / playTime * 100.0 + 0.5);
|
||||
// We have a valid video.
|
||||
double playTime = mPlayTime.Total();
|
||||
double hiddenPlayTime = mHiddenPlayTime.Total();
|
||||
|
||||
// Keyed by audio+video or video alone, and by a resolution range.
|
||||
nsCString key(mMediaInfo.HasAudio() ? "AV," : "V,");
|
||||
static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
|
||||
{ 240, "0<h<=240" },
|
||||
{ 480, "240<h<=480" },
|
||||
{ 576, "480<h<=576" },
|
||||
{ 720, "576<h<=720" },
|
||||
{ 1080, "720<h<=1080" },
|
||||
{ 2160, "1080<h<=2160" }
|
||||
};
|
||||
const char* resolution = "h>2160";
|
||||
int32_t height = mMediaInfo.mVideo.mImage.height;
|
||||
for (const auto& res : sResolutions) {
|
||||
if (height <= res.mH) {
|
||||
resolution = res.mRes;
|
||||
break;
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(playTime));
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, playTime));
|
||||
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(hiddenPlayTime));
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, hiddenPlayTime));
|
||||
|
||||
if (playTime > 0.0) {
|
||||
// We have actually played something -> Report hidden/total ratio.
|
||||
uint32_t hiddenPercentage = uint32_t(hiddenPlayTime / playTime * 100.0 + 0.5);
|
||||
|
||||
// Keyed by audio+video or video alone, and by a resolution range.
|
||||
nsCString key(mMediaInfo.HasAudio() ? "AV," : "V,");
|
||||
static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
|
||||
{ 240, "0<h<=240" },
|
||||
{ 480, "240<h<=480" },
|
||||
{ 576, "480<h<=576" },
|
||||
{ 720, "576<h<=720" },
|
||||
{ 1080, "720<h<=1080" },
|
||||
{ 2160, "1080<h<=2160" }
|
||||
};
|
||||
const char* resolution = "h>2160";
|
||||
int32_t height = mMediaInfo.mVideo.mImage.height;
|
||||
for (const auto& res : sResolutions) {
|
||||
if (height <= res.mH) {
|
||||
resolution = res.mRes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
key.AppendASCII(resolution);
|
||||
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
|
||||
key,
|
||||
hiddenPercentage);
|
||||
// Also accumulate all percentages in an "All" key.
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
|
||||
NS_LITERAL_CSTRING("All"),
|
||||
hiddenPercentage);
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE = %u, keys: '%s' and 'All'",
|
||||
this, hiddenPercentage, key.get()));
|
||||
|
||||
if (data.mInterKeyframeCount != 0) {
|
||||
uint32_t average_ms =
|
||||
uint32_t(std::min<uint64_t>(double(data.mInterKeyframeSum_us)
|
||||
/ double(data.mInterKeyframeCount)
|
||||
/ 1000.0
|
||||
+ 0.5,
|
||||
UINT32_MAX));
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_AVERAGE_MS,
|
||||
key,
|
||||
average_ms);
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_AVERAGE_MS,
|
||||
NS_LITERAL_CSTRING("All"),
|
||||
average_ms);
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_INTER_KEYFRAME_AVERAGE_MS = %u, keys: '%s' and 'All'",
|
||||
this, average_ms, key.get()));
|
||||
|
||||
uint32_t max_ms =
|
||||
uint32_t(std::min<uint64_t>((data.mInterKeyFrameMax_us + 500) / 1000,
|
||||
UINT32_MAX));
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_MAX_MS,
|
||||
key,
|
||||
max_ms);
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_MAX_MS,
|
||||
NS_LITERAL_CSTRING("All"),
|
||||
max_ms);
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_INTER_KEYFRAME_MAX_MS = %u, keys: '%s' and 'All'",
|
||||
this, max_ms, key.get()));
|
||||
}
|
||||
}
|
||||
key.AppendASCII(resolution);
|
||||
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
|
||||
key,
|
||||
hiddenPercentage);
|
||||
// Also accumulate all percentages in an "All" key.
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
|
||||
NS_LITERAL_CSTRING("All"),
|
||||
hiddenPercentage);
|
||||
LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE = %u, keys: '%s' and 'All'",
|
||||
this, hiddenPercentage, key.get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,7 @@ static constexpr const nsAttrValue::EnumTable* kKindTableInvalidValueDefault = &
|
||||
/** HTMLTrackElement */
|
||||
HTMLTrackElement::HTMLTrackElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo)
|
||||
, mLoadResourceDispatched(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -182,9 +183,46 @@ HTMLTrackElement::ParseAttribute(int32_t aNamespaceID,
|
||||
aResult);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLTrackElement::SetSrc(const nsAString& aSrc, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLAttr(nsGkAtoms::src, aSrc, aError);
|
||||
uint16_t oldReadyState = ReadyState();
|
||||
SetReadyState(TextTrackReadyState::NotLoaded);
|
||||
if (!mMediaParent) {
|
||||
return;
|
||||
}
|
||||
if (mTrack && (oldReadyState != TextTrackReadyState::NotLoaded)) {
|
||||
// Remove all the cues in MediaElement.
|
||||
mMediaParent->RemoveTextTrack(mTrack);
|
||||
// Recreate mTrack.
|
||||
CreateTextTrack();
|
||||
}
|
||||
// Stop WebVTTListener.
|
||||
mListener = nullptr;
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(NS_BINDING_ABORTED);
|
||||
mChannel = nullptr;
|
||||
}
|
||||
|
||||
DispatchLoadResource();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLTrackElement::DispatchLoadResource()
|
||||
{
|
||||
if (!mLoadResourceDispatched) {
|
||||
RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLTrackElement::LoadResource);
|
||||
nsContentUtils::RunInStableState(r.forget());
|
||||
mLoadResourceDispatched = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLTrackElement::LoadResource()
|
||||
{
|
||||
mLoadResourceDispatched = false;
|
||||
|
||||
// Find our 'src' url
|
||||
nsAutoString src;
|
||||
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
|
||||
@ -258,8 +296,7 @@ HTMLTrackElement::BindToTree(nsIDocument* aDocument,
|
||||
if (!mTrack) {
|
||||
CreateTextTrack();
|
||||
}
|
||||
RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLTrackElement::LoadResource);
|
||||
nsContentUtils::RunInStableState(r.forget());
|
||||
DispatchLoadResource();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -46,10 +46,8 @@ public:
|
||||
{
|
||||
GetHTMLURIAttr(nsGkAtoms::src, aSrc);
|
||||
}
|
||||
void SetSrc(const nsAString& aSrc, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLAttr(nsGkAtoms::src, aSrc, aError);
|
||||
}
|
||||
|
||||
void SetSrc(const nsAString& aSrc, ErrorResult& aError);
|
||||
|
||||
void GetSrclang(DOMString& aSrclang) const
|
||||
{
|
||||
@ -134,6 +132,10 @@ protected:
|
||||
RefPtr<WebVTTListener> mListener;
|
||||
|
||||
void CreateTextTrack();
|
||||
|
||||
private:
|
||||
void DispatchLoadResource();
|
||||
bool mLoadResourceDispatched;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -31,6 +31,9 @@
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/VideoPlaybackQuality.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Video)
|
||||
|
||||
namespace mozilla {
|
||||
@ -225,6 +228,12 @@ HTMLVideoElement::NotifyOwnerDocumentActivityChangedInternal()
|
||||
return pauseElement;
|
||||
}
|
||||
|
||||
FrameStatistics*
|
||||
HTMLVideoElement::GetFrameStatistics()
|
||||
{
|
||||
return mDecoder ? &(mDecoder->GetFrameStatistics()) : nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<VideoPlaybackQuality>
|
||||
HTMLVideoElement::GetVideoPlaybackQuality()
|
||||
{
|
||||
@ -242,11 +251,24 @@ HTMLVideoElement::GetVideoPlaybackQuality()
|
||||
}
|
||||
|
||||
if (mDecoder) {
|
||||
FrameStatistics& stats = mDecoder->GetFrameStatistics();
|
||||
static_assert(sizeof(uint32_t) >= sizeof (stats.GetParsedFrames()),
|
||||
"possible truncation from FrameStatistics to VideoPlaybackQuality");
|
||||
totalFrames = stats.GetParsedFrames();
|
||||
droppedFrames = stats.GetDroppedFrames();
|
||||
FrameStatisticsData stats =
|
||||
mDecoder->GetFrameStatistics().GetFrameStatisticsData();
|
||||
if (sizeof(totalFrames) >= sizeof(stats.mParsedFrames)) {
|
||||
totalFrames = stats.mParsedFrames;
|
||||
droppedFrames = stats.mDroppedFrames;
|
||||
} else {
|
||||
auto maxStat = std::max(stats.mParsedFrames, stats.mDroppedFrames);
|
||||
const auto maxNumber = std::numeric_limits<uint32_t>::max();
|
||||
if (maxStat <= maxNumber) {
|
||||
totalFrames = static_cast<uint32_t>(stats.mParsedFrames);
|
||||
droppedFrames = static_cast<uint32_t>(stats.mDroppedFrames);
|
||||
} else {
|
||||
// Too big number(s) -> Resize everything to fit in 32 bits.
|
||||
double ratio = double(maxNumber) / double(maxStat);
|
||||
totalFrames = double(stats.mParsedFrames) * ratio;
|
||||
droppedFrames = double(stats.mDroppedFrames) * ratio;
|
||||
}
|
||||
}
|
||||
corruptedFrames = 0;
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,9 @@ public:
|
||||
|
||||
bool NotifyOwnerDocumentActivityChangedInternal() override;
|
||||
|
||||
// Gives access to the decoder's frame statistics, if present.
|
||||
FrameStatistics* GetFrameStatistics();
|
||||
|
||||
already_AddRefed<VideoPlaybackQuality> GetVideoPlaybackQuality();
|
||||
|
||||
protected:
|
||||
|
@ -185,7 +185,7 @@ for (var test of data) {
|
||||
input.min = '2009-02-01';
|
||||
input.step = '1.1';
|
||||
input.value = '2009-02-02';
|
||||
checkValidity(input, false, apply, { low: "2009-02-01", high: "2009-02-12" });
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
// Without any step attribute the date is valid
|
||||
input.removeAttribute('step');
|
||||
@ -203,21 +203,21 @@ for (var test of data) {
|
||||
|
||||
input.step = '0.9';
|
||||
input.value = '1951-01-02';
|
||||
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-10" });
|
||||
|
||||
input.value = '1951-01-10'
|
||||
is(input.step, '0.9', "check that step value is unchanged");
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
input.step = '0.5';
|
||||
input.step = '0.4';
|
||||
input.value = '1951-01-02';
|
||||
is(input.step, '0.4', "check that step value is unchanged");
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
input.step = '1.5';
|
||||
input.value = '1951-01-03';
|
||||
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-04" });
|
||||
input.value = '1951-01-02';
|
||||
is(input.step, '1.5', "check that step value is unchanged");
|
||||
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-03" });
|
||||
|
||||
input.value = '1951-01-08';
|
||||
checkValidity(input, false, apply, { low: "1951-01-07", high: "1951-01-10" });
|
||||
checkValidity(input, false, apply, { low: "1951-01-07", high: "1951-01-09" });
|
||||
|
||||
input.step = '3000';
|
||||
input.min= '1968-01-01';
|
||||
@ -236,26 +236,26 @@ for (var test of data) {
|
||||
input.value = '1992-08-22';
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
input.step = '1.1';
|
||||
input.step = '2.1';
|
||||
input.min = '1991-01-01';
|
||||
input.value = '1991-01-01';
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
input.value = '1991-01-02';
|
||||
checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-12" });
|
||||
checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-03" });
|
||||
|
||||
input.value = '1991-01-12';
|
||||
input.value = '1991-01-03';
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
input.step = '1.1';
|
||||
input.step = '2.1';
|
||||
input.min = '1969-12-20';
|
||||
input.value = '1969-12-20';
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
input.value = '1969-12-21';
|
||||
checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-31" });
|
||||
checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-22" });
|
||||
|
||||
input.value = '1969-12-31';
|
||||
input.value = '1969-12-22';
|
||||
checkValidity(input, true, apply);
|
||||
|
||||
break;
|
||||
|
@ -271,12 +271,12 @@ function checkStepDown()
|
||||
[ '2012-01-03', '0.5', null, null, null, '2012-01-02', false ],
|
||||
[ '2012-01-02', '0.5', null, null, null, '2012-01-01', false ],
|
||||
[ '2012-01-01', '2', null, null, null, '2011-12-30', false ],
|
||||
[ '2012-01-02', '0.25',null, null, 4, '2012-01-01', false ],
|
||||
[ '2012-01-15', '1.1', '2012-01-01', null, 1, '2012-01-12', false ],
|
||||
[ '2012-01-12', '1.1', '2012-01-01', null, 2, '2012-01-01', false ],
|
||||
[ '2012-01-23', '1.1', '2012-01-01', null, 10, '2012-01-12', false ],
|
||||
[ '2012-01-23', '1.1', '2012-01-01', null, 11, '2012-01-01', false ],
|
||||
[ '1968-01-12', '1.1', '1968-01-01', null, 8, '1968-01-01', false ],
|
||||
[ '2012-01-02', '0.25',null, null, 4, '2011-12-29', false ],
|
||||
[ '2012-01-15', '1.1', '2012-01-01', null, 1, '2012-01-14', false ],
|
||||
[ '2012-01-12', '1.1', '2012-01-01', null, 2, '2012-01-10', false ],
|
||||
[ '2012-01-23', '1.1', '2012-01-01', null, 10, '2012-01-13', false ],
|
||||
[ '2012-01-23', '1.1', '2012-01-01', null, 11, '2012-01-12', false ],
|
||||
[ '1968-01-12', '1.1', '1968-01-01', null, 8, '1968-01-04', false ],
|
||||
// step = 0 isn't allowed (-> step = 1).
|
||||
[ '2012-01-02', '0', null, null, null, '2012-01-01', false ],
|
||||
// step < 0 isn't allowed (-> step = 1).
|
||||
@ -588,14 +588,13 @@ function checkStepUp()
|
||||
[ '2012-01-01', null, null, null, 1.9, '2012-01-02', false ],
|
||||
// With step values.
|
||||
[ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
|
||||
[ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
|
||||
[ '2012-01-01', '2', null, null, null, '2012-01-03', false ],
|
||||
[ '2012-01-01', '0.25', null, null, 4, '2012-01-02', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 1, '2012-01-12', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 2, '2012-01-12', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 10, '2012-01-12', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 11, '2012-01-23', false ],
|
||||
[ '1968-01-01', '1.1', '1968-01-01', null, 8, '1968-01-12', false ],
|
||||
[ '2012-01-01', '0.25', null, null, 4, '2012-01-05', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 1, '2012-01-02', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 2, '2012-01-03', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 10, '2012-01-11', false ],
|
||||
[ '2012-01-01', '1.1', '2012-01-01', null, 11, '2012-01-12', false ],
|
||||
[ '1968-01-01', '1.1', '1968-01-01', null, 8, '1968-01-09', false ],
|
||||
// step = 0 isn't allowed (-> step = 1).
|
||||
[ '2012-01-01', '0', null, null, null, '2012-01-02', false ],
|
||||
// step < 0 isn't allowed (-> step = 1).
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/StateMirroring.h"
|
||||
|
||||
#include "FrameStatistics.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "nsISupports.h"
|
||||
@ -57,8 +58,7 @@ public:
|
||||
// Increments the parsed, decoded and dropped frame counters by the passed in
|
||||
// counts.
|
||||
// Can be called on any thread.
|
||||
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
uint32_t aDropped) = 0;
|
||||
virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) = 0;
|
||||
|
||||
virtual AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() { return nullptr; };
|
||||
|
||||
@ -99,15 +99,15 @@ public:
|
||||
class AutoNotifyDecoded {
|
||||
public:
|
||||
explicit AutoNotifyDecoded(AbstractMediaDecoder* aDecoder)
|
||||
: mParsed(0), mDecoded(0), mDropped(0), mDecoder(aDecoder) {}
|
||||
: mDecoder(aDecoder)
|
||||
{}
|
||||
~AutoNotifyDecoded() {
|
||||
if (mDecoder) {
|
||||
mDecoder->NotifyDecodedFrames(mParsed, mDecoded, mDropped);
|
||||
mDecoder->NotifyDecodedFrames(mStats);
|
||||
}
|
||||
}
|
||||
uint32_t mParsed;
|
||||
uint32_t mDecoded;
|
||||
uint32_t mDropped;
|
||||
|
||||
FrameStatisticsData mStats;
|
||||
|
||||
private:
|
||||
AbstractMediaDecoder* mDecoder;
|
||||
|
@ -7,90 +7,137 @@
|
||||
#ifndef FrameStatistics_h_
|
||||
#define FrameStatistics_h_
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct FrameStatisticsData
|
||||
{
|
||||
// Number of frames parsed and demuxed from media.
|
||||
// Access protected by mReentrantMonitor.
|
||||
uint64_t mParsedFrames = 0;
|
||||
|
||||
// Number of parsed frames which were actually decoded.
|
||||
// Access protected by mReentrantMonitor.
|
||||
uint64_t mDecodedFrames = 0;
|
||||
|
||||
// Number of decoded frames which were actually sent down the rendering
|
||||
// pipeline to be painted ("presented"). Access protected by mReentrantMonitor.
|
||||
uint64_t mPresentedFrames = 0;
|
||||
|
||||
// Number of frames that have been skipped because they have missed their
|
||||
// composition deadline.
|
||||
uint64_t mDroppedFrames = 0;
|
||||
|
||||
// Sum of all inter-keyframe segment durations, in microseconds.
|
||||
// Dividing by count will give the average inter-keyframe time.
|
||||
uint64_t mInterKeyframeSum_us = 0;
|
||||
// Number of inter-keyframe segments summed so far.
|
||||
size_t mInterKeyframeCount = 0;
|
||||
|
||||
// Maximum inter-keyframe segment duration, in microseconds.
|
||||
uint64_t mInterKeyFrameMax_us = 0;
|
||||
|
||||
FrameStatisticsData() = default;
|
||||
FrameStatisticsData(uint64_t aParsed, uint64_t aDecoded, uint64_t aDropped)
|
||||
: mParsedFrames(aParsed)
|
||||
, mDecodedFrames(aDecoded)
|
||||
, mDroppedFrames(aDropped)
|
||||
{}
|
||||
|
||||
void
|
||||
Accumulate(const FrameStatisticsData& aStats)
|
||||
{
|
||||
mParsedFrames += aStats.mParsedFrames;
|
||||
mDecodedFrames += aStats.mDecodedFrames;
|
||||
mPresentedFrames += aStats.mPresentedFrames;
|
||||
mDroppedFrames += aStats.mDroppedFrames;
|
||||
mInterKeyframeSum_us += aStats.mInterKeyframeSum_us;
|
||||
mInterKeyframeCount += aStats.mInterKeyframeCount;
|
||||
// It doesn't make sense to add max numbers, instead keep the bigger one.
|
||||
if (mInterKeyFrameMax_us < aStats.mInterKeyFrameMax_us) {
|
||||
mInterKeyFrameMax_us = aStats.mInterKeyFrameMax_us;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Frame decoding/painting related performance counters.
|
||||
// Threadsafe.
|
||||
class FrameStatistics {
|
||||
class FrameStatistics
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FrameStatistics);
|
||||
|
||||
FrameStatistics() :
|
||||
mReentrantMonitor("FrameStats"),
|
||||
mParsedFrames(0),
|
||||
mDecodedFrames(0),
|
||||
mPresentedFrames(0),
|
||||
mDroppedFrames(0) {}
|
||||
FrameStatistics()
|
||||
: mReentrantMonitor("FrameStats")
|
||||
{}
|
||||
|
||||
// Returns a copy of all frame statistics data.
|
||||
// Can be called on any thread.
|
||||
FrameStatisticsData GetFrameStatisticsData() const
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mFrameStatisticsData;
|
||||
}
|
||||
|
||||
// Returns number of frames which have been parsed from the media.
|
||||
// Can be called on any thread.
|
||||
uint32_t GetParsedFrames() {
|
||||
uint64_t GetParsedFrames() const
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mParsedFrames;
|
||||
return mFrameStatisticsData.mParsedFrames;
|
||||
}
|
||||
|
||||
// Returns the number of parsed frames which have been decoded.
|
||||
// Can be called on any thread.
|
||||
uint32_t GetDecodedFrames() {
|
||||
uint64_t GetDecodedFrames() const
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mDecodedFrames;
|
||||
return mFrameStatisticsData.mDecodedFrames;
|
||||
}
|
||||
|
||||
// Returns the number of decoded frames which have been sent to the rendering
|
||||
// pipeline for painting ("presented").
|
||||
// Can be called on any thread.
|
||||
uint32_t GetPresentedFrames() {
|
||||
uint64_t GetPresentedFrames() const
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mPresentedFrames;
|
||||
return mFrameStatisticsData.mPresentedFrames;
|
||||
}
|
||||
|
||||
// Number of frames that have been skipped because they have missed their
|
||||
// compoisition deadline.
|
||||
uint32_t GetDroppedFrames() {
|
||||
// Returns the number of frames that have been skipped because they have
|
||||
// missed their composition deadline.
|
||||
uint64_t GetDroppedFrames() const
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mDroppedFrames;
|
||||
return mFrameStatisticsData.mDroppedFrames;
|
||||
}
|
||||
|
||||
// Increments the parsed and decoded frame counters by the passed in counts.
|
||||
// Can be called on any thread.
|
||||
void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
uint32_t aDropped) {
|
||||
if (aParsed == 0 && aDecoded == 0 && aDropped == 0)
|
||||
return;
|
||||
void NotifyDecodedFrames(const FrameStatisticsData& aStats)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mParsedFrames += aParsed;
|
||||
mDecodedFrames += aDecoded;
|
||||
mDroppedFrames += aDropped;
|
||||
mFrameStatisticsData.Accumulate(aStats);
|
||||
}
|
||||
|
||||
// Increments the presented frame counters.
|
||||
// Can be called on any thread.
|
||||
void NotifyPresentedFrame() {
|
||||
void NotifyPresentedFrame()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
++mPresentedFrames;
|
||||
++mFrameStatisticsData.mPresentedFrames;
|
||||
}
|
||||
|
||||
private:
|
||||
~FrameStatistics() {}
|
||||
|
||||
// ReentrantMonitor to protect access of playback statistics.
|
||||
ReentrantMonitor mReentrantMonitor;
|
||||
mutable ReentrantMonitor mReentrantMonitor;
|
||||
|
||||
// Number of frames parsed and demuxed from media.
|
||||
// Access protected by mReentrantMonitor.
|
||||
uint32_t mParsedFrames;
|
||||
|
||||
// Number of parsed frames which were actually decoded.
|
||||
// Access protected by mReentrantMonitor.
|
||||
uint32_t mDecodedFrames;
|
||||
|
||||
// Number of decoded frames which were actually sent down the rendering
|
||||
// pipeline to be painted ("presented"). Access protected by mReentrantMonitor.
|
||||
uint32_t mPresentedFrames;
|
||||
|
||||
uint32_t mDroppedFrames;
|
||||
FrameStatisticsData mFrameStatisticsData;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
#endif // FrameStatistics_h_
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "nsITimer.h"
|
||||
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "FrameStatistics.h"
|
||||
#include "MediaDecoderOwner.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaMetadataManager.h"
|
||||
@ -484,10 +483,9 @@ private:
|
||||
|
||||
// Increments the parsed and decoded frame counters by the passed in counts.
|
||||
// Can be called on any thread.
|
||||
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
uint32_t aDropped) override
|
||||
virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) override
|
||||
{
|
||||
GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded, aDropped);
|
||||
GetFrameStatistics().NotifyDecodedFrames(aStats);
|
||||
}
|
||||
|
||||
void UpdateReadyState()
|
||||
|
@ -67,6 +67,7 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||
, mDemuxer(aDemuxer)
|
||||
, mDemuxerInitDone(false)
|
||||
, mLastReportedNumDecodedFrames(0)
|
||||
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
|
||||
, mLayersBackendType(aLayersBackend)
|
||||
, mInitDone(false)
|
||||
, mIsEncrypted(false)
|
||||
@ -1015,7 +1016,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
|
||||
decoder.mNumSamplesInput++;
|
||||
decoder.mSizeOfQueue++;
|
||||
if (aTrack == TrackInfo::kVideoTrack) {
|
||||
aA.mParsed++;
|
||||
aA.mStats.mParsedFrames++;
|
||||
}
|
||||
|
||||
if (mDemuxOnly) {
|
||||
@ -1159,6 +1160,8 @@ MediaFormatReader::Update(TrackType aTrack)
|
||||
if (time >= target.Time()) {
|
||||
// We have reached our internal seek target.
|
||||
decoder.mTimeThreshold.reset();
|
||||
// We might have dropped some keyframes.
|
||||
mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe;
|
||||
}
|
||||
if (time < target.Time() || (target.mDropTarget && target.Contains(time))) {
|
||||
LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)",
|
||||
@ -1192,8 +1195,20 @@ MediaFormatReader::Update(TrackType aTrack)
|
||||
if (aTrack == TrackType::kVideoTrack) {
|
||||
uint64_t delta =
|
||||
decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
|
||||
a.mDecoded = static_cast<uint32_t>(delta);
|
||||
a.mStats.mDecodedFrames = static_cast<uint32_t>(delta);
|
||||
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
|
||||
if (output->mKeyframe) {
|
||||
if (mPreviousDecodedKeyframeTime_us < output->mTime) {
|
||||
// There is a previous keyframe -> Record inter-keyframe stats.
|
||||
uint64_t segment_us = output->mTime - mPreviousDecodedKeyframeTime_us;
|
||||
a.mStats.mInterKeyframeSum_us += segment_us;
|
||||
a.mStats.mInterKeyframeCount += 1;
|
||||
if (a.mStats.mInterKeyFrameMax_us < segment_us) {
|
||||
a.mStats.mInterKeyFrameMax_us = segment_us;
|
||||
}
|
||||
}
|
||||
mPreviousDecodedKeyframeTime_us = output->mTime;
|
||||
}
|
||||
nsCString error;
|
||||
mVideo.mIsHardwareAccelerated =
|
||||
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
|
||||
@ -1491,7 +1506,7 @@ MediaFormatReader::DropDecodedSamples(TrackType aTrack)
|
||||
decoder.mOutput.Clear();
|
||||
decoder.mSizeOfQueue -= lengthDecodedQueue;
|
||||
if (aTrack == TrackInfo::kVideoTrack && mDecoder) {
|
||||
mDecoder->NotifyDecodedFrames(0, 0, lengthDecodedQueue);
|
||||
mDecoder->NotifyDecodedFrames({ 0, 0, lengthDecodedQueue });
|
||||
}
|
||||
}
|
||||
|
||||
@ -1527,7 +1542,7 @@ MediaFormatReader::VideoSkipReset(uint32_t aSkipped)
|
||||
DropDecodedSamples(TrackInfo::kVideoTrack);
|
||||
// Report the pending frames as dropped.
|
||||
if (mDecoder) {
|
||||
mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames());
|
||||
mDecoder->NotifyDecodedFrames({ 0, 0, SizeOfVideoQueueInFrames() });
|
||||
}
|
||||
|
||||
// Cancel any pending demux request and pending demuxed samples.
|
||||
@ -1535,7 +1550,7 @@ MediaFormatReader::VideoSkipReset(uint32_t aSkipped)
|
||||
Reset(TrackType::kVideoTrack);
|
||||
|
||||
if (mDecoder) {
|
||||
mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
|
||||
mDecoder->NotifyDecodedFrames({ aSkipped, 0, aSkipped });
|
||||
}
|
||||
|
||||
mVideo.mNumSamplesSkippedTotal += aSkipped;
|
||||
@ -1757,6 +1772,8 @@ MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
|
||||
LOGV("Video seeked to %lld", aTime.ToMicroseconds());
|
||||
mVideo.mSeekRequest.Complete();
|
||||
|
||||
mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe;
|
||||
|
||||
SetVideoDecodeThreshold();
|
||||
|
||||
if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) {
|
||||
@ -1773,6 +1790,13 @@ MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::OnVideoSeekFailed(DemuxerFailureReason aFailure)
|
||||
{
|
||||
mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe;
|
||||
OnSeekFailed(TrackType::kVideoTrack, aFailure);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetVideoDecodeThreshold()
|
||||
{
|
||||
@ -1833,6 +1857,12 @@ MediaFormatReader::OnAudioSeekCompleted(media::TimeUnit aTime)
|
||||
mSeekPromise.Resolve(aTime, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::OnAudioSeekFailed(DemuxerFailureReason aFailure)
|
||||
{
|
||||
OnSeekFailed(TrackType::kAudioTrack, aFailure);
|
||||
}
|
||||
|
||||
media::TimeIntervals
|
||||
MediaFormatReader::GetBuffered()
|
||||
{
|
||||
|
@ -515,6 +515,11 @@ private:
|
||||
// delta there.
|
||||
uint64_t mLastReportedNumDecodedFrames;
|
||||
|
||||
// Timestamp of the previous decoded keyframe, in microseconds.
|
||||
int64_t mPreviousDecodedKeyframeTime_us;
|
||||
// Default mLastDecodedKeyframeTime_us value, must be bigger than anything.
|
||||
static const int64_t sNoPreviousDecodedKeyframe = INT64_MAX;
|
||||
|
||||
layers::LayersBackend mLayersBackendType;
|
||||
|
||||
// Metadata objects
|
||||
@ -546,18 +551,12 @@ private:
|
||||
void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
|
||||
void DoVideoSeek();
|
||||
void OnVideoSeekCompleted(media::TimeUnit aTime);
|
||||
void OnVideoSeekFailed(DemuxerFailureReason aFailure)
|
||||
{
|
||||
OnSeekFailed(TrackType::kVideoTrack, aFailure);
|
||||
}
|
||||
void OnVideoSeekFailed(DemuxerFailureReason aFailure);
|
||||
bool mSeekScheduled;
|
||||
|
||||
void DoAudioSeek();
|
||||
void OnAudioSeekCompleted(media::TimeUnit aTime);
|
||||
void OnAudioSeekFailed(DemuxerFailureReason aFailure)
|
||||
{
|
||||
OnSeekFailed(TrackType::kAudioTrack, aFailure);
|
||||
}
|
||||
void OnAudioSeekFailed(DemuxerFailureReason aFailure);
|
||||
// The SeekTarget that was last given to Seek()
|
||||
SeekTarget mOriginalSeekTarget;
|
||||
// Temporary seek information while we wait for the data
|
||||
|
@ -361,6 +361,9 @@ public:
|
||||
if (mVideoDevice) {
|
||||
mVideoDevice->GetSource()->GetSettings(aOutSettings);
|
||||
}
|
||||
if (mAudioDevice) {
|
||||
mAudioDevice->GetSource()->GetSettings(aOutSettings);
|
||||
}
|
||||
}
|
||||
|
||||
// implement in .cpp to avoid circular dependency with MediaOperationTask
|
||||
@ -1618,7 +1621,7 @@ already_AddRefed<MediaManager::PledgeSourceSet>
|
||||
MediaManager::EnumerateRawDevices(uint64_t aWindowId,
|
||||
MediaSourceEnum aVideoType,
|
||||
MediaSourceEnum aAudioType,
|
||||
bool aFake, bool aFakeTracks)
|
||||
bool aFake)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aVideoType != MediaSourceEnum::Other ||
|
||||
@ -1639,15 +1642,9 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId,
|
||||
}
|
||||
}
|
||||
|
||||
if (!aFake) {
|
||||
// Fake tracks only make sense when we have a fake stream.
|
||||
aFakeTracks = false;
|
||||
}
|
||||
|
||||
MediaManager::PostTask(NewTaskFrom([id, aWindowId, audioLoopDev,
|
||||
videoLoopDev, aVideoType,
|
||||
aAudioType, aFake,
|
||||
aFakeTracks]() mutable {
|
||||
aAudioType, aFake]() mutable {
|
||||
// Only enumerate what's asked for, and only fake cams and mics.
|
||||
bool hasVideo = aVideoType != MediaSourceEnum::Other;
|
||||
bool hasAudio = aAudioType != MediaSourceEnum::Other;
|
||||
@ -1656,7 +1653,7 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId,
|
||||
|
||||
RefPtr<MediaEngine> fakeBackend, realBackend;
|
||||
if (fakeCams || fakeMics) {
|
||||
fakeBackend = new MediaEngineDefault(aFakeTracks);
|
||||
fakeBackend = new MediaEngineDefault();
|
||||
}
|
||||
if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) {
|
||||
RefPtr<MediaManager> manager = MediaManager_GetInstance();
|
||||
@ -2345,14 +2342,11 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
bool fake = c.mFake.WasPassed()? c.mFake.Value() :
|
||||
Preferences::GetBool("media.navigator.streams.fake");
|
||||
|
||||
bool fakeTracks = c.mFakeTracks.WasPassed()? c.mFakeTracks.Value() : false;
|
||||
|
||||
bool askPermission = !privileged &&
|
||||
(!fake || Preferences::GetBool("media.navigator.permission.fake"));
|
||||
|
||||
RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
|
||||
audioType, fake,
|
||||
fakeTracks);
|
||||
audioType, fake);
|
||||
p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission,
|
||||
prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable {
|
||||
|
||||
@ -2539,7 +2533,7 @@ already_AddRefed<MediaManager::PledgeSourceSet>
|
||||
MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
|
||||
MediaSourceEnum aVideoType,
|
||||
MediaSourceEnum aAudioType,
|
||||
bool aFake, bool aFakeTracks)
|
||||
bool aFake)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsPIDOMWindowInner* window =
|
||||
@ -2568,13 +2562,12 @@ MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
|
||||
RefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, privateBrowsing,
|
||||
persist);
|
||||
p->Then([id, aWindowId, aVideoType, aAudioType,
|
||||
aFake, aFakeTracks](const nsCString& aOriginKey) mutable {
|
||||
aFake](const nsCString& aOriginKey) mutable {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
||||
|
||||
RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId,
|
||||
aVideoType, aAudioType,
|
||||
aFake, aFakeTracks);
|
||||
RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aVideoType,
|
||||
aAudioType, aFake);
|
||||
p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
|
||||
UniquePtr<SourceSet> devices(aDevices); // secondary result
|
||||
|
||||
|
@ -96,7 +96,7 @@ protected:
|
||||
nsString mID;
|
||||
dom::MediaSourceEnum mMediaSource;
|
||||
RefPtr<MediaEngineSource> mSource;
|
||||
RefPtr<MediaEngineSource::BaseAllocationHandle> mAllocationHandle;
|
||||
RefPtr<MediaEngineSource::AllocationHandle> mAllocationHandle;
|
||||
public:
|
||||
dom::MediaSourceEnum GetMediaSource() {
|
||||
return mMediaSource;
|
||||
@ -273,12 +273,12 @@ private:
|
||||
EnumerateRawDevices(uint64_t aWindowId,
|
||||
dom::MediaSourceEnum aVideoType,
|
||||
dom::MediaSourceEnum aAudioType,
|
||||
bool aFake, bool aFakeTracks);
|
||||
bool aFake);
|
||||
already_AddRefed<PledgeSourceSet>
|
||||
EnumerateDevicesImpl(uint64_t aWindowId,
|
||||
dom::MediaSourceEnum aVideoSrcType,
|
||||
dom::MediaSourceEnum aAudioSrcType,
|
||||
bool aFake = false, bool aFakeTracks = false);
|
||||
bool aFake = false);
|
||||
already_AddRefed<PledgeChar>
|
||||
SelectSettings(
|
||||
dom::MediaStreamConstraints& aConstraints,
|
||||
|
@ -36,17 +36,17 @@ public:
|
||||
return mCreationTime;
|
||||
}
|
||||
|
||||
uint32_t TotalVideoFrames()
|
||||
uint32_t TotalVideoFrames() const
|
||||
{
|
||||
return mTotalFrames;
|
||||
}
|
||||
|
||||
uint32_t DroppedVideoFrames()
|
||||
uint32_t DroppedVideoFrames() const
|
||||
{
|
||||
return mDroppedFrames;
|
||||
}
|
||||
|
||||
uint32_t CorruptedVideoFrames()
|
||||
uint32_t CorruptedVideoFrames() const
|
||||
{
|
||||
return mCorruptedFrames;
|
||||
}
|
||||
@ -64,4 +64,4 @@ private:
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_VideoPlaybackQuality_h_ */
|
||||
#endif // mozilla_dom_VideoPlaybackQuality_h_
|
||||
|
@ -155,8 +155,8 @@ bool AndroidMediaReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
// when a frame is a keyframe.
|
||||
#if 0
|
||||
if (!frame.mKeyFrame) {
|
||||
++a.mParsed;
|
||||
++a.mDropped;
|
||||
++a.mStats.mParsedFrames;
|
||||
++a.mStats.mDroppedFrames;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
@ -244,9 +244,9 @@ bool AndroidMediaReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
if (!v) {
|
||||
return false;
|
||||
}
|
||||
a.mParsed++;
|
||||
a.mDecoded++;
|
||||
NS_ASSERTION(a.mDecoded <= a.mParsed, "Expect to decode fewer frames than parsed in AndroidMedia...");
|
||||
a.mStats.mParsedFrames++;
|
||||
a.mStats.mDecodedFrames++;
|
||||
NS_ASSERTION(a.mStats.mDecodedFrames <= a.mStats.mParsedFrames, "Expect to decode fewer frames than parsed in AndroidMedia...");
|
||||
|
||||
// Since MPAPI doesn't give us the end time of frames, we keep one frame
|
||||
// buffered in AndroidMediaReader and push it into the queue as soon
|
||||
|
@ -406,7 +406,7 @@ VideoSink::UpdateRenderedVideoFrames()
|
||||
}
|
||||
++framesRemoved;
|
||||
if (!currentFrame->As<VideoData>()->mSentToCompositor) {
|
||||
mFrameStats.NotifyDecodedFrames(0, 0, 1);
|
||||
mFrameStats.NotifyDecodedFrames({ 0, 0, 1 });
|
||||
VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
|
||||
currentFrame->mTime, clockTime);
|
||||
}
|
||||
|
@ -921,7 +921,7 @@ bool OggReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
}
|
||||
nsAutoRef<ogg_packet> autoRelease(packet);
|
||||
|
||||
a.mParsed++;
|
||||
a.mStats.mParsedFrames++;
|
||||
NS_ASSERTION(packet && packet->granulepos != -1,
|
||||
"Must know first packet's granulepos");
|
||||
bool eos = packet->e_o_s;
|
||||
@ -931,7 +931,7 @@ bool OggReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
{
|
||||
aKeyframeSkip = false;
|
||||
nsresult res = DecodeTheora(packet, aTimeThreshold);
|
||||
a.mDecoded++;
|
||||
a.mStats.mDecodedFrames++;
|
||||
if (NS_FAILED(res)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "AudioOffloadPlayer.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsITimer.h"
|
||||
#include "MediaOmxCommonDecoder.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
@ -58,8 +59,7 @@ AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
|
||||
mSampleRate(0),
|
||||
mStartPosUs(0),
|
||||
mPositionTimeMediaUs(-1),
|
||||
mInputBuffer(nullptr),
|
||||
mObserver(aObserver)
|
||||
mInputBuffer(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -73,6 +73,17 @@ AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
|
||||
#endif
|
||||
mAudioSink = new AudioOutput(mSessionId,
|
||||
IPCThreadState::self()->getCallingUid());
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_GetMainThread(getter_AddRefs(thread)));
|
||||
mPositionChanged = mOnPositionChanged.Connect(
|
||||
thread, aObserver, &MediaOmxCommonDecoder::NotifyOffloadPlayerPositionChanged);
|
||||
mPlaybackEnded = mOnPlaybackEnded.Connect(
|
||||
thread, aObserver, &MediaDecoder::PlaybackEnded);
|
||||
mPlayerTearDown = mOnPlayerTearDown.Connect(
|
||||
thread, aObserver, &MediaOmxCommonDecoder::AudioOffloadTearDown);
|
||||
mSeekingStarted = mOnSeekingStarted.Connect(
|
||||
thread, aObserver, &MediaDecoder::SeekingStarted);
|
||||
}
|
||||
|
||||
AudioOffloadPlayer::~AudioOffloadPlayer()
|
||||
@ -83,6 +94,13 @@ AudioOffloadPlayer::~AudioOffloadPlayer()
|
||||
#else
|
||||
AudioSystem::releaseAudioSessionId(mSessionId);
|
||||
#endif
|
||||
|
||||
// Disconnect the listeners to prevent notifications from reaching
|
||||
// the MediaOmxCommonDecoder object after shutdown.
|
||||
mPositionChanged.Disconnect();
|
||||
mPlaybackEnded.Disconnect();
|
||||
mPlayerTearDown.Disconnect();
|
||||
mSeekingStarted.Disconnect();
|
||||
}
|
||||
|
||||
void AudioOffloadPlayer::SetSource(const sp<MediaSource> &aSource)
|
||||
@ -353,12 +371,7 @@ status_t AudioOffloadPlayer::DoSeek()
|
||||
mStartPosUs = mSeekTarget.GetTime().ToMicroseconds();
|
||||
|
||||
if (!mSeekPromise.IsEmpty()) {
|
||||
nsCOMPtr<nsIRunnable> nsEvent =
|
||||
NewRunnableMethod<MediaDecoderEventVisibility>(
|
||||
mObserver,
|
||||
&MediaDecoder::SeekingStarted,
|
||||
mSeekTarget.mEventVisibility);
|
||||
NS_DispatchToCurrentThread(nsEvent);
|
||||
mOnSeekingStarted.Notify(mSeekTarget.mEventVisibility);
|
||||
}
|
||||
|
||||
if (mPlaying) {
|
||||
@ -425,14 +438,12 @@ void AudioOffloadPlayer::NotifyAudioEOS()
|
||||
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
|
||||
mSeekPromise.Resolve(val, __func__);
|
||||
}
|
||||
NS_DispatchToMainThread(NewRunnableMethod(mObserver,
|
||||
&MediaDecoder::PlaybackEnded));
|
||||
mOnPlaybackEnded.Notify();
|
||||
}
|
||||
|
||||
void AudioOffloadPlayer::NotifyPositionChanged()
|
||||
{
|
||||
NS_DispatchToMainThread(NewRunnableMethod(mObserver,
|
||||
&MediaOmxCommonDecoder::NotifyOffloadPlayerPositionChanged));
|
||||
mOnPositionChanged.Notify();
|
||||
}
|
||||
|
||||
void AudioOffloadPlayer::NotifyAudioTearDown()
|
||||
@ -446,8 +457,7 @@ void AudioOffloadPlayer::NotifyAudioTearDown()
|
||||
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
|
||||
mSeekPromise.Resolve(val, __func__);
|
||||
}
|
||||
NS_DispatchToMainThread(NewRunnableMethod(mObserver,
|
||||
&MediaOmxCommonDecoder::AudioOffloadTearDown));
|
||||
mOnPlayerTearDown.Notify();
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "AudioOutput.h"
|
||||
#include "AudioOffloadPlayerBase.h"
|
||||
#include "MediaDecoderOwner.h"
|
||||
#include "MediaOmxCommonDecoder.h"
|
||||
#include "MediaEventSource.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -58,6 +58,8 @@ class WakeLock;
|
||||
* offload playback
|
||||
*/
|
||||
|
||||
class MediaOmxCommonDecoder;
|
||||
|
||||
class AudioOffloadPlayer : public AudioOffloadPlayerBase
|
||||
{
|
||||
typedef android::Mutex Mutex;
|
||||
@ -73,7 +75,7 @@ public:
|
||||
SEEK_COMPLETE
|
||||
};
|
||||
|
||||
AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr);
|
||||
AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder);
|
||||
|
||||
~AudioOffloadPlayer();
|
||||
|
||||
@ -175,9 +177,6 @@ private:
|
||||
// Buffer used to get date from audio source. Used in offload callback thread
|
||||
MediaBuffer* mInputBuffer;
|
||||
|
||||
// MediaOmxCommonDecoder object used mainly to notify the audio sink status
|
||||
MediaOmxCommonDecoder* mObserver;
|
||||
|
||||
TimeStamp mLastFireUpdateTime;
|
||||
|
||||
// Timer to trigger position changed events
|
||||
@ -192,6 +191,15 @@ private:
|
||||
// Used only from main thread so no lock is needed.
|
||||
RefPtr<mozilla::dom::WakeLock> mWakeLock;
|
||||
|
||||
MediaEventProducer<void> mOnPositionChanged;
|
||||
MediaEventProducer<void> mOnPlaybackEnded;
|
||||
MediaEventProducer<void> mOnPlayerTearDown;
|
||||
MediaEventProducer<MediaDecoderEventVisibility> mOnSeekingStarted;
|
||||
MediaEventListener mPositionChanged;
|
||||
MediaEventListener mPlaybackEnded;
|
||||
MediaEventListener mPlayerTearDown;
|
||||
MediaEventListener mSeekingStarted;
|
||||
|
||||
// Provide the playback position in microseconds from total number of
|
||||
// frames played by audio track
|
||||
int64_t GetOutputPlayPositionUs_l() const;
|
||||
|
@ -165,6 +165,7 @@ void
|
||||
MediaOmxCommonDecoder::AudioOffloadTearDown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!IsShutdown());
|
||||
DECODER_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
|
||||
|
||||
// mAudioOffloadPlayer can be null here if ResumeStateMachine was called
|
||||
@ -286,4 +287,11 @@ MediaOmxCommonDecoder::CreateStateMachine()
|
||||
return CreateStateMachineFromReader(mReader);
|
||||
}
|
||||
|
||||
void
|
||||
MediaOmxCommonDecoder::Shutdown()
|
||||
{
|
||||
mAudioOffloadPlayer = nullptr;
|
||||
MediaDecoder::Shutdown();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
|
||||
void NotifyOffloadPlayerPositionChanged() { UpdateLogicalPosition(); }
|
||||
|
||||
void Shutdown() override;
|
||||
|
||||
protected:
|
||||
virtual ~MediaOmxCommonDecoder();
|
||||
void PauseStateMachine();
|
||||
|
@ -357,7 +357,7 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
continue;
|
||||
}
|
||||
|
||||
a.mParsed++;
|
||||
a.mStats.mParsedFrames++;
|
||||
if (frame.mShouldSkip && mSkipCount < MAX_DROPPED_FRAMES) {
|
||||
mSkipCount++;
|
||||
continue;
|
||||
@ -434,8 +434,8 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
return false;
|
||||
}
|
||||
|
||||
a.mDecoded++;
|
||||
NS_ASSERTION(a.mDecoded <= a.mParsed, "Expect to decode fewer frames than parsed in OMX decoder...");
|
||||
a.mStats.mDecodedFrames++;
|
||||
NS_ASSERTION(a.mStats.mDecodedFrames <= a.mStats.mParsedFrames, "Expect to decode fewer frames than parsed in OMX decoder...");
|
||||
|
||||
mVideoQueue.Push(v);
|
||||
|
||||
|
@ -156,7 +156,7 @@ bool RawReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
return false;
|
||||
}
|
||||
|
||||
a.mParsed++;
|
||||
a.mStats.mParsedFrames++;
|
||||
|
||||
if (currentFrameTime >= aTimeThreshold)
|
||||
break;
|
||||
@ -200,7 +200,7 @@ bool RawReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
|
||||
mVideoQueue.Push(v);
|
||||
mCurrentFrame++;
|
||||
a.mDecoded++;
|
||||
a.mStats.mDecodedFrames++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -856,6 +856,8 @@ tags = webvtt
|
||||
[test_timeupdate_small_files.html]
|
||||
[test_trackelementevent.html]
|
||||
tags = webvtt
|
||||
[test_trackelementsrc.html]
|
||||
tags = webvtt
|
||||
[test_trackevent.html]
|
||||
tags = webvtt
|
||||
[test_unseekable.html]
|
||||
|
@ -10,8 +10,11 @@
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
function startTest() {
|
||||
navigator.mediaDevices.getUserMedia({audio:true, video:true, fake:true, fakeTracks:true})
|
||||
.then(function(stream) {
|
||||
navigator.mediaDevices.getUserMedia({audio:true, video:true, fake:true})
|
||||
.then(function(orgStream) {
|
||||
var a = orgStream.getAudioTracks()[0];
|
||||
var v = orgStream.getVideoTracks()[0];
|
||||
var stream = new MediaStream([a, a, a, a, v, v, v].map(track => track.clone()));
|
||||
var element = document.createElement("video");
|
||||
|
||||
element.onloadedmetadata = function() {
|
||||
@ -20,8 +23,7 @@ function startTest() {
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
mStream = stream;
|
||||
element.srcObject = mStream;
|
||||
element.srcObject = stream;
|
||||
element.play();
|
||||
})
|
||||
.catch(function(reason) {
|
||||
|
55
dom/media/test/test_trackelementsrc.html
Normal file
55
dom/media/test/test_trackelementsrc.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1281418 - Change the src attribue for TrackElement.</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
|
||||
["media.webvtt.regions.enabled", true]]}, function() {
|
||||
|
||||
var video = document.createElement("video");
|
||||
video.src = "seek.webm";
|
||||
video.preload = "metadata";
|
||||
var trackElement = document.createElement("track");
|
||||
trackElement.src = "basic.vtt";
|
||||
trackElement.default = true;
|
||||
|
||||
document.getElementById("content").appendChild(video);
|
||||
video.appendChild(trackElement);
|
||||
|
||||
video.addEventListener("loadedmetadata", function metadata() {
|
||||
if (trackElement.readyState <= 1) {
|
||||
return setTimeout(metadata, 0);
|
||||
}
|
||||
is(video.textTracks.length, 1, "Length should be 1.");
|
||||
is(video.textTracks[0].cues.length, 6, "Cue length should be 6.");
|
||||
|
||||
trackElement.src = "sequential.vtt";
|
||||
trackElement.track.mode = "showing";
|
||||
video.play();
|
||||
});
|
||||
|
||||
video.addEventListener("ended", function end() {
|
||||
is(trackElement.readyState, 2, "readyState should be 2.")
|
||||
is(video.textTracks.length, 1, "Length should be 1.");
|
||||
is(video.textTracks[0].cues.length, 3, "Cue length should be 3.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -42,8 +42,7 @@ BufferDecoder::GetResource() const
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
uint32_t aDropped)
|
||||
BufferDecoder::NotifyDecodedFrames(const FrameStatisticsData& aStats)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
@ -33,8 +33,7 @@ public:
|
||||
|
||||
MediaResource* GetResource() const final override;
|
||||
|
||||
void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
uint32_t aDropped) final override;
|
||||
void NotifyDecodedFrames(const FrameStatisticsData& aStats) final override;
|
||||
|
||||
VideoFrameContainer* GetVideoFrameContainer() final override;
|
||||
layers::ImageContainer* GetImageContainer() final override;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "DOMMediaStream.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaTrackConstraints.h"
|
||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
|
||||
@ -32,7 +33,6 @@ enum {
|
||||
*/
|
||||
class MediaEngineVideoSource;
|
||||
class MediaEngineAudioSource;
|
||||
class MediaEnginePrefs;
|
||||
|
||||
enum MediaEngineState {
|
||||
kAllocated,
|
||||
@ -79,154 +79,6 @@ protected:
|
||||
virtual ~MediaEngine() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback interface for TakePhoto(). Either PhotoComplete() or PhotoError()
|
||||
* should be called.
|
||||
*/
|
||||
class MediaEnginePhotoCallback {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEnginePhotoCallback)
|
||||
|
||||
// aBlob is the image captured by MediaEngineSource. It is
|
||||
// called on main thread.
|
||||
virtual nsresult PhotoComplete(already_AddRefed<dom::Blob> aBlob) = 0;
|
||||
|
||||
// It is called on main thread. aRv is the error code.
|
||||
virtual nsresult PhotoError(nsresult aRv) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~MediaEnginePhotoCallback() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Common abstract base class for audio and video sources.
|
||||
*/
|
||||
class MediaEngineSource : public nsISupports
|
||||
{
|
||||
public:
|
||||
// code inside webrtc.org assumes these sizes; don't use anything smaller
|
||||
// without verifying it's ok
|
||||
static const unsigned int kMaxDeviceNameLength = 128;
|
||||
static const unsigned int kMaxUniqueIdLength = 256;
|
||||
|
||||
virtual ~MediaEngineSource() {}
|
||||
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
/* Populate the human readable name of this device in the nsAString */
|
||||
virtual void GetName(nsAString&) const = 0;
|
||||
|
||||
/* Populate the UUID of this device in the nsACString */
|
||||
virtual void GetUUID(nsACString&) const = 0;
|
||||
|
||||
class BaseAllocationHandle
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BaseAllocationHandle);
|
||||
protected:
|
||||
virtual ~BaseAllocationHandle() {}
|
||||
};
|
||||
|
||||
/* Release the device back to the system. */
|
||||
virtual nsresult Deallocate(BaseAllocationHandle* aHandle) = 0;
|
||||
|
||||
/* Start the device and add the track to the provided SourceMediaStream, with
|
||||
* the provided TrackID. You may start appending data to the track
|
||||
* immediately after. */
|
||||
virtual nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) = 0;
|
||||
|
||||
/* tell the source if there are any direct listeners attached */
|
||||
virtual void SetDirectListeners(bool) = 0;
|
||||
|
||||
/* Called when the stream wants more data */
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
const PrincipalHandle& aPrincipalHandle) = 0;
|
||||
|
||||
/* Stop the device and release the corresponding MediaStream */
|
||||
virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
|
||||
|
||||
/* Restart with new capability */
|
||||
virtual nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) = 0;
|
||||
|
||||
/* Returns true if a source represents a fake capture device and
|
||||
* false otherwise
|
||||
*/
|
||||
virtual bool IsFake() = 0;
|
||||
|
||||
/* Returns the type of media source (camera, microphone, screen, window, etc) */
|
||||
virtual dom::MediaSourceEnum GetMediaSource() const = 0;
|
||||
|
||||
/* If implementation of MediaEngineSource supports TakePhoto(), the picture
|
||||
* should be return via aCallback object. Otherwise, it returns NS_ERROR_NOT_IMPLEMENTED.
|
||||
* Currently, only Gonk MediaEngineSource implementation supports it.
|
||||
*/
|
||||
virtual nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) = 0;
|
||||
|
||||
/* Return false if device is currently allocated or started */
|
||||
bool IsAvailable() {
|
||||
if (mState == kAllocated || mState == kStarted) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* It is an error to call Start() before an Allocate(), and Stop() before
|
||||
* a Start(). Only Allocate() may be called after a Deallocate(). */
|
||||
|
||||
void SetHasFakeTracks(bool aHasFakeTracks) {
|
||||
mHasFakeTracks = aHasFakeTracks;
|
||||
}
|
||||
|
||||
/* This call reserves but does not start the device. */
|
||||
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) = 0;
|
||||
|
||||
virtual uint32_t GetBestFitnessDistance(
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
const nsString& aDeviceId) const = 0;
|
||||
|
||||
void GetSettings(dom::MediaTrackSettings& aOutSettings)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aOutSettings = mSettings;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Only class' own members can be initialized in constructor initializer list.
|
||||
explicit MediaEngineSource(MediaEngineState aState)
|
||||
: mState(aState)
|
||||
#ifdef DEBUG
|
||||
, mOwningThread(PR_GetCurrentThread())
|
||||
#endif
|
||||
, mHasFakeTracks(false)
|
||||
{}
|
||||
|
||||
void AssertIsOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
|
||||
}
|
||||
|
||||
MediaEngineState mState;
|
||||
#ifdef DEBUG
|
||||
PRThread* mOwningThread;
|
||||
#endif
|
||||
bool mHasFakeTracks;
|
||||
// Main-thread only:
|
||||
dom::MediaTrackSettings mSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Video source and friends.
|
||||
*/
|
||||
@ -301,6 +153,291 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback interface for TakePhoto(). Either PhotoComplete() or PhotoError()
|
||||
* should be called.
|
||||
*/
|
||||
class MediaEnginePhotoCallback {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEnginePhotoCallback)
|
||||
|
||||
// aBlob is the image captured by MediaEngineSource. It is
|
||||
// called on main thread.
|
||||
virtual nsresult PhotoComplete(already_AddRefed<dom::Blob> aBlob) = 0;
|
||||
|
||||
// It is called on main thread. aRv is the error code.
|
||||
virtual nsresult PhotoError(nsresult aRv) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~MediaEnginePhotoCallback() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Common abstract base class for audio and video sources.
|
||||
*
|
||||
* By default, the base class implements Allocate and Deallocate using its
|
||||
* UpdateSingleSource pattern, which manages allocation handles and calculates
|
||||
* net constraints from competing allocations and updates a single shared device.
|
||||
*
|
||||
* Classes that don't operate as a single shared device can override Allocate
|
||||
* and Deallocate and simply not pass the methods up.
|
||||
*/
|
||||
class MediaEngineSource : public nsISupports,
|
||||
protected MediaConstraintsHelper
|
||||
{
|
||||
public:
|
||||
// code inside webrtc.org assumes these sizes; don't use anything smaller
|
||||
// without verifying it's ok
|
||||
static const unsigned int kMaxDeviceNameLength = 128;
|
||||
static const unsigned int kMaxUniqueIdLength = 256;
|
||||
|
||||
virtual ~MediaEngineSource()
|
||||
{
|
||||
if (!mInShutdown) {
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Shutdown()
|
||||
{
|
||||
mInShutdown = true;
|
||||
};
|
||||
|
||||
/* Populate the human readable name of this device in the nsAString */
|
||||
virtual void GetName(nsAString&) const = 0;
|
||||
|
||||
/* Populate the UUID of this device in the nsACString */
|
||||
virtual void GetUUID(nsACString&) const = 0;
|
||||
|
||||
class AllocationHandle
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AllocationHandle);
|
||||
protected:
|
||||
~AllocationHandle() {}
|
||||
public:
|
||||
AllocationHandle(const dom::MediaTrackConstraints& aConstraints,
|
||||
const nsACString& aOrigin,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
: mConstraints(aConstraints),
|
||||
mOrigin(aOrigin),
|
||||
mPrefs(aPrefs),
|
||||
mDeviceId(aDeviceId) {}
|
||||
public:
|
||||
NormalizedConstraints mConstraints;
|
||||
nsCString mOrigin;
|
||||
MediaEnginePrefs mPrefs;
|
||||
nsString mDeviceId;
|
||||
};
|
||||
|
||||
/* Release the device back to the system. */
|
||||
virtual nsresult Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
MOZ_ASSERT(aHandle);
|
||||
RefPtr<AllocationHandle> handle = aHandle;
|
||||
|
||||
class Comparator {
|
||||
public:
|
||||
static bool Equals(const RefPtr<AllocationHandle>& a,
|
||||
const RefPtr<AllocationHandle>& b) {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
};
|
||||
MOZ_ASSERT(mRegisteredHandles.Contains(handle, Comparator()));
|
||||
mRegisteredHandles.RemoveElementAt(mRegisteredHandles.IndexOf(handle, 0,
|
||||
Comparator()));
|
||||
if (mRegisteredHandles.Length() && !mInShutdown) {
|
||||
// Whenever constraints are removed, other parties may get closer to ideal.
|
||||
auto& first = mRegisteredHandles[0];
|
||||
const char* badConstraint = nullptr;
|
||||
return ReevaluateAllocation(nullptr, nullptr, first->mPrefs,
|
||||
first->mDeviceId, &badConstraint);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* Start the device and add the track to the provided SourceMediaStream, with
|
||||
* the provided TrackID. You may start appending data to the track
|
||||
* immediately after. */
|
||||
virtual nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) = 0;
|
||||
|
||||
/* tell the source if there are any direct listeners attached */
|
||||
virtual void SetDirectListeners(bool) = 0;
|
||||
|
||||
/* Called when the stream wants more data */
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
const PrincipalHandle& aPrincipalHandle) = 0;
|
||||
|
||||
/* Stop the device and release the corresponding MediaStream */
|
||||
virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
|
||||
|
||||
/* Restart with new capability */
|
||||
virtual nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) = 0;
|
||||
|
||||
/* Returns true if a source represents a fake capture device and
|
||||
* false otherwise
|
||||
*/
|
||||
virtual bool IsFake() = 0;
|
||||
|
||||
/* Returns the type of media source (camera, microphone, screen, window, etc) */
|
||||
virtual dom::MediaSourceEnum GetMediaSource() const = 0;
|
||||
|
||||
/* If implementation of MediaEngineSource supports TakePhoto(), the picture
|
||||
* should be return via aCallback object. Otherwise, it returns NS_ERROR_NOT_IMPLEMENTED.
|
||||
* Currently, only Gonk MediaEngineSource implementation supports it.
|
||||
*/
|
||||
virtual nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) = 0;
|
||||
|
||||
/* Return false if device is currently allocated or started */
|
||||
bool IsAvailable() {
|
||||
if (mState == kAllocated || mState == kStarted) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* It is an error to call Start() before an Allocate(), and Stop() before
|
||||
* a Start(). Only Allocate() may be called after a Deallocate(). */
|
||||
|
||||
/* This call reserves but does not start the device. */
|
||||
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aOutHandle);
|
||||
RefPtr<AllocationHandle> handle = new AllocationHandle(aConstraints, aOrigin,
|
||||
aPrefs, aDeviceId);
|
||||
nsresult rv = ReevaluateAllocation(handle, nullptr, aPrefs, aDeviceId,
|
||||
aOutBadConstraint);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mRegisteredHandles.AppendElement(handle);
|
||||
handle.forget(aOutHandle);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual uint32_t GetBestFitnessDistance(
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
const nsString& aDeviceId) const = 0;
|
||||
|
||||
void GetSettings(dom::MediaTrackSettings& aOutSettings)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aOutSettings = mSettings;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Only class' own members can be initialized in constructor initializer list.
|
||||
explicit MediaEngineSource(MediaEngineState aState)
|
||||
: mState(aState)
|
||||
#ifdef DEBUG
|
||||
, mOwningThread(PR_GetCurrentThread())
|
||||
#endif
|
||||
, mInShutdown(false)
|
||||
{}
|
||||
|
||||
/* UpdateSingleSource - Centralized abstract function to implement in those
|
||||
* cases where a single device is being shared between users. Should apply net
|
||||
* constraints and restart the device as needed.
|
||||
*
|
||||
* aHandle - New or existing handle, or null to update after removal.
|
||||
* aNetConstraints - Net constraints to be applied to the single device.
|
||||
* aPrefs - As passed in (in case of changes in about:config).
|
||||
* aDeviceId - As passed in (origin dependent).
|
||||
* aOutBadConstraint - Result: nonzero if failed to apply. Name of culprit.
|
||||
*/
|
||||
|
||||
virtual nsresult
|
||||
UpdateSingleSource(const AllocationHandle* aHandle,
|
||||
const NormalizedConstraints& aNetConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
/* ReevaluateAllocation - Call to change constraints for an allocation of
|
||||
* a single device. Manages allocation handles, calculates net constraints
|
||||
* from all competing allocations, and calls UpdateSingleSource with the net
|
||||
* result, to restart the single device as needed.
|
||||
*
|
||||
* aHandle - New or existing handle, or null to update after removal.
|
||||
* aConstraintsUpdate - Constraints to be applied to existing handle, or null.
|
||||
* aPrefs - As passed in (in case of changes from about:config).
|
||||
* aDeviceId - As passed in (origin-dependent id).
|
||||
* aOutBadConstraint - Result: nonzero if failed to apply. Name of culprit.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
ReevaluateAllocation(AllocationHandle* aHandle,
|
||||
NormalizedConstraints* aConstraintsUpdate,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
// aHandle and/or aConstraintsUpdate may be nullptr (see below)
|
||||
|
||||
AutoTArray<const NormalizedConstraints*, 10> allConstraints;
|
||||
for (auto& registered : mRegisteredHandles) {
|
||||
if (aConstraintsUpdate && registered.get() == aHandle) {
|
||||
continue; // Don't count old constraints
|
||||
}
|
||||
allConstraints.AppendElement(®istered->mConstraints);
|
||||
}
|
||||
if (aConstraintsUpdate) {
|
||||
allConstraints.AppendElement(aConstraintsUpdate);
|
||||
} else if (aHandle) {
|
||||
// In the case of AddShareOfSingleSource, the handle isn't registered yet.
|
||||
allConstraints.AppendElement(&aHandle->mConstraints);
|
||||
}
|
||||
|
||||
NormalizedConstraints netConstraints(allConstraints);
|
||||
if (netConstraints.mBadConstraint) {
|
||||
*aOutBadConstraint = netConstraints.mBadConstraint;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = UpdateSingleSource(aHandle, netConstraints, aPrefs, aDeviceId,
|
||||
aOutBadConstraint);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (aHandle && aConstraintsUpdate) {
|
||||
aHandle->mConstraints = *aConstraintsUpdate;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void AssertIsOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
|
||||
}
|
||||
|
||||
MediaEngineState mState;
|
||||
#ifdef DEBUG
|
||||
PRThread* mOwningThread;
|
||||
#endif
|
||||
nsTArray<RefPtr<AllocationHandle>> mRegisteredHandles;
|
||||
bool mInShutdown;
|
||||
|
||||
// Main-thread only:
|
||||
dom::MediaTrackSettings mSettings;
|
||||
};
|
||||
|
||||
class MediaEngineVideoSource : public MediaEngineSource
|
||||
{
|
||||
public:
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define MediaEngineCameraVideoSource_h
|
||||
|
||||
#include "MediaEngine.h"
|
||||
#include "MediaTrackConstraints.h"
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
@ -21,10 +20,10 @@ bool operator == (const webrtc::CaptureCapability& a,
|
||||
bool operator != (const webrtc::CaptureCapability& a,
|
||||
const webrtc::CaptureCapability& b);
|
||||
|
||||
class MediaEngineCameraVideoSource : public MediaEngineVideoSource,
|
||||
protected MediaConstraintsHelper
|
||||
class MediaEngineCameraVideoSource : public MediaEngineVideoSource
|
||||
{
|
||||
public:
|
||||
// Some subclasses use an index to track multiple instances.
|
||||
explicit MediaEngineCameraVideoSource(int aIndex,
|
||||
const char* aMonitorName = "Camera.Monitor")
|
||||
: MediaEngineVideoSource(kReleased)
|
||||
@ -33,11 +32,12 @@ public:
|
||||
, mHeight(0)
|
||||
, mInitDone(false)
|
||||
, mHasDirectListeners(false)
|
||||
, mNrAllocations(0)
|
||||
, mCaptureIndex(aIndex)
|
||||
, mTrackID(0)
|
||||
{}
|
||||
|
||||
explicit MediaEngineCameraVideoSource(const char* aMonitorName = "Camera.Monitor")
|
||||
: MediaEngineCameraVideoSource(0, aMonitorName) {}
|
||||
|
||||
void GetName(nsAString& aName) const override;
|
||||
void GetUUID(nsACString& aUUID) const override;
|
||||
@ -114,7 +114,6 @@ protected:
|
||||
|
||||
bool mInitDone;
|
||||
bool mHasDirectListeners;
|
||||
int mNrAllocations; // When this becomes 0, we shut down HW
|
||||
int mCaptureIndex;
|
||||
TrackID mTrackID;
|
||||
|
||||
|
@ -32,18 +32,13 @@ namespace mozilla {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
// Enable the testing flag fakeTracks and fake in MediaStreamConstraints, will
|
||||
// return you a MediaStream with additional fake video tracks and audio tracks.
|
||||
static const int kFakeVideoTrackCount = 2;
|
||||
static const int kFakeAudioTrackCount = 3;
|
||||
|
||||
NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback)
|
||||
/**
|
||||
* Default video source.
|
||||
*/
|
||||
|
||||
MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
|
||||
: MediaEngineVideoSource(kReleased)
|
||||
: MediaEngineCameraVideoSource("FakeVideo.Monitor")
|
||||
, mTimer(nullptr)
|
||||
, mMonitor("Fake video")
|
||||
, mCb(16), mCr(16)
|
||||
@ -89,29 +84,34 @@ MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConst
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
if (mState != kReleased) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FlattenedConstraints c(aConstraints);
|
||||
|
||||
// Mock failure for automated tests.
|
||||
if (aConstraints.mDeviceId.IsString() &&
|
||||
aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) {
|
||||
if (c.mDeviceId.mIdeal.find(NS_LITERAL_STRING("bad device")) !=
|
||||
c.mDeviceId.mIdeal.end()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
mOpts = aPrefs;
|
||||
mOpts.mWidth = mOpts.mWidth ? mOpts.mWidth : MediaEngine::DEFAULT_43_VIDEO_WIDTH;
|
||||
mOpts.mHeight = mOpts.mHeight ? mOpts.mHeight : MediaEngine::DEFAULT_43_VIDEO_HEIGHT;
|
||||
mOpts.mWidth = c.mWidth.Get(aPrefs.mWidth ? aPrefs.mWidth :
|
||||
MediaEngine::DEFAULT_43_VIDEO_WIDTH);
|
||||
mOpts.mHeight = c.mHeight.Get(aPrefs.mHeight ? aPrefs.mHeight :
|
||||
MediaEngine::DEFAULT_43_VIDEO_HEIGHT);
|
||||
mState = kAllocated;
|
||||
aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineDefaultVideoSource::Deallocate(BaseAllocationHandle* aHandle)
|
||||
MediaEngineDefaultVideoSource::Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
MOZ_ASSERT(!aHandle);
|
||||
if (mState != kStopped && mState != kAllocated) {
|
||||
@ -170,12 +170,6 @@ MediaEngineDefaultVideoSource::Start(SourceMediaStream* aStream, TrackID aID,
|
||||
|
||||
aStream->AddTrack(aID, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED);
|
||||
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeVideoTrackCount; ++i) {
|
||||
aStream->AddTrack(kTrackCount + i, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember TrackID so we can end it later
|
||||
mTrackID = aID;
|
||||
|
||||
@ -205,11 +199,6 @@ MediaEngineDefaultVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
mTimer = nullptr;
|
||||
|
||||
aSource->EndTrack(aID);
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeVideoTrackCount; ++i) {
|
||||
aSource->EndTrack(kTrackCount + i);
|
||||
}
|
||||
}
|
||||
|
||||
mState = kStopped;
|
||||
mImage = nullptr;
|
||||
@ -218,7 +207,7 @@ MediaEngineDefaultVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
|
||||
nsresult
|
||||
MediaEngineDefaultVideoSource::Restart(
|
||||
BaseAllocationHandle* aHandle,
|
||||
AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -309,14 +298,6 @@ MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
// This can fail if either a) we haven't added the track yet, or b)
|
||||
// we've removed or finished the track.
|
||||
aSource->AppendToTrack(aID, &segment);
|
||||
// Generate null data for fake tracks.
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeVideoTrackCount; ++i) {
|
||||
VideoSegment nullSegment;
|
||||
nullSegment.AppendNullData(delta);
|
||||
aSource->AppendToTrack(kTrackCount + i, &nullSegment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,7 +398,7 @@ MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConst
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
if (mState != kReleased) {
|
||||
@ -439,7 +420,7 @@ MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConst
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineDefaultAudioSource::Deallocate(BaseAllocationHandle* aHandle)
|
||||
MediaEngineDefaultAudioSource::Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
MOZ_ASSERT(!aHandle);
|
||||
if (mState != kStopped && mState != kAllocated) {
|
||||
@ -473,15 +454,6 @@ MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID,
|
||||
AppendToSegment(*segment, mBufferSize);
|
||||
mSource->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeAudioTrackCount; ++i) {
|
||||
segment = new AudioSegment();
|
||||
segment->AppendNullData(mBufferSize);
|
||||
mSource->AddAudioTrack(kTrackCount + kFakeVideoTrackCount+i,
|
||||
AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember TrackID so we can finish later
|
||||
mTrackID = aID;
|
||||
|
||||
@ -518,18 +490,13 @@ MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
mTimer = nullptr;
|
||||
|
||||
aSource->EndTrack(aID);
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeAudioTrackCount; ++i) {
|
||||
aSource->EndTrack(kTrackCount + kFakeVideoTrackCount+i);
|
||||
}
|
||||
}
|
||||
|
||||
mState = kStopped;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineDefaultAudioSource::Restart(BaseAllocationHandle* aHandle,
|
||||
MediaEngineDefaultAudioSource::Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -569,14 +536,6 @@ MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
|
||||
AppendToSegment(segment, samplesToAppend);
|
||||
mSource->AppendToTrack(mTrackID, &segment);
|
||||
|
||||
// Generate null data for fake tracks.
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeAudioTrackCount; ++i) {
|
||||
AudioSegment nullSegment;
|
||||
nullSegment.AppendNullData(samplesToAppend);
|
||||
mSource->AppendToTrack(kTrackCount + kFakeVideoTrackCount+i, &nullSegment);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -594,7 +553,6 @@ MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
|
||||
// This no longer is possible since the resolution is being set in Allocate().
|
||||
|
||||
RefPtr<MediaEngineVideoSource> newSource = new MediaEngineDefaultVideoSource();
|
||||
newSource->SetHasFakeTracks(mHasFakeTracks);
|
||||
mVSources.AppendElement(newSource);
|
||||
aVSources->AppendElement(newSource);
|
||||
|
||||
@ -620,7 +578,6 @@ MediaEngineDefault::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
|
||||
if (aASources->Length() == 0) {
|
||||
RefPtr<MediaEngineAudioSource> newSource =
|
||||
new MediaEngineDefaultAudioSource();
|
||||
newSource->SetHasFakeTracks(mHasFakeTracks);
|
||||
mASources.AppendElement(newSource);
|
||||
aASources->AppendElement(newSource);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "VideoSegment.h"
|
||||
#include "AudioSegment.h"
|
||||
#include "StreamTracks.h"
|
||||
#include "MediaEngineCameraVideoSource.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaTrackConstraints.h"
|
||||
|
||||
@ -33,14 +34,11 @@ class MediaEngineDefault;
|
||||
* The default implementation of the MediaEngine interface.
|
||||
*/
|
||||
class MediaEngineDefaultVideoSource : public nsITimerCallback,
|
||||
public MediaEngineVideoSource,
|
||||
private MediaConstraintsHelper
|
||||
public MediaEngineCameraVideoSource
|
||||
{
|
||||
public:
|
||||
MediaEngineDefaultVideoSource();
|
||||
|
||||
void Shutdown() override {};
|
||||
|
||||
void GetName(nsAString&) const override;
|
||||
void GetUUID(nsACString&) const override;
|
||||
|
||||
@ -48,12 +46,12 @@ public:
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) override;
|
||||
nsresult Deallocate(BaseAllocationHandle* aHandle) override;
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override;
|
||||
nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override;
|
||||
nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||
nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -108,14 +106,11 @@ protected:
|
||||
class SineWaveGenerator;
|
||||
|
||||
class MediaEngineDefaultAudioSource : public nsITimerCallback,
|
||||
public MediaEngineAudioSource,
|
||||
private MediaConstraintsHelper
|
||||
public MediaEngineAudioSource
|
||||
{
|
||||
public:
|
||||
MediaEngineDefaultAudioSource();
|
||||
|
||||
void Shutdown() override {};
|
||||
|
||||
void GetName(nsAString&) const override;
|
||||
void GetUUID(nsACString&) const override;
|
||||
|
||||
@ -123,12 +118,12 @@ public:
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) override;
|
||||
nsresult Deallocate(BaseAllocationHandle* aHandle) override;
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override;
|
||||
nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override;
|
||||
nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||
nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -197,11 +192,9 @@ protected:
|
||||
|
||||
class MediaEngineDefault : public MediaEngine
|
||||
{
|
||||
typedef MediaEngine Super;
|
||||
public:
|
||||
explicit MediaEngineDefault(bool aHasFakeTracks = false)
|
||||
: mHasFakeTracks(aHasFakeTracks)
|
||||
, mMutex("mozilla::MediaEngineDefault")
|
||||
{}
|
||||
explicit MediaEngineDefault() : mMutex("mozilla::MediaEngineDefault") {}
|
||||
|
||||
void EnumerateVideoDevices(dom::MediaSourceEnum,
|
||||
nsTArray<RefPtr<MediaEngineVideoSource> >*) override;
|
||||
@ -214,13 +207,8 @@ public:
|
||||
mASources.Clear();
|
||||
};
|
||||
|
||||
protected:
|
||||
bool mHasFakeTracks;
|
||||
|
||||
private:
|
||||
~MediaEngineDefault() {
|
||||
Shutdown();
|
||||
}
|
||||
~MediaEngineDefault() {}
|
||||
|
||||
Mutex mMutex;
|
||||
// protected with mMutex:
|
||||
|
@ -32,8 +32,7 @@ MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
|
||||
dom::MediaSourceEnum aMediaSource, const char* aMonitorName)
|
||||
: MediaEngineCameraVideoSource(aIndex, aMonitorName),
|
||||
mMediaSource(aMediaSource),
|
||||
mCapEngine(aCapEngine),
|
||||
mInShutdown(false)
|
||||
mCapEngine(aCapEngine)
|
||||
{
|
||||
MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other);
|
||||
mSettings.mWidth.Construct(0);
|
||||
@ -72,7 +71,7 @@ MediaEngineRemoteVideoSource::Shutdown()
|
||||
if (!mInitDone) {
|
||||
return;
|
||||
}
|
||||
mInShutdown = true;
|
||||
Super::Shutdown();
|
||||
if (mState == kStarted) {
|
||||
SourceMediaStream *source;
|
||||
bool empty;
|
||||
@ -108,7 +107,7 @@ MediaEngineRemoteVideoSource::Allocate(
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
@ -119,10 +118,8 @@ MediaEngineRemoteVideoSource::Allocate(
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<AllocationHandle> handle = new AllocationHandle(aConstraints, aOrigin,
|
||||
aPrefs, aDeviceId);
|
||||
|
||||
nsresult rv = UpdateNew(handle, aPrefs, aDeviceId, aOutBadConstraint);
|
||||
nsresult rv = Super::Allocate(aConstraints, aPrefs, aDeviceId, aOrigin,
|
||||
aOutHandle, aOutBadConstraint);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -136,35 +133,18 @@ MediaEngineRemoteVideoSource::Allocate(
|
||||
LOG(("Video device %d allocated shared", mCaptureIndex));
|
||||
}
|
||||
}
|
||||
mRegisteredHandles.AppendElement(handle);
|
||||
++mNrAllocations;
|
||||
handle.forget(aOutHandle);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineRemoteVideoSource::Deallocate(BaseAllocationHandle* aHandle)
|
||||
MediaEngineRemoteVideoSource::Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aHandle);
|
||||
RefPtr<AllocationHandle> handle = static_cast<AllocationHandle*>(aHandle);
|
||||
|
||||
class Comparator {
|
||||
public:
|
||||
static bool Equals(const RefPtr<AllocationHandle>& a,
|
||||
const RefPtr<AllocationHandle>& b) {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
};
|
||||
MOZ_ASSERT(mRegisteredHandles.Contains(handle, Comparator()));
|
||||
mRegisteredHandles.RemoveElementAt(mRegisteredHandles.IndexOf(handle, 0,
|
||||
Comparator()));
|
||||
--mNrAllocations;
|
||||
MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
|
||||
Super::Deallocate(aHandle);
|
||||
|
||||
if (mNrAllocations == 0) {
|
||||
MOZ_ASSERT(!mRegisteredHandles.Length());
|
||||
if (!mRegisteredHandles.Length()) {
|
||||
if (mState != kStopped && mState != kAllocated) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -175,13 +155,6 @@ MediaEngineRemoteVideoSource::Deallocate(BaseAllocationHandle* aHandle)
|
||||
LOG(("Video device %d deallocated", mCaptureIndex));
|
||||
} else {
|
||||
LOG(("Video device %d deallocated but still in use", mCaptureIndex));
|
||||
MOZ_ASSERT(mRegisteredHandles.Length());
|
||||
if (!mInShutdown) {
|
||||
// Whenever constraints are removed, other parties may get closer to ideal.
|
||||
auto& first = mRegisteredHandles[0];
|
||||
const char* badConstraint = nullptr;
|
||||
return UpdateRemove(first->mPrefs, first->mDeviceId, &badConstraint);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -267,7 +240,7 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineRemoteVideoSource::Restart(BaseAllocationHandle* aHandle,
|
||||
MediaEngineRemoteVideoSource::Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -280,49 +253,26 @@ MediaEngineRemoteVideoSource::Restart(BaseAllocationHandle* aHandle,
|
||||
}
|
||||
MOZ_ASSERT(aHandle);
|
||||
NormalizedConstraints constraints(aConstraints);
|
||||
return UpdateExisting(static_cast<AllocationHandle*>(aHandle), &constraints,
|
||||
aPrefs, aDeviceId, aOutBadConstraint);
|
||||
return ReevaluateAllocation(aHandle, &constraints, aPrefs, aDeviceId,
|
||||
aOutBadConstraint);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineRemoteVideoSource::UpdateExisting(AllocationHandle* aHandle,
|
||||
NormalizedConstraints* aNewConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint)
|
||||
MediaEngineRemoteVideoSource::UpdateSingleSource(
|
||||
const AllocationHandle* aHandle,
|
||||
const NormalizedConstraints& aNetConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
// aHandle and/or aNewConstraints may be nullptr
|
||||
|
||||
AutoTArray<const NormalizedConstraints*, 10> allConstraints;
|
||||
for (auto& registered : mRegisteredHandles) {
|
||||
if (aNewConstraints && registered.get() == aHandle) {
|
||||
continue; // Don't count old constraints
|
||||
}
|
||||
allConstraints.AppendElement(®istered->mConstraints);
|
||||
}
|
||||
if (aNewConstraints) {
|
||||
allConstraints.AppendElement(aNewConstraints);
|
||||
} else if (aHandle) {
|
||||
// In the case of UpdateNew, the handle isn't registered yet.
|
||||
allConstraints.AppendElement(&aHandle->mConstraints);
|
||||
}
|
||||
|
||||
NormalizedConstraints netConstraints(allConstraints);
|
||||
if (netConstraints.mBadConstraint) {
|
||||
*aOutBadConstraint = netConstraints.mBadConstraint;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!ChooseCapability(netConstraints, aPrefs, aDeviceId)) {
|
||||
*aOutBadConstraint = FindBadConstraint(netConstraints, *this, aDeviceId);
|
||||
if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId)) {
|
||||
*aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
switch (mState) {
|
||||
case kReleased:
|
||||
MOZ_ASSERT(aHandle);
|
||||
MOZ_ASSERT(!aNewConstraints);
|
||||
MOZ_ASSERT(!mRegisteredHandles.Length());
|
||||
if (camera::GetChildAndCall(&camera::CamerasChild::AllocateCaptureDevice,
|
||||
mCapEngine, GetUUID().get(),
|
||||
kMaxUniqueIdLength, mCaptureIndex,
|
||||
@ -354,9 +304,6 @@ MediaEngineRemoteVideoSource::UpdateExisting(AllocationHandle* aHandle,
|
||||
(aHandle? aHandle->mOrigin.get() : ""), mState));
|
||||
break;
|
||||
}
|
||||
if (aHandle && aNewConstraints) {
|
||||
aHandle->mConstraints = *aNewConstraints;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ namespace mozilla {
|
||||
class MediaEngineRemoteVideoSource : public MediaEngineCameraVideoSource,
|
||||
public webrtc::ExternalRenderer
|
||||
{
|
||||
typedef MediaEngineCameraVideoSource Super;
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
@ -71,36 +72,16 @@ public:
|
||||
dom::MediaSourceEnum aMediaSource,
|
||||
const char* aMonitorName = "RemoteVideo.Monitor");
|
||||
|
||||
class AllocationHandle : public BaseAllocationHandle
|
||||
{
|
||||
public:
|
||||
AllocationHandle(const dom::MediaTrackConstraints& aConstraints,
|
||||
const nsACString& aOrigin,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
: mConstraints(aConstraints),
|
||||
mOrigin(aOrigin),
|
||||
mPrefs(aPrefs),
|
||||
mDeviceId(aDeviceId) {}
|
||||
private:
|
||||
~AllocationHandle() override {}
|
||||
public:
|
||||
NormalizedConstraints mConstraints;
|
||||
nsCString mOrigin;
|
||||
MediaEnginePrefs mPrefs;
|
||||
nsString mDeviceId;
|
||||
};
|
||||
|
||||
nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) override;
|
||||
nsresult Deallocate(BaseAllocationHandle* aHandle) override;
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override;
|
||||
nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override;
|
||||
nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||
nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -123,7 +104,7 @@ public:
|
||||
void Shutdown() override;
|
||||
|
||||
protected:
|
||||
~MediaEngineRemoteVideoSource() { Shutdown(); }
|
||||
~MediaEngineRemoteVideoSource() { }
|
||||
|
||||
private:
|
||||
// Initialize the needed Video engine interfaces.
|
||||
@ -132,46 +113,18 @@ private:
|
||||
void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut) const override;
|
||||
void SetLastCapability(const webrtc::CaptureCapability& aCapability);
|
||||
|
||||
/* UpdateExisting - Centralized function to apply constraints and restart
|
||||
* device as needed, considering all allocations and changes to one.
|
||||
*
|
||||
* aHandle - New or existing handle, or null to update after removal.
|
||||
* aNewConstraints - Constraints to be applied to existing handle, or null.
|
||||
* aPrefs - As passed in (in case of changes in about:config).
|
||||
* aDeviceId - As passed in (origin dependent).
|
||||
* aOutBadConstraint - Result: nonzero if failed to apply. Name of culprit.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
UpdateExisting(AllocationHandle* aHandle,
|
||||
NormalizedConstraints* aNewConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint);
|
||||
|
||||
nsresult
|
||||
UpdateNew(AllocationHandle* aHandle,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) {
|
||||
return UpdateExisting(aHandle, nullptr, aPrefs, aDeviceId, aOutBadConstraint);
|
||||
}
|
||||
|
||||
nsresult
|
||||
UpdateRemove(const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) {
|
||||
return UpdateExisting(nullptr, nullptr, aPrefs, aDeviceId, aOutBadConstraint);
|
||||
}
|
||||
UpdateSingleSource(const AllocationHandle* aHandle,
|
||||
const NormalizedConstraints& aNetConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) override;
|
||||
|
||||
dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
|
||||
mozilla::camera::CaptureEngine mCapEngine;
|
||||
|
||||
nsTArray<RefPtr<AllocationHandle>> mRegisteredHandles;
|
||||
|
||||
// To only restart camera when needed, we keep track previous settings.
|
||||
webrtc::CaptureCapability mLastCapability;
|
||||
bool mInShutdown;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstrain
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
// windowId is not a proper constraint, so just read it.
|
||||
@ -153,7 +153,7 @@ MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstrain
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineTabVideoSource::Restart(BaseAllocationHandle* aHandle,
|
||||
MediaEngineTabVideoSource::Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const mozilla::MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -184,7 +184,7 @@ MediaEngineTabVideoSource::Restart(BaseAllocationHandle* aHandle,
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineTabVideoSource::Deallocate(BaseAllocationHandle* aHandle)
|
||||
MediaEngineTabVideoSource::Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
MOZ_ASSERT(!aHandle);
|
||||
return NS_OK;
|
||||
|
@ -19,21 +19,20 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
MediaEngineTabVideoSource();
|
||||
|
||||
void Shutdown() override {};
|
||||
void GetName(nsAString_internal&) const override;
|
||||
void GetUUID(nsACString_internal&) const override;
|
||||
nsresult Allocate(const dom::MediaTrackConstraints &,
|
||||
const mozilla::MediaEnginePrefs&,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) override;
|
||||
nsresult Deallocate(BaseAllocationHandle* aHandle) override;
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override;
|
||||
nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID, const mozilla::PrincipalHandle&) override;
|
||||
void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||
void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, const mozilla::PrincipalHandle& aPrincipalHandle) override;
|
||||
nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
|
||||
nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const mozilla::MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
|
@ -78,28 +78,24 @@ public:
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
AllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) override
|
||||
{
|
||||
// Nothing to do here, everything is managed in MediaManager.cpp
|
||||
aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult Deallocate(BaseAllocationHandle* aHandle) override
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override
|
||||
{
|
||||
// Nothing to do here, everything is managed in MediaManager.cpp
|
||||
MOZ_ASSERT(!aHandle);
|
||||
return NS_OK;
|
||||
}
|
||||
void Shutdown() override
|
||||
{
|
||||
// Nothing to do here, everything is managed in MediaManager.cpp
|
||||
}
|
||||
nsresult Start(SourceMediaStream* aMediaStream,
|
||||
TrackID aId,
|
||||
const PrincipalHandle& aPrincipalHandle) override;
|
||||
nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override;
|
||||
nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -139,7 +135,7 @@ public:
|
||||
const nsString& aDeviceId) const override;
|
||||
|
||||
protected:
|
||||
virtual ~MediaEngineWebRTCAudioCaptureSource() { Shutdown(); }
|
||||
virtual ~MediaEngineWebRTCAudioCaptureSource() {}
|
||||
nsCString mUUID;
|
||||
};
|
||||
|
||||
@ -419,53 +415,26 @@ private:
|
||||
};
|
||||
|
||||
class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource,
|
||||
public webrtc::VoEMediaProcess,
|
||||
private MediaConstraintsHelper
|
||||
public webrtc::VoEMediaProcess
|
||||
{
|
||||
typedef MediaEngineAudioSource Super;
|
||||
public:
|
||||
MediaEngineWebRTCMicrophoneSource(nsIThread* aThread,
|
||||
webrtc::VoiceEngine* aVoiceEnginePtr,
|
||||
mozilla::AudioInput* aAudioInput,
|
||||
int aIndex,
|
||||
const char* name,
|
||||
const char* uuid)
|
||||
: MediaEngineAudioSource(kReleased)
|
||||
, mVoiceEngine(aVoiceEnginePtr)
|
||||
, mAudioInput(aAudioInput)
|
||||
, mMonitor("WebRTCMic.Monitor")
|
||||
, mThread(aThread)
|
||||
, mCapIndex(aIndex)
|
||||
, mChannel(-1)
|
||||
, mNrAllocations(0)
|
||||
, mStarted(false)
|
||||
, mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
|
||||
, mPlayoutDelay(0)
|
||||
, mNullTransport(nullptr)
|
||||
, mSkipProcessing(false)
|
||||
{
|
||||
MOZ_ASSERT(aVoiceEnginePtr);
|
||||
MOZ_ASSERT(aAudioInput);
|
||||
mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
|
||||
mDeviceUUID.Assign(uuid);
|
||||
mListener = new mozilla::WebRTCAudioDataListener(this);
|
||||
// We'll init lazily as needed
|
||||
}
|
||||
const char* uuid);
|
||||
|
||||
void GetName(nsAString& aName) const override;
|
||||
void GetUUID(nsACString& aUUID) const override;
|
||||
|
||||
nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint) override;
|
||||
nsresult Deallocate(BaseAllocationHandle* aHandle) override;
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override;
|
||||
nsresult Start(SourceMediaStream* aStream,
|
||||
TrackID aID,
|
||||
const PrincipalHandle& aPrincipalHandle) override;
|
||||
nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
||||
nsresult Restart(BaseAllocationHandle* aHandle,
|
||||
nsresult Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
@ -515,11 +484,18 @@ public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
protected:
|
||||
~MediaEngineWebRTCMicrophoneSource() {
|
||||
Shutdown();
|
||||
}
|
||||
~MediaEngineWebRTCMicrophoneSource() {}
|
||||
|
||||
private:
|
||||
nsresult
|
||||
UpdateSingleSource(const AllocationHandle* aHandle,
|
||||
const NormalizedConstraints& aNetConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) override;
|
||||
|
||||
void SetLastPrefs(const MediaEnginePrefs& aPrefs);
|
||||
|
||||
// These allocate/configure and release the channel
|
||||
bool AllocChannel();
|
||||
void FreeChannel();
|
||||
@ -570,7 +546,6 @@ private:
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
int mCapIndex;
|
||||
int mChannel;
|
||||
int mNrAllocations; // Per-channel - When this becomes 0, we shut down HW for the channel
|
||||
TrackID mTrackID;
|
||||
bool mStarted;
|
||||
|
||||
@ -587,10 +562,14 @@ private:
|
||||
// because of prefs or constraints. This allows simply copying the audio into
|
||||
// the MSG, skipping resampling and the whole webrtc.org code.
|
||||
bool mSkipProcessing;
|
||||
|
||||
// To only update microphone when needed, we keep track of previous settings.
|
||||
MediaEnginePrefs mLastPrefs;
|
||||
};
|
||||
|
||||
class MediaEngineWebRTC : public MediaEngine
|
||||
{
|
||||
typedef MediaEngine Super;
|
||||
public:
|
||||
explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
|
||||
|
||||
@ -607,7 +586,6 @@ public:
|
||||
nsTArray<RefPtr<MediaEngineAudioSource>>*) override;
|
||||
private:
|
||||
~MediaEngineWebRTC() {
|
||||
Shutdown();
|
||||
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
|
||||
AsyncLatencyLogger::Get()->Release();
|
||||
#endif
|
||||
|
@ -183,6 +183,37 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame
|
||||
}
|
||||
}
|
||||
|
||||
MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
||||
nsIThread* aThread,
|
||||
webrtc::VoiceEngine* aVoiceEnginePtr,
|
||||
mozilla::AudioInput* aAudioInput,
|
||||
int aIndex,
|
||||
const char* name,
|
||||
const char* uuid)
|
||||
: MediaEngineAudioSource(kReleased)
|
||||
, mVoiceEngine(aVoiceEnginePtr)
|
||||
, mAudioInput(aAudioInput)
|
||||
, mMonitor("WebRTCMic.Monitor")
|
||||
, mThread(aThread)
|
||||
, mCapIndex(aIndex)
|
||||
, mChannel(-1)
|
||||
, mStarted(false)
|
||||
, mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
|
||||
, mPlayoutDelay(0)
|
||||
, mNullTransport(nullptr)
|
||||
, mSkipProcessing(false)
|
||||
{
|
||||
MOZ_ASSERT(aVoiceEnginePtr);
|
||||
MOZ_ASSERT(aAudioInput);
|
||||
mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
|
||||
mDeviceUUID.Assign(uuid);
|
||||
mListener = new mozilla::WebRTCAudioDataListener(this);
|
||||
mSettings.mEchoCancellation.Construct(0);
|
||||
mSettings.mMozAutoGainControl.Construct(0);
|
||||
mSettings.mMozNoiseSuppression.Construct(0);
|
||||
// We'll init lazily as needed
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::GetName(nsAString& aName) const
|
||||
{
|
||||
@ -219,109 +250,152 @@ uint32_t MediaEngineWebRTCMicrophoneSource::GetBestFitnessDistance(
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const nsACString& aOrigin,
|
||||
BaseAllocationHandle** aOutHandle,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
if (mState == kReleased) {
|
||||
if (sChannelsOpen == 0) {
|
||||
if (!InitEngine()) {
|
||||
LOG(("Audio engine is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!AllocChannel()) {
|
||||
if (sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
LOG(("Audio device is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (mAudioInput->SetRecordingDevice(mCapIndex)) {
|
||||
FreeChannel();
|
||||
if (sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
sChannelsOpen++;
|
||||
mState = kAllocated;
|
||||
LOG(("Audio device %d allocated", mCapIndex));
|
||||
} else if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (mSources.IsEmpty()) {
|
||||
LOG(("Audio device %d reallocated", mCapIndex));
|
||||
} else {
|
||||
LOG(("Audio device %d allocated shared", mCapIndex));
|
||||
}
|
||||
}
|
||||
++mNrAllocations;
|
||||
aOutHandle = nullptr;
|
||||
return Restart(nullptr, aConstraints, aPrefs, aDeviceId, aOutBadConstraint);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::Restart(BaseAllocationHandle* aHandle,
|
||||
MediaEngineWebRTCMicrophoneSource::Restart(AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
MOZ_ASSERT(!aHandle);
|
||||
FlattenedConstraints c(aConstraints);
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aHandle);
|
||||
NormalizedConstraints constraints(aConstraints);
|
||||
return ReevaluateAllocation(aHandle, &constraints, aPrefs, aDeviceId,
|
||||
aOutBadConstraint);
|
||||
}
|
||||
|
||||
bool aec_on = c.mEchoCancellation.Get(aPrefs.mAecOn);
|
||||
bool agc_on = c.mMozAutoGainControl.Get(aPrefs.mAgcOn);
|
||||
bool noise_on = c.mMozNoiseSuppression.Get(aPrefs.mNoiseOn);
|
||||
bool operator == (const MediaEnginePrefs& a, const MediaEnginePrefs& b)
|
||||
{
|
||||
return !memcmp(&a, &b, sizeof(MediaEnginePrefs));
|
||||
};
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
||||
const AllocationHandle* aHandle,
|
||||
const NormalizedConstraints& aNetConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint)
|
||||
{
|
||||
FlattenedConstraints c(aNetConstraints);
|
||||
|
||||
MediaEnginePrefs prefs = aPrefs;
|
||||
prefs.mAecOn = c.mEchoCancellation.Get(prefs.mAecOn);
|
||||
prefs.mAgcOn = c.mMozAutoGainControl.Get(prefs.mAgcOn);
|
||||
prefs.mNoiseOn = c.mMozNoiseSuppression.Get(prefs.mNoiseOn);
|
||||
|
||||
LOG(("Audio config: aec: %d, agc: %d, noise: %d, delay: %d",
|
||||
aec_on ? aPrefs.mAec : -1,
|
||||
agc_on ? aPrefs.mAgc : -1,
|
||||
noise_on ? aPrefs.mNoise : -1,
|
||||
aPrefs.mPlayoutDelay));
|
||||
prefs.mAecOn ? prefs.mAec : -1,
|
||||
prefs.mAgcOn ? prefs.mAgc : -1,
|
||||
prefs.mNoiseOn ? prefs.mNoise : -1,
|
||||
prefs.mPlayoutDelay));
|
||||
|
||||
mPlayoutDelay = aPrefs.mPlayoutDelay;
|
||||
mPlayoutDelay = prefs.mPlayoutDelay;
|
||||
|
||||
switch (mState) {
|
||||
case kReleased:
|
||||
MOZ_ASSERT(aHandle);
|
||||
if (sChannelsOpen == 0) {
|
||||
if (!InitEngine()) {
|
||||
LOG(("Audio engine is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!AllocChannel()) {
|
||||
if (sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
LOG(("Audio device is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (mAudioInput->SetRecordingDevice(mCapIndex)) {
|
||||
FreeChannel();
|
||||
if (sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
sChannelsOpen++;
|
||||
mState = kAllocated;
|
||||
LOG(("Audio device %d allocated", mCapIndex));
|
||||
break;
|
||||
|
||||
case kStarted:
|
||||
if (prefs == mLastPrefs) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (mSources.IsEmpty()) {
|
||||
LOG(("Audio device %d reallocated", mCapIndex));
|
||||
} else {
|
||||
LOG(("Audio device %d allocated shared", mCapIndex));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(("Audio device %d %s in ignored state %d", mCapIndex,
|
||||
(aHandle? aHandle->mOrigin.get() : ""), mState));
|
||||
break;
|
||||
}
|
||||
|
||||
if (sChannelsOpen > 0) {
|
||||
int error;
|
||||
|
||||
if (0 != (error = mVoEProcessing->SetEcStatus(aec_on, (webrtc::EcModes) aPrefs.mAec))) {
|
||||
error = mVoEProcessing->SetEcStatus(prefs.mAecOn, (webrtc::EcModes)prefs.mAec);
|
||||
if (error) {
|
||||
LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error));
|
||||
// Overhead of capturing all the time is very low (<0.1% of an audio only call)
|
||||
if (aec_on) {
|
||||
if (0 != (error = mVoEProcessing->SetEcMetricsStatus(true))) {
|
||||
if (prefs.mAecOn) {
|
||||
error = mVoEProcessing->SetEcMetricsStatus(true);
|
||||
if (error) {
|
||||
LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 != (error = mVoEProcessing->SetAgcStatus(agc_on, (webrtc::AgcModes) aPrefs.mAgc))) {
|
||||
error = mVoEProcessing->SetAgcStatus(prefs.mAgcOn, (webrtc::AgcModes)prefs.mAgc);
|
||||
if (error) {
|
||||
LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error));
|
||||
}
|
||||
if (0 != (error = mVoEProcessing->SetNsStatus(noise_on, (webrtc::NsModes) aPrefs.mNoise))) {
|
||||
error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise);
|
||||
if (error) {
|
||||
LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error));
|
||||
}
|
||||
}
|
||||
|
||||
mSkipProcessing = !(aec_on || agc_on || noise_on);
|
||||
mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn);
|
||||
if (mSkipProcessing) {
|
||||
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
|
||||
}
|
||||
|
||||
SetLastPrefs(prefs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
|
||||
const MediaEnginePrefs& aPrefs)
|
||||
{
|
||||
mLastPrefs = aPrefs;
|
||||
|
||||
RefPtr<MediaEngineWebRTCMicrophoneSource> that = this;
|
||||
|
||||
NS_DispatchToMainThread(media::NewRunnableFrom([this, that, aPrefs]() mutable {
|
||||
mSettings.mEchoCancellation.Value() = aPrefs.mAecOn;
|
||||
mSettings.mMozAutoGainControl.Value() = aPrefs.mAgcOn;
|
||||
mSettings.mMozNoiseSuppression.Value() = aPrefs.mNoiseOn;
|
||||
return NS_OK;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::Deallocate(BaseAllocationHandle* aHandle)
|
||||
MediaEngineWebRTCMicrophoneSource::Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(!aHandle);
|
||||
--mNrAllocations;
|
||||
MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
|
||||
if (mNrAllocations == 0) {
|
||||
|
||||
Super::Deallocate(aHandle);
|
||||
|
||||
if (!mRegisteredHandles.Length()) {
|
||||
// If empty, no callbacks to deliver data should be occuring
|
||||
if (mState != kStopped && mState != kAllocated) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -714,6 +788,7 @@ MediaEngineWebRTCMicrophoneSource::FreeChannel()
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::Shutdown()
|
||||
{
|
||||
Super::Shutdown();
|
||||
if (mListener) {
|
||||
// breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us
|
||||
mListener->Shutdown();
|
||||
@ -739,7 +814,7 @@ MediaEngineWebRTCMicrophoneSource::Shutdown()
|
||||
MOZ_ASSERT(mState == kStopped);
|
||||
}
|
||||
|
||||
while (mNrAllocations) {
|
||||
while (mRegisteredHandles.Length()) {
|
||||
MOZ_ASSERT(mState == kAllocated || mState == kStopped);
|
||||
Deallocate(nullptr); // XXX Extend concurrent constraints code to mics.
|
||||
}
|
||||
@ -847,7 +922,7 @@ MediaEngineWebRTCAudioCaptureSource::Stop(SourceMediaStream *aMediaStream,
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCAudioCaptureSource::Restart(
|
||||
BaseAllocationHandle* aHandle,
|
||||
AllocationHandle* aHandle,
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId,
|
||||
|
@ -4,6 +4,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MediaTrackConstraints.h"
|
||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
@ -144,7 +145,7 @@ NormalizedConstraintSet::BooleanRange::BooleanRange(
|
||||
mIdeal.emplace(aOther.GetAsBoolean());
|
||||
}
|
||||
} else {
|
||||
const ConstrainBooleanParameters& r = aOther.GetAsConstrainBooleanParameters();
|
||||
const dom::ConstrainBooleanParameters& r = aOther.GetAsConstrainBooleanParameters();
|
||||
if (r.mIdeal.WasPassed()) {
|
||||
mIdeal.emplace(r.mIdeal.Value());
|
||||
}
|
||||
@ -188,7 +189,7 @@ NormalizedConstraintSet::StringRange::StringRange(
|
||||
|
||||
void
|
||||
NormalizedConstraintSet::StringRange::SetFrom(
|
||||
const ConstrainDOMStringParameters& aOther)
|
||||
const dom::ConstrainDOMStringParameters& aOther)
|
||||
{
|
||||
if (aOther.mIdeal.WasPassed()) {
|
||||
mIdeal.clear();
|
||||
@ -295,7 +296,7 @@ NormalizedConstraints::NormalizedConstraints(
|
||||
{
|
||||
// Create a list of member pointers.
|
||||
nsTArray<MemberPtrType> list;
|
||||
NormalizedConstraints dummy(MediaTrackConstraints(), &list);
|
||||
NormalizedConstraints dummy(dom::MediaTrackConstraints(), &list);
|
||||
|
||||
// Do intersection of all required constraints, and average of ideals,
|
||||
|
||||
|
@ -33,7 +33,6 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
'MediaEngineRemoteVideoSource.cpp',
|
||||
'MediaEngineTabVideoSource.cpp',
|
||||
'MediaEngineWebRTCAudio.cpp',
|
||||
'MediaTrackConstraints.cpp',
|
||||
'RTCCertificate.cpp',
|
||||
'RTCIdentityProviderRegistrar.cpp',
|
||||
]
|
||||
@ -66,6 +65,7 @@ XPIDL_SOURCES += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'MediaEngineDefault.cpp',
|
||||
'MediaTrackConstraints.cpp',
|
||||
'PeerIdentity.cpp',
|
||||
]
|
||||
|
||||
|
@ -20,10 +20,6 @@ dictionary MediaStreamConstraints {
|
||||
boolean fake; // For testing purpose. Generates frames of solid
|
||||
// colors if video is enabled, and sound of 1Khz sine
|
||||
// wave if audio is enabled.
|
||||
boolean fakeTracks; // For testing purpose, works only if fake is
|
||||
// enabled. Enable fakeTracks returns a stream
|
||||
// with two extra empty video tracks and three
|
||||
// extra empty audio tracks.
|
||||
DOMString? peerIdentity = null;
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,9 @@ dictionary MediaTrackSettings {
|
||||
double frameRate;
|
||||
DOMString facingMode;
|
||||
DOMString deviceId;
|
||||
boolean echoCancellation;
|
||||
boolean mozNoiseSuppression;
|
||||
boolean mozAutoGainControl;
|
||||
|
||||
// Mozilla-specific extensions:
|
||||
|
||||
|
@ -21,7 +21,9 @@ addEventListener('fetch', function(evt) {
|
||||
evt.respondWith(registration.unregister().then(function() {
|
||||
return new Response('service worker generated download', {
|
||||
headers: {
|
||||
'Content-Disposition': 'attachment; filename="fake_download.bin"'
|
||||
'Content-Disposition': 'attachment; filename="fake_download.bin"',
|
||||
// fake encoding header that should have no effect
|
||||
'Content-Encoding': 'gzip',
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
@ -34,9 +34,12 @@ namespace image {
|
||||
static LazyLogModule sPNGLog("PNGDecoder");
|
||||
static LazyLogModule sPNGDecoderAccountingLog("PNGDecoderAccounting");
|
||||
|
||||
// Limit image dimensions (bug #251381, #591822, and #967656)
|
||||
#ifndef MOZ_PNG_MAX_DIMENSION
|
||||
# define MOZ_PNG_MAX_DIMENSION 32767
|
||||
// limit image dimensions (bug #251381, #591822, #967656, and #1283961)
|
||||
#ifndef MOZ_PNG_MAX_WIDTH
|
||||
# define MOZ_PNG_MAX_WIDTH 0x7fffffff // Unlimited
|
||||
#endif
|
||||
#ifndef MOZ_PNG_MAX_HEIGHT
|
||||
# define MOZ_PNG_MAX_HEIGHT 0x7fffffff // Unlimited
|
||||
#endif
|
||||
|
||||
nsPNGDecoder::AnimFrameInfo::AnimFrameInfo()
|
||||
@ -323,6 +326,7 @@ nsPNGDecoder::InitInternal()
|
||||
#endif
|
||||
|
||||
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
|
||||
png_set_user_limits(mPNG, MOZ_PNG_MAX_WIDTH, MOZ_PNG_MAX_HEIGHT);
|
||||
if (mCMSMode != eCMSMode_Off) {
|
||||
png_set_chunk_malloc_max(mPNG, 4000000L);
|
||||
}
|
||||
@ -557,11 +561,6 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
|
||||
&interlace_type, &compression_type, &filter_type);
|
||||
|
||||
// Are we too big?
|
||||
if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) {
|
||||
png_longjmp(decoder->mPNG, 1);
|
||||
}
|
||||
|
||||
const IntRect frameRect(0, 0, width, height);
|
||||
|
||||
// Post our size to the superclass
|
||||
|
@ -990,7 +990,7 @@ function ArrayConcat(arg1) {
|
||||
if (n + len > MAX_NUMERIC_INDEX)
|
||||
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
||||
|
||||
if (IsPackedArray(E)) {
|
||||
if (IsPackedArray(A) && IsPackedArray(E)) {
|
||||
// Step 5.c.i, 5.c.iv, and 5.c.iv.5.
|
||||
for (k = 0; k < len; k++) {
|
||||
// Steps 5.c.iv.1-3.
|
||||
|
@ -1792,8 +1792,11 @@ InitDateTimeFormatClass(JSContext* cx, HandleObject Intl, Handle<GlobalObject*>
|
||||
// is enabled, also add it.
|
||||
if (cx->compartment()->creationOptions().experimentalDateTimeFormatFormatToPartsEnabled()) {
|
||||
RootedValue ftp(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(),
|
||||
cx->names().DateTimeFormatFormatToParts, &ftp))
|
||||
HandlePropertyName name = cx->names().formatToParts;
|
||||
if (!GlobalObject::getSelfHostedFunction(cx, cx->global(),
|
||||
cx->names().DateTimeFormatFormatToParts,
|
||||
name,
|
||||
0, &ftp))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1559,6 +1559,15 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* resul
|
||||
return true;
|
||||
}
|
||||
|
||||
JSNative unicodeGetter;
|
||||
if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().unicode), &unicodeGetter))
|
||||
return false;
|
||||
|
||||
if (unicodeGetter != regexp_unicode) {
|
||||
*result = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if @@match, @@search, and exec are own data properties,
|
||||
// those values should be tested in selfhosted JS.
|
||||
bool has = false;
|
||||
|
@ -579,6 +579,7 @@ function IsRegExpSplitOptimizable(rx, C) {
|
||||
// If RegExpPrototypeOptimizable succeeds, `RegExpProto.exec` is guaranteed
|
||||
// to be a data property.
|
||||
return RegExpPrototypeOptimizable(RegExpProto) &&
|
||||
RegExpInstanceOptimizable(rx, RegExpProto) &&
|
||||
RegExpProto.exec === RegExp_prototype_Exec;
|
||||
}
|
||||
|
||||
@ -603,7 +604,8 @@ function RegExpSplit(string, limit) {
|
||||
// Steps 6-7.
|
||||
var unicodeMatching = callFunction(std_String_includes, flags, "u");
|
||||
|
||||
var optimizable = IsRegExpSplitOptimizable(rx, C);
|
||||
var optimizable = IsRegExpSplitOptimizable(rx, C) &&
|
||||
(limit === undefined || typeof limit == "number");
|
||||
var splitter;
|
||||
if (optimizable) {
|
||||
// Steps 8-9 (skipped).
|
||||
|
@ -864,23 +864,7 @@ GCState(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* state;
|
||||
gc::State globalState = cx->runtime()->gc.state();
|
||||
if (globalState == gc::NO_INCREMENTAL)
|
||||
state = "none";
|
||||
else if (globalState == gc::MARK)
|
||||
state = "mark";
|
||||
else if (globalState == gc::SWEEP)
|
||||
state = "sweep";
|
||||
else if (globalState == gc::FINALIZE)
|
||||
state = "finalize";
|
||||
else if (globalState == gc::COMPACT)
|
||||
state = "compact";
|
||||
else if (globalState == gc::DECOMMIT)
|
||||
state = "decommit";
|
||||
else
|
||||
MOZ_CRASH("Unobserveable global GC state");
|
||||
|
||||
const char* state = StateName(cx->runtime()->gc.state());
|
||||
JSString* str = JS_NewStringCopyZ(cx, state);
|
||||
if (!str)
|
||||
return false;
|
||||
|
@ -689,8 +689,8 @@ class GCRuntime
|
||||
public:
|
||||
// Internal public interface
|
||||
State state() const { return incrementalState; }
|
||||
bool isHeapCompacting() const { return state() == COMPACT; }
|
||||
bool isForegroundSweeping() const { return state() == SWEEP; }
|
||||
bool isHeapCompacting() const { return state() == State::Compact; }
|
||||
bool isForegroundSweeping() const { return state() == State::Sweep; }
|
||||
bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
|
||||
void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
|
||||
void waitBackgroundSweepOrAllocEnd() {
|
||||
@ -766,7 +766,7 @@ class GCRuntime
|
||||
void disallowIncrementalGC() { incrementalAllowed = false; }
|
||||
|
||||
bool isIncrementalGCEnabled() const { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
|
||||
bool isIncrementalGCInProgress() const { return state() != NO_INCREMENTAL; }
|
||||
bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
|
||||
|
||||
bool isGenerationalGCEnabled() const { return generationalDisabled == 0; }
|
||||
void disableGenerationalGC();
|
||||
|
@ -183,28 +183,28 @@ IsShapeAllocKind(AllocKind kind)
|
||||
|
||||
// Returns a sequence for use in a range-based for loop,
|
||||
// to iterate over all alloc kinds.
|
||||
inline decltype(mozilla::MakeEnumeratedRange<int>(AllocKind::FIRST, AllocKind::LIMIT))
|
||||
inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
|
||||
AllAllocKinds()
|
||||
{
|
||||
return mozilla::MakeEnumeratedRange<int>(AllocKind::FIRST, AllocKind::LIMIT);
|
||||
return mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT);
|
||||
}
|
||||
|
||||
// Returns a sequence for use in a range-based for loop,
|
||||
// to iterate over all object alloc kinds.
|
||||
inline decltype(mozilla::MakeEnumeratedRange<int>(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT))
|
||||
inline decltype(mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT))
|
||||
ObjectAllocKinds()
|
||||
{
|
||||
return mozilla::MakeEnumeratedRange<int>(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT);
|
||||
return mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT);
|
||||
}
|
||||
|
||||
// Returns a sequence for use in a range-based for loop,
|
||||
// to iterate over alloc kinds from |first| to |limit|, exclusive.
|
||||
inline decltype(mozilla::MakeEnumeratedRange<int>(AllocKind::FIRST, AllocKind::LIMIT))
|
||||
inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
|
||||
SomeAllocKinds(AllocKind first = AllocKind::FIRST, AllocKind limit = AllocKind::LIMIT)
|
||||
{
|
||||
MOZ_ASSERT(IsAllocKind(first), "|first| is not a valid AllocKind!");
|
||||
MOZ_ASSERT(IsAllocKind(limit), "|limit| is not a valid AllocKind!");
|
||||
return mozilla::MakeEnumeratedRange<int>(first, limit);
|
||||
return mozilla::MakeEnumeratedRange(first, limit);
|
||||
}
|
||||
|
||||
// AllAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
|
||||
|
@ -346,8 +346,8 @@ static void
|
||||
AssertRootMarkingPhase(JSTracer* trc)
|
||||
{
|
||||
MOZ_ASSERT_IF(trc->isMarkingTracer(),
|
||||
trc->runtime()->gc.state() == NO_INCREMENTAL ||
|
||||
trc->runtime()->gc.state() == MARK_ROOTS);
|
||||
trc->runtime()->gc.state() == State::NotActive ||
|
||||
trc->runtime()->gc.state() == State::MarkRoots);
|
||||
}
|
||||
|
||||
|
||||
@ -1945,7 +1945,7 @@ bool
|
||||
GCMarker::markDelayedChildren(SliceBudget& budget)
|
||||
{
|
||||
GCRuntime& gc = runtime()->gc;
|
||||
gcstats::AutoPhase ap(gc.stats, gc.state() == MARK, gcstats::PHASE_MARK_DELAYED);
|
||||
gcstats::AutoPhase ap(gc.stats, gc.state() == State::Mark, gcstats::PHASE_MARK_DELAYED);
|
||||
|
||||
MOZ_ASSERT(unmarkedArenaStackTop);
|
||||
do {
|
||||
@ -2439,7 +2439,7 @@ CheckIsMarkedThing(T* thingp)
|
||||
JSRuntime* rt = (*thingp)->runtimeFromAnyThread();
|
||||
MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
|
||||
CurrentThreadCanAccessRuntime(rt) ||
|
||||
(rt->isHeapCollecting() && rt->gc.state() == SWEEP));
|
||||
(rt->isHeapCollecting() && rt->gc.state() == State::Sweep));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ struct Statistics
|
||||
double startTimestamp, size_t startFaults, gc::State initialState)
|
||||
: budget(budget), reason(reason),
|
||||
initialState(initialState),
|
||||
finalState(gc::NO_INCREMENTAL),
|
||||
finalState(gc::State::NotActive),
|
||||
resetReason(nullptr),
|
||||
start(start), startTimestamp(startTimestamp),
|
||||
startFaults(startFaults)
|
||||
|
@ -203,7 +203,7 @@ gc::GCRuntime::startVerifyPreBarriers()
|
||||
/* Create the root node. */
|
||||
trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
|
||||
|
||||
incrementalState = MARK_ROOTS;
|
||||
incrementalState = State::MarkRoots;
|
||||
|
||||
/* Make all the roots be edges emanating from the root node. */
|
||||
markRuntime(trc, TraceRuntime, prep.session().lock);
|
||||
@ -230,7 +230,7 @@ gc::GCRuntime::startVerifyPreBarriers()
|
||||
}
|
||||
|
||||
verifyPreData = trc;
|
||||
incrementalState = MARK;
|
||||
incrementalState = State::Mark;
|
||||
marker.start();
|
||||
|
||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||
@ -244,7 +244,7 @@ gc::GCRuntime::startVerifyPreBarriers()
|
||||
return;
|
||||
|
||||
oom:
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
incrementalState = State::NotActive;
|
||||
js_delete(trc);
|
||||
verifyPreData = nullptr;
|
||||
}
|
||||
@ -342,7 +342,7 @@ gc::GCRuntime::endVerifyPreBarriers()
|
||||
number++;
|
||||
|
||||
verifyPreData = nullptr;
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
incrementalState = State::NotActive;
|
||||
|
||||
if (!compartmentCreated && IsIncrementalGCSafe(rt)) {
|
||||
CheckEdgeTracer cetrc(rt);
|
||||
|
@ -286,13 +286,6 @@ GetCaseIndependentLetters(char16_t character,
|
||||
for (size_t i = 0; i < choices_length; i++) {
|
||||
char16_t c = choices[i];
|
||||
|
||||
// The standard requires that non-ASCII characters cannot have ASCII
|
||||
// character codes in their equivalence class, even though this
|
||||
// situation occurs multiple times in the unicode tables.
|
||||
static const unsigned kMaxAsciiCharCode = 127;
|
||||
if (!unicode && character > kMaxAsciiCharCode && c <= kMaxAsciiCharCode)
|
||||
continue;
|
||||
|
||||
// Skip characters that can't appear in one byte strings.
|
||||
if (!unicode && ascii_subject && c > kMaxOneByteCharCode)
|
||||
continue;
|
||||
@ -332,10 +325,40 @@ GetCaseIndependentLetters(char16_t character,
|
||||
choices, ArrayLength(choices), letters);
|
||||
}
|
||||
|
||||
char16_t upper = unicode::ToUpperCase(character);
|
||||
unicode::CodepointsWithSameUpperCase others(character);
|
||||
char16_t other1 = others.other1();
|
||||
char16_t other2 = others.other2();
|
||||
char16_t other3 = others.other3();
|
||||
|
||||
// ES 2017 draft 996af87b7072b3c3dd2b1def856c66f456102215 21.2.4.2
|
||||
// step 3.g.
|
||||
// The standard requires that non-ASCII characters cannot have ASCII
|
||||
// character codes in their equivalence class, even though this
|
||||
// situation occurs multiple times in the unicode tables.
|
||||
static const unsigned kMaxAsciiCharCode = 127;
|
||||
if (upper <= kMaxAsciiCharCode) {
|
||||
if (character > kMaxAsciiCharCode) {
|
||||
// If Canonicalize(character) == character, all other characters
|
||||
// should be ignored.
|
||||
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
||||
&character, 1, letters);
|
||||
}
|
||||
|
||||
if (other1 > kMaxAsciiCharCode)
|
||||
other1 = character;
|
||||
if (other2 > kMaxAsciiCharCode)
|
||||
other2 = character;
|
||||
if (other3 > kMaxAsciiCharCode)
|
||||
other3 = character;
|
||||
}
|
||||
|
||||
const char16_t choices[] = {
|
||||
character,
|
||||
unicode::ToLowerCase(character),
|
||||
unicode::ToUpperCase(character)
|
||||
upper,
|
||||
other1,
|
||||
other2,
|
||||
other3
|
||||
};
|
||||
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
||||
choices, ArrayLength(choices), letters);
|
||||
|
@ -17,12 +17,12 @@ var g = newGlobal();
|
||||
|
||||
// Start an off thread compilation that will not run until GC has finished
|
||||
if ("gcstate" in this)
|
||||
assertEq(gcstate(), "mark");
|
||||
assertEq(gcstate(), "Mark");
|
||||
g.offThreadCompileScript('23;', {});
|
||||
|
||||
// Wait for the compilation to finish, which must finish the GC first
|
||||
assertEq(23, g.runOffThreadScript());
|
||||
if ("gcstate" in this)
|
||||
assertEq(gcstate(), "none");
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
||||
print("done");
|
||||
|
@ -27,7 +27,7 @@ function testAbort(zoneCount, objectCount, sliceCount, abortState)
|
||||
|
||||
var didAbort = false;
|
||||
startgc(sliceCount, "shrinking");
|
||||
while (gcstate() !== "none") {
|
||||
while (gcstate() !== "NotActive") {
|
||||
var state = gcstate();
|
||||
if (state == abortState) {
|
||||
abortgc();
|
||||
@ -38,7 +38,7 @@ function testAbort(zoneCount, objectCount, sliceCount, abortState)
|
||||
gcslice(sliceCount);
|
||||
}
|
||||
|
||||
assertEq(gcstate(), "none");
|
||||
assertEq(gcstate(), "NotActive");
|
||||
if (abortState)
|
||||
assertEq(didAbort, true);
|
||||
|
||||
@ -47,6 +47,8 @@ function testAbort(zoneCount, objectCount, sliceCount, abortState)
|
||||
|
||||
gczeal(0);
|
||||
testAbort(10, 10000, 10000);
|
||||
testAbort(10, 10000, 10000, "mark");
|
||||
testAbort(10, 10000, 10000, "sweep");
|
||||
testAbort(10, 10000, 10000, "compact");
|
||||
testAbort(10, 10000, 10000, "Mark");
|
||||
testAbort(10, 10000, 10000, "Sweep");
|
||||
testAbort(10, 10000, 10000, "Compact");
|
||||
// Note: we do not yield automatically before Finalize or Decommit, as they yield internally.
|
||||
// Thus, we may not witness an incremental state in this phase and cannot test it explicitly.
|
||||
|
@ -27,11 +27,11 @@ function testCompacting(zoneCount, objectCount, sliceCount)
|
||||
}
|
||||
|
||||
// Finish any alloc-triggered incremental GC
|
||||
if (gcstate() !== "none")
|
||||
if (gcstate() !== "NotActive")
|
||||
gc();
|
||||
|
||||
startgc(sliceCount, "shrinking");
|
||||
while (gcstate() !== "none") {
|
||||
while (gcstate() !== "NotActive") {
|
||||
gcslice(sliceCount);
|
||||
}
|
||||
|
||||
|
@ -6,26 +6,26 @@ gczeal(0);
|
||||
|
||||
// Non-incremental GC.
|
||||
gc();
|
||||
assertEq(gcstate(), "none");
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
||||
// Incremental GC in minimal slice. Note that finalization always uses zero-
|
||||
// sized slices while background finalization is on-going, so we need to loop.
|
||||
gcslice(1000000);
|
||||
while (gcstate() == "finalize") { gcslice(1); }
|
||||
while (gcstate() == "decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "none");
|
||||
while (gcstate() == "Finalize") { gcslice(1); }
|
||||
while (gcstate() == "Decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
||||
// Incremental GC in multiple slices: if marking takes more than one slice,
|
||||
// we yield before we start sweeping.
|
||||
gczeal(0);
|
||||
gcslice(1);
|
||||
assertEq(gcstate(), "mark");
|
||||
assertEq(gcstate(), "Mark");
|
||||
gcslice(1000000);
|
||||
assertEq(gcstate(), "mark");
|
||||
assertEq(gcstate(), "Mark");
|
||||
gcslice(1000000);
|
||||
while (gcstate() == "finalize") { gcslice(1); }
|
||||
while (gcstate() == "decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "none");
|
||||
while (gcstate() == "Finalize") { gcslice(1); }
|
||||
while (gcstate() == "Decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
||||
// Zeal mode 8: Incremental GC in two main slices:
|
||||
// 1) mark roots
|
||||
@ -33,11 +33,11 @@ assertEq(gcstate(), "none");
|
||||
// *) finalize.
|
||||
gczeal(8, 0);
|
||||
gcslice(1);
|
||||
assertEq(gcstate(), "mark");
|
||||
assertEq(gcstate(), "Mark");
|
||||
gcslice(1);
|
||||
while (gcstate() == "finalize") { gcslice(1); }
|
||||
while (gcstate() == "decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "none");
|
||||
while (gcstate() == "Finalize") { gcslice(1); }
|
||||
while (gcstate() == "Decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
||||
// Zeal mode 9: Incremental GC in two main slices:
|
||||
// 1) mark roots and marking
|
||||
@ -45,19 +45,19 @@ assertEq(gcstate(), "none");
|
||||
// *) finalize.
|
||||
gczeal(9, 0);
|
||||
gcslice(1);
|
||||
assertEq(gcstate(), "mark");
|
||||
assertEq(gcstate(), "Mark");
|
||||
gcslice(1);
|
||||
while (gcstate() == "finalize") { gcslice(1); }
|
||||
while (gcstate() == "decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "none");
|
||||
while (gcstate() == "Finalize") { gcslice(1); }
|
||||
while (gcstate() == "Decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
||||
// Zeal mode 10: Incremental GC in multiple slices (always yeilds before
|
||||
// sweeping). This test uses long slices to prove that this zeal mode yields
|
||||
// in sweeping, where normal IGC (above) does not.
|
||||
gczeal(10, 0);
|
||||
gcslice(1000000);
|
||||
assertEq(gcstate(), "sweep");
|
||||
assertEq(gcstate(), "Sweep");
|
||||
gcslice(1000000);
|
||||
while (gcstate() == "finalize") { gcslice(1); }
|
||||
while (gcstate() == "decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "none");
|
||||
while (gcstate() == "Finalize") { gcslice(1); }
|
||||
while (gcstate() == "Decommit") { gcslice(1); }
|
||||
assertEq(gcstate(), "NotActive");
|
||||
|
@ -1502,11 +1502,9 @@ Simulator::exclusiveMonitorClear()
|
||||
}
|
||||
|
||||
int
|
||||
Simulator::readW(int32_t addr, SimInstruction* instr)
|
||||
Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||
{
|
||||
// The regexp engine emits unaligned loads, so we don't check for them here
|
||||
// like most of the other methods do.
|
||||
if ((addr & 3) == 0 || !HasAlignmentFault()) {
|
||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
@ -1527,9 +1525,9 @@ Simulator::readW(int32_t addr, SimInstruction* instr)
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::writeW(int32_t addr, int value, SimInstruction* instr)
|
||||
Simulator::writeW(int32_t addr, int value, SimInstruction* instr, UnalignedPolicy f)
|
||||
{
|
||||
if ((addr & 3) == 0 || !HasAlignmentFault()) {
|
||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
|
||||
*ptr = value;
|
||||
return;
|
||||
@ -1592,10 +1590,10 @@ Simulator::writeExW(int32_t addr, int value, SimInstruction* instr)
|
||||
return 1;
|
||||
int32_t old = compareExchangeRelaxed(ptr, expected, int32_t(value));
|
||||
return old != expected;
|
||||
} else {
|
||||
printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
uint16_t
|
||||
@ -1607,6 +1605,15 @@ Simulator::readHU(int32_t addr, SimInstruction* instr)
|
||||
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
// See comments in readW.
|
||||
if (wasm::IsPCInWasmCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
char* ptr = reinterpret_cast<char*>(addr);
|
||||
uint16_t value;
|
||||
memcpy(&value, ptr, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
return 0;
|
||||
@ -1619,6 +1626,15 @@ Simulator::readH(int32_t addr, SimInstruction* instr)
|
||||
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
// See comments in readW.
|
||||
if (wasm::IsPCInWasmCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
char* ptr = reinterpret_cast<char*>(addr);
|
||||
int16_t value;
|
||||
memcpy(&value, ptr, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
printf("Unaligned signed halfword read at 0x%08x\n", addr);
|
||||
MOZ_CRASH();
|
||||
return 0;
|
||||
@ -1630,10 +1646,18 @@ Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
|
||||
*ptr = value;
|
||||
} else {
|
||||
printf("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
return;
|
||||
}
|
||||
|
||||
// See the comments above in readW.
|
||||
if (wasm::IsPCInWasmCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
char* ptr = reinterpret_cast<char*>(addr);
|
||||
memcpy(ptr, &value, sizeof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1642,10 +1666,18 @@ Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr)
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
|
||||
*ptr = value;
|
||||
} else {
|
||||
printf("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
return;
|
||||
}
|
||||
|
||||
// See the comments above in readW.
|
||||
if (wasm::IsPCInWasmCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
char* ptr = reinterpret_cast<char*>(addr);
|
||||
memcpy(ptr, &value, sizeof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
uint16_t
|
||||
@ -3194,9 +3226,9 @@ Simulator::decodeType2(SimInstruction* instr)
|
||||
}
|
||||
} else {
|
||||
if (instr->hasL())
|
||||
set_register(rd, readW(addr, instr));
|
||||
set_register(rd, readW(addr, instr, AllowUnaligned));
|
||||
else
|
||||
writeW(addr, get_register(rd), instr);
|
||||
writeW(addr, get_register(rd), instr, AllowUnaligned);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3461,9 +3493,9 @@ Simulator::decodeType3(SimInstruction* instr)
|
||||
}
|
||||
} else {
|
||||
if (instr->hasL())
|
||||
set_register(rd, readW(addr, instr));
|
||||
set_register(rd, readW(addr, instr, AllowUnaligned));
|
||||
else
|
||||
writeW(addr, get_register(rd), instr);
|
||||
writeW(addr, get_register(rd), instr, AllowUnaligned);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4288,8 +4320,10 @@ Simulator::decodeSpecialCondition(SimInstruction* instr)
|
||||
while (r < regs) {
|
||||
uint32_t data[2];
|
||||
get_d_register(Vd + r, data);
|
||||
writeW(address, data[0], instr);
|
||||
writeW(address + 4, data[1], instr);
|
||||
// TODO: We should AllowUnaligned here only if the alignment attribute of
|
||||
// the instruction calls for default alignment.
|
||||
writeW(address, data[0], instr, AllowUnaligned);
|
||||
writeW(address + 4, data[1], instr, AllowUnaligned);
|
||||
address += 8;
|
||||
r++;
|
||||
}
|
||||
@ -4327,8 +4361,10 @@ Simulator::decodeSpecialCondition(SimInstruction* instr)
|
||||
int r = 0;
|
||||
while (r < regs) {
|
||||
uint32_t data[2];
|
||||
data[0] = readW(address, instr);
|
||||
data[1] = readW(address + 4, instr);
|
||||
// TODO: We should AllowUnaligned here only if the alignment attribute of
|
||||
// the instruction calls for default alignment.
|
||||
data[0] = readW(address, instr, AllowUnaligned);
|
||||
data[1] = readW(address + 4, instr, AllowUnaligned);
|
||||
set_d_register(Vd + r, data);
|
||||
address += 8;
|
||||
r++;
|
||||
|
@ -204,6 +204,20 @@ class Simulator
|
||||
end_sim_pc = -2
|
||||
};
|
||||
|
||||
// ForbidUnaligned means "always fault on unaligned access".
|
||||
//
|
||||
// AllowUnaligned means "allow the unaligned access if other conditions are
|
||||
// met". The "other conditions" vary with the instruction: For all
|
||||
// instructions the base condition is !HasAlignmentFault(), ie, the chip is
|
||||
// configured to allow unaligned accesses. For instructions like VLD1
|
||||
// there is an additional constraint that the alignment attribute in the
|
||||
// instruction must be set to "default alignment".
|
||||
|
||||
enum UnalignedPolicy {
|
||||
ForbidUnaligned,
|
||||
AllowUnaligned
|
||||
};
|
||||
|
||||
bool init();
|
||||
|
||||
// Checks if the current instruction should be executed based on its
|
||||
@ -261,8 +275,8 @@ class Simulator
|
||||
inline uint16_t readExHU(int32_t addr, SimInstruction* instr);
|
||||
inline int32_t writeExH(int32_t addr, uint16_t value, SimInstruction* instr);
|
||||
|
||||
inline int readW(int32_t addr, SimInstruction* instr);
|
||||
inline void writeW(int32_t addr, int value, SimInstruction* instr);
|
||||
inline int readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f = ForbidUnaligned);
|
||||
inline void writeW(int32_t addr, int value, SimInstruction* instr, UnalignedPolicy f = ForbidUnaligned);
|
||||
|
||||
inline int readExW(int32_t addr, SimInstruction* instr);
|
||||
inline int writeExW(int32_t addr, int value, SimInstruction* instr);
|
||||
|
@ -85,6 +85,7 @@ static constexpr Register ABINonArgReturnReg1 = { Registers::invalid_reg };
|
||||
|
||||
static constexpr Register WasmTableCallPtrReg = { Registers::invalid_reg };
|
||||
static constexpr Register WasmTableCallSigReg = { Registers::invalid_reg };
|
||||
static constexpr Register WasmTlsReg = { Registers::invalid_reg };
|
||||
|
||||
static constexpr uint32_t ABIStackAlignment = 4;
|
||||
static constexpr uint32_t CodeAlignment = 4;
|
||||
|
@ -44,6 +44,7 @@ UNIFIED_SOURCES += [
|
||||
'testGCExactRooting.cpp',
|
||||
'testGCFinalizeCallback.cpp',
|
||||
'testGCHeapPostBarriers.cpp',
|
||||
'testGCHooks.cpp',
|
||||
'testGCMarking.cpp',
|
||||
'testGCOutOfMemory.cpp',
|
||||
'testGCStoreBufferRemoval.cpp',
|
||||
|
@ -103,7 +103,7 @@ BEGIN_TEST(testGCFinalizeCallback)
|
||||
JS::PrepareForFullGC(cx);
|
||||
js::SliceBudget budget(js::WorkBudget(1));
|
||||
cx->gc.startDebugGC(GC_NORMAL, budget);
|
||||
CHECK(cx->gc.state() == js::gc::MARK);
|
||||
CHECK(cx->gc.state() == js::gc::State::Mark);
|
||||
CHECK(cx->gc.isFullGc());
|
||||
|
||||
JS::RootedObject global4(cx, createTestGlobal());
|
||||
|
36
js/src/jsapi-tests/testGCHooks.cpp
Normal file
36
js/src/jsapi-tests/testGCHooks.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/* 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 "mozilla/UniquePtr.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
static unsigned gSliceCallbackCount = 0;
|
||||
|
||||
static void
|
||||
NonIncrementalGCSliceCallback(JSContext* cx, JS::GCProgress progress, const JS::GCDescription& desc)
|
||||
{
|
||||
++gSliceCallbackCount;
|
||||
MOZ_RELEASE_ASSERT(progress == JS::GC_CYCLE_BEGIN || progress == JS::GC_CYCLE_END);
|
||||
MOZ_RELEASE_ASSERT(desc.isCompartment_ == false);
|
||||
MOZ_RELEASE_ASSERT(desc.invocationKind_ == GC_NORMAL);
|
||||
MOZ_RELEASE_ASSERT(desc.reason_ == JS::gcreason::API);
|
||||
if (progress == JS::GC_CYCLE_END) {
|
||||
mozilla::UniquePtr<char16_t> summary(desc.formatSummaryMessage(cx));
|
||||
mozilla::UniquePtr<char16_t> message(desc.formatSliceMessage(cx));
|
||||
mozilla::UniquePtr<char16_t> json(desc.formatJSON(cx, 0));
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_TEST(testGCSliceCallback)
|
||||
{
|
||||
JS::SetGCSliceCallback(cx, NonIncrementalGCSliceCallback);
|
||||
JS_GC(cx);
|
||||
JS::SetGCSliceCallback(cx, nullptr);
|
||||
CHECK(gSliceCallbackCount == 2);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testGCSliceCallback)
|
127
js/src/jsgc.cpp
127
js/src/jsgc.cpp
@ -53,46 +53,49 @@
|
||||
* The collector proceeds through the following states, the current state being
|
||||
* held in JSRuntime::gcIncrementalState:
|
||||
*
|
||||
* - MARK_ROOTS - marks the stack and other roots
|
||||
* - MARK - incrementally marks reachable things
|
||||
* - SWEEP - sweeps zones in groups and continues marking unswept zones
|
||||
* - MarkRoots - marks the stack and other roots
|
||||
* - Mark - incrementally marks reachable things
|
||||
* - Sweep - sweeps zones in groups and continues marking unswept zones
|
||||
* - Finalize - performs background finalization, concurrent with mutator
|
||||
* - Compact - incrementally compacts by zone
|
||||
* - Decommit - performs background decommit and chunk removal
|
||||
*
|
||||
* The MARK_ROOTS activity always takes place in the first slice. The next two
|
||||
* The MarkRoots activity always takes place in the first slice. The next two
|
||||
* states can take place over one or more slices.
|
||||
*
|
||||
* In other words an incremental collection proceeds like this:
|
||||
*
|
||||
* Slice 1: MARK_ROOTS: Roots pushed onto the mark stack.
|
||||
* MARK: The mark stack is processed by popping an element,
|
||||
* Slice 1: MarkRoots: Roots pushed onto the mark stack.
|
||||
* Mark: The mark stack is processed by popping an element,
|
||||
* marking it, and pushing its children.
|
||||
*
|
||||
* ... JS code runs ...
|
||||
*
|
||||
* Slice 2: MARK: More mark stack processing.
|
||||
* Slice 2: Mark: More mark stack processing.
|
||||
*
|
||||
* ... JS code runs ...
|
||||
*
|
||||
* Slice n-1: MARK: More mark stack processing.
|
||||
* Slice n-1: Mark: More mark stack processing.
|
||||
*
|
||||
* ... JS code runs ...
|
||||
*
|
||||
* Slice n: MARK: Mark stack is completely drained.
|
||||
* SWEEP: Select first group of zones to sweep and sweep them.
|
||||
* Slice n: Mark: Mark stack is completely drained.
|
||||
* Sweep: Select first group of zones to sweep and sweep them.
|
||||
*
|
||||
* ... JS code runs ...
|
||||
*
|
||||
* Slice n+1: SWEEP: Mark objects in unswept zones that were newly
|
||||
* Slice n+1: Sweep: Mark objects in unswept zones that were newly
|
||||
* identified as alive (see below). Then sweep more zone
|
||||
* groups.
|
||||
*
|
||||
* ... JS code runs ...
|
||||
*
|
||||
* Slice n+2: SWEEP: Mark objects in unswept zones that were newly
|
||||
* Slice n+2: Sweep: Mark objects in unswept zones that were newly
|
||||
* identified as alive. Then sweep more zone groups.
|
||||
*
|
||||
* ... JS code runs ...
|
||||
*
|
||||
* Slice m: SWEEP: Sweeping is finished, and background sweeping
|
||||
* Slice m: Sweep: Sweeping is finished, and background sweeping
|
||||
* started on the helper thread.
|
||||
*
|
||||
* ... JS code runs, remaining sweeping done on background thread ...
|
||||
@ -141,7 +144,7 @@
|
||||
*
|
||||
* The order of sweeping is restricted by cross compartment pointers - for
|
||||
* example say that object |a| from zone A points to object |b| in zone B and
|
||||
* neither object was marked when we transitioned to the SWEEP phase. Imagine we
|
||||
* neither object was marked when we transitioned to the Sweep phase. Imagine we
|
||||
* sweep B first and then return to the mutator. It's possible that the mutator
|
||||
* could cause |a| to become alive through a read barrier (perhaps it was a
|
||||
* shape that was accessed via a shape table). Then we would need to mark |b|,
|
||||
@ -822,7 +825,7 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
||||
#ifdef DEBUG
|
||||
disableStrictProxyCheckingCount(0),
|
||||
#endif
|
||||
incrementalState(gc::NO_INCREMENTAL),
|
||||
incrementalState(gc::State::NotActive),
|
||||
lastMarkSlice(false),
|
||||
sweepOnBackgroundThread(false),
|
||||
foundBlackGrayEdges(false),
|
||||
@ -4176,7 +4179,7 @@ js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock)
|
||||
|
||||
/* Re-do all the marking, but non-incrementally. */
|
||||
js::gc::State state = gc->incrementalState;
|
||||
gc->incrementalState = MARK_ROOTS;
|
||||
gc->incrementalState = State::MarkRoots;
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_MARK);
|
||||
@ -4196,12 +4199,12 @@ js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock)
|
||||
|
||||
gc->markRuntime(gcmarker, GCRuntime::MarkRuntime, lock);
|
||||
|
||||
gc->incrementalState = MARK;
|
||||
gc->incrementalState = State::Mark;
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
MOZ_RELEASE_ASSERT(gc->marker.drainMarkStack(unlimited));
|
||||
}
|
||||
|
||||
gc->incrementalState = SWEEP;
|
||||
gc->incrementalState = State::Sweep;
|
||||
{
|
||||
gcstats::AutoPhase ap1(gc->stats, gcstats::PHASE_SWEEP);
|
||||
gcstats::AutoPhase ap2(gc->stats, gcstats::PHASE_SWEEP_MARK);
|
||||
@ -5621,10 +5624,14 @@ void
|
||||
GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lock)
|
||||
{
|
||||
switch (incrementalState) {
|
||||
case NO_INCREMENTAL:
|
||||
case State::NotActive:
|
||||
return;
|
||||
|
||||
case MARK: {
|
||||
case State::MarkRoots:
|
||||
MOZ_CRASH("resetIncrementalGC did not expect MarkRoots state");
|
||||
break;
|
||||
|
||||
case State::Mark: {
|
||||
/* Cancel any ongoing marking. */
|
||||
marker.reset();
|
||||
marker.stop();
|
||||
@ -5641,14 +5648,14 @@ GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lo
|
||||
|
||||
blocksToFreeAfterSweeping.freeAll();
|
||||
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
incrementalState = State::NotActive;
|
||||
|
||||
MOZ_ASSERT(!marker.shouldCheckCompartments());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SWEEP: {
|
||||
case State::Sweep: {
|
||||
marker.reset();
|
||||
|
||||
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
|
||||
@ -5673,7 +5680,7 @@ GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lo
|
||||
break;
|
||||
}
|
||||
|
||||
case FINALIZE: {
|
||||
case State::Finalize: {
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||
rt->gc.waitBackgroundSweepOrAllocEnd();
|
||||
@ -5690,7 +5697,7 @@ GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lo
|
||||
break;
|
||||
}
|
||||
|
||||
case COMPACT: {
|
||||
case State::Compact: {
|
||||
bool wasCompacting = isCompacting;
|
||||
|
||||
isCompacting = true;
|
||||
@ -5704,14 +5711,11 @@ GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lo
|
||||
break;
|
||||
}
|
||||
|
||||
case DECOMMIT: {
|
||||
case State::Decommit: {
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalCollectSlice(unlimited, JS::gcreason::RESET, lock);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Invalid incremental GC state");
|
||||
}
|
||||
|
||||
stats.reset(reason);
|
||||
@ -5724,7 +5728,7 @@ GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lo
|
||||
MOZ_ASSERT(!zone->isOnList());
|
||||
}
|
||||
MOZ_ASSERT(zonesToMaybeCompact.isEmpty());
|
||||
MOZ_ASSERT(incrementalState == NO_INCREMENTAL);
|
||||
MOZ_ASSERT(incrementalState == State::NotActive);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -5843,33 +5847,33 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
}
|
||||
|
||||
switch (incrementalState) {
|
||||
case NO_INCREMENTAL:
|
||||
case State::NotActive:
|
||||
initialReason = reason;
|
||||
cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind);
|
||||
isCompacting = shouldCompact();
|
||||
lastMarkSlice = false;
|
||||
|
||||
incrementalState = MARK_ROOTS;
|
||||
incrementalState = State::MarkRoots;
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case MARK_ROOTS:
|
||||
case State::MarkRoots:
|
||||
if (!beginMarkPhase(reason, lock)) {
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
incrementalState = State::NotActive;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!destroyingRuntime)
|
||||
pushZealSelectedObjects();
|
||||
|
||||
incrementalState = MARK;
|
||||
incrementalState = State::Mark;
|
||||
|
||||
if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalRootsThenFinish))
|
||||
break;
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case MARK:
|
||||
case State::Mark:
|
||||
AutoGCRooter::traceAllWrappers(&marker);
|
||||
|
||||
/* If we needed delayed marking for gray roots, then collect until done. */
|
||||
@ -5884,19 +5888,19 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
MOZ_ASSERT(marker.isDrained());
|
||||
|
||||
if (!lastMarkSlice && isIncremental && useZeal &&
|
||||
((initialState == MARK && !hasZealMode(ZealMode::IncrementalRootsThenFinish)) ||
|
||||
((initialState == State::Mark && !hasZealMode(ZealMode::IncrementalRootsThenFinish)) ||
|
||||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
|
||||
{
|
||||
/*
|
||||
* Yield with the aim of starting the sweep in the next
|
||||
* slice. We will need to mark anything new on the stack
|
||||
* when we resume, so we stay in MARK state.
|
||||
* when we resume, so we stay in Mark state.
|
||||
*/
|
||||
lastMarkSlice = true;
|
||||
break;
|
||||
}
|
||||
|
||||
incrementalState = SWEEP;
|
||||
incrementalState = State::Sweep;
|
||||
|
||||
/*
|
||||
* This runs to completion, but we don't continue if the budget is
|
||||
@ -5915,13 +5919,13 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case SWEEP:
|
||||
case State::Sweep:
|
||||
if (sweepPhase(budget, lock) == NotFinished)
|
||||
break;
|
||||
|
||||
endSweepPhase(destroyingRuntime, lock);
|
||||
|
||||
incrementalState = FINALIZE;
|
||||
incrementalState = State::Finalize;
|
||||
|
||||
/* Yield before compacting since it is not incremental. */
|
||||
if (isCompacting && isIncremental)
|
||||
@ -5929,7 +5933,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case FINALIZE:
|
||||
case State::Finalize:
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||
|
||||
@ -5955,11 +5959,11 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!startedCompacting);
|
||||
incrementalState = COMPACT;
|
||||
incrementalState = State::Compact;
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case COMPACT:
|
||||
case State::Compact:
|
||||
if (isCompacting) {
|
||||
if (!startedCompacting)
|
||||
beginCompactPhase();
|
||||
@ -5971,11 +5975,11 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
}
|
||||
|
||||
startDecommit();
|
||||
incrementalState = DECOMMIT;
|
||||
incrementalState = State::Decommit;
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case DECOMMIT:
|
||||
case State::Decommit:
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||
|
||||
@ -5987,11 +5991,8 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
}
|
||||
|
||||
finishCollection(reason);
|
||||
incrementalState = NO_INCREMENTAL;
|
||||
incrementalState = State::NotActive;
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("unexpected GC incrementalState");
|
||||
}
|
||||
}
|
||||
|
||||
@ -6154,7 +6155,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
|
||||
}
|
||||
|
||||
/* The GC was reset, so we need a do-over. */
|
||||
if (prevState != NO_INCREMENTAL && !isIncrementalGCInProgress())
|
||||
if (prevState != State::NotActive && !isIncrementalGCInProgress())
|
||||
return true;
|
||||
|
||||
TraceMajorGCStart();
|
||||
@ -6361,7 +6362,7 @@ GCRuntime::finishGC(JS::gcreason::Reason reason)
|
||||
// compacting phase if we need to finish an ongoing incremental GC
|
||||
// non-incrementally to avoid janking the browser.
|
||||
if (!IsOOMReason(initialReason)) {
|
||||
if (incrementalState == COMPACT) {
|
||||
if (incrementalState == State::Compact) {
|
||||
abortGC();
|
||||
return;
|
||||
}
|
||||
@ -6799,8 +6800,8 @@ GCRuntime::runDebugGC()
|
||||
* or compact phases.
|
||||
*/
|
||||
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
|
||||
if ((initialState == MARK && incrementalState == SWEEP) ||
|
||||
(initialState == SWEEP && incrementalState == COMPACT))
|
||||
if ((initialState == State::Mark && incrementalState == State::Sweep) ||
|
||||
(initialState == State::Sweep && incrementalState == State::Compact))
|
||||
{
|
||||
incrementalLimit = zealFrequency / 2;
|
||||
}
|
||||
@ -7313,7 +7314,7 @@ JS::IsIncrementalGCInProgress(JSContext* cx)
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::IsIncrementalBarrierNeeded(JSContext* cx)
|
||||
{
|
||||
return cx->gc.state() == gc::MARK && !cx->isHeapBusy();
|
||||
return cx->gc.state() == gc::State::Mark && !cx->isHeapBusy();
|
||||
}
|
||||
|
||||
struct IncrementalReferenceBarrierFunctor {
|
||||
@ -7596,18 +7597,12 @@ NewMemoryInfoObject(JSContext* cx)
|
||||
const char*
|
||||
StateName(State state)
|
||||
{
|
||||
static const char* names[] = {
|
||||
"None",
|
||||
"MarkRoots",
|
||||
"Mark",
|
||||
"Sweep",
|
||||
"Finalize",
|
||||
"Compact",
|
||||
"Decommit"
|
||||
};
|
||||
MOZ_ASSERT(ArrayLength(names) == NUM_STATES);
|
||||
MOZ_ASSERT(state < NUM_STATES);
|
||||
return names[state];
|
||||
switch(state) {
|
||||
#define MAKE_CASE(name) case State::name: return #name;
|
||||
GCSTATES(MAKE_CASE)
|
||||
#undef MAKE_CASE
|
||||
}
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("invalide gc::State enum value");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -43,16 +43,18 @@ namespace gc {
|
||||
|
||||
struct FinalizePhase;
|
||||
|
||||
enum State {
|
||||
NO_INCREMENTAL,
|
||||
MARK_ROOTS,
|
||||
MARK,
|
||||
SWEEP,
|
||||
FINALIZE,
|
||||
COMPACT,
|
||||
DECOMMIT,
|
||||
|
||||
NUM_STATES
|
||||
#define GCSTATES(D) \
|
||||
D(NotActive) \
|
||||
D(MarkRoots) \
|
||||
D(Mark) \
|
||||
D(Sweep) \
|
||||
D(Finalize) \
|
||||
D(Compact) \
|
||||
D(Decommit)
|
||||
enum class State {
|
||||
#define MAKE_STATE(name) name,
|
||||
GCSTATES(MAKE_STATE)
|
||||
#undef MAKE_STATE
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -528,6 +528,7 @@ dnl Android libstdc++, placed here so it can use MOZ_ARCH
|
||||
dnl computed above.
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_ANDROID_CPU_ARCH
|
||||
MOZ_ANDROID_STLPORT
|
||||
|
||||
dnl ========================================================
|
||||
|
25
js/src/tests/ecma_6/Array/concat-proxy.js
Normal file
25
js/src/tests/ecma_6/Array/concat-proxy.js
Normal file
@ -0,0 +1,25 @@
|
||||
var BUGNUMBER = 1287520;
|
||||
var summary = 'Array.prototype.concat should check HasProperty everytime for non-dense array';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = [1, 2, 3];
|
||||
a.constructor = {
|
||||
[Symbol.species]: function(...args) {
|
||||
var p = new Proxy(new Array(...args), {
|
||||
defineProperty(target, propertyKey, receiver) {
|
||||
if (propertyKey === "0") delete a[1];
|
||||
return Reflect.defineProperty(target, propertyKey, receiver);
|
||||
}
|
||||
});
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
var p = a.concat();
|
||||
assertEq(0 in p, true);
|
||||
assertEq(1 in p, false);
|
||||
assertEq(2 in p, true);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
71
js/src/tests/ecma_6/RegExp/ignoreCase-multiple.js
Normal file
71
js/src/tests/ecma_6/RegExp/ignoreCase-multiple.js
Normal file
@ -0,0 +1,71 @@
|
||||
var BUGNUMBER = 1280046;
|
||||
var summary = "ignoreCase match should perform Canonicalize both on input and pattern.";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// Each element [code1, upper, code2] satisfies the following condition:
|
||||
// ToUpperCase(code1) == upper
|
||||
// ToUpperCase(code2) == upper
|
||||
var pairs =
|
||||
[
|
||||
// U+00B5: MICRO SIGN
|
||||
// U+039C: GREEK CAPITAL LETTER MU
|
||||
// U+03BC: GREEK SMALL LETTER MU
|
||||
["\u00B5", "\u039C", "\u03BC"],
|
||||
// U+0345: COMBINING GREEK YPOGEGRAMMENI
|
||||
// U+0399: GREEK CAPITAL LETTER IOTA
|
||||
// U+03B9: GREEK SMALL LETTER IOTA
|
||||
["\u0345", "\u0399", "\u03B9"],
|
||||
// U+03C2: GREEK SMALL LETTER FINAL SIGMA
|
||||
// U+03A3: GREEK CAPITAL LETTER SIGMA
|
||||
// U+03C3: GREEK SMALL LETTER SIGMA
|
||||
["\u03C2", "\u03A3", "\u03C3"],
|
||||
// U+03D0: GREEK BETA SYMBOL
|
||||
// U+0392: GREEK CAPITAL LETTER BETA
|
||||
// U+03B2: GREEK SMALL LETTER BETA
|
||||
["\u03D0", "\u0392", "\u03B2"],
|
||||
// U+03D1: GREEK THETA SYMBOL
|
||||
// U+0398: GREEK CAPITAL LETTER THETA
|
||||
// U+03B8: GREEK SMALL LETTER THETA
|
||||
["\u03D1", "\u0398", "\u03B8"],
|
||||
// U+03D5: GREEK PHI SYMBOL
|
||||
// U+03A6: GREEK CAPITAL LETTER PHI
|
||||
// U+03C6: GREEK SMALL LETTER PHI
|
||||
["\u03D5", "\u03A6", "\u03C6"],
|
||||
// U+03D6: GREEK PI SYMBOL
|
||||
// U+03A0: GREEK CAPITAL LETTER PI
|
||||
// U+03C0: GREEK SMALL LETTER PI
|
||||
["\u03D6", "\u03A0", "\u03C0"],
|
||||
// U+03F0: GREEK KAPPA SYMBOL
|
||||
// U+039A: GREEK CAPITAL LETTER KAPPA
|
||||
// U+03BA: GREEK SMALL LETTER KAPPA
|
||||
["\u03F0", "\u039A", "\u03BA"],
|
||||
// U+03F1: GREEK RHO SYMBOL
|
||||
// U+03A1: GREEK CAPITAL LETTER RHO
|
||||
// U+03C1: GREEK SMALL LETTER RHO
|
||||
["\u03F1", "\u03A1", "\u03C1"],
|
||||
// U+03F5: GREEK LUNATE EPSILON SYMBOL
|
||||
// U+0395: GREEK CAPITAL LETTER EPSILON
|
||||
// U+03B5: GREEK SMALL LETTER EPSILON
|
||||
["\u03F5", "\u0395", "\u03B5"],
|
||||
// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
|
||||
// U+1E60: LATIN CAPITAL LETTER S WITH DOT ABOVE
|
||||
// U+1E61: LATIN SMALL LETTER S WITH DOT ABOVE
|
||||
["\u1E9B", "\u1E60", "\u1E61"],
|
||||
// U+1FBE: GREEK PROSGEGRAMMENI
|
||||
// U+0399: GREEK CAPITAL LETTER IOTA
|
||||
// U+03B9: GREEK SMALL LETTER IOTA
|
||||
["\u1FBE", "\u0399", "\u03B9"],
|
||||
];
|
||||
|
||||
for (var [code1, upper, code2] of pairs) {
|
||||
assertEq(new RegExp(code1, "i").test(code2), true);
|
||||
assertEq(new RegExp(code1, "i").test(upper), true);
|
||||
assertEq(new RegExp(upper, "i").test(code1), true);
|
||||
assertEq(new RegExp(upper, "i").test(code2), true);
|
||||
assertEq(new RegExp(code2, "i").test(code1), true);
|
||||
assertEq(new RegExp(code2, "i").test(upper), true);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
18
js/src/tests/ecma_6/RegExp/replace-global-unicode.js
Normal file
18
js/src/tests/ecma_6/RegExp/replace-global-unicode.js
Normal file
@ -0,0 +1,18 @@
|
||||
var BUGNUMBER = 1287524;
|
||||
var summary = 'RegExp.prototype[@@replace] should not use optimized path if RegExp.prototype.unicode is modified.';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
Object.defineProperty(RegExp.prototype, "unicode", {
|
||||
get() {
|
||||
RegExp.prototype.exec = () => null;
|
||||
}
|
||||
});
|
||||
|
||||
var rx = RegExp("a", "g");
|
||||
var s = "abba";
|
||||
var r = rx[Symbol.replace](s, "c");
|
||||
assertEq(r, "abba");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
14
js/src/tests/ecma_6/RegExp/split-limit.js
Normal file
14
js/src/tests/ecma_6/RegExp/split-limit.js
Normal file
@ -0,0 +1,14 @@
|
||||
var BUGNUMBER = 1287525;
|
||||
var summary = "RegExp.prototype[@@split] shouldn't use optimized path if limit is not number.";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var rx = /a/;
|
||||
var r = rx[Symbol.split]("abba", {valueOf() {
|
||||
RegExp.prototype.exec = () => null;
|
||||
return 100;
|
||||
}});
|
||||
assertEq(r.length, 1);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
19
js/src/tests/ecma_6/RegExp/split-prop-access.js
Normal file
19
js/src/tests/ecma_6/RegExp/split-prop-access.js
Normal file
@ -0,0 +1,19 @@
|
||||
var BUGNUMBER = 1287525;
|
||||
var summary = 'String.prototype.split should call ToUint32(limit) before ToString(separator).';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var accessed = false;
|
||||
|
||||
var rx = /a/;
|
||||
Object.defineProperty(rx, Symbol.match, {
|
||||
get() {
|
||||
accessed = true;
|
||||
}
|
||||
});
|
||||
rx[Symbol.split]("abba");
|
||||
|
||||
assertEq(accessed, true);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
@ -2829,12 +2829,12 @@ Debugger::markIncomingCrossCompartmentEdges(JSTracer* trc)
|
||||
{
|
||||
JSRuntime* rt = trc->runtime();
|
||||
gc::State state = rt->gc.state();
|
||||
MOZ_ASSERT(state == gc::MARK_ROOTS || state == gc::COMPACT);
|
||||
MOZ_ASSERT(state == gc::State::MarkRoots || state == gc::State::Compact);
|
||||
|
||||
for (Debugger* dbg : rt->debuggerList) {
|
||||
Zone* zone = MaybeForwarded(dbg->object.get())->zone();
|
||||
if ((state == gc::MARK_ROOTS && !zone->isCollecting()) ||
|
||||
(state == gc::COMPACT && !zone->isGCCompacting()))
|
||||
if ((state == gc::State::MarkRoots && !zone->isCollecting()) ||
|
||||
(state == gc::State::Compact && !zone->isGCCompacting()))
|
||||
{
|
||||
dbg->markCrossCompartmentEdges(trc);
|
||||
}
|
||||
|
@ -326,6 +326,7 @@ class RegExpCompartment
|
||||
* The shape of RegExp.prototype object that satisfies following:
|
||||
* * RegExp.prototype.global getter is not modified
|
||||
* * RegExp.prototype.sticky getter is not modified
|
||||
* * RegExp.prototype.unicode getter is not modified
|
||||
* * RegExp.prototype.exec is an own data property
|
||||
* * RegExp.prototype[@@match] is an own data property
|
||||
* * RegExp.prototype[@@search] is an own data property
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user