mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
19dc73f937
@ -254,7 +254,7 @@ endif # MOZ_CRASHREPORTER
|
||||
uploadsymbols:
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
ifdef SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE
|
||||
$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
|
||||
$(PYTHON) -u $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
|
||||
else
|
||||
$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(SYMBOL_INDEX_NAME) '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
|
||||
endif
|
||||
|
@ -15,6 +15,9 @@ var FullScreen = {
|
||||
init: function() {
|
||||
// called when we go into full screen, even if initiated by a web page script
|
||||
window.addEventListener("fullscreen", this, true);
|
||||
window.addEventListener("MozDOMFullscreen:Exited", this,
|
||||
/* useCapture */ true,
|
||||
/* wantsUntrusted */ false);
|
||||
for (let type of this._MESSAGES) {
|
||||
window.messageManager.addMessageListener(type, this);
|
||||
}
|
||||
@ -97,6 +100,9 @@ var FullScreen = {
|
||||
if (event.propertyName == "opacity")
|
||||
this.cancelWarning();
|
||||
break;
|
||||
case "MozDOMFullscreen:Exited":
|
||||
this.cleanupDomFullscreen();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -125,15 +131,7 @@ var FullScreen = {
|
||||
if (this._isRemoteBrowser(browser)) {
|
||||
this._windowUtils.remoteFrameFullscreenReverted();
|
||||
}
|
||||
document.documentElement.removeAttribute("inDOMFullscreen");
|
||||
this.cleanupDomFullscreen();
|
||||
this.showNavToolbox();
|
||||
// If we are still in fullscreen mode, re-hide
|
||||
// the toolbox with animation.
|
||||
if (window.fullScreen) {
|
||||
this._shouldAnimate = true;
|
||||
this.hideNavToolbox();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -189,8 +187,6 @@ var FullScreen = {
|
||||
document.removeEventListener("keypress", this._keyToggleCallback, false);
|
||||
document.removeEventListener("popupshown", this._setPopupOpen, false);
|
||||
document.removeEventListener("popuphidden", this._setPopupOpen, false);
|
||||
|
||||
this.cleanupDomFullscreen();
|
||||
}
|
||||
},
|
||||
|
||||
@ -202,6 +198,15 @@ var FullScreen = {
|
||||
if (!this.useLionFullScreen)
|
||||
window.removeEventListener("activate", this);
|
||||
|
||||
document.documentElement.removeAttribute("inDOMFullscreen");
|
||||
this.showNavToolbox();
|
||||
// If we are still in fullscreen mode, re-hide
|
||||
// the toolbox with animation.
|
||||
if (window.fullScreen) {
|
||||
this._shouldAnimate = true;
|
||||
this.hideNavToolbox();
|
||||
}
|
||||
|
||||
window.messageManager
|
||||
.broadcastAsyncMessage("DOMFullscreen:CleanUp");
|
||||
},
|
||||
|
@ -344,7 +344,7 @@ skip-if = asan # Disabled because it takes a long time (see test for more inform
|
||||
[browser_popupUI.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_popup_blocker.js]
|
||||
skip-if = e10s && debug # Frequent bug 1125520 failures
|
||||
skip-if = (os == 'linux') || (e10s && debug) # Frequent bug 1081925 and bug 1125520 failures
|
||||
[browser_printpreview.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1101973 - breaks the next test in e10s, and may be responsible for later timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
|
||||
[browser_private_browsing_window.js]
|
||||
|
@ -36,56 +36,14 @@ var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
formHistoryStartup.observe(null, "profile-after-change", null);
|
||||
|
||||
let notificationIndex = 0;
|
||||
|
||||
let notificationsObserver = {
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
print("Received notification: " + aTopic);
|
||||
|
||||
// Note that some of these notifications could arrive multiple times, for
|
||||
// example in case of sync, we allow that.
|
||||
if (EXPECTED_NOTIFICATIONS[notificationIndex] != aTopic)
|
||||
notificationIndex++;
|
||||
do_check_eq(EXPECTED_NOTIFICATIONS[notificationIndex], aTopic);
|
||||
|
||||
if (aTopic != TOPIC_CONNECTION_CLOSED)
|
||||
return;
|
||||
|
||||
getDistinctNotifications().forEach(
|
||||
function (topic) Services.obs.removeObserver(notificationsObserver, topic)
|
||||
);
|
||||
|
||||
print("Looking for uncleared stuff.");
|
||||
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT id FROM moz_places WHERE url = :page_url "
|
||||
);
|
||||
|
||||
try {
|
||||
URIS.forEach(function(aUrl) {
|
||||
stmt.params.page_url = aUrl;
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
});
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Check cache.
|
||||
checkCache(FTP_URL);
|
||||
}
|
||||
}
|
||||
|
||||
let timeInMicroseconds = Date.now() * 1000;
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function test_execute() {
|
||||
do_test_pending();
|
||||
|
||||
print("Initialize browserglue before Places");
|
||||
add_task(function* test_execute() {
|
||||
do_print("Initialize browserglue before Places");
|
||||
|
||||
// Avoid default bookmarks import.
|
||||
let glue = Cc["@mozilla.org/browser/browserglue;1"].
|
||||
@ -105,66 +63,78 @@ add_task(function test_execute() {
|
||||
|
||||
Services.prefs.setBoolPref("privacy.sanitize.sanitizeOnShutdown", true);
|
||||
|
||||
print("Add visits.");
|
||||
do_print("Add visits.");
|
||||
for (let aUrl of URIS) {
|
||||
yield PlacesTestUtils.addVisits({
|
||||
uri: uri(aUrl), visitDate: timeInMicroseconds++,
|
||||
transition: PlacesUtils.history.TRANSITION_TYPED
|
||||
});
|
||||
}
|
||||
print("Add cache.");
|
||||
storeCache(FTP_URL, "testData");
|
||||
do_print("Add cache.");
|
||||
yield storeCache(FTP_URL, "testData");
|
||||
});
|
||||
|
||||
function run_test_continue()
|
||||
{
|
||||
print("Simulate and wait shutdown.");
|
||||
getDistinctNotifications().forEach(
|
||||
function (topic)
|
||||
Services.obs.addObserver(notificationsObserver, topic, false)
|
||||
add_task(function* run_test_continue() {
|
||||
do_print("Simulate and wait shutdown.");
|
||||
yield shutdownPlaces();
|
||||
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT id FROM moz_places WHERE url = :page_url "
|
||||
);
|
||||
|
||||
shutdownPlaces();
|
||||
try {
|
||||
URIS.forEach(function(aUrl) {
|
||||
stmt.params.page_url = aUrl;
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
});
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
do_print("Check cache");
|
||||
// Check cache.
|
||||
let promiseCacheChecked = checkCache(FTP_URL);
|
||||
|
||||
do_print("Shutdown the download manager");
|
||||
// Shutdown the download manager.
|
||||
Services.obs.notifyObservers(null, "quit-application", null);
|
||||
}
|
||||
|
||||
function getDistinctNotifications() {
|
||||
let ar = EXPECTED_NOTIFICATIONS.concat(UNEXPECTED_NOTIFICATIONS);
|
||||
return [ar[i] for (i in ar) if (ar.slice(0, i).indexOf(ar[i]) == -1)];
|
||||
}
|
||||
yield promiseCacheChecked;
|
||||
});
|
||||
|
||||
function storeCache(aURL, aContent) {
|
||||
let cache = Services.cache2;
|
||||
let storage = cache.diskCacheStorage(LoadContextInfo.default, false);
|
||||
|
||||
var storeCacheListener = {
|
||||
onCacheEntryCheck: function (entry, appcache) {
|
||||
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
|
||||
},
|
||||
return new Promise(resolve => {
|
||||
let storeCacheListener = {
|
||||
onCacheEntryCheck: function (entry, appcache) {
|
||||
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
|
||||
},
|
||||
|
||||
onCacheEntryAvailable: function (entry, isnew, appcache, status) {
|
||||
do_check_eq(status, Cr.NS_OK);
|
||||
onCacheEntryAvailable: function (entry, isnew, appcache, status) {
|
||||
do_check_eq(status, Cr.NS_OK);
|
||||
|
||||
entry.setMetaDataElement("servertype", "0");
|
||||
var os = entry.openOutputStream(0);
|
||||
entry.setMetaDataElement("servertype", "0");
|
||||
var os = entry.openOutputStream(0);
|
||||
|
||||
var written = os.write(aContent, aContent.length);
|
||||
if (written != aContent.length) {
|
||||
do_throw("os.write has not written all data!\n" +
|
||||
" Expected: " + written + "\n" +
|
||||
" Actual: " + aContent.length + "\n");
|
||||
var written = os.write(aContent, aContent.length);
|
||||
if (written != aContent.length) {
|
||||
do_throw("os.write has not written all data!\n" +
|
||||
" Expected: " + written + "\n" +
|
||||
" Actual: " + aContent.length + "\n");
|
||||
}
|
||||
os.close();
|
||||
entry.close();
|
||||
resolve();
|
||||
}
|
||||
os.close();
|
||||
entry.close();
|
||||
do_execute_soon(run_test_continue);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
storage.asyncOpenURI(Services.io.newURI(aURL, null, null), "",
|
||||
Ci.nsICacheStorage.OPEN_NORMALLY,
|
||||
storeCacheListener);
|
||||
storage.asyncOpenURI(Services.io.newURI(aURL, null, null), "",
|
||||
Ci.nsICacheStorage.OPEN_NORMALLY,
|
||||
storeCacheListener);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -172,14 +142,16 @@ function checkCache(aURL) {
|
||||
let cache = Services.cache2;
|
||||
let storage = cache.diskCacheStorage(LoadContextInfo.default, false);
|
||||
|
||||
var checkCacheListener = {
|
||||
onCacheEntryAvailable: function (entry, isnew, appcache, status) {
|
||||
do_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
|
||||
do_test_finished();
|
||||
}
|
||||
};
|
||||
return new Promise(resolve => {
|
||||
let checkCacheListener = {
|
||||
onCacheEntryAvailable: function (entry, isnew, appcache, status) {
|
||||
do_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
storage.asyncOpenURI(Services.io.newURI(aURL, null, null), "",
|
||||
Ci.nsICacheStorage.OPEN_READONLY,
|
||||
checkCacheListener);
|
||||
storage.asyncOpenURI(Services.io.newURI(aURL, null, null), "",
|
||||
Ci.nsICacheStorage.OPEN_READONLY,
|
||||
checkCacheListener);
|
||||
});
|
||||
}
|
||||
|
@ -264,8 +264,7 @@ public class SUTAgentAndroid extends Activity
|
||||
{
|
||||
if (ba != null)
|
||||
{
|
||||
sUniqueID = ba.getAddress();
|
||||
sUniqueID.toLowerCase();
|
||||
sUniqueID = ba.getAddress().toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ packages.txt:testing/mozbase/packages.txt
|
||||
objdir:build
|
||||
gyp.pth:media/webrtc/trunk/tools/gyp/pylib
|
||||
pyasn1.pth:python/pyasn1
|
||||
pyasn1_modules.pth:python/pyasn1-modules
|
||||
bitstring.pth:python/bitstring
|
||||
redo.pth:python/redo
|
||||
requests.pth:python/requests
|
||||
rsa.pth:python/rsa
|
||||
|
@ -3034,6 +3034,7 @@ then
|
||||
esac
|
||||
LDFLAGS="${_PTHREAD_LDFLAGS} ${LDFLAGS}"
|
||||
AC_SUBST(MOZ_USE_PTHREADS)
|
||||
MOZ_CHECK_HEADERS(pthread.h)
|
||||
fi
|
||||
|
||||
|
||||
@ -3603,7 +3604,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
|
||||
_USE_SYSTEM_NSS=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSS"; then
|
||||
AM_PATH_NSS(3.19, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
AM_PATH_NSS(3.19.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSS"; then
|
||||
|
@ -17,11 +17,15 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=386782
|
||||
// designMode document is in a frame inside the window, as this means
|
||||
// the editable region is not in the root docshell (a less complicated case).
|
||||
|
||||
var pageShowChecker = '<scr' + 'ipt>' +
|
||||
'window.addEventListener("pageshow", function(event) {' +
|
||||
'window.opener.postMessage({persisted:event.persisted}, "*");' +
|
||||
'});</scr' + 'ipt>';
|
||||
|
||||
var gTests = [
|
||||
{
|
||||
// <html><body><p>designModeDocument</p></body></html>
|
||||
url: "data:text/html;charset=utf-8,<html><body><p>designModeDocument</p></body></html>",
|
||||
url: "data:text/html;charset=utf-8,<html><head>" + pageShowChecker + "</head><body><p>designModeDocument</p></body></html>",
|
||||
name: 'designModeNavigate',
|
||||
onload(doc) { doc.designMode = "on"; },
|
||||
expectedBodyBeforeEdit: '<p>designModeDocument</p>',
|
||||
@ -30,7 +34,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=386782
|
||||
},
|
||||
{
|
||||
// <html><body contentEditable="true"><p>contentEditable</p></body></html>
|
||||
url: 'data:text/html;charset=utf-8,<html><body contentEditable="true"><p>contentEditable</p></body></html>',
|
||||
url: "data:text/html;charset=utf-8,<html><head>" + pageShowChecker + "</head><body contentEditable=\"true\"><p>contentEditable</p></body></html>",
|
||||
name: 'contentEditableNavigate',
|
||||
expectedBodyBeforeEdit: '<p>contentEditable</p>',
|
||||
expectedBodyAfterEdit: 'EDITED <br><p>contentEditable</p>',
|
||||
@ -51,12 +55,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=386782
|
||||
}
|
||||
gTest = gTests[gTestNum];
|
||||
gTest.window = window.open(gTest.url, gTest.name, "width=500,height=500");
|
||||
gTest.window.addEventListener("load", function() {
|
||||
window.onmessage = function(e) {
|
||||
is(e.data.persisted, false, "Initial load cannot be persisted");
|
||||
window.onmessage = null;
|
||||
if ("onload" in gTest) {
|
||||
gTest.onload(gTest.window.document);
|
||||
}
|
||||
SimpleTest.waitForFocus(beginTest, gTest.window);
|
||||
}, false);
|
||||
};
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
@ -73,10 +79,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=386782
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
window.onmessage = function(e) {
|
||||
window.onmessage = null;
|
||||
// Skip the test if the page is not loaded from the bf-cache when going back.
|
||||
if (e.data.persisted) {
|
||||
checkStillEditable();
|
||||
} else {
|
||||
gTest.window.close();
|
||||
goNext();
|
||||
}
|
||||
};
|
||||
gTest.window.history.back();
|
||||
setTimeout(function() {
|
||||
SimpleTest.waitForFocus(checkStillEditable, gTest.window);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function checkStillEditable() {
|
||||
|
@ -43,6 +43,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=669671
|
||||
* *should* load from the cache.
|
||||
*
|
||||
**/
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function onChildLoad()
|
||||
|
@ -668,7 +668,7 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arguments.AppendElement(value)) {
|
||||
if (!arguments.AppendElement(value, fallible)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -830,7 +830,7 @@ Console::Time(JSContext* aCx, const JS::Handle<JS::Value> aTime)
|
||||
Sequence<JS::Value> data;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &data);
|
||||
|
||||
if (!aTime.isUndefined() && !data.AppendElement(aTime)) {
|
||||
if (!aTime.isUndefined() && !data.AppendElement(aTime, fallible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -843,7 +843,7 @@ Console::TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime)
|
||||
Sequence<JS::Value> data;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &data);
|
||||
|
||||
if (!aTime.isUndefined() && !data.AppendElement(aTime)) {
|
||||
if (!aTime.isUndefined() && !data.AppendElement(aTime, fallible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -856,7 +856,7 @@ Console::TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData)
|
||||
Sequence<JS::Value> data;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &data);
|
||||
|
||||
if (aData.isString() && !data.AppendElement(aData)) {
|
||||
if (aData.isString() && !data.AppendElement(aData, fallible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -896,7 +896,7 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||
Sequence<JS::Value>& sequence = event.mArguments.Value();
|
||||
|
||||
for (uint32_t i = 0; i < aData.Length(); ++i) {
|
||||
if (!sequence.AppendElement(aData[i])) {
|
||||
if (!sequence.AppendElement(aData[i], fallible)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1440,7 +1440,7 @@ FlushOutput(JSContext* aCx, Sequence<JS::Value>& aSequence, nsString &aOutput)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aSequence.AppendElement(JS::StringValue(str))) {
|
||||
if (!aSequence.AppendElement(JS::StringValue(str), fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1571,7 +1571,7 @@ Console::ProcessArguments(JSContext* aCx,
|
||||
v = aData[index++];
|
||||
}
|
||||
|
||||
if (!aSequence.AppendElement(v)) {
|
||||
if (!aSequence.AppendElement(v, fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1594,13 +1594,13 @@ Console::ProcessArguments(JSContext* aCx,
|
||||
int32_t diff = aSequence.Length() - aStyles.Length();
|
||||
if (diff > 0) {
|
||||
for (int32_t i = 0; i < diff; i++) {
|
||||
if (!aStyles.AppendElement(JS::NullValue())) {
|
||||
if (!aStyles.AppendElement(JS::NullValue(), fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!aStyles.AppendElement(JS::StringValue(jsString))) {
|
||||
if (!aStyles.AppendElement(JS::StringValue(jsString), fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1672,7 +1672,7 @@ Console::ProcessArguments(JSContext* aCx,
|
||||
|
||||
// The rest of the array, if unused by the format string.
|
||||
for (; index < aData.Length(); ++index) {
|
||||
if (!aSequence.AppendElement(aData[index])) {
|
||||
if (!aSequence.AppendElement(aData[index], fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1808,7 +1808,7 @@ Console::ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
|
||||
Sequence<JS::Value>& aSequence)
|
||||
{
|
||||
for (uint32_t i = 0; i < aData.Length(); ++i) {
|
||||
if (!aSequence.AppendElement(aData[i])) {
|
||||
if (!aSequence.AppendElement(aData[i], fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -960,7 +960,7 @@ WebSocket::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
Sequence<nsString> protocols;
|
||||
if (!protocols.AppendElement(aProtocol)) {
|
||||
if (!protocols.AppendElement(aProtocol, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -620,8 +620,13 @@ IsSelectionInsideRuby(nsISelection* aSelection)
|
||||
}
|
||||
|
||||
bool
|
||||
nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPresShell* aPresShell, nsISelection* aSelection)
|
||||
nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPresShell* aPresShell,
|
||||
nsISelection* aSelection, bool* aActionTaken)
|
||||
{
|
||||
if (aActionTaken) {
|
||||
*aActionTaken = false;
|
||||
}
|
||||
|
||||
NS_ASSERTION(aType == NS_CUT || aType == NS_COPY || aType == NS_PASTE,
|
||||
"Invalid clipboard event type");
|
||||
|
||||
@ -646,14 +651,6 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
|
||||
// retrieve the event target node from the start of the selection
|
||||
nsresult rv;
|
||||
if (sel) {
|
||||
// Only cut or copy when there is an uncollapsed selection
|
||||
if (aType == NS_CUT || aType == NS_COPY) {
|
||||
bool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
rv = sel->GetRangeAt(0, getter_AddRefs(range));
|
||||
if (NS_SUCCEEDED(rv) && range) {
|
||||
@ -696,7 +693,7 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
|
||||
// If the event was cancelled, don't do the clipboard operation
|
||||
doDefault = (status != nsEventStatus_eConsumeNoDefault);
|
||||
}
|
||||
|
||||
|
||||
// No need to do anything special during a paste. Either an event listener
|
||||
// took care of it and cancelled the event, or the caller will handle it.
|
||||
// Return true to indicate that the event wasn't cancelled.
|
||||
@ -708,6 +705,9 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
|
||||
clipboardData->SetReadOnly();
|
||||
}
|
||||
|
||||
if (aActionTaken) {
|
||||
*aActionTaken = true;
|
||||
}
|
||||
return doDefault;
|
||||
}
|
||||
|
||||
@ -721,20 +721,43 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
|
||||
// use the data added to the data transfer and copy that instead.
|
||||
uint32_t count = 0;
|
||||
if (doDefault) {
|
||||
// get the data from the selection if any
|
||||
bool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed) {
|
||||
return false;
|
||||
// find the focused node
|
||||
nsCOMPtr<nsIContent> srcNode = content;
|
||||
if (content->IsInNativeAnonymousSubtree()) {
|
||||
srcNode = content->FindFirstNonChromeOnlyAccessContent();
|
||||
}
|
||||
// XXX Code which decides whether we should copy text with ruby
|
||||
// annotation is currenct depending on whether each range of the
|
||||
// selection is inside a same ruby container. But we really should
|
||||
// expose the full functionality in browser. See bug 1130891.
|
||||
bool withRubyAnnotation = IsSelectionInsideRuby(sel);
|
||||
// call the copy code
|
||||
rv = HTMLCopy(sel, doc, aClipboardType, withRubyAnnotation);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
||||
// check if we are looking at a password input
|
||||
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(srcNode);
|
||||
if (formControl) {
|
||||
if (formControl->GetType() == NS_FORM_INPUT_PASSWORD) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// when cutting non-editable content, do nothing
|
||||
// XXX this is probably the wrong editable flag to check
|
||||
if (aType != NS_CUT || content->IsEditable()) {
|
||||
// get the data from the selection if any
|
||||
bool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed) {
|
||||
if (aActionTaken) {
|
||||
*aActionTaken = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// XXX Code which decides whether we should copy text with ruby
|
||||
// annotation is currenct depending on whether each range of the
|
||||
// selection is inside a same ruby container. But we really should
|
||||
// expose the full functionality in browser. See bug 1130891.
|
||||
bool withRubyAnnotation = IsSelectionInsideRuby(sel);
|
||||
// call the copy code
|
||||
rv = HTMLCopy(sel, doc, aClipboardType, withRubyAnnotation);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (clipboardData) {
|
||||
@ -763,5 +786,8 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
|
||||
piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"), nullptr, 0);
|
||||
}
|
||||
|
||||
if (aActionTaken) {
|
||||
*aActionTaken = true;
|
||||
}
|
||||
return doDefault;
|
||||
}
|
||||
|
@ -83,12 +83,16 @@ class nsCopySupport
|
||||
*
|
||||
* aClipboardType specifies which clipboard to use, from nsIClipboard.
|
||||
*
|
||||
* If aActionTaken is non-NULL, it will be set to true if an action was
|
||||
* taken, whether it be the default action or the default being prevented.
|
||||
*
|
||||
* If the event is cancelled or an error occurs, false will be returned.
|
||||
*/
|
||||
static bool FireClipboardEvent(int32_t aType,
|
||||
int32_t aClipboardType,
|
||||
nsIPresShell* aPresShell,
|
||||
nsISelection* aSelection);
|
||||
nsISelection* aSelection,
|
||||
bool* aActionTaken = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -714,7 +714,8 @@ nsDOMMutationObserver::GetObservingInfo(
|
||||
mozilla::dom::Sequence<nsString>& filtersAsStrings =
|
||||
info.mAttributeFilter.Value();
|
||||
for (int32_t j = 0; j < filters.Count(); ++j) {
|
||||
if (!filtersAsStrings.AppendElement(nsDependentAtomString(filters[j]))) {
|
||||
if (!filtersAsStrings.AppendElement(nsDependentAtomString(filters[j]),
|
||||
mozilla::fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
@ -773,7 +774,7 @@ nsDOMMutationObserver::HandleMutation()
|
||||
for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
|
||||
nsRefPtr<nsDOMMutationRecord> next;
|
||||
current->mNext.swap(next);
|
||||
*mutations.AppendElement() = current;
|
||||
*mutations.AppendElement(mozilla::fallible) = current;
|
||||
current.swap(next);
|
||||
}
|
||||
}
|
||||
|
@ -2976,19 +2976,10 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
rv = csp->GetReferrerPolicy(&referrerPolicy, &hasReferrerPolicy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (hasReferrerPolicy) {
|
||||
// Referrer policy spec (section 6.1) says that once the referrer policy
|
||||
// is set, any future attempts to change it result in No-Referrer.
|
||||
if (!mReferrerPolicySet) {
|
||||
mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
|
||||
mReferrerPolicySet = true;
|
||||
} else if (mReferrerPolicy != referrerPolicy) {
|
||||
mReferrerPolicy = mozilla::net::RP_No_Referrer;
|
||||
{
|
||||
MOZ_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s",
|
||||
"CSP wants to set referrer, but nsDocument"
|
||||
"already has it set. No referrers will be sent"));
|
||||
}
|
||||
}
|
||||
// Referrer policy spec (section 6.1) says that we always use the newest
|
||||
// referrer policy we find
|
||||
mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
|
||||
mReferrerPolicySet = true;
|
||||
|
||||
// Referrer Policy is set separately for the speculative parser in
|
||||
// nsHTMLDocument::StartDocumentLoad() so there's nothing to do here for
|
||||
@ -3776,14 +3767,10 @@ nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
|
||||
if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
|
||||
ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
|
||||
|
||||
// Referrer policy spec (section 6.1) says that once the referrer policy
|
||||
// is set, any future attempts to change it result in No-Referrer.
|
||||
if (!mReferrerPolicySet) {
|
||||
mReferrerPolicy = policy;
|
||||
mReferrerPolicySet = true;
|
||||
} else if (mReferrerPolicy != policy) {
|
||||
mReferrerPolicy = mozilla::net::RP_No_Referrer;
|
||||
}
|
||||
// Referrer policy spec (section 6.1) says that we always use the newest
|
||||
// referrer policy we find
|
||||
mReferrerPolicy = policy;
|
||||
mReferrerPolicySet = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,7 +500,8 @@ nsClipboardCommand::IsCommandEnabled(const char* aCommandName, nsISupports *aCon
|
||||
nsresult
|
||||
nsClipboardCommand::DoCommand(const char *aCommandName, nsISupports *aContext)
|
||||
{
|
||||
if (strcmp(aCommandName, "cmd_copy") &&
|
||||
if (strcmp(aCommandName, "cmd_cut") &&
|
||||
strcmp(aCommandName, "cmd_copy") &&
|
||||
strcmp(aCommandName, "cmd_copyAndCollapseToEnd"))
|
||||
return NS_OK;
|
||||
|
||||
@ -513,7 +514,13 @@ nsClipboardCommand::DoCommand(const char *aCommandName, nsISupports *aContext)
|
||||
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
||||
|
||||
nsCopySupport::FireClipboardEvent(NS_COPY, nsIClipboard::kGlobalClipboard, presShell, nullptr);
|
||||
int32_t eventType = NS_COPY;
|
||||
if (strcmp(aCommandName, "cmd_cut") == 0) {
|
||||
eventType = NS_CUT;
|
||||
}
|
||||
|
||||
bool actionTaken = false;
|
||||
nsCopySupport::FireClipboardEvent(eventType, nsIClipboard::kGlobalClipboard, presShell, nullptr, &actionTaken);
|
||||
|
||||
if (!strcmp(aCommandName, "cmd_copyAndCollapseToEnd")) {
|
||||
dom::Selection *sel =
|
||||
@ -522,7 +529,10 @@ nsClipboardCommand::DoCommand(const char *aCommandName, nsISupports *aContext)
|
||||
sel->CollapseToEnd();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
if (actionTaken) {
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -368,7 +368,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
for (uint32_t idx = 0; idx < argCount; ++idx) {
|
||||
*args.AppendElement() = argv[idx + 2];
|
||||
*args.AppendElement(fallible) = argv[idx + 2];
|
||||
}
|
||||
args.SwapElements(mArgs);
|
||||
} else {
|
||||
@ -409,7 +409,7 @@ NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow, Function& aFunction,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
FallibleTArray<JS::Heap<JS::Value> > args;
|
||||
if (!args.AppendElements(aArguments)) {
|
||||
if (!args.AppendElements(aArguments, fallible)) {
|
||||
aError.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "nsIDOMHTMLObjectElement.h"
|
||||
#include "nsIDOMHTMLAppletElement.h"
|
||||
#include "nsIExternalProtocolHandler.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIObjectFrame.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsPluginHost.h"
|
||||
@ -2509,6 +2510,13 @@ nsObjectLoadingContent::OpenChannel()
|
||||
scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChan);
|
||||
if (internalChannel) {
|
||||
// Bug 1168676. object/embed tags are not allowed to be intercepted by
|
||||
// ServiceWorkers.
|
||||
internalChannel->ForceNoIntercept();
|
||||
}
|
||||
|
||||
// AsyncOpen can fail if a file does not exist.
|
||||
rv = chan->AsyncOpen(shim, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -228,7 +228,7 @@ nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv)
|
||||
{
|
||||
nsString orientation(aOrientation);
|
||||
Sequence<nsString> orientations;
|
||||
if (!orientations.AppendElement(orientation)) {
|
||||
if (!orientations.AppendElement(orientation, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
|
@ -44,17 +44,23 @@ function create2ndLevelIframeUrl(schemeFrom, schemeTo, policy, type) {
|
||||
// loaded. The click triggers a redirection to file_bug704320_redirect.html,
|
||||
// which in turn notifies the main window that it's time to check the test
|
||||
// results.
|
||||
function createTest(schemeFrom, schemeTo, policy) {
|
||||
function createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy) {
|
||||
var _createTestUrl = createTestUrl.bind(
|
||||
null, schemeFrom, schemeTo, policy, 'test');
|
||||
|
||||
var _create2ndLevelIframeUrl = create2ndLevelIframeUrl.bind(
|
||||
null, schemeFrom, schemeTo, policy);
|
||||
|
||||
var metaReferrerPolicyString = '';
|
||||
if (optionalEarlierPolicy && optionalEarlierPolicy != '') {
|
||||
metaReferrerPolicyString += '<meta name="referrer" content="' + optionalEarlierPolicy + '">\n';
|
||||
}
|
||||
metaReferrerPolicyString += '<meta name="referrer" content="' + policy + '">';
|
||||
|
||||
return '<!DOCTYPE HTML>\n\
|
||||
<html>\n\
|
||||
<head>\n\
|
||||
<meta name="referrer" content="' + policy + '">\n\
|
||||
'+metaReferrerPolicyString+'\n\
|
||||
<link rel="stylesheet" type="text/css" href="' + _createTestUrl('stylesheet') + '">\n\
|
||||
<style type="text/css">\n\
|
||||
@import "' + _createTestUrl('import-css') + '";\n\
|
||||
@ -165,11 +171,17 @@ function createIframedWindowLocationTest(schemeFrom, schemeTo, policy) {
|
||||
</html>';
|
||||
}
|
||||
|
||||
function createPolicyTest(refpol) {
|
||||
function createPolicyTest(policy, optionalEarlierPolicy) {
|
||||
var metaReferrerPolicyString = '';
|
||||
if (optionalEarlierPolicy && optionalEarlierPolicy != '') {
|
||||
metaReferrerPolicyString += '<meta name="referrer" content="' + optionalEarlierPolicy + '">\n';
|
||||
}
|
||||
metaReferrerPolicyString += '<meta name="referrer" content="' + policy + '">';
|
||||
|
||||
return '<!DOCTYPE HTML>\n\
|
||||
<html>\n\
|
||||
<head>\n\
|
||||
<meta name="referrer" content="' + refpol + '">\n\
|
||||
'+metaReferrerPolicyString+'\n\
|
||||
<script type="text/javascript" src="/tests/dom/base/test/file_bug704320_preload_common.js"></script>\n\
|
||||
</head>\n\
|
||||
<body>\n\
|
||||
@ -191,10 +203,14 @@ function handleRequest(request, response) {
|
||||
var schemeFrom = params[1].split('=')[1];
|
||||
var schemeTo = params[2].split('=')[1];
|
||||
var policy = params[3].split('=')[1];
|
||||
var optionalEarlierPolicy = '';
|
||||
if (params[4]) {
|
||||
optionalEarlierPolicy = params[4].split('=')[1];
|
||||
}
|
||||
|
||||
response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
|
||||
response.setHeader('Cache-Control', 'no-cache', false);
|
||||
response.write(createTest(schemeFrom, schemeTo, policy));
|
||||
response.write(createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy));
|
||||
}
|
||||
else if (action === 'create-2nd-level-iframe') {
|
||||
// ?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=origin&type=form"
|
||||
@ -266,7 +282,12 @@ function handleRequest(request, response) {
|
||||
// ?action=generate-policy-test&policy=b64-encoded-string
|
||||
response.setHeader('Cache-Control', 'no-cache', false);
|
||||
response.setHeader('Content-Type', 'text/html', false);
|
||||
var refpol = unescape(params[1].split('=')[1]);
|
||||
response.write(createPolicyTest(refpol));
|
||||
var policy = unescape(params[1].split('=')[1]);
|
||||
var optionalEarlierPolicy = '';
|
||||
if (params[2]) {
|
||||
optionalEarlierPolicy = params[2].split('=')[1];
|
||||
}
|
||||
|
||||
response.write(createPolicyTest(policy, optionalEarlierPolicy));
|
||||
}
|
||||
}
|
||||
|
@ -678,6 +678,8 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
|
||||
[test_bug1163743.html]
|
||||
support-files = referrerHelper.js
|
||||
[test_bug1165501.html]
|
||||
support-files = referrerHelper.js
|
||||
[test_caretPositionFromPoint.html]
|
||||
[test_classList.html]
|
||||
# This test fails on the Mac for some reason
|
||||
|
51
dom/base/test/test_bug1165501.html
Normal file
51
dom/base/test/test_bug1165501.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Spot test to see if newer meta-referrer policy is used
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1165501
|
||||
-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test policies for Bug 1165501</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="referrerHelper.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var advance = function() { tests.next(); };
|
||||
|
||||
/**
|
||||
* testing if policy is overwritten if there are two meta statements (1165501)
|
||||
* XXX: would be nice to test this with CSP and meta as well
|
||||
*/
|
||||
var tests = (function() {
|
||||
var iframe = document.getElementById("testframe");
|
||||
const sjs = "/tests/dom/base/test/bug704320.sjs?action=generate-policy-test";
|
||||
|
||||
// setting first unsafe-url and then origin - origin shall prevail
|
||||
yield resetCounter();
|
||||
yield iframe.src = sjs + "&policy=" + escape('origin')+ "&wrongPolicy=" + escape('unsafe-url');
|
||||
yield checkIndividualResults("unsafe-url then origin", ["origin"]);
|
||||
|
||||
// setting first no-referrer and then default - default shall prevail
|
||||
yield resetCounter();
|
||||
yield iframe.src = sjs + "&policy=" + escape('default')+ "&wrongPolicy=" + escape('no-referrer');
|
||||
yield checkIndividualResults("no-referrer then default", ["full"]);
|
||||
|
||||
|
||||
// complete. Be sure to yield so we don't call this twice.
|
||||
yield SimpleTest.finish();
|
||||
})();
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="tests.next();">
|
||||
<iframe id="testframe"></iframe>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -25,8 +25,9 @@
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
performance.clearResourceTimings();
|
||||
arr = performance.getEntries();
|
||||
is(arr.length, 0, "clearing performance entries");
|
||||
is(performance.getEntriesByType("resource").length, 0, "clearing performance resource entries");
|
||||
is(performance.getEntriesByType("mark").length, 0, "clearing performance mark entries");
|
||||
is(performance.getEntriesByType("measure").length, 0, "clearing performance measure entries");
|
||||
steps[i]();
|
||||
} catch(ex) {
|
||||
ok(false, "Caught exception", ex);
|
||||
|
@ -266,6 +266,14 @@ DOMInterfaces = {
|
||||
'concrete': False
|
||||
},
|
||||
|
||||
'ChromeUtils': {
|
||||
# The codegen is dumb, and doesn't understand that this interface is only a
|
||||
# collection of static methods, so we have this `concrete: False` hack.
|
||||
'concrete': False,
|
||||
'nativeType': 'mozilla::devtools::ChromeUtils',
|
||||
'implicitJSContext': ['readHeapSnapshot', 'saveHeapSnapshot']
|
||||
},
|
||||
|
||||
'ChromeWindow': {
|
||||
'concrete': False,
|
||||
},
|
||||
@ -501,6 +509,10 @@ DOMInterfaces = {
|
||||
'headerFile': 'nsGeolocation.h'
|
||||
},
|
||||
|
||||
'HeapSnapshot': {
|
||||
'nativeType': 'mozilla::devtools::HeapSnapshot'
|
||||
},
|
||||
|
||||
'History': {
|
||||
'headerFile': 'nsHistory.h',
|
||||
'nativeType': 'nsHistory'
|
||||
|
@ -4315,7 +4315,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
if (done${nestingLevel}) {
|
||||
break;
|
||||
}
|
||||
${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement();
|
||||
${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(mozilla::fallible);
|
||||
if (!slotPtr${nestingLevel}) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
$*{exceptionCode}
|
||||
@ -5610,7 +5610,7 @@ class CGArgumentConverter(CGThing):
|
||||
return false;
|
||||
}
|
||||
for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
|
||||
${elemType}& slot = *${declName}.AppendElement();
|
||||
${elemType}& slot = *${declName}.AppendElement(mozilla::fallible);
|
||||
""")
|
||||
).substitute(replacer)
|
||||
|
||||
|
277
dom/cache/Connection.cpp
vendored
Normal file
277
dom/cache/Connection.cpp
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/cache/Connection.h"
|
||||
|
||||
#include "mozilla/dom/cache/DBSchema.h"
|
||||
#include "mozStorageHelper.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
NS_IMPL_ISUPPORTS(cache::Connection, mozIStorageAsyncConnection,
|
||||
mozIStorageConnection);
|
||||
|
||||
Connection::Connection(mozIStorageConnection* aBase)
|
||||
: mBase(aBase)
|
||||
, mClosed(false)
|
||||
{
|
||||
MOZ_ASSERT(mBase);
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Connection);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Close()));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::Close()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Connection);
|
||||
|
||||
if (mClosed) {
|
||||
return NS_OK;
|
||||
}
|
||||
mClosed = true;
|
||||
|
||||
// If we are closing here, then Cache must not have a transaction
|
||||
// open anywhere else. This should be guaranteed to succeed.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(db::IncrementalVacuum(this)));
|
||||
|
||||
return mBase->Close();
|
||||
}
|
||||
|
||||
// The following methods are all boilerplate that either forward to the
|
||||
// base connection or block the method. All the async execution methods
|
||||
// are blocked because Cache does not use them and they would require more
|
||||
// work to wrap properly.
|
||||
|
||||
// mozIStorageAsyncConnection methods
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::AsyncClose(mozIStorageCompletionCallback*)
|
||||
{
|
||||
// async methods are not supported
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::AsyncClone(bool, mozIStorageCompletionCallback*)
|
||||
{
|
||||
// async methods are not supported
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetDatabaseFile(nsIFile** aFileOut)
|
||||
{
|
||||
return mBase->GetDatabaseFile(aFileOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CreateAsyncStatement(const nsACString&, mozIStorageAsyncStatement**)
|
||||
{
|
||||
// async methods are not supported
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::ExecuteAsync(mozIStorageBaseStatement**, uint32_t,
|
||||
mozIStorageStatementCallback*,
|
||||
mozIStoragePendingStatement**)
|
||||
{
|
||||
// async methods are not supported
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::ExecuteSimpleSQLAsync(const nsACString&,
|
||||
mozIStorageStatementCallback*,
|
||||
mozIStoragePendingStatement**)
|
||||
{
|
||||
// async methods are not supported
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CreateFunction(const nsACString& aFunctionName,
|
||||
int32_t aNumArguments,
|
||||
mozIStorageFunction* aFunction)
|
||||
{
|
||||
// async methods are not supported
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CreateAggregateFunction(const nsACString& aFunctionName,
|
||||
int32_t aNumArguments,
|
||||
mozIStorageAggregateFunction* aFunction)
|
||||
{
|
||||
return mBase->CreateAggregateFunction(aFunctionName, aNumArguments,
|
||||
aFunction);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::RemoveFunction(const nsACString& aFunctionName)
|
||||
{
|
||||
return mBase->RemoveFunction(aFunctionName);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::SetProgressHandler(int32_t aGranularity,
|
||||
mozIStorageProgressHandler* aHandler,
|
||||
mozIStorageProgressHandler** aHandlerOut)
|
||||
{
|
||||
return mBase->SetProgressHandler(aGranularity, aHandler, aHandlerOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::RemoveProgressHandler(mozIStorageProgressHandler** aHandlerOut)
|
||||
{
|
||||
return mBase->RemoveProgressHandler(aHandlerOut);
|
||||
}
|
||||
|
||||
// mozIStorageConnection methods
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::Clone(bool aReadOnly, mozIStorageConnection** aConnectionOut)
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> conn;
|
||||
nsresult rv = mBase->Clone(aReadOnly, getter_AddRefs(conn));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> wrapped = new Connection(conn);
|
||||
wrapped.forget(aConnectionOut);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetDefaultPageSize(int32_t* aSizeOut)
|
||||
{
|
||||
return mBase->GetDefaultPageSize(aSizeOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetConnectionReady(bool* aReadyOut)
|
||||
{
|
||||
return mBase->GetConnectionReady(aReadyOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetLastInsertRowID(int64_t* aRowIdOut)
|
||||
{
|
||||
return mBase->GetLastInsertRowID(aRowIdOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetAffectedRows(int32_t* aCountOut)
|
||||
{
|
||||
return mBase->GetAffectedRows(aCountOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetLastError(int32_t* aErrorOut)
|
||||
{
|
||||
return mBase->GetLastError(aErrorOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetLastErrorString(nsACString& aErrorOut)
|
||||
{
|
||||
return mBase->GetLastErrorString(aErrorOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetSchemaVersion(int32_t* aVersionOut)
|
||||
{
|
||||
return mBase->GetSchemaVersion(aVersionOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::SetSchemaVersion(int32_t aVersion)
|
||||
{
|
||||
return mBase->SetSchemaVersion(aVersion);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CreateStatement(const nsACString& aQuery,
|
||||
mozIStorageStatement** aStatementOut)
|
||||
{
|
||||
return mBase->CreateStatement(aQuery, aStatementOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::ExecuteSimpleSQL(const nsACString& aQuery)
|
||||
{
|
||||
return mBase->ExecuteSimpleSQL(aQuery);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::TableExists(const nsACString& aTableName, bool* aExistsOut)
|
||||
{
|
||||
return mBase->TableExists(aTableName, aExistsOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::IndexExists(const nsACString& aIndexName, bool* aExistsOut)
|
||||
{
|
||||
return mBase->IndexExists(aIndexName, aExistsOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::GetTransactionInProgress(bool* aResultOut)
|
||||
{
|
||||
return mBase->GetTransactionInProgress(aResultOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::BeginTransaction()
|
||||
{
|
||||
return mBase->BeginTransaction();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::BeginTransactionAs(int32_t aType)
|
||||
{
|
||||
return mBase->BeginTransactionAs(aType);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CommitTransaction()
|
||||
{
|
||||
return mBase->CommitTransaction();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::RollbackTransaction()
|
||||
{
|
||||
return mBase->RollbackTransaction();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::CreateTable(const char* aTable, const char* aSchema)
|
||||
{
|
||||
return mBase->CreateTable(aTable, aSchema);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::SetGrowthIncrement(int32_t aIncrement, const nsACString& aDatabase)
|
||||
{
|
||||
return mBase->SetGrowthIncrement(aIncrement, aDatabase);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Connection::EnableModule(const nsACString& aModule)
|
||||
{
|
||||
return mBase->EnableModule(aModule);
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
36
dom/cache/Connection.h
vendored
Normal file
36
dom/cache/Connection.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_cache_Connection_h
|
||||
#define mozilla_dom_cache_Connection_h
|
||||
|
||||
#include "mozIStorageConnection.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
class Connection final : public mozIStorageConnection
|
||||
{
|
||||
public:
|
||||
explicit Connection(mozIStorageConnection* aBase);
|
||||
|
||||
private:
|
||||
~Connection();
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> mBase;
|
||||
bool mClosed;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEASYNCCONNECTION
|
||||
NS_DECL_MOZISTORAGECONNECTION
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_cache_Connection_h
|
84
dom/cache/Context.cpp
vendored
84
dom/cache/Context.cpp
vendored
@ -131,11 +131,15 @@ class Context::QuotaInitRunnable final : public nsIRunnable
|
||||
public:
|
||||
QuotaInitRunnable(Context* aContext,
|
||||
Manager* aManager,
|
||||
Action* aQuotaIOThreadAction)
|
||||
Data* aData,
|
||||
nsIThread* aTarget,
|
||||
Action* aInitAction)
|
||||
: mContext(aContext)
|
||||
, mThreadsafeHandle(aContext->CreateThreadsafeHandle())
|
||||
, mManager(aManager)
|
||||
, mQuotaIOThreadAction(aQuotaIOThreadAction)
|
||||
, mData(aData)
|
||||
, mTarget(aTarget)
|
||||
, mInitAction(aInitAction)
|
||||
, mInitiatingThread(NS_GetCurrentThread())
|
||||
, mResult(NS_OK)
|
||||
, mState(STATE_INIT)
|
||||
@ -144,6 +148,8 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mManager);
|
||||
MOZ_ASSERT(mData);
|
||||
MOZ_ASSERT(mTarget);
|
||||
MOZ_ASSERT(mInitiatingThread);
|
||||
}
|
||||
|
||||
@ -166,7 +172,7 @@ public:
|
||||
NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
mCanceled = true;
|
||||
mQuotaIOThreadAction->CancelOnInitiatingThread();
|
||||
mInitAction->CancelOnInitiatingThread();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -202,7 +208,7 @@ private:
|
||||
{
|
||||
MOZ_ASSERT(mState == STATE_COMPLETE);
|
||||
MOZ_ASSERT(!mContext);
|
||||
MOZ_ASSERT(!mQuotaIOThreadAction);
|
||||
MOZ_ASSERT(!mInitAction);
|
||||
}
|
||||
|
||||
enum State
|
||||
@ -211,6 +217,7 @@ private:
|
||||
STATE_CALL_WAIT_FOR_OPEN_ALLOWED,
|
||||
STATE_WAIT_FOR_OPEN_ALLOWED,
|
||||
STATE_ENSURE_ORIGIN_INITIALIZED,
|
||||
STATE_RUN_ON_TARGET,
|
||||
STATE_RUNNING,
|
||||
STATE_COMPLETING,
|
||||
STATE_COMPLETE
|
||||
@ -222,13 +229,15 @@ private:
|
||||
MOZ_ASSERT(mContext);
|
||||
mContext = nullptr;
|
||||
mManager = nullptr;
|
||||
mQuotaIOThreadAction = nullptr;
|
||||
mInitAction = nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Context> mContext;
|
||||
nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;
|
||||
nsRefPtr<Manager> mManager;
|
||||
nsRefPtr<Action> mQuotaIOThreadAction;
|
||||
nsRefPtr<Data> mData;
|
||||
nsCOMPtr<nsIThread> mTarget;
|
||||
nsRefPtr<Action> mInitAction;
|
||||
nsCOMPtr<nsIThread> mInitiatingThread;
|
||||
nsresult mResult;
|
||||
QuotaInfo mQuotaInfo;
|
||||
@ -266,9 +275,14 @@ NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
|
||||
// | (Quota IO Thread) +----------------+
|
||||
// +----------+------------+ |
|
||||
// | |
|
||||
// +----------v------------+ |
|
||||
// | RunOnTarget | Resolve(error) |
|
||||
// | (Target Thread) +----------------+
|
||||
// +----------+------------+ |
|
||||
// | |
|
||||
// +---------v---------+ +------v------+
|
||||
// | Running | | Completing |
|
||||
// | (Quota IO Thread) +------------>(Orig Thread)|
|
||||
// | (Target Thread) +------------>(Orig Thread)|
|
||||
// +-------------------+ +------+------+
|
||||
// |
|
||||
// +-----v----+
|
||||
@ -384,16 +398,27 @@ Context::QuotaInitRunnable::Run()
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_RUNNING;
|
||||
|
||||
if (!mQuotaIOThreadAction) {
|
||||
if (!mInitAction) {
|
||||
resolver->Resolve(NS_OK);
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_RUN_ON_TARGET;
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL)));
|
||||
break;
|
||||
}
|
||||
// -------------------
|
||||
case STATE_RUN_ON_TARGET:
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
|
||||
|
||||
mState = STATE_RUNNING;
|
||||
|
||||
// Execute the provided initialization Action. The Action must Resolve()
|
||||
// before returning.
|
||||
mQuotaIOThreadAction->RunOnTarget(resolver, mQuotaInfo, nullptr);
|
||||
mInitAction->RunOnTarget(resolver, mQuotaInfo, mData);
|
||||
MOZ_ASSERT(resolver->Resolved());
|
||||
|
||||
break;
|
||||
@ -402,8 +427,8 @@ Context::QuotaInitRunnable::Run()
|
||||
case STATE_COMPLETING:
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
|
||||
if (mQuotaIOThreadAction) {
|
||||
mQuotaIOThreadAction->CompleteOnInitiatingThread(mResult);
|
||||
if (mInitAction) {
|
||||
mInitAction->CompleteOnInitiatingThread(mResult);
|
||||
}
|
||||
mContext->OnQuotaInit(mResult, mQuotaInfo, mOfflineStorage);
|
||||
mState = STATE_COMPLETE;
|
||||
@ -776,21 +801,10 @@ Context::ThreadsafeHandle::ContextDestroyed(Context* aContext)
|
||||
// static
|
||||
already_AddRefed<Context>
|
||||
Context::Create(Manager* aManager, nsIThread* aTarget,
|
||||
Action* aQuotaIOThreadAction, Context* aOldContext)
|
||||
Action* aInitAction, Context* aOldContext)
|
||||
{
|
||||
nsRefPtr<Context> context = new Context(aManager, aTarget);
|
||||
|
||||
// Do this here to avoid doing an AddRef() in the constructor
|
||||
// TODO: pass context->mData to allow connetion sharing with init
|
||||
context->mInitRunnable = new QuotaInitRunnable(context, aManager,
|
||||
aQuotaIOThreadAction);
|
||||
|
||||
if (aOldContext) {
|
||||
aOldContext->SetNextContext(context);
|
||||
} else {
|
||||
context->Start();
|
||||
}
|
||||
|
||||
context->Init(aInitAction, aOldContext);
|
||||
return context.forget();
|
||||
}
|
||||
|
||||
@ -913,6 +927,24 @@ Context::~Context()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::Init(Action* aInitAction, Context* aOldContext)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(!mInitRunnable);
|
||||
|
||||
// Do this here to avoid doing an AddRef() in the constructor
|
||||
mInitRunnable = new QuotaInitRunnable(this, mManager, mData, mTarget,
|
||||
aInitAction);
|
||||
|
||||
if (aOldContext) {
|
||||
aOldContext->SetNextContext(this);
|
||||
return;
|
||||
}
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
void
|
||||
Context::Start()
|
||||
{
|
||||
|
3
dom/cache/Context.h
vendored
3
dom/cache/Context.h
vendored
@ -112,7 +112,7 @@ public:
|
||||
// be execute synchronously.
|
||||
static already_AddRefed<Context>
|
||||
Create(Manager* aManager, nsIThread* aTarget,
|
||||
Action* aQuotaIOThreadAction, Context* aOldContext);
|
||||
Action* aInitAction, Context* aOldContext);
|
||||
|
||||
// Execute given action on the target once the quota manager has been
|
||||
// initialized.
|
||||
@ -173,6 +173,7 @@ private:
|
||||
|
||||
Context(Manager* aManager, nsIThread* aTarget);
|
||||
~Context();
|
||||
void Init(Action* aInitAction, Context* aOldContext);
|
||||
void Start();
|
||||
void DispatchAction(Action* aAction, bool aDoomData = false);
|
||||
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
|
||||
|
12
dom/cache/DBAction.cpp
vendored
12
dom/cache/DBAction.cpp
vendored
@ -6,6 +6,9 @@
|
||||
|
||||
#include "mozilla/dom/cache/DBAction.h"
|
||||
|
||||
#include "mozilla/dom/cache/Connection.h"
|
||||
#include "mozilla/dom/cache/DBSchema.h"
|
||||
#include "mozilla/dom/cache/FileUtils.h"
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "mozilla/net/nsFileProtocolHandler.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
@ -15,8 +18,6 @@
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "DBSchema.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -79,7 +80,12 @@ DBAction::RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
|
||||
// Save this connection in the shared Data object so later Actions can
|
||||
// use it. This avoids opening a new connection for every Action.
|
||||
if (aOptionalData) {
|
||||
aOptionalData->SetConnection(conn);
|
||||
// Since we know this connection will be around for as long as the
|
||||
// Cache is open, use our special wrapped connection class. This
|
||||
// will let us perform certain operations once the Cache origin
|
||||
// is closed.
|
||||
nsCOMPtr<mozIStorageConnection> wrapped = new Connection(conn);
|
||||
aOptionalData->SetConnection(wrapped);
|
||||
}
|
||||
}
|
||||
|
||||
|
176
dom/cache/DBSchema.cpp
vendored
176
dom/cache/DBSchema.cpp
vendored
@ -29,13 +29,30 @@ namespace dom {
|
||||
namespace cache {
|
||||
namespace db {
|
||||
|
||||
const int32_t kMaxWipeSchemaVersion = 9;
|
||||
const int32_t kMaxWipeSchemaVersion = 10;
|
||||
|
||||
namespace {
|
||||
|
||||
const int32_t kLatestSchemaVersion = 9;
|
||||
const int32_t kLatestSchemaVersion = 10;
|
||||
const int32_t kMaxEntriesPerStatement = 255;
|
||||
|
||||
const uint32_t kPageSize = 4 * 1024;
|
||||
|
||||
// Grow the database in chunks to reduce fragmentation
|
||||
const uint32_t kGrowthSize = 32 * 1024;
|
||||
const uint32_t kGrowthPages = kGrowthSize / kPageSize;
|
||||
static_assert(kGrowthSize % kPageSize == 0,
|
||||
"Growth size must be multiple of page size");
|
||||
|
||||
// Only release free pages when we have more than this limit
|
||||
const int32_t kMaxFreePages = kGrowthPages;
|
||||
|
||||
// Limit WAL journal to a reasonable size
|
||||
const uint32_t kWalAutoCheckpointSize = 512 * 1024;
|
||||
const uint32_t kWalAutoCheckpointPages = kWalAutoCheckpointSize / kPageSize;
|
||||
static_assert(kWalAutoCheckpointSize % kPageSize == 0,
|
||||
"WAL checkpoint size must be multiple of page size");
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// If any of the static_asserts below fail, it means that you have changed
|
||||
@ -213,28 +230,12 @@ CreateSchema(mozIStorageConnection* aConn)
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aConn);
|
||||
|
||||
nsAutoCString pragmas(
|
||||
// Enable auto-vaccum but in incremental mode in order to avoid doing a lot
|
||||
// of work at the end of each transaction.
|
||||
// NOTE: This must be done here instead of InitializeConnection() because it
|
||||
// only works when the database is empty.
|
||||
"PRAGMA auto_vacuum = INCREMENTAL; "
|
||||
);
|
||||
|
||||
nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
int32_t schemaVersion;
|
||||
rv = aConn->GetSchemaVersion(&schemaVersion);
|
||||
nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
if (schemaVersion == kLatestSchemaVersion) {
|
||||
// We already have the correct schema, so just do an incremental vaccum and
|
||||
// get started.
|
||||
rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"PRAGMA incremental_vacuum;"));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// We already have the correct schema, so just get started.
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -380,24 +381,69 @@ InitializeConnection(mozIStorageConnection* aConn)
|
||||
// This function needs to perform per-connection initialization tasks that
|
||||
// need to happen regardless of the schema.
|
||||
|
||||
nsAutoCString pragmas(
|
||||
"PRAGMA journal_mode = WAL; "
|
||||
// Use default mozStorage 32kb page size for now
|
||||
// WAL journal can grow to 512kb before being flushed to disk
|
||||
"PRAGMA wal_autocheckpoint = 16; "
|
||||
// Always truncate the journal back to 512kb after large transactions
|
||||
"PRAGMA journal_size_limit = 524288; "
|
||||
"PRAGMA foreign_keys = ON; "
|
||||
|
||||
// Note, the default encoding of UTF-8 is preferred. mozStorage does all
|
||||
// the work necessary to convert UTF-16 nsString values for us. We don't
|
||||
// need ordering and the binary equality operations are correct. So, do
|
||||
// NOT set PRAGMA encoding to UTF-16.
|
||||
nsPrintfCString pragmas(
|
||||
// Use a smaller page size to improve perf/footprint; default is too large
|
||||
"PRAGMA page_size = %u; "
|
||||
// Enable auto_vacuum; this must happen after page_size and before WAL
|
||||
"PRAGMA auto_vacuum = INCREMENTAL; "
|
||||
"PRAGMA foreign_keys = ON; ",
|
||||
kPageSize
|
||||
);
|
||||
|
||||
// Note, the default encoding of UTF-8 is preferred. mozStorage does all
|
||||
// the work necessary to convert UTF-16 nsString values for us. We don't
|
||||
// need ordering and the binary equality operations are correct. So, do
|
||||
// NOT set PRAGMA encoding to UTF-16.
|
||||
|
||||
nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// Limit fragmentation by growing the database by many pages at once.
|
||||
rv = aConn->SetGrowthIncrement(kGrowthSize, EmptyCString());
|
||||
if (rv == NS_ERROR_FILE_TOO_BIG) {
|
||||
NS_WARNING("Not enough disk space to set sqlite growth increment.");
|
||||
rv = NS_OK;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// Enable WAL journaling. This must be performed in a separate transaction
|
||||
// after changing the page_size and enabling auto_vacuum.
|
||||
nsPrintfCString wal(
|
||||
// WAL journal can grow to given number of *pages*
|
||||
"PRAGMA wal_autocheckpoint = %u; "
|
||||
// Always truncate the journal back to given number of *bytes*
|
||||
"PRAGMA journal_size_limit = %u; "
|
||||
// WAL must be enabled at the end to allow page size to be changed, etc.
|
||||
"PRAGMA journal_mode = WAL; ",
|
||||
kWalAutoCheckpointPages,
|
||||
kWalAutoCheckpointSize
|
||||
);
|
||||
|
||||
rv = aConn->ExecuteSimpleSQL(wal);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// Verify that we successfully set the vacuum mode to incremental. It
|
||||
// is very easy to put the database in a state where the auto_vacuum
|
||||
// pragma above fails silently.
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<mozIStorageStatement> state;
|
||||
rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"PRAGMA auto_vacuum;"
|
||||
), getter_AddRefs(state));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
bool hasMoreData = false;
|
||||
rv = state->ExecuteStep(&hasMoreData);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
int32_t mode;
|
||||
rv = state->GetInt32(0, &mode);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// integer value 2 is incremental mode
|
||||
if (NS_WARN_IF(mode != 2)) { return NS_ERROR_UNEXPECTED; }
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1916,6 +1962,70 @@ CreateAndBindKeyStatement(mozIStorageConnection* aConn,
|
||||
|
||||
} // anonymouns namespace
|
||||
|
||||
nsresult
|
||||
IncrementalVacuum(mozIStorageConnection* aConn)
|
||||
{
|
||||
// Determine how much free space is in the database.
|
||||
nsCOMPtr<mozIStorageStatement> state;
|
||||
nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"PRAGMA freelist_count;"
|
||||
), getter_AddRefs(state));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
bool hasMoreData = false;
|
||||
rv = state->ExecuteStep(&hasMoreData);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
int32_t freePages = 0;
|
||||
rv = state->GetInt32(0, &freePages);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// We have a relatively small page size, so we want to be careful to avoid
|
||||
// fragmentation. We already use a growth incremental which will cause
|
||||
// sqlite to allocate and release multiple pages at the same time. We can
|
||||
// further reduce fragmentation by making our allocated chunks a bit
|
||||
// "sticky". This is done by creating some hysteresis where we allocate
|
||||
// pages/chunks as soon as we need them, but we only release pages/chunks
|
||||
// when we have a large amount of free space. This helps with the case
|
||||
// where a page is adding and remove resources causing it to dip back and
|
||||
// forth across a chunk boundary.
|
||||
//
|
||||
// So only proceed with releasing pages if we have more than our constant
|
||||
// threshold.
|
||||
if (freePages <= kMaxFreePages) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Release the excess pages back to the sqlite VFS. This may also release
|
||||
// chunks of multiple pages back to the OS.
|
||||
int32_t pagesToRelease = freePages - kMaxFreePages;
|
||||
|
||||
rv = aConn->ExecuteSimpleSQL(nsPrintfCString(
|
||||
"PRAGMA incremental_vacuum(%d);", pagesToRelease
|
||||
));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
// Verify that our incremental vacuum actually did something
|
||||
#ifdef DEBUG
|
||||
rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"PRAGMA freelist_count;"
|
||||
), getter_AddRefs(state));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
hasMoreData = false;
|
||||
rv = state->ExecuteStep(&hasMoreData);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
freePages = 0;
|
||||
rv = state->GetInt32(0, &freePages);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
MOZ_ASSERT(freePages <= kMaxFreePages);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace db
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
|
5
dom/cache/DBSchema.h
vendored
5
dom/cache/DBSchema.h
vendored
@ -32,6 +32,7 @@ namespace db {
|
||||
nsresult
|
||||
CreateSchema(mozIStorageConnection* aConn);
|
||||
|
||||
// Note, this cannot be executed within a transaction.
|
||||
nsresult
|
||||
InitializeConnection(mozIStorageConnection* aConn);
|
||||
|
||||
@ -104,6 +105,10 @@ nsresult
|
||||
StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
|
||||
nsTArray<nsString>& aKeysOut);
|
||||
|
||||
// Note, this works best when its NOT executed within a transaction.
|
||||
nsresult
|
||||
IncrementalVacuum(mozIStorageConnection* aConn);
|
||||
|
||||
// We will wipe out databases with a schema versions less than this.
|
||||
extern const int32_t kMaxWipeSchemaVersion;
|
||||
|
||||
|
2
dom/cache/moz.build
vendored
2
dom/cache/moz.build
vendored
@ -21,6 +21,7 @@ EXPORTS.mozilla.dom.cache += [
|
||||
'CacheStorageParent.h',
|
||||
'CacheStreamControlChild.h',
|
||||
'CacheStreamControlParent.h',
|
||||
'Connection.h',
|
||||
'Context.h',
|
||||
'DBAction.h',
|
||||
'DBSchema.h',
|
||||
@ -56,6 +57,7 @@ UNIFIED_SOURCES += [
|
||||
'CacheStorageParent.cpp',
|
||||
'CacheStreamControlChild.cpp',
|
||||
'CacheStreamControlParent.cpp',
|
||||
'Connection.cpp',
|
||||
'Context.cpp',
|
||||
'DBAction.cpp',
|
||||
'DBSchema.cpp',
|
||||
|
1002
dom/cache/test/mochitest/large_url_list.js
vendored
Normal file
1002
dom/cache/test/mochitest/large_url_list.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
dom/cache/test/mochitest/mochitest.ini
vendored
2
dom/cache/test/mochitest/mochitest.ini
vendored
@ -21,6 +21,7 @@ support-files =
|
||||
test_cache_delete.js
|
||||
test_cache_put_reorder.js
|
||||
test_cache_https.js
|
||||
large_url_list.js
|
||||
|
||||
[test_cache.html]
|
||||
[test_cache_add.html]
|
||||
@ -37,3 +38,4 @@ support-files =
|
||||
[test_cache_https.html]
|
||||
skip-if = buildapp == 'b2g' # bug 1162353
|
||||
[test_cache_restart.html]
|
||||
[test_cache_shrink.html]
|
||||
|
131
dom/cache/test/mochitest/test_cache_shrink.html
vendored
Normal file
131
dom/cache/test/mochitest/test_cache_shrink.html
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Cache with QuotaManager Restart</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="large_url_list.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
function clearStorage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
var appId, inBrowser;
|
||||
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
|
||||
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
|
||||
principal.appId != nsIPrincipal.NO_APP_ID) {
|
||||
appId = principal.appId;
|
||||
inBrowser = principal.isInBrowserElement;
|
||||
}
|
||||
SpecialPowers.clearStorageForURI(document.documentURI, resolve, appId,
|
||||
inBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
function storageUsage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
var appId, inBrowser;
|
||||
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
|
||||
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
|
||||
principal.appId != nsIPrincipal.NO_APP_ID) {
|
||||
appId = principal.appId;
|
||||
inBrowser = principal.isInBrowserElement;
|
||||
}
|
||||
SpecialPowers.getStorageUsageForURI(document.documentURI, resolve, appId,
|
||||
inBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
function resetStorage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
var appId, inBrowser;
|
||||
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
|
||||
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
|
||||
principal.appId != nsIPrincipal.NO_APP_ID) {
|
||||
appId = principal.appId;
|
||||
inBrowser = principal.isInBrowserElement;
|
||||
}
|
||||
SpecialPowers.resetStorageForURI(document.documentURI, resolve, appId,
|
||||
inBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
function gc() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
SpecialPowers.exactGC(window, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [["dom.caches.enabled", true],
|
||||
["dom.quotaManager.testing", true]],
|
||||
}, function() {
|
||||
var name = 'foo';
|
||||
var cache = null;
|
||||
var initialUsage = 0;
|
||||
var fullUsage = 0;
|
||||
var endUsage = 0;
|
||||
// start from a fresh origin directory so other tests do not influence our
|
||||
// results
|
||||
clearStorage().then(function() {
|
||||
return storageUsage();
|
||||
}).then(function(usage) {
|
||||
is(0, usage, 'disk usage should be zero to start');
|
||||
return caches.open(name);
|
||||
}).then(function(c) {
|
||||
cache = c;
|
||||
return storageUsage();
|
||||
}).then(function(usage) {
|
||||
initialUsage = usage;
|
||||
return Promise.all(largeUrlList.map(function(url) {
|
||||
return cache.put(new Request(url), new Response());
|
||||
}));
|
||||
}).then(function() {
|
||||
return cache.keys();
|
||||
}).then(function(keyList) {
|
||||
is(keyList.length, largeUrlList.length, 'Large URL list is stored in cache');
|
||||
cache = null;
|
||||
// Ensure the Cache DOM object is gone before proceeding. If its alive
|
||||
// it will keep the related entries on-disk as well.
|
||||
return gc();
|
||||
}).then(function() {
|
||||
// reset the quota manager storage to ensure the DB connection is flushed
|
||||
return resetStorage();
|
||||
}).then(function() {
|
||||
return storageUsage();
|
||||
}).then(function(usage) {
|
||||
fullUsage = usage;
|
||||
ok(fullUsage > initialUsage, 'disk usage should have grown');
|
||||
return caches.delete(name);
|
||||
}).then(function(result) {
|
||||
ok(result, 'cache should be deleted');
|
||||
// This is a bit superfluous, but its necessary to make sure the Cache is
|
||||
// fully deleted before we proceed. The deletion actually takes place in
|
||||
// two async steps. We don't want to resetStorage() until the second step
|
||||
// has taken place. This extra Cache operation ensure that all the
|
||||
// runnables have been flushed through the threads, etc.
|
||||
return caches.has(name);
|
||||
}).then(function(result) {
|
||||
ok(!result, 'cache should not exist in storage');
|
||||
// reset the quota manager storage to ensure the DB connection is flushed
|
||||
return resetStorage();
|
||||
}).then(function() {
|
||||
return storageUsage();
|
||||
}).then(function(usage) {
|
||||
endUsage = usage;
|
||||
dump("### ### initial:" + initialUsage + ", full:" + fullUsage +
|
||||
", end:" + endUsage + "\n");
|
||||
ok(endUsage < (fullUsage / 2), 'disk usage should have shrank significantly');
|
||||
ok(endUsage > initialUsage, 'disk usage should not shrink back to orig size');
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1381,7 +1381,7 @@ nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces
|
||||
|
||||
if (faces.SetCapacity(len, fallible)) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
*faces.AppendElement() =
|
||||
*faces.AppendElement(fallible) =
|
||||
new DOMCameraDetectedFace(static_cast<DOMMediaStream*>(this), aFaces[i]);
|
||||
}
|
||||
}
|
||||
|
@ -2676,7 +2676,7 @@ void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement
|
||||
// set dashing for foreground
|
||||
FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
if (!dash.AppendElement(1)) {
|
||||
if (!dash.AppendElement(1, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
@ -3964,14 +3964,14 @@ CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dash.AppendElement(aSegments[x])) {
|
||||
if (!dash.AppendElement(aSegments[x], fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
|
||||
for (uint32_t x = 0; x < aSegments.Length(); x++) {
|
||||
if (!dash.AppendElement(aSegments[x])) {
|
||||
if (!dash.AppendElement(aSegments[x], fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ JSValToDashArray(JSContext* cx, const JS::Value& patternArray,
|
||||
} else if (d > 0.0) {
|
||||
haveNonzeroElement = true;
|
||||
}
|
||||
if (!dashes.AppendElement(d)) {
|
||||
if (!dashes.AppendElement(d, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
@ -349,19 +349,19 @@ TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenu
|
||||
for (size_t i = 0; i < in.Length(); i++) {
|
||||
switch (in[i]) {
|
||||
case LOCAL_GL_COLOR:
|
||||
if (!out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0)) {
|
||||
if (!out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0, fallible)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_DEPTH:
|
||||
if (!out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT)) {
|
||||
if (!out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT, fallible)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_STENCIL:
|
||||
if (!out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT)) {
|
||||
if (!out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT, fallible)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -377,7 +377,7 @@ WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
||||
if (requiredNumLeaves != NumLeaves()) {
|
||||
// See class comment for why we the tree storage size is 2 * numLeaves.
|
||||
if (!mTreeData.SetLength(2 * requiredNumLeaves, fallible)) {
|
||||
mTreeData.SetLength(0);
|
||||
mTreeData.Clear();
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(NumLeaves() == requiredNumLeaves);
|
||||
@ -471,7 +471,7 @@ WebGLElementArrayCache::BufferData(const void* ptr, size_t byteLength)
|
||||
{
|
||||
if (mBytes.Length() != byteLength) {
|
||||
if (!mBytes.SetLength(byteLength, fallible)) {
|
||||
mBytes.SetLength(0);
|
||||
mBytes.Clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ CryptoBuffer::Assign(const ArrayBufferViewOrArrayBuffer& aData)
|
||||
|
||||
// If your union is uninitialized, something's wrong
|
||||
MOZ_ASSERT(false);
|
||||
SetLength(0);
|
||||
Clear();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ CryptoBuffer::Assign(const OwningArrayBufferViewOrArrayBuffer& aData)
|
||||
|
||||
// If your union is uninitialized, something's wrong
|
||||
MOZ_ASSERT(false);
|
||||
SetLength(0);
|
||||
Clear();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -951,6 +951,41 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
|
||||
}
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
CreateECPublicKey(const SECItem* aKeyData, const nsString& aNamedCurve)
|
||||
{
|
||||
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
if (!arena) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SECKEYPublicKey* key = PORT_ArenaZNew(arena, SECKEYPublicKey);
|
||||
if (!key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
key->keyType = ecKey;
|
||||
key->pkcs11Slot = nullptr;
|
||||
key->pkcs11ID = CK_INVALID_HANDLE;
|
||||
|
||||
// Create curve parameters.
|
||||
SECItem* params = CreateECParamsForCurve(aNamedCurve, arena);
|
||||
if (!params) {
|
||||
return nullptr;
|
||||
}
|
||||
key->u.ec.DEREncodedParams = *params;
|
||||
|
||||
// Set public point.
|
||||
key->u.ec.publicValue = *aKeyData;
|
||||
|
||||
// Ensure the given point is on the curve.
|
||||
if (!CryptoKey::PublicKeyValid(key)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SECKEY_CopyPublicKey(key);
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
@ -1002,39 +1037,18 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SECKEYPublicKey* key = PORT_ArenaZNew(arena, SECKEYPublicKey);
|
||||
if (!key) {
|
||||
// Create point.
|
||||
SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
|
||||
if (!point) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
key->keyType = ecKey;
|
||||
key->pkcs11Slot = nullptr;
|
||||
key->pkcs11ID = CK_INVALID_HANDLE;
|
||||
|
||||
nsString namedCurve;
|
||||
if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create parameters.
|
||||
SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
|
||||
if (!params) {
|
||||
return nullptr;
|
||||
}
|
||||
key->u.ec.DEREncodedParams = *params;
|
||||
|
||||
// Create point.
|
||||
SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
|
||||
if (!point) {
|
||||
return nullptr;
|
||||
}
|
||||
key->u.ec.publicValue = *point;
|
||||
|
||||
if (!PublicKeyValid(key)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SECKEY_CopyPublicKey(key);
|
||||
return CreateECPublicKey(point, namedCurve);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -1115,6 +1129,57 @@ CryptoKey::PublicDhKeyToRaw(SECKEYPublicKey* aPubKey,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
CryptoKey::PublicECKeyFromRaw(CryptoBuffer& aKeyData,
|
||||
const nsString& aNamedCurve,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
if (!arena) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SECItem rawItem = { siBuffer, nullptr, 0 };
|
||||
if (!aKeyData.ToSECItem(arena, &rawItem)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t flen;
|
||||
if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
|
||||
flen = 32; // bytes
|
||||
} else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
|
||||
flen = 48; // bytes
|
||||
} else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
|
||||
flen = 66; // bytes
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check length of uncompressed point coordinates. There are 2 field elements
|
||||
// and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED).
|
||||
if (rawItem.len != (2 * flen + 1)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// No support for compressed points.
|
||||
if (rawItem.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateECPublicKey(&rawItem, aNamedCurve);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CryptoKey::PublicECKeyToRaw(SECKEYPublicKey* aPubKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
if (!aRetVal.Assign(&aPubKey->u.ec.publicValue)) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey)
|
||||
{
|
||||
|
@ -180,6 +180,13 @@ public:
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static SECKEYPublicKey* PublicECKeyFromRaw(CryptoBuffer& aKeyData,
|
||||
const nsString& aNamedCurve,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PublicECKeyToRaw(SECKEYPublicKey* aPubKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static bool PublicKeyValid(SECKEYPublicKey* aPubKey);
|
||||
|
||||
// Structured clone methods use these to clone keys
|
||||
|
@ -563,16 +563,18 @@ private:
|
||||
// Perform the encryption/decryption
|
||||
if (mEncrypt) {
|
||||
rv = MapSECStatus(PK11_Encrypt(symKey.get(), mMechanism, ¶m,
|
||||
mResult.Elements(), &outLen, maxLen,
|
||||
mData.Elements(), mData.Length()));
|
||||
mResult.Elements(), &outLen,
|
||||
mResult.Length(), mData.Elements(),
|
||||
mData.Length()));
|
||||
} else {
|
||||
rv = MapSECStatus(PK11_Decrypt(symKey.get(), mMechanism, ¶m,
|
||||
mResult.Elements(), &outLen, maxLen,
|
||||
mData.Elements(), mData.Length()));
|
||||
mResult.Elements(), &outLen,
|
||||
mResult.Length(), mData.Elements(),
|
||||
mData.Length()));
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
mResult.SetLength(outLen);
|
||||
mResult.TruncateLength(outLen);
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
@ -848,7 +850,7 @@ private:
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
mResult.SetLength(outLen);
|
||||
mResult.TruncateLength(outLen);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
@ -934,10 +936,10 @@ private:
|
||||
rv = MapSECStatus(PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
|
||||
rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(),
|
||||
&outLen, HASH_LENGTH_MAX));
|
||||
&outLen, mResult.Length()));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
mResult.SetLength(outLen);
|
||||
mResult.TruncateLength(outLen);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -1644,7 +1646,7 @@ public:
|
||||
const ObjectOrString& aAlgorithm, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
{
|
||||
ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
|
||||
Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
|
||||
}
|
||||
|
||||
ImportEcKeyTask(JSContext* aCx, const nsAString& aFormat,
|
||||
@ -1652,7 +1654,7 @@ public:
|
||||
const ObjectOrString& aAlgorithm, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
{
|
||||
ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
|
||||
Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
@ -1661,6 +1663,30 @@ public:
|
||||
NS_ENSURE_SUCCESS_VOID(mEarlyRv);
|
||||
}
|
||||
|
||||
void Init(JSContext* aCx, const nsAString& aFormat,
|
||||
const ObjectOrString& aAlgorithm, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
{
|
||||
ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
|
||||
RootedDictionary<EcKeyImportParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
|
||||
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mNamedCurve;
|
||||
|
||||
@ -1680,14 +1706,19 @@ private:
|
||||
|
||||
mKey->SetPrivateKey(privKey.get());
|
||||
mKey->SetType(CryptoKey::PRIVATE);
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
|
||||
mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
|
||||
(mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
|
||||
!mJwk.mD.WasPassed())) {
|
||||
// Public key import
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
|
||||
pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve, locker);
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
pubKey = CryptoKey::PublicKeyFromSpki(mKeyData, locker);
|
||||
} else {
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
||||
pubKey = CryptoKey::PublicKeyFromJwk(mJwk, locker);
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
if (!pubKey) {
|
||||
@ -1901,6 +1932,14 @@ private:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mPublicKey && mPublicKey->keyType == ecKey) {
|
||||
nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey, mResult, locker);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mResult = mSymKey;
|
||||
if (mResult.Length() == 0) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
@ -1966,7 +2005,7 @@ private:
|
||||
|
||||
if (!mKeyUsages.IsEmpty()) {
|
||||
mJwk.mKey_ops.Construct();
|
||||
if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages)) {
|
||||
if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
@ -518,6 +518,11 @@ tv = {
|
||||
"405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
|
||||
),
|
||||
|
||||
raw: util.hex2abv(
|
||||
"045ce7b86e3b32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e" +
|
||||
"98f4cf075b39405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
|
||||
),
|
||||
|
||||
secret: util.hex2abv(
|
||||
"35669cd5c244ba6c1ea89b8802c3d1db815cd769979072e6556eb98548c65f7d"
|
||||
)
|
||||
@ -606,7 +611,31 @@ tv = {
|
||||
kty: "EC",
|
||||
crv: "P-256",
|
||||
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
|
||||
}
|
||||
},
|
||||
|
||||
// Public point with Y not on the curve.
|
||||
raw_bad: util.hex2abv(
|
||||
"045ce7b86e3b32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e" +
|
||||
"98f4cf075b39405dd1f1adeb090106edcfb2b4963739d87679e3056cb0557d0adf"
|
||||
),
|
||||
|
||||
// Public point with Y a little too short.
|
||||
raw_short: util.hex2abv(
|
||||
"045ce7b86e3b32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e" +
|
||||
"98f4cf075b39405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0a"
|
||||
),
|
||||
|
||||
// Public point with Y a little too long.
|
||||
raw_long: util.hex2abv(
|
||||
"045ce7b86e3b32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e" +
|
||||
"98f4cf075b39405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0adfff"
|
||||
),
|
||||
|
||||
// Public point with EC_POINT_FORM_COMPRESSED_Y0.
|
||||
raw_compressed: util.hex2abv(
|
||||
"025ce7b86e3b32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e" +
|
||||
"98f4cf075b39405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
|
||||
)
|
||||
},
|
||||
|
||||
// NIST ECDSA test vectors
|
||||
@ -626,6 +655,13 @@ tv = {
|
||||
"9Y33NGQ1_wQ0GZWDyXxmWpfxL3BvI1faS0Aoje-Ijlnm",
|
||||
},
|
||||
|
||||
raw: util.hex2abv(
|
||||
"040061387fd6b95914e885f912edfbb5fb274655027f216c4091ca83e19336740fd" +
|
||||
"81aedfe047f51b42bdf68161121013e0d55b117a14e4303f926c8debb77a7fdaad1" +
|
||||
"00e7d0c75c38626e895ca21526b9f9fdf84dcecb93f2b233390550d2b1463b7ee3f" +
|
||||
"58df7346435ff0434199583c97c665a97f12f706f2357da4b40288def888e59e6"
|
||||
),
|
||||
|
||||
"data": util.hex2abv(
|
||||
"9ecd500c60e701404922e58ab20cc002651fdee7cbc9336adda33e4c1088fab1" +
|
||||
"964ecb7904dc6856865d6c8e15041ccf2d5ac302e99d346ff2f686531d255216" +
|
||||
|
@ -438,6 +438,114 @@ TestArray.addTest(
|
||||
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Raw import/export of a public ECDH key (P-256)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
|
||||
function doExport(x) {
|
||||
return crypto.subtle.exportKey("raw", x);
|
||||
}
|
||||
|
||||
crypto.subtle.importKey("raw", tv.ecdh_p256.raw, alg, true, ["deriveBits"])
|
||||
.then(doExport)
|
||||
.then(memcmp_complete(that, tv.ecdh_p256.raw), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that importing bad raw ECDH keys fails",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
var tvs = tv.ecdh_p256_negative.raw_bad;
|
||||
|
||||
crypto.subtle.importKey("raw", tv, alg, false, ["deriveBits"])
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that importing ECDH keys with an unknown format fails",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
var tvs = tv.ecdh_p256.raw;
|
||||
|
||||
crypto.subtle.importKey("unknown", tv, alg, false, ["deriveBits"])
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that importing too short raw ECDH keys fails",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
var tvs = tv.ecdh_p256_negative.raw_short;
|
||||
|
||||
crypto.subtle.importKey("raw", tv, alg, false, ["deriveBits"])
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that importing too long raw ECDH keys fails",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
var tvs = tv.ecdh_p256_negative.raw_long;
|
||||
|
||||
crypto.subtle.importKey("raw", tv, alg, false, ["deriveBits"])
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Test that importing compressed raw ECDH keys fails",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
var tvs = tv.ecdh_p256_negative.raw_compressed;
|
||||
|
||||
crypto.subtle.importKey("raw", tv, alg, false, ["deriveBits"])
|
||||
.then(error(that), complete(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"RAW/JWK import ECDH keys (P-256) and derive a known secret",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH", namedCurve: "P-256" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p256.secret.byteLength * 8);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("raw", tv.ecdh_p256.raw, alg, false, ["deriveBits"])
|
||||
.then(setPub),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv)
|
||||
]).then(doDerive)
|
||||
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
|
||||
}
|
||||
);
|
||||
/*]]>*/</script>
|
||||
</head>
|
||||
|
||||
|
@ -144,6 +144,40 @@ TestArray.addTest(
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Raw import/export of a public ECDSA key (P-521)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
|
||||
|
||||
function doExport(x) {
|
||||
return crypto.subtle.exportKey("raw", x);
|
||||
}
|
||||
|
||||
crypto.subtle.importKey("raw", tv.ecdsa_verify.raw, alg, true, ["verify"])
|
||||
.then(doExport)
|
||||
.then(memcmp_complete(that, tv.ecdsa_verify.raw), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"ECDSA raw import and verify a known-good signature",
|
||||
function() {
|
||||
var that = this;
|
||||
var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
|
||||
|
||||
function doVerify(x) {
|
||||
return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
|
||||
}
|
||||
|
||||
crypto.subtle.importKey("raw", tv.ecdsa_verify.raw, alg, true, ["verify"])
|
||||
.then(doVerify)
|
||||
.then(complete(that), error(that))
|
||||
}
|
||||
);
|
||||
|
||||
/*]]>*/</script>
|
||||
</head>
|
||||
|
||||
|
@ -316,7 +316,8 @@ DataStoreDB::DatabaseOpened()
|
||||
}
|
||||
|
||||
StringOrStringSequence objectStores;
|
||||
if (!objectStores.RawSetAsStringSequence().AppendElements(mObjectStores)) {
|
||||
if (!objectStores.RawSetAsStringSequence().AppendElements(mObjectStores,
|
||||
fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
@ -1358,7 +1358,7 @@ DataStoreService::CreateFirstRevisionId(uint32_t aAppId,
|
||||
new FirstRevisionIdCallback(aAppId, aName, aManifestURL);
|
||||
|
||||
Sequence<nsString> dbs;
|
||||
if (!dbs.AppendElement(NS_LITERAL_STRING(DATASTOREDB_REVISION))) {
|
||||
if (!dbs.AppendElement(NS_LITERAL_STRING(DATASTOREDB_REVISION), fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,13 @@ HTMLAnchorElement::~HTMLAnchorElement()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
|
||||
nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
|
||||
}
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement)
|
||||
NS_INTERFACE_TABLE_INHERITED(HTMLAnchorElement,
|
||||
nsIDOMHTMLAnchorElement,
|
||||
|
@ -43,10 +43,7 @@ public:
|
||||
virtual bool Draggable() const override;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
|
||||
|
||||
// nsIDOMHTMLAnchorElement
|
||||
NS_DECL_NSIDOMHTMLANCHORELEMENT
|
||||
|
@ -1860,7 +1860,7 @@ HTMLInputElement::SetValue(const nsAString& aValue, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
Sequence<nsString> list;
|
||||
if (!list.AppendElement(aValue)) {
|
||||
if (!list.AppendElement(aValue, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
@ -2445,7 +2445,7 @@ HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLen
|
||||
|
||||
Sequence<nsString> list;
|
||||
for (uint32_t i = 0; i < aLength; ++i) {
|
||||
if (!list.AppendElement(nsDependentString(aFileNames[i]))) {
|
||||
if (!list.AppendElement(nsDependentString(aFileNames[i]), fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
@ -2498,7 +2498,7 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
|
||||
if (mType == NS_FORM_INPUT_FILE)
|
||||
{
|
||||
Sequence<nsString> list;
|
||||
if (!list.AppendElement(aValue)) {
|
||||
if (!list.AppendElement(aValue, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
@ -3251,8 +3251,11 @@ nsHTMLDocument::ExecCommand(const nsAString& commandID,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isCutCopy = (commandID.LowerCaseEqualsLiteral("cut") ||
|
||||
commandID.LowerCaseEqualsLiteral("copy"));
|
||||
|
||||
// if editing is not on, bail
|
||||
if (!IsEditingOnAfterFlush()) {
|
||||
if (!isCutCopy && !IsEditingOnAfterFlush()) {
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return false;
|
||||
}
|
||||
@ -3262,14 +3265,33 @@ nsHTMLDocument::ExecCommand(const nsAString& commandID,
|
||||
return false;
|
||||
}
|
||||
|
||||
// special case for cut & copy
|
||||
// cut & copy are allowed in non editable documents
|
||||
if (isCutCopy) {
|
||||
if (!nsContentUtils::IsCutCopyAllowed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For cut & copy commands, we need the behaviour from nsWindowRoot::GetControllers
|
||||
// which is to look at the focused element, and defer to a focused textbox's controller
|
||||
// The code past taken by other commands in ExecCommand always uses the window directly,
|
||||
// rather than deferring to the textbox, which is desireable for most editor commands,
|
||||
// but not 'cut' and 'copy' (as those should allow copying out of embedded editors).
|
||||
// This behaviour is invoked if we call DoCommand directly on the docShell.
|
||||
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
|
||||
if (docShell) {
|
||||
nsresult res = docShell->DoCommand(cmdToDispatch.get());
|
||||
return NS_SUCCEEDED(res);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (commandID.LowerCaseEqualsLiteral("gethtml")) {
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool restricted = commandID.LowerCaseEqualsLiteral("cut") ||
|
||||
commandID.LowerCaseEqualsLiteral("copy")||
|
||||
commandID.LowerCaseEqualsLiteral("paste");
|
||||
bool restricted = commandID.LowerCaseEqualsLiteral("paste");
|
||||
if (restricted && !nsContentUtils::IsCallerChrome()) {
|
||||
rv = NS_ERROR_DOM_SECURITY_ERR;
|
||||
return false;
|
||||
|
@ -33,6 +33,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=229925
|
||||
<textarea class="yes" cols="1" rows="1"></textarea>
|
||||
<video class="yes" controls></video>
|
||||
|
||||
<a class="no">a</a>
|
||||
<audio class="no"></audio>
|
||||
<img class="no" src="data:image/png,">
|
||||
<input class="no" type="hidden">
|
||||
|
@ -8521,7 +8521,7 @@ ConvertBlobsToActors(PBackgroundParent* aBackgroundActor,
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(aActors.AppendElement(actor));
|
||||
MOZ_ALWAYS_TRUE(aActors.AppendElement(actor, fallible));
|
||||
|
||||
if (collectFileInfos) {
|
||||
nsRefPtr<FileInfo> fileInfo = file.mFileInfo;
|
||||
@ -8529,7 +8529,7 @@ ConvertBlobsToActors(PBackgroundParent* aBackgroundActor,
|
||||
// Transfer a reference to the receiver.
|
||||
auto transferedFileInfo =
|
||||
reinterpret_cast<intptr_t>(fileInfo.forget().take());
|
||||
MOZ_ALWAYS_TRUE(aFileInfos.AppendElement(transferedFileInfo));
|
||||
MOZ_ALWAYS_TRUE(aFileInfos.AppendElement(transferedFileInfo, fallible));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12358,7 +12358,7 @@ Database::Invalidate()
|
||||
auto* array =
|
||||
static_cast<FallibleTArray<nsRefPtr<TransactionBase>>*>(aUserData);
|
||||
|
||||
if (NS_WARN_IF(!array->AppendElement(aEntry->GetKey()))) {
|
||||
if (NS_WARN_IF(!array->AppendElement(aEntry->GetKey(), fallible))) {
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
@ -12626,7 +12626,7 @@ Database::AllocPBackgroundIDBTransactionParent(
|
||||
|
||||
if (closure->mName == aValue->mCommonMetadata.name() &&
|
||||
!aValue->mDeleted) {
|
||||
MOZ_ALWAYS_TRUE(closure->mObjectStores.AppendElement(aValue));
|
||||
MOZ_ALWAYS_TRUE(closure->mObjectStores.AppendElement(aValue, fallible));
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
@ -18785,7 +18785,7 @@ FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
|
||||
Database* database = aDatabaseActorInfo->mLiveDatabases[index];
|
||||
if ((!aOpeningDatabase || database != aOpeningDatabase) &&
|
||||
!database->IsClosed() &&
|
||||
NS_WARN_IF(!maybeBlockedDatabases.AppendElement(database))) {
|
||||
NS_WARN_IF(!maybeBlockedDatabases.AppendElement(database, fallible))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
@ -20904,7 +20904,8 @@ VersionChangeOp::RunOnOwningThread()
|
||||
MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
|
||||
|
||||
FallibleTArray<Database*> liveDatabases;
|
||||
if (NS_WARN_IF(!liveDatabases.AppendElements(info->mLiveDatabases))) {
|
||||
if (NS_WARN_IF(!liveDatabases.AppendElements(info->mLiveDatabases,
|
||||
fallible))) {
|
||||
deleteOp->SetFailureCode(NS_ERROR_OUT_OF_MEMORY);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
@ -23046,7 +23047,7 @@ ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction)
|
||||
TPBackgroundIDBDatabaseFileParent ||
|
||||
fileOrFileId.type() == DatabaseFileOrMutableFileId::Tint64_t);
|
||||
|
||||
StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement();
|
||||
StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(fallible);
|
||||
MOZ_ASSERT(storedFileInfo);
|
||||
|
||||
switch (fileOrFileId.type()) {
|
||||
@ -23609,7 +23610,7 @@ ObjectStoreGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
|
||||
bool hasResult;
|
||||
while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
||||
StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement();
|
||||
StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(fallible);
|
||||
if (NS_WARN_IF(!cloneInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -23736,7 +23737,7 @@ ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
|
||||
bool hasResult;
|
||||
while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
||||
Key* key = mResponse.AppendElement();
|
||||
Key* key = mResponse.AppendElement(fallible);
|
||||
if (NS_WARN_IF(!key)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -24158,7 +24159,7 @@ IndexGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
|
||||
bool hasResult;
|
||||
while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
||||
StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement();
|
||||
StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(fallible);
|
||||
if (NS_WARN_IF(!cloneInfo)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -24350,7 +24351,7 @@ IndexGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
|
||||
bool hasResult;
|
||||
while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
||||
Key* key = mResponse.AppendElement();
|
||||
Key* key = mResponse.AppendElement(fallible);
|
||||
if (NS_WARN_IF(!key)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -1230,14 +1230,17 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileActor));
|
||||
MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileActor,
|
||||
fallible));
|
||||
} else {
|
||||
const int64_t fileId = blobOrFileInfo.mFileInfo->Id();
|
||||
MOZ_ASSERT(fileId > 0);
|
||||
|
||||
MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileId));
|
||||
MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileId,
|
||||
fallible));
|
||||
|
||||
nsRefPtr<FileInfo>* newFileInfo = fileInfosToKeepAlive.AppendElement();
|
||||
nsRefPtr<FileInfo>* newFileInfo =
|
||||
fileInfosToKeepAlive.AppendElement(fallible);
|
||||
if (NS_WARN_IF(!newFileInfo)) {
|
||||
aRv = NS_ERROR_OUT_OF_MEMORY;
|
||||
return nullptr;
|
||||
|
@ -2035,6 +2035,15 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
||||
obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
|
||||
}
|
||||
|
||||
// Remove any and all idle listeners.
|
||||
nsCOMPtr<nsIIdleService> idleService =
|
||||
do_GetService("@mozilla.org/widget/idleservice;1");
|
||||
MOZ_ASSERT(idleService);
|
||||
nsRefPtr<ParentIdleListener> listener;
|
||||
for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
|
||||
listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
|
||||
idleService->RemoveIdleObserver(listener, listener->mTime);
|
||||
}
|
||||
mIdleListeners.Clear();
|
||||
|
||||
MessageLoop::current()->
|
||||
@ -4660,30 +4669,34 @@ ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, const uint32_t& aI
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIdleService> idleService =
|
||||
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
|
||||
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
nsRefPtr<ParentIdleListener> listener = new ParentIdleListener(this, aObserver);
|
||||
mIdleListeners.Put(aObserver, listener);
|
||||
idleService->AddIdleObserver(listener, aIdleTimeInS);
|
||||
nsRefPtr<ParentIdleListener> listener =
|
||||
new ParentIdleListener(this, aObserver, aIdleTimeInS);
|
||||
rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
mIdleListeners.AppendElement(listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIdleService> idleService =
|
||||
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
nsRefPtr<ParentIdleListener> listener;
|
||||
bool found = mIdleListeners.Get(aObserver, &listener);
|
||||
if (found) {
|
||||
mIdleListeners.Remove(aObserver);
|
||||
idleService->RemoveIdleObserver(listener, aIdleTimeInS);
|
||||
for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
|
||||
listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
|
||||
if (listener->mObserver == aObserver &&
|
||||
listener->mTime == aIdleTimeInS) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIdleService> idleService =
|
||||
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
idleService->RemoveIdleObserver(listener, aIdleTimeInS);
|
||||
mIdleListeners.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -922,7 +922,7 @@ private:
|
||||
nsRefPtr<nsConsoleService> mConsoleService;
|
||||
nsConsoleService* GetConsoleService();
|
||||
|
||||
nsDataHashtable<nsUint64HashKey, nsRefPtr<ParentIdleListener> > mIdleListeners;
|
||||
nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
// Dup of child's X socket, used to scope its resources to this
|
||||
@ -942,17 +942,20 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
class ParentIdleListener : public nsIObserver {
|
||||
friend class mozilla::dom::ContentParent;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver)
|
||||
: mParent(aParent), mObserver(aObserver)
|
||||
ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver, uint32_t aTime)
|
||||
: mParent(aParent), mObserver(aObserver), mTime(aTime)
|
||||
{}
|
||||
private:
|
||||
virtual ~ParentIdleListener() {}
|
||||
nsRefPtr<mozilla::dom::ContentParent> mParent;
|
||||
uint64_t mObserver;
|
||||
uint32_t mTime;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -508,8 +508,8 @@ MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
|
||||
}
|
||||
|
||||
// We ensure sufficient capacity above so this shouldn't fail.
|
||||
MOZ_ALWAYS_TRUE(mBuffer->AppendElements(aData, aSize));
|
||||
MOZ_ALWAYS_TRUE(mBuffer->AppendElements(RAW_DATA_ALIGNMENT));
|
||||
MOZ_ALWAYS_TRUE(mBuffer->AppendElements(aData, aSize, fallible));
|
||||
MOZ_ALWAYS_TRUE(mBuffer->AppendElements(RAW_DATA_ALIGNMENT, fallible));
|
||||
mSize = aSize;
|
||||
}
|
||||
|
||||
@ -530,8 +530,8 @@ MediaRawData::Clone() const
|
||||
}
|
||||
|
||||
// We ensure sufficient capacity above so this shouldn't fail.
|
||||
MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(mData, mSize));
|
||||
MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(RAW_DATA_ALIGNMENT));
|
||||
MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(mData, mSize, fallible));
|
||||
MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(RAW_DATA_ALIGNMENT, fallible));
|
||||
s->mSize = mSize;
|
||||
}
|
||||
return s.forget();
|
||||
|
@ -579,8 +579,8 @@ void MediaDecoder::CallSeek(const SeekTarget& aTarget)
|
||||
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(),
|
||||
mDecoderStateMachine.get(), __func__,
|
||||
&MediaDecoderStateMachine::Seek, aTarget)
|
||||
->RefableThen(AbstractThread::MainThread(), __func__, this,
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
->Then(AbstractThread::MainThread(), __func__, this,
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
}
|
||||
|
||||
double MediaDecoder::GetCurrentTime()
|
||||
|
@ -1019,7 +1019,7 @@ protected:
|
||||
|
||||
virtual void CallSeek(const SeekTarget& aTarget);
|
||||
|
||||
MediaPromiseConsumerHolder<SeekPromise> mSeekRequest;
|
||||
MediaPromiseRequestHolder<SeekPromise> mSeekRequest;
|
||||
|
||||
// True when seeking or otherwise moving the play position around in
|
||||
// such a manner that progress event data is inaccurate. This is set
|
||||
|
@ -182,9 +182,6 @@ MediaDecoderReader::AsyncReadMetadata()
|
||||
mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
|
||||
DECODER_LOG("MediaDecoderReader::AsyncReadMetadata");
|
||||
|
||||
// PreReadMetadata causes us to try to allocate various hardware and OS
|
||||
// resources, which may not be available at the moment.
|
||||
PreReadMetadata();
|
||||
if (IsWaitingMediaResources()) {
|
||||
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
|
||||
}
|
||||
|
@ -165,13 +165,10 @@ public:
|
||||
virtual bool HasVideo() = 0;
|
||||
|
||||
// The default implementation of AsyncReadMetadata is implemented in terms of
|
||||
// synchronous PreReadMetadata() / ReadMetadata() calls. Implementations may also
|
||||
// synchronous ReadMetadata() calls. Implementations may also
|
||||
// override AsyncReadMetadata to create a more proper async implementation.
|
||||
virtual nsRefPtr<MetadataPromise> AsyncReadMetadata();
|
||||
|
||||
// A function that is called before ReadMetadata() call.
|
||||
virtual void PreReadMetadata() {};
|
||||
|
||||
// Read header data for all bitstreams in the file. Fills aInfo with
|
||||
// the data required to present the media, and optionally fills *aTags
|
||||
// with tag metadata from the file.
|
||||
|
@ -976,16 +976,16 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
||||
nsRefPtr<MediaDecoderStateMachine> self = this;
|
||||
WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::WaitForData, aType)
|
||||
->RefableThen(TaskQueue(), __func__,
|
||||
[self] (MediaData::Type aType) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->WaitRequestRef(aType).Complete();
|
||||
self->DispatchDecodeTasksIfNeeded();
|
||||
},
|
||||
[self] (WaitForDataRejectValue aRejection) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->WaitRequestRef(aRejection.mType).Complete();
|
||||
}));
|
||||
->Then(TaskQueue(), __func__,
|
||||
[self] (MediaData::Type aType) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->WaitRequestRef(aType).Complete();
|
||||
self->DispatchDecodeTasksIfNeeded();
|
||||
},
|
||||
[self] (WaitForDataRejectValue aRejection) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->WaitRequestRef(aRejection.mType).Complete();
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1961,20 +1961,20 @@ MediaDecoderStateMachine::InitiateSeek()
|
||||
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
|
||||
GetEndTime())
|
||||
->RefableThen(TaskQueue(), __func__,
|
||||
[self] (int64_t) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->mSeekRequest.Complete();
|
||||
// We must decode the first samples of active streams, so we can determine
|
||||
// the new stream time. So dispatch tasks to do that.
|
||||
self->mDecodeToSeekTarget = true;
|
||||
self->DispatchDecodeTasksIfNeeded();
|
||||
}, [self] (nsresult aResult) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->mSeekRequest.Complete();
|
||||
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
|
||||
self->DecodeError();
|
||||
}));
|
||||
->Then(TaskQueue(), __func__,
|
||||
[self] (int64_t) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->mSeekRequest.Complete();
|
||||
// We must decode the first samples of active streams, so we can determine
|
||||
// the new stream time. So dispatch tasks to do that.
|
||||
self->mDecodeToSeekTarget = true;
|
||||
self->DispatchDecodeTasksIfNeeded();
|
||||
}, [self] (nsresult aResult) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->mSeekRequest.Complete();
|
||||
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
|
||||
self->DecodeError();
|
||||
}));
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -2020,9 +2020,9 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
|
||||
|
||||
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||
__func__, &MediaDecoderReader::RequestAudioData)
|
||||
->RefableThen(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||
->Then(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2080,9 +2080,9 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
||||
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData,
|
||||
skipToNextKeyFrame, currentTime)
|
||||
->RefableThen(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||
->Then(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2364,18 +2364,18 @@ MediaDecoderStateMachine::DecodeFirstFrame()
|
||||
if (HasAudio()) {
|
||||
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||
__func__, &MediaDecoderReader::RequestAudioData)
|
||||
->RefableThen(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||
->Then(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||
}
|
||||
if (HasVideo()) {
|
||||
mVideoDecodeStartTime = TimeStamp::Now();
|
||||
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||
__func__, &MediaDecoderReader::RequestVideoData, false,
|
||||
int64_t(0))
|
||||
->RefableThen(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||
->Then(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2648,9 +2648,9 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
DECODER_LOG("Dispatching AsyncReadMetadata");
|
||||
mMetadataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
->RefableThen(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMetadataRead,
|
||||
&MediaDecoderStateMachine::OnMetadataNotRead));
|
||||
->Then(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMetadataRead,
|
||||
&MediaDecoderStateMachine::OnMetadataNotRead));
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -825,7 +825,7 @@ public:
|
||||
}
|
||||
Reset();
|
||||
mTarget = aTarget;
|
||||
mRequest.Begin(mMediaTimer->WaitUntil(mTarget, __func__)->RefableThen(
|
||||
mRequest.Begin(mMediaTimer->WaitUntil(mTarget, __func__)->Then(
|
||||
mSelf->TaskQueue(), __func__, mSelf,
|
||||
&MediaDecoderStateMachine::OnDelayedSchedule,
|
||||
&MediaDecoderStateMachine::NotReached));
|
||||
@ -841,7 +841,7 @@ public:
|
||||
private:
|
||||
MediaDecoderStateMachine* mSelf;
|
||||
nsRefPtr<MediaTimer> mMediaTimer;
|
||||
MediaPromiseConsumerHolder<mozilla::MediaTimerPromise> mRequest;
|
||||
MediaPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
|
||||
TimeStamp mTarget;
|
||||
|
||||
} mDelayedScheduler;
|
||||
@ -1151,8 +1151,8 @@ protected:
|
||||
// Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise)
|
||||
// should exist at any given moment.
|
||||
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::AudioDataPromise> mAudioDataRequest;
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::WaitForDataPromise> mAudioWaitRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::AudioDataPromise> mAudioDataRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mAudioWaitRequest;
|
||||
const char* AudioRequestStatus()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
@ -1165,8 +1165,8 @@ protected:
|
||||
return "idle";
|
||||
}
|
||||
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::WaitForDataPromise> mVideoWaitRequest;
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::VideoDataPromise> mVideoDataRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mVideoWaitRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::VideoDataPromise> mVideoDataRequest;
|
||||
const char* VideoRequestStatus()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
@ -1179,7 +1179,7 @@ protected:
|
||||
return "idle";
|
||||
}
|
||||
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::WaitForDataPromise>& WaitRequestRef(MediaData::Type aType)
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise>& WaitRequestRef(MediaData::Type aType)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return aType == MediaData::AUDIO_DATA ? mAudioWaitRequest : mVideoWaitRequest;
|
||||
@ -1255,7 +1255,7 @@ protected:
|
||||
bool mDecodeToSeekTarget;
|
||||
|
||||
// Track the current seek promise made by the reader.
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
|
||||
|
||||
// We record the playback position before we seek in order to
|
||||
// determine where the seek terminated relative to the playback position
|
||||
@ -1263,7 +1263,7 @@ protected:
|
||||
int64_t mCurrentTimeBeforeSeek;
|
||||
|
||||
// Track our request for metadata from the reader.
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
|
||||
|
||||
// Stores presentation info required for playback. The decoder monitor
|
||||
// must be held when accessing this.
|
||||
|
@ -278,10 +278,9 @@ MediaFormatReader::AsyncReadMetadata()
|
||||
nsRefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
|
||||
|
||||
mDemuxerInitRequest.Begin(mDemuxer->Init()
|
||||
->RefableThen(GetTaskQueue(), __func__,
|
||||
this,
|
||||
&MediaFormatReader::OnDemuxerInitDone,
|
||||
&MediaFormatReader::OnDemuxerInitFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnDemuxerInitDone,
|
||||
&MediaFormatReader::OnDemuxerInitFailed));
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -587,9 +586,9 @@ MediaFormatReader::DoDemuxVideo()
|
||||
{
|
||||
// TODO Use DecodeAhead value rather than 1.
|
||||
mVideo.mDemuxRequest.Begin(mVideo.mTrackDemuxer->GetSamples(1)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnVideoDemuxCompleted,
|
||||
&MediaFormatReader::OnVideoDemuxFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnVideoDemuxCompleted,
|
||||
&MediaFormatReader::OnVideoDemuxFailed));
|
||||
}
|
||||
|
||||
void
|
||||
@ -642,9 +641,9 @@ MediaFormatReader::DoDemuxAudio()
|
||||
{
|
||||
// TODO Use DecodeAhead value rather than 1.
|
||||
mAudio.mDemuxRequest.Begin(mAudio.mTrackDemuxer->GetSamples(1)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnAudioDemuxCompleted,
|
||||
&MediaFormatReader::OnAudioDemuxFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnAudioDemuxCompleted,
|
||||
&MediaFormatReader::OnAudioDemuxFailed));
|
||||
}
|
||||
|
||||
void
|
||||
@ -1109,9 +1108,9 @@ MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold)
|
||||
}
|
||||
|
||||
mSkipRequest.Begin(mVideo.mTrackDemuxer->SkipToNextRandomAccessPoint(aTimeThreshold)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnVideoSkipCompleted,
|
||||
&MediaFormatReader::OnVideoSkipFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnVideoSkipCompleted,
|
||||
&MediaFormatReader::OnVideoSkipFailed));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1212,9 +1211,9 @@ MediaFormatReader::DoVideoSeek()
|
||||
LOGV("Seeking video to %lld", mPendingSeekTime.ref().ToMicroseconds());
|
||||
media::TimeUnit seekTime = mPendingSeekTime.ref();
|
||||
mVideoSeekRequest.Begin(mVideo.mTrackDemuxer->Seek(seekTime)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnVideoSeekCompleted,
|
||||
&MediaFormatReader::OnVideoSeekFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnVideoSeekCompleted,
|
||||
&MediaFormatReader::OnVideoSeekFailed));
|
||||
}
|
||||
|
||||
void
|
||||
@ -1241,9 +1240,9 @@ MediaFormatReader::DoAudioSeek()
|
||||
LOGV("Seeking audio to %lld", mPendingSeekTime.ref().ToMicroseconds());
|
||||
media::TimeUnit seekTime = mPendingSeekTime.ref();
|
||||
mAudioSeekRequest.Begin(mAudio.mTrackDemuxer->Seek(seekTime)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnAudioSeekCompleted,
|
||||
&MediaFormatReader::OnAudioSeekFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaFormatReader::OnAudioSeekCompleted,
|
||||
&MediaFormatReader::OnAudioSeekFailed));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -224,7 +224,7 @@ private:
|
||||
|
||||
// Queued demux samples waiting to be decoded.
|
||||
nsTArray<nsRefPtr<MediaRawData>> mQueuedSamples;
|
||||
MediaPromiseConsumerHolder<MediaTrackDemuxer::SamplesPromise> mDemuxRequest;
|
||||
MediaPromiseRequestHolder<MediaTrackDemuxer::SamplesPromise> mDemuxRequest;
|
||||
MediaPromiseHolder<WaitForDataPromise> mWaitingPromise;
|
||||
bool HasWaitingPromise()
|
||||
{
|
||||
@ -316,7 +316,7 @@ private:
|
||||
// Demuxer objects.
|
||||
void OnDemuxerInitDone(nsresult);
|
||||
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
|
||||
MediaPromiseConsumerHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
|
||||
MediaPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
|
||||
void OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure);
|
||||
|
||||
void DoDemuxVideo();
|
||||
@ -334,7 +334,7 @@ private:
|
||||
}
|
||||
|
||||
void SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold);
|
||||
MediaPromiseConsumerHolder<MediaTrackDemuxer::SkipAccessPointPromise> mSkipRequest;
|
||||
MediaPromiseRequestHolder<MediaTrackDemuxer::SkipAccessPointPromise> mSkipRequest;
|
||||
void OnVideoSkipCompleted(uint32_t aSkipped);
|
||||
void OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure);
|
||||
|
||||
@ -382,8 +382,8 @@ private:
|
||||
}
|
||||
// Temporary seek information while we wait for the data
|
||||
Maybe<media::TimeUnit> mPendingSeekTime;
|
||||
MediaPromiseConsumerHolder<MediaTrackDemuxer::SeekPromise> mVideoSeekRequest;
|
||||
MediaPromiseConsumerHolder<MediaTrackDemuxer::SeekPromise> mAudioSeekRequest;
|
||||
MediaPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mVideoSeekRequest;
|
||||
MediaPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mAudioSeekRequest;
|
||||
MediaPromiseHolder<SeekPromise> mSeekPromise;
|
||||
|
||||
#ifdef MOZ_EME
|
||||
|
@ -33,27 +33,91 @@ extern PRLogModuleInfo* gMediaPromiseLog;
|
||||
MOZ_ASSERT(gMediaPromiseLog); \
|
||||
MOZ_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__))
|
||||
|
||||
namespace detail {
|
||||
template<typename ThisType, typename Ret, typename ArgType>
|
||||
static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType));
|
||||
template<typename ThisType, typename Ret, typename ArgType>
|
||||
static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType) const);
|
||||
template<typename ThisType, typename Ret>
|
||||
static FalseType TakesArgumentHelper(Ret (ThisType::*)());
|
||||
template<typename ThisType, typename Ret>
|
||||
static FalseType TakesArgumentHelper(Ret (ThisType::*)() const);
|
||||
|
||||
template<typename ThisType, typename Ret, typename ArgType>
|
||||
static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType));
|
||||
template<typename ThisType, typename Ret, typename ArgType>
|
||||
static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType) const);
|
||||
template<typename ThisType, typename Ret>
|
||||
static Ret ReturnTypeHelper(Ret (ThisType::*)());
|
||||
template<typename ThisType, typename Ret>
|
||||
static Ret ReturnTypeHelper(Ret (ThisType::*)() const);
|
||||
|
||||
template<typename MethodType>
|
||||
struct ReturnType {
|
||||
typedef decltype(detail::ReturnTypeHelper(DeclVal<MethodType>())) Type;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename MethodType>
|
||||
struct TakesArgument {
|
||||
static const bool value = decltype(detail::TakesArgumentHelper(DeclVal<MethodType>()))::value;
|
||||
};
|
||||
|
||||
template<typename MethodType, typename TargetType>
|
||||
struct ReturnTypeIs {
|
||||
static const bool value = IsConvertible<typename detail::ReturnType<MethodType>::Type, TargetType>::value;
|
||||
};
|
||||
|
||||
/*
|
||||
* A promise manages an asynchronous request that may or may not be able to be
|
||||
* fulfilled immediately. When an API returns a promise, the consumer may attach
|
||||
* callbacks to be invoked (asynchronously, on a specified thread) when the
|
||||
* request is either completed (resolved) or cannot be completed (rejected).
|
||||
*
|
||||
* MediaPromises attempt to mirror the spirit of JS Promises to the extent that
|
||||
* is possible (and desirable) in C++. While the intent is that MediaPromises
|
||||
* feel familiar to programmers who are accustomed to their JS-implemented cousin,
|
||||
* we don't shy away from imposing restrictions and adding features that make
|
||||
* sense for the use cases we encounter.
|
||||
*
|
||||
* A MediaPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
|
||||
* call accepts resolve and reject callbacks, and returns a MediaPromise::Request.
|
||||
* The Request object serves several purposes for the consumer.
|
||||
*
|
||||
* (1) It allows the caller to cancel the delivery of the resolve/reject value
|
||||
* if it has not already occurred, via Disconnect() (this must be done on
|
||||
* the target thread to avoid racing).
|
||||
*
|
||||
* (2) It provides access to a "Completion Promise", which is roughly analagous
|
||||
* to the Promise returned directly by ->then() calls on JS promises. If
|
||||
* the resolve/reject callback returns a new MediaPromise, that promise is
|
||||
* chained to the completion promise, such that its resolve/reject value
|
||||
* will be forwarded along when it arrives. If the resolve/reject callback
|
||||
* returns void, the completion promise is resolved/rejected with the same
|
||||
* value that was passed to the callback.
|
||||
*
|
||||
* The MediaPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
|
||||
* (rather than already_AddRefed) from various methods. This is done to allow elegant
|
||||
* chaining of calls without cluttering up the code with intermediate variables, and
|
||||
* without introducing separate API variants for callers that want a return value
|
||||
* (from, say, ->Then()) from those that don't.
|
||||
*
|
||||
* When IsExclusive is true, the MediaPromise does a release-mode assertion that
|
||||
* there is at most one call to either Then(...) or ChainTo(...).
|
||||
*/
|
||||
|
||||
class MediaPromiseBase
|
||||
class MediaPromiseRefcountable
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromiseBase)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromiseRefcountable)
|
||||
protected:
|
||||
virtual ~MediaPromiseBase() {}
|
||||
virtual ~MediaPromiseRefcountable() {}
|
||||
};
|
||||
|
||||
template<typename T> class MediaPromiseHolder;
|
||||
template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
|
||||
class MediaPromise : public MediaPromiseBase
|
||||
class MediaPromise : public MediaPromiseRefcountable
|
||||
{
|
||||
public:
|
||||
typedef ResolveValueT ResolveValueType;
|
||||
@ -61,18 +125,32 @@ public:
|
||||
class ResolveOrRejectValue
|
||||
{
|
||||
public:
|
||||
void SetResolve(ResolveValueType& aResolveValue)
|
||||
void SetResolve(const ResolveValueType& aResolveValue)
|
||||
{
|
||||
MOZ_ASSERT(IsNothing());
|
||||
mResolveValue.emplace(aResolveValue);
|
||||
}
|
||||
|
||||
void SetReject(RejectValueType& aRejectValue)
|
||||
void SetReject(const RejectValueType& aRejectValue)
|
||||
{
|
||||
MOZ_ASSERT(IsNothing());
|
||||
mRejectValue.emplace(aRejectValue);
|
||||
}
|
||||
|
||||
static ResolveOrRejectValue MakeResolve(const ResolveValueType aResolveValue)
|
||||
{
|
||||
ResolveOrRejectValue val;
|
||||
val.SetResolve(aResolveValue);
|
||||
return val;
|
||||
}
|
||||
|
||||
static ResolveOrRejectValue MakeReject(const RejectValueType aRejectValue)
|
||||
{
|
||||
ResolveOrRejectValue val;
|
||||
val.SetReject(aRejectValue);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool IsResolve() const { return mResolveValue.isSome(); }
|
||||
bool IsReject() const { return mRejectValue.isSome(); }
|
||||
bool IsNothing() const { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
|
||||
@ -90,7 +168,7 @@ protected:
|
||||
explicit MediaPromise(const char* aCreationSite)
|
||||
: mCreationSite(aCreationSite)
|
||||
, mMutex("MediaPromise Mutex")
|
||||
, mHaveConsumer(false)
|
||||
, mHaveRequest(false)
|
||||
{
|
||||
PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this);
|
||||
}
|
||||
@ -121,20 +199,20 @@ public:
|
||||
return Move(p);
|
||||
}
|
||||
|
||||
class Consumer
|
||||
class Request : public MediaPromiseRefcountable
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer)
|
||||
|
||||
virtual void Disconnect() = 0;
|
||||
|
||||
// MSVC complains when an inner class (ThenValueBase::{Resolve,Reject}Runnable)
|
||||
// tries to access an inherited protected member.
|
||||
bool IsDisconnected() const { return mDisconnected; }
|
||||
|
||||
virtual MediaPromise* CompletionPromise() = 0;
|
||||
|
||||
protected:
|
||||
Consumer() : mComplete(false), mDisconnected(false) {}
|
||||
virtual ~Consumer() {}
|
||||
Request() : mComplete(false), mDisconnected(false) {}
|
||||
virtual ~Request() {}
|
||||
|
||||
bool mComplete;
|
||||
bool mDisconnected;
|
||||
@ -148,7 +226,7 @@ protected:
|
||||
* resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
|
||||
* invokes the resolve/reject method and then deletes the ThenValue.
|
||||
*/
|
||||
class ThenValueBase : public Consumer
|
||||
class ThenValueBase : public Request
|
||||
{
|
||||
public:
|
||||
class ResolveOrRejectRunnable : public nsRunnable
|
||||
@ -179,6 +257,16 @@ protected:
|
||||
explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
|
||||
: mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
|
||||
|
||||
MediaPromise* CompletionPromise() override
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
|
||||
if (!mCompletionPromise) {
|
||||
mCompletionPromise = new MediaPromise::Private("<completion promise>");
|
||||
}
|
||||
return mCompletionPromise;
|
||||
}
|
||||
|
||||
void Dispatch(MediaPromise *aPromise)
|
||||
{
|
||||
aPromise->mMutex.AssertCurrentThreadOwns();
|
||||
@ -190,7 +278,7 @@ protected:
|
||||
aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
|
||||
runnable.get(), aPromise, this);
|
||||
|
||||
// Promise consumers are allowed to disconnect the Consumer object and
|
||||
// Promise consumers are allowed to disconnect the Request object and
|
||||
// then shut down the thread or task queue that the promise result would
|
||||
// be dispatched on. So we unfortunately can't assert that promise
|
||||
// dispatch succeeds. :-(
|
||||
@ -200,25 +288,55 @@ protected:
|
||||
virtual void Disconnect() override
|
||||
{
|
||||
MOZ_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!Consumer::mComplete);
|
||||
Consumer::mDisconnected = true;
|
||||
MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
|
||||
Request::mDisconnected = true;
|
||||
|
||||
// We could support rejecting the completion promise on disconnection, but
|
||||
// then we'd need to have some sort of default reject value. The use cases
|
||||
// of disconnection and completion promise chaining seem pretty orthogonal,
|
||||
// so let's use assert against it.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mCompletionPromise);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
|
||||
virtual already_AddRefed<MediaPromise> DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
|
||||
|
||||
void DoResolveOrReject(ResolveOrRejectValue& aValue)
|
||||
{
|
||||
Consumer::mComplete = true;
|
||||
if (Consumer::mDisconnected) {
|
||||
Request::mComplete = true;
|
||||
if (Request::mDisconnected) {
|
||||
PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
|
||||
return;
|
||||
}
|
||||
|
||||
DoResolveOrRejectInternal(aValue);
|
||||
// Invoke the resolve or reject method.
|
||||
nsRefPtr<MediaPromise> p = DoResolveOrRejectInternal(aValue);
|
||||
|
||||
// If there's a completion promise, resolve it appropriately with the
|
||||
// result of the method.
|
||||
//
|
||||
// We jump through some hoops to cast to MediaPromise::Private here. This
|
||||
// can go away when we can just declare mCompletionPromise as
|
||||
// MediaPromise::Private. See the declaration below.
|
||||
nsRefPtr<MediaPromise::Private> completionPromise =
|
||||
dont_AddRef(static_cast<MediaPromise::Private*>(mCompletionPromise.forget().take()));
|
||||
if (completionPromise) {
|
||||
if (p) {
|
||||
p->ChainTo(completionPromise.forget(), "<chained completion promise>");
|
||||
} else {
|
||||
completionPromise->ResolveOrReject(aValue, "<completion of non-promise-returning method>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<AbstractThread> mResponseTarget; // May be released on any thread.
|
||||
|
||||
// Declaring nsRefPtr<MediaPromise::Private> here causes build failures
|
||||
// on MSVC because MediaPromise::Private is only forward-declared at this
|
||||
// point. This hack can go away when we inline-declare MediaPromise::Private,
|
||||
// which is blocked on the B2G ICS compiler being too old.
|
||||
nsRefPtr<MediaPromise> mCompletionPromise;
|
||||
|
||||
const char* mCallSite;
|
||||
};
|
||||
|
||||
@ -227,25 +345,42 @@ protected:
|
||||
* make the resolve/reject value argument "optional".
|
||||
*/
|
||||
|
||||
// Avoid confusing the compiler when the callback accepts T* but the ValueType
|
||||
// is nsRefPtr<T>. See bug 1109954 comment 6.
|
||||
template <typename T>
|
||||
struct NonDeduced
|
||||
template<typename ThisType, typename MethodType, typename ValueType>
|
||||
static typename EnableIf<ReturnTypeIs<MethodType, nsRefPtr<MediaPromise>>::value &&
|
||||
TakesArgument<MethodType>::value,
|
||||
already_AddRefed<MediaPromise>>::Type
|
||||
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType aValue)
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template<typename ThisType, typename ValueType>
|
||||
static void InvokeCallbackMethod(ThisType* aThisVal, void(ThisType::*aMethod)(ValueType),
|
||||
typename NonDeduced<ValueType>::type aValue)
|
||||
{
|
||||
((*aThisVal).*aMethod)(aValue);
|
||||
return ((*aThisVal).*aMethod)(aValue).forget();
|
||||
}
|
||||
|
||||
template<typename ThisType, typename ValueType>
|
||||
static void InvokeCallbackMethod(ThisType* aThisVal, void(ThisType::*aMethod)(), ValueType aValue)
|
||||
template<typename ThisType, typename MethodType, typename ValueType>
|
||||
static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
|
||||
TakesArgument<MethodType>::value,
|
||||
already_AddRefed<MediaPromise>>::Type
|
||||
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType aValue)
|
||||
{
|
||||
((*aThisVal).*aMethod)();
|
||||
((*aThisVal).*aMethod)(aValue);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename ThisType, typename MethodType, typename ValueType>
|
||||
static typename EnableIf<ReturnTypeIs<MethodType, nsRefPtr<MediaPromise>>::value &&
|
||||
!TakesArgument<MethodType>::value,
|
||||
already_AddRefed<MediaPromise>>::Type
|
||||
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType aValue)
|
||||
{
|
||||
return ((*aThisVal).*aMethod)().forget();
|
||||
}
|
||||
|
||||
template<typename ThisType, typename MethodType, typename ValueType>
|
||||
static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
|
||||
!TakesArgument<MethodType>::value,
|
||||
already_AddRefed<MediaPromise>>::Type
|
||||
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType aValue)
|
||||
{
|
||||
((*aThisVal).*aMethod)();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
|
||||
@ -264,19 +399,20 @@ protected:
|
||||
{
|
||||
ThenValueBase::Disconnect();
|
||||
|
||||
// If a Consumer has been disconnected, we don't guarantee that the
|
||||
// If a Request has been disconnected, we don't guarantee that the
|
||||
// resolve/reject runnable will be dispatched. Null out our refcounted
|
||||
// this-value now so that it's released predictably on the dispatch thread.
|
||||
mThisVal = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
|
||||
virtual already_AddRefed<MediaPromise> DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
|
||||
{
|
||||
nsRefPtr<MediaPromise> completion;
|
||||
if (aValue.IsResolve()) {
|
||||
InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue());
|
||||
completion = InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue());
|
||||
} else {
|
||||
InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue());
|
||||
completion = InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue());
|
||||
}
|
||||
|
||||
// Null out mThisVal after invoking the callback so that any references are
|
||||
@ -284,6 +420,8 @@ protected:
|
||||
// released on whatever thread last drops its reference to the ThenValue,
|
||||
// which may or may not be ok.
|
||||
mThisVal = nullptr;
|
||||
|
||||
return completion.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -311,7 +449,7 @@ protected:
|
||||
{
|
||||
ThenValueBase::Disconnect();
|
||||
|
||||
// If a Consumer has been disconnected, we don't guarantee that the
|
||||
// If a Request has been disconnected, we don't guarantee that the
|
||||
// resolve/reject runnable will be dispatched. Destroy our callbacks
|
||||
// now so that any references in closures are released predictable on
|
||||
// the dispatch thread.
|
||||
@ -320,12 +458,18 @@ protected:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
|
||||
virtual already_AddRefed<MediaPromise> DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
|
||||
{
|
||||
// Note: The usage of InvokeCallbackMethod here requires that
|
||||
// ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
|
||||
// classes with ::operator()), since it allows us to share code more easily.
|
||||
// We could fix this if need be, though it's quite easy to work around by
|
||||
// just capturing something.
|
||||
nsRefPtr<MediaPromise> completion;
|
||||
if (aValue.IsResolve()) {
|
||||
mResolveFunction.ref()(aValue.ResolveValue());
|
||||
completion = InvokeCallbackMethod(mResolveFunction.ptr(), &ResolveFunction::operator(), aValue.ResolveValue());
|
||||
} else {
|
||||
mRejectFunction.ref()(aValue.RejectValue());
|
||||
completion = InvokeCallbackMethod(mRejectFunction.ptr(), &RejectFunction::operator(), aValue.RejectValue());
|
||||
}
|
||||
|
||||
// Destroy callbacks after invocation so that any references in closures are
|
||||
@ -334,6 +478,8 @@ protected:
|
||||
// which may or may not be ok.
|
||||
mResolveFunction.reset();
|
||||
mRejectFunction.reset();
|
||||
|
||||
return completion.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -347,8 +493,8 @@ public:
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(aResponseThread->IsDispatchReliable());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
|
||||
mHaveConsumer = true;
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
|
||||
mHaveRequest = true;
|
||||
PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
|
||||
aCallSite, this, aThenValue, (int) IsPending());
|
||||
if (!IsPending()) {
|
||||
@ -361,48 +507,30 @@ public:
|
||||
public:
|
||||
|
||||
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
|
||||
already_AddRefed<Consumer> RefableThen(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
|
||||
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
|
||||
nsRefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
|
||||
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
|
||||
{
|
||||
nsRefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
|
||||
aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
|
||||
ThenInternal(aResponseThread, thenValue, aCallSite);
|
||||
return thenValue.forget();
|
||||
return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to nsRefPtr<Request>.
|
||||
}
|
||||
|
||||
template<typename ResolveFunction, typename RejectFunction>
|
||||
already_AddRefed<Consumer> RefableThen(AbstractThread* aResponseThread, const char* aCallSite,
|
||||
ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
|
||||
nsRefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
|
||||
ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
|
||||
{
|
||||
nsRefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
|
||||
Move(aResolveFunction), Move(aRejectFunction), aCallSite);
|
||||
ThenInternal(aResponseThread, thenValue, aCallSite);
|
||||
return thenValue.forget();
|
||||
}
|
||||
|
||||
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
|
||||
void Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
|
||||
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
|
||||
{
|
||||
nsRefPtr<Consumer> c =
|
||||
RefableThen(aResponseThread, aCallSite, aThisVal, aResolveMethod, aRejectMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename ThisType, typename ResolveFunction, typename RejectFunction>
|
||||
void Then(AbstractThread* aResponseThread, const char* aCallSite,
|
||||
ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
|
||||
{
|
||||
nsRefPtr<Consumer> c =
|
||||
RefableThen(aResponseThread, aCallSite, Move(aResolveFunction), Move(aRejectFunction));
|
||||
return;
|
||||
return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to nsRefPtr<Request>.
|
||||
}
|
||||
|
||||
void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
|
||||
mHaveConsumer = true;
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
|
||||
mHaveRequest = true;
|
||||
nsRefPtr<Private> chainedPromise = aChainedPromise;
|
||||
PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
|
||||
aCallSite, this, chainedPromise.get(), (int) IsPending());
|
||||
@ -452,7 +580,7 @@ protected:
|
||||
ResolveOrRejectValue mValue;
|
||||
nsTArray<nsRefPtr<ThenValueBase>> mThenValues;
|
||||
nsTArray<nsRefPtr<Private>> mChainedPromises;
|
||||
bool mHaveConsumer;
|
||||
bool mHaveRequest;
|
||||
};
|
||||
|
||||
template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
|
||||
@ -479,6 +607,15 @@ public:
|
||||
mValue.SetReject(aRejectValue);
|
||||
DispatchAll();
|
||||
}
|
||||
|
||||
void ResolveOrReject(ResolveOrRejectValue aValue, const char* aSite)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(IsPending());
|
||||
PROMISE_LOG("%s resolveOrRejecting MediaPromise (%p created at %s)", aSite, this, mCreationSite);
|
||||
mValue = aValue;
|
||||
DispatchAll();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -583,34 +720,34 @@ private:
|
||||
};
|
||||
|
||||
/*
|
||||
* Class to encapsulate a MediaPromise::Consumer reference. Use this as the member
|
||||
* Class to encapsulate a MediaPromise::Request reference. Use this as the member
|
||||
* variable for a class waiting on a media promise.
|
||||
*/
|
||||
template<typename PromiseType>
|
||||
class MediaPromiseConsumerHolder
|
||||
class MediaPromiseRequestHolder
|
||||
{
|
||||
public:
|
||||
MediaPromiseConsumerHolder() {}
|
||||
~MediaPromiseConsumerHolder() { MOZ_ASSERT(!mConsumer); }
|
||||
MediaPromiseRequestHolder() {}
|
||||
~MediaPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
|
||||
|
||||
void Begin(already_AddRefed<typename PromiseType::Consumer> aConsumer)
|
||||
void Begin(typename PromiseType::Request* aRequest)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!Exists());
|
||||
mConsumer = aConsumer;
|
||||
mRequest = aRequest;
|
||||
}
|
||||
|
||||
void Complete()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(Exists());
|
||||
mConsumer = nullptr;
|
||||
mRequest = nullptr;
|
||||
}
|
||||
|
||||
// Disconnects and forgets an outstanding promise. The resolve/reject methods
|
||||
// will never be called.
|
||||
void Disconnect() {
|
||||
MOZ_ASSERT(Exists());
|
||||
mConsumer->Disconnect();
|
||||
mConsumer = nullptr;
|
||||
mRequest->Disconnect();
|
||||
mRequest = nullptr;
|
||||
}
|
||||
|
||||
void DisconnectIfExists() {
|
||||
@ -619,10 +756,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool Exists() { return !!mConsumer; }
|
||||
bool Exists() { return !!mRequest; }
|
||||
|
||||
private:
|
||||
nsRefPtr<typename PromiseType::Consumer> mConsumer;
|
||||
nsRefPtr<typename PromiseType::Request> mRequest;
|
||||
};
|
||||
|
||||
// Proxy Media Calls.
|
||||
|
@ -4,6 +4,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPChild.h"
|
||||
#include "GMPAudioDecoderChild.h"
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPVideoDecoderChild.h"
|
||||
#include "GMPVideoEncoderChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "GMPVideoEncoderParent.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#define GMPContentParent_h_
|
||||
|
||||
#include "mozilla/gmp/PGMPContentParent.h"
|
||||
#include "GMPSharedMemManager.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -4,6 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPChild.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
@ -24,6 +24,7 @@
|
||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
#include "mozilla/SandboxInfo.h"
|
||||
#endif
|
||||
#include "GMPContentParent.h"
|
||||
|
||||
#include "mozilla/dom/CrashReporterParent.h"
|
||||
using mozilla::dom::CrashReporterParent;
|
||||
@ -45,6 +46,11 @@ extern PRLogModuleInfo* GetGMPLog();
|
||||
#define LOG(level, x, ...) MOZ_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
|
||||
#define LOGD(x, ...) LOG(PR_LOG_DEBUG, "GMPParent[%p|childPid=%d] " x, this, mChildPid, ##__VA_ARGS__)
|
||||
|
||||
#ifdef __CLASS__
|
||||
#undef __CLASS__
|
||||
#endif
|
||||
#define __CLASS__ "GMPParent"
|
||||
|
||||
namespace gmp {
|
||||
|
||||
GMPParent::GMPParent()
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "GMPProcessParent.h"
|
||||
#include "GMPUtils.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIRunnable.h"
|
||||
|
||||
#include "base/string_util.h"
|
||||
#include "base/process_util.h"
|
||||
|
@ -3,8 +3,10 @@
|
||||
* 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 "GMPService.h"
|
||||
#include "GMPServiceParent.h"
|
||||
#include "GMPServiceChild.h"
|
||||
#include "GMPContentParent.h"
|
||||
#include "prio.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "GMPParent.h"
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
|
@ -3,8 +3,16 @@
|
||||
* 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 "GMPService.h"
|
||||
#include "GMPServiceChild.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "mozIGeckoMediaPluginChromeService.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "GMPParent.h"
|
||||
#include "GMPContentParent.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "runnable_utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -38,20 +46,6 @@ GeckoMediaPluginServiceChild::GetSingleton()
|
||||
return service.forget().downcast<GeckoMediaPluginServiceChild>();
|
||||
}
|
||||
|
||||
class GetServiceChildCallback
|
||||
{
|
||||
public:
|
||||
GetServiceChildCallback()
|
||||
{
|
||||
MOZ_COUNT_CTOR(GetServiceChildCallback);
|
||||
}
|
||||
virtual ~GetServiceChildCallback()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GetServiceChildCallback);
|
||||
}
|
||||
virtual void Done(GMPServiceChild* aGMPServiceChild) = 0;
|
||||
};
|
||||
|
||||
class GetContentParentFromDone : public GetServiceChildCallback
|
||||
{
|
||||
public:
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "base/process.h"
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
#include "mozilla/gmp/PGMPServiceChild.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
@ -18,7 +19,20 @@ namespace gmp {
|
||||
|
||||
class GMPContentParent;
|
||||
class GMPServiceChild;
|
||||
class GetServiceChildCallback;
|
||||
|
||||
class GetServiceChildCallback
|
||||
{
|
||||
public:
|
||||
GetServiceChildCallback()
|
||||
{
|
||||
MOZ_COUNT_CTOR(GetServiceChildCallback);
|
||||
}
|
||||
virtual ~GetServiceChildCallback()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GetServiceChildCallback);
|
||||
}
|
||||
virtual void Done(GMPServiceChild* aGMPServiceChild) = 0;
|
||||
};
|
||||
|
||||
class GeckoMediaPluginServiceChild : public GeckoMediaPluginService
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
* 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 "GMPServiceParent.h"
|
||||
#include "GMPService.h"
|
||||
#include "prio.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsLiteralString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
class nsIFile;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename T>
|
||||
|
176
dom/media/gtest/TestMediaPromise.cpp
Normal file
176
dom/media/gtest/TestMediaPromise.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "MediaPromise.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
typedef MediaPromise<int, double, false> TestPromise;
|
||||
typedef TestPromise::ResolveOrRejectValue RRValue;
|
||||
|
||||
class MOZ_STACK_CLASS AutoTaskQueue
|
||||
{
|
||||
public:
|
||||
AutoTaskQueue()
|
||||
: mTaskQueue(new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
|
||||
{}
|
||||
|
||||
~AutoTaskQueue()
|
||||
{
|
||||
mTaskQueue->AwaitShutdownAndIdle();
|
||||
}
|
||||
|
||||
MediaTaskQueue* TaskQueue() { return mTaskQueue; }
|
||||
private:
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
};
|
||||
|
||||
class DelayedResolveOrReject : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DelayedResolveOrReject)
|
||||
|
||||
DelayedResolveOrReject(MediaTaskQueue* aTaskQueue,
|
||||
TestPromise::Private* aPromise,
|
||||
TestPromise::ResolveOrRejectValue aValue,
|
||||
int aIterations)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mPromise(aPromise)
|
||||
, mValue(aValue)
|
||||
, mIterations(aIterations)
|
||||
{}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
if (!mPromise) {
|
||||
// Canceled.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (--mIterations == 0) {
|
||||
mPromise->ResolveOrReject(mValue, __func__);
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> r = this;
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Cancel() {
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
~DelayedResolveOrReject() {}
|
||||
|
||||
private:
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
nsRefPtr<TestPromise::Private> mPromise;
|
||||
TestPromise::ResolveOrRejectValue mValue;
|
||||
int mIterations;
|
||||
};
|
||||
|
||||
template<typename FunctionType>
|
||||
void
|
||||
RunOnTaskQueue(MediaTaskQueue* aQueue, FunctionType aFun)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(aFun);
|
||||
aQueue->Dispatch(r.forget());
|
||||
}
|
||||
|
||||
// std::function can't come soon enough. :-(
|
||||
#define DO_FAIL []()->void { EXPECT_TRUE(false); }
|
||||
|
||||
TEST(MediaPromise, BasicResolve)
|
||||
{
|
||||
AutoTaskQueue atq;
|
||||
nsRefPtr<MediaTaskQueue> queue = atq.TaskQueue();
|
||||
RunOnTaskQueue(queue, [queue] () -> void {
|
||||
TestPromise::CreateAndResolve(42, __func__)->Then(queue, __func__,
|
||||
[queue] (int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); queue->BeginShutdown(); },
|
||||
DO_FAIL);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(MediaPromise, BasicReject)
|
||||
{
|
||||
AutoTaskQueue atq;
|
||||
nsRefPtr<MediaTaskQueue> queue = atq.TaskQueue();
|
||||
RunOnTaskQueue(queue, [queue] () -> void {
|
||||
TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
|
||||
DO_FAIL,
|
||||
[queue] (int aRejectValue) -> void { EXPECT_EQ(aRejectValue, 42.0); queue->BeginShutdown(); });
|
||||
});
|
||||
}
|
||||
|
||||
TEST(MediaPromise, AsyncResolve)
|
||||
{
|
||||
AutoTaskQueue atq;
|
||||
nsRefPtr<MediaTaskQueue> queue = atq.TaskQueue();
|
||||
RunOnTaskQueue(queue, [queue] () -> void {
|
||||
nsRefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
|
||||
|
||||
// Kick off three racing tasks, and make sure we get the one that finishes earliest.
|
||||
nsRefPtr<DelayedResolveOrReject> a = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(32), 10);
|
||||
nsRefPtr<DelayedResolveOrReject> b = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(42), 5);
|
||||
nsRefPtr<DelayedResolveOrReject> c = new DelayedResolveOrReject(queue, p, RRValue::MakeReject(32.0), 7);
|
||||
|
||||
nsCOMPtr<nsIRunnable> ref = a.get();
|
||||
queue->Dispatch(ref.forget());
|
||||
ref = b.get();
|
||||
queue->Dispatch(ref.forget());
|
||||
ref = c.get();
|
||||
queue->Dispatch(ref.forget());
|
||||
|
||||
p->Then(queue, __func__, [queue, a, b, c] (int aResolveValue) -> void {
|
||||
EXPECT_EQ(aResolveValue, 42);
|
||||
a->Cancel();
|
||||
b->Cancel();
|
||||
c->Cancel();
|
||||
queue->BeginShutdown();
|
||||
}, DO_FAIL);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(MediaPromise, CompletionPromises)
|
||||
{
|
||||
bool invokedPass = false;
|
||||
AutoTaskQueue atq;
|
||||
nsRefPtr<MediaTaskQueue> queue = atq.TaskQueue();
|
||||
RunOnTaskQueue(queue, [queue, &invokedPass] () -> void {
|
||||
TestPromise::CreateAndResolve(40, __func__)
|
||||
->Then(queue, __func__,
|
||||
[] (int aVal) -> nsRefPtr<TestPromise> { return TestPromise::CreateAndResolve(aVal + 10, __func__); },
|
||||
DO_FAIL)
|
||||
->CompletionPromise()
|
||||
->Then(queue, __func__, [&invokedPass] () -> void { invokedPass = true; }, DO_FAIL)
|
||||
->CompletionPromise()
|
||||
->Then(queue, __func__,
|
||||
[queue] (int aVal) -> nsRefPtr<TestPromise> {
|
||||
nsRefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
|
||||
nsCOMPtr<nsIRunnable> resolver = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(aVal - 8), 10);
|
||||
queue->Dispatch(resolver.forget());
|
||||
return nsRefPtr<TestPromise>(p);
|
||||
},
|
||||
DO_FAIL)
|
||||
->CompletionPromise()
|
||||
->Then(queue, __func__,
|
||||
[queue] (int aVal) -> nsRefPtr<TestPromise> { return TestPromise::CreateAndReject(double(aVal - 42) + 42.0, __func__); },
|
||||
DO_FAIL)
|
||||
->CompletionPromise()
|
||||
->Then(queue, __func__,
|
||||
DO_FAIL,
|
||||
[queue, &invokedPass] (double aVal) -> void { EXPECT_EQ(aVal, 42.0); EXPECT_TRUE(invokedPass); queue->BeginShutdown(); });
|
||||
});
|
||||
}
|
||||
|
||||
#undef DO_FAIL
|
@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
|
||||
'TestGMPCrossOrigin.cpp',
|
||||
'TestGMPRemoveAndDelete.cpp',
|
||||
'TestIntervalSet.cpp',
|
||||
'TestMediaPromise.cpp',
|
||||
'TestMP3Demuxer.cpp',
|
||||
'TestMP4Demuxer.cpp',
|
||||
'TestMP4Reader.cpp',
|
||||
|
@ -157,9 +157,9 @@ MediaSourceReader::RequestAudioData()
|
||||
case SOURCE_NEW:
|
||||
GetAudioReader()->ResetDecode();
|
||||
mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteAudioSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteAudioSeekAndRejectPromise));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteAudioSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteAudioSeekAndRejectPromise));
|
||||
break;
|
||||
case SOURCE_NONE:
|
||||
if (!mLastAudioTime) {
|
||||
@ -182,9 +182,9 @@ MediaSourceReader::RequestAudioData()
|
||||
void MediaSourceReader::DoAudioRequest()
|
||||
{
|
||||
mAudioRequest.Begin(GetAudioReader()->RequestAudioData()
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded));
|
||||
}
|
||||
|
||||
void
|
||||
@ -205,9 +205,9 @@ MediaSourceReader::OnAudioDecoded(AudioData* aSample)
|
||||
MSE_DEBUG("mTime=%lld < mTimeThreshold=%lld",
|
||||
ourTime, mTimeThreshold);
|
||||
mAudioRequest.Begin(GetAudioReader()->RequestAudioData()
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded));
|
||||
return;
|
||||
}
|
||||
mDropAudioBeforeThreshold = false;
|
||||
@ -275,9 +275,9 @@ MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
|
||||
if (result == SOURCE_NEW) {
|
||||
GetAudioReader()->ResetDecode();
|
||||
mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteAudioSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteAudioSeekAndRejectPromise));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteAudioSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteAudioSeekAndRejectPromise));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -330,9 +330,9 @@ MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThres
|
||||
case SOURCE_NEW:
|
||||
GetVideoReader()->ResetDecode();
|
||||
mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteVideoSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteVideoSeekAndRejectPromise));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteVideoSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteVideoSeekAndRejectPromise));
|
||||
break;
|
||||
case SOURCE_NONE:
|
||||
if (!mLastVideoTime) {
|
||||
@ -357,9 +357,9 @@ void
|
||||
MediaSourceReader::DoVideoRequest()
|
||||
{
|
||||
mVideoRequest.Begin(GetVideoReader()->RequestVideoData(mDropVideoBeforeThreshold, GetReaderVideoTime(mTimeThreshold))
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded,
|
||||
&MediaSourceReader::OnVideoNotDecoded));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded,
|
||||
&MediaSourceReader::OnVideoNotDecoded));
|
||||
}
|
||||
|
||||
void
|
||||
@ -427,9 +427,9 @@ MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
|
||||
if (result == SOURCE_NEW) {
|
||||
GetVideoReader()->ResetDecode();
|
||||
mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteVideoSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteVideoSeekAndRejectPromise));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::CompleteVideoSeekAndDoRequest,
|
||||
&MediaSourceReader::CompleteVideoSeekAndRejectPromise));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -919,9 +919,9 @@ MediaSourceReader::DoAudioSeek()
|
||||
}
|
||||
GetAudioReader()->ResetDecode();
|
||||
mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(seekTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioSeekCompleted,
|
||||
&MediaSourceReader::OnAudioSeekFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioSeekCompleted,
|
||||
&MediaSourceReader::OnAudioSeekFailed));
|
||||
MSE_DEBUG("reader=%p", GetAudioReader());
|
||||
}
|
||||
|
||||
@ -991,9 +991,9 @@ MediaSourceReader::DoVideoSeek()
|
||||
}
|
||||
GetVideoReader()->ResetDecode();
|
||||
mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(seekTime), 0)
|
||||
->RefableThen(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoSeekCompleted,
|
||||
&MediaSourceReader::OnVideoSeekFailed));
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoSeekCompleted,
|
||||
&MediaSourceReader::OnVideoSeekFailed));
|
||||
MSE_DEBUG("reader=%p", GetVideoReader());
|
||||
}
|
||||
|
||||
|
@ -240,8 +240,8 @@ private:
|
||||
nsRefPtr<TrackBuffer> mAudioTrack;
|
||||
nsRefPtr<TrackBuffer> mVideoTrack;
|
||||
|
||||
MediaPromiseConsumerHolder<AudioDataPromise> mAudioRequest;
|
||||
MediaPromiseConsumerHolder<VideoDataPromise> mVideoRequest;
|
||||
MediaPromiseRequestHolder<AudioDataPromise> mAudioRequest;
|
||||
MediaPromiseRequestHolder<VideoDataPromise> mVideoRequest;
|
||||
|
||||
MediaPromiseHolder<AudioDataPromise> mAudioPromise;
|
||||
MediaPromiseHolder<VideoDataPromise> mVideoPromise;
|
||||
@ -261,8 +261,8 @@ private:
|
||||
int64_t mLastAudioTime;
|
||||
int64_t mLastVideoTime;
|
||||
|
||||
MediaPromiseConsumerHolder<SeekPromise> mAudioSeekRequest;
|
||||
MediaPromiseConsumerHolder<SeekPromise> mVideoSeekRequest;
|
||||
MediaPromiseRequestHolder<SeekPromise> mAudioSeekRequest;
|
||||
MediaPromiseRequestHolder<SeekPromise> mVideoSeekRequest;
|
||||
MediaPromiseHolder<SeekPromise> mSeekPromise;
|
||||
|
||||
// Temporary seek information while we wait for the data
|
||||
|
@ -113,7 +113,8 @@ uint32_t ResourceQueue::EvictBefore(uint64_t aOffset, ErrorResult& aRv)
|
||||
evicted += offset;
|
||||
nsRefPtr<MediaLargeByteBuffer> data = new MediaLargeByteBuffer;
|
||||
if (!data->AppendElements(item->mData->Elements() + offset,
|
||||
item->mData->Length() - offset)) {
|
||||
item->mData->Length() - offset,
|
||||
fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return 0;
|
||||
}
|
||||
|
@ -452,9 +452,9 @@ SourceBuffer::AppendData(MediaLargeByteBuffer* aData, double aTimestampOffset,
|
||||
}
|
||||
|
||||
mPendingAppend.Begin(mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
|
||||
->RefableThen(AbstractThread::MainThread(), __func__, this,
|
||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||
&SourceBuffer::AppendDataErrored));
|
||||
->Then(AbstractThread::MainThread(), __func__, this,
|
||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||
&SourceBuffer::AppendDataErrored));
|
||||
}
|
||||
|
||||
void
|
||||
@ -566,7 +566,7 @@ SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult&
|
||||
}
|
||||
|
||||
nsRefPtr<MediaLargeByteBuffer> data = new MediaLargeByteBuffer();
|
||||
if (!data->AppendElements(aData, aLength)) {
|
||||
if (!data->AppendElements(aData, aLength, fallible)) {
|
||||
aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ private:
|
||||
// aborted and another AppendData queued.
|
||||
uint32_t mUpdateID;
|
||||
|
||||
MediaPromiseConsumerHolder<TrackBufferAppendPromise> mPendingAppend;
|
||||
MediaPromiseRequestHolder<TrackBufferAppendPromise> mPendingAppend;
|
||||
const nsCString mType;
|
||||
};
|
||||
|
||||
|
@ -679,11 +679,10 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
return;
|
||||
}
|
||||
|
||||
mMetadataRequest.Begin(promise
|
||||
->RefableThen(reader->GetTaskQueue(), __func__,
|
||||
recipient.get(),
|
||||
&MetadataRecipient::OnMetadataRead,
|
||||
&MetadataRecipient::OnMetadataNotRead));
|
||||
mMetadataRequest.Begin(promise->Then(reader->GetTaskQueue(), __func__,
|
||||
recipient.get(),
|
||||
&MetadataRecipient::OnMetadataRead,
|
||||
&MetadataRecipient::OnMetadataNotRead));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -221,7 +221,7 @@ private:
|
||||
|
||||
MediaPromiseHolder<TrackBufferAppendPromise> mInitializationPromise;
|
||||
// Track our request for metadata from the reader.
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
|
||||
MediaPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -138,6 +138,20 @@ MediaCodecProxy::AskMediaCodecAndWait()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecProxy::AsyncAskMediaCodec()
|
||||
{
|
||||
if ((strncasecmp(mCodecMime.get(), "video/", 6) != 0) ||
|
||||
(mResourceHandler == nullptr)) {
|
||||
return false;
|
||||
}
|
||||
// request video codec
|
||||
mResourceHandler->requestResource(mCodecEncoder
|
||||
? IMediaResourceManagerService::HW_VIDEO_ENCODER
|
||||
: IMediaResourceManagerService::HW_VIDEO_DECODER);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecProxy::SetMediaCodecFree()
|
||||
{
|
||||
@ -472,6 +486,11 @@ MediaCodecProxy::resourceReserved()
|
||||
releaseCodec();
|
||||
if (!allocateCodec()) {
|
||||
SetMediaCodecFree();
|
||||
// Notification
|
||||
sp<CodecResourceListener> listener = mListener.promote();
|
||||
if (listener != nullptr) {
|
||||
listener->codecCanceled();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -487,6 +506,17 @@ MediaCodecProxy::resourceReserved()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecProxy::resourceCanceled()
|
||||
{
|
||||
SetMediaCodecFree();
|
||||
// Notification
|
||||
sp<CodecResourceListener> listener = mListener.promote();
|
||||
if (listener != nullptr) {
|
||||
listener->codecCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
bool MediaCodecProxy::Prepare()
|
||||
{
|
||||
|
||||
|
@ -139,6 +139,10 @@ public:
|
||||
// allocated.
|
||||
bool AskMediaCodecAndWait();
|
||||
|
||||
// It asks for the OMX codec asynchronously.
|
||||
// Only video codec is supported.
|
||||
bool AsyncAskMediaCodec();
|
||||
|
||||
// Free the OMX codec so others can allocate it.
|
||||
void SetMediaCodecFree();
|
||||
|
||||
@ -148,7 +152,7 @@ protected:
|
||||
// MediaResourceHandler::EventListener::resourceReserved()
|
||||
virtual void resourceReserved();
|
||||
// MediaResourceHandler::EventListener::resourceCanceled()
|
||||
virtual void resourceCanceled() {}
|
||||
virtual void resourceCanceled();
|
||||
|
||||
private:
|
||||
// Forbidden
|
||||
|
@ -67,6 +67,33 @@ IsValidTimestampUs(int64_t aTimestamp)
|
||||
return aTimestamp >= INT64_C(0);
|
||||
}
|
||||
|
||||
MediaCodecReader::VideoResourceListener::VideoResourceListener(
|
||||
MediaCodecReader* aReader)
|
||||
: mReader(aReader)
|
||||
{
|
||||
}
|
||||
|
||||
MediaCodecReader::VideoResourceListener::~VideoResourceListener()
|
||||
{
|
||||
mReader = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::VideoResourceListener::codecReserved()
|
||||
{
|
||||
if (mReader) {
|
||||
mReader->VideoCodecReserved();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::VideoResourceListener::codecCanceled()
|
||||
{
|
||||
if (mReader) {
|
||||
mReader->VideoCodecCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
MediaCodecReader::TrackInputCopier::~TrackInputCopier()
|
||||
{
|
||||
}
|
||||
@ -241,7 +268,6 @@ MediaCodecReader::ProcessCachedDataTask::Run()
|
||||
MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaOmxCommonReader(aDecoder)
|
||||
, mExtractor(nullptr)
|
||||
, mIsWaitingResources(false)
|
||||
, mTextureClientIndexesLock("MediaCodecReader::mTextureClientIndexesLock")
|
||||
, mColorConverterBufferSize(0)
|
||||
, mParserMonitor("MediaCodecReader::mParserMonitor")
|
||||
@ -249,6 +275,7 @@ MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
|
||||
, mNextParserPosition(INT64_C(0))
|
||||
, mParsedDataLength(INT64_C(0))
|
||||
{
|
||||
mVideoListener = new VideoResourceListener(this);
|
||||
}
|
||||
|
||||
MediaCodecReader::~MediaCodecReader()
|
||||
@ -261,19 +288,6 @@ MediaCodecReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::IsWaitingMediaResources()
|
||||
{
|
||||
return mIsWaitingResources;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::UpdateIsWaitingMediaResources()
|
||||
{
|
||||
mIsWaitingResources = (mVideoTrack.mCodec != nullptr) &&
|
||||
(!mVideoTrack.mCodec->allocated());
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::ReleaseMediaResources()
|
||||
{
|
||||
@ -616,50 +630,66 @@ MediaCodecReader::ParseDataSegment(const char* aBuffer,
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::PreReadMetadata()
|
||||
{
|
||||
UpdateIsWaitingMediaResources();
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaCodecReader::AsyncReadMetadata()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
if (!ReallocateResources()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!ReallocateExtractorResources()) {
|
||||
return MediaDecoderReader::MetadataPromise::CreateAndReject(
|
||||
ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
}
|
||||
|
||||
bool incrementalParserNeeded =
|
||||
mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
|
||||
if (incrementalParserNeeded && !TriggerIncrementalParser()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return MediaDecoderReader::MetadataPromise::CreateAndReject(
|
||||
ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
}
|
||||
|
||||
// Bug 1050667, both MediaDecoderStateMachine and MediaCodecReader
|
||||
// relies on IsWaitingMediaResources() function. And the waiting state will be
|
||||
// changed by binder thread, so we store the waiting state in a cache value to
|
||||
// make them in the same waiting state.
|
||||
UpdateIsWaitingMediaResources();
|
||||
if (IsWaitingMediaResources()) {
|
||||
return NS_OK;
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise> p = mMetadataPromise.Ensure(__func__);
|
||||
|
||||
nsRefPtr<MediaCodecReader> self = this;
|
||||
mMediaResourceRequest.Begin(CreateMediaCodecs()
|
||||
->Then(GetTaskQueue(), __func__,
|
||||
[self] (bool) -> void {
|
||||
self->mMediaResourceRequest.Complete();
|
||||
self->HandleResourceAllocated();
|
||||
}, [self] (bool) -> void {
|
||||
self->mMediaResourceRequest.Complete();
|
||||
self->mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
}));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::HandleResourceAllocated()
|
||||
{
|
||||
// Configure video codec after the codecReserved.
|
||||
if (mVideoTrack.mSource != nullptr) {
|
||||
if (!ConfigureMediaCodec(mVideoTrack)) {
|
||||
DestroyMediaCodec(mVideoTrack);
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: start streaming
|
||||
|
||||
if (!UpdateDuration()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UpdateAudioInfo()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UpdateVideoInfo()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the total duration (the max of the audio and video track).
|
||||
@ -688,14 +718,15 @@ MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
|
||||
mozilla::TimeStamp::Now());
|
||||
}
|
||||
|
||||
*aInfo = mInfo;
|
||||
*aTags = nullptr;
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
metadata->mInfo = mInfo;
|
||||
metadata->mTags = nullptr;
|
||||
|
||||
#ifdef MOZ_AUDIO_OFFLOAD
|
||||
CheckAudioOffload();
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
mMetadataPromise.Resolve(metadata, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -1044,13 +1075,12 @@ MediaCodecReader::GetAudioOffloadTrack()
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::ReallocateResources()
|
||||
MediaCodecReader::ReallocateExtractorResources()
|
||||
{
|
||||
if (CreateLooper() &&
|
||||
CreateExtractor() &&
|
||||
CreateMediaSources() &&
|
||||
CreateTaskQueues() &&
|
||||
CreateMediaCodecs()) {
|
||||
CreateTaskQueues()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1061,6 +1091,10 @@ MediaCodecReader::ReallocateResources()
|
||||
void
|
||||
MediaCodecReader::ReleaseCriticalResources()
|
||||
{
|
||||
mMediaResourceRequest.DisconnectIfExists();
|
||||
mMediaResourcePromise.RejectIfExists(true, __func__);
|
||||
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
|
||||
ResetDecode();
|
||||
// Before freeing a video codec, all video buffers needed to be released
|
||||
// even from graphics pipeline.
|
||||
@ -1256,20 +1290,35 @@ MediaCodecReader::CreateTaskQueues()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsRefPtr<MediaOmxCommonReader::MediaResourcePromise>
|
||||
MediaCodecReader::CreateMediaCodecs()
|
||||
{
|
||||
if (CreateMediaCodec(mLooper, mAudioTrack, nullptr) &&
|
||||
CreateMediaCodec(mLooper, mVideoTrack, nullptr)) {
|
||||
return true;
|
||||
bool isWaiting = false;
|
||||
nsRefPtr<MediaResourcePromise> p = mMediaResourcePromise.Ensure(__func__);
|
||||
|
||||
if (!CreateMediaCodec(mLooper, mAudioTrack, false, isWaiting, nullptr)) {
|
||||
mMediaResourcePromise.Reject(true, __func__);
|
||||
return p;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!CreateMediaCodec(mLooper, mVideoTrack, true, isWaiting, mVideoListener)) {
|
||||
mMediaResourcePromise.Reject(true, __func__);
|
||||
return p;
|
||||
}
|
||||
|
||||
if (!isWaiting) {
|
||||
// No MediaCodec allocation wait.
|
||||
mMediaResourcePromise.Resolve(true, __func__);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
|
||||
Track& aTrack,
|
||||
bool aAsync,
|
||||
bool& aIsWaiting,
|
||||
wp<MediaCodecProxy::CodecResourceListener> aListener)
|
||||
{
|
||||
if (aTrack.mSource != nullptr && aTrack.mCodec == nullptr) {
|
||||
@ -1284,10 +1333,6 @@ MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
|
||||
NS_WARNING("Couldn't create MediaCodecProxy");
|
||||
return false;
|
||||
}
|
||||
if (!aTrack.mCodec->AskMediaCodecAndWait()) {
|
||||
NS_WARNING("AskMediaCodecAndWait fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
|
||||
aTrack.mInputCopier = new VorbisInputCopier;
|
||||
@ -1310,10 +1355,22 @@ MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)) {
|
||||
NS_WARNING("Couldn't create and configure MediaCodec synchronously");
|
||||
DestroyMediaCodec(aTrack);
|
||||
return false;
|
||||
if (!aAsync && aTrack.mCodec->AskMediaCodecAndWait()) {
|
||||
// Pending configure() and start() to codecReserved() if the creation
|
||||
// should be asynchronous.
|
||||
if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)){
|
||||
NS_WARNING("Couldn't create and configure MediaCodec synchronously");
|
||||
DestroyMediaCodec(aTrack);
|
||||
return false;
|
||||
}
|
||||
} else if (aAsync) {
|
||||
if (aTrack.mCodec->AsyncAskMediaCodec()) {
|
||||
aIsWaiting = true;
|
||||
} else {
|
||||
NS_WARNING("Couldn't request MediaCodec asynchronously");
|
||||
DestroyMediaCodec(aTrack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1843,8 +1900,7 @@ MediaCodecReader::EnsureCodecFormatParsed(Track& aTrack)
|
||||
NS_WARNING("Couldn't get output buffers from MediaCodec");
|
||||
return false;
|
||||
}
|
||||
} else if (status != -EAGAIN && status != INVALID_OPERATION) {
|
||||
// FIXME: let INVALID_OPERATION pass?
|
||||
} else if (status != -EAGAIN) {
|
||||
return false; // something wrong!!!
|
||||
}
|
||||
FillCodecInputData(aTrack);
|
||||
@ -1875,4 +1931,18 @@ MediaCodecReader::ClearColorConverterBuffer()
|
||||
mColorConverterBufferSize = 0;
|
||||
}
|
||||
|
||||
// Called on Binder thread.
|
||||
void
|
||||
MediaCodecReader::VideoCodecReserved()
|
||||
{
|
||||
mMediaResourcePromise.ResolveIfExists(true, __func__);
|
||||
}
|
||||
|
||||
// Called on Binder thread.
|
||||
void
|
||||
MediaCodecReader::VideoCodecCanceled()
|
||||
{
|
||||
mMediaResourcePromise.RejectIfExists(true, __func__);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -52,6 +52,7 @@ class MediaCodecReader : public MediaOmxCommonReader
|
||||
{
|
||||
typedef mozilla::layers::TextureClient TextureClient;
|
||||
typedef mozilla::layers::FenceHandle FenceHandle;
|
||||
typedef MediaOmxCommonReader::MediaResourcePromise MediaResourcePromise;
|
||||
|
||||
public:
|
||||
MediaCodecReader(AbstractMediaDecoder* aDecoder);
|
||||
@ -61,9 +62,6 @@ public:
|
||||
// on failure.
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
|
||||
|
||||
// True if this reader is waiting media resource allocation
|
||||
virtual bool IsWaitingMediaResources();
|
||||
|
||||
// True when this reader need to become dormant state
|
||||
virtual bool IsDormantNeeded() { return true;}
|
||||
|
||||
@ -94,13 +92,7 @@ public:
|
||||
virtual bool HasAudio();
|
||||
virtual bool HasVideo();
|
||||
|
||||
virtual void PreReadMetadata() override;
|
||||
// Read header data for all bitstreams in the file. Fills aInfo with
|
||||
// the data required to present the media, and optionally fills *aTags
|
||||
// with tag metadata from the file.
|
||||
// Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags);
|
||||
virtual nsRefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
|
||||
|
||||
// Moves the decode head to aTime microseconds. aStartTime and aEndTime
|
||||
// denote the start and end times of the media in usecs, and aCurrentTime
|
||||
@ -177,18 +169,45 @@ protected:
|
||||
// Called on MediaCodecReader::mLooper thread.
|
||||
void onMessageReceived(const android::sp<android::AMessage>& aMessage);
|
||||
|
||||
// Receive a notify from ResourceListener.
|
||||
// Called on Binder thread.
|
||||
virtual void VideoCodecReserved();
|
||||
virtual void VideoCodecCanceled();
|
||||
|
||||
virtual bool CreateExtractor();
|
||||
|
||||
// Check the underlying HW resource is available and store the result in
|
||||
// mIsWaitingResources.
|
||||
void UpdateIsWaitingMediaResources();
|
||||
virtual void HandleResourceAllocated();
|
||||
|
||||
android::sp<android::MediaExtractor> mExtractor;
|
||||
// A cache value updated by UpdateIsWaitingMediaResources(), makes the
|
||||
// "waiting resources state" is synchronous to StateMachine.
|
||||
bool mIsWaitingResources;
|
||||
|
||||
MediaPromiseHolder<MediaDecoderReader::MetadataPromise> mMetadataPromise;
|
||||
// XXX Remove after bug 1168008 land.
|
||||
MediaPromiseRequestHolder<MediaResourcePromise> mMediaResourceRequest;
|
||||
MediaPromiseHolder<MediaResourcePromise> mMediaResourcePromise;
|
||||
|
||||
private:
|
||||
|
||||
// An intermediary class that can be managed by android::sp<T>.
|
||||
// Redirect codecReserved() and codecCanceled() to MediaCodecReader.
|
||||
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
|
||||
{
|
||||
public:
|
||||
VideoResourceListener(MediaCodecReader* aReader);
|
||||
~VideoResourceListener();
|
||||
|
||||
virtual void codecReserved();
|
||||
virtual void codecCanceled();
|
||||
|
||||
private:
|
||||
// Forbidden
|
||||
VideoResourceListener() = delete;
|
||||
VideoResourceListener(const VideoResourceListener& rhs) = delete;
|
||||
const VideoResourceListener& operator=(const VideoResourceListener& rhs) = delete;
|
||||
|
||||
MediaCodecReader* mReader;
|
||||
};
|
||||
friend class VideoResourceListener;
|
||||
|
||||
class VorbisInputCopier : public TrackInputCopier
|
||||
{
|
||||
virtual bool Copy(android::MediaBuffer* aSourceBuffer,
|
||||
@ -312,7 +331,7 @@ private:
|
||||
MediaCodecReader() = delete;
|
||||
const MediaCodecReader& operator=(const MediaCodecReader& rhs) = delete;
|
||||
|
||||
bool ReallocateResources();
|
||||
bool ReallocateExtractorResources();
|
||||
void ReleaseCriticalResources();
|
||||
void ReleaseResources();
|
||||
|
||||
@ -324,9 +343,11 @@ private:
|
||||
bool CreateMediaSources();
|
||||
void DestroyMediaSources();
|
||||
|
||||
bool CreateMediaCodecs();
|
||||
nsRefPtr<MediaResourcePromise> CreateMediaCodecs();
|
||||
static bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
|
||||
Track& aTrack,
|
||||
bool aAsync,
|
||||
bool& aIsWaiting,
|
||||
android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
|
||||
static bool ConfigureMediaCodec(Track& aTrack);
|
||||
void DestroyMediaCodecs();
|
||||
@ -387,6 +408,8 @@ private:
|
||||
|
||||
void ReleaseAllTextureClients();
|
||||
|
||||
android::sp<VideoResourceListener> mVideoListener;
|
||||
|
||||
android::sp<android::ALooper> mLooper;
|
||||
android::sp<android::MetaData> mMetaData;
|
||||
|
||||
|
@ -93,8 +93,8 @@ MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
MediaDecoderEventVisibility::Observable);
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
mSeekRequest.Begin(mAudioOffloadPlayer->Seek(target)
|
||||
->RefableThen(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
->Then(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
}
|
||||
// Call ChangeState() to run AudioOffloadPlayer since offload state enabled
|
||||
ChangeState(mPlayState);
|
||||
@ -232,8 +232,8 @@ MediaOmxCommonDecoder::CallSeek(const SeekTarget& aTarget)
|
||||
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
mSeekRequest.Begin(mAudioOffloadPlayer->Seek(aTarget)
|
||||
->RefableThen(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
->Then(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
}
|
||||
|
||||
int64_t
|
||||
|
@ -251,7 +251,7 @@ MediaOmxReader::AsyncReadMetadata()
|
||||
|
||||
nsRefPtr<MediaOmxReader> self = this;
|
||||
mMediaResourceRequest.Begin(mOmxDecoder->AllocateMediaResources()
|
||||
->RefableThen(GetTaskQueue(), __func__,
|
||||
->Then(GetTaskQueue(), __func__,
|
||||
[self] (bool) -> void {
|
||||
self->mMediaResourceRequest.Complete();
|
||||
self->HandleResourceAllocated();
|
||||
|
@ -45,7 +45,7 @@ class MediaOmxReader : public MediaOmxCommonReader
|
||||
// AbstractMediaDecoder::mDecoder will be non-null.
|
||||
bool mIsShutdown;
|
||||
MediaPromiseHolder<MediaDecoderReader::MetadataPromise> mMetadataPromise;
|
||||
MediaPromiseConsumerHolder<MediaResourcePromise> mMediaResourceRequest;
|
||||
MediaPromiseRequestHolder<MediaResourcePromise> mMediaResourceRequest;
|
||||
protected:
|
||||
android::sp<android::OmxDecoder> mOmxDecoder;
|
||||
android::sp<android::MediaExtractor> mExtractor;
|
||||
|
@ -88,20 +88,28 @@ RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
return MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
|
||||
}
|
||||
|
||||
nsresult
|
||||
RtspMediaCodecReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise>
|
||||
RtspMediaCodecReader::AsyncReadMetadata()
|
||||
{
|
||||
mRtspResource->DisablePlayoutDelay();
|
||||
EnsureActive();
|
||||
nsresult rv = MediaCodecReader::ReadMetadata(aInfo, aTags);
|
||||
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise> p =
|
||||
MediaCodecReader::AsyncReadMetadata();
|
||||
|
||||
// Send a PAUSE to the RTSP server because the underlying media resource is
|
||||
// not ready.
|
||||
SetIdle();
|
||||
|
||||
if (rv == NS_OK && !IsWaitingMediaResources()) {
|
||||
mRtspResource->EnablePlayoutDelay();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
return rv;
|
||||
void
|
||||
RtspMediaCodecReader::HandleResourceAllocated()
|
||||
{
|
||||
EnsureActive();
|
||||
MediaCodecReader::HandleResourceAllocated();
|
||||
mRtspResource->EnablePlayoutDelay();;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -58,8 +58,10 @@ public:
|
||||
// Disptach a DecodeAudioDataTask to decode audio data.
|
||||
virtual nsRefPtr<AudioDataPromise> RequestAudioData() override;
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
virtual nsRefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata()
|
||||
override;
|
||||
|
||||
virtual void HandleResourceAllocated() override;
|
||||
|
||||
private:
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user