Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-05-29 09:32:46 -04:00
commit 19dc73f937
739 changed files with 97065 additions and 9157 deletions

View File

@ -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

View File

@ -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");
},

View File

@ -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]

View File

@ -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);
});
}

View File

@ -264,8 +264,7 @@ public class SUTAgentAndroid extends Activity
{
if (ba != null)
{
sUniqueID = ba.getAddress();
sUniqueID.toLowerCase();
sUniqueID = ba.getAddress().toLowerCase();
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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()

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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

View 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>

View File

@ -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);

View File

@ -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'

View File

@ -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
View 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
View 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
View File

@ -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
View File

@ -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,

View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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]

View 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>

View File

@ -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]);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -563,16 +563,18 @@ private:
// Perform the encryption/decryption
if (mEncrypt) {
rv = MapSECStatus(PK11_Encrypt(symKey.get(), mMechanism, &param,
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, &param,
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;
}
}

View File

@ -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" +

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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">

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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()

View File

@ -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

View File

@ -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__);
}

View File

@ -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.

View 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;

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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 {

View File

@ -12,6 +12,7 @@
#include "GMPVideoEncoderParent.h"
#include "mozIGeckoMediaPluginService.h"
#include "mozilla/Logging.h"
#include "mozilla/unused.h"
namespace mozilla {

View File

@ -7,6 +7,7 @@
#define GMPContentParent_h_
#include "mozilla/gmp/PGMPContentParent.h"
#include "GMPSharedMemManager.h"
#include "nsISupportsImpl.h"
namespace mozilla {

View File

@ -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"

View File

@ -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()

View File

@ -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"

View File

@ -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"

View File

@ -15,6 +15,7 @@
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
template <class> struct already_AddRefed;

View File

@ -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:

View File

@ -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
{

View File

@ -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"

View File

@ -8,6 +8,7 @@
#include "nsDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsCOMPtr.h"
#include "nsLiteralString.h"
namespace mozilla {

View File

@ -8,6 +8,8 @@
#include "mozilla/UniquePtr.h"
class nsIFile;
namespace mozilla {
template<typename T>

View 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

View File

@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
'TestGMPCrossOrigin.cpp',
'TestGMPRemoveAndDelete.cpp',
'TestIntervalSet.cpp',
'TestMediaPromise.cpp',
'TestMP3Demuxer.cpp',
'TestMP4Demuxer.cpp',
'TestMP4Reader.cpp',

View File

@ -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());
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -186,7 +186,7 @@ private:
// aborted and another AppendData queued.
uint32_t mUpdateID;
MediaPromiseConsumerHolder<TrackBufferAppendPromise> mPendingAppend;
MediaPromiseRequestHolder<TrackBufferAppendPromise> mPendingAppend;
const nsCString mType;
};

View File

@ -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

View File

@ -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

View File

@ -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()
{

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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