--- a/b2g/chrome/content/content.css
+++ b/b2g/chrome/content/content.css
@@ -173,18 +173,16 @@ input[type="submit"],
input[type="reset"],
button {
border-width: 1px;
padding: 0 7px 0 7px;
}
input[type="radio"],
input[type="checkbox"] {
- max-width: 14px;
- max-height: 14px;
border: 1px solid #a7a7a7 !important;
padding: 2px 1px 2px 1px;
}
select > button {
border-width: 0px !important;
margin: 0px !important;
padding: 0px !important;
--- a/browser/base/content/test/general/browser_notification_tab_switching.js
+++ b/browser/base/content/test/general/browser_notification_tab_switching.js
@@ -1,58 +1,95 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-"use strict";
-
-let tab;
-let notification;
-let notificationURL = "http://example.org/browser/browser/base/content/test/general/file_dom_notifications.html";
-
-function test () {
- waitForExplicitFinish();
-
- let pm = Services.perms;
- registerCleanupFunction(function() {
- pm.remove(notificationURL, "desktop-notification");
- gBrowser.removeTab(tab);
- window.restore();
- });
-
- pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
-
- tab = gBrowser.addTab(notificationURL);
- tab.linkedBrowser.addEventListener("load", onLoad, true);
-}
-
-function onLoad() {
- isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
- tab.linkedBrowser.removeEventListener("load", onLoad, true);
- let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
- notification = win.showNotification();
- notification.addEventListener("show", onAlertShowing);
-}
-
-function onAlertShowing() {
- info("Notification alert showing");
- notification.removeEventListener("show", onAlertShowing);
-
- let alertWindow = findChromeWindowByURI("chrome://global/content/alerts/alert.xul");
- if (!alertWindow) {
- todo(false, "Notifications don't use XUL windows on all platforms.");
- notification.close();
- finish();
- return;
- }
- gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect);
- EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow);
- info("Clicked on notification");
- alertWindow.close();
-}
-
-function onTabSelect() {
- gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect);
- is(gBrowser.selectedTab.linkedBrowser.contentWindow.location.href, notificationURL,
- "Notification tab should be selected.");
-
- finish();
-}
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+let tab;
+let notification;
+let notificationURL = "http://example.org/browser/browser/base/content/test/general/file_dom_notifications.html";
+let newWindowOpenedFromTab;
+
+function test () {
+ waitForExplicitFinish();
+
+ let pm = Services.perms;
+ registerCleanupFunction(function() {
+ pm.remove(notificationURL, "desktop-notification");
+ gBrowser.removeTab(tab);
+ window.restore();
+ });
+
+ pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
+
+ tab = gBrowser.addTab(notificationURL);
+ tab.linkedBrowser.addEventListener("load", onLoad, true);
+}
+
+function onLoad() {
+ isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+ tab.linkedBrowser.removeEventListener("load", onLoad, true);
+ let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+ win.newWindow = win.open("about:blank", "", "height=100,width=100");
+ newWindowOpenedFromTab = win.newWindow;
+ win.newWindow.addEventListener("load", function() {
+ info("new window loaded");
+ win.newWindow.addEventListener("blur", function b() {
+ info("new window got blur");
+ win.newWindow.removeEventListener("blur", b);
+ notification = win.showNotification1();
+ win.newWindow.addEventListener("focus", onNewWindowFocused);
+ notification.addEventListener("show", onAlertShowing);
+ });
+
+ function waitUntilNewWindowHasFocus() {
+ if (!win.newWindow.document.hasFocus()) {
+ setTimeout(waitUntilNewWindowHasFocus, 50);
+ } else {
+ // Focus another window so that new window gets blur event.
+ gBrowser.selectedTab.linkedBrowser.contentWindow.focus();
+ }
+ }
+ win.newWindow.focus();
+ waitUntilNewWindowHasFocus();
+ });
+}
+
+function onAlertShowing() {
+ info("Notification alert showing");
+ notification.removeEventListener("show", onAlertShowing);
+
+ let alertWindow = findChromeWindowByURI("chrome://global/content/alerts/alert.xul");
+ if (!alertWindow) {
+ todo(false, "Notifications don't use XUL windows on all platforms.");
+ notification.close();
+ newWindowOpenedFromTab.close();
+ finish();
+ return;
+ }
+ gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect);
+ EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow);
+ info("Clicked on notification");
+ alertWindow.close();
+}
+
+function onNewWindowFocused(event) {
+ event.target.close();
+ isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+ // Using timeout to test that something do *not* happen!
+ setTimeout(openSecondNotification, 50);
+}
+
+function openSecondNotification() {
+ isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+ let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+ notification = win.showNotification2();
+ notification.addEventListener("show", onAlertShowing);
+}
+
+function onTabSelect() {
+ gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect);
+ is(gBrowser.selectedTab.linkedBrowser.contentWindow.location.href, notificationURL,
+ "Notification tab should be selected.");
+
+ finish();
+}
--- a/browser/base/content/test/general/file_dom_notifications.html
+++ b/browser/base/content/test/general/file_dom_notifications.html
@@ -1,23 +1,40 @@
-<html>
-<head>
-<script>
-"use strict";
-
-function showNotification() {
- var options = {
- dir: undefined,
- lang: undefined,
- body: "Test body",
- tag: "Test tag",
- icon: undefined,
- };
- return new Notification("Test title", options);
-}
-</script>
-</head>
-<body>
-<form id="notificationForm" onsubmit="showNotification();">
- <input type="submit" value="Show notification" id="submit"/>
-</form>
-</body>
-</html>
+<html>
+<head>
+<script>
+"use strict";
+
+function showNotification1() {
+ var options = {
+ dir: undefined,
+ lang: undefined,
+ body: "Test body",
+ tag: "Test tag",
+ icon: undefined,
+ };
+ var n = new Notification("Test title", options);
+ n.addEventListener("click", function(event) {
+ event.preventDefault();
+ dump("Should focus new window.");
+ newWindow.focus();
+ });
+ return n;
+}
+
+function showNotification2() {
+ var options = {
+ dir: undefined,
+ lang: undefined,
+ body: "Test body",
+ tag: "Test tag",
+ icon: undefined,
+ };
+ return new Notification("Test title", options);
+}
+</script>
+</head>
+<body>
+<form id="notificationForm" onsubmit="showNotification();">
+ <input type="submit" value="Show notification" id="submit"/>
+</form>
+</body>
+</html>
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -82,16 +82,17 @@ CERT_EncodePolicyMappingExtension
CERT_EncodeSubjectKeyID
CERT_EncodeUserNotice
CERT_ExtractPublicKey
CERT_FilterCertListByCANames
CERT_FilterCertListByUsage
CERT_FilterCertListForUserCerts
CERT_FindCertByDERCert
CERT_FindCertByIssuerAndSN
+CERT_FindCertByName
CERT_FindCertByNickname
CERT_FindCertByNicknameOrEmailAddr
CERT_FindCertExtension
CERT_FindCertIssuer
CERT_FindKeyUsageExtension
CERT_FindUserCertByUsage
CERT_FindUserCertsByUsage
CERT_FinishCertificateRequestAttributes
@@ -154,16 +155,18 @@ DER_EncodeTimeChoice_Util
DER_Encode_Util
DER_GeneralizedTimeToTime
DER_GeneralizedTimeToTime_Util
DER_GetInteger
DER_GetInteger_Util
DER_Lengths
DER_SetUInteger
DER_UTCTimeToTime_Util
+DSAU_DecodeDerSigToLen
+DSAU_EncodeDerSigWithLen
DTLS_GetHandshakeTimeout
DTLS_ImportFD
HASH_Begin
HASH_Create
HASH_Destroy
HASH_End
HASH_GetHashObject
HASH_GetType
@@ -302,16 +305,17 @@ PK11_DeleteTokenCertAndKey
PK11_DeleteTokenPrivateKey
PK11_DeleteTokenPublicKey
PK11_DEREncodePublicKey
PK11_Derive
PK11_DeriveWithTemplate
PK11_DestroyContext
PK11_DestroyGenericObject
PK11_DestroyMergeLog
+PK11_DestroyObject
PK11_DestroyTokenObject
PK11_DigestBegin
PK11_DigestFinal
PK11_DigestOp
PK11_DoesMechanism
PK11_Encrypt
PK11_ExportDERPrivateKeyInfo
PK11_ExtractKeyValue
@@ -363,16 +367,17 @@ PK11_GetSlotSeries
PK11_GetTokenInfo
PK11_GetTokenName
PK11_HashBuf
PK11_HasRootCerts
PK11_ImportCert
PK11_ImportCertForKey
PK11_ImportCRL
PK11_ImportDERPrivateKeyInfoAndReturnKey
+PK11_ImportPublicKey
PK11_ImportSymKey
PK11_InitPin
PK11_IsDisabled
PK11_IsFIPS
PK11_IsFriendly
PK11_IsHW
PK11_IsInternal
PK11_IsLoggedIn
@@ -627,16 +632,17 @@ SGN_NewContext
SGN_Update
SSL_AuthCertificateComplete
SSL_AuthCertificateHook
SSL_CipherPrefGet
SSL_CipherPrefSet
SSL_CipherPrefSetDefault
SSL_ClearSessionCache
SSL_ConfigSecureServer
+SSL_ConfigSecureServerWithCertChain
SSL_ConfigServerSessionIDCache
SSL_ExportKeyingMaterial
SSL_ForceHandshake
SSL_GetChannelInfo
SSL_GetCipherSuiteInfo
SSL_GetClientAuthDataHook
SSL_GetImplementedCiphers
SSL_GetNextProto
@@ -665,11 +671,12 @@ SSL_VersionRangeGet
SSL_VersionRangeSet
SSL_VersionRangeSetDefault
UTIL_SetForkState
VFY_Begin
VFY_CreateContext
VFY_DestroyContext
VFY_End
VFY_Update
+VFY_VerifyData
VFY_VerifyDataDirect
VFY_VerifyDataWithAlgorithmID
_SGN_VerifyPKCS1DigestInfo
--- a/configure.in
+++ b/configure.in
@@ -7236,19 +7236,16 @@ AC_SUBST_LIST(WIN32_CRT_LIBS)
dnl Need to set this for make because NSS doesn't have configure
AC_SUBST(DLLFLAGS)
dnl We need to wrap dlopen and related functions on Android because we use
dnl our own linker.
if test "$OS_TARGET" = Android; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -L$_objdir/dist/lib -lmozglue"
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
- if test -z "$gonkdir"; then
- WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=memccpy,--wrap=memchr,--wrap=memrchr,--wrap=memcmp,--wrap=memcpy,--wrap=memmove,--wrap=memset,--wrap=memmem,--wrap=index,--wrap=strchr,--wrap=strrchr,--wrap=strlen,--wrap=strcmp,--wrap=strcpy,--wrap=strcat,--wrap=strcasecmp,--wrap=strncasecmp,--wrap=strstr,--wrap=strcasestr,--wrap=strtok,--wrap=strtok_r,--wrap=strerror,--wrap=strerror_r,--wrap=strnlen,--wrap=strncat,--wrap=strncmp,--wrap=strncpy,--wrap=strlcat,--wrap=strlcpy,--wrap=strcspn,--wrap=strpbrk,--wrap=strsep,--wrap=strspn,--wrap=strcoll,--wrap=strxfrm"
- fi
if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill"
fi
fi
dnl ========================================================
dnl = Use JS Call tracing
dnl ========================================================
--- a/content/base/src/DOMMatrix.cpp
+++ b/content/base/src/DOMMatrix.cpp
@@ -430,17 +430,17 @@ DOMMatrix::TranslateSelf(double aTx,
double aTz)
{
if (aTx == 0 && aTy == 0 && aTz == 0) {
return this;
}
if (mMatrix3D || aTz != 0) {
Ensure3DMatrix();
- mMatrix3D->Translate(aTx, aTy, aTz);
+ mMatrix3D->PreTranslate(aTx, aTy, aTz);
} else {
mMatrix2D->PreTranslate(aTx, aTy);
}
return this;
}
DOMMatrix*
--- a/content/base/src/EventSource.cpp
+++ b/content/base/src/EventSource.cpp
@@ -22,17 +22,16 @@
#include "nsIStringBundle.h"
#include "nsIConsoleService.h"
#include "nsIObserverService.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsJSUtils.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIScriptError.h"
#include "mozilla/dom/EncodingUtils.h"
-#include "nsIChannelPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsContentUtils.h"
#include "mozilla/Preferences.h"
#include "xpcpublic.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/Attributes.h"
#include "nsError.h"
@@ -732,51 +731,39 @@ EventSource::InitChannelAndRequestEventS
if (!CheckCanRequestSrc()) {
DispatchFailConnection();
return NS_ERROR_DOM_SECURITY_ERR;
}
nsLoadFlags loadFlags;
loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE;
- // get Content Security Policy from principal to pass into channel
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- nsresult rv = mPrincipal->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_DATAREQUEST);
- }
-
+ nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
nsCOMPtr<nsIDocument> doc =
nsContentUtils::GetDocumentFromScriptContext(sc);
nsCOMPtr<nsIChannel> channel;
// If we have the document, use it
if (doc) {
rv = NS_NewChannel(getter_AddRefs(channel),
mSrc,
doc,
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
nsIContentPolicy::TYPE_DATAREQUEST,
- channelPolicy, // aChannelPolicy
mLoadGroup, // loadGroup
nullptr, // aCallbacks
loadFlags); // aLoadFlags
} else {
// otherwise use the principal
rv = NS_NewChannel(getter_AddRefs(channel),
mSrc,
mPrincipal,
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
nsIContentPolicy::TYPE_DATAREQUEST,
- channelPolicy, // aChannelPolicy
mLoadGroup, // loadGroup
nullptr, // aCallbacks
loadFlags); // aLoadFlags
}
NS_ENSURE_SUCCESS(rv, rv);
mHttpChannel = do_QueryInterface(channel);
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -7,17 +7,16 @@
#include "ImportManager.h"
#include "mozilla/EventListenerManager.h"
#include "HTMLLinkElement.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsIChannel.h"
-#include "nsIChannelPolicy.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMEvent.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsScriptLoader.h"
@@ -480,33 +479,22 @@ ImportLoader::Open()
}
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
rv = secMan->CheckLoadURIWithPrincipal(principal, mURI,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsILoadGroup> loadGroup = master->GetDocumentLoadGroup();
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = principal->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS_VOID(rv);
-
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
- }
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
mURI,
mImportParent,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SUBDOCUMENT,
- channelPolicy,
loadGroup,
nullptr, // aCallbacks
nsIRequest::LOAD_BACKGROUND);
NS_ENSURE_SUCCESS_VOID(rv);
// Init CORSListenerProxy and omit credentials.
nsRefPtr<nsCORSListenerProxy> corsListener =
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -107,17 +107,16 @@ UNIFIED_SOURCES += [
'Link.cpp',
'MultipartFileImpl.cpp',
'NodeIterator.cpp',
'nsAtomListUtils.cpp',
'nsAttrAndChildArray.cpp',
'nsAttrValue.cpp',
'nsAttrValueOrString.cpp',
'nsCCUncollectableMarker.cpp',
- 'nsChannelPolicy.cpp',
'nsContentAreaDragDrop.cpp',
'nsContentIterator.cpp',
'nsContentList.cpp',
'nsContentPolicy.cpp',
'nsContentSink.cpp',
'nsCopySupport.cpp',
'nsCrossSiteListenerProxy.cpp',
'nsCSPContext.cpp',
--- a/content/base/src/nsCSPContext.cpp
+++ b/content/base/src/nsCSPContext.cpp
@@ -6,36 +6,33 @@
#include "nsCOMPtr.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
#include "nsCSPContext.h"
#include "nsCSPParser.h"
#include "nsCSPService.h"
#include "nsError.h"
#include "nsIAsyncVerifyRedirectCallback.h"
-#include "nsIChannelPolicy.h"
#include "nsIClassInfoImpl.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMNode.h"
#include "nsIHttpChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
-#include "nsIPropertyBag2.h"
#include "nsIStringStream.h"
#include "nsIUploadChannel.h"
#include "nsIScriptError.h"
#include "nsIWebNavigation.h"
-#include "nsIWritablePropertyBag2.h"
#include "nsNetUtil.h"
#include "nsNullPrincipal.h"
#include "nsIContentPolicy.h"
#include "nsSupportsPrimitives.h"
#include "nsThreadUtils.h"
#include "nsString.h"
#include "prlog.h"
#include "mozilla/dom/CSPReportBinding.h"
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -7,27 +7,23 @@
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIURI.h"
#include "nsIPrincipal.h"
#include "nsIObserver.h"
#include "nsIContent.h"
#include "nsCSPService.h"
#include "nsIContentSecurityPolicy.h"
-#include "nsIChannelPolicy.h"
-#include "nsIChannelEventSink.h"
-#include "nsIPropertyBag2.h"
-#include "nsIWritablePropertyBag2.h"
#include "nsError.h"
-#include "nsChannelProperties.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "mozilla/Preferences.h"
#include "nsIScriptError.h"
#include "nsContentUtils.h"
+#include "nsContentPolicyUtils.h"
#include "nsPrincipal.h"
using namespace mozilla;
/* Keeps track of whether or not CSP is enabled */
bool CSPService::sCSPEnabled = true;
#ifdef PR_LOGGING
@@ -231,63 +227,66 @@ CSPService::ShouldProcess(uint32_t
NS_IMETHODIMP
CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
nsIChannel *newChannel,
uint32_t flags,
nsIAsyncVerifyRedirectCallback *callback)
{
nsAsyncRedirectAutoCallback autoCallback(callback);
- // get the Content Security Policy and load type from the property bag
- nsCOMPtr<nsISupports> policyContainer;
- nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
- if (!props)
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ nsresult rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+
+ // if no loadInfo on the channel, nothing for us to do
+ if (!loadInfo) {
return NS_OK;
+ }
- props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
- NS_GET_IID(nsISupports),
- getter_AddRefs(policyContainer));
+ // The loadInfo must not necessarily contain a Node, hence we try to query
+ // the CSP in the following order:
+ // a) Get the Node, the Principal of that Node, and the CSP of that Principal
+ // b) Get the Principal and the CSP of that Principal
- // see if we have a valid nsIChannelPolicy containing CSP and load type
- nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
- if (!channelPolicy)
- return NS_OK;
-
- nsCOMPtr<nsISupports> supports;
+ nsCOMPtr<nsINode> loadingNode = loadInfo->LoadingNode();
+ nsCOMPtr<nsIPrincipal> principal = loadingNode ?
+ loadingNode->NodePrincipal() :
+ loadInfo->LoadingPrincipal();
+ NS_ASSERTION(principal, "Can not evaluate CSP without a principal");
nsCOMPtr<nsIContentSecurityPolicy> csp;
- channelPolicy->GetContentSecurityPolicy(getter_AddRefs(supports));
- csp = do_QueryInterface(supports);
- uint32_t loadType;
- channelPolicy->GetLoadType(&loadType);
+ rv = principal->GetCsp(getter_AddRefs(csp));
+ NS_ENSURE_SUCCESS(rv, rv);
- // if no CSP in the channelPolicy, nothing for us to add to the channel
- if (!csp)
+ // if there is no CSP, nothing for us to do
+ if (!csp) {
return NS_OK;
+ }
/* Since redirecting channels don't call into nsIContentPolicy, we call our
- * Content Policy implementation directly when redirects occur. When channels
- * are created using NS_NewChannel(), callers can optionally pass in a
- * nsIChannelPolicy containing a CSP object and load type, which is placed in
- * the new channel's property bag. This container is propagated forward when
- * channels redirect.
+ * Content Policy implementation directly when redirects occur using the
+ * information set in the LoadInfo when channels are created.
+ *
+ * We check if the CSP permits this host for this type of load, if not,
+ * we cancel the load now.
*/
- // Does the CSP permit this host for this type of load?
- // If not, cancel the load now.
nsCOMPtr<nsIURI> newUri;
- newChannel->GetURI(getter_AddRefs(newUri));
+ rv = newChannel->GetURI(getter_AddRefs(newUri));
+ NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> originalUri;
- oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
+ rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsContentPolicyType policyType = loadInfo->GetContentPolicyType();
+
int16_t aDecision = nsIContentPolicy::ACCEPT;
- csp->ShouldLoad(loadType, // load type per nsIContentPolicy (uint32_t)
- newUri, // nsIURI
- nullptr, // nsIURI
- nullptr, // nsISupports
- EmptyCString(), // ACString - MIME guess
- originalUri, // nsISupports - extra
+ csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t)
+ newUri, // nsIURI
+ nullptr, // nsIURI
+ nullptr, // nsISupports
+ EmptyCString(), // ACString - MIME guess
+ originalUri, // aMimeTypeGuess
&aDecision);
#ifdef PR_LOGGING
if (newUri) {
nsAutoCString newUriSpec("None");
newUri->GetSpec(newUriSpec);
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
("CSPService::AsyncOnChannelRedirect called for %s",
@@ -297,41 +296,14 @@ CSPService::AsyncOnChannelRedirect(nsICh
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
("CSPService::AsyncOnChannelRedirect ALLOWING request."));
else
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
("CSPService::AsyncOnChannelRedirect CANCELLING request."));
#endif
// if ShouldLoad doesn't accept the load, cancel the request
- if (aDecision != 1) {
+ if (!NS_CP_ACCEPTED(aDecision)) {
autoCallback.DontCallback();
return NS_BINDING_FAILED;
}
-
- // the redirect is permitted, so propagate the Content Security Policy
- // and load type to the redirecting channel
- nsresult rv;
- nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
- if (props2) {
- rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
- channelPolicy);
- if (NS_SUCCEEDED(rv)) {
- return NS_OK;
- }
- }
-
- // The redirecting channel isn't a writable property bag, we won't be able
- // to enforce the load policy if it redirects again, so we stop it now.
- nsAutoCString newUriSpec;
- rv = newUri->GetSpec(newUriSpec);
- NS_ConvertUTF8toUTF16 unicodeSpec(newUriSpec);
- const char16_t *formatParams[] = { unicodeSpec.get() };
- if (NS_SUCCEEDED(rv)) {
- nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("Redirect Error"), nullptr,
- nsContentUtils::eDOM_PROPERTIES,
- "InvalidRedirectChannelWarning",
- formatParams, 1);
- }
-
- return NS_BINDING_FAILED;
+ return NS_OK;
}
deleted file mode 100644
--- a/content/base/src/nsChannelPolicy.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 "nsChannelPolicy.h"
-
-nsChannelPolicy::nsChannelPolicy()
- : mLoadType(0)
-{
-}
-
-nsChannelPolicy::~nsChannelPolicy()
-{
-}
-
-NS_IMPL_ISUPPORTS(nsChannelPolicy, nsIChannelPolicy)
-
-NS_IMETHODIMP
-nsChannelPolicy::GetLoadType(uint32_t *aLoadType)
-{
- *aLoadType = mLoadType;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsChannelPolicy::SetLoadType(uint32_t aLoadType)
-{
- mLoadType = aLoadType;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsChannelPolicy::GetContentSecurityPolicy(nsISupports **aCSP)
-{
- *aCSP = mCSP;
- NS_IF_ADDREF(*aCSP);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsChannelPolicy::SetContentSecurityPolicy(nsISupports *aCSP)
-{
- mCSP = aCSP;
- return NS_OK;
-}
deleted file mode 100644
--- a/content/base/src/nsChannelPolicy.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 nsChannelPolicy_h___
-#define nsChannelPolicy_h___
-
-#include "nsCOMPtr.h"
-#include "nsIChannelPolicy.h"
-
-#define NSCHANNELPOLICY_CONTRACTID "@mozilla.org/nschannelpolicy;1"
-#define NSCHANNELPOLICY_CID \
-{ 0xd396b3cd, 0xf164, 0x4ce8, \
- { 0x93, 0xa7, 0xe3, 0x85, 0xe1, 0x46, 0x56, 0x3c } }
-
-class nsChannelPolicy : public nsIChannelPolicy
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSICHANNELPOLICY
-
- nsChannelPolicy();
-
-protected:
- virtual ~nsChannelPolicy();
-
- /* Represents the type of content being loaded in the channel per
- * nsIContentPolicy, e.g. TYPE_IMAGE, TYPE_SCRIPT
- */
- unsigned long mLoadType;
-
- /* pointer to a Content Security Policy object if available */
- nsCOMPtr<nsISupports> mCSP;
-};
-
-#endif /* nsChannelPolicy_h___ */
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -54,17 +54,16 @@
#include "mozilla/dom/Selection.h"
#include "mozilla/TextEvents.h"
#include "nsAString.h"
#include "nsAttrName.h"
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsBindingManager.h"
#include "nsCCUncollectableMarker.h"
-#include "nsChannelPolicy.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsCOMPtr.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentDLF.h"
#include "nsContentList.h"
#include "nsContentPolicyUtils.h"
#include "nsCPrefetchService.h"
#include "nsCRT.h"
@@ -83,17 +82,16 @@
#include "nsGenericHTMLFrameElement.h"
#include "nsGkAtoms.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsHtml5Module.h"
#include "nsHtml5StringParser.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsICategoryManager.h"
#include "nsIChannelEventSink.h"
-#include "nsIChannelPolicy.h"
#include "nsICharsetDetectionObserver.h"
#include "nsIChromeRegistry.h"
#include "nsIConsoleService.h"
#include "nsIContent.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIContentSink.h"
#include "nsIContentViewer.h"
#include "nsIDocShell.h"
@@ -3002,45 +3000,30 @@ nsContentUtils::LoadImage(nsIURI* aURI,
nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
"Could not get loadgroup; onload may fire too early");
- // check for a Content Security Policy to pass down to the channel that
- // will get created to load the image
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- if (aLoadingPrincipal) {
- nsresult rv = aLoadingPrincipal->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_IMAGE);
- }
- }
-
// Make the URI immutable so people won't change it under us
NS_TryToSetImmutable(aURI);
// XXXbz using "documentURI" for the initialDocumentURI is not quite
// right, but the best we can do here...
return imgLoader->LoadImage(aURI, /* uri to load */
documentURI, /* initialDocumentURI */
aReferrer, /* referrer */
aLoadingPrincipal, /* loading principal */
loadGroup, /* loadgroup */
aObserver, /* imgINotificationObserver */
aLoadingDocument, /* uniquification key */
aLoadFlags, /* load flags */
nullptr, /* cache key */
- channelPolicy, /* CSP info */
initiatorType, /* the load initiator */
aRequest);
}
// static
already_AddRefed<imgIContainer>
nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
imgIRequest **aRequest)
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -1117,29 +1117,27 @@ NS_StartCORSPreflight(nsIChannel* aReque
rv = aRequestChannel->GetLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> preflightChannel;
if (loadInfo) {
rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
uri,
loadInfo,
- nullptr, // aChannelPolicy
loadGroup,
nullptr, // aCallbacks
loadFlags);
}
else {
rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
uri,
nullptr, // aRequestingNode,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
loadGroup,
nullptr, // aCallbacks
loadFlags);
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1325,17 +1325,16 @@ nsExternalResourceMap::PendingLoad::Star
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
aURI,
aRequestingNode,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
loadGroup,
req); // aCallbacks
NS_ENSURE_SUCCESS(rv, rv);
mURI = aURI;
return channel->AsyncOpen(this, nullptr);
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -60,18 +60,16 @@
#include "nsSandboxFlags.h"
// Concrete classes
#include "nsFrameLoader.h"
#include "nsObjectLoadingContent.h"
#include "mozAutoDocUpdate.h"
#include "nsIContentSecurityPolicy.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
#include "GeckoProfiler.h"
#include "nsPluginFrame.h"
#include "nsDOMClassInfo.h"
#include "nsWrapperCacheInlines.h"
#include "nsDOMJSUtils.h"
#include "nsWidgetsCID.h"
#include "nsContentCID.h"
@@ -2487,25 +2485,16 @@ nsObjectLoadingContent::OpenChannel()
return NS_ERROR_NOT_AVAILABLE;
}
rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), mURI, 0);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
nsCOMPtr<nsIChannel> chan;
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_OBJECT);
- }
nsRefPtr<ObjectInterfaceRequestorShim> shim =
new ObjectInterfaceRequestorShim(this);
bool isSandBoxed = doc->GetSandboxFlags() & SANDBOXED_ORIGIN;
bool inherit = nsContentUtils::ChannelShouldInheritPrincipal(thisContent->NodePrincipal(),
mURI,
true, // aInheritForAboutBlank
false); // aForceInherit
@@ -2517,17 +2506,16 @@ nsObjectLoadingContent::OpenChannel()
securityFlags |= nsILoadInfo::SEC_SANDBOXED;
}
rv = NS_NewChannel(getter_AddRefs(chan),
mURI,
thisContent,
securityFlags,
nsIContentPolicy::TYPE_OBJECT,
- channelPolicy,
group, // aLoadGroup
shim, // aCallbacks
nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
nsIChannel::LOAD_CLASSIFY_URI);
NS_ENSURE_SUCCESS(rv, rv);
// Referrer
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -36,18 +36,16 @@
#include "nsUnicharUtils.h"
#include "nsAutoPtr.h"
#include "nsIXPConnect.h"
#include "nsError.h"
#include "nsThreadUtils.h"
#include "nsDocShellCID.h"
#include "nsIContentSecurityPolicy.h"
#include "prlog.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
#include "nsCRT.h"
#include "nsContentCreatorFunctions.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsSandboxFlags.h"
#include "nsContentTypeParser.h"
#include "nsINetworkPredictor.h"
#include "ImportManager.h"
#include "mozilla/dom/EncodingUtils.h"
@@ -302,35 +300,22 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
// If this document is sandboxed without 'allow-scripts', abort.
if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
return NS_OK;
}
- // check for a Content Security Policy to pass down to the channel
- // that will be created to load the script
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
- }
-
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
aRequest->mURI,
mDocument,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT,
- channelPolicy,
loadGroup,
prompter,
nsIRequest::LOAD_NORMAL |
nsIChannel::LOAD_CLASSIFY_URI);
NS_ENSURE_SUCCESS(rv, rv);
nsIScriptElement *script = aRequest->mElement;
--- a/content/base/src/nsSyncLoadService.cpp
+++ b/content/base/src/nsSyncLoadService.cpp
@@ -310,17 +310,16 @@ nsSyncLoadService::LoadDocument(nsIURI *
nsIDOMDocument** aResult)
{
nsCOMPtr<nsIChannel> channel;
nsresult rv = NS_NewChannel(getter_AddRefs(channel),
aURI,
aLoaderPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
aLoadGroup);
NS_ENSURE_SUCCESS(rv, rv);
if (!aForceToXML) {
channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
}
bool isChrome = false, isResource = false;
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -47,18 +47,16 @@
#include "nsContentPolicyUtils.h"
#include "nsError.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsIHTMLDocument.h"
#include "nsIStorageStream.h"
#include "nsIPromptFactory.h"
#include "nsIWindowWatcher.h"
#include "nsIConsoleService.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsStringBuffer.h"
#include "nsIFileChannel.h"
#include "mozilla/Telemetry.h"
#include "jsfriendapi.h"
#include "GeckoProfiler.h"
#include "mozilla/dom/EncodingUtils.h"
@@ -469,16 +467,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
nsXHREventTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMFile)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
+
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
nsXHREventTarget)
@@ -487,16 +489,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
tmp->mResultJSON = JSVAL_VOID;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMFile)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
+
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
nsXHREventTarget)
@@ -1719,27 +1725,16 @@ nsXMLHttpRequest::Open(const nsACString&
// operations will merge/override correctly.
mAlreadySetHeaders.Clear();
// When we are called from JS we can find the load group for the page,
// and add ourselves to it. This way any pending requests
// will be automatically aborted if the user leaves the page.
nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
- // get Content Security Policy from principal to pass into channel
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = mPrincipal->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_XMLHTTPREQUEST);
- }
-
nsSecurityFlags secFlags = nsILoadInfo::SEC_NORMAL;
if (IsSystemXHR()) {
// Don't give this document the system principal. We need to keep track of
// mPrincipal being system because we use it for various security checks
// that should be passing, but the document data shouldn't get a system
// principal. Hence we set the sandbox flag in loadinfo, so that
// GetChannelResultPrincipal will give us the nullprincipal.
secFlags |= nsILoadInfo::SEC_SANDBOXED;
@@ -1749,28 +1744,26 @@ nsXMLHttpRequest::Open(const nsACString&
// If we have the document, use it
if (doc) {
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri,
doc,
secFlags,
nsIContentPolicy::TYPE_XMLHTTPREQUEST,
- channelPolicy,
loadGroup,
nullptr, // aCallbacks
nsIRequest::LOAD_BACKGROUND);
} else {
//otherwise use the principal
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri,
mPrincipal,
secFlags,
nsIContentPolicy::TYPE_XMLHTTPREQUEST,
- channelPolicy,
loadGroup,
nullptr, // aCallbacks
nsIRequest::LOAD_BACKGROUND);
}
if (NS_FAILED(rv)) return rv;
mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -286,16 +286,17 @@ skip-if = (buildapp == 'b2g' && toolkit
[test_bug338583.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(https not working, bug 907770) b2g-debug(https not working, bug 907770) b2g-desktop(43 total - bug 901343, specialpowers.wrap issue createsystemxhr)
[test_bug338679.html]
[test_bug339494.html]
[test_bug339494.xhtml]
[test_bug340571.html]
[test_bug343596.html]
[test_bug345339.html]
+skip-if = e10s # Bug 1081453 - shutdown leaks with e10s and WebIDL dom::File
[test_bug346485.html]
[test_bug352728.html]
[test_bug352728.xhtml]
[test_bug353334.html]
[test_bug355026.html]
[test_bug357450.html]
[test_bug357450.js]
[test_bug357450.xhtml]
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -94,18 +94,16 @@ static PRLogModuleInfo* gMediaElementEve
#define LOG(type, msg) PR_LOG(gMediaElementLog, type, msg)
#define LOG_EVENT(type, msg) PR_LOG(gMediaElementEventsLog, type, msg)
#else
#define LOG(type, msg)
#define LOG_EVENT(type, msg)
#endif
#include "nsIContentSecurityPolicy.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
#include "mozilla/Preferences.h"
#include "nsIPermissionManager.h"
#include "nsContentTypeParser.h"
using namespace mozilla::layers;
using mozilla::net::nsMediaFragmentURIParser;
@@ -1165,35 +1163,22 @@ nsresult HTMLMediaElement::LoadResource(
}
mMediaSource = source.forget();
nsRefPtr<MediaResource> resource =
MediaSourceDecoder::CreateResource(mMediaSource->GetPrincipal());
return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
}
nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
-
- // check for a Content Security Policy to pass down to the channel
- // created to load the media content
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv,rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
- }
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
mLoadingSrc,
static_cast<Element*>(this),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_MEDIA,
- channelPolicy,
loadGroup,
nullptr, // aCallbacks
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
NS_ENSURE_SUCCESS(rv,rv);
--- a/content/html/content/src/HTMLTrackElement.cpp
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -16,17 +16,16 @@
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsICachingChannel.h"
#include "nsIChannelEventSink.h"
-#include "nsIChannelPolicy.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIDocument.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMHTMLMediaElement.h"
#include "nsIHttpChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsILoadGroup.h"
@@ -232,38 +231,23 @@ HTMLTrackElement::LoadResource()
// We may already have a TextTrack at this point if GetTrack() has already
// been called. This happens, for instance, if script tries to get the
// TextTrack before its mTrackElement has been bound to the DOM tree.
if (!mTrack) {
CreateTextTrack();
}
- // Check for a Content Security Policy to pass down to the channel
- // created to load the media content.
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- if (!channelPolicy) {
- return;
- }
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
- }
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
static_cast<Element*>(this),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_MEDIA,
- channelPolicy,
loadGroup);
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
mListener = new WebVTTListener(this);
rv = mListener->LoadResource();
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
channel->SetNotificationCallbacks(mListener);
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -286,26 +286,21 @@ static const nsAttrValue::EnumTable kDir
{ "rtl", eDir_RTL },
{ "auto", eDir_Auto },
{ 0 }
};
void
nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
{
- //XXXsmaug We shouldn't need PresContext for this.
- nsPresContext *presContext = GetPresContext(eForComposedDoc);
-
- if (presContext) {
- nsAutoString suffix;
- GetAccessKey(suffix);
- if (!suffix.IsEmpty() &&
- presContext->EventStateManager()->GetAccessKeyLabelPrefix(aLabel)) {
- aLabel.Append(suffix);
- }
+ nsAutoString suffix;
+ GetAccessKey(suffix);
+ if (!suffix.IsEmpty()) {
+ EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
+ aLabel.Append(suffix);
}
}
static bool IS_TABLE_CELL(nsIAtom* frameType) {
return nsGkAtoms::tableCellFrame == frameType ||
nsGkAtoms::bcTableCellFrame == frameType;
}
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1511,17 +1511,16 @@ nsHTMLDocument::Open(JSContext* cx,
// So we reset the document and create a new one.
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
callerDoc,
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
group);
if (rv.Failed()) {
return nullptr;
}
// We can't depend on channels implementing property bags, so do our
// base URI manually after reset.
--- a/content/media/GraphDriver.cpp
+++ b/content/media/GraphDriver.cpp
@@ -459,17 +459,17 @@ OfflineClockDriver::WakeUp()
MOZ_ASSERT(false, "An offline graph should not have to wake up.");
}
AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation)
: mDriver(aDriver),
mOperation(aOperation),
mShutdownGrip(aDriver->GraphImpl())
{
- MOZ_ASSERT(mDriver->mAudioStream || aOperation == INIT, "No audio stream !");
+ NS_WARN_IF_FALSE(mDriver->mAudioStream || aOperation == INIT, "No audio stream !");
}
AsyncCubebTask::~AsyncCubebTask()
{
}
NS_IMETHODIMP
AsyncCubebTask::Run()
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -919,17 +919,16 @@ ChannelMediaResource::RecreateChannel()
nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
mURI,
element,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_MEDIA,
- nullptr, // aChannelPolicy
loadGroup,
nullptr, // aCallbacks
loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
// We have cached the Content-Type, which should not change. Give a hint to
// the channel to avoid a sniffing failure, which would be expected because we
// are probably seeking in the middle of the bitstream, and sniffing relies
@@ -1437,17 +1436,16 @@ already_AddRefed<MediaResource> FileMedi
nsCOMPtr<nsIChannel> channel;
nsresult rv =
NS_NewChannel(getter_AddRefs(channel),
mURI,
element,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_MEDIA,
- nullptr, // aChannelPolicy
loadGroup);
if (NS_FAILED(rv))
return nullptr;
nsRefPtr<MediaResource> resource(new FileMediaResource(aDecoder, channel, mURI, GetContentType()));
return resource.forget();
}
--- a/content/media/mediasource/MediaSourceReader.cpp
+++ b/content/media/mediasource/MediaSourceReader.cpp
@@ -197,34 +197,36 @@ MediaSourceReader::OnDecodeError()
MSE_DEBUG("MediaSourceReader(%p)::OnDecodeError", this);
GetCallback()->OnDecodeError();
}
void
MediaSourceReader::Shutdown()
{
MediaDecoderReader::Shutdown();
+ for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
+ mTrackBuffers[i]->Shutdown();
+ }
mAudioTrack = nullptr;
mAudioReader = nullptr;
mVideoTrack = nullptr;
mVideoReader = nullptr;
- for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
- mTrackBuffers[i]->Shutdown();
- }
- mTrackBuffers.Clear();
}
void
MediaSourceReader::BreakCycles()
{
MediaDecoderReader::BreakCycles();
- mAudioTrack = nullptr;
- mAudioReader = nullptr;
- mVideoTrack = nullptr;
- mVideoReader = nullptr;
+
+ // These were cleared in Shutdown().
+ MOZ_ASSERT(!mAudioTrack);
+ MOZ_ASSERT(!mAudioReader);
+ MOZ_ASSERT(!mVideoTrack);
+ MOZ_ASSERT(!mVideoReader);
+
for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
mTrackBuffers[i]->BreakCycles();
}
mTrackBuffers.Clear();
}
already_AddRefed<MediaDecoderReader>
MediaSourceReader::SelectReader(int64_t aTarget,
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -411,18 +411,31 @@ void
SourceBuffer::Dump(const char* aPath)
{
if (mTrackBuffer) {
mTrackBuffer->Dump(aPath);
}
}
#endif
-NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper,
- mMediaSource)
+NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
+ // Tell the TrackBuffer to end its current SourceBufferResource.
+ TrackBuffer* track = tmp->mTrackBuffer;
+ if (track) {
+ track->Detach();
+ }
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SourceBuffer,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
} // namespace dom
--- a/content/media/mediasource/TrackBuffer.cpp
+++ b/content/media/mediasource/TrackBuffer.cpp
@@ -49,55 +49,44 @@ TrackBuffer::TrackBuffer(MediaSourceDeco
TrackBuffer::~TrackBuffer()
{
MOZ_COUNT_DTOR(TrackBuffer);
}
class ReleaseDecoderTask : public nsRunnable {
public:
explicit ReleaseDecoderTask(SourceBufferDecoder* aDecoder)
+ : mDecoder(aDecoder)
{
- mDecoders.AppendElement(aDecoder);
- }
-
- explicit ReleaseDecoderTask(nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders)
- {
- mDecoders.SwapElements(aDecoders);
}
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
- mDecoders.Clear();
+ mDecoder = nullptr;
return NS_OK;
}
private:
- nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
+ nsRefPtr<SourceBufferDecoder> mDecoder;
};
void
TrackBuffer::Shutdown()
{
- // End the SourceBufferResource associated with mCurrentDecoder, which will
- // unblock any decoder initialization in ReadMetadata().
- DiscardDecoder();
-
// Finish any decoder initialization, which may add to mInitializedDecoders.
// Shutdown waits for any pending events, which may require the monitor,
// so we must not hold the monitor during this call.
mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
mTaskQueue->Shutdown();
mTaskQueue = nullptr;
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->Shutdown();
}
mInitializedDecoders.Clear();
- NS_DispatchToMainThread(new ReleaseDecoderTask(mDecoders));
- MOZ_ASSERT(mDecoders.IsEmpty());
mParentDecoder = nullptr;
}
bool
TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Run more of the buffer append algorithm asynchronously.
@@ -394,23 +383,26 @@ TrackBuffer::ContainsTime(int64_t aTime)
}
return false;
}
void
TrackBuffer::BreakCycles()
{
+ MOZ_ASSERT(NS_IsMainThread());
+
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->BreakCycles();
}
- mInitializedDecoders.Clear();
- NS_DispatchToMainThread(new ReleaseDecoderTask(mDecoders));
- MOZ_ASSERT(mDecoders.IsEmpty());
- mParentDecoder = nullptr;
+ mDecoders.Clear();
+
+ // These are cleared in Shutdown()
+ MOZ_ASSERT(mInitializedDecoders.IsEmpty());
+ MOZ_ASSERT(!mParentDecoder);
}
void
TrackBuffer::ResetDecode()
{
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->ResetDecode();
}
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -455,17 +455,16 @@ skip-if = true # bug 1021673
[test_seekLies.html]
[test_source.html]
[test_source_media.html]
[test_source_null.html]
[test_source_write.html]
[test_standalone.html]
[test_streams_autoplay.html]
[test_streams_element_capture.html]
-skip-if = e10s && os == 'win' # Bug 1065881 - Crash on child process shutdown in ShadowLayerForwarder::InWorkerThread
[test_streams_element_capture_createObjectURL.html]
[test_streams_element_capture_playback.html]
[test_streams_element_capture_reset.html]
[test_streams_gc.html]
skip-if = buildapp == 'b2g' # bug 1021682
[test_streams_srcObject.html]
[test_streams_tracks.html]
[test_texttrack.html]
--- a/content/media/test/test_mediarecorder_record_immediate_stop.html
+++ b/content/media/test/test_mediarecorder_record_immediate_stop.html
@@ -92,32 +92,31 @@ function startTest(test, token) {
ok(false, 'ondataavailable unexpectedly fired later than onstop');
manager.finished(token);
}
}
};
// This handler completes a start and stop of recording and verifies
// respective media recorder state.
- var canPlayThrough = function() {
- element.removeEventListener('canplaythrough', canPlayThrough, false);
-
+ element.onloadedmetadata = function () {
+ element.onloadedmetadata = null;
+ element.play();
mediaRecorder.start();
is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
is(mediaRecorder.stream, element.stream,
'Media recorder stream = element stream at the start of recording');
mediaRecorder.stop();
is(mediaRecorder.state, 'inactive',
'Media recorder is inactive after being stopped');
is(mediaRecorder.stream, element.stream,
'Media recorder stream = element stream post recording');
};
- element.addEventListener('canplaythrough', canPlayThrough, false);
- element.play();
+ element.preload = "metadata";
}
manager.runTests(gMediaRecorderTests, startTest);
</script>
</pre>
</body>
</html>
--- a/content/media/test/test_mediarecorder_record_no_timeslice.html
+++ b/content/media/test/test_mediarecorder_record_no_timeslice.html
@@ -79,33 +79,27 @@ function startTest(test, token) {
// onstop should not have fired before ondataavailable
if (onStopFired) {
ok(false, 'ondataavailable unexpectedly fired later than onstop');
manager.finished(token);
}
}
};
- element.oncanplaythrough = function () {
- element.oncanplaythrough = null;
- // If content has ended, skip the test
- if (element.ended) {
- ok(true, 'ended fired before canplaythrough, skipping test');
- manager.finished(token);
- } else {
- // If content hasn't ended, start recording
- mediaRecorder.start();
- is(mediaRecorder.state, 'recording',
- 'Media recorder should be recording');
- is(mediaRecorder.stream, element.stream,
- 'Media recorder stream = element stream at the start of recording');
- // Recording will automatically stop when the stream ends.
- }
+ element.preload = "metadata";
+
+ element.onloadedmetadata = function () {
+ element.onloadedmetadata = null;
+ mediaRecorder.start();
+ is(mediaRecorder.state, 'recording',
+ 'Media recorder should be recording');
+ is(mediaRecorder.stream, element.stream,
+ 'Media recorder stream = element stream at the start of recording');
+
+ element.play();
}
-
- element.play();
}
manager.runTests(gMediaRecorderTests, startTest);
</script>
</pre>
</body>
</html>
--- a/content/media/test/test_mediarecorder_record_session.html
+++ b/content/media/test/test_mediarecorder_record_session.html
@@ -49,25 +49,27 @@ function startTest(test, token) {
mediaRecorder.onerror = function(err) {
ok(false, 'Unexpected error fired with:' + err);
}
mediaRecorder.onwarning = function() {
ok(false, 'Unexpected warning fired');
}
- element.oncanplaythrough = function () {
- element.oncanplaythrough = null;
+ element.preload = "metadata";
+
+ element.onloadedmetadata = function () {
+ element.onloadedmetadata = null;
+ element.play();
for (var i = 0; i < mExpectStopCount; i++) {
mediaRecorder.start(1000);
mediaRecorder.stop();
}
}
- element.play();
}
manager.runTests(gMediaRecorderTests, startTest);
</script>
</pre>
</body>
</html>
--- a/content/media/test/test_timeupdate_small_files.html
+++ b/content/media/test/test_timeupdate_small_files.html
@@ -12,78 +12,75 @@ https://bugzilla.mozilla.org/show_bug.cg
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495319">Mozilla Bug 495319</a>
<pre id="test">
<script class="testbody" type="text/javascript">
var manager = new MediaTestManager;
-function timeupdate(e) {
- var v = e.target;
- v._timeupdateCount++;
- ok(v.gotEnded == 0, v._name + " - shouldn't get timeupdate after ended");
-}
-
function ended(e) {
var v = e.target;
- ++v.gotEnded;
- ok(v._timeupdateCount > 0, v._name + " - should see at least one timeupdate: " + v.currentTime);
- v._finished = true;
+ ++v.counter["ended"];
+ is(v.counter["ended"], 1, v._name + " should see ended only once");
+ ok(v.counter["timeupdate"] > 0, v._name + " should see at least one timeupdate: " + v.currentTime);
+
+ // Rest event counters for we don't allow events after ended.
+ eventsToLog.forEach(function(e) {
+ v.counter[e] = 0;
+ });
+
// Finish the test after 500ms. We shouldn't receive any timeupdate events
// after the ended event, so this gives time for any pending timeupdate events
// to fire so we can ensure we don't regress behaviour.
setTimeout(
function() {
// Remove the event listeners before removing the video from the document.
// We should receive a timeupdate and pause event when we remove the element
// from the document (as the element is specified to behave as if pause() was
// invoked when it's removed from a document), and we don't want those
// confusing the test results.
v.removeEventListener("ended", ended, false);
- v.removeEventListener("timeupdate", timeupdate, false);
- for (var i = 0; i < eventsToLog.length; ++i) {
- v.removeEventListener(eventsToLog[i], logEvent, false);
- }
+ eventsToLog.forEach(function(e) {
+ v.removeEventListener(e, logEvent, false);
+ });
removeNodeAndSource(v);
manager.finished(v.token);
},
500);
}
var eventsToLog = ["play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
- "loadeddata", "playing", "progress", "timeupdate", "ended", "suspend", "error", "stalled", "emptied", "abort",
+ "loadeddata", "playing", "timeupdate", "error", "stalled", "emptied", "abort",
"waiting", "pause"];
+
function logEvent(event) {
- if (event.target.gotEnded > (event.type == "ended" ? 1 : 0)) {
- if (event.target.currentSrc.slice(-9) == "seek.webm" && event.type == "stalled") {
- todo(false, event.target.currentSrc + " got unexpected stalled after ended (bug 760770)");
- } else {
- ok(false, event.target.currentSrc + " got unexpected " + event.type + " after ended");
- }
- } else {
- info(event.target.currentSrc + " got " + event.type);
+ var v = event.target;
+ ++v.counter[event.type];
+ if (v.counter["ended"] > 0) {
+ is(v.counter[event.type], 0, v._name + " got unexpected " + event.type + " after ended");
}
}
function startTest(test, token) {
var type = getMajorMimeType(test.type);
var v = document.createElement(type);
v.token = token;
manager.started(token);
v.src = test.name;
v._name = test.name;
- v._timeupdateCount = 0;
- v._finished = false;
- v.gotEnded = 0;
+
+ // Keep how many events received for each event type.
+ v.counter = {};
+ eventsToLog.forEach(function(e) {
+ v.addEventListener(e, logEvent, false);
+ v.counter[e] = 0;
+ });
v.addEventListener("ended", ended, false);
- v.addEventListener("timeupdate", timeupdate, false);
- for (var i = 0; i < eventsToLog.length; ++i) {
- v.addEventListener(eventsToLog[i], logEvent, false);
- }
+ v.counter["ended"] = 0;
document.body.appendChild(v);
v.play();
}
manager.runTests(gSmallTests, startTest);
</script>
</pre>
--- a/content/media/webspeech/synth/pico/nsPicoService.cpp
+++ b/content/media/webspeech/synth/pico/nsPicoService.cpp
@@ -406,52 +406,63 @@ NS_IMETHODIMP
PicoCallbackRunnable::OnCancel()
{
mService->mCurrentTask = nullptr;
return NS_OK;
}
NS_INTERFACE_MAP_BEGIN(nsPicoService)
NS_INTERFACE_MAP_ENTRY(nsISpeechService)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechService)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsPicoService)
NS_IMPL_RELEASE(nsPicoService)
nsPicoService::nsPicoService()
: mInitialized(false)
, mVoicesMonitor("nsPicoService::mVoices")
, mCurrentTask(nullptr)
, mPicoSystem(nullptr)
, mPicoEngine(nullptr)
, mSgResource(nullptr)
, mTaResource(nullptr)
, mPicoMemArea(nullptr)
{
- DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- rv = mThread->Dispatch(NS_NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
}
nsPicoService::~nsPicoService()
{
// We don't worry about removing the voices because this gets
// destructed at shutdown along with the voice registry.
MonitorAutoLock autoLock(mVoicesMonitor);
mVoices.Clear();
if (mThread) {
mThread->Shutdown();
}
UnloadEngine();
}
+// nsIObserver
+
+NS_IMETHODIMP
+nsPicoService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
+
+ DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return mThread->Dispatch(
+ NS_NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL);
+}
// nsISpeechService
NS_IMETHODIMP
nsPicoService::Speak(const nsAString& aText, const nsAString& aUri,
float aRate, float aPitch, nsISpeechTask* aTask)
{
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_AVAILABLE);
--- a/content/media/webspeech/synth/pico/nsPicoService.h
+++ b/content/media/webspeech/synth/pico/nsPicoService.h
@@ -5,40 +5,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsPicoService_h
#define nsPicoService_h
#include "mozilla/Mutex.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
+#include "nsIObserver.h"
#include "nsIThread.h"
#include "nsISpeechService.h"
#include "nsRefPtrHashtable.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Monitor.h"
namespace mozilla {
namespace dom {
class PicoVoice;
class PicoCallbackRunnable;
typedef void* pico_System;
typedef void* pico_Resource;
typedef void* pico_Engine;
-class nsPicoService : public nsISpeechService
+class nsPicoService : public nsIObserver,
+ public nsISpeechService
{
friend class PicoCallbackRunnable;
friend class PicoInitRunnable;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISPEECHSERVICE
+ NS_DECL_NSIOBSERVER
nsPicoService();
static nsPicoService* GetInstance();
static already_AddRefed<nsPicoService> GetInstanceForService();
static void Shutdown();
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -2697,17 +2697,16 @@ XULDocument::LoadOverlayInternal(nsIURI*
// that the overlay's JSObjects etc end up being created
// with the right principal and in the correct
// compartment.
rv = NS_NewChannel(getter_AddRefs(channel),
aURI,
NodePrincipal(),
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
group);
if (NS_SUCCEEDED(rv)) {
rv = channel->AsyncOpen(listener, nullptr);
}
if (NS_FAILED(rv)) {
// Abandon this prototype
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -62,17 +62,17 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "support", "chrome://global/content/aboutSupport.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "telemetry", "chrome://global/content/aboutTelemetry.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "networking", "chrome://global/content/aboutNetworking.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
- { "webrtc", "chrome://global/content/aboutWebrtc.xhtml",
+ { "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
// about:srcdoc is unresolvable by specification. It is included here
// because the security manager would disallow srcdoc iframes otherwise.
{ "srcdoc", "about:blank",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT }
};
static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -163,17 +163,16 @@
#include "nsIWebBrowserChromeFocus.h"
#if NS_PRINT_PREVIEW
#include "nsIDocumentViewerPrint.h"
#include "nsIWebBrowserPrint.h"
#endif
#include "nsContentUtils.h"
-#include "nsIChannelPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsILoadInfo.h"
#include "nsSandboxFlags.h"
#include "nsXULAppAPI.h"
#include "nsDOMNavigationTiming.h"
#include "nsISecurityUITelemetry.h"
#include "nsIAppsService.h"
#include "nsDSURIContentListener.h"
@@ -10153,37 +10152,17 @@ nsDocShell::DoURILoad(nsIURI * aURI,
loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
}
if (mLoadType == LOAD_ERROR_PAGE) {
// Error pages are LOAD_BACKGROUND
loadFlags |= nsIChannel::LOAD_BACKGROUND;
}
- // check for Content Security Policy to pass along with the
- // new channel we are creating
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
if (IsFrame()) {
- // check the parent docshell for a CSP
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- nsCOMPtr<nsIDocShellTreeItem> parentItem;
- GetSameTypeParent(getter_AddRefs(parentItem));
- if (parentItem) {
- nsCOMPtr<nsIDocument> doc = parentItem->GetDocument();
- if (doc) {
- rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
- }
- }
- }
-
// Only allow view-source scheme in top-level docshells. view-source is
// the only scheme to which this applies at the moment due to potential
// timing attacks to read data from cross-origin iframes. If this widens
// we should add a protocol flag for whether the scheme is allowed in
// frames and use something like nsNetUtil::NS_URIChainHasFlags.
nsCOMPtr<nsIURI> tempURI = aURI;
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
while (nestedURI) {
@@ -10242,17 +10221,16 @@ nsDocShell::DoURILoad(nsIURI * aURI,
if (!isSrcdoc) {
rv = NS_NewChannelInternal(getter_AddRefs(channel),
aURI,
requestingNode,
requestingPrincipal,
securityFlags,
aContentPolicyType,
- channelPolicy,
nullptr, // loadGroup
static_cast<nsIInterfaceRequestor*>(this),
loadFlags);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
// This is a uri with a protocol scheme we don't know how
// to handle. Embedders might still be interested in
--- a/dom/apps/AppsServiceChild.jsm
+++ b/dom/apps/AppsServiceChild.jsm
@@ -126,19 +126,16 @@ this.DOMApplicationRegistry = {
// cpmm.addMessageListener causes the DOMApplicationRegistry object to
// live forever if we don't clean up properly.
this.webapps = null;
this.DOMApps = null;
APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
this.cpmm.removeMessageListener(aMsgName, this);
});
-
- this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
- APPS_IPC_MSG_NAMES)
},
receiveMessage: function receiveMessage(aMessage) {
debug("Received " + aMessage.name + " message.");
let msg = aMessage.data;
switch (aMessage.name) {
case "Webapps:AddApp":
this.webapps[msg.id] = msg.app;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -62,17 +62,16 @@
#include "TimeManager.h"
#include "DeviceStorage.h"
#include "nsIDOMNavigatorSystemMessages.h"
#include "nsStreamUtils.h"
#include "nsIAppsService.h"
#include "mozIApplication.h"
#include "WidgetUtils.h"
#include "mozIThirdPartyUtil.h"
-#include "nsChannelPolicy.h"
#ifdef MOZ_MEDIA_NAVIGATOR
#include "MediaManager.h"
#endif
#ifdef MOZ_B2G_BT
#include "BluetoothManager.h"
#endif
#include "DOMCameraManager.h"
@@ -1051,36 +1050,21 @@ Navigator::SendBeacon(const nsAString& a
nsContentUtils::GetSecurityManager());
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
// Disallowed by content policy
aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
return false;
}
nsCOMPtr<nsIChannel> channel;
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = principal->GetCsp(getter_AddRefs(csp));
- if (NS_FAILED(rv)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
-
- if (csp) {
- channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID);
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_BEACON);
- }
-
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
doc,
nsILoadInfo::SEC_NORMAL,
- nsIContentPolicy::TYPE_BEACON,
- channelPolicy);
+ nsIContentPolicy::TYPE_BEACON);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
if (pbChannel) {
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -70,17 +70,17 @@ public:
void Set(const nsAString& aName, const nsAString& aValue);
void Append(const nsAString& aName, const nsAString& aValue);
bool Has(const nsAString& aName);
void Delete(const nsAString& aName);
- void Stringify(nsString& aRetval)
+ void Stringify(nsString& aRetval) const
{
Serialize(aRetval);
}
private:
void AppendInternal(const nsAString& aName, const nsAString& aValue);
void DeleteAll();
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -57,8 +57,9 @@ MSG_DEF(MSG_INVALID_READ_SIZE, 0, "0 (Ze
MSG_DEF(MSG_HEADERS_IMMUTABLE, 0, "Headers are immutable and cannot be modified.")
MSG_DEF(MSG_INVALID_HEADER_NAME, 1, "{0} is an invalid header name.")
MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, "{0} is an invalid header value.")
MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, "Headers require name/value tuples when being initialized by a sequence.")
MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.")
MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.")
MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, "Invalid request method {0}.")
MSG_DEF(MSG_REQUEST_BODY_CONSUMED_ERROR, 0, "Request body has already been consumed.")
+MSG_DEF(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR, 0, "Response statusText may not contain newline or carriage return.")
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -78,29 +78,31 @@ let SEC_ERROR_UNTRUSTED_CERT = (SEC_ERRO
let SEC_ERROR_EXPIRED_CERTIFICATE = (SEC_ERROR_BASE + 11);
let SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = (SEC_ERROR_BASE + 176);
let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
let SSL_ERROR_BAD_CERT_DOMAIN = (SSL_ERROR_BASE + 12);
let MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
let MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = (MOZILLA_PKIX_ERROR_BASE + 1);
+let MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = (MOZILLA_PKIX_ERROR_BASE + 3);
function getErrorClass(errorCode) {
let NSPRCode = -1 * NS_ERROR_GET_CODE(errorCode);
switch (NSPRCode) {
case SEC_ERROR_UNKNOWN_ISSUER:
case SEC_ERROR_UNTRUSTED_ISSUER:
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
case SEC_ERROR_UNTRUSTED_CERT:
case SSL_ERROR_BAD_CERT_DOMAIN:
case SEC_ERROR_EXPIRED_CERTIFICATE:
case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
case MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY:
+ case MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA:
return Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT;
default:
return Ci.nsINSSErrorsService.ERROR_CLASS_SSL_PROTOCOL;
}
return null;
}
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1017,18 +1017,20 @@ CanvasRenderingContext2D::Redraw(const m
nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
mCanvasElement->InvalidateCanvasContent(&r);
}
void
CanvasRenderingContext2D::DidRefresh()
{
- SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
- if (glue) {
+ if (IsTargetValid() && SkiaGLTex()) {
+ SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+ MOZ_ASSERT(glue);
+
auto gl = glue->GetGLContext();
gl->FlushIfHeavyGLCallsSinceLastFlush();
}
}
void
CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
{
@@ -4818,16 +4820,24 @@ CanvasRenderingContext2D::CreateImageDat
ErrorResult& error)
{
return mozilla::dom::CreateImageData(cx, this, imagedata.Width(),
imagedata.Height(), error);
}
static uint8_t g2DContextLayerUserData;
+
+uint32_t
+CanvasRenderingContext2D::SkiaGLTex() const
+{
+ MOZ_ASSERT(IsTargetValid());
+ return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
+}
+
already_AddRefed<CanvasLayer>
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
LayerManager *aManager)
{
if (mOpaque) {
// If we're opaque then make sure we have a surface so we paint black
// instead of transparent.
@@ -4849,19 +4859,21 @@ CanvasRenderingContext2D::GetCanvasLayer
if (!mResetLayer && aOldLayer) {
CanvasRenderingContext2DUserData* userData =
static_cast<CanvasRenderingContext2DUserData*>(
aOldLayer->GetUserData(&g2DContextLayerUserData));
CanvasLayer::Data data;
- SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
- GLuint skiaGLTex = (GLuint)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
- if (glue && skiaGLTex) {
+ GLuint skiaGLTex = SkiaGLTex();
+ if (skiaGLTex) {
+ SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+ MOZ_ASSERT(glue);
+
data.mGLContext = glue->GetGLContext();
data.mFrontbufferGLTex = skiaGLTex;
} else {
data.mDrawTarget = mTarget;
}
if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
nsRefPtr<CanvasLayer> ret = aOldLayer;
@@ -4893,21 +4905,24 @@ CanvasRenderingContext2D::GetCanvasLayer
canvasLayer->SetDidTransactionCallback(
CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
CanvasLayer::Data data;
data.mSize = nsIntSize(mWidth, mHeight);
data.mHasAlpha = !mOpaque;
- SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
- GLuint skiaGLTex = (GLuint)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
- if (glue && skiaGLTex) {
+ GLuint skiaGLTex = SkiaGLTex();
+ if (skiaGLTex) {
canvasLayer->SetPreTransactionCallback(
CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
+
+ SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+ MOZ_ASSERT(glue);
+
data.mGLContext = glue->GetGLContext();
data.mFrontbufferGLTex = skiaGLTex;
} else {
data.mDrawTarget = mTarget;
}
canvasLayer->Initialize(data);
uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -693,17 +693,19 @@ protected:
/*
* Disposes an old target and prepares to lazily create a new target.
*/
void ClearTarget();
/**
* Check if the target is valid after calling EnsureTarget.
*/
- bool IsTargetValid() { return mTarget != sErrorTarget && mTarget != nullptr; }
+ bool IsTargetValid() const {
+ return mTarget != sErrorTarget && mTarget != nullptr;
+ }
/**
* Returns the surface format this canvas should be allocated using. Takes
* into account mOpaque, platform requirements, etc.
*/
mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
/**
@@ -759,16 +761,18 @@ protected:
// If mCanvasElement is not provided, then a docshell is
nsCOMPtr<nsIDocShell> mDocShell;
// This is created lazily so it is necessary to call EnsureTarget before
// accessing it. In the event of an error it will be equal to
// sErrorTarget.
mozilla::RefPtr<mozilla::gfx::DrawTarget> mTarget;
+ uint32_t SkiaGLTex() const;
+
/**
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
* Redraw is called, reset to false when Render is called.
*/
bool mIsEntireFrameInvalid;
/**
* When this is set, the first call to Redraw(gfxRect) should set
* mIsEntireFrameInvalid since we expect it will be followed by
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -346,16 +346,18 @@ WebGL2Context::CompressedTexSubImage3D(G
MOZ_CRASH("Not Implemented.");
}
JS::Value
WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname)
{
switch (pname) {
case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
+ case LOCAL_GL_TEXTURE_BASE_LEVEL:
+ case LOCAL_GL_TEXTURE_MAX_LEVEL:
{
GLint i = 0;
gl->fGetTexParameteriv(target.get(), pname, &i);
return JS::NumberValue(uint32_t(i));
}
}
return WebGLContext::GetTexParameterInternal(target, pname);
}
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -917,17 +917,17 @@ WebGLContext::GenerateMipmap(GLenum rawT
WebGLTexture *tex = activeBoundTextureForTarget(target);
if (!tex)
return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D)
? LOCAL_GL_TEXTURE_2D
: LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
- if (!tex->HasImageInfoAt(imageTarget, 0))
+ if (!tex->HasImageInfoAt(imageTarget, tex->GetBaseMipmapLevel()))
{
return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
}
if (!IsWebGL2() && !tex->IsFirstImagePowerOfTwo())
return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
@@ -1505,16 +1505,29 @@ void WebGLContext::TexParameter_base(GLe
WebGLTexture *tex = activeBoundTextureForTarget(texTarget);
if (!tex)
return ErrorInvalidOperation("texParameter: no texture is bound to this target");
bool pnameAndParamAreIncompatible = false;
bool paramValueInvalid = false;
switch (pname) {
+ case LOCAL_GL_TEXTURE_BASE_LEVEL:
+ case LOCAL_GL_TEXTURE_MAX_LEVEL:
+ if (!IsWebGL2())
+ return ErrorInvalidEnumInfo("texParameter: pname", pname);
+ if (intParam < 0) {
+ paramValueInvalid = true;
+ break;
+ }
+ if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL)
+ tex->SetBaseMipmapLevel(intParam);
+ else
+ tex->SetMaxMipmapLevel(intParam);
+ break;
case LOCAL_GL_TEXTURE_MIN_FILTER:
switch (intParam) {
case LOCAL_GL_NEAREST:
case LOCAL_GL_LINEAR:
case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -181,16 +181,20 @@ WebGLContext::GetParameter(JSContext* cx
switch (pname) {
case LOCAL_GL_MAX_SAMPLES:
case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
GLint val;
gl->fGetIntegerv(pname, &val);
return JS::NumberValue(uint32_t(val));
}
+
+ case LOCAL_GL_TEXTURE_BINDING_3D: {
+ return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
+ }
}
}
switch (pname) {
//
// String params
//
case LOCAL_GL_VENDOR:
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -214,16 +214,23 @@ bool WebGLContext::ValidateBlendFuncEnum
}
bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char *info)
{
switch (target) {
case LOCAL_GL_TEXTURE_2D:
case LOCAL_GL_TEXTURE_CUBE_MAP:
return true;
+ case LOCAL_GL_TEXTURE_3D: {
+ const bool isValid = IsWebGL2();
+ if (!isValid) {
+ ErrorInvalidEnumInfo(info, target);
+ }
+ return isValid;
+ }
default:
ErrorInvalidEnumInfo(info, target);
return false;
}
}
bool WebGLContext::ValidateComparisonEnum(GLenum target, const char *info)
{
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -28,16 +28,18 @@ WebGLTexture::WebGLTexture(WebGLContext
, mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
, mMagFilter(LOCAL_GL_LINEAR)
, mWrapS(LOCAL_GL_REPEAT)
, mWrapT(LOCAL_GL_REPEAT)
, mFacesCount(0)
, mMaxLevelWithCustomImages(0)
, mHaveGeneratedMipmap(false)
, mImmutable(false)
+ , mBaseMipmapLevel(0)
+ , mMaxMipmapLevel(1000)
, mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
{
mContext->MakeContextCurrent();
mContext->gl->fGenTextures(1, &mGLName);
mContext->mTextures.insertBack(this);
}
void
@@ -57,51 +59,46 @@ WebGLTexture::ImageInfo::MemoryUsage() c
}
size_t
WebGLTexture::MemoryUsage() const {
if (IsDeleted())
return 0;
size_t result = 0;
for(size_t face = 0; face < mFacesCount; face++) {
- if (mHaveGeneratedMipmap) {
- size_t level0MemoryUsage = ImageInfoAtFace(face, 0).MemoryUsage();
- // Each mipmap level is 1/(2^d) the size of the previous level,
- // where d is 2 or 3 depending on whether the images are 2D or 3D
- // 1 + x + x^2 + ... = 1/(1-x)
- // for x = 1/(2^2), we get 1/(1-1/4) = 4/3
- // for x = 1/(2^3), we get 1/(1-1/8) = 8/7
- size_t allLevelsMemoryUsage =
- mTarget == LOCAL_GL_TEXTURE_3D
- ? level0MemoryUsage * 8 / 7
- : level0MemoryUsage * 4 / 3;
- result += allLevelsMemoryUsage;
- } else {
- for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
- result += ImageInfoAtFace(face, level).MemoryUsage();
- }
+ for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
+ result += ImageInfoAtFace(face, level).MemoryUsage();
}
return result;
}
bool
WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const
{
if (mHaveGeneratedMipmap)
return true;
+ if (GetMaxMipmapLevel() < GetBaseMipmapLevel())
+ return false;
+
// We want a copy here so we can modify it temporarily.
- ImageInfo expected = ImageInfoAt(texImageTarget, 0);
+ ImageInfo expected = ImageInfoAt(texImageTarget, GetBaseMipmapLevel());
// checks if custom level>0 images are all defined up to the highest level defined
// and have the expected dimensions
- for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
+ for (size_t level = GetBaseMipmapLevel(); level <= GetMaxMipmapLevel(); ++level) {
const ImageInfo& actual = ImageInfoAt(texImageTarget, level);
if (actual != expected)
return false;
+
+ // Check the raw value here, not the clamped one, since we don't want
+ // to terminate early if there aren't enough levels defined.
+ if (level == mMaxMipmapLevel)
+ return true;
+
expected.mWidth = std::max(1, expected.mWidth / 2);
expected.mHeight = std::max(1, expected.mHeight / 2);
expected.mDepth = std::max(1, expected.mDepth / 2);
// if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence
// of extra useless levels.
if (actual.mWidth == 1 &&
actual.mHeight == 1 &&
@@ -180,30 +177,30 @@ WebGLTexture::SetGeneratedMipmap() {
void
WebGLTexture::SetCustomMipmap() {
if (mHaveGeneratedMipmap) {
// if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
// we need to compute now all the mipmap image info.
// since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
// and are power-of-two.
- ImageInfo imageInfo = ImageInfoAtFace(0, 0);
+ ImageInfo imageInfo = ImageInfoAtFace(0, GetBaseMipmapLevel());
NS_ASSERTION(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(),
"this texture is NPOT, so how could GenerateMipmap() ever accept it?");
GLsizei size = std::max(std::max(imageInfo.mWidth, imageInfo.mHeight), imageInfo.mDepth);
// Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping).
size_t maxLevel = 0;
for (GLsizei n = size; n > 1; n >>= 1)
++maxLevel;
EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
- for (size_t level = 1; level <= maxLevel; ++level) {
+ for (size_t level = GetBaseMipmapLevel() + 1; level <= GetMaxMipmapLevel(); ++level) {
imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1);
imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1);
imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1);
for(size_t face = 0; face < mFacesCount; ++face)
ImageInfoAtFace(face, level) = imageInfo;
}
}
mHaveGeneratedMipmap = false;
@@ -218,17 +215,17 @@ WebGLTexture::AreAllLevel0ImageInfosEqua
return true;
}
bool
WebGLTexture::IsMipmapComplete() const {
MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_2D ||
mTarget == LOCAL_GL_TEXTURE_3D);
- if (!ImageInfoAtFace(0, 0).IsPositive())
+ if (!ImageInfoAtFace(0, GetBaseMipmapLevel()).IsPositive())
return false;
if (mHaveGeneratedMipmap)
return true;
return DoesMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D);
}
bool
WebGLTexture::IsCubeComplete() const {
@@ -257,17 +254,17 @@ WebGLTexture::ResolvedFakeBlackStatus()
if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) {
return mFakeBlackStatus;
}
// Determine if the texture needs to be faked as a black texture.
// See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec.
for (size_t face = 0; face < mFacesCount; ++face) {
- if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
+ if (ImageInfoAtFace(face, GetBaseMipmapLevel()).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
// In case of undefined texture image, we don't print any message because this is a very common
// and often legitimate case (asynchronous texture loading).
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
return mFakeBlackStatus;
}
}
const char *msg_rendering_as_black
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -208,16 +208,19 @@ protected:
TexWrap mWrapS, mWrapT;
size_t mFacesCount, mMaxLevelWithCustomImages;
nsTArray<ImageInfo> mImageInfos;
bool mHaveGeneratedMipmap; // set by generateMipmap
bool mImmutable; // set by texStorage*
+ size_t mBaseMipmapLevel; // set by texParameter (defaults to 0)
+ size_t mMaxMipmapLevel; // set by texParameter (defaults to 1000)
+
WebGLTextureFakeBlackStatus mFakeBlackStatus;
void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) {
mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages);
mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
}
bool CheckFloatTextureFilterParams() const {
@@ -278,16 +281,27 @@ public:
bool IsMipmapCubeComplete() const;
void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
bool IsImmutable() const { return mImmutable; }
void SetImmutable() { mImmutable = true; }
+ void SetBaseMipmapLevel(unsigned level) { mBaseMipmapLevel = level; }
+ void SetMaxMipmapLevel(unsigned level) { mMaxMipmapLevel = level; }
+ size_t GetBaseMipmapLevel() const {
+ // Clamp to [0, levels - 1]
+ return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages);
+ }
+ size_t GetMaxMipmapLevel() const {
+ // Clamp to [base, levels - 1]
+ return std::min(mMaxMipmapLevel, mMaxLevelWithCustomImages);
+ }
+
size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; }
// Returns the current fake-black-status, except if it was Unknown,
// in which case this function resolves it first, so it never returns Unknown.
WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
};
inline TexImageTarget
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -390,17 +390,22 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffe
SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
&oidData->oid);
if (rv != SECSuccess) {
return nullptr;
}
}
- return SECKEY_ExtractPublicKey(spki.get());
+ ScopedSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
+ if (!tmp.get() || !PublicKeyValid(tmp.get())) {
+ return nullptr;
+ }
+
+ return SECKEY_CopyPublicKey(tmp);
}
nsresult
CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
CryptoBuffer& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
ScopedSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
@@ -838,16 +843,20 @@ CryptoKey::PublicKeyFromJwk(const JsonWe
// 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 nullptr;
}
nsresult
CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
@@ -877,16 +886,36 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKe
}
return NS_OK;
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
}
bool
+CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey)
+{
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot.get()) {
+ return false;
+ }
+
+ // This assumes that NSS checks the validity of a public key when
+ // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
+ // if it is invalid.
+ CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot, aPubKey, PR_FALSE);
+ if (id == CK_INVALID_HANDLE) {
+ return false;
+ }
+
+ SECStatus rv = PK11_DestroyObject(slot, id);
+ return (rv == SECSuccess);
+}
+
+bool
CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return false;
}
// Write in five pieces
--- a/dom/crypto/CryptoKey.h
+++ b/dom/crypto/CryptoKey.h
@@ -166,16 +166,18 @@ public:
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static SECKEYPublicKey* PublicKeyFromJwk(const JsonWebKey& aKeyData,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static nsresult PublicKeyToJwk(SECKEYPublicKey* aPrivKey,
JsonWebKey& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
+ static bool PublicKeyValid(SECKEYPublicKey* aPubKey);
+
// Structured clone methods use these to clone keys
bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
bool ReadStructuredClone(JSStructuredCloneReader* aReader);
private:
~CryptoKey();
nsRefPtr<nsIGlobalObject> mGlobal;
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -22,16 +22,17 @@
#define WEBCRYPTO_ALG_SHA256 "SHA-256"
#define WEBCRYPTO_ALG_SHA384 "SHA-384"
#define WEBCRYPTO_ALG_SHA512 "SHA-512"
#define WEBCRYPTO_ALG_HMAC "HMAC"
#define WEBCRYPTO_ALG_PBKDF2 "PBKDF2"
#define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5"
#define WEBCRYPTO_ALG_RSA_OAEP "RSA-OAEP"
#define WEBCRYPTO_ALG_ECDH "ECDH"
+#define WEBCRYPTO_ALG_ECDSA "ECDSA"
// WebCrypto key formats
#define WEBCRYPTO_KEY_FORMAT_RAW "raw"
#define WEBCRYPTO_KEY_FORMAT_PKCS8 "pkcs8"
#define WEBCRYPTO_KEY_FORMAT_SPKI "spki"
#define WEBCRYPTO_KEY_FORMAT_JWK "jwk"
// WebCrypto key types
@@ -79,16 +80,19 @@
#define JWK_ALG_RS1 "RS1" // RSASSA-PKCS1
#define JWK_ALG_RS256 "RS256"
#define JWK_ALG_RS384 "RS384"
#define JWK_ALG_RS512 "RS512"
#define JWK_ALG_RSA_OAEP "RSA-OAEP" // RSA-OAEP
#define JWK_ALG_RSA_OAEP_256 "RSA-OAEP-256"
#define JWK_ALG_RSA_OAEP_384 "RSA-OAEP-384"
#define JWK_ALG_RSA_OAEP_512 "RSA-OAEP-512"
+#define JWK_ALG_ECDSA_P_256 "ES256"
+#define JWK_ALG_ECDSA_P_384 "ES384"
+#define JWK_ALG_ECDSA_P_521 "ES521"
// JWK usages
#define JWK_USE_ENC "enc"
#define JWK_USE_SIG "sig"
// Define an unknown mechanism type
#define UNKNOWN_CK_MECHANISM CKM_VENDOR_DEFINED+1
@@ -220,16 +224,18 @@ NormalizeToken(const nsString& aName, ns
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) {
aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH);
+ } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDSA)) {
+ aDest.AssignLiteral(WEBCRYPTO_ALG_ECDSA);
// Named curve values
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
} else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
} else {
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -61,17 +61,18 @@ enum TelemetryAlgorithm {
TA_SHA_1 = 14,
TA_SHA_224 = 15,
TA_SHA_256 = 16,
TA_SHA_384 = 17,
TA_SHA_512 = 18,
// Later additions
TA_AES_KW = 19,
TA_ECDH = 20,
- TA_PBKDF2 = 21
+ TA_PBKDF2 = 21,
+ TA_ECDSA = 22,
};
// Convenience functions for extracting / converting information
// OOM-safe CryptoBuffer initialization, suitable for constructors
#define ATTEMPT_BUFFER_INIT(dst, src) \
if (!dst.Assign(src)) { \
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
@@ -777,20 +778,19 @@ public:
case CKM_SHA_1:
mMgfMechanism = CKG_MGF1_SHA1; break;
case CKM_SHA256:
mMgfMechanism = CKG_MGF1_SHA256; break;
case CKM_SHA384:
mMgfMechanism = CKG_MGF1_SHA384; break;
case CKM_SHA512:
mMgfMechanism = CKG_MGF1_SHA512; break;
- default: {
+ default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
- }
}
}
private:
CK_MECHANISM_TYPE mHashMechanism;
CK_MECHANISM_TYPE mMgfMechanism;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
@@ -950,59 +950,107 @@ private:
mSignature.Length());
equal = (cmp == 0);
}
mResultPromise->MaybeResolve(equal);
}
}
};
-class RsassaPkcs1Task : public WebCryptoTask
+class AsymmetricSignVerifyTask : public WebCryptoTask
{
public:
- RsassaPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
- CryptoKey& aKey,
- const CryptoOperationData& aSignature,
- const CryptoOperationData& aData,
- bool aSign)
+ AsymmetricSignVerifyTask(JSContext* aCx,
+ const ObjectOrString& aAlgorithm,
+ CryptoKey& aKey,
+ const CryptoOperationData& aSignature,
+ const CryptoOperationData& aData,
+ bool aSign)
: mOidTag(SEC_OID_UNKNOWN)
, mPrivKey(aKey.GetPrivateKey())
, mPubKey(aKey.GetPublicKey())
, mSign(aSign)
, mVerified(false)
+ , mEcdsa(false)
{
- Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
-
- CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
-
ATTEMPT_BUFFER_INIT(mData, aData);
if (!aSign) {
ATTEMPT_BUFFER_INIT(mSignature, aSignature);
}
- // Look up the SECOidTag based on the KeyAlgorithm
- // static_cast is safe because we only get here if the algorithm name
- // is RSASSA-PKCS1-v1_5, and that only happens if we've constructed
- // an RsaHashedKeyAlgorithm
- CK_MECHANISM_TYPE mech;
- mech = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash);
-
- switch (mech) {
- case CKM_SHA_1:
- mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
- case CKM_SHA256:
- mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
- case CKM_SHA384:
- mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
- case CKM_SHA512:
- mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
- default: {
- mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ nsString algName;
+ mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
+ if (NS_FAILED(mEarlyRv)) {
+ return;
+ }
+
+ // Look up the SECOidTag
+ if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+ mEcdsa = false;
+ Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
+ CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
+
+ // For RSA, the hash name comes from the key algorithm
+ nsString hashName = aKey.Algorithm().mRsa.mHash.mName;
+ switch (MapAlgorithmNameToMechanism(hashName)) {
+ case CKM_SHA_1:
+ mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
+ case CKM_SHA256:
+ mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
+ case CKM_SHA384:
+ mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
+ case CKM_SHA512:
+ mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
+ default:
+ mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ return;
+ }
+ } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+ mEcdsa = true;
+ Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
+ CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
+
+ // For ECDSA, the hash name comes from the algorithm parameter
+ RootedDictionary<EcdsaParams> params(aCx);
+ mEarlyRv = Coerce(aCx, params, aAlgorithm);
+ if (NS_FAILED(mEarlyRv)) {
+ mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
+
+ nsString hashName;
+ mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+ if (NS_FAILED(mEarlyRv)) {
+ mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+ return;
+ }
+
+ CK_MECHANISM_TYPE hashMechanism = MapAlgorithmNameToMechanism(hashName);
+ if (hashMechanism == UNKNOWN_CK_MECHANISM) {
+ mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+ return;
+ }
+
+ switch (hashMechanism) {
+ case CKM_SHA_1:
+ mOidTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE; break;
+ case CKM_SHA256:
+ mOidTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; break;
+ case CKM_SHA384:
+ mOidTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; break;
+ case CKM_SHA512:
+ mOidTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; break;
+ default:
+ mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ return;
+ }
+ } else {
+ // This shouldn't happen; CreateSignVerifyTask shouldn't create
+ // one of these unless it's for the above algorithms.
+ MOZ_ASSERT(false);
}
// Check that we have the appropriate key
if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
}
@@ -1010,61 +1058,71 @@ public:
private:
SECOidTag mOidTag;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
CryptoBuffer mSignature;
CryptoBuffer mData;
bool mSign;
bool mVerified;
+ bool mEcdsa;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
nsresult rv;
if (mSign) {
ScopedSECItem signature((SECItem*) PORT_Alloc(sizeof(SECItem)));
ScopedSGNContext ctx(SGN_NewContext(mOidTag, mPrivKey));
- if (!ctx) {
+ if (!signature.get() || !ctx.get()) {
return NS_ERROR_DOM_OPERATION_ERR;
}
- rv = MapSECStatus(SGN_Begin(ctx));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- rv = MapSECStatus(SGN_Update(ctx, mData.Elements(), mData.Length()));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- rv = MapSECStatus(SGN_End(ctx, signature));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
- } else {
- ScopedSECItem signature(mSignature.ToSECItem());
- if (!signature) {
- return NS_ERROR_DOM_UNKNOWN_ERR;
+ rv = MapSECStatus(SEC_SignData(signature, mData.Elements(),
+ mData.Length(), mPrivKey, mOidTag));
+
+ if (mEcdsa) {
+ // DER-decode the signature
+ int signatureLength = PK11_SignatureLen(mPrivKey);
+ ScopedSECItem rawSignature(DSAU_DecodeDerSigToLen(signature.get(),
+ signatureLength));
+ if (!rawSignature.get()) {
+ return NS_ERROR_DOM_OPERATION_ERR;
+ }
+
+ ATTEMPT_BUFFER_ASSIGN(mSignature, rawSignature);
+ } else {
+ ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
}
- ScopedVFYContext ctx(VFY_CreateContext(mPubKey, signature,
- mOidTag, nullptr));
- if (!ctx) {
- int err = PORT_GetError();
- if (err == SEC_ERROR_BAD_SIGNATURE) {
- mVerified = false;
- return NS_OK;
+ } else {
+ ScopedSECItem signature;
+
+ if (mEcdsa) {
+ // DER-encode the signature
+ ScopedSECItem rawSignature(mSignature.ToSECItem());
+ if (!rawSignature.get()) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
}
- return NS_ERROR_DOM_OPERATION_ERR;
+
+ signature = (SECItem*) PORT_Alloc(sizeof(SECItem));
+ if (!signature.get()) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
+ }
+ rv = MapSECStatus(DSAU_EncodeDerSigWithLen(signature, rawSignature,
+ rawSignature->len));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+ } else {
+ signature = mSignature.ToSECItem();
+ if (!signature) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
+ }
}
- rv = MapSECStatus(VFY_Begin(ctx));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- rv = MapSECStatus(VFY_Update(ctx, mData.Elements(), mData.Length()));
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
- rv = MapSECStatus(VFY_End(ctx));
+ rv = MapSECStatus(VFY_VerifyData(mData.Elements(), mData.Length(),
+ mPubKey, signature, mOidTag, nullptr));
mVerified = NS_SUCCEEDED(rv);
}
return NS_OK;
}
virtual void Resolve() MOZ_OVERRIDE
{
@@ -1656,21 +1714,32 @@ private:
}
}
return NS_OK;
}
virtual nsresult AfterCrypto() MOZ_OVERRIDE
{
+ uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
+ if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+ privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
+ publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
+ } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+ privateAllowedUsages = CryptoKey::SIGN;
+ publicAllowedUsages = CryptoKey::VERIFY;
+ }
+
// Check permissions for the requested operation
- if (mKey->GetKeyType() == CryptoKey::PRIVATE &&
- mKey->HasUsageOtherThan(CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY)) {
- return NS_ERROR_DOM_DATA_ERR;
- }
+ if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
+ mKey->HasUsageOtherThan(privateAllowedUsages)) ||
+ (mKey->GetKeyType() == CryptoKey::PUBLIC &&
+ mKey->HasUsageOtherThan(publicAllowedUsages))) {
+ return NS_ERROR_DOM_DATA_ERR;
+ }
mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
return NS_ERROR_DOM_DATA_ERR;
}
return NS_OK;
@@ -1986,17 +2055,18 @@ public:
// Set up params struct
mRsaParams.keySizeInBits = modulusLength;
bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
if (!converted) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
- } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+ } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+ algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
RootedDictionary<EcKeyGenParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv)) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
@@ -2009,17 +2079,18 @@ public:
mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
mMechanism = CKM_EC_KEY_PAIR_GEN;
} else {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
// Set key usages.
- if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+ if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+ algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
privateAllowedUsages = CryptoKey::SIGN;
publicAllowedUsages = CryptoKey::VERIFY;
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
publicAllowedUsages = 0;
@@ -2043,17 +2114,17 @@ public:
mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
publicAllowedUsages);
if (NS_FAILED(mEarlyRv)) {
return;
}
}
// If no usages ended up being allowed, DataError
- if (!mKeyPair.mPrivateKey.get()->HasAnyUsage() ||
+ if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
!mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
}
private:
CryptoKeyPair mKeyPair;
@@ -2171,20 +2242,19 @@ public:
}
// Check the given hash algorithm.
switch (MapAlgorithmNameToMechanism(hashName)) {
case CKM_SHA_1: mHashOidTag = SEC_OID_HMAC_SHA1; break;
case CKM_SHA256: mHashOidTag = SEC_OID_HMAC_SHA256; break;
case CKM_SHA384: mHashOidTag = SEC_OID_HMAC_SHA384; break;
case CKM_SHA512: mHashOidTag = SEC_OID_HMAC_SHA512; break;
- default: {
+ default:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
- }
}
ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
mLength = aLength >> 3; // bits to bytes
mIterations = params.mIterations;
}
private:
@@ -2539,18 +2609,20 @@ WebCryptoTask::CreateSignVerifyTask(JSCo
nsString algName;
nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(rv)) {
return new FailureTask(rv);
}
if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
- } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
- return new RsassaPkcs1Task(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
+ } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+ algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+ return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
+ aData, aSign);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
WebCryptoTask*
WebCryptoTask::CreateDigestTask(JSContext* aCx,
const ObjectOrString& aAlgorithm,
@@ -2613,17 +2685,18 @@ WebCryptoTask::CreateImportKeyTask(JSCon
algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
- } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+ } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+ algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
return new ImportEcKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
}
WebCryptoTask*
@@ -2689,17 +2762,18 @@ WebCryptoTask::CreateGenerateKeyTask(JSC
if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
- algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
+ algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
+ algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
}
WebCryptoTask*
WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -603,9 +603,59 @@ tv = {
// The Y coordinate is missing.
jwk_missing_y: {
kty: "EC",
crv: "P-256",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
}
},
+
+ // NIST ECDSA test vectors
+ // http://csrc.nist.gov/groups/STM/cavp/index.html
+ ecdsa_verify: {
+ pub_jwk: {
+ "kty": "EC",
+ "crv": "P-521",
+
+ // 0061387fd6b95914e885f912edfbb5fb274655027f216c4091ca83e19336740fd8
+ // 1aedfe047f51b42bdf68161121013e0d55b117a14e4303f926c8debb77a7fdaad1
+ "x": "AGE4f9a5WRTohfkS7fu1-ydGVQJ_IWxAkcqD4ZM2dA_Y" +
+ "Gu3-BH9RtCvfaBYRIQE-DVWxF6FOQwP5Jsjeu3en_arR",
+ // 00e7d0c75c38626e895ca21526b9f9fdf84dcecb93f2b233390550d2b1463b7ee3
+ // f58df7346435ff0434199583c97c665a97f12f706f2357da4b40288def888e59e6
+ "y": "AOfQx1w4Ym6JXKIVJrn5_fhNzsuT8rIzOQVQ0rFGO37j" +
+ "9Y33NGQ1_wQ0GZWDyXxmWpfxL3BvI1faS0Aoje-Ijlnm",
+ },
+
+ "data": util.hex2abv(
+ "9ecd500c60e701404922e58ab20cc002651fdee7cbc9336adda33e4c1088fab1" +
+ "964ecb7904dc6856865d6c8e15041ccf2d5ac302e99d346ff2f686531d255216" +
+ "78d4fd3f76bbf2c893d246cb4d7693792fe18172108146853103a51f824acc62" +
+ "1cb7311d2463c3361ea707254f2b052bc22cb8012873dcbb95bf1a5cc53ab89f"
+ ),
+ "sig": util.hex2abv(
+ "004de826ea704ad10bc0f7538af8a3843f284f55c8b946af9235af5af74f2b76e0" +
+ "99e4bc72fd79d28a380f8d4b4c919ac290d248c37983ba05aea42e2dd79fdd33e8" +
+ "0087488c859a96fea266ea13bf6d114c429b163be97a57559086edb64aed4a1859" +
+ "4b46fb9efc7fd25d8b2de8f09ca0587f54bd287299f47b2ff124aac566e8ee3b43"
+ ),
+
+ // Same as "sig", but with the last few octets set to 0
+ "sig_tampered": util.hex2abv(
+ "004de826ea704ad10bc0f7538af8a3843f284f55c8b946af9235af5af74f2b76e0" +
+ "99e4bc72fd79d28a380f8d4b4c919ac290d248c37983ba05aea42e2dd79fdd33e8" +
+ "0087488c859a96fea266ea13bf6d114c429b163be97a57559086edb64aed4a1859" +
+ "4b46fb9efc7fd25d8b2de8f09ca0587f54bd287299f47b2ff124aac56600000000"
+ )
+ },
+
+ ecdsa_bad: {
+ pub_jwk: {
+ "kty": "EC",
+ "crv": "P-521",
+ "x": "BhOH_WuVkU6IX5Eu37tfsnRlUCfyFsQJHKg-GTNnQP2B" +
+ "rt_gR_UbQr32gWESEBPg1VsRehTkMD-SbI3rt3p_2q0B",
+ "y": "AUNouOdGgHsraPNhXNeNdhpGTd15GPyN9R0iWWL98ePc" +
+ "JD4mUQD/DsEzNZ4zLkTdSa/Y5fOP6GEzVzQy0zwC+goD"
+ }
+ }
}
--- a/dom/crypto/test/test_WebCrypto.html
+++ b/dom/crypto/test/test_WebCrypto.html
@@ -1303,16 +1303,42 @@ TestArray.addTest(
}
return crypto.subtle.generateKey(alg, false, ["sign"]).then(doSign);
}
doCheckRSASSA().then(error(that), complete(that));
}
);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Test that we reject generating keys without any usage",
+ function() {
+ var that = this;
+ var alg = {
+ name: "RSA-OAEP",
+ hash: "SHA-256",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01])
+ };
+
+ function generateKey(usages) {
+ return crypto.subtle.generateKey(alg, false, usages);
+ }
+
+ generateKey(["encrypt", "decrypt"]).then(function () {
+ return generateKey(["encrypt"]);
+ }).then(function () {
+ return generateKey(["decrypt"]);
+ }).then(function () {
+ return generateKey(["sign"])
+ }, error(that)).then(error(that), complete(that));
+ }
+);
/*]]>*/</script>
</head>
<body>
<div id="content">
<div id="head">
<b>Web</b>Crypto<br>
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_ECDSA.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Generate an ECDSA key for named curve P-256",
+ function() {
+ var that = this;
+ var alg = { name: "ECDSA", namedCurve: "P-256" };
+ crypto.subtle.generateKey(alg, false, ["sign", "verify"]).then(
+ complete(that, function(x) {
+ return exists(x.publicKey) &&
+ (x.publicKey.algorithm.name == alg.name) &&
+ (x.publicKey.algorithm.namedCurve == alg.namedCurve) &&
+ (x.publicKey.type == "public") &&
+ x.publicKey.extractable &&
+ (x.publicKey.usages.length == 1) &&
+ (x.publicKey.usages[0] == "verify") &&
+ exists(x.privateKey) &&
+ (x.privateKey.algorithm.name == alg.name) &&
+ (x.privateKey.algorithm.namedCurve == alg.namedCurve) &&
+ (x.privateKey.type == "private") &&
+ !x.privateKey.extractable &&
+ (x.privateKey.usages.length == 1) &&
+ (x.privateKey.usages[0] == "sign")
+ }),
+ error(that)
+ );
+ }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "ECDSA JWK import and verify a known-good signature",
+ function() {
+ var that = this;
+ var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+
+ function doVerify(x) {
+ return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
+ }
+
+ crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
+ .then(doVerify)
+ .then(complete(that), error(that))
+ }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "ECDSA JWK import and reject a known-bad signature",
+ function() {
+ var that = this;
+ var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+
+ function doVerify(x) {
+ return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig_tampered,
+ tv.ecdsa_verify.data);
+ }
+
+ crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
+ .then(doVerify)
+ .then(complete(that, x => !x), error(that))
+ }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "ECDSA sign/verify round-trip",
+ function() {
+ var that = this;
+ var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
+ var pubKey;
+
+
+ function doSign(keyPair) {
+ pubKey = keyPair.publicKey;
+ return crypto.subtle.sign(alg, keyPair.privateKey, tv.ecdsa_verify.data);
+ }
+ function doVerify(sig) {
+ return crypto.subtle.verify(alg, pubKey, sig, tv.ecdsa_verify.data);
+ }
+
+ crypto.subtle.generateKey(alg, true, ["sign", "verify"])
+ .then(doSign)
+ .then(doVerify)
+ .then(complete(that), error(that))
+ }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Verify that ECDSA import fails with a known-bad public key",
+ function() {
+ var that = this;
+ var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+
+ function doVerify(x) {
+ return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
+ }
+
+ crypto.subtle.importKey("jwk", tv.ecdsa_bad.pub_jwk, alg, true, ["verify"])
+ .then(error(that), complete(that))
+ }
+);
+
+/*]]>*/</script>
+</head>
+
+<body>
+
+<div id="content">
+ <div id="head">
+ <b>Web</b>Crypto<br>
+ </div>
+
+ <div id="start" onclick="start();">RUN ALL</div>
+
+ <div id="resultDiv" class="content">
+ Summary:
+ <span class="pass"><span id="passN">0</span> passed, </span>
+ <span class="fail"><span id="failN">0</span> failed, </span>
+ <span class="pending"><span id="pendingN">0</span> pending.</span>
+ <br/>
+ <br/>
+
+ <table id="results">
+ <tr>
+ <th>Test</th>
+ <th>Result</th>
+ <th>Time</th>
+ </tr>
+ </table>
+
+ </div>
+
+ <div id="foot"></div>
+</div>
+
+</body>
+</html>
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -71,16 +71,17 @@ Event::ConstructorInit(EventTarget* aOwn
if (mIsMainThreadEvent && !sReturnHighResTimeStampIsSet) {
Preferences::AddBoolVarCache(&sReturnHighResTimeStamp,
"dom.event.highrestimestamp.enabled",
sReturnHighResTimeStamp);
sReturnHighResTimeStampIsSet = true;
}
mPrivateDataDuplicated = false;
+ mWantsPopupControlCheck = false;
if (aEvent) {
mEvent = aEvent;
mEventIsInternal = false;
}
else {
mEventIsInternal = true;
/*
@@ -650,22 +651,30 @@ PopupAllowedForEvent(const char *eventNa
startiter = enditer;
}
return false;
}
// static
PopupControlState
-Event::GetEventPopupControlState(WidgetEvent* aEvent)
+Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent)
{
// generally if an event handler is running, new windows are disallowed.
// check for exceptions:
PopupControlState abuse = openAbused;
+ if (aDOMEvent && aDOMEvent->InternalDOMEvent()->GetWantsPopupControlCheck()) {
+ nsAutoString type;
+ aDOMEvent->GetType(type);
+ if (PopupAllowedForEvent(NS_ConvertUTF16toUTF8(type).get())) {
+ return openAllowed;
+ }
+ }
+
switch(aEvent->mClass) {
case eBasicEventClass:
// For these following events only allow popups if they're
// triggered while handling user input. See
// nsPresShell::HandleEventInternal() for details.
if (EventStateManager::IsHandlingUserInput()) {
switch(aEvent->message) {
case NS_FORM_SELECTED :
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -25,16 +25,17 @@ class nsIDOMEventTarget;
class nsPresContext;
namespace mozilla {
namespace dom {
class EventTarget;
class ErrorEvent;
class ProgressEvent;
+class WantsPopupControlCheck;
// Dummy class so we can cast through it to get from nsISupports to
// Event subclasses with only two non-ambiguous static casts.
class EventBase : public nsIDOMEvent
{
};
class Event : public EventBase,
@@ -108,17 +109,18 @@ public:
// nsIDOMEvent Interface
NS_DECL_NSIDOMEVENT
void InitPresContextData(nsPresContext* aPresContext);
// Returns true if the event should be trusted.
bool Init(EventTarget* aGlobal);
- static PopupControlState GetEventPopupControlState(WidgetEvent* aEvent);
+ static PopupControlState GetEventPopupControlState(WidgetEvent* aEvent,
+ nsIDOMEvent* aDOMEvent = nullptr);
static void PopupAllowedEventsChanged();
static void Shutdown();
static const char* GetEventName(uint32_t aEventType);
static CSSIntPoint GetClientCoords(nsPresContext* aPresContext,
WidgetEvent* aEvent,
@@ -230,29 +232,62 @@ public:
nsIContent* aRelatedTarget);
protected:
// Internal helper functions
void SetEventType(const nsAString& aEventTypeArg);
already_AddRefed<nsIContent> GetTargetFromFrame();
+ friend class WantsPopupControlCheck;
+ void SetWantsPopupControlCheck(bool aCheck)
+ {
+ mWantsPopupControlCheck = aCheck;
+ }
+
+ bool GetWantsPopupControlCheck()
+ {
+ return IsTrusted() && mWantsPopupControlCheck;
+ }
+
/**
* IsChrome() returns true if aCx is chrome context or the event is created
* in chrome's thread. Otherwise, false.
*/
bool IsChrome(JSContext* aCx) const;
mozilla::WidgetEvent* mEvent;
nsRefPtr<nsPresContext> mPresContext;
nsCOMPtr<EventTarget> mExplicitOriginalTarget;
nsCOMPtr<nsPIDOMWindow> mOwner; // nsPIDOMWindow for now.
bool mEventIsInternal;
bool mPrivateDataDuplicated;
bool mIsMainThreadEvent;
+ // True when popup control check should rely on event.type, not
+ // WidgetEvent.message.
+ bool mWantsPopupControlCheck;
+};
+
+class MOZ_STACK_CLASS WantsPopupControlCheck
+{
+public:
+ WantsPopupControlCheck(nsIDOMEvent* aEvent) : mEvent(aEvent->InternalDOMEvent())
+ {
+ mOriginalWantsPopupControlCheck = mEvent->GetWantsPopupControlCheck();
+ mEvent->SetWantsPopupControlCheck(mEvent->IsTrusted());
+ }
+
+ ~WantsPopupControlCheck()
+ {
+ mEvent->SetWantsPopupControlCheck(mOriginalWantsPopupControlCheck);
+ }
+
+private:
+ Event* mEvent;
+ bool mOriginalWantsPopupControlCheck;
};
} // namespace dom
} // namespace mozilla
#define NS_FORWARD_TO_EVENT \
NS_FORWARD_NSIDOMEVENT(Event::)
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -971,17 +971,17 @@ EventListenerManager::HandleEventInterna
//Set the value of the internal PreventDefault flag properly based on aEventStatus
if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
aEvent->mFlags.mDefaultPrevented = true;
}
nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
Maybe<nsAutoPopupStatePusher> popupStatePusher;
if (mIsMainThreadELM) {
- popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent));
+ popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent, *aDOMEvent));
}
bool hasListener = false;
while (iter.HasMore()) {
if (aEvent->mFlags.mImmediatePropagationStopped) {
break;
}
Listener* listener = &iter.GetNext();
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -939,26 +939,31 @@ EventStateManager::ExecuteAccessKey(nsTA
}
return true;
}
}
}
return false;
}
-bool
-EventStateManager::GetAccessKeyLabelPrefix(nsAString& aPrefix)
+// static
+void
+EventStateManager::GetAccessKeyLabelPrefix(Element* aElement, nsAString& aPrefix)
{
aPrefix.Truncate();
nsAutoString separator, modifierText;
nsContentUtils::GetModifierSeparatorText(separator);
- nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
+ nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetDocShell();
int32_t modifierMask = GetAccessModifierMaskFor(container);
+ if (modifierMask == -1) {
+ return;
+ }
+
if (modifierMask & NS_MODIFIER_CONTROL) {
nsContentUtils::GetControlText(modifierText);
aPrefix.Append(modifierText + separator);
}
if (modifierMask & NS_MODIFIER_META) {
nsContentUtils::GetMetaText(modifierText);
aPrefix.Append(modifierText + separator);
}
@@ -969,17 +974,16 @@ EventStateManager::GetAccessKeyLabelPref
if (modifierMask & NS_MODIFIER_ALT) {
nsContentUtils::GetAltText(modifierText);
aPrefix.Append(modifierText + separator);
}
if (modifierMask & NS_MODIFIER_SHIFT) {
nsContentUtils::GetShiftText(modifierText);
aPrefix.Append(modifierText + separator);
}
- return !aPrefix.IsEmpty();
}
void
EventStateManager::HandleAccessKey(nsPresContext* aPresContext,
WidgetKeyboardEvent* aEvent,
nsEventStatus* aStatus,
nsIDocShellTreeItem* aBubbledFrom,
ProcessingAccessKeyState aAccessKeyState,
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -35,16 +35,17 @@ namespace mozilla {
class EnterLeaveDispatcher;
class EventStates;
class IMEContentObserver;
class ScrollbarsForWheel;
class WheelTransaction;
namespace dom {
class DataTransfer;
+class Element;
class TabParent;
} // namespace dom
class OverOutElementsWrapper MOZ_FINAL : public nsISupports
{
~OverOutElementsWrapper();
public:
@@ -164,17 +165,17 @@ public:
/**
* Get accesskey registered on the given element or 0 if there is none.
*
* @param aContent the given element (must not be null)
* @return registered accesskey
*/
uint32_t GetRegisteredAccessKey(nsIContent* aContent);
- bool GetAccessKeyLabelPrefix(nsAString& aPrefix);
+ static void GetAccessKeyLabelPrefix(dom::Element* aElement, nsAString& aPrefix);
nsresult SetCursor(int32_t aCursor, imgIContainer* aContainer,
bool aHaveHotspot, float aHotspotX, float aHotspotY,
nsIWidget* aWidget, bool aLockCursor);
static void StartHandlingUserInput()
{
++sUserInputEventDepth;
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -17,16 +17,17 @@ support-files =
[test_addEventListenerExtraArg.html]
[test_all_synthetic_events.html]
[test_bug226361.xhtml]
skip-if = buildapp == 'b2g'
[test_bug238987.html]
skip-if = buildapp == 'b2g'
[test_bug288392.html]
[test_bug299673-1.html]
+[test_bug1037990.html]
[test_bug299673-2.html]
[test_bug322588.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage
[test_bug328885.html]
[test_bug336682.js]
[test_bug336682_1.html]
[test_bug367781.html]
[test_bug368835.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug1037990.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1037990
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1037990</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1037990">Mozilla Bug 1037990</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+ /** Test for Bug 1037990 **/
+
+ var pre, node, detachedAccess, attachedAcess;
+
+ node = document.createElement('a');
+ node.href = 'http://example.org';
+ node.accessKey = 'e';
+ detachedAccess = node.accessKeyLabel;
+ info('[window.document] detached: ' + detachedAccess);
+ document.body.appendChild(node);
+ attachedAcess = node.accessKeyLabel;
+ info('[window.document] attached: ' + attachedAcess);
+ is(detachedAccess, attachedAcess, "Both values are same for the window.document");
+
+ var parser=new DOMParser();
+ var xmlDoc=parser.parseFromString("<root></root>","text/xml");
+ var nn = xmlDoc.createElementNS('http://www.w3.org/1999/xhtml','a');
+ nn.setAttribute('accesskey','t')
+ detachedAccess = nn.accessKeyLabel;
+ info('[xmlDoc] detached: ' + detachedAccess);
+ var root = xmlDoc.getElementsByTagName('root')[0];
+ root.appendChild(nn);
+ attachedAcess = nn.accessKeyLabel;
+ info('[xmlDoc] attached: ' + attachedAcess);
+ is(detachedAccess, attachedAcess, "Both values are same for the xmlDoc");
+
+ var myDoc = new Document();
+ var newnode = myDoc.createElementNS('http://www.w3.org/1999/xhtml','a');
+ newnode.href = 'http://example.org';
+ newnode.accessKey = 'f';
+ detachedAccess = newnode.accessKeyLabel;
+ info('[new document] detached: ' + detachedAccess);
+ myDoc.appendChild(newnode);
+ attachedAcess = newnode.accessKeyLabel;
+ info('[new document] attached: ' + attachedAcess);
+ is(detachedAccess, attachedAcess, "Both values are same for the new Document()");
+
+</script>
+</body>
+</html>
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -1,100 +1,365 @@
/* -*- 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 "Fetch.h"
#include "nsIStringStream.h"
+#include "nsIUnicodeDecoder.h"
#include "nsIUnicodeEncoder.h"
+#include "nsDOMString.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
#include "nsStringStream.h"
+#include "mozilla/ErrorResult.h"
#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/Headers.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/Request.h"
+#include "mozilla/dom/Response.h"
#include "mozilla/dom/URLSearchParams.h"
namespace mozilla {
namespace dom {
+namespace {
nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit,
+ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
+ nsIInputStream** aStream)
+{
+ aBuffer.ComputeLengthAndData();
+ //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
+ return NS_NewByteInputStream(aStream,
+ reinterpret_cast<char*>(aBuffer.Data()),
+ aBuffer.Length(), NS_ASSIGNMENT_COPY);
+}
+
+nsresult
+ExtractFromArrayBufferView(const ArrayBufferView& aBuffer,
+ nsIInputStream** aStream)
+{
+ aBuffer.ComputeLengthAndData();
+ //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
+ return NS_NewByteInputStream(aStream,
+ reinterpret_cast<char*>(aBuffer.Data()),
+ aBuffer.Length(), NS_ASSIGNMENT_COPY);
+}
+
+nsresult
+ExtractFromBlob(const File& aFile, nsIInputStream** aStream,
+ nsCString& aContentType)
+{
+ nsRefPtr<FileImpl> impl = aFile.Impl();
+ nsresult rv = impl->GetInternalStream(aStream);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString type;
+ impl->GetType(type);
+ aContentType = NS_ConvertUTF16toUTF8(type);
+ return NS_OK;
+}
+
+nsresult
+ExtractFromScalarValueString(const nsString& aStr,
+ nsIInputStream** aStream,
+ nsCString& aContentType)
+{
+ nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
+ if (!encoder) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t destBufferLen;
+ nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString encoded;
+ if (!encoded.SetCapacity(destBufferLen, fallible_t())) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char* destBuffer = encoded.BeginWriting();
+ int32_t srcLen = (int32_t) aStr.Length();
+ int32_t outLen = destBufferLen;
+ rv = encoder->Convert(aStr.get(), &srcLen, destBuffer, &outLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(outLen <= destBufferLen);
+ encoded.SetLength(outLen);
+
+ aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+
+ return NS_NewCStringInputStream(aStream, encoded);
+}
+
+nsresult
+ExtractFromURLSearchParams(const URLSearchParams& aParams,
+ nsIInputStream** aStream,
+ nsCString& aContentType)
+{
+ nsAutoString serialized;
+ aParams.Stringify(serialized);
+ aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+ return NS_NewStringInputStream(aStream, serialized);
+}
+}
+
+nsresult
+ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType)
+{
+ MOZ_ASSERT(aStream);
+
+ if (aBodyInit.IsArrayBuffer()) {
+ const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
+ return ExtractFromArrayBuffer(buf, aStream);
+ } else if (aBodyInit.IsArrayBufferView()) {
+ const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
+ return ExtractFromArrayBufferView(buf, aStream);
+ } else if (aBodyInit.IsBlob()) {
+ const File& blob = aBodyInit.GetAsBlob();
+ return ExtractFromBlob(blob, aStream, aContentType);
+ } else if (aBodyInit.IsScalarValueString()) {
+ nsAutoString str;
+ str.Assign(aBodyInit.GetAsScalarValueString());
+ return ExtractFromScalarValueString(str, aStream, aContentType);
+ } else if (aBodyInit.IsURLSearchParams()) {
+ URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
+ return ExtractFromURLSearchParams(params, aStream, aContentType);
+ }
+
+ NS_NOTREACHED("Should never reach here");
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType)
{
MOZ_ASSERT(aStream);
- nsresult rv;
- nsCOMPtr<nsIInputStream> byteStream;
if (aBodyInit.IsArrayBuffer()) {
const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
- buf.ComputeLengthAndData();
- //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
- rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
- reinterpret_cast<char*>(buf.Data()),
- buf.Length(), NS_ASSIGNMENT_COPY);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
+ return ExtractFromArrayBuffer(buf, aStream);
} else if (aBodyInit.IsArrayBufferView()) {
const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
- buf.ComputeLengthAndData();
- //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
- rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
- reinterpret_cast<char*>(buf.Data()),
- buf.Length(), NS_ASSIGNMENT_COPY);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
+ return ExtractFromArrayBufferView(buf, aStream);
+ } else if (aBodyInit.IsBlob()) {
+ const File& blob = aBodyInit.GetAsBlob();
+ return ExtractFromBlob(blob, aStream, aContentType);
} else if (aBodyInit.IsScalarValueString()) {
- nsString str = aBodyInit.GetAsScalarValueString();
-
- nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
- if (!encoder) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- int32_t destBufferLen;
- rv = encoder->GetMaxLength(str.get(), str.Length(), &destBufferLen);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- nsCString encoded;
- if (!encoded.SetCapacity(destBufferLen, fallible_t())) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- char* destBuffer = encoded.BeginWriting();
- int32_t srcLen = (int32_t) str.Length();
- int32_t outLen = destBufferLen;
- rv = encoder->Convert(str.get(), &srcLen, destBuffer, &outLen);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- MOZ_ASSERT(outLen <= destBufferLen);
- encoded.SetLength(outLen);
- rv = NS_NewCStringInputStream(getter_AddRefs(byteStream), encoded);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+ nsAutoString str;
+ str.Assign(aBodyInit.GetAsScalarValueString());
+ return ExtractFromScalarValueString(str, aStream, aContentType);
} else if (aBodyInit.IsURLSearchParams()) {
URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
- nsString serialized;
- params.Stringify(serialized);
- rv = NS_NewStringInputStream(getter_AddRefs(byteStream), serialized);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ return ExtractFromURLSearchParams(params, aStream, aContentType);
+ }
+
+ NS_NOTREACHED("Should never reach here");
+ return NS_ERROR_FAILURE;
+}
+
+namespace {
+nsresult
+DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded)
+{
+ nsCOMPtr<nsIUnicodeDecoder> decoder =
+ EncodingUtils::DecoderForEncoding("UTF-8");
+ if (!decoder) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t destBufferLen;
+ nsresult rv =
+ decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char16_t* destBuffer = aDecoded.BeginWriting();
+ int32_t srcLen = (int32_t) aBuffer.Length();
+ int32_t outLen = destBufferLen;
+ rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(outLen <= destBufferLen);
+ aDecoded.SetLength(outLen);
+ return NS_OK;
+}
+}
+
+template <class Derived>
+already_AddRefed<Promise>
+FetchBody<Derived>::ConsumeBody(ConsumeType aType, ErrorResult& aRv)
+{
+ nsRefPtr<Promise> promise = Promise::Create(DerivedClass()->GetParentObject(), aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ if (BodyUsed()) {
+ aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
+ return nullptr;
+ }
+
+ SetBodyUsed();
+
+ // While the spec says to do this asynchronously, all the body constructors
+ // right now only accept bodies whose streams are backed by an in-memory
+ // buffer that can be read without blocking. So I think this is fine.
+ nsCOMPtr<nsIInputStream> stream;
+ DerivedClass()->GetBody(getter_AddRefs(stream));
+
+ if (!stream) {
+ aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0,
+ NS_ASSIGNMENT_COPY);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
}
- aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+ }
+
+ AutoJSAPI api;
+ api.Init(DerivedClass()->GetParentObject());
+ JSContext* cx = api.cx();
+
+ // We can make this assertion because for now we only support memory backed
+ // structures for the body argument for a Request.
+ MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
+ nsCString buffer;
+ uint64_t len;
+ aRv = stream->Available(&len);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ aRv = NS_ReadInputStreamToString(stream, buffer, len);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
}
- MOZ_ASSERT(byteStream);
- byteStream.forget(aStream);
- return NS_OK;
+ buffer.SetLength(len);
+
+ switch (aType) {
+ case CONSUME_ARRAYBUFFER: {
+ JS::Rooted<JSObject*> arrayBuffer(cx);
+ arrayBuffer =
+ ArrayBuffer::Create(cx, buffer.Length(),
+ reinterpret_cast<const uint8_t*>(buffer.get()));
+ JS::Rooted<JS::Value> val(cx);
+ val.setObjectOrNull(arrayBuffer);
+ promise->MaybeResolve(cx, val);
+ return promise.forget();
+ }
+ case CONSUME_BLOB: {
+ // XXXnsm it is actually possible to avoid these duplicate allocations
+ // for the Blob case by having the Blob adopt the stream's memory
+ // directly, but I've not added a special case for now.
+ //
+ // FIXME(nsm): Use nsContentUtils::CreateBlobBuffer once blobs have been fixed on
+ // workers.
+ uint32_t blobLen = buffer.Length();
+ void* blobData = moz_malloc(blobLen);
+ nsRefPtr<File> blob;
+ if (blobData) {
+ memcpy(blobData, buffer.BeginReading(), blobLen);
+ blob = File::CreateMemoryFile(DerivedClass()->GetParentObject(), blobData, blobLen,
+ NS_ConvertUTF8toUTF16(mMimeType));
+ } else {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ promise->MaybeResolve(blob);
+ return promise.forget();
+ }
+ case CONSUME_JSON: {
+ nsAutoString decoded;
+ aRv = DecodeUTF8(buffer, decoded);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ JS::Rooted<JS::Value> json(cx);
+ if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
+ JS::Rooted<JS::Value> exn(cx);
+ if (JS_GetPendingException(cx, &exn)) {
+ JS_ClearPendingException(cx);
+ promise->MaybeReject(cx, exn);
+ }
+ }
+ promise->MaybeResolve(cx, json);
+ return promise.forget();
+ }
+ case CONSUME_TEXT: {
+ nsAutoString decoded;
+ aRv = DecodeUTF8(buffer, decoded);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ promise->MaybeResolve(decoded);
+ return promise.forget();
+ }
+ }
+
+ NS_NOTREACHED("Unexpected consume body type");
+ // Silence warnings.
+ return nullptr;
}
+template
+already_AddRefed<Promise>
+FetchBody<Request>::ConsumeBody(ConsumeType aType, ErrorResult& aRv);
+
+template
+already_AddRefed<Promise>
+FetchBody<Response>::ConsumeBody(ConsumeType aType, ErrorResult& aRv);
+
+template <class Derived>
+void
+FetchBody<Derived>::SetMimeType(ErrorResult& aRv)
+{
+ // Extract mime type.
+ nsTArray<nsCString> contentTypeValues;
+ MOZ_ASSERT(DerivedClass()->GetInternalHeaders());
+ DerivedClass()->GetInternalHeaders()->GetAll(NS_LITERAL_CSTRING("Content-Type"), contentTypeValues, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ // HTTP ABNF states Content-Type may have only one value.
+ // This is from the "parse a header value" of the fetch spec.
+ if (contentTypeValues.Length() == 1) {
+ mMimeType = contentTypeValues[0];
+ ToLowerCase(mMimeType);
+ }
+}
+
+template
+void
+FetchBody<Request>::SetMimeType(ErrorResult& aRv);
+
+template
+void
+FetchBody<Response>::SetMimeType(ErrorResult& aRv);
} // namespace dom
} // namespace mozilla
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -8,22 +8,99 @@
#include "mozilla/dom/UnionTypes.h"
class nsIInputStream;
namespace mozilla {
namespace dom {
+class Promise;
+
/*
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
* Stores content type in out param aContentType.
*/
nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType);
+
+/*
+ * Non-owning version.
+ */
+nsresult
+ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType);
+template <class Derived>
+class FetchBody {
+public:
+ bool
+ BodyUsed() { return mBodyUsed; }
+
+ already_AddRefed<Promise>
+ ArrayBuffer(ErrorResult& aRv)
+ {
+ return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
+ }
+
+ already_AddRefed<Promise>
+ Blob(ErrorResult& aRv)
+ {
+ return ConsumeBody(CONSUME_BLOB, aRv);
+ }
+
+ already_AddRefed<Promise>
+ Json(ErrorResult& aRv)
+ {
+ return ConsumeBody(CONSUME_JSON, aRv);
+ }
+
+ already_AddRefed<Promise>
+ Text(ErrorResult& aRv)
+ {
+ return ConsumeBody(CONSUME_TEXT, aRv);
+ }
+
+protected:
+ FetchBody()
+ : mBodyUsed(false)
+ {
+ }
+
+ void
+ SetBodyUsed()
+ {
+ mBodyUsed = true;
+ }
+
+ void
+ SetMimeType(ErrorResult& aRv);
+
+private:
+ enum ConsumeType
+ {
+ CONSUME_ARRAYBUFFER,
+ CONSUME_BLOB,
+ // FormData not supported right now,
+ CONSUME_JSON,
+ CONSUME_TEXT,
+ };
+
+ Derived*
+ DerivedClass() const
+ {
+ return static_cast<Derived*>(const_cast<FetchBody*>(this));
+ }
+
+ already_AddRefed<Promise>
+ ConsumeBody(ConsumeType aType, ErrorResult& aRv);
+
+ bool mBodyUsed;
+ nsCString mMimeType;
+};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Fetch_h
--- a/dom/fetch/Headers.cpp
+++ b/dom/fetch/Headers.cpp
@@ -5,23 +5,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Headers.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/Preferences.h"
-#include "nsCharSeparatedTokenizer.h"
-#include "nsContentUtils.h"
-#include "nsDOMString.h"
-#include "nsNetUtil.h"
-#include "nsPIDOMWindow.h"
-#include "nsReadableUtils.h"
-
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(Headers)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Headers)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Headers, mOwner)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Headers)
@@ -56,314 +49,65 @@ Headers::PrefEnabled(JSContext* aCx, JSO
}
// static
already_AddRefed<Headers>
Headers::Constructor(const GlobalObject& aGlobal,
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
ErrorResult& aRv)
{
- nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports());
+ nsRefPtr<InternalHeaders> ih = new InternalHeaders();
+ nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports(), ih);
if (!aInit.WasPassed()) {
return headers.forget();
}
if (aInit.Value().IsHeaders()) {
- headers->Fill(aInit.Value().GetAsHeaders(), aRv);
+ ih->Fill(*aInit.Value().GetAsHeaders().mInternalHeaders, aRv);
} else if (aInit.Value().IsByteStringSequenceSequence()) {
- headers->Fill(aInit.Value().GetAsByteStringSequenceSequence(), aRv);
+ ih->Fill(aInit.Value().GetAsByteStringSequenceSequence(), aRv);
} else if (aInit.Value().IsByteStringMozMap()) {
- headers->Fill(aInit.Value().GetAsByteStringMozMap(), aRv);
+ ih->Fill(aInit.Value().GetAsByteStringMozMap(), aRv);
}
if (aRv.Failed()) {
return nullptr;
}
return headers.forget();
}
// static
already_AddRefed<Headers>
Headers::Constructor(const GlobalObject& aGlobal,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
ErrorResult& aRv)
{
- nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports());
+ nsRefPtr<InternalHeaders> ih = new InternalHeaders();
+ nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports(), ih);
if (aInit.IsHeaders()) {
- headers->Fill(aInit.GetAsHeaders(), aRv);
+ ih->Fill(*(aInit.GetAsHeaders().get()->mInternalHeaders), aRv);
} else if (aInit.IsByteStringSequenceSequence()) {
- headers->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
+ ih->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
} else if (aInit.IsByteStringMozMap()) {
- headers->Fill(aInit.GetAsByteStringMozMap(), aRv);
+ ih->Fill(aInit.GetAsByteStringMozMap(), aRv);
}
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return headers.forget();
}
-Headers::Headers(const Headers& aOther)
- : mOwner(aOther.mOwner)
- , mGuard(aOther.mGuard)
-{
- ErrorResult result;
- Fill(aOther, result);
- MOZ_ASSERT(!result.Failed());
-}
-
-void
-Headers::Append(const nsACString& aName, const nsACString& aValue,
- ErrorResult& aRv)
-{
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
-
- if (IsInvalidMutableHeader(lowerName, &aValue, aRv)) {
- return;
- }
-
- mList.AppendElement(Entry(lowerName, aValue));
-}
-
-void
-Headers::Delete(const nsACString& aName, ErrorResult& aRv)
-{
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
-
- if (IsInvalidMutableHeader(lowerName, nullptr, aRv)) {
- return;
- }
-
- // remove in reverse order to minimize copying
- for (int32_t i = mList.Length() - 1; i >= 0; --i) {
- if (lowerName == mList[i].mName) {
- mList.RemoveElementAt(i);
- }
- }
-}
-
-void
-Headers::Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
-{
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
-
- if (IsInvalidName(lowerName, aRv)) {
- return;
- }
-
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (lowerName == mList[i].mName) {
- aValue = mList[i].mValue;
- return;
- }
- }
-
- // No value found, so return null to content
- aValue.SetIsVoid(true);
-}
-
-void
-Headers::GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
- ErrorResult& aRv) const
-{
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
-
- if (IsInvalidName(lowerName, aRv)) {
- return;
- }
-
- aResults.SetLength(0);
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (lowerName == mList[i].mName) {
- aResults.AppendElement(mList[i].mValue);
- }
- }
-}
-
-bool
-Headers::Has(const nsACString& aName, ErrorResult& aRv) const
-{
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
-
- if (IsInvalidName(lowerName, aRv)) {
- return false;
- }
-
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (lowerName == mList[i].mName) {
- return true;
- }
- }
- return false;
-}
-
-void
-Headers::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
-{
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
-
- if (IsInvalidMutableHeader(lowerName, &aValue, aRv)) {
- return;
- }
-
- int32_t firstIndex = INT32_MAX;
-
- // remove in reverse order to minimize copying
- for (int32_t i = mList.Length() - 1; i >= 0; --i) {
- if (lowerName == mList[i].mName) {
- firstIndex = std::min(firstIndex, i);
- mList.RemoveElementAt(i);
- }
- }
-
- if (firstIndex < INT32_MAX) {
- Entry* entry = mList.InsertElementAt(firstIndex);
- entry->mName = lowerName;
- entry->mValue = aValue;
- } else {
- mList.AppendElement(Entry(lowerName, aValue));
- }
-}
-
-void
-Headers::Clear()
-{
- mList.Clear();
-}
-
-void
-Headers::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
-{
- // Rather than re-validate all current headers, just require code to set
- // this prior to populating the Headers object. Allow setting immutable
- // late, though, as that is pretty much required to have a useful, immutable
- // headers object.
- if (aGuard != HeadersGuardEnum::Immutable && mList.Length() > 0) {
- aRv.Throw(NS_ERROR_FAILURE);
- }
- mGuard = aGuard;
-}
-
JSObject*
Headers::WrapObject(JSContext* aCx)
{
return mozilla::dom::HeadersBinding::Wrap(aCx, this);
}
Headers::~Headers()
{
}
-
-// static
-bool
-Headers::IsSimpleHeader(const nsACString& aName, const nsACString* aValue)
-{
- // Note, we must allow a null content-type value here to support
- // get("content-type"), but the IsInvalidValue() check will prevent null
- // from being set or appended.
- return aName.EqualsLiteral("accept") ||
- aName.EqualsLiteral("accept-language") ||
- aName.EqualsLiteral("content-language") ||
- (aName.EqualsLiteral("content-type") &&
- (!aValue || nsContentUtils::IsAllowedNonCorsContentType(*aValue)));
-}
-
-//static
-bool
-Headers::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
-{
- if (!NS_IsValidHTTPToken(aName)) {
- NS_ConvertUTF8toUTF16 label(aName);
- aRv.ThrowTypeError(MSG_INVALID_HEADER_NAME, &label);
- return true;
- }
-
- return false;
-}
-
-// static
-bool
-Headers::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
-{
- if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
- NS_ConvertUTF8toUTF16 label(aValue);
- aRv.ThrowTypeError(MSG_INVALID_HEADER_VALUE, &label);
- return true;
- }
- return false;
-}
-
-bool
-Headers::IsImmutable(ErrorResult& aRv) const
-{
- if (mGuard == HeadersGuardEnum::Immutable) {
- aRv.ThrowTypeError(MSG_HEADERS_IMMUTABLE);
- return true;
- }
- return false;
-}
-
-bool
-Headers::IsForbiddenRequestHeader(const nsACString& aName) const
-{
- return mGuard == HeadersGuardEnum::Request &&
- nsContentUtils::IsForbiddenRequestHeader(aName);
-}
-
-bool
-Headers::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
- const nsACString* aValue) const
-{
- return mGuard == HeadersGuardEnum::Request_no_cors &&
- !IsSimpleHeader(aName, aValue);
-}
-
-bool
-Headers::IsForbiddenResponseHeader(const nsACString& aName) const
-{
- return mGuard == HeadersGuardEnum::Response &&
- nsContentUtils::IsForbiddenResponseHeader(aName);
-}
-
-void
-Headers::Fill(const Headers& aInit, ErrorResult& aRv)
-{
- const nsTArray<Entry>& list = aInit.mList;
- for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
- const Entry& entry = list[i];
- Append(entry.mName, entry.mValue, aRv);
- }
-}
-
-void
-Headers::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
-{
- for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
- const Sequence<nsCString>& tuple = aInit[i];
- if (tuple.Length() != 2) {
- aRv.ThrowTypeError(MSG_INVALID_HEADER_SEQUENCE);
- return;
- }
- Append(tuple[0], tuple[1], aRv);
- }
-}
-
-void
-Headers::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
-{
- nsTArray<nsString> keys;
- aInit.GetKeys(keys);
- for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
- Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
- }
-}
} // namespace dom
} // namespace mozilla
--- a/dom/fetch/Headers.h
+++ b/dom/fetch/Headers.h
@@ -8,121 +8,122 @@
#define mozilla_dom_Headers_h
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/UnionTypes.h"
#include "nsClassHashtable.h"
#include "nsWrapperCache.h"
+#include "InternalHeaders.h"
+
class nsPIDOMWindow;
namespace mozilla {
class ErrorResult;
namespace dom {
template<typename T> class MozMap;
class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
+/**
+ * This Headers class is only used to represent the content facing Headers
+ * object. It is actually backed by an InternalHeaders implementation. Gecko
+ * code should NEVER use this, except in the Request and Response
+ * implementations, where they must always be created from the backing
+ * InternalHeaders object.
+ */
class Headers MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Headers)
-private:
- struct Entry
- {
- Entry(const nsACString& aName, const nsACString& aValue)
- : mName(aName)
- , mValue(aValue)
- { }
+ friend class Request;
+ friend class Response;
- Entry() { }
-
- nsCString mName;
- nsCString mValue;
- };
-
+private:
nsCOMPtr<nsISupports> mOwner;
- HeadersGuardEnum mGuard;
- nsTArray<Entry> mList;
+ nsRefPtr<InternalHeaders> mInternalHeaders;
public:
- explicit Headers(nsISupports* aOwner, HeadersGuardEnum aGuard = HeadersGuardEnum::None)
+ explicit Headers(nsISupports* aOwner, InternalHeaders* aInternalHeaders)
: mOwner(aOwner)
- , mGuard(aGuard)
+ , mInternalHeaders(aInternalHeaders)
{
}
- explicit Headers(const Headers& aOther);
+ explicit Headers(const Headers& aOther) MOZ_DELETE;
static bool PrefEnabled(JSContext* cx, JSObject* obj);
static already_AddRefed<Headers>
Constructor(const GlobalObject& aGlobal,
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
ErrorResult& aRv);
static already_AddRefed<Headers>
Constructor(const GlobalObject& aGlobal,
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
ErrorResult& aRv);
void Append(const nsACString& aName, const nsACString& aValue,
- ErrorResult& aRv);
- void Delete(const nsACString& aName, ErrorResult& aRv);
- void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const;
+ ErrorResult& aRv)
+ {
+ mInternalHeaders->Append(aName, aValue, aRv);
+ }
+
+ void Delete(const nsACString& aName, ErrorResult& aRv)
+ {
+ mInternalHeaders->Delete(aName, aRv);
+ }
+
+ void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
+ {
+ mInternalHeaders->Get(aName, aValue, aRv);
+ }
+
void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
- ErrorResult& aRv) const;
- bool Has(const nsACString& aName, ErrorResult& aRv) const;
- void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);
+ ErrorResult& aRv) const
+ {
+ mInternalHeaders->GetAll(aName, aResults, aRv);
+ }
- void Clear();
+ bool Has(const nsACString& aName, ErrorResult& aRv) const
+ {
+ return mInternalHeaders->Has(aName, aRv);
+ }
+
+ void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
+ {
+ mInternalHeaders->Set(aName, aValue, aRv);
+ }
// ChromeOnly
- HeadersGuardEnum Guard() const { return mGuard; }
- void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
+ HeadersGuardEnum Guard() const
+ {
+ return mInternalHeaders->Guard();
+ }
+
+ void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
+ {
+ mInternalHeaders->SetGuard(aGuard, aRv);
+ }
virtual JSObject* WrapObject(JSContext* aCx);
nsISupports* GetParentObject() const { return mOwner; }
- void Fill(const Headers& aInit, ErrorResult& aRv);
private:
- // Since Headers is also an nsISupports, the above constructor can
- // accidentally be invoked as new Headers(Headers*[, implied None guard]) when
- // the intention is to use the copy constructor. Explicitly disallow it.
- Headers(Headers* aOther) MOZ_DELETE;
-
virtual ~Headers();
- static bool IsSimpleHeader(const nsACString& aName,
- const nsACString* aValue = nullptr);
- static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
- static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
- bool IsImmutable(ErrorResult& aRv) const;
- bool IsForbiddenRequestHeader(const nsACString& aName) const;
- bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
- const nsACString* aValue = nullptr) const;
- bool IsForbiddenResponseHeader(const nsACString& aName) const;
-
- bool IsInvalidMutableHeader(const nsACString& aName,
- const nsACString* aValue,
- ErrorResult& aRv) const
+ InternalHeaders*
+ GetInternalHeaders() const
{
- return IsInvalidName(aName, aRv) ||
- (aValue && IsInvalidValue(*aValue, aRv)) ||
- IsImmutable(aRv) ||
- IsForbiddenRequestHeader(aName) ||
- IsForbiddenRequestNoCorsHeader(aName, aValue) ||
- IsForbiddenResponseHeader(aName);
+ return mInternalHeaders;
}
-
- void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
- void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Headers_h
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalHeaders.cpp
@@ -0,0 +1,272 @@
+/* -*- 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/InternalHeaders.h"
+
+#include "mozilla/ErrorResult.h"
+
+#include "nsCharSeparatedTokenizer.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "nsReadableUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+InternalHeaders::Append(const nsACString& aName, const nsACString& aValue,
+ ErrorResult& aRv)
+{
+ nsAutoCString lowerName;
+ ToLowerCase(aName, lowerName);
+
+ if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
+ return;
+ }
+
+ mList.AppendElement(Entry(lowerName, aValue));
+}
+
+void
+InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
+{
+ nsAutoCString lowerName;
+ ToLowerCase(aName, lowerName);
+
+ if (IsInvalidMutableHeader(lowerName, aRv)) {
+ return;
+ }
+
+ // remove in reverse order to minimize copying
+ for (int32_t i = mList.Length() - 1; i >= 0; --i) {
+ if (lowerName == mList[i].mName) {
+ mList.RemoveElementAt(i);
+ }
+ }
+}
+
+void
+InternalHeaders::Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
+{
+ nsAutoCString lowerName;
+ ToLowerCase(aName, lowerName);
+
+ if (IsInvalidName(lowerName, aRv)) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < mList.Length(); ++i) {
+ if (lowerName == mList[i].mName) {
+ aValue = mList[i].mValue;
+ return;
+ }
+ }
+
+ // No value found, so return null to content
+ aValue.SetIsVoid(true);
+}
+
+void
+InternalHeaders::GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
+ ErrorResult& aRv) const
+{
+ nsAutoCString lowerName;
+ ToLowerCase(aName, lowerName);
+
+ if (IsInvalidName(lowerName, aRv)) {
+ return;
+ }
+
+ aResults.SetLength(0);
+ for (uint32_t i = 0; i < mList.Length(); ++i) {
+ if (lowerName == mList[i].mName) {
+ aResults.AppendElement(mList[i].mValue);
+ }
+ }
+}
+
+bool
+InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
+{
+ nsAutoCString lowerName;
+ ToLowerCase(aName, lowerName);
+
+ if (IsInvalidName(lowerName, aRv)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < mList.Length(); ++i) {
+ if (lowerName == mList[i].mName) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
+{
+ nsAutoCString lowerName;
+ ToLowerCase(aName, lowerName);
+
+ if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
+ return;
+ }
+
+ int32_t firstIndex = INT32_MAX;
+
+ // remove in reverse order to minimize copying
+ for (int32_t i = mList.Length() - 1; i >= 0; --i) {
+ if (lowerName == mList[i].mName) {
+ firstIndex = std::min(firstIndex, i);
+ mList.RemoveElementAt(i);
+ }
+ }
+
+ if (firstIndex < INT32_MAX) {
+ Entry* entry = mList.InsertElementAt(firstIndex);
+ entry->mName = lowerName;
+ entry->mValue = aValue;
+ } else {
+ mList.AppendElement(Entry(lowerName, aValue));
+ }
+}
+
+void
+InternalHeaders::Clear()
+{
+ mList.Clear();
+}
+
+void
+InternalHeaders::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
+{
+ // Rather than re-validate all current headers, just require code to set
+ // this prior to populating the InternalHeaders object. Allow setting immutable
+ // late, though, as that is pretty much required to have a useful, immutable
+ // headers object.
+ if (aGuard != HeadersGuardEnum::Immutable && mList.Length() > 0) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+ mGuard = aGuard;
+}
+
+InternalHeaders::~InternalHeaders()
+{
+}
+
+// static
+bool
+InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue)
+{
+ // Note, we must allow a null content-type value here to support
+ // get("content-type"), but the IsInvalidValue() check will prevent null
+ // from being set or appended.
+ return aName.EqualsLiteral("accept") ||
+ aName.EqualsLiteral("accept-language") ||
+ aName.EqualsLiteral("content-language") ||
+ (aName.EqualsLiteral("content-type") &&
+ nsContentUtils::IsAllowedNonCorsContentType(aValue));
+}
+
+//static
+bool
+InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
+{
+ if (!NS_IsValidHTTPToken(aName)) {
+ NS_ConvertUTF8toUTF16 label(aName);
+ aRv.ThrowTypeError(MSG_INVALID_HEADER_NAME, &label);
+ return true;
+ }
+
+ return false;
+}
+
+// static
+bool
+InternalHeaders::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
+{
+ if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
+ NS_ConvertUTF8toUTF16 label(aValue);
+ aRv.ThrowTypeError(MSG_INVALID_HEADER_VALUE, &label);
+ return true;
+ }
+ return false;
+}
+
+bool
+InternalHeaders::IsImmutable(ErrorResult& aRv) const
+{
+ if (mGuard == HeadersGuardEnum::Immutable) {
+ aRv.ThrowTypeError(MSG_HEADERS_IMMUTABLE);
+ return true;
+ }
+ return false;
+}
+
+bool
+InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName) const
+{
+ return mGuard == HeadersGuardEnum::Request &&
+ nsContentUtils::IsForbiddenRequestHeader(aName);
+}
+
+bool
+InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName) const
+{
+ return mGuard == HeadersGuardEnum::Request_no_cors &&
+ !IsSimpleHeader(aName, EmptyCString());
+}
+
+bool
+InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
+ const nsACString& aValue) const
+{
+ return mGuard == HeadersGuardEnum::Request_no_cors &&
+ !IsSimpleHeader(aName, aValue);
+}
+
+bool
+InternalHeaders::IsForbiddenResponseHeader(const nsACString& aName) const
+{
+ return mGuard == HeadersGuardEnum::Response &&
+ nsContentUtils::IsForbiddenResponseHeader(aName);
+}
+
+void
+InternalHeaders::Fill(const InternalHeaders& aInit, ErrorResult& aRv)
+{
+ const nsTArray<Entry>& list = aInit.mList;
+ for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
+ const Entry& entry = list[i];
+ Append(entry.mName, entry.mValue, aRv);
+ }
+}
+
+void
+InternalHeaders::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
+{
+ for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
+ const Sequence<nsCString>& tuple = aInit[i];
+ if (tuple.Length() != 2) {
+ aRv.ThrowTypeError(MSG_INVALID_HEADER_SEQUENCE);
+ return;
+ }
+ Append(tuple[0], tuple[1], aRv);
+ }
+}
+
+void
+InternalHeaders::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
+{
+ nsTArray<nsString> keys;
+ aInit.GetKeys(keys);
+ for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
+ Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
+ }
+}
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalHeaders.h
@@ -0,0 +1,116 @@
+/* -*- 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_InternalHeaders_h
+#define mozilla_dom_InternalHeaders_h
+
+// needed for HeadersGuardEnum.
+#include "mozilla/dom/HeadersBinding.h"
+#include "mozilla/dom/UnionTypes.h"
+
+#include "nsClassHashtable.h"
+#include "nsWrapperCache.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+template<typename T> class MozMap;
+class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
+
+class InternalHeaders MOZ_FINAL
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalHeaders)
+
+private:
+ struct Entry
+ {
+ Entry(const nsACString& aName, const nsACString& aValue)
+ : mName(aName)
+ , mValue(aValue)
+ { }
+
+ Entry() { }
+
+ nsCString mName;
+ nsCString mValue;
+ };
+
+ HeadersGuardEnum mGuard;
+ nsTArray<Entry> mList;
+
+public:
+ explicit InternalHeaders(HeadersGuardEnum aGuard = HeadersGuardEnum::None)
+ : mGuard(aGuard)
+ {
+ }
+
+ explicit InternalHeaders(const InternalHeaders& aOther)
+ : mGuard(aOther.mGuard)
+ {
+ ErrorResult result;
+ Fill(aOther, result);
+ MOZ_ASSERT(!result.Failed());
+ }
+
+ void Append(const nsACString& aName, const nsACString& aValue,
+ ErrorResult& aRv);
+ void Delete(const nsACString& aName, ErrorResult& aRv);
+ void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const;
+ void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
+ ErrorResult& aRv) const;
+ bool Has(const nsACString& aName, ErrorResult& aRv) const;
+ void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);
+
+ void Clear();
+
+ HeadersGuardEnum Guard() const { return mGuard; }
+ void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
+
+ void Fill(const InternalHeaders& aInit, ErrorResult& aRv);
+ void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
+ void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
+private:
+ virtual ~InternalHeaders();
+
+ static bool IsSimpleHeader(const nsACString& aName,
+ const nsACString& aValue);
+ static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
+ static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
+ bool IsImmutable(ErrorResult& aRv) const;
+ bool IsForbiddenRequestHeader(const nsACString& aName) const;
+ bool IsForbiddenRequestNoCorsHeader(const nsACString& aName) const;
+ bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
+ const nsACString& aValue) const;
+ bool IsForbiddenResponseHeader(const nsACString& aName) const;
+
+ bool IsInvalidMutableHeader(const nsACString& aName,
+ ErrorResult& aRv) const
+ {
+ return IsInvalidMutableHeader(aName, EmptyCString(), aRv);
+ }
+
+ bool IsInvalidMutableHeader(const nsACString& aName,
+ const nsACString& aValue,
+ ErrorResult& aRv) const
+ {
+ return IsInvalidName(aName, aRv) ||
+ IsInvalidValue(aValue, aRv) ||
+ IsImmutable(aRv) ||
+ IsForbiddenRequestHeader(aName) ||
+ IsForbiddenRequestNoCorsHeader(aName, aValue) ||
+ IsForbiddenResponseHeader(aName);
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_InternalHeaders_h
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -19,17 +19,17 @@ namespace dom {
// The global is used to extract the principal.
already_AddRefed<InternalRequest>
InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const
{
nsRefPtr<InternalRequest> copy = new InternalRequest();
copy->mURL.Assign(mURL);
copy->SetMethod(mMethod);
- copy->mHeaders = new Headers(*mHeaders);
+ copy->mHeaders = new InternalHeaders(*mHeaders);
copy->mBodyStream = mBodyStream;
copy->mPreserveContentCodings = true;
if (NS_IsMainThread()) {
nsIPrincipal* principal = aGlobal->PrincipalOrNull();
MOZ_ASSERT(principal);
aRv = nsContentUtils::GetASCIIOrigin(principal, copy->mOrigin);
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -1,17 +1,18 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_InternalRequest_h
#define mozilla_dom_InternalRequest_h
-#include "mozilla/dom/Headers.h"
+#include "mozilla/dom/HeadersBinding.h"
+#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/UnionTypes.h"
#include "nsIContentPolicy.h"
#include "nsIInputStream.h"
#include "nsISupportsImpl.h"
class nsIDocument;
@@ -50,17 +51,17 @@ public:
{
RESPONSETAINT_BASIC,
RESPONSETAINT_CORS,
RESPONSETAINT_OPAQUE,
};
explicit InternalRequest()
: mMethod("GET")
- , mHeaders(new Headers(nullptr, HeadersGuardEnum::None))
+ , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
, mContextFrameType(FRAMETYPE_NONE)
, mReferrerType(REFERRER_CLIENT)
, mMode(RequestMode::No_cors)
, mCredentialsMode(RequestCredentials::Omit)
, mResponseTainting(RESPONSETAINT_BASIC)
, mRedirectCount(0)
, mAuthenticationFlag(false)
, mForceOriginHeader(false)
@@ -154,16 +155,22 @@ public:
}
bool
IsSynchronous() const
{
return mSynchronous;
}
+ RequestMode
+ Mode() const
+ {
+ return mMode;
+ }
+
void
SetMode(RequestMode aMode)
{
mMode = aMode;
}
void
SetCredentialsMode(RequestCredentials aCredentialsMode)
@@ -172,18 +179,18 @@ public:
}
nsContentPolicyType
GetContext() const
{
return mContext;
}
- Headers*
- Headers_()
+ InternalHeaders*
+ Headers()
{
return mHeaders;
}
bool
ForceOriginHeader()
{
return mForceOriginHeader;
@@ -220,17 +227,17 @@ private:
void
SetURL(const nsACString& aURL)
{
mURL.Assign(aURL);
}
nsCString mMethod;
nsCString mURL;
- nsRefPtr<Headers> mHeaders;
+ nsRefPtr<InternalHeaders> mHeaders;
nsCOMPtr<nsIInputStream> mBodyStream;
// nsContentPolicyType does not cover the complete set defined in the spec,
// but it is a good start.
nsContentPolicyType mContext;
nsCString mOrigin;
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalResponse.cpp
@@ -0,0 +1,24 @@
+/* -*- 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 "InternalResponse.h"
+
+#include "nsIDOMFile.h"
+
+#include "mozilla/dom/InternalHeaders.h"
+
+namespace mozilla {
+namespace dom {
+
+InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
+ : mType(ResponseType::Default)
+ , mStatus(aStatus)
+ , mStatusText(aStatusText)
+ , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalResponse.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_InternalResponse_h
+#define mozilla_dom_InternalResponse_h
+
+#include "nsIInputStream.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/dom/ResponseBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class InternalHeaders;
+
+class InternalResponse MOZ_FINAL
+{
+ friend class FetchDriver;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalResponse)
+
+ InternalResponse(uint16_t aStatus, const nsACString& aStatusText);
+
+ explicit InternalResponse(const InternalResponse& aOther) MOZ_DELETE;
+
+ static already_AddRefed<InternalResponse>
+ NetworkError()
+ {
+ nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
+ response->mType = ResponseType::Error;
+ return response.forget();
+ }
+
+ ResponseType
+ Type() const
+ {
+ return mType;
+ }
+
+ bool
+ IsError() const
+ {
+ return Type() == ResponseType::Error;
+ }
+
+ // FIXME(nsm): Return with exclude fragment.
+ nsCString&
+ GetUrl()
+ {
+ return mURL;
+ }
+
+ uint16_t
+ GetStatus() const
+ {
+ return mStatus;
+ }
+
+ const nsCString&
+ GetStatusText() const
+ {
+ return mStatusText;
+ }
+
+ InternalHeaders*
+ Headers()
+ {
+ return mHeaders;
+ }
+
+ void
+ GetBody(nsIInputStream** aStream)
+ {
+ nsCOMPtr<nsIInputStream> stream = mBody;
+ stream.forget(aStream);
+ }
+
+ void
+ SetBody(nsIInputStream* aBody)
+ {
+ mBody = aBody;
+ }
+
+private:
+ ~InternalResponse()
+ { }
+
+ ResponseType mType;
+ nsCString mTerminationReason;
+ nsCString mURL;
+ const uint16_t mStatus;
+ const nsCString mStatusText;
+ nsRefPtr<InternalHeaders> mHeaders;
+ nsCOMPtr<nsIInputStream> mBody;
+ nsCString mContentType;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_InternalResponse_h
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -1,52 +1,43 @@
/* -*- 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 "Request.h"
-#include "nsIUnicodeDecoder.h"
#include "nsIURI.h"
-
-#include "nsDOMString.h"
-#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
-#include "nsStreamUtils.h"
-#include "nsStringStream.h"
#include "mozilla/ErrorResult.h"
-#include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/dom/File.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/URL.h"
#include "mozilla/dom/workers/bindings/URL.h"
-// dom/workers
#include "WorkerPrivate.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest)
- : mOwner(aOwner)
+ : FetchBody<Request>()
+ , mOwner(aOwner)
, mRequest(aRequest)
- , mBodyUsed(false)
{
}
Request::~Request()
{
}
already_AddRefed<InternalRequest>
@@ -159,274 +150,95 @@ Request::Constructor(const GlobalObject&
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
return nullptr;
}
ToUpperCase(method);
request->SetMethod(method);
}
- nsRefPtr<Request> domRequest = new Request(global, request);
- nsRefPtr<Headers> domRequestHeaders = domRequest->Headers_();
+ nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
- nsRefPtr<Headers> headers;
+ nsRefPtr<InternalHeaders> headers;
if (aInit.mHeaders.WasPassed()) {
- headers = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
+ nsRefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
if (aRv.Failed()) {
return nullptr;
}
+ headers = h->GetInternalHeaders();
} else {
- headers = new Headers(*domRequestHeaders);
+ headers = new InternalHeaders(*requestHeaders);
}
- domRequestHeaders->Clear();
+ requestHeaders->Clear();
- if (domRequest->Mode() == RequestMode::No_cors) {
+ if (request->Mode() == RequestMode::No_cors) {
nsCString method;
- domRequest->GetMethod(method);
+ request->GetMethod(method);
ToLowerCase(method);
if (!method.EqualsASCII("get") &&
!method.EqualsASCII("head") &&
!method.EqualsASCII("post")) {
NS_ConvertUTF8toUTF16 label(method);
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
return nullptr;
}
- domRequestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
+ requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
if (aRv.Failed()) {
return nullptr;
}
}
- domRequestHeaders->Fill(*headers, aRv);
+ requestHeaders->Fill(*headers, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (aInit.mBody.WasPassed()) {
- const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
+ const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
nsCOMPtr<nsIInputStream> stream;
nsCString contentType;
aRv = ExtractByteStreamFromBody(bodyInit,
getter_AddRefs(stream), contentType);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
request->SetBody(stream);
if (!contentType.IsVoid() &&
- !domRequestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
- domRequestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
- contentType, aRv);
+ !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
+ requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
+ contentType, aRv);
}
if (aRv.Failed()) {
return nullptr;
}
}
- // Extract mime type.
- nsTArray<nsCString> contentTypeValues;
- domRequestHeaders->GetAll(NS_LITERAL_CSTRING("Content-Type"),
- contentTypeValues, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- // HTTP ABNF states Content-Type may have only one value.
- // This is from the "parse a header value" of the fetch spec.
- if (contentTypeValues.Length() == 1) {
- domRequest->mMimeType = contentTypeValues[0];
- ToLowerCase(domRequest->mMimeType);
- }
-
+ nsRefPtr<Request> domRequest = new Request(global, request);
+ domRequest->SetMimeType(aRv);
return domRequest.forget();
}
already_AddRefed<Request>
Request::Clone() const
{
// FIXME(nsm): Bug 1073231. This is incorrect, but the clone method isn't
// well defined yet.
nsRefPtr<Request> request = new Request(mOwner,
new InternalRequest(*mRequest));
return request.forget();
}
-namespace {
-nsresult
-DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded)
-{
- nsCOMPtr<nsIUnicodeDecoder> decoder =
- EncodingUtils::DecoderForEncoding("UTF-8");
- if (!decoder) {
- return NS_ERROR_FAILURE;
- }
-
- int32_t destBufferLen;
- nsresult rv =
- decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- char16_t* destBuffer = aDecoded.BeginWriting();
- int32_t srcLen = (int32_t) aBuffer.Length();
- int32_t outLen = destBufferLen;
- rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- MOZ_ASSERT(outLen <= destBufferLen);
- aDecoded.SetLength(outLen);
- return NS_OK;
-}
-}
-
-already_AddRefed<Promise>
-Request::ConsumeBody(ConsumeType aType, ErrorResult& aRv)
+Headers*
+Request::Headers_()
{
- nsRefPtr<Promise> promise = Promise::Create(mOwner, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- if (BodyUsed()) {
- aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
- return nullptr;
- }
-
- SetBodyUsed();
-
- // While the spec says to do this asynchronously, all the body constructors
- // right now only accept bodies whose streams are backed by an in-memory
- // buffer that can be read without blocking. So I think this is fine.
- nsCOMPtr<nsIInputStream> stream;
- mRequest->GetBody(getter_AddRefs(stream));
-
- if (!stream) {
- aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0,
- NS_ASSIGNMENT_COPY);
- if (aRv.Failed()) {
- return nullptr;
- }
- }
-
- AutoJSAPI api;
- api.Init(mOwner);
- JSContext* cx = api.cx();
-
- // We can make this assertion because for now we only support memory backed
- // structures for the body argument for a Request.
- MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
- nsCString buffer;
- uint64_t len;
- aRv = stream->Available(&len);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- aRv = NS_ReadInputStreamToString(stream, buffer, len);
- if (aRv.Failed()) {
- return nullptr;
+ if (!mHeaders) {
+ mHeaders = new Headers(mOwner, mRequest->Headers());
}
- buffer.SetLength(len);
-
- switch (aType) {
- case CONSUME_ARRAYBUFFER: {
- JS::Rooted<JSObject*> arrayBuffer(cx);
- arrayBuffer =
- ArrayBuffer::Create(cx, buffer.Length(),
- reinterpret_cast<const uint8_t*>(buffer.get()));
- JS::Rooted<JS::Value> val(cx);
- val.setObjectOrNull(arrayBuffer);
- promise->MaybeResolve(cx, val);
- return promise.forget();
- }
- case CONSUME_BLOB: {
- // XXXnsm it is actually possible to avoid these duplicate allocations
- // for the Blob case by having the Blob adopt the stream's memory
- // directly, but I've not added a special case for now.
- //
- // This is similar to nsContentUtils::CreateBlobBuffer, but also deals
- // with worker wrapping.
- uint32_t blobLen = buffer.Length();
- void* blobData = moz_malloc(blobLen);
- nsRefPtr<File> blob;
- if (blobData) {
- memcpy(blobData, buffer.BeginReading(), blobLen);
- blob = File::CreateMemoryFile(GetParentObject(), blobData, blobLen,
- NS_ConvertUTF8toUTF16(mMimeType));
- } else {
- aRv = NS_ERROR_OUT_OF_MEMORY;
- return nullptr;
- }
-
- promise->MaybeResolve(blob);
- return promise.forget();
- }
- case CONSUME_JSON: {
- nsString decoded;
- aRv = DecodeUTF8(buffer, decoded);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- JS::Rooted<JS::Value> json(cx);
- if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
- JS::Rooted<JS::Value> exn(cx);
- if (JS_GetPendingException(cx, &exn)) {
- JS_ClearPendingException(cx);
- promise->MaybeReject(cx, exn);
- }
- }
- promise->MaybeResolve(cx, json);
- return promise.forget();
- }
- case CONSUME_TEXT: {
- nsString decoded;
- aRv = DecodeUTF8(buffer, decoded);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- promise->MaybeResolve(decoded);
- return promise.forget();
- }
- }
-
- NS_NOTREACHED("Unexpected consume body type");
- // Silence warnings.
- return nullptr;
+ return mHeaders;
}
-already_AddRefed<Promise>
-Request::ArrayBuffer(ErrorResult& aRv)
-{
- return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
-}
-
-already_AddRefed<Promise>
-Request::Blob(ErrorResult& aRv)
-{
- return ConsumeBody(CONSUME_BLOB, aRv);
-}
-
-already_AddRefed<Promise>
-Request::Json(ErrorResult& aRv)
-{
- return ConsumeBody(CONSUME_JSON, aRv);
-}
-
-already_AddRefed<Promise>
-Request::Text(ErrorResult& aRv)
-{
- return ConsumeBody(CONSUME_TEXT, aRv);
-}
} // namespace dom
} // namespace mozilla
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -4,32 +4,35 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_Request_h
#define mozilla_dom_Request_h
#include "nsISupportsImpl.h"
#include "nsWrapperCache.h"
+#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/InternalRequest.h"
// Required here due to certain WebIDL enums/classes being declared in both
// files.
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/UnionTypes.h"
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class Headers;
+class InternalHeaders;
class Promise;
class Request MOZ_FINAL : public nsISupports
, public nsWrapperCache
+ , public FetchBody<Request>
{
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request)
public:
Request(nsIGlobalObject* aOwner, InternalRequest* aRequest);
JSObject*
@@ -69,73 +72,46 @@ public:
aReferrer.AsAString() = EmptyString();
return;
}
// FIXME(nsm): Spec doesn't say what to do if referrer is client.
aReferrer.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mReferrerURL);
}
- Headers* Headers_() const { return mRequest->Headers_(); }
+ InternalHeaders*
+ GetInternalHeaders() const
+ {
+ return mRequest->Headers();
+ }
+
+ Headers* Headers_();
+
+ void
+ GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
static already_AddRefed<Request>
Constructor(const GlobalObject& aGlobal, const RequestOrScalarValueString& aInput,
const RequestInit& aInit, ErrorResult& rv);
- nsISupports* GetParentObject() const
+ nsIGlobalObject* GetParentObject() const
{
return mOwner;
}
already_AddRefed<Request>
Clone() const;
- already_AddRefed<Promise>
- ArrayBuffer(ErrorResult& aRv);
-
- already_AddRefed<Promise>
- Blob(ErrorResult& aRv);
-
- already_AddRefed<Promise>
- Json(ErrorResult& aRv);
-
- already_AddRefed<Promise>
- Text(ErrorResult& aRv);
-
- bool
- BodyUsed() const
- {
- return mBodyUsed;
- }
-
already_AddRefed<InternalRequest>
GetInternalRequest();
private:
- enum ConsumeType
- {
- CONSUME_ARRAYBUFFER,
- CONSUME_BLOB,
- // FormData not supported right now,
- CONSUME_JSON,
- CONSUME_TEXT,
- };
-
~Request();
- already_AddRefed<Promise>
- ConsumeBody(ConsumeType aType, ErrorResult& aRv);
-
- void
- SetBodyUsed()
- {
- mBodyUsed = true;
- }
-
nsCOMPtr<nsIGlobalObject> mOwner;
nsRefPtr<InternalRequest> mRequest;
- bool mBodyUsed;
- nsCString mMimeType;
+ // Lazily created.
+ nsRefPtr<Headers> mHeaders;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Request_h
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -1,138 +1,163 @@
/* -*- 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 "Response.h"
-#include "nsDOMString.h"
+
+#include "nsISupportsImpl.h"
+#include "nsIURI.h"
#include "nsPIDOMWindow.h"
-#include "nsIURI.h"
-#include "nsISupportsImpl.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/Promise.h"
+#include "nsDOMString.h"
+
+#include "InternalResponse.h"
+
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
-Response::Response(nsISupports* aOwner)
- : mOwner(aOwner)
- , mHeaders(new Headers(aOwner))
+Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse)
+ : FetchBody<Response>()
+ , mOwner(aGlobal)
+ , mInternalResponse(aInternalResponse)
{
}
Response::~Response()
{
}
/* static */ already_AddRefed<Response>
Response::Error(const GlobalObject& aGlobal)
{
- ErrorResult result;
- ResponseInit init;
- init.mStatus = 0;
- Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams> body;
- nsRefPtr<Response> r = Response::Constructor(aGlobal, body, init, result);
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
+ nsRefPtr<Response> r = new Response(global, error);
return r.forget();
}
/* static */ already_AddRefed<Response>
Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
uint16_t aStatus)
{
ErrorResult result;
ResponseInit init;
- Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams> body;
+ Optional<ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams> body;
nsRefPtr<Response> r = Response::Constructor(aGlobal, body, init, result);
return r.forget();
}
/*static*/ already_AddRefed<Response>
-Response::Constructor(const GlobalObject& global,
- const Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams>& aBody,
- const ResponseInit& aInit, ErrorResult& rv)
-{
- nsRefPtr<Response> response = new Response(global.GetAsSupports());
- return response.forget();
-}
-
-already_AddRefed<Response>
-Response::Clone()
-{
- nsRefPtr<Response> response = new Response(mOwner);
- return response.forget();
-}
-
-already_AddRefed<Promise>
-Response::ArrayBuffer(ErrorResult& aRv)
+Response::Constructor(const GlobalObject& aGlobal,
+ const Optional<ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams>& aBody,
+ const ResponseInit& aInit, ErrorResult& aRv)
{
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
- MOZ_ASSERT(global);
- nsRefPtr<Promise> promise = Promise::Create(global, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
-
- promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
- return promise.forget();
-}
-
-already_AddRefed<Promise>
-Response::Blob(ErrorResult& aRv)
-{
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
- MOZ_ASSERT(global);
- nsRefPtr<Promise> promise = Promise::Create(global, aRv);
- if (aRv.Failed()) {
+ if (aInit.mStatus < 200 || aInit.mStatus > 599) {
+ aRv.Throw(NS_ERROR_RANGE_ERR);
return nullptr;
}
- promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
- return promise.forget();
-}
-
-already_AddRefed<Promise>
-Response::Json(ErrorResult& aRv)
-{
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
- MOZ_ASSERT(global);
- nsRefPtr<Promise> promise = Promise::Create(global, aRv);
- if (aRv.Failed()) {
- return nullptr;
+ nsCString statusText;
+ if (aInit.mStatusText.WasPassed()) {
+ statusText = aInit.mStatusText.Value();
+ nsACString::const_iterator start, end;
+ statusText.BeginReading(start);
+ statusText.EndReading(end);
+ if (FindCharInReadable('\r', start, end)) {
+ aRv.ThrowTypeError(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR);
+ return nullptr;
+ }
+ // Reset iterator since FindCharInReadable advances it.
+ statusText.BeginReading(start);
+ if (FindCharInReadable('\n', start, end)) {
+ aRv.ThrowTypeError(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR);
+ return nullptr;
+ }
+ } else {
+ // Since we don't support default values for ByteString.
+ statusText = NS_LITERAL_CSTRING("OK");
}
- promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
- return promise.forget();
+ nsRefPtr<InternalResponse> internalResponse =
+ new InternalResponse(aInit.mStatus, statusText);
+
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ nsRefPtr<Response> r = new Response(global, internalResponse);
+
+ if (aInit.mHeaders.WasPassed()) {
+ internalResponse->Headers()->Clear();
+
+ // Instead of using Fill, create an object to allow the constructor to
+ // unwrap the HeadersInit.
+ nsRefPtr<Headers> headers =
+ Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ internalResponse->Headers()->Fill(*headers->GetInternalHeaders(), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+
+ if (aBody.WasPassed()) {
+ nsCOMPtr<nsIInputStream> bodyStream;
+ nsCString contentType;
+ aRv = ExtractByteStreamFromBody(aBody.Value(), getter_AddRefs(bodyStream), contentType);
+ internalResponse->SetBody(bodyStream);
+
+ if (!contentType.IsVoid() &&
+ !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
+ internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, aRv);
+ }
+
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ }
+
+ r->SetMimeType(aRv);
+ return r.forget();
}
-already_AddRefed<Promise>
-Response::Text(ErrorResult& aRv)
+// FIXME(nsm): Bug 1073231: This is currently unspecced!
+already_AddRefed<Response>
+Response::Clone()
{
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
- MOZ_ASSERT(global);
- nsRefPtr<Promise> promise = Promise::Create(global, aRv);
- if (aRv.Failed()) {
- return nullptr;
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mOwner);
+ nsRefPtr<Response> response = new Response(global, mInternalResponse);
+ return response.forget();
+}
+
+void
+Response::SetBody(nsIInputStream* aBody)
+{
+ // FIXME(nsm): Do we flip bodyUsed here?
+ mInternalResponse->SetBody(aBody);
+}
+
+Headers*
+Response::Headers_()
+{
+ if (!mHeaders) {
+ mHeaders = new Headers(mOwner, mInternalResponse->Headers());
}
- promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
- return promise.forget();
-}
-
-bool
-Response::BodyUsed()
-{
- return false;
+ return mHeaders;
}
} // namespace dom
} // namespace mozilla
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -4,105 +4,110 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_Response_h
#define mozilla_dom_Response_h
#include "nsWrapperCache.h"
#include "nsISupportsImpl.h"
+#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/UnionTypes.h"
+#include "InternalResponse.h"
+
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class Headers;
+class InternalHeaders;
class Promise;
class Response MOZ_FINAL : public nsISupports
, public nsWrapperCache
+ , public FetchBody<Response>
{
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Response)
public:
- explicit Response(nsISupports* aOwner);
+ Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse);
+
+ Response(const Response& aOther) MOZ_DELETE;
JSObject*
WrapObject(JSContext* aCx)
{
return ResponseBinding::Wrap(aCx, this);
}
ResponseType
Type() const
{
- return ResponseType::Error;
+ return mInternalResponse->Type();
}
void
GetUrl(DOMString& aUrl) const
{
- aUrl.AsAString() = EmptyString();
+ aUrl.AsAString() = NS_ConvertUTF8toUTF16(mInternalResponse->GetUrl());
}
uint16_t
Status() const
{
- return 400;
+ return mInternalResponse->GetStatus();
}
void
GetStatusText(nsCString& aStatusText) const
{
- aStatusText = EmptyCString();
+ aStatusText = mInternalResponse->GetStatusText();
}
- Headers*
- Headers_() const { return mHeaders; }
+ InternalHeaders*
+ GetInternalHeaders() const
+ {
+ return mInternalResponse->Headers();
+ }
+
+ Headers* Headers_();
+
+ void
+ GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
static already_AddRefed<Response>
Error(const GlobalObject& aGlobal);
static already_AddRefed<Response>
Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus);
static already_AddRefed<Response>
Constructor(const GlobalObject& aGlobal,
- const Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams>& aBody,
+ const Optional<ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams>& aBody,
const ResponseInit& aInit, ErrorResult& rv);
- nsISupports* GetParentObject() const
+ nsIGlobalObject* GetParentObject() const
{
return mOwner;
}
already_AddRefed<Response>
Clone();
- already_AddRefed<Promise>
- ArrayBuffer(ErrorResult& aRv);
-
- already_AddRefed<Promise>
- Blob(ErrorResult& aRv);
-
- already_AddRefed<Promise>
- Json(ErrorResult& aRv);
-
- already_AddRefed<Promise>
- Text(ErrorResult& aRv);
-
- bool
- BodyUsed();
+ void
+ SetBody(nsIInputStream* aBody);
private:
~Response();
- nsCOMPtr<nsISupports> mOwner;
+ nsCOMPtr<nsIGlobalObject> mOwner;
+ nsRefPtr<InternalResponse> mInternalResponse;
+ // Lazily created
nsRefPtr<Headers> mHeaders;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Response_h
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -2,25 +2,29 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
'Fetch.h',
'Headers.h',
+ 'InternalHeaders.h',
'InternalRequest.h',
+ 'InternalResponse.h',
'Request.h',
'Response.h',
]
UNIFIED_SOURCES += [
'Fetch.cpp',
'Headers.cpp',
+ 'InternalHeaders.cpp',
'InternalRequest.cpp',
+ 'InternalResponse.cpp',
'Request.cpp',
'Response.cpp',
]
LOCAL_INCLUDES += [
'../workers',
]
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -19,16 +19,17 @@
#include "nsServiceManagerUtils.h"
#include "nsStructuredCloneContainer.h"
#include "nsToolkitCompsCID.h"
#include "nsGlobalWindow.h"
#include "nsDOMJSUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/Event.h"
#include "mozilla/Services.h"
#include "nsContentPermissionHelper.h"
#ifdef MOZ_B2G
#include "nsIDOMDesktopNotification.h"
#endif
namespace mozilla {
namespace dom {
@@ -355,29 +356,41 @@ NotificationTask::Run()
NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
NS_IMETHODIMP
NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
nsCOMPtr<nsPIDOMWindow> window = mNotification->GetOwner();
- if (!window) {
+ if (!window || !window->IsCurrentInnerWindow()) {
// Window has been closed, this observer is not valid anymore
return NS_ERROR_FAILURE;
}
if (!strcmp("alertclickcallback", aTopic)) {
- nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
- if (doc) {
- nsContentUtils::DispatchChromeEvent(doc, window,
- NS_LITERAL_STRING("DOMWebNotificationClicked"),
- true, true);
+
+ nsCOMPtr<nsIDOMEvent> event;
+ NS_NewDOMEvent(getter_AddRefs(event), mNotification, nullptr, nullptr);
+ nsresult rv = event->InitEvent(NS_LITERAL_STRING("click"), false, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ event->SetTrusted(true);
+ WantsPopupControlCheck popupControlCheck(event);
+ bool doDefaultAction = true;
+ mNotification->DispatchEvent(event, &doDefaultAction);
+ if (doDefaultAction) {
+ nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
+ if (doc) {
+ // Browser UI may use DOMWebNotificationClicked to focus the tab
+ // from which the event was dispatched.
+ nsContentUtils::DispatchChromeEvent(doc, window->GetOuterWindow(),
+ NS_LITERAL_STRING("DOMWebNotificationClicked"),
+ true, true);
+ }
}
- mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click"));
} else if (!strcmp("alertfinished", aTopic)) {
nsCOMPtr<nsINotificationStorage> notificationStorage =
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
if (notificationStorage && mNotification->IsStored()) {
nsString origin;
nsresult rv = Notification::GetOrigin(mNotification->GetOwner(), origin);
if (NS_SUCCEEDED(rv)) {
nsString id;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2852,17 +2852,16 @@ nsresult nsPluginHost::NewPluginURLStrea
// form |nsDocShell::OnLinkClickSync| bug 166613
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannelInternal(getter_AddRefs(channel),
url,
doc,
principal,
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
- nullptr, // aChannelPolicy
nullptr, // aLoadGroup
listenerPeer);
if (NS_FAILED(rv))
return rv;
if (doc) {
// And if it's a script allow it to execute against the
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -657,17 +657,16 @@ nsPluginStreamListenerPeer::RequestRead(
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannelInternal(getter_AddRefs(channel),
mURL,
doc,
principal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
loadGroup,
callbacks);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (!httpChannel)
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -830,16 +830,21 @@ AudioManager::SetStreamVolumeIndex(int32
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_EARPIECE);
+ status += AudioSystem::setStreamVolumeIndex(
+ static_cast<audio_stream_type_t>(aStream),
+ aIndex,
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+
return status ? NS_ERROR_FAILURE : NS_OK;
#endif
}
nsresult
AudioManager::GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex) {
if (!aIndex) {
return NS_ERROR_INVALID_ARG;
--- a/dom/webidl/Fetch.webidl
+++ b/dom/webidl/Fetch.webidl
@@ -3,20 +3,19 @@
* 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/.
*
* The origin of this IDL file is
* http://fetch.spec.whatwg.org/
*/
typedef object JSON;
-// FIXME(nsm): Bug 1071290: Blobs can't be passed as unions in workers.
// FIXME(nsm): Bug 739173: FormData is not available in workers.
// typedef (ArrayBuffer or ArrayBufferView or Blob or FormData or ScalarValueString or URLSearchParams) BodyInit;
-typedef (ArrayBuffer or ArrayBufferView or ScalarValueString or URLSearchParams) BodyInit;
+typedef (ArrayBuffer or ArrayBufferView or Blob or ScalarValueString or URLSearchParams) BodyInit;
[NoInterfaceObject, Exposed=(Window,Worker)]
interface Body {
readonly attribute boolean bodyUsed;
[Throws]
Promise<ArrayBuffer> arrayBuffer();
[Throws]
Promise<Blob> blob();
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -82,16 +82,19 @@ dictionary AesDerivedKeyParams : Algorit
dictionary HmacDerivedKeyParams : HmacImportParams {
[EnforceRange] unsigned long length;
};
dictionary EcdhKeyDeriveParams : Algorithm {
required CryptoKey public;
};
+dictionary EcdsaParams : Algorithm {
+ required AlgorithmIdentifier hash;
+};
/***** JWK *****/
dictionary RsaOtherPrimesInfo {
// The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms
required DOMString r;
required DOMString d;
required DOMString t;
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1,28 +1,26 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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 "ScriptLoader.h"
#include "nsIChannel.h"
-#include "nsIChannelPolicy.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIHttpChannel.h"
#include "nsIIOService.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptSecurityManager.h"
#include "nsIStreamLoader.h"
#include "nsIURI.h"
#include "jsapi.h"
-#include "nsChannelPolicy.h"
#include "nsError.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
#include "nsDocShellCID.h"
#include "nsISupportsPrimitives.h"
#include "nsNetUtil.h"
#include "nsScriptLoader.h"
#include "nsString.h"
@@ -99,61 +97,42 @@ ChannelFromScriptURL(nsIPrincipal* princ
rv = principal->CheckMayLoad(uri, false, true);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
else {
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
- // Get Content Security Policy from parent document to pass into channel.
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = principal->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- if (csp) {
- channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = channelPolicy->SetContentSecurityPolicy(csp);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
nsCOMPtr<nsIChannel> channel;
// If we have the document, use it
if (parentDoc) {
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
parentDoc,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT,
- channelPolicy,
loadGroup,
nullptr, // aCallbacks
flags,
ios);
} else {
// we should use 'principal' here; needs to be fixed before
// we move security checks to AsyncOpen. We use nullPrincipal
// for now, because the loadGroup is null and hence causes
// GetChannelUriPrincipal to return the wrong principal.
nsCOMPtr<nsIPrincipal> nullPrincipal =
do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
nullPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT,
- channelPolicy,
loadGroup,
nullptr, // aCallbacks
flags,
ios);
}
NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/workers/test/fetch/mochitest.ini
+++ b/dom/workers/test/fetch/mochitest.ini
@@ -1,7 +1,9 @@
[DEFAULT]
support-files =
worker_interfaces.js
worker_test_request.js
+ worker_test_response.js
[test_interfaces.html]
[test_request.html]
+[test_response.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fetch/test_response.html
@@ -0,0 +1,48 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1039846 - Test Response object in worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ var worker = new Worker("worker_test_response.js");
+ worker.onmessage = function(event) {
+
+ if (event.data.type == 'finish') {
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ }
+ }
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage(true);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.fetch.enabled", true]
+ ]}, function() {
+ runTest();
+ });
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/workers/test/fetch/worker_test_request.js
+++ b/dom/workers/test/fetch/worker_test_request.js
@@ -126,17 +126,16 @@ function testBodyUsed() {
return req.blob().then((v) => {
ok(false, "Attempting to read body again should fail.");
}, (e) => {
ok(true, "Attempting to read body again should fail.");
})
});
}
-// FIXME(nsm): Bug 1071290: We can't use Blobs as the body yet.
function testBodyCreation() {
var text = "κόσμε";
var req1 = new Request("", { body: text });
var p1 = req1.text().then(function(v) {
ok(typeof v === "string", "Should resolve to string");
is(text, v, "Extracted string should match");
});
@@ -145,29 +144,34 @@ function testBodyCreation() {
is("Hello", v, "Extracted string should match");
});
var req2b = new Request("", { body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
var p2b = req2b.text().then(function(v) {
is("Hello", v, "Extracted string should match");
});
+ var reqblob = new Request("", { body: new Blob([text]) });
+ var pblob = reqblob.text().then(function(v) {
+ is(v, text, "Extracted string should match");
+ });
+
var params = new URLSearchParams();
params.append("item", "Geckos");
params.append("feature", "stickyfeet");
params.append("quantity", "700");
var req3 = new Request("", { body: params });
var p3 = req3.text().then(function(v) {
var extracted = new URLSearchParams(v);
is(extracted.get("item"), "Geckos", "Param should match");
is(extracted.get("feature"), "stickyfeet", "Param should match");
is(extracted.get("quantity"), "700", "Param should match");
});
- return Promise.all([p1, p2, p2b, p3]);
+ return Promise.all([p1, p2, p2b, pblob, p3]);
}
function testBodyExtraction() {
var text = "κόσμε";
var newReq = function() { return new Request("", { body: text }); }
return newReq().text().then(function(v) {
ok(typeof v === "string", "Should resolve to string");
is(text, v, "Extracted string should match");
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fetch/worker_test_response.js
@@ -0,0 +1,128 @@
+function ok(a, msg) {
+ dump("OK: " + !!a + " => " + a + " " + msg + "\n");
+ postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+ dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
+ postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+}
+
+function testDefaultCtor() {
+ var res = new Response();
+ is(res.type, "default", "Default Response type is default");
+ ok(res.headers instanceof Headers, "Response should have non-null Headers object");
+ is(res.url, "", "URL should be empty string");
+ is(res.status, 200, "Default status is 200");
+ is(res.statusText, "OK", "Default statusText is OK");
+}
+
+function testClone() {
+ var res = (new Response("This is a body", {
+ status: 404,
+ statusText: "Not Found",
+ headers: { "Content-Length": 5 },
+ })).clone();
+ is(res.status, 404, "Response status is 404");
+ is(res.statusText, "Not Found", "Response statusText is POST");
+ ok(res.headers instanceof Headers, "Response should have non-null Headers object");
+ is(res.headers.get('content-length'), "5", "Response content-length should be 5.");
+}
+
+function testBodyUsed() {
+ var res = new Response("Sample body");
+ ok(!res.bodyUsed, "bodyUsed is initially false.");
+ return res.text().then((v) => {
+ is(v, "Sample body", "Body should match");
+ ok(res.bodyUsed, "After reading body, bodyUsed should be true.");
+ }).then(() => {
+ return res.blob().then((v) => {
+ ok(false, "Attempting to read body again should fail.");
+ }, (e) => {
+ ok(true, "Attempting to read body again should fail.");
+ })
+ });
+}
+
+function testBodyCreation() {
+ var text = "κόσμε";
+ var res1 = new Response(text);
+ var p1 = res1.text().then(function(v) {
+ ok(typeof v === "string", "Should resolve to string");
+ is(text, v, "Extracted string should match");
+ });
+
+ var res2 = new Response(new Uint8Array([72, 101, 108, 108, 111]));
+ var p2 = res2.text().then(function(v) {
+ is("Hello", v, "Extracted string should match");
+ });
+
+ var res2b = new Response((new Uint8Array([72, 101, 108, 108, 111])).buffer);
+ var p2b = res2b.text().then(function(v) {
+ is("Hello", v, "Extracted string should match");
+ });
+
+ var resblob = new Response(new Blob([text]));
+ var pblob = resblob.text().then(function(v) {
+ is(v, text, "Extracted string should match");
+ });
+
+ var params = new URLSearchParams();
+ params.append("item", "Geckos");
+ params.append("feature", "stickyfeet");
+ params.append("quantity", "700");
+ var res3 = new Response(params);
+ var p3 = res3.text().then(function(v) {
+ var extracted = new URLSearchParams(v);
+ is(extracted.get("item"), "Geckos", "Param should match");
+ is(extracted.get("feature"), "stickyfeet", "Param should match");
+ is(extracted.get("quantity"), "700", "Param should match");
+ });
+
+ return Promise.all([p1, p2, p2b, pblob, p3]);
+}
+
+function testBodyExtraction() {
+ var text = "κόσμε";
+ var newRes = function() { return new Response(text); }
+ return newRes().text().then(function(v) {
+ ok(typeof v === "string", "Should resolve to string");
+ is(text, v, "Extracted string should match");
+ }).then(function() {
+ return newRes().blob().then(function(v) {
+ ok(v instanceof Blob, "Should resolve to Blob");
+ var fs = new FileReaderSync();
+ is(fs.readAsText(v), text, "Decoded Blob should match original");
+ });
+ }).then(function() {
+ return newRes().json().then(function(v) {
+ ok(false, "Invalid json should reject");
+ }, function(e) {
+ ok(true, "Invalid json should reject");
+ })
+ }).then(function() {
+ return newRes().arrayBuffer().then(function(v) {
+ ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
+ var dec = new TextDecoder();
+ is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original");
+ });
+ })
+}
+
+onmessage = function() {
+ var done = function() { postMessage({ type: 'finish' }) }
+
+ testDefaultCtor();
+ testClone();
+
+ Promise.resolve()
+ .then(testBodyCreation)
+ .then(testBodyUsed)
+ .then(testBodyExtraction)
+ // Put more promise based tests here.
+ .then(done)
+ .catch(function(e) {
+ ok(false, "Some Response tests failed " + e);
+ done();
+ })
+}
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -1071,17 +1071,16 @@ nsXBLService::FetchBindingDocument(nsICo
// Note that we are calling NS_NewChannelInternal here with both a node and a principal.
// This is because the principal and node could be different.
rv = NS_NewChannelInternal(getter_AddRefs(channel),
aDocumentURI,
aBoundDocument,
requestingPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
loadGroup);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::GetSameOriginChecker();
NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
channel->SetNotificationCallbacks(sameOriginChecker);
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -444,17 +444,16 @@ XMLDocument::Load(const nsAString& aUrl,
// nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,
// which in turn keeps STOP button from becoming active
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
callingDoc ? callingDoc.get() :
static_cast<nsIDocument*>(this),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_XMLHTTPREQUEST,
- nullptr, // aChannelPolicy
loadGroup,
req,
nsIRequest::LOAD_BACKGROUND);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
--- a/dom/xslt/base/txURIUtils.cpp
+++ b/dom/xslt/base/txURIUtils.cpp
@@ -59,17 +59,16 @@ URIUtils::ResetWithSource(nsIDocument *a
nsCOMPtr<nsIChannel> channel = sourceDoc->GetChannel();
if (!channel) {
// Need to synthesize one
nsresult rv = NS_NewChannel(getter_AddRefs(channel),
sourceDoc->GetDocumentURI(),
sourceDoc,
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
loadGroup);
if (NS_FAILED(rv)) {
return;
}
}
aNewDoc->Reset(channel, loadGroup);
aNewDoc->SetPrincipal(sourcePrincipal);
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -460,17 +460,16 @@ txCompileObserver::startLoad(nsIURI* aUr
}
nsCOMPtr<nsIChannel> channel;
nsresult rv = NS_NewChannel(getter_AddRefs(channel),
aUri,
aReferrerPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_STYLESHEET,
- nullptr,
loadGroup);
NS_ENSURE_SUCCESS(rv, rv);
channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (httpChannel) {
--- a/embedding/browser/nsContextMenuInfo.cpp
+++ b/embedding/browser/nsContextMenuInfo.cpp
@@ -18,17 +18,16 @@
#include "nsIDOMWindow.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIDOMCSSValue.h"
#include "nsIDOMCSSPrimitiveValue.h"
#include "nsNetUtil.h"
#include "nsUnicharUtils.h"
#include "nsIDocument.h"
#include "nsIPrincipal.h"
-#include "nsIChannelPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIContentPolicy.h"
#include "nsAutoPtr.h"
#include "imgRequestProxy.h"
//*****************************************************************************
// class nsContextMenuInfo
@@ -263,32 +262,19 @@ nsContextMenuInfo::GetBackgroundImageReq
nsCOMPtr<nsIDOMWindow> window;
document->GetDefaultView(getter_AddRefs(window));
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue;
nsAutoString bgStringValue;
- // get Content Security Policy to pass to LoadImage
nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
- nsCOMPtr<nsIPrincipal> principal;
- nsCOMPtr<nsIChannelPolicy> channelPolicy;
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- if (doc) {
- principal = doc->NodePrincipal();
- nsresult rv = principal->GetCsp(getter_AddRefs(csp));
- NS_ENSURE_SUCCESS(rv, rv);
- if (csp) {
- channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
- channelPolicy->SetContentSecurityPolicy(csp);
- channelPolicy->SetLoadType(nsIContentPolicy::TYPE_IMAGE);
- }
- }
-
+ nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
+
while (true) {
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(domNode));
// bail for the parent node of the root element or null argument
if (!domElement)
break;
nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle;
window->GetComputedStyle(domElement, EmptyString(),
@@ -305,17 +291,17 @@ nsContextMenuInfo::GetBackgroundImageReq
NS_NewURI(getter_AddRefs(bgUri), bgStringValue);
NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE);
nsRefPtr<imgLoader> il = imgLoader::GetInstance();
NS_ENSURE_TRUE(il, NS_ERROR_FAILURE);
return il->LoadImage(bgUri, nullptr, nullptr, principal, nullptr,
nullptr, nullptr, nsIRequest::LOAD_NORMAL,
- nullptr, channelPolicy, EmptyString(), aRequest);
+ nullptr, EmptyString(), aRequest);
}
}
// bail if we encounter non-transparent background-color
computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-color"),
getter_AddRefs(cssValue));
primitiveValue = do_QueryInterface(cssValue);
if (primitiveValue) {
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1200,17 +1200,16 @@ nsresult nsWebBrowserPersist::SaveURIInt
// Open a channel to the URI
nsCOMPtr<nsIChannel> inputChannel;
rv = NS_NewChannel(getter_AddRefs(inputChannel),
aURI,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
nullptr, // aLoadGroup
static_cast<nsIInterfaceRequestor*>(this),
loadFlags);
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
if (pbChannel)
{
pbChannel->SetPrivate(aIsPrivate);
--- a/extensions/pref/autoconfig/src/nsAutoConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -276,17 +276,16 @@ nsresult nsAutoConfig::downloadAutoConfi
PR_LOG(MCD, PR_LOG_DEBUG, ("running MCD url %s\n", mConfigURL.get()));
// open a channel for the url
rv = NS_NewChannel(getter_AddRefs(channel),
url,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
- nullptr, // aChannelPolicy
nullptr, // loadGroup
nullptr, // aCallbacks
nsIRequest::INHIBIT_PERSISTENT_CACHING |
nsIRequest::LOAD_BYPASS_CACHE);
if (NS_FAILED(rv))
return rv;
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1270,17 +1270,24 @@ DestroyPixmap(void *data)
delete static_cast<DestroyPixmapClosure*>(data);
}
#endif
TemporaryRef<SourceSurface>
DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
{
#ifdef CAIRO_HAS_XLIB_SURFACE
- if (cairo_surface_get_type(mSurface) != CAIRO_SURFACE_TYPE_XLIB) {
+ cairo_surface_type_t ctype = cairo_surface_get_type(mSurface);
+ if (aSurface->GetType() == SurfaceType::CAIRO &&
+ cairo_surface_get_type(
+ static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface()) == ctype) {
+ return aSurface;
+ }
+
+ if (ctype != CAIRO_SURFACE_TYPE_XLIB) {
return aSurface;
}
IntSize size = aSurface->GetSize();
if (!size.width || !size.height) {
return aSurface;
}
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -553,17 +553,17 @@ public:
* Matrix4x4::Translation(x, y) * this
*
* (Note that in performance critical code multiplying by the result of a
* Translation()/Scaling() call is not recommended since that results in a
* full matrix multiply involving 64 floating-point multiplications. Calling
* this method would be preferred since it only involves 12 floating-point
* multiplications.)
*/
- Matrix4x4 &Translate(Float aX, Float aY, Float aZ)
+ Matrix4x4 &PreTranslate(Float aX, Float aY, Float aZ)
{
_41 += aX * _11 + aY * _21 + aZ * _31;
_42 += aX * _12 + aY * _22 + aZ * _32;
_43 += aX * _13 + aY * _23 + aZ * _33;
_44 += aX * _14 + aY * _24 + aZ * _34;
return *this;
}
@@ -604,17 +604,17 @@ public:
0.0f, aScaleY, 0.0f, 0.0f,
0.0f, 0.0f, aScaleZ, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
/**
* Similar to PreTranslate, but applies a scale instead of a translation.
*/
- Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
+ Matrix4x4 &PreScale(Float aX, Float aY, Float aZ)
{
_11 *= aX;
_12 *= aX;
_13 *= aX;
_21 *= aY;
_22 *= aY;
_23 *= aY;
_31 *= aZ;
@@ -658,17 +658,17 @@ public:
void SkewYZ(Float aSkew)
{
(*this)[2] += (*this)[1] * aSkew;
}
Matrix4x4 &ChangeBasis(Float aX, Float aY, Float aZ)
{
// Translate to the origin before applying this matrix
- Translate(-aX, -aY, -aZ);
+ PreTranslate(-aX, -aY, -aZ);
// Translate back into position after applying this matrix
PostTranslate(aX, aY, aZ);
return *this;
}
bool operator==(const Matrix4x4& o) const
@@ -772,52 +772,16 @@ public:
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
(*this)[i][j] /= (*this)[3][3];
}
}
}
- void ScalePost(Float aX, Float aY, Float aZ)
- {
- _11 *= aX;
- _21 *= aX;
- _31 *= aX;
- _41 *= aX;
-
- _12 *= aY;
- _22 *= aY;
- _32 *= aY;
- _42 *= aY;
-
- _13 *= aZ;
- _23 *= aZ;
- _33 *= aZ;
- _43 *= aZ;
- }
-
- void TranslatePost(Float aX, Float aY, Float aZ)
- {
- _11 += _14 * aX;
- _21 += _24 * aX;
- _31 += _34 * aX;
- _41 += _44 * aX;
-
- _12 += _14 * aY;
- _22 += _24 * aY;
- _32 += _34 * aY;
- _42 += _44 * aY;
-
- _13 += _14 * aZ;
- _23 += _24 * aZ;
- _33 += _34 * aZ;
- _43 += _44 * aZ;
- }
-
bool FuzzyEqual(const Matrix4x4& o) const
{
return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) &&
gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) &&
gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) &&
gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) &&
gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) &&
gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) &&
--- a/gfx/2d/PathHelpers.cpp
+++ b/gfx/2d/PathHelpers.cpp
@@ -157,11 +157,70 @@ AppendEllipseToPath(PathBuilder* aPathBu
{
Size halfDim = aDimensions / 2.0;
Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
Size radii[] = { halfDim, halfDim, halfDim, halfDim };
AppendRoundedRectToPath(aPathBuilder, rect, radii);
}
+bool
+SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+ const DrawTarget& aDrawTarget)
+{
+ Matrix mat = aDrawTarget.GetTransform();
+ if (mat.HasNonTranslation()) {
+ return false;
+ }
+ if (aP1.x != aP2.x && aP1.y != aP2.y) {
+ return false; // not a horizontal or vertical line
+ }
+ Point p1 = aP1 + mat.GetTranslation(); // into device space
+ Point p2 = aP2 + mat.GetTranslation();
+ p1.Round();
+ p2.Round();
+ p1 -= mat.GetTranslation(); // back into user space
+ p2 -= mat.GetTranslation();
+ if (aP1.x == aP2.x) {
+ // snap vertical line, adding 0.5 to align it to be mid-pixel:
+ aP1 = p1 + Point(0.5, 0);
+ aP2 = p2 + Point(0.5, 0);
+ } else {
+ // snap horizontal line, adding 0.5 to align it to be mid-pixel:
+ aP1 = p1 + Point(0, 0.5);
+ aP2 = p2 + Point(0, 0.5);
+ }
+ return true;
+}
+
+void
+StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (aRect.IsEmpty()) {
+ return;
+ }
+
+ Point p1 = aRect.TopLeft();
+ Point p2 = aRect.BottomLeft();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.BottomLeft();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopLeft();
+ p2 = aRect.TopRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+ p1 = aRect.TopRight();
+ p2 = aRect.BottomRight();
+ SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+ aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+}
+
} // namespace gfx
} // namespace mozilla
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -151,16 +151,41 @@ GFX2D_API void AppendRoundedRectToPath(P
*
* The ellipse extends aDimensions.width / 2.0 in the horizontal direction
* from aCenter, and aDimensions.height / 2.0 in the vertical direction.
*/
GFX2D_API void AppendEllipseToPath(PathBuilder* aPathBuilder,
const Point& aCenter,
const Size& aDimensions);
+/**
+ * If aDrawTarget's transform only contains a translation, and if this line is
+ * a horizontal or vertical line, this function will snap the line's vertices
+ * to align with the device pixel grid so that stroking the line with a one
+ * pixel wide stroke will result in a crisp line that is not antialiased over
+ * two pixels across its width.
+ *
+ * @return Returns true if this function snaps aRect's vertices, else returns
+ * false.
+ */
+GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+ const DrawTarget& aDrawTarget);
+
+/**
+ * This function paints each edge of aRect separately, snapping the edges using
+ * SnapLineToDevicePixelsForStroking. Stroking the edges as separate paths
+ * helps ensure not only that the stroke spans a single row of device pixels if
+ * possible, but also that the ends of stroke dashes start and end on device
+ * pixels too.
+ */
+GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
+ DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions);
+
static inline bool
UserToDevicePixelSnapped(Rect& aRect, const Matrix& aTransform)
{
Point p1 = aTransform * aRect.TopLeft();
Point p2 = aTransform * aRect.TopRight();
Point p3 = aTransform * aRect.BottomRight();
// Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -33,18 +33,18 @@ void ImageLayer::ComputeEffectiveTransfo
// Snap image edges to pixel boundaries
gfxRect sourceRect(0, 0, 0, 0);
if (mContainer) {
sourceRect.SizeTo(gfx::ThebesIntSize(mContainer->GetCurrentSize()));
if (mScaleMode != ScaleMode::SCALE_NONE &&
sourceRect.width != 0.0 && sourceRect.height != 0.0) {
NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
"No other scalemodes than stretch and none supported yet.");
- local.Scale(mScaleToSize.width / sourceRect.width,
- mScaleToSize.height / sourceRect.height, 1.0);
+ local.PreScale(mScaleToSize.width / sourceRect.width,
+ mScaleToSize.height / sourceRect.height, 1.0);
}
}
// Snap our local transform first, and snap the inherited transform as well.
// This makes our snapping equivalent to what would happen if our content
// was drawn into a PaintedLayer (gfxContext would snap using the local
// transform, then we'd snap again when compositing the PaintedLayer).
mEffectiveTransform =
SnapTransform(local, sourceRect, nullptr) *
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -462,13 +462,13 @@ LayerPropertiesBase::ComputeDifferences(
}
return invalid;
}
}
void
LayerPropertiesBase::MoveBy(const nsIntPoint& aOffset)
{
- mTransform.TranslatePost(aOffset.x, aOffset.y, 0);
+ mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
}
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -712,33 +712,33 @@ Layer::IsScrollInfoLayer() const
}
const Matrix4x4
Layer::GetTransform() const
{
Matrix4x4 transform = mTransform;
transform.PostScale(mPostXScale, mPostYScale, 1.0f);
if (const ContainerLayer* c = AsContainerLayer()) {
- transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
}
return transform;
}
const Matrix4x4
Layer::GetLocalTransform()
{
Matrix4x4 transform;
if (LayerComposite* shadow = AsLayerComposite())
transform = shadow->GetShadowTransform();
else
transform = mTransform;
transform.PostScale(mPostXScale, mPostYScale, 1.0f);
if (ContainerLayer* c = AsContainerLayer()) {
- transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
}
return transform;
}
void
Layer::ApplyPendingUpdatesForThisTransaction()
{
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -169,19 +169,19 @@ TranslateShadowLayer2D(Layer* aLayer,
layerTransform._31 += aTranslation.x;
layerTransform._32 += aTranslation.y;
// The transform already takes the resolution scale into account. Since we
// will apply the resolution scale again when computing the effective
// transform, we must apply the inverse resolution scale here.
Matrix4x4 layerTransform3D = Matrix4x4::From2D(layerTransform);
if (ContainerLayer* c = aLayer->AsContainerLayer()) {
- layerTransform3D.Scale(1.0f/c->GetPreXScale(),
- 1.0f/c->GetPreYScale(),
- 1);
+ layerTransform3D.PreScale(1.0f/c->GetPreXScale(),
+ 1.0f/c->GetPreYScale(),
+ 1);
}
layerTransform3D.PostScale(1.0f/aLayer->GetPostXScale(),
1.0f/aLayer->GetPostYScale(),
1);
LayerComposite* layerComposite = aLayer->AsLayerComposite();
layerComposite->SetShadowTransform(layerTransform3D);
layerComposite->SetShadowTransformSetByAnimation(false);
@@ -615,36 +615,36 @@ AsyncCompositionManager::ApplyAsyncConte
if (hasAsyncTransform) {
Matrix4x4 transform = AdjustAndCombineWithCSSTransform(combinedAsyncTransform, aLayer);
// GetTransform already takes the pre- and post-scale into account. Since we
// will apply the pre- and post-scale again when computing the effective
// transform, we must apply the inverses here.
if (ContainerLayer* container = aLayer->AsContainerLayer()) {
- transform.Scale(1.0f/container->GetPreXScale(),
- 1.0f/container->GetPreYScale(),
- 1);
+ transform.PreScale(1.0f/container->GetPreXScale(),
+ 1.0f/container->GetPreYScale(),
+ 1);
}
transform.PostScale(1.0f/aLayer->GetPostXScale(),
1.0f/aLayer->GetPostYScale(),
1);
layerComposite->SetShadowTransform(transform);
NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(),
"overwriting animated transform!");
const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
MOZ_ASSERT(bottom.IsScrollable()); // must be true because hasAsyncTransform is true
// Apply resolution scaling to the old transform - the layer tree as it is
// doesn't have the necessary transform to display correctly. We use the
// bottom-most scrollable metrics because that should have the most accurate
// cumulative resolution for aLayer.
LayoutDeviceToLayerScale resolution = bottom.mCumulativeResolution;
- oldTransform.Scale(resolution.scale, resolution.scale, 1);
+ oldTransform.PreScale(resolution.scale, resolution.scale, 1);
// For the purpose of aligning fixed and sticky layers, we disregard
// the overscroll transform when computing the 'aCurrentTransformForRoot'
// parameter. This ensures that the overscroll transform is not unapplied,
// and therefore that the visual effect applies to fixed and sticky layers.
Matrix4x4 transformWithoutOverscroll = AdjustAndCombineWithCSSTransform(
combinedAsyncTransformWithoutOverscroll, aLayer);
// Since fixed/sticky layers are relative to their nearest scrolling ancestor,
@@ -762,19 +762,19 @@ ApplyAsyncTransformToScrollbarForContent
TransformClipRect(ancestor, compensation);
}
}
// GetTransform already takes the pre- and post-scale into account. Since we
// will apply the pre- and post-scale again when computing the effective
// transform, we must apply the inverses here.
if (ContainerLayer* container = aScrollbar->AsContainerLayer()) {
- transform.Scale(1.0f/container->GetPreXScale(),
- 1.0f/container->GetPreYScale(),
- 1);
+ transform.PreScale(1.0f/container->GetPreXScale(),
+ 1.0f/container->GetPreYScale(),
+ 1);
}
transform.PostScale(1.0f/aScrollbar->GetPostXScale(),
1.0f/aScrollbar->GetPostYScale(),
1);
aScrollbar->AsLayerComposite()->SetShadowTransform(transform);
}
static LayerMetricsWrapper
@@ -909,30 +909,30 @@ AsyncCompositionManager::TransformScroll
ScreenPoint translation = userScroll - geckoScroll;
Matrix4x4 treeTransform = ViewTransform(scale, -translation);
// The transform already takes the resolution scale into account. Since we
// will apply the resolution scale again when computing the effective
// transform, we must apply the inverse resolution scale here.
Matrix4x4 computedTransform = oldTransform * treeTransform;
if (ContainerLayer* container = aLayer->AsContainerLayer()) {
- computedTransform.Scale(1.0f/container->GetPreXScale(),
- 1.0f/container->GetPreYScale(),
- 1);
+ computedTransform.PreScale(1.0f/container->GetPreXScale(),
+ 1.0f/container->GetPreYScale(),
+ 1);
}
- computedTransform.ScalePost(1.0f/aLayer->GetPostXScale(),
+ computedTransform.PostScale(1.0f/aLayer->GetPostXScale(),
1.0f/aLayer->GetPostYScale(),
1);
layerComposite->SetShadowTransform(computedTransform);
NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(),
"overwriting animated transform!");
// Apply resolution scaling to the old transform - the layer tree as it is
// doesn't have the necessary transform to display correctly.
- oldTransform.Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
+ oldTransform.PreScale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
// Make sure that overscroll and under-zoom are represented in the old
// transform so that fixed position content moves and scales accordingly.
// These calculations will effectively scale and offset fixed position layers
// in screen space when the compensatory transform is performed in
// AlignFixedAndStickyLayers.
ScreenRect contentScreenRect = mContentRect * userZoom;
Point3D overscrollTranslation;
@@ -943,30 +943,30 @@ AsyncCompositionManager::TransformScroll
(userScroll.x + metrics.mCompositionBounds.width);
}
if (userScroll.y < contentScreenRect.y) {
overscrollTranslation.y = contentScreenRect.y - userScroll.y;
} else if (userScroll.y + metrics.mCompositionBounds.height > contentScreenRect.YMost()) {
overscrollTranslation.y = contentScreenRect.YMost() -
(userScroll.y + metrics.mCompositionBounds.height);
}
- oldTransform.Translate(overscrollTranslation.x,
- overscrollTranslation.y,
- overscrollTranslation.z);
+ oldTransform.PreTranslate(overscrollTranslation.x,
+ overscrollTranslation.y,
+ overscrollTranslation.z);
gfx::Size underZoomScale(1.0f, 1.0f);
if (mContentRect.width * userZoom.scale < metrics.mCompositionBounds.width) {
underZoomScale.width = (mContentRect.width * userZoom.scale) /
metrics.mCompositionBounds.width;
}
if (mContentRect.height * userZoom.scale < metrics.mCompositionBounds.height) {
underZoomScale.height = (mContentRect.height * userZoom.scale) /
metrics.mCompositionBounds.height;
}
- oldTransform.Scale(underZoomScale.width, underZoomScale.height, 1);
+ oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1);
// Make sure fixed position layers don't move away from their anchor points
// when we're asynchronously panning or zooming
AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
aLayer->GetLocalTransform(), fixedLayerMargins);
}
void
--- a/gfx/layers/composite/FPSCounter.cpp
+++ b/gfx/layers/composite/FPSCounter.cpp
@@ -378,17 +378,17 @@ static void DrawDigits(unsigned int aVal
if (aValue > 999) {
aValue = 999;
}
unsigned int divisor = 100;
float textureWidth = FontWidth * 10;
gfx::Float opacity = 1;
gfx::Matrix4x4 transform;
- transform.Scale(FontScaleX, FontScaleY, 1);
+ transform.PreScale(FontScaleX, FontScaleY, 1);
for (size_t n = 0; n < 3; ++n) {
unsigned int digit = aValue % (divisor * 10) / divisor;
divisor /= 10;
RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -124,18 +124,18 @@ ImageLayerComposite::ComputeEffectiveTra
mImageHost->IsAttached() &&
mImageHost->GetAsTextureHost()) {
IntSize size = mImageHost->GetAsTextureHost()->GetSize();
sourceRect.SizeTo(size.width, size.height);
if (mScaleMode != ScaleMode::SCALE_NONE &&
sourceRect.width != 0.0 && sourceRect.height != 0.0) {
NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
"No other scalemodes than stretch and none supported yet.");
- local.Scale(mScaleToSize.width / sourceRect.width,
- mScaleToSize.height / sourceRect.height, 1.0);
+ local.PreScale(mScaleToSize.width / sourceRect.width,
+ mScaleToSize.height / sourceRect.height, 1.0);
}
}
// Snap our local transform first, and snap the inherited transform as well.
// This makes our snapping equivalent to what would happen if our content
// was drawn into a PaintedLayer (gfxContext would snap using the local
// transform, then we'd snap again when compositing the PaintedLayer).
mEffectiveTransform =
SnapTransform(local, sourceRect, nullptr) *
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -850,17 +850,17 @@ LayerManagerComposite::ComputeRenderInte
nsTArray<Layer*> rootScrollableLayers;
GetRootScrollableLayers(rootScrollableLayers);
if (rootScrollableLayers.Length() > 0) {
// This is derived from the code in
// AsyncCompositionManager::TransformScrollableLayer
Layer* rootScrollable = rootScrollableLayers[0];
const FrameMetrics& metrics = LayerMetricsWrapper::TopmostScrollableMetrics(rootScrollable);
Matrix4x4 transform = rootScrollable->GetEffectiveTransform();
- transform.ScalePost(metrics.mResolution.scale, metrics.mResolution.scale, 1);
+ transform.PostScale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
// Clip the screen rect to the document bounds
Rect documentBounds =
transform.TransformBounds(Rect(metrics.mScrollableRect.x - metrics.GetScrollOffset().x,
metrics.mScrollableRect.y - metrics.GetScrollOffset().y,
metrics.mScrollableRect.width,
metrics.mScrollableRect.height));
documentBounds.RoundOut();
--- a/gfx/layers/composite/TextRenderer.cpp
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -129,17 +129,17 @@ TextRenderer::RenderText(const string& a
return;
}
RefPtr<EffectRGB> effect = new EffectRGB(src, true, Filter::LINEAR);
EffectChain chain;
chain.mPrimaryEffect = effect;
Matrix4x4 transform = aTransform;
- transform.Scale(scaleFactor, scaleFactor, 1.0f);
+ transform.PreScale(scaleFactor, scaleFactor, 1.0f);
mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16),
Rect(-10000, -10000, 20000, 20000), chain, 1.0f, transform);
}
void
TextRenderer::EnsureInitialized()
{
if (mGlyphBitmaps) {
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -551,18 +551,18 @@ TiledContentHost::RenderLayerBuffer(Tile
maskRegion = mTiledBuffer.GetValidRegion();
// XXX This should be ScaleRoundIn, but there is no such function on
// nsIntRegion.
maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
}
// Make sure the resolution and difference in frame resolution are accounted
// for in the layer transform.
- aTransform.Scale(1/(resolution * layerScale.width),
- 1/(resolution * layerScale.height), 1);
+ aTransform.PreScale(1/(resolution * layerScale.width),
+ 1/(resolution * layerScale.height), 1);
uint32_t rowCount = 0;
uint32_t tileX = 0;
nsIntRect visibleRect = aVisibleRegion.GetBounds();
gfx::IntSize scaledTileSize = aLayerBuffer.GetScaledTileSize();
for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
rowCount++;
int32_t tileStartX = aLayerBuffer.GetTileStart(x, scaledTileSize.width);
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -665,17 +665,17 @@ LayerTransactionParent::RecvGetAnimation
// The following code recovers the untranslated transform
// from the shadow transform by undoing the translations in
// AsyncCompositionManager::SampleValue.
Matrix4x4 transform = layer->AsLayerComposite()->GetShadowTransform();
if (ContainerLayer* c = layer->AsContainerLayer()) {
// Undo the scale transform applied by AsyncCompositionManager::SampleValue
- transform.ScalePost(1.0f/c->GetInheritedXScale(),
+ transform.PostScale(1.0f/c->GetInheritedXScale(),
1.0f/c->GetInheritedYScale(),
1.0f);
}
float scale = 1;
Point3D scaledOrigin;
Point3D transformOrigin;
for (uint32_t i=0; i < layer->GetAnimations().Length(); i++) {
if (layer->GetAnimations()[i].data().type() == AnimationData::TTransformData) {
@@ -689,17 +689,17 @@ LayerTransactionParent::RecvGetAnimation
double(nsDeviceContext::AppUnitsPerCSSPixel()) / double(scale);
transformOrigin = data.transformOrigin() * cssPerDev;
break;
}
}
// Undo the translation to the origin of the reference frame applied by
// AsyncCompositionManager::SampleValue
- transform.Translate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+ transform.PreTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
// Undo the rebasing applied by
// nsDisplayTransform::GetResultingTransformMatrixInternal
Point3D basis = -scaledOrigin - transformOrigin;
transform.ChangeBasis(basis.x, basis.y, basis.z);
// Convert to CSS pixels (this undoes the operations performed by
// nsStyleTransformMatrix::ProcessTranslatePart which is called from
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -489,17 +489,17 @@ ShadowLayerForwarder::RemoveTextureFromC
}
CompositableClient::HoldUntilComplete(aCompositable->GetIPDLActor(),
aAsyncTransactionTracker);
}
bool
ShadowLayerForwarder::InWorkerThread()
{
- return GetMessageLoop()->id() == MessageLoop::current()->id();
+ return MessageLoop::current() && (GetMessageLoop()->id() == MessageLoop::current()->id());
}
static void RemoveTextureWorker(TextureClient* aTexture, ReentrantMonitor* aBarrier, bool* aDone)
{
aTexture->ForceRemove();
ReentrantMonitorAutoEnter autoMon(*aBarrier);
*aDone = true;
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -693,18 +693,18 @@ TEST_F(APZCBasicTester, ComplexTransform
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(nsIntRect(0, 0, 300, 300)),
nsIntRegion(nsIntRect(0, 0, 150, 300)),
};
Matrix4x4 transforms[] = {
Matrix4x4(),
Matrix4x4(),
};
- transforms[0].ScalePost(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
- transforms[1].ScalePost(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
+ transforms[0].PostScale(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
+ transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
nsTArray<nsRefPtr<Layer> > layers;
nsRefPtr<LayerManager> lm;
nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
FrameMetrics metrics;
metrics.mCompositionBounds = ParentLayerRect(0, 0, 24, 24);
metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -83,17 +83,16 @@ private:
gfxContext *mContext;
Pattern *mPattern;
};
gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
: mPathIsRect(false)
, mTransformChanged(false)
, mRefCairo(nullptr)
- , mSurface(nullptr)
, mFlags(0)
, mDT(aTarget)
, mOriginalDT(aTarget)
{
MOZ_ASSERT(aTarget, "Don't create a gfxContext without a DrawTarget");
MOZ_COUNT_CTOR(gfxContext);
@@ -125,34 +124,16 @@ gfxContext::~gfxContext()
if (mStateStack[i].clipWasReset) {
break;
}
}
mDT->Flush();
MOZ_COUNT_DTOR(gfxContext);
}
-gfxASurface *
-gfxContext::OriginalSurface()
-{
- if (mSurface) {
- return mSurface;
- }
-
- if (mOriginalDT && mOriginalDT->GetBackendType() == BackendType::CAIRO) {
- cairo_surface_t *s =
- (cairo_surface_t*)mOriginalDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
- if (s) {
- mSurface = gfxASurface::Wrap(s);
- return mSurface;
- }
- }
- return nullptr;
-}
-
already_AddRefed<gfxASurface>
gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
{
if (mDT->GetBackendType() == BackendType::CAIRO) {
cairo_surface_t *s =
(cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
if (s) {
if (dx && dy) {
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -56,21 +56,16 @@ public:
/**
* Create a new gfxContext wrapping aTarget and preserving aTarget's
* transform. Note that the transform is moved from aTarget to the resulting
* gfxContext, aTarget will no longer have its transform.
*/
static already_AddRefed<gfxContext> ContextForDrawTarget(mozilla::gfx::DrawTarget* aTarget);
/**
- * Return the surface that this gfxContext was created with
- */
- gfxASurface *OriginalSurface();
-
- /**
* Return the current transparency group target, if any, along
* with its device offsets from the top. If no group is
* active, returns the surface the gfxContext was created with,
* and 0,0 in dx,dy.
*/
already_AddRefed<gfxASurface> CurrentSurface(gfxFloat *dx, gfxFloat *dy);
already_AddRefed<gfxASurface> CurrentSurface() {
return CurrentSurface(nullptr, nullptr);
@@ -512,20 +507,16 @@ public:
OPERATOR_COLOR,
OPERATOR_LUMINOSITY
};
/**
* Sets the operator used for all further drawing. The operator affects
* how drawing something will modify the destination. For example, the
* OVER operator will do alpha blending of source and destination, while
* SOURCE will replace the destination with the source.
- *
- * Note that if the flag FLAG_SIMPLIFY_OPERATORS is set on this
- * gfxContext, the actual operator set might change for optimization
- * purposes. Check the comments below around that flag.
*/
void SetOperator(GraphicsOperator op);
GraphicsOperator CurrentOperator() const;
void SetAntialiasMode(mozilla::gfx::AntialiasMode mode);
mozilla::gfx::AntialiasMode CurrentAntialiasMode() const;
/**
@@ -588,42 +579,22 @@ public:
already_AddRefed<gfxPattern> PopGroup();
void PopGroupToSource();
mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
PopGroupToSurface(mozilla::gfx::Matrix* aMatrix);
mozilla::gfx::Point GetDeviceOffset() const;
- /**
- ** Flags
- **/
-
enum {
- /* If this flag is set, operators other than CLEAR, SOURCE, or
- * OVER will be converted to OVER before being sent to cairo.
- *
- * This is most useful with a printing surface, where
- * operators such as ADD are used to avoid seams for on-screen
- * display, but where such errors aren't noticeable in print.
- * This approach is currently used in border rendering.
- *
- * However, when printing complex renderings such as SVG,
- * care should be taken to clear this flag.
- */
- FLAG_SIMPLIFY_OPERATORS = (1 << 0),
- /**
+ /**
* When this flag is set, snapping to device pixels is disabled.
* It simply never does anything.
*/
FLAG_DISABLE_SNAPPING = (1 << 1),
- /**
- * Disable copying of backgrounds in PushGroupAndCopyBackground.
- */
- FLAG_DISABLE_COPY_BACKGROUND = (1 << 2)
};
void SetFlag(int32_t aFlag) { mFlags |= aFlag; }
void ClearFlag(int32_t aFlag) { mFlags &= ~aFlag; }
int32_t GetFlags() const { return mFlags; }
// Work out whether cairo will snap inter-glyph spacing to pixels.
void GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY);
@@ -727,17 +698,16 @@ private:
mozilla::RefPtr<Path> mPath;
Matrix mTransform;
nsTArray<AzureState> mStateStack;
AzureState &CurrentState() { return mStateStack[mStateStack.Length() - 1]; }
const AzureState &CurrentState() const { return mStateStack[mStateStack.Length() - 1]; }
cairo_t *mRefCairo;
- nsRefPtr<gfxASurface> mSurface;
int32_t mFlags;
mozilla::RefPtr<DrawTarget> mDT;
mozilla::RefPtr<DrawTarget> mOriginalDT;
};
/**
* Sentry helper class for functions with multiple return points that need to
@@ -901,22 +871,19 @@ public:
if (aDisable) {
mDT = aContext->GetDrawTarget();
mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
mDT->SetPermitSubpixelAA(false);
}
}
~gfxContextAutoDisableSubpixelAntialiasing()
{
- if (mSurface) {
- mSurface->SetSubpixelAntialiasingEnabled(mSubpixelAntialiasingEnabled);
- } else if (mDT) {
+ if (mDT) {
mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
}
}
private:
- nsRefPtr<gfxASurface> mSurface;
mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
bool mSubpixelAntialiasingEnabled;
};
#endif /* GFX_CONTEXT_H */
--- a/image/public/imgILoader.idl
+++ b/image/public/imgILoader.idl
@@ -11,28 +11,27 @@ interface imgIRequest;
interface nsIChannel;
interface nsILoadGroup;
interface nsIPrincipal;
interface nsIStreamListener;
interface nsIURI;
interface nsISimpleEnumerator;
-interface nsIChannelPolicy;
#include "nsIRequest.idl" // for nsLoadFlags
/**
* imgILoader interface
*
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.3
* @see imagelib2
*/
-[scriptable, builtinclass, uuid(c8126129-8dac-43cd-b1ba-3896fba2dd01)]
+[scriptable, builtinclass, uuid(046d5fa6-ac59-489d-b51e-0ffe57e8df59)]
interface imgILoader : nsISupports
{
// Extra flags to pass to loadImage if you want a load to use CORS
// validation.
const unsigned long LOAD_CORS_ANONYMOUS = 1 << 16;
const unsigned long LOAD_CORS_USE_CREDENTIALS = 1 << 17;
/**
@@ -57,18 +56,17 @@ interface imgILoader : nsISupports
imgIRequest loadImageXPCOM(in nsIURI aURI,
in nsIURI aInitialDocumentURL,
in nsIURI aReferrerURI,
in nsIPrincipal aLoadingPrincipal,
in nsILoadGroup aLoadGroup,
in imgINotificationObserver aObserver,
in nsISupports aCX,
in nsLoadFlags aLoadFlags,
- in nsISupports cacheKey,
- in nsIChannelPolicy channelPolicy);
+ in nsISupports cacheKey);
/**
* Start the load and decode of an image.
* @param aChannel the channel to load the image from. This must
* already be opened before ths method is called, and there
* must have been no OnDataAvailable calls for it yet.
* @param aObserver the observer (may be null)
* @param cx some random data
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -15,16 +15,17 @@ namespace mozilla {
namespace image {
Decoder::Decoder(RasterImage &aImage)
: mImage(aImage)
, mCurrentFrame(nullptr)
, mImageData(nullptr)
, mColormap(nullptr)
, mDecodeFlags(0)
+ , mBytesDecoded(0)
, mDecodeDone(false)
, mDataError(false)
, mFrameCount(0)
, mFailCode(NS_OK)
, mNeedsNewFrame(false)
, mInitialized(false)
, mSizeDecode(false)
, mInFrame(false)
@@ -88,18 +89,21 @@ void
Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
{
PROFILER_LABEL("ImageDecoder", "Write",
js::ProfileEntry::Category::GRAPHICS);
MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
// We're strict about decoder errors
- NS_ABORT_IF_FALSE(!HasDecoderError(),
- "Not allowed to make more decoder calls after error!");
+ MOZ_ASSERT(!HasDecoderError(),
+ "Not allowed to make more decoder calls after error!");
+
+ // Keep track of the total number of bytes written.
+ mBytesDecoded += aCount;
// If a data error occured, just ignore future data
if (HasDataError())
return;
if (IsSizeDecode() && HasSize()) {
// More data came in since we found the size. We have nothing to do here.
return;
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -96,16 +96,18 @@ public:
}
void SetObserver(imgDecoderObserver* aObserver)
{
MOZ_ASSERT(aObserver);
mObserver = aObserver;
}
+ size_t BytesDecoded() const { return mBytesDecoded; }
+
// The number of frames we have, including anything in-progress. Thus, this
// is only 0 if we haven't begun any frames.
uint32_t GetFrameCount() { return mFrameCount; }
// The number of complete frames we have (ie, not including anything in-progress).
uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
// Error tracking
@@ -229,16 +231,17 @@ protected:
ImageMetadata mImageMetadata;
uint8_t* mImageData; // Pointer to image data in either Cairo or 8bit format
uint32_t mImageDataLength;
uint32_t* mColormap; // Current colormap to be used in Cairo format
uint32_t mColormapSize;
uint32_t mDecodeFlags;
+ size_t mBytesDecoded;
bool mDecodeDone;
bool mDataError;
private:
uint32_t mFrameCount; // Number of frames, including anything in-progress
nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -57,17 +57,19 @@
#endif
namespace mozilla {
using namespace gfx;
using namespace layers;
namespace image {
+
using std::ceil;
+using std::min;
// a mask for flags that will affect the decoding
#define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
#define DECODE_FLAGS_DEFAULT 0
static uint32_t
DecodeFlags(uint32_t aFlags)
{
@@ -314,17 +316,16 @@ RasterImage::RasterImage(imgStatusTracke
mLockCount(0),
mDecodeCount(0),
mRequestedSampleSize(0),
#ifdef DEBUG
mFramesNotified(0),
#endif
mDecodingMonitor("RasterImage Decoding Monitor"),
mDecoder(nullptr),
- mBytesDecoded(0),
mInDecoder(false),
mStatusDiff(ImageStatusDiff::NoChange()),
mNotifying(false),
mHasSize(false),
mDecodeOnDraw(false),
mMultipart(false),
mDiscardable(false),
mHasSourceData(false),
@@ -1530,17 +1531,17 @@ RasterImage::AddSourceData(const char *a
// be extra garbage data at the end of a file.
if (mDecoded) {
return NS_OK;
}
// Starting a new part's frames, let's clean up before we add any
// This needs to happen just before we start getting EnsureFrame() call(s),
// so that there's no gap for anything to miss us.
- if (mMultipart && mBytesDecoded == 0) {
+ if (mMultipart && (!mDecoder || mDecoder->BytesDecoded() == 0)) {
// Our previous state may have been animated, so let's clean up
if (mAnimating)
StopAnimation();
mAnimationFinished = false;
if (mAnim) {
mAnim = nullptr;
}
// If there's only one frame, this could cause flickering
@@ -2090,18 +2091,16 @@ RasterImage::ShutdownDecoder(eShutdownIn
}
// If we finished a full decode, and we're not meant to be storing source
// data, stop storing it.
if (!wasSizeDecode && !StoringSourceData()) {
mSourceData.Clear();
}
- mBytesDecoded = 0;
-
return NS_OK;
}
// Writes the data to the decoder, updating the total number of bytes written.
nsresult
RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
{
mDecodingMonitor.AssertCurrentThreadIn();
@@ -2112,20 +2111,16 @@ RasterImage::WriteToDecoder(const char *
// Write
nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
mInDecoder = true;
mDecoder->Write(aBuffer, aCount, aStrategy);
mInDecoder = false;
CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
- // Keep track of the total number of bytes written over the lifetime of the
- // decoder
- mBytesDecoded += aCount;
-
return NS_OK;
}
// This function is called in situations where it's clear that we want the
// frames in decoded form (Draw, LookupFrame, etc). If we're completely decoded,
// this method resets the discard timer (if we're discardable), since wanting
// the frames now is a good indicator of wanting them again soon. If we're not
// decoded, this method kicks off asynchronous decoding to generate the frames.
@@ -2247,28 +2242,29 @@ RasterImage::RequestDecodeCore(RequestDe
// synchronous decode if we're already waiting on a full decode).
if (mDecoded) {
return NS_OK;
}
// If we've already got a full decoder running, and have already decoded
// some bytes, we have nothing to do if we haven't been asked to do some
// sync decoding
- if (mDecoder && !mDecoder->IsSizeDecode() && mBytesDecoded &&
+ if (mDecoder && !mDecoder->IsSizeDecode() && mDecoder->BytesDecoded() > 0 &&
aDecodeType != SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) {
return NS_OK;
}
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
// If we don't have any bytes to flush to the decoder, we can't do anything.
- // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
- // the source data.
- if (mBytesDecoded > mSourceData.Length())
+ // mDecoder->BytesDecoded() can be bigger than mSourceData.Length() if we're
+ // not storing the source data.
+ if (mDecoder && mDecoder->BytesDecoded() > mSourceData.Length()) {
return NS_OK;
+ }
// After acquiring the lock we may have finished some more decoding, so
// we need to repeat the following three checks after getting the lock.
// If the image is waiting for decode work to be notified, go ahead and do that.
if (mDecodeRequest &&
mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE &&
aDecodeType != ASYNCHRONOUS) {
@@ -2280,18 +2276,18 @@ RasterImage::RequestDecodeCore(RequestDe
// DecodeUntilSizeAvailable and FinishedSomeDecoding because they can result
// in us finishing an in-progress decode (or kicking off and finishing a
// synchronous decode if we're already waiting on a full decode).
if (mDecoded) {
return NS_OK;
}
// If we've already got a full decoder running, and have already
- // decoded some bytes, we have nothing to do
- if (mDecoder && !mDecoder->IsSizeDecode() && mBytesDecoded) {
+ // decoded some bytes, we have nothing to do.
+ if (mDecoder && !mDecoder->IsSizeDecode() && mDecoder->BytesDecoded() > 0) {
return NS_OK;
}
// If we have a size decode open, interrupt it and shut it down; or if
// the decoder has different flags than what we need
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
CONTAINER_ENSURE_SUCCESS(rv);
@@ -2299,23 +2295,24 @@ RasterImage::RequestDecodeCore(RequestDe
// If we don't have a decoder, create one
if (!mDecoder) {
rv = InitDecoder(/* aDoSizeDecode = */ false);
CONTAINER_ENSURE_SUCCESS(rv);
rv = FinishedSomeDecoding();
CONTAINER_ENSURE_SUCCESS(rv);
-
- MOZ_ASSERT(mDecoder);
}
+ MOZ_ASSERT(mDecoder);
+
// If we've read all the data we have, we're done
- if (mHasSourceData && mBytesDecoded == mSourceData.Length())
+ if (mHasSourceData && mDecoder->BytesDecoded() == mSourceData.Length()) {
return NS_OK;
+ }
// If we can do decoding now, do so. Small images will decode completely,
// large images will decode a bit and post themselves to the event loop
// to finish decoding.
if (!mDecoded && !mInDecoder && mHasSourceData && aDecodeType == SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) {
PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf",
js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
@@ -2372,20 +2369,21 @@ RasterImage::SyncDecode()
nsresult rv;
// If we're decoded already, or decoding until the size was available
// finished us as a side-effect, no worries
if (mDecoded)
return NS_OK;
// If we don't have any bytes to flush to the decoder, we can't do anything.
- // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
- // the source data.
- if (mBytesDecoded > mSourceData.Length())
+ // mDecoder->BytesDecoded() can be bigger than mSourceData.Length() if we're
+ // not storing the source data.
+ if (mDecoder && mDecoder->BytesDecoded() > mSourceData.Length()) {
return NS_OK;
+ }
// If we have a decoder open with different flags than what we need, shut it
// down
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
CONTAINER_ENSURE_SUCCESS(rv);
if (mDecoded) {
@@ -2406,18 +2404,21 @@ RasterImage::SyncDecode()
}
// If we don't have a decoder, create one
if (!mDecoder) {
rv = InitDecoder(/* aDoSizeDecode = */ false);
CONTAINER_ENSURE_SUCCESS(rv);
}
+ MOZ_ASSERT(mDecoder);
+
// Write everything we have
- rv = DecodeSomeData(mSourceData.Length() - mBytesDecoded, DECODE_SYNC);
+ rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
+ DECODE_SYNC);
CONTAINER_ENSURE_SUCCESS(rv);
// When we're doing a sync decode, we want to get as much information from the
// image as possible. We've send the decoder all of our data, so now's a good
// time to flush any invalidations (in case we don't have all the data and what
// we got left us mid-frame).
nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
mInDecoder = true;
@@ -2767,58 +2768,57 @@ RasterImage::RequestDiscard()
return NS_OK;
}
// Flushes up to aMaxBytes to the decoder.
nsresult
RasterImage::DecodeSomeData(size_t aMaxBytes, DecodeStrategy aStrategy)
{
- // We should have a decoder if we get here
- NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
+ MOZ_ASSERT(mDecoder, "Should have a decoder");
mDecodingMonitor.AssertCurrentThreadIn();
// First, if we've just been called because we allocated a frame on the main
// thread, let the decoder deal with the data it set aside at that time by
// passing it a null buffer.
if (mDecodeRequest->mAllocatedNewFrame) {
mDecodeRequest->mAllocatedNewFrame = false;
nsresult rv = WriteToDecoder(nullptr, 0, aStrategy);
if (NS_FAILED(rv) || mDecoder->NeedsNewFrame()) {
return rv;
}
}
- // If we have nothing else to decode, return
- if (mBytesDecoded == mSourceData.Length())
+ // If we have nothing else to decode, return.
+ if (mDecoder->BytesDecoded() == mSourceData.Length()) {
return NS_OK;
-
- MOZ_ASSERT(mBytesDecoded < mSourceData.Length());
+ }
+
+ MOZ_ASSERT(mDecoder->BytesDecoded() < mSourceData.Length());
// write the proper amount of data
- size_t bytesToDecode = std::min(aMaxBytes,
- mSourceData.Length() - mBytesDecoded);
- nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
- bytesToDecode,
- aStrategy);
-
- return rv;
+ size_t bytesToDecode = min(aMaxBytes,
+ mSourceData.Length() - mDecoder->BytesDecoded());
+ return WriteToDecoder(mSourceData.Elements() + mDecoder->BytesDecoded(),
+ bytesToDecode,
+ aStrategy);
+
}
// There are various indicators that tell us we're finished with the decode
// task at hand and can shut down the decoder.
//
// This method may not be called if there is no decoder.
bool
RasterImage::IsDecodeFinished()
{
// Precondition
mDecodingMonitor.AssertCurrentThreadIn();
- NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
+ MOZ_ASSERT(mDecoder, "Should have a decoder");
// The decode is complete if we got what we wanted.
if (mDecoder->IsSizeDecode()) {
if (mDecoder->HasSize()) {
return true;
}
} else if (mDecoder->GetDecodeDone()) {
return true;
@@ -2833,17 +2833,17 @@ RasterImage::IsDecodeFinished()
}
// Otherwise, if we have all the source data and wrote all the source data,
// we're done.
//
// (NB - This can be the case even for non-erroneous images because
// Decoder::GetDecodeDone() might not return true until after we call
// Decoder::Finish() in ShutdownDecoder())
- if (mHasSourceData && (mBytesDecoded == mSourceData.Length())) {
+ if (mHasSourceData && (mDecoder->BytesDecoded() == mSourceData.Length())) {
return true;
}
// If we get here, assume it's not finished.
return false;
}
// Indempotent error flagging routine. If a decoder is open, shuts it down.
@@ -3026,17 +3026,17 @@ RasterImage::FinishedSomeDecoding(eShutd
if (request && !wasSize) {
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
int32_t(request->mDecodeTime.ToMicroseconds()));
// We record the speed for only some decoders. The rest have
// SpeedHistogram return HistogramCount.
Telemetry::ID id = decoder->SpeedHistogram();
if (id < Telemetry::HistogramCount) {
- int32_t KBps = int32_t(request->mImage->mBytesDecoded /
+ int32_t KBps = int32_t(decoder->BytesDecoded() /
(1024 * request->mDecodeTime.ToSeconds()));
Telemetry::Accumulate(id, KBps);
}
}
// We need to shut down the decoder first, in order to ensure all
// decoding routines have been finished.
rv = image->ShutdownDecoder(aIntent);
@@ -3202,17 +3202,18 @@ RasterImage::DecodePool::RequestDecode(R
MOZ_ASSERT(aImg->mDecoder);
aImg->mDecodingMonitor.AssertCurrentThreadIn();
// If we're currently waiting on a new frame for this image, we can't do any
// decoding.
if (!aImg->mDecoder->NeedsNewFrame()) {
// No matter whether this is currently being decoded, we need to update the
// number of bytes we want it to decode.
- aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
+ aImg->mDecodeRequest->mBytesToDecode =
+ aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded();
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING ||
aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_ACTIVE) {
// The image is already in our list of images to decode, or currently being
// decoded, so we don't have to do anything else.
return;
}
@@ -3250,17 +3251,17 @@ RasterImage::DecodePool::DecodeABitOf(Ra
if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
FrameNeededWorker::GetNewFrame(aImg);
} else {
// If we aren't yet finished decoding and we have more data in hand, add
// this request to the back of the priority list.
if (aImg->mDecoder &&
!aImg->mError &&
!aImg->IsDecodeFinished() &&
- aImg->mSourceData.Length() > aImg->mBytesDecoded) {
+ aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded()) {
RequestDecode(aImg);
}
}
}
/* static */ void
RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
{
@@ -3294,29 +3295,29 @@ RasterImage::DecodePool::DecodeJob::Run(
// still needs a new frame, we can't do anything. Wait until the
// FrameNeededWorker enqueues another frame.
if (mImage->mDecoder->NeedsNewFrame()) {
return NS_OK;
}
mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE;
- size_t oldByteCount = mImage->mBytesDecoded;
+ size_t oldByteCount = mImage->mDecoder->BytesDecoded();
DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES;
// Multithreaded decoding can be disabled. If we've done so, we don't want to
// monopolize the main thread, and will allow a timeout in DecodeSomeOfImage.
if (NS_IsMainThread()) {
type = DECODE_TYPE_UNTIL_TIME;
}
DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC, type, mRequest->mBytesToDecode);
- size_t bytesDecoded = mImage->mBytesDecoded - oldByteCount;
+ size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
mRequest->mRequestStatus = DecodeRequest::REQUEST_WORK_DONE;
// If the decoder needs a new frame, enqueue an event to get it; that event
// will enqueue another decode request when it's done.
if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
FrameNeededWorker::GetNewFrame(mImage);
}
@@ -3437,31 +3438,31 @@ RasterImage::DecodePool::DecodeSomeOfIma
} else {
// We're only guaranteed to decode this many bytes, so in particular,
// gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
// to read the size from most images.
maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
}
if (bytesToDecode == 0) {
- bytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
+ bytesToDecode = aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded();
}
int32_t chunkCount = 0;
TimeStamp start = TimeStamp::Now();
TimeStamp deadline = start + TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
// We keep decoding chunks until:
// * we don't have any data left to decode,
// * the decode completes,
// * we're an UNTIL_SIZE decode and we get the size, or
// * we run out of time.
// We also try to decode at least one "chunk" if we've allocated a new frame,
// even if we have no more data to send to the decoder.
- while ((aImg->mSourceData.Length() > aImg->mBytesDecoded &&
+ while ((aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded() &&
bytesToDecode > 0 &&
!aImg->IsDecodeFinished() &&
!(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) &&
!aImg->mDecoder->NeedsNewFrame()) ||
(aImg->mDecodeRequest && aImg->mDecodeRequest->mAllocatedNewFrame)) {
chunkCount++;
uint32_t chunkSize = std::min(bytesToDecode, maxBytes);
nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy);
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -651,17 +651,16 @@ private: // data
// BEGIN LOCKED MEMBER VARIABLES
ReentrantMonitor mDecodingMonitor;
FallibleTArray<char> mSourceData;
// Decoder and friends
nsRefPtr<Decoder> mDecoder;
nsRefPtr<DecodeRequest> mDecodeRequest;
- size_t mBytesDecoded;
bool mInDecoder;
// END LOCKED MEMBER VARIABLES
// Notification state. Used to avoid recursive notifications.
ImageStatusDiff mStatusDiff;
bool mNotifying:1;
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -630,17 +630,16 @@ static nsresult NewImageChannel(nsIChann
// aLoadingPrincipal and false otherwise.
bool *aForcePrincipalCheckForCacheEntry,
nsIURI *aURI,
nsIURI *aInitialDocumentURI,
nsIURI *aReferringURI,
nsILoadGroup *aLoadGroup,
const nsCString& aAcceptHeader,
nsLoadFlags aLoadFlags,
- nsIChannelPolicy *aPolicy,
nsIPrincipal *aLoadingPrincipal,
nsISupports *aRequestingContext)
{
nsresult rv;
nsCOMPtr<nsIHttpChannel> newHttpChannel;
nsCOMPtr<nsIInterfaceRequestor> callbacks;
@@ -686,17 +685,16 @@ static nsresult NewImageChannel(nsIChann
// stylesheets, where the document is being styled, but the principal is that
// of the user stylesheet.
rv = NS_NewChannelInternal(aResult,
aURI,
requestingNode,
requestingPrincipal,
securityFlags,
nsIContentPolicy::TYPE_IMAGE,
- aPolicy,
nullptr, // loadGroup
callbacks,
aLoadFlags);
if (NS_FAILED(rv))
return rv;
*aForcePrincipalCheckForCacheEntry = inherit && !isSandBoxed;
@@ -1440,17 +1438,16 @@ bool imgLoader::ValidateRequestWithNewCh
nsIURI *aURI,
nsIURI *aInitialDocumentURI,
nsIURI *aReferrerURI,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
imgRequestProxy **aProxyRequest,
- nsIChannelPolicy *aPolicy,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode)
{
// now we need to insert a new channel request object inbetween the real
// request and the proxy that basically delays loading the image until it
// gets a 304 or figures out that this needs to be a new request
nsresult rv;
@@ -1488,17 +1485,16 @@ bool imgLoader::ValidateRequestWithNewCh
rv = NewImageChannel(getter_AddRefs(newChannel),
&forcePrincipalCheck,
aURI,
aInitialDocumentURI,
aReferrerURI,
aLoadGroup,
mAcceptHeader,
aLoadFlags,
- aPolicy,
aLoadingPrincipal,
aCX);
if (NS_FAILED(rv)) {
return false;
}
nsRefPtr<imgRequestProxy> req;
rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
@@ -1568,17 +1564,16 @@ bool imgLoader::ValidateEntry(imgCacheEn
nsIURI *aInitialDocumentURI,
nsIURI *aReferrerURI,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
bool aCanMakeNewChannel,
imgRequestProxy **aProxyRequest,
- nsIChannelPolicy *aPolicy,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode)
{
LOG_SCOPE(GetImgLog(), "imgLoader::ValidateEntry");
bool hasExpired;
uint32_t expirationTime = aEntry->GetExpiryTime();
if (expirationTime <= SecondsFromPRTime(PR_Now())) {
@@ -1673,17 +1668,17 @@ bool imgLoader::ValidateEntry(imgCacheEn
return false;
}
if (validateRequest && aCanMakeNewChannel) {
LOG_SCOPE(GetImgLog(), "imgLoader::ValidateRequest |cache hit| must validate");
return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
aReferrerURI, aLoadGroup, aObserver,
- aCX, aLoadFlags, aProxyRequest, aPolicy,
+ aCX, aLoadFlags, aProxyRequest,
aLoadingPrincipal, aCORSMode);
}
return !validateRequest;
}
bool imgLoader::RemoveFromCache(nsIURI *aKey)
{
@@ -1848,52 +1843,54 @@ NS_IMETHODIMP imgLoader::LoadImageXPCOM(
nsIURI *aInitialDocumentURI,
nsIURI *aReferrerURI,
nsIPrincipal* aLoadingPrincipal,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
- nsIChannelPolicy *aPolicy,
imgIRequest **_retval)
{
imgRequestProxy *proxy;
nsresult result = LoadImage(aURI,
aInitialDocumentURI,
aReferrerURI,
aLoadingPrincipal,
aLoadGroup,
aObserver,
aCX,
aLoadFlags,
aCacheKey,
- aPolicy,
EmptyString(),
&proxy);
*_retval = proxy;
return result;
}
-
-
-/* imgIRequest loadImage(in nsIURI aURI, in nsIURI aInitialDocumentURL, in nsIURI aReferrerURI, in nsIPrincipal aLoadingPrincipal, in nsILoadGroup aLoadGroup, in imgINotificationObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in nsIChannelPolicy channelPolicy); */
-
+// imgIRequest loadImage(in nsIURI aURI,
+// in nsIURI aInitialDocumentURL,
+// in nsIURI aReferrerURI,
+// in nsIPrincipal aLoadingPrincipal,
+// in nsILoadGroup aLoadGroup,
+// in imgINotificationObserver aObserver,
+// in nsISupports aCX,
+// in nsLoadFlags aLoadFlags,
+// in nsISupports cacheKey);
nsresult imgLoader::LoadImage(nsIURI *aURI,
- nsIURI *aInitialDocumentURI,
- nsIURI *aReferrerURI,
- nsIPrincipal* aLoadingPrincipal,
- nsILoadGroup *aLoadGroup,
- imgINotificationObserver *aObserver,
- nsISupports *aCX,
- nsLoadFlags aLoadFlags,
- nsISupports *aCacheKey,
- nsIChannelPolicy *aPolicy,
- const nsAString& initiatorType,
- imgRequestProxy **_retval)
+ nsIURI *aInitialDocumentURI,
+ nsIURI *aReferrerURI,
+ nsIPrincipal* aLoadingPrincipal,
+ nsILoadGroup *aLoadGroup,
+ imgINotificationObserver *aObserver,
+ nsISupports *aCX,
+ nsLoadFlags aLoadFlags,
+ nsISupports *aCacheKey,
+ const nsAString& initiatorType,
+ imgRequestProxy **_retval)
{
VerifyCacheSizes();
NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
if (!aURI)
return NS_ERROR_NULL_POINTER;
@@ -1961,17 +1958,17 @@ nsresult imgLoader::LoadImage(nsIURI *aU
// XXX For now ignore aCacheKey. We will need it in the future
// for correctly dealing with image load requests that are a result
// of post data.
imgCacheTable &cache = GetCache(aURI);
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
aLoadGroup, aObserver, aCX, requestFlags, true,
- _retval, aPolicy, aLoadingPrincipal, corsmode)) {
+ _retval, aLoadingPrincipal, corsmode)) {
request = entry->GetRequest();
// If this entry has no proxies, its request has no reference to the entry.
if (entry->HasNoProxies()) {
LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
request->SetCacheEntry(entry);
@@ -2003,17 +2000,16 @@ nsresult imgLoader::LoadImage(nsIURI *aU
rv = NewImageChannel(getter_AddRefs(newChannel),
&forcePrincipalCheck,
aURI,
aInitialDocumentURI,
aReferrerURI,
aLoadGroup,
mAcceptHeader,
requestFlags,
- aPolicy,
aLoadingPrincipal,
aCX);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
NewRequestAndEntry(forcePrincipalCheck, this, getter_AddRefs(request), getter_AddRefs(entry));
@@ -2187,17 +2183,17 @@ nsresult imgLoader::LoadImageWithChannel
// We don't want to kick off another network load. So we ask
// ValidateEntry to only do validation without creating a new proxy. If
// it says that the entry isn't valid any more, we'll only use the entry
// we're getting if the channel is loading from the cache anyways.
//
// XXX -- should this be changed? it's pretty much verbatim from the old
// code, but seems nonsensical.
if (ValidateEntry(entry, uri, nullptr, nullptr, nullptr, aObserver, aCX,
- requestFlags, false, nullptr, nullptr, nullptr,
+ requestFlags, false, nullptr, nullptr,
imgIRequest::CORS_NONE)) {
request = entry->GetRequest();
} else {
nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
bool bUseCacheCopy;
if (cacheChan)
cacheChan->IsFromCache(&bUseCacheCopy);
--- a/image/src/imgLoader.h
+++ b/image/src/imgLoader.h
@@ -24,17 +24,16 @@
#include "imgIRequest.h"
class imgLoader;
class imgRequestProxy;
class imgINotificationObserver;
class nsILoadGroup;
class imgCacheExpirationTracker;
class imgMemoryReporter;
-class nsIChannelPolicy;
namespace mozilla {
namespace image {
class ImageURL;
}
}
class imgCacheEntry
@@ -253,17 +252,16 @@ public:
nsIURI *aInitialDocumentURI,
nsIURI *aReferrerURI,
nsIPrincipal* aLoadingPrincipal,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
- nsIChannelPolicy *aPolicy,
const nsAString& initiatorType,
imgRequestProxy **_retval);
nsresult LoadImageWithChannel(nsIChannel *channel,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsIStreamListener **listener,
imgRequestProxy **_retval);
@@ -335,28 +333,26 @@ public:
private: // methods
bool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aKey,
nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver, nsISupports *aCX,
nsLoadFlags aLoadFlags, bool aCanMakeNewChannel,
imgRequestProxy **aProxyRequest,
- nsIChannelPolicy *aPolicy,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode);
bool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI,
nsIURI *aInitialDocumentURI,
nsIURI *aReferrerURI,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsISupports *aCX, nsLoadFlags aLoadFlags,
imgRequestProxy **aProxyRequest,
- nsIChannelPolicy *aPolicy,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode);
nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsLoadFlags aLoadFlags, imgRequestProxy **_retval);
void ReadAcceptHeaderPref();
--- a/image/test/unit/async_load_tests.js
+++ b/image/test/unit/async_load_tests.js
@@ -91,17 +91,17 @@ function secondLoadDone(oldlistener, aRe
// therefore would be at most risk of being served synchronously.
function checkSecondLoad()
{
do_test_pending();
var listener = new ImageListener(checkClone, secondLoadDone);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
- requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null, null));
+ requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null));
listener.synchronous = false;
}
function firstLoadDone(oldlistener, aRequest)
{
checkSecondLoad(uri);
do_test_finished();
@@ -189,17 +189,17 @@ function startImageCallback(otherCb)
{
return function(listener, request)
{
// Make sure we can load the same image immediately out of the cache.
do_test_pending();
var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); });
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener2);
- requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null, null));
+ requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null));
listener2.synchronous = false;
// Now that we've started another load, chain to the callback.
otherCb(listener, request);
}
}
var gCurrentLoader;
@@ -216,16 +216,16 @@ function run_test()
do_register_cleanup(cleanup);
gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
do_test_pending();
var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
- var req = gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null, null);
+ var req = gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null);
requests.push(req);
// Ensure that we don't cause any mayhem when we lock an image.
req.lockImage();
listener.synchronous = false;
}
--- a/image/test/unit/test_private_channel.js
+++ b/image/test/unit/test_private_channel.js
@@ -72,17 +72,17 @@ function setup_chan(path, isPrivate, cal
function loadImage(isPrivate, callback) {
var listener = new ImageListener(null, callback);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var uri = gIoService.newURI(gImgPath, null, null);
var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate);
var loader = isPrivate ? gPrivateLoader : gPublicLoader;
- requests.push(loader.loadImageXPCOM(uri, null, null, null, loadGroup, outer, null, 0, null, null));
+ requests.push(loader.loadImageXPCOM(uri, null, null, null, loadGroup, outer, null, 0, null));
listener.synchronous = false;
}
function run_loadImage_tests() {
function observer() {
Services.obs.removeObserver(observer, "cacheservice:empty-cache");
gHits = 0;
loadImage(false, function() {
--- a/js/public/UbiNodeTraverse.h
+++ b/js/public/UbiNodeTraverse.h
@@ -90,16 +90,25 @@ struct BreadthFirst {
// Initialize this traversal object. Return false on OOM.
bool init() { return visited.init(); }
// Add |node| as a starting point for the traversal. You may add
// as many starting points as you like. Return false on OOM.
bool addStart(Node node) { return pending.append(node); }
+ // Add |node| as a starting point for the traversal (see addStart) and also
+ // add it to the |visited| set. Return false on OOM.
+ bool addStartVisited(Node node) {
+ typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
+ if (!ptr && !visited.add(ptr, node, typename Handler::NodeData()))
+ return false;
+ return addStart(node);
+ }
+
// True if the handler wants us to compute edge names; doing so can be
// expensive in time and memory. True by default.
bool wantNames;
// Traverse the graph in breadth-first order, starting at the given
// start nodes, applying |handler::operator()| for each edge traversed
// as described above.
//
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -467,16 +467,24 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
ScopedJSFreePtr<char> msg(
JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
"by const heap accesses and/or change-heap minimum-length requirements).",
heapLength,
module.minHeapLength()));
return LinkFail(cx, msg.get());
}
+ if (heapLength > module.maxHeapLength()) {
+ ScopedJSFreePtr<char> msg(
+ JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
+ heapLength,
+ module.maxHeapLength()));
+ return LinkFail(cx, msg.get());
+ }
+
// If we've generated the code with signal handlers in mind (for bounds
// checks on x64 and for interrupt callback requesting on all platforms),
// we need to be able to use signals at runtime. In particular, a module
// can have been created using signals and cached, and executed without
// signals activated.
if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");
@@ -488,17 +496,17 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
module.initHeap(heap, cx);
return true;
}
static bool
DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
- module.setIsDynamicallyLinked();
+ module.setIsDynamicallyLinked(cx->runtime());
HandleValue globalVal = args.get(0);
HandleValue importVal = args.get(1);
HandleValue bufferVal = args.get(2);
Rooted<ArrayBufferObjectMaybeShared *> heap(cx);
if (module.hasArrayView()) {
if (IsArrayBuffer(bufferVal) || IsSharedArrayBuffer(bufferVal))
@@ -566,21 +574,29 @@ ChangeHeap(JSContext *cx, AsmJSModule &m
HandleValue bufferArg = args.get(0);
if (!IsArrayBuffer(bufferArg)) {
ReportIncompatible(cx, args);
return false;
}
Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
uint32_t heapLength = newBuffer->byteLength();
- if (heapLength & module.heapLengthMask() || heapLength < module.minHeapLength()) {
+ if (heapLength & module.heapLengthMask() ||
+ heapLength < module.minHeapLength() ||
+ heapLength > module.maxHeapLength())
+ {
args.rval().set(BooleanValue(false));
return true;
}
+ if (!module.hasArrayView()) {
+ args.rval().set(BooleanValue(true));
+ return true;
+ }
+
MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
MOZ_ASSERT(!IsDeprecatedAsmJSHeapLength(heapLength));
if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, module.usesSignalHandlersForOOB()))
return false;
args.rval().set(BooleanValue(module.changeHeap(newBuffer, cx)));
return true;
@@ -673,25 +689,24 @@ CallAsmJS(JSContext *cx, unsigned argc,
if (!ToSimdConstant<Float32x4>(cx, v, &simd))
return false;
memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
break;
}
}
}
- // An asm.js module is specialized to its heap's base address and length
- // which is normally immutable except for the neuter operation that occurs
- // when an ArrayBuffer is transfered. Throw an internal error if we're
- // about to run with a neutered heap.
- if (module.maybeHeapBufferObject() &&
- module.maybeHeapBufferObject()->is<ArrayBufferObject>() &&
- module.maybeHeapBufferObject()->as<ArrayBufferObject>().isNeutered())
- {
- js_ReportOverRecursed(cx);
+ // The correct way to handle this situation would be to allocate a new range
+ // of PROT_NONE memory and module.changeHeap to this memory. That would
+ // cause every access to take the out-of-bounds signal-handler path which
+ // does the right thing. For now, just throw an out-of-memory exception
+ // since these can technically pop out anywhere and the full fix may
+ // actually OOM when trying to allocate the PROT_NONE memory.
+ if (module.hasDetachedHeap()) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
return false;
}
{
// Push an AsmJSActivation to describe the asm.js frames we're about to
// push when running this module. Additionally, push a JitActivation so
// that the optimized asm.js-to-Ion FFI call path (which we want to be
// very fast) can avoid doing so. The JitActivation is marked as
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -49,16 +49,46 @@ using namespace js;
using namespace jit;
using namespace frontend;
using mozilla::BinarySearch;
using mozilla::PodCopy;
using mozilla::PodEqual;
using mozilla::Compression::LZ4;
using mozilla::Swap;
+// At any time, the executable code of an asm.js module can be protected (as
+// part of RequestInterruptForAsmJSCode). When we touch the executable outside
+// of executing it (which the AsmJSFaultHandler will correctly handle), we need
+// to guard against this by unprotecting the code (if it has been protected) and
+// preventing it from being protected while we are touching it.
+class AutoUnprotectCode
+{
+ JSRuntime *rt_;
+ JSRuntime::AutoLockForInterrupt lock_;
+ const AsmJSModule &module_;
+ const bool protectedBefore_;
+
+ public:
+ AutoUnprotectCode(JSContext *cx, const AsmJSModule &module)
+ : rt_(cx->runtime()),
+ lock_(rt_),
+ module_(module),
+ protectedBefore_(module_.codeIsProtected(rt_))
+ {
+ if (protectedBefore_)
+ module_.unprotectCode(rt_);
+ }
+
+ ~AutoUnprotectCode()
+ {
+ if (protectedBefore_)
+ module_.protectCode(rt_);
+ }
+};
+
static uint8_t *
AllocateExecutableMemory(ExclusiveContext *cx, size_t bytes)
{
#ifdef XP_WIN
unsigned permissions = PAGE_EXECUTE_READWRITE;
#else
unsigned permissions = PROT_READ | PROT_WRITE | PROT_EXEC;
#endif
@@ -73,26 +103,29 @@ AsmJSModule::AsmJSModule(ScriptSource *s
: srcStart_(srcStart),
srcBodyStart_(srcBodyStart),
scriptSource_(scriptSource),
globalArgumentName_(nullptr),
importArgumentName_(nullptr),
bufferArgumentName_(nullptr),
code_(nullptr),
interruptExit_(nullptr),
+ prevLinked_(nullptr),
+ nextLinked_(nullptr),
dynamicallyLinked_(false),
loadedFromCache_(false),
profilingEnabled_(false),
interrupted_(false),
codeIsProtected_(false)
{
mozilla::PodZero(&pod);
pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
pod.functionBytes_ = UINT32_MAX;
pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
+ pod.maxHeapLength_ = 0x80000000;
pod.strict_ = strict;
pod.usesSignalHandlers_ = canUseSignalHandlers;
scriptSource_->incref();
}
AsmJSModule::~AsmJSModule()
{
@@ -110,16 +143,21 @@ AsmJSModule::~AsmJSModule()
exitDatum.ionScript->removeDependentAsmJSModule(exit);
}
DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
}
for (size_t i = 0; i < numFunctionCounts(); i++)
js_delete(functionCounts(i));
+
+ if (prevLinked_)
+ *prevLinked_ = nextLinked_;
+ if (nextLinked_)
+ nextLinked_->prevLinked_ = prevLinked_;
}
void
AsmJSModule::trace(JSTracer *trc)
{
for (unsigned i = 0; i < globals_.length(); i++)
globals_[i].trace(trc);
for (unsigned i = 0; i < exits_.length(); i++) {
@@ -441,16 +479,24 @@ AsmJSModule::setAutoFlushICacheRange()
static void
AsmJSReportOverRecursed()
{
JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
js_ReportOverRecursed(cx);
}
+static void
+OnDetached()
+{
+ // See hasDetachedHeap comment in LinkAsmJS.
+ JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+}
+
static bool
AsmJSHandleExecutionInterrupt()
{
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
act->module().setInterrupted(true);
bool ret = HandleExecutionInterrupt(act->cx());
act->module().setInterrupted(false);
return ret;
@@ -624,16 +670,18 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
case AsmJSImm_Runtime:
return cx->runtimeAddressForJit();
case AsmJSImm_RuntimeInterrupt:
return cx->runtimeAddressOfInterrupt();
case AsmJSImm_StackLimit:
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
case AsmJSImm_ReportOverRecursed:
return RedirectCall(FuncCast(AsmJSReportOverRecursed), Args_General0);
+ case AsmJSImm_OnDetached:
+ return RedirectCall(FuncCast(OnDetached), Args_General0);
case AsmJSImm_HandleExecutionInterrupt:
return RedirectCall(FuncCast(AsmJSHandleExecutionInterrupt), Args_General0);
case AsmJSImm_InvokeFromAsmJS_Ignore:
return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General3);
case AsmJSImm_InvokeFromAsmJS_ToInt32:
return RedirectCall(FuncCast(InvokeFromAsmJS_ToInt32), Args_General3);
case AsmJSImm_InvokeFromAsmJS_ToNumber:
return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General3);
@@ -831,16 +879,53 @@ AsmJSModule::restoreToInitialState(Array
PatchedImmPtr(originalValue));
}
}
#endif
restoreHeapToInitialState(maybePrevBuffer);
}
+bool
+AsmJSModule::detachHeap(JSContext *cx)
+{
+ MOZ_ASSERT(isDynamicallyLinked());
+ MOZ_ASSERT(maybeHeap_);
+
+ // Content JS should not be able to run (and detach heap) from within an
+ // interrupt callback, but in case it does, fail. Otherwise, the heap can
+ // change at an arbitrary instruction and break the assumption below.
+ if (interrupted_) {
+ JS_ReportError(cx, "attempt to detach from inside interrupt handler");
+ return false;
+ }
+
+ // Even if this->active(), to reach here, the activation must have called
+ // out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
+ // and throw an exception if so.
+ MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
+ activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
+
+ AutoUnprotectCode auc(cx, *this);
+ restoreHeapToInitialState(maybeHeap_);
+
+ MOZ_ASSERT(hasDetachedHeap());
+ return true;
+}
+
+bool
+js::OnDetachAsmJSArrayBuffer(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+ for (AsmJSModule *m = cx->runtime()->linkedAsmJSModules; m; m = m->nextLinked()) {
+ if (buffer == m->maybeHeapBufferObject() && !m->detachHeap(cx))
+ return false;
+ }
+ return true;
+}
+
static void
AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
{
fop->delete_(&obj->as<AsmJSModuleObject>().module());
}
static void
AsmJSModuleObject_trace(JSTracer *trc, JSObject *obj)
@@ -1472,46 +1557,16 @@ AsmJSModule::deserialize(ExclusiveContex
#endif
(cursor = staticLinkData_.deserialize(cx, cursor));
loadedFromCache_ = true;
return cursor;
}
-// At any time, the executable code of an asm.js module can be protected (as
-// part of RequestInterruptForAsmJSCode). When we touch the executable outside
-// of executing it (which the AsmJSFaultHandler will correctly handle), we need
-// to guard against this by unprotecting the code (if it has been protected) and
-// preventing it from being protected while we are touching it.
-class AutoUnprotectCode
-{
- JSRuntime *rt_;
- JSRuntime::AutoLockForInterrupt lock_;
- const AsmJSModule &module_;
- const bool protectedBefore_;
-
- public:
- AutoUnprotectCode(JSContext *cx, const AsmJSModule &module)
- : rt_(cx->runtime()),
- lock_(rt_),
- module_(module),
- protectedBefore_(module_.codeIsProtected(rt_))
- {
- if (protectedBefore_)
- module_.unprotectCode(rt_);
- }
-
- ~AutoUnprotectCode()
- {
- if (protectedBefore_)
- module_.protectCode(rt_);
- }
-};
-
bool
AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
{
AutoUnprotectCode auc(cx, *this);
*moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_,
pod.usesSignalHandlers_);
if (!*moduleOut)
@@ -1556,16 +1611,18 @@ AsmJSModule::clone(JSContext *cx, Scoped
out.restoreToInitialState(maybeHeap_, code_, cx);
return true;
}
bool
AsmJSModule::changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext *cx)
{
+ MOZ_ASSERT(hasArrayView());
+
// Content JS should not be able to run (and change heap) from within an
// interrupt callback, but in case it does, fail to change heap. Otherwise,
// the heap can change at every single instruction which would prevent
// future optimizations like heap-base hoisting.
if (interrupted_)
return false;
AutoUnprotectCode auc(cx, *this);
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -769,16 +769,17 @@ class AsmJSModule
private:
struct Pod {
size_t funcPtrTableAndExitBytes_;
size_t functionBytes_; // just the function bodies, no stubs
size_t codeBytes_; // function bodies and stubs
size_t totalBytes_; // function bodies, stubs, and global data
uint32_t minHeapLength_;
+ uint32_t maxHeapLength_;
uint32_t heapLengthMask_;
uint32_t numGlobalScalarVars_;
uint32_t numGlobalSimdVars_;
uint32_t numFFIs_;
uint32_t srcLength_;
uint32_t srcLengthWithRightBrace_;
bool strict_;
bool hasArrayView_;
@@ -813,25 +814,31 @@ class AsmJSModule
ScriptSource * scriptSource_;
PropertyName * globalArgumentName_;
PropertyName * importArgumentName_;
PropertyName * bufferArgumentName_;
uint8_t * code_;
uint8_t * interruptExit_;
StaticLinkData staticLinkData_;
HeapPtrArrayBufferObjectMaybeShared maybeHeap_;
+ AsmJSModule ** prevLinked_;
+ AsmJSModule * nextLinked_;
bool dynamicallyLinked_;
bool loadedFromCache_;
bool profilingEnabled_;
bool interrupted_;
// This field is accessed concurrently when requesting an interrupt.
// Access must be synchronized via the runtime's interrupt lock.
mutable bool codeIsProtected_;
+ void restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer);
+ void restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer, uint8_t *prevCode,
+ ExclusiveContext *cx);
+
public:
explicit AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
bool strict, bool canUseSignalHandlers);
void trace(JSTracer *trc);
~AsmJSModule();
// An AsmJSModule transitions monotonically through these states:
bool isFinishedWithModulePrologue() const { return pod.funcPtrTableAndExitBytes_ != SIZE_MAX; }
@@ -879,16 +886,19 @@ class AsmJSModule
return srcBodyStart_;
}
// While these functions may be accessed at any time, their values will
// change as the module is compiled.
uint32_t minHeapLength() const {
return pod.minHeapLength_;
}
+ uint32_t maxHeapLength() const {
+ return pod.maxHeapLength_;
+ }
uint32_t heapLengthMask() const {
MOZ_ASSERT(pod.hasFixedMinHeapLength_);
return pod.heapLengthMask_;
}
unsigned numFunctionCounts() const {
return functionCounts_.length();
}
jit::IonScriptCounts *functionCounts(unsigned i) {
@@ -1039,29 +1049,38 @@ class AsmJSModule
MOZ_ASSERT(!isFinishedWithModulePrologue());
pod.funcPtrTableAndExitBytes_ = 0;
MOZ_ASSERT(isFinishedWithModulePrologue());
}
/*************************************************************************/
// These functions are called while parsing/compiling function bodies:
- void addChangeHeap(uint32_t mask, uint32_t min) {
+ bool hasArrayView() const {
+ MOZ_ASSERT(isFinishedWithModulePrologue());
+ return pod.hasArrayView_;
+ }
+ void addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
MOZ_ASSERT(isFinishedWithModulePrologue());
MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
+ MOZ_ASSERT(max <= pod.maxHeapLength_);
+ MOZ_ASSERT(min <= max);
pod.heapLengthMask_ = mask;
pod.minHeapLength_ = min;
+ pod.maxHeapLength_ = max;
pod.hasFixedMinHeapLength_ = true;
}
bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
return false;
+ if (len > pod.maxHeapLength_)
+ return false;
len = RoundUpToNextValidAsmJSHeapLength(len);
if (len > pod.minHeapLength_)
pod.minHeapLength_ = len;
return true;
}
bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t end) {
return codeRanges_.append(CodeRange(kind, begin, end));
}
@@ -1223,20 +1242,16 @@ class AsmJSModule
bool finish(ExclusiveContext *cx,
frontend::TokenStream &tokenStream,
jit::MacroAssembler &masm,
const jit::Label &interruptLabel);
/*************************************************************************/
// These accessor functions can be used after finish():
- bool hasArrayView() const {
- MOZ_ASSERT(isFinished());
- return pod.hasArrayView_;
- }
unsigned numFFIs() const {
MOZ_ASSERT(isFinished());
return pod.numFFIs_;
}
uint32_t srcEndBeforeCurly() const {
MOZ_ASSERT(isFinished());
return srcStart_ + pod.srcLength_;
}
@@ -1435,33 +1450,47 @@ class AsmJSModule
// linked" which specializes the code to its current address (this allows
// code to be relocated between serialization and deserialization).
void staticallyLink(ExclusiveContext *cx);
// After a module is statically linked, it is "dynamically linked" which
// specializes it to a particular set of arguments. In particular, this
// binds the code to a particular heap (via initHeap) and set of global
// variables. A given asm.js module cannot be dynamically linked more than
- // once so, if JS tries, the module is cloned.
- void setIsDynamicallyLinked() {
+ // once so, if JS tries, the module is cloned. When linked, an asm.js module
+ // is kept in a list so that it can be updated if the linked buffer is
+ // detached.
+ void setIsDynamicallyLinked(JSRuntime *rt) {
MOZ_ASSERT(!isDynamicallyLinked());
dynamicallyLinked_ = true;
+ nextLinked_ = rt->linkedAsmJSModules;
+ prevLinked_ = &rt->linkedAsmJSModules;
+ if (nextLinked_)
+ nextLinked_->prevLinked_ = &nextLinked_;
+ rt->linkedAsmJSModules = this;
MOZ_ASSERT(isDynamicallyLinked());
}
+
void initHeap(Handle<ArrayBufferObjectMaybeShared*> heap, JSContext *cx);
- void restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer);
- void restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer,
- uint8_t *prevCode,
- ExclusiveContext *cx);
+ bool changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext *cx);
+ bool detachHeap(JSContext *cx);
+
bool clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const;
- bool changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext *cx);
/*************************************************************************/
// Functions that can be called after dynamic linking succeeds:
+ AsmJSModule *nextLinked() const {
+ MOZ_ASSERT(isDynamicallyLinked());
+ return nextLinked_;
+ }
+ bool hasDetachedHeap() const {
+ MOZ_ASSERT(isDynamicallyLinked());
+ return hasArrayView() && !heapDatum();
+ }
CodePtr entryTrampoline(const ExportedFunction &func) const {
MOZ_ASSERT(isDynamicallyLinked());
MOZ_ASSERT(!func.isChangeHeap());
return JS_DATA_TO_FUNC_PTR(CodePtr, code_ + func.pod.codeOffset_);
}
uint8_t *interruptExit() const {
MOZ_ASSERT(isDynamicallyLinked());
return interruptExit_;
@@ -1506,16 +1535,20 @@ StoreAsmJSModuleInCache(AsmJSParser &par
// return value indicates whether or not an error was encountered, not whether
// there was a cache hit.
extern bool
LookupAsmJSModuleInCache(ExclusiveContext *cx,
AsmJSParser &parser,
ScopedJSDeletePtr<AsmJSModule> *module,
ScopedJSFreePtr<char> *compilationTimeReport);
+// This function must be called for every detached ArrayBuffer.
+extern bool
+OnDetachAsmJSArrayBuffer(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+
// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
// directly to user script) which manages the lifetime of an AsmJSModule. A
// JSObject is necessary since we want LinkAsmJS/CallAsmJS JSFunctions to be
// able to point to their module via their extended slots.
class AsmJSModuleObject : public NativeObject
{
static const unsigned MODULE_SLOT = 0;
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1324,16 +1324,17 @@ class MOZ_STACK_CLASS ModuleCompiler
FuncPtrTableVector funcPtrTables_;
ArrayViewVector arrayViews_;
ExitMap exits_;
MathNameMap standardLibraryMathNames_;
SimdOperationNameMap standardLibrarySimdOpNames_;
NonAssertingLabel stackOverflowLabel_;
NonAssertingLabel asyncInterruptLabel_;
NonAssertingLabel syncInterruptLabel_;
+ NonAssertingLabel onDetachedLabel_;
UniquePtr<char[], JS::FreePolicy> errorString_;
uint32_t errorOffset_;
bool errorOverRecursed_;
int64_t usecBefore_;
SlowFunctionVector slowFunctions_;
@@ -1517,16 +1518,17 @@ class MOZ_STACK_CLASS ModuleCompiler
ExclusiveContext *cx() const { return cx_; }
AsmJSParser &parser() const { return parser_; }
TokenStream &tokenStream() const { return parser_.tokenStream; }
MacroAssembler &masm() { return masm_; }
Label &stackOverflowLabel() { return stackOverflowLabel_; }
Label &asyncInterruptLabel() { return asyncInterruptLabel_; }
Label &syncInterruptLabel() { return syncInterruptLabel_; }
+ Label &onDetachedLabel() { return onDetachedLabel_; }
bool hasError() const { return errorString_ != nullptr; }
const AsmJSModule &module() const { return *module_.get(); }
bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
bool supportsSimd() const { return supportsSimd_; }
ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; }
PropertyName *moduleFunctionName() const { return moduleFunctionName_; }
@@ -1653,19 +1655,19 @@ class MOZ_STACK_CLASS ModuleCompiler
}
bool addByteLength(PropertyName *name) {
canValidateChangeHeap_ = true;
if (!module_->addByteLength())
return false;
Global *global = moduleLifo_.new_<Global>(Global::ByteLength);
return global && globals_.putNew(name, global);
}
- bool addChangeHeap(PropertyName *name, ParseNode *fn, uint32_t mask, uint32_t min) {
+ bool addChangeHeap(PropertyName *name, ParseNode *fn, uint32_t mask, uint32_t min, uint32_t max) {
hasChangeHeap_ = true;
- module_->addChangeHeap(mask, min);
+ module_->addChangeHeap(mask, min, max);
Global *global = moduleLifo_.new_<Global>(Global::ChangeHeap);
if (!global)
return false;
global->u.changeHeap.srcBegin_ = fn->pn_pos.begin;
global->u.changeHeap.srcEnd_ = fn->pn_pos.end;
return globals_.putNew(name, global);
}
bool addFFI(PropertyName *varName, PropertyName *field) {
@@ -4159,18 +4161,19 @@ CheckArrayAccess(FunctionCompiler &f, Pa
uint32_t index;
if (IsLiteralOrConstInt(f, indexExpr, &index)) {
uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
if (byteOffset > INT32_MAX)
return f.fail(indexExpr, "constant index out of range");
unsigned elementSize = 1 << TypedArrayShift(*viewType);
if (!f.m().tryRequireHeapLengthToBeAtLeast(byteOffset + elementSize)) {
- return f.failf(indexExpr, "constant index outside heap size declared by the "
- "change-heap function (0x%x)", f.m().minHeapLength());
+ return f.failf(indexExpr, "constant index outside heap size range declared by the "
+ "change-heap function (0x%x - 0x%x)",
+ f.m().minHeapLength(), f.m().module().maxHeapLength());
}
*needsBoundsCheck = NO_BOUNDS_CHECK;
*def = f.constant(Int32Value(byteOffset), Type::Int);
return true;
}
// Mask off the low bits to account for the clearing effect of a right shift
@@ -6540,51 +6543,68 @@ CheckByteLengthCall(ModuleCompiler &m, P
if (CallArgListLength(pn) != 1 || !IsUseOfName(CallArgList(pn), newBufferName))
return m.failName(pn, "expecting %s as argument to byteLength call", newBufferName);
return true;
}
static bool
CheckHeapLengthCondition(ModuleCompiler &m, ParseNode *cond, PropertyName *newBufferName,
- uint32_t *mask, uint32_t *minimumLength)
-{
- if (!cond->isKind(PNK_OR))
- return m.fail(cond, "expecting byteLength & K || byteLength <= L");
-
- ParseNode *leftCond = BinaryLeft(cond);
- ParseNode *rightCond = BinaryRight(cond);
-
- if (!leftCond->isKind(PNK_BITAND))
- return m.fail(leftCond, "expecting byteLength & K");
-
- if (!CheckByteLengthCall(m, BinaryLeft(leftCond), newBufferName))
- return false;
-
- ParseNode *maskNode = BinaryRight(leftCond);
+ uint32_t *mask, uint32_t *minLength, uint32_t *maxLength)
+{
+ if (!cond->isKind(PNK_OR) || !BinaryLeft(cond)->isKind(PNK_OR))
+ return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
+
+ ParseNode *cond1 = BinaryLeft(BinaryLeft(cond));
+ ParseNode *cond2 = BinaryRight(BinaryLeft(cond));
+ ParseNode *cond3 = BinaryRight(cond);
+
+ if (!cond1->isKind(PNK_BITAND))
+ return m.fail(cond1, "expecting byteLength & K");
+
+ if (!CheckByteLengthCall(m, BinaryLeft(cond1), newBufferName))
+ return false;
+
+ ParseNode *maskNode = BinaryRight(cond1);
if (!IsLiteralInt(m, maskNode, mask))
return m.fail(maskNode, "expecting integer literal mask");
if ((*mask & 0xffffff) != 0xffffff)
return m.fail(maskNode, "mask value must have the bits 0xffffff set");
- if (!rightCond->isKind(PNK_LE))
- return m.fail(rightCond, "expecting byteLength <= L");
-
- if (!CheckByteLengthCall(m, BinaryLeft(rightCond), newBufferName))
- return false;
-
- ParseNode *minLengthNode = BinaryRight(rightCond);
+ if (!cond2->isKind(PNK_LE))
+ return m.fail(cond2, "expecting byteLength <= L");
+
+ if (!CheckByteLengthCall(m, BinaryLeft(cond2), newBufferName))
+ return false;
+
+ ParseNode *minLengthNode = BinaryRight(cond2);
uint32_t minLengthExclusive;
if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
- return m.fail(minLengthNode, "expecting integer limit literal");
+ return m.fail(minLengthNode, "expecting integer literal");
if (minLengthExclusive < 0xffffff)
- return m.fail(minLengthNode, "limit value must be >= 0xffffff");
+ return m.fail(minLengthNode, "literal must be >= 0xffffff");
// Add one to convert from exclusive (the branch rejects if ==) to inclusive.
- *minimumLength = minLengthExclusive + 1;
+ *minLength = minLengthExclusive + 1;
+
+ if (!cond3->isKind(PNK_GT))
+ return m.fail(cond3, "expecting byteLength > M");
+
+ if (!CheckByteLengthCall(m, BinaryLeft(cond3), newBufferName))
+ return false;
+
+ ParseNode *maxLengthNode = BinaryRight(cond3);
+ if (!IsLiteralInt(m, maxLengthNode, maxLength))
+ return m.fail(maxLengthNode, "expecting integer literal");
+ if (*maxLength > 0x80000000)
+ return m.fail(maxLengthNode, "literal must be <= 0x80000000");
+
+ if (*maxLength < *minLength)
+ return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
+
return true;
}
static bool
CheckReturnBoolLiteral(ModuleCompiler &m, ParseNode *stmt, bool retval)
{
if (!stmt)
return m.fail(stmt, "expected return statement");
@@ -6660,18 +6680,18 @@ CheckChangeHeap(ModuleCompiler &m, Parse
if (!bufferName)
return m.fail(fn, "to change heaps, the module must have a buffer argument");
ParseNode *cond = TernaryKid1(stmtIter);
ParseNode *thenStmt = TernaryKid2(stmtIter);
if (ParseNode *elseStmt = TernaryKid3(stmtIter))
return m.fail(elseStmt, "unexpected else statement");
- uint32_t mask, min;
- if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min))
+ uint32_t mask, min, max;
+ if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min, &max))
return false;
if (!CheckReturnBoolLiteral(m, thenStmt, false))
return false;
stmtIter = NextNonEmptyStatement(stmtIter);
for (unsigned i = 0; i < m.numArrayViews(); i++, stmtIter = NextNonEmptyStatement(stmtIter)) {
@@ -6708,17 +6728,17 @@ CheckChangeHeap(ModuleCompiler &m, Parse
if (!CheckReturnBoolLiteral(m, stmtIter, true))
return false;
stmtIter = NextNonEmptyStatement(stmtIter);
if (stmtIter)
return m.fail(stmtIter, "expecting end of function");
- return m.addChangeHeap(changeHeapName, fn, mask, min);
+ return m.addChangeHeap(changeHeapName, fn, mask, min, max);
}
static bool
ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
{
TokenStream &tokenStream = m.tokenStream();
DebugOnly<TokenKind> tk = tokenStream.getToken();
@@ -7615,16 +7635,37 @@ FillArgumentArray(ModuleCompiler &m, con
masm.canonicalizeDouble(ScratchDoubleReg);
masm.storeDouble(ScratchDoubleReg, dstAddr);
}
break;
}
}
}
+// If an FFI detaches its heap (viz., via ArrayBuffer.transfer), it must
+// call change-heap to another heap (viz., the new heap returned by transfer)
+// before returning to asm.js code. If the application fails to do this (if the
+// heap pointer is null), jump to a stub.
+static void
+GenerateCheckForHeapDetachment(ModuleCompiler &m, Register scratch)
+{
+ if (!m.module().hasArrayView())
+ return;
+
+ MacroAssembler &masm = m.masm();
+ AssertStackAlignment(masm, ABIStackAlignment);
+#if defined(JS_CODEGEN_X86)
+ CodeOffsetLabel label = masm.movlWithPatch(PatchedAbsoluteAddress(), scratch);
+ masm.append(AsmJSGlobalAccess(label, AsmJSHeapGlobalDataOffset));
+ masm.branchTestPtr(Assembler::Zero, scratch, scratch, &m.onDetachedLabel());
+#else
+ masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, &m.onDetachedLabel());
+#endif
+}
+
static bool
GenerateFFIInterpExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
unsigned exitIndex, Label *throwLabel)
{
MacroAssembler &masm = m.masm();
MOZ_ASSERT(masm.framePushed() == 0);
// Argument types for InvokeFromAsmJS_*:
@@ -7698,18 +7739,20 @@ GenerateFFIInterpExit(ModuleCompiler &m,
break;
case RetType::Float:
MOZ_CRASH("Float32 shouldn't be returned from a FFI");
case RetType::Int32x4:
case RetType::Float32x4:
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}
- // The heap pointer may have changed during the FFI, so reload it.
+ // The heap pointer may have changed during the FFI, so reload it and test
+ // for detachment.
masm.loadAsmJSHeapRegisterFromGlobalData();
+ GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
Label profilingReturn;
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::SlowFFI, &profilingReturn);
return m.finishGeneratingInterpExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
}
// On ARM/MIPS, we need to include an extra word of space at the top of the
// stack so we can explicitly store the return address before making the call
@@ -7908,24 +7951,28 @@ GenerateFFIIonExit(ModuleCompiler &m, co
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}
Label done;
masm.bind(&done);
MOZ_ASSERT(masm.framePushed() == framePushed);
- // Reload pinned registers after all calls into arbitrary JS.
+ // Reload the global register since Ion code can clobber any register.
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
JS_STATIC_ASSERT(MaybeSavedGlobalReg > 0);
masm.loadPtr(Address(StackPointer, savedGlobalOffset), GlobalReg);
#else
JS_STATIC_ASSERT(MaybeSavedGlobalReg == 0);
#endif
+
+ // The heap pointer has to be reloaded anyway since Ion could have clobbered
+ // it. Additionally, the FFI may have detached the heap buffer.
masm.loadAsmJSHeapRegisterFromGlobalData();
+ GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
Label profilingReturn;
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn);
if (oolConvert.used()) {
masm.bind(&oolConvert);
masm.setFramePushed(framePushed);
@@ -8076,16 +8123,30 @@ GenerateBuiltinThunk(ModuleCompiler &m,
static bool
GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
{
MacroAssembler &masm = m.masm();
GenerateAsmJSStackOverflowExit(masm, &m.stackOverflowLabel(), throwLabel);
return m.finishGeneratingInlineStub(&m.stackOverflowLabel()) && !masm.oom();
}
+static bool
+GenerateOnDetachedLabelExit(ModuleCompiler &m, Label *throwLabel)
+{
+ MacroAssembler &masm = m.masm();
+ masm.bind(&m.onDetachedLabel());
+ masm.assertStackAlignment(ABIStackAlignment);
+
+ // For now, OnDetached always throws (see OnDetached comment).
+ masm.call(AsmJSImmPtr(AsmJSImm_OnDetached));
+ masm.jump(throwLabel);
+
+ return m.finishGeneratingInlineStub(&m.onDetachedLabel()) && !masm.oom();
+}
+
static const RegisterSet AllRegsExceptSP =
RegisterSet(GeneralRegisterSet(Registers::AllMask &
~(uint32_t(1) << Registers::StackPointer)),
FloatRegisterSet(FloatRegisters::AllDoubleMask));
// The async interrupt-callback exit is called from arbitrarily-interrupted asm.js
// code. That means we must first save *all* registers and restore *all*
// registers (except the stack pointer) when we resume. The address to resume to
@@ -8129,17 +8190,16 @@ GenerateAsyncInterruptExit(ModuleCompile
masm.branchIfFalseBool(ReturnReg, throwLabel);
// Restore the StackPointer to it's position before the call.
masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer);
// Restore the machine state to before the interrupt.
masm.PopRegsInMask(AllRegsExceptSP, AllRegsExceptSP.fpus()); // restore all GP/FP registers (except SP)
- masm.loadAsmJSHeapRegisterFromGlobalData(); // In case there was a changeHeap
masm.popFlags(); // after this, nothing that sets conditions
masm.ret(); // pop resumePC into PC
#elif defined(JS_CODEGEN_MIPS)
// Reserve space to store resumePC.
masm.subPtr(Imm32(sizeof(intptr_t)), StackPointer);
// set to zero so we can use masm.framePushed() below.
masm.setFramePushed(0);
// When this platform supports SIMD extensions, we'll need to push high lanes
@@ -8171,17 +8231,16 @@ GenerateAsyncInterruptExit(ModuleCompile
// This will restore stack to the address before the call.
masm.movePtr(s0, StackPointer);
masm.PopRegsInMask(AllRegsExceptSP);
// Pop resumePC into PC. Clobber HeapReg to make the jump and restore it
// during jump delay slot.
masm.pop(HeapReg);
masm.as_jr(HeapReg);
- masm.loadAsmJSHeapRegisterFromGlobalData(); // In case there was a changeHeap
#elif defined(JS_CODEGEN_ARM)
masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below
masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(Registers::AllMask & ~(1<<Registers::sp)), FloatRegisterSet(uint32_t(0)))); // save all GP registers,excep sp
// Save both the APSR and FPSCR in non-volatile registers.
masm.as_mrs(r4);
masm.as_vmrs(r5);
// Save the stack pointer in a non-volatile register.
@@ -8221,17 +8280,16 @@ GenerateAsyncInterruptExit(ModuleCompile
masm.transferReg(r7);
masm.transferReg(r8);
masm.transferReg(r9);
masm.transferReg(r10);
masm.transferReg(r11);
masm.transferReg(r12);
masm.transferReg(lr);
masm.finishDataTransfer();
- masm.loadAsmJSHeapRegisterFromGlobalData(); // In case there was a changeHeap
masm.ret();
#elif defined (JS_CODEGEN_NONE)
MOZ_CRASH();
#else
# error "Unknown architecture!"
#endif
@@ -8247,19 +8305,16 @@ GenerateSyncInterruptExit(ModuleCompiler
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Interrupt, &m.syncInterruptLabel());
AssertStackAlignment(masm, ABIStackAlignment);
masm.call(AsmJSImmPtr(AsmJSImm_HandleExecutionInterrupt));
masm.branchIfFalseBool(ReturnReg, throwLabel);
- // Reload the heap register in case the callback changed heaps.
- masm.loadAsmJSHeapRegisterFromGlobalData();
-
Label profilingReturn;
GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Interrupt, &profilingReturn);
return m.finishGeneratingInterrupt(&m.syncInterruptLabel(), &profilingReturn) && !masm.oom();
}
// If an exception is thrown, simply pop all frames (since asm.js does not
// contain try/catch). To do this:
// 1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
@@ -8306,16 +8361,19 @@ GenerateStubs(ModuleCompiler &m)
for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) {
if (!GenerateFFIExits(m, r.front().key(), r.front().value(), &throwLabel))
return false;
}
if (m.stackOverflowLabel().used() && !GenerateStackOverflowExit(m, &throwLabel))
return false;
+ if (m.onDetachedLabel().used() && !GenerateOnDetachedLabelExit(m, &throwLabel))
+ return false;
+
if (!GenerateAsyncInterruptExit(m, &throwLabel))
return false;
if (m.syncInterruptLabel().used() && !GenerateSyncInterruptExit(m, &throwLabel))
return false;
if (!GenerateThrowStub(m, &throwLabel))
return false;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -169,17 +169,16 @@ AC_SUBST(JS_SHARED_LIBRARY)
if test "$JS_STANDALONE" = no; then
autoconfmk=autoconf-js.mk
JS_STANDALONE=
else
JS_STANDALONE=1
LIBXUL_DIST="$MOZ_BUILD_ROOT/dist"
AC_DEFINE(JS_STANDALONE)
fi
-
AC_SUBST(JS_STANDALONE)
BUILDING_JS=1
AC_SUBST(autoconfmk)
MOZ_ARG_WITH_STRING(gonk,
[ --with-gonk=DIR
location of gonk dir],
gonkdir=$withval)
--- a/js/src/doc/Debugger/Debugger.md
+++ b/js/src/doc/Debugger/Debugger.md
@@ -373,16 +373,42 @@ other kinds of objects.
returns all debuggee code scripts.
Note that the result may include [`Debugger.Script`][script] instances for
scripts that can no longer ever be used by the debuggee, say, those for
eval code that has finished running, or unreachable functions. Whether
such scripts appear can be affected by the garbage collector's
behavior, so this function's behavior is not entirely deterministic.
+<code>findObjects([<i>query</i>])</code>
+: Return an array of [`Debugger.Object`][object] instances referring to each
+ live object allocated in the scope of the debuggee globals that matches
+ *query*. Each instance appears only once in the array. *Query* is an object
+ whose properties restrict which objects are returned; an object must meet
+ all the criteria given by *query* to be returned. If *query* is omitted, we
+ return the [`Debugger.Object`][object] instances for all objects allocated
+ in the scope of debuggee globals.
+
+ The *query* object may have the following properties:
+
+ `class`
+ : If present, only return objects whose internal `[[Class]]`'s name
+ matches the given string. Note that in some cases, the prototype object
+ for a given constructor has the same `[[Class]]` as the instances that
+ refer to it, but cannot itself be used as a valid instance of the
+ class. Code gathering objects by class name may need to examine them
+ further before trying to use them.
+
+ All properties of *query* are optional. Passing an empty object returns all
+ objects in debuggee globals.
+
+ Unlike `findScripts`, this function is deterministic and will never return
+ [`Debugger.Object`s][object] referring to previously unreachable objects
+ that had not been collected yet.
+
<code>clearBreakpoint(<i>handler</i>)</code>
: Remove all breakpoints set in this `Debugger` instance that use
<i>handler</i> as their handler. Note that, if breakpoints using other
handler objects are set at the same location(s) as <i>handler</i>, they
remain in place.
`clearAllBreakpoints()`
: Remove all breakpoints set using this `Debugger` instance.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1249,16 +1249,44 @@ ConvertDefinitionToNamedLambdaUse(TokenS
* setter can either ignore the set (in non-strict mode) or
* produce an error (in strict mode).
*/
if (dn->isClosed() || dn->isAssigned())
funbox->setNeedsDeclEnvObject();
return true;
}
+static bool
+IsNonDominatingInScopedSwitch(ParseContext<FullParseHandler> *pc, HandleAtom name,
+ Definition *dn)
+{
+ MOZ_ASSERT(dn->isLet());
+ StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr);
+ if (stmt && stmt->type == STMT_SWITCH)
+ return dn->pn_cookie.slot() < stmt->firstDominatingLexicalInCase;
+ return false;
+}
+
+static void
+AssociateUsesWithOuterDefinition(ParseNode *pnu, Definition *dn, Definition *outer_dn,
+ bool markUsesAsLet)
+{
+ uint32_t dflags = markUsesAsLet ? PND_LET : 0;
+ while (true) {
+ pnu->pn_lexdef = outer_dn;
+ pnu->pn_dflags |= dflags;
+ if (!pnu->pn_link)
+ break;
+ pnu = pnu->pn_link;
+ }
+ pnu->pn_link = outer_dn->dn_uses;
+ outer_dn->dn_uses = dn->dn_uses;
+ dn->dn_uses = nullptr;
+}
+
/*
* Beware: this function is called for functions nested in other functions or
* global scripts but not for functions compiled through the Function
* constructor or JSAPI. To always execute code when a function has finished
* parsing, use Parser::functionBody.
*/
template <>
bool
@@ -1333,45 +1361,35 @@ Parser<FullParseHandler>::leaveFunction(
* (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME
* case), and nowhere else, currently.
*/
if (dn != outer_dn) {
if (ParseNode *pnu = dn->dn_uses) {
// In ES6, lexical bindings cannot be accessed until
// initialized. If we are parsing a function with a
// hoisted body-level use, all free variables that get
- // linked to an outer 'let' binding need to be marked as
+ // linked to an outer lexical binding need to be marked as
// needing dead zone checks. e.g.,
//
// function outer() {
// inner();
// function inner() { use(x); }
// let x;
// }
//
// The use of 'x' inside 'inner' needs to be marked.
- if (bodyLevelHoistedUse && outer_dn->isLet()) {
- while (true) {
- pnu->pn_dflags |= PND_LET;
- if (!pnu->pn_link)
- break;
- pnu = pnu->pn_link;
- }
- pnu = dn->dn_uses;
- }
-
- while (true) {
- pnu->pn_lexdef = outer_dn;
- if (!pnu->pn_link)
- break;
- pnu = pnu->pn_link;
- }
- pnu->pn_link = outer_dn->dn_uses;
- outer_dn->dn_uses = dn->dn_uses;
- dn->dn_uses = nullptr;
+ //
+ // Similarly, if we are closing over a lexical binding
+ // from another case in a switch, those uses also need to
+ // be marked as needing dead zone checks.
+ RootedAtom name(context, atom);
+ bool markUsesAsLet = outer_dn->isLet() &&
+ (bodyLevelHoistedUse ||
+ IsNonDominatingInScopedSwitch(outerpc, name, outer_dn));
+ AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLet);
}
outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
}
/* Mark the outer dn as escaping. */
outer_dn->pn_dflags |= PND_CLOSED;
}
@@ -1867,16 +1885,23 @@ Parser<ParseHandler>::addFreeVariablesFr
// In ES6, lexical bindings are unaccessible before initialization. If
// the inner function closes over a placeholder definition, we need to
// mark the variable as maybe needing a dead zone check when we emit
// bytecode.
//
// Note that body-level function declaration statements are always
// hoisted to the top, so all accesses to free let variables need the
// dead zone check.
+ //
+ // Subtlety: we don't need to check for closing over a non-dominating
+ // lexical binding in a switch, as lexical declarations currently
+ // disable syntax parsing. So a non-dominating but textually preceding
+ // lexical declaration would have aborted syntax parsing, and a
+ // textually following declaration would return true for
+ // handler.isPlaceholderDefinition(dn) below.
if (handler.isPlaceholderDefinition(dn) || bodyLevelHoistedUse)
freeVariables[i].setIsHoistedUse();
/* Mark the outer dn as escaping. */
handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
}
PropagateTransitiveParseFlags(lazy, pc->sc);
@@ -3137,17 +3162,17 @@ Parser<ParseHandler>::noteNameUse(Handle
handler.linkUseToDef(pn, dn);
if (stmt) {
if (stmt->type == STMT_WITH) {
handler.setFlag(pn, PND_DEOPTIMIZED);
} else if (stmt->type == STMT_SWITCH && stmt->isBlockScope) {
// See comments above StmtInfoPC and switchStatement for how
- // firstDominatingLetInCase is computed.
+ // firstDominatingLexicalInCase is computed.
MOZ_ASSERT(stmt->firstDominatingLexicalInCase <= stmt->staticBlock().numVariables());
handler.markMaybeUninitializedLexicalUseInSwitch(pn, dn,
stmt->firstDominatingLexicalInCase);
}
}
return true;
}
--- a/js/src/jit-test/tests/asm.js/testNeuter.js
+++ b/js/src/jit-test/tests/asm.js/testNeuter.js
@@ -1,81 +1,113 @@
load(libdir + "asm.js");
+load(libdir + "asserts.js");
+
+if (!isAsmJSCompilationAvailable())
+ quit();
-function f(stdlib, foreign, buffer) {
- "use asm";
- var i32 = new stdlib.Int32Array(buffer);
- function set(i,j) {
- i=i|0;
- j=j|0;
- i32[i>>2] = j;
- }
- function get(i) {
- i=i|0;
- return i32[i>>2]|0
- }
- return {get:get, set:set}
-}
-if (isAsmJSCompilationAvailable())
- assertEq(isAsmJSModule(f), true);
+var m = asmCompile('stdlib', 'foreign', 'buffer',
+ `"use asm";
+ var i32 = new stdlib.Int32Array(buffer);
+ function set(i,j) {
+ i=i|0;
+ j=j|0;
+ i32[i>>2] = j;
+ }
+ function get(i) {
+ i=i|0;
+ return i32[i>>2]|0
+ }
+ return {get:get, set:set}`);
-var i32 = new Int32Array(1024);
-var buffer = i32.buffer;
-var {get, set} = f(this, null, buffer);
-if (isAsmJSCompilationAvailable())
- assertEq(isAsmJSFunction(get) && isAsmJSFunction(set), true);
-
+var buffer = new ArrayBuffer(BUF_MIN);
+var {get, set} = asmLink(m, this, null, buffer);
set(4, 42);
assertEq(get(4), 42);
-
neuter(buffer, "change-data");
neuter(buffer, "same-data");
+assertThrowsInstanceOf(() => get(4), InternalError);
-// These operations may throw internal errors
-try {
- assertEq(get(4), 0);
- set(0, 42);
- assertEq(get(0), 0);
-} catch (e) {
- assertEq(String(e).indexOf("InternalError"), 0);
-}
+var buf1 = new ArrayBuffer(BUF_MIN);
+var buf2 = new ArrayBuffer(BUF_MIN);
+var {get:get1, set:set1} = asmLink(m, this, null, buf1);
+var {get:get2, set:set2} = asmLink(m, this, null, buf2);
+set1(0, 13);
+set2(0, 42);
+neuter(buf1, "change-data");
+assertThrowsInstanceOf(() => get1(0), InternalError);
+assertEq(get2(0), 42);
-function f2(stdlib, foreign, buffer) {
- "use asm";
- var i32 = new stdlib.Int32Array(buffer);
- var ffi = foreign.ffi;
- function inner(i) {
- i=i|0;
- ffi();
- return i32[i>>2]|0
- }
- return inner
-}
-if (isAsmJSCompilationAvailable())
- assertEq(isAsmJSModule(f2), true);
+var m = asmCompile('stdlib', 'foreign', 'buffer',
+ `"use asm";
+ var i32 = new stdlib.Int32Array(buffer);
+ var ffi = foreign.ffi;
+ function inner(i) {
+ i=i|0;
+ ffi();
+ return i32[i>>2]|0
+ }
+ return inner`);
+
+var buffer = new ArrayBuffer(BUF_MIN);
+function ffi1() { neuter(buffer, "change-data"); }
+var inner = asmLink(m, this, {ffi:ffi1}, buffer);
+assertThrowsInstanceOf(() => inner(8), InternalError);
-var i32 = new Int32Array(1024);
-var buffer = i32.buffer;
-var threw = false;
-function ffi() {
- try {
- neuter(buffer, "same-data");
- } catch (e) {
- assertEq(String(e).indexOf("InternalError"), 0);
- threw = true;
- }
- try {
- neuter(buffer, "change-data");
- } catch (e) {
- assertEq(String(e).indexOf("InternalError"), 0);
- threw = true;
- }
+var byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
+var m = asmCompile('stdlib', 'foreign', 'buffer',
+ `"use asm";
+ var ffi = foreign.ffi;
+ var I32 = stdlib.Int32Array;
+ var i32 = new I32(buffer);
+ var len = stdlib.byteLength;
+ function changeHeap(newBuffer) {
+ if (len(newBuffer) & 0xffffff || len(newBuffer) <= 0xffffff || len(newBuffer) > 0x80000000)
+ return false;
+ i32 = new I32(newBuffer);
+ buffer = newBuffer;
+ return true;
+ }
+ function get(i) {
+ i=i|0;
+ return i32[i>>2]|0;
+ }
+ function inner(i) {
+ i=i|0;
+ ffi();
+ return get(i)|0;
+ }
+ return {changeHeap:changeHeap, get:get, inner:inner}`);
+
+var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
+var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
+var buf3 = new ArrayBuffer(BUF_CHANGE_MIN);
+var buf4 = new ArrayBuffer(BUF_CHANGE_MIN);
+new Int32Array(buf2)[13] = 42;
+new Int32Array(buf3)[13] = 1024;
+new Int32Array(buf4)[13] = 1337;
+
+function ffi2() { neuter(buf1, "change-data"); assertEq(changeHeap(buf2), true); }
+var {changeHeap, get:get2, inner} = asmLink(m, this, {ffi:ffi2}, buf1);
+assertEq(inner(13*4), 42);
+
+function ffi3() {
+ assertEq(get2(13*4), 42);
+ assertEq(get2(BUF_CHANGE_MIN), 0)
+ assertEq(get3(13*4), 42);
+ assertEq(get3(BUF_CHANGE_MIN), 0)
+ neuter(buf2, "change-data");
+ assertThrowsInstanceOf(()=>get2(13*4), InternalError);
+ assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);