import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1204983 - Allow about: pages to load remote content (r=bholley,bz,florian,dragana) (234bb3d551)
- Bug 1201272 - add reftest for canvas shadows with destination-out op. r=jmuizelaar (2d4618d60f)
- Bug 1224976. Recover from singular-matrix cairo errors. r=mattwoodrow (7ae2e051e9)
- Bug 1200021 - Part 3: Add DrawTarget::IsValid and don't let Cairo version snapshot invalid surface. r=bas (069d0db780)
- Bug 1219991 - Make RestyleManager::{Begin,End}ProcessingRestyles private. r=dholbert (29f36406bb)
- Bug 1147692 - Remove gmp-decryptor backwards compatibility hack. r=edwin (d53204cb84)
- Bug 1229508 - Support current and previous GMP_API_DECRYPTORs. r=gerald (887b0b8245)
- Bug 1228215 - Expose the GMP name on each GMPParent. r=jwwang (9c5205143d)
- Bug 1183433 - Make GMPParent's AbortWaitingForGMPAsyncShutdown class-static, to simplify upcoming work. r=cpearce (483648359e)
- Bug 1174064 - Ensure we don't try to reuse a GMP doing async shutdown. r=edwin (fe62f2daec)
- fix log (000dc88d7b)
- Bug 1169129 - Make GMPService's GMP crash handlers easier to register. r=gerald (9c05cfed78)
- Bug 1227908 - JavaScript error: resource://gre/modules/PushService.jsm, line 281: ReferenceError: data is not defined. r=kcambridge (99cb65c1cd)
- Bug 1225968 - Add authentication secret to push API, r=kitcambridge,smaug (60d57d206a)
This commit is contained in:
2023-04-17 11:04:43 +08:00
parent 522696571d
commit 6f7ba1a01e
45 changed files with 836 additions and 251 deletions
+20 -4
View File
@@ -9,6 +9,7 @@
#include "nsAboutProtocolUtils.h"
#include "mozilla/ArrayUtils.h"
#include "nsDOMString.h"
#include "nsIProtocolHandler.h"
NS_IMPL_ISUPPORTS(nsAboutRedirector, nsIAboutModule)
@@ -137,12 +138,27 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
nsCOMPtr<nsIURI> tempURI;
rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
NS_ENSURE_SUCCESS(rv, rv);
// If tempURI links to an external URI (i.e. something other than
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
// channel which forces the channel owner to reflect the displayed
// URL rather then being the systemPrincipal.
bool isUIResource = false;
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
&isUIResource);
NS_ENSURE_SUCCESS(rv, rv);
nsLoadFlags loadFlags =
isUIResource ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,
aLoadInfo);
if (NS_FAILED(rv)) {
return rv;
}
aLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
tempChannel->SetOriginalURI(aURI);
+4 -2
View File
@@ -17,13 +17,15 @@ interface nsIPrincipal;
* endpoint.
*/
[scriptable, uuid(dc201064-8e5c-4a26-bd37-d1e33558a903)]
[scriptable, uuid(d83e398f-9920-4451-b23a-6d5a5ad2fa26)]
interface nsIPushEndpointCallback : nsISupports
{
void onPushEndpoint(in nsresult status,
in DOMString endpoint,
in uint32_t keyLen,
[array, size_is(keyLen)] in octet key);
[array, size_is(keyLen)] in octet key,
in uint32_t authSecretLen,
[array, size_is(authSecretLen)] in octet authSecret);
};
/**
+1 -1
View File
@@ -1,5 +1,5 @@
Name: fake
Description: Fake GMP Plugin
Description: Fake GMP Plugin, which deliberately uses GMP_API_DECRYPTOR_BACKWARDS_COMPAT for its decryptor.
Version: 1.0
APIs: decode-video[h264:broken], eme-decrypt-v7[fake]
Libraries: dxva2.dll
+1 -1
View File
@@ -72,7 +72,7 @@ extern "C" {
// happens when decoder init fails.
return GMPGenericErr;
#if defined(GMP_FAKE_SUPPORT_DECRYPT)
} else if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
} else if (!strcmp (aApiName, GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
*aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
return GMPNoErr;
} else if (!strcmp (aApiName, GMP_API_ASYNC_SHUTDOWN)) {
+10 -6
View File
@@ -118,13 +118,17 @@ GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
void* session = nullptr;
GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session);
if (err != GMPNoErr && !session) {
// XXX to remove in bug 1147692
err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_COMPAT, host, &session);
}
if (err != GMPNoErr || !session) {
return false;
// We Adapt the previous GMPDecryptor version to the current, so that
// Gecko thinks it's only talking to the current version. Helpfully,
// v7 is ABI compatible with v8, it only has different enumerations.
// If the GMP uses a v8-only enum value in an IPDL message, the IPC
// layer will terminate, so we rev'd the API version to signal to the
// GMP that it's safe to use the new enum values.
err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_BACKWARDS_COMPAT, host, &session);
if (err != GMPNoErr || !session) {
return false;
}
}
child->Init(static_cast<GMPDecryptor*>(session));
+32 -18
View File
@@ -187,16 +187,18 @@ GMPParent::LoadProcess()
return NS_OK;
}
// static
void
AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
GMPParent::AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
{
NS_WARNING("Timed out waiting for GMP async shutdown!");
GMPParent* parent = reinterpret_cast<GMPParent*>(aClosure);
RefPtr<GeckoMediaPluginServiceParent> service =
GeckoMediaPluginServiceParent::GetSingleton();
if (service) {
service->AsyncShutdownComplete(parent);
}
MOZ_ASSERT(parent->mService);
#if defined(MOZ_CRASHREPORTER)
parent->mService->SetAsyncShutdownPluginState(parent, 'G',
NS_LITERAL_CSTRING("Timed out waiting for async shutdown"));
#endif
parent->mService->AsyncShutdownComplete(parent);
}
nsresult
@@ -468,8 +470,7 @@ GMPParent::ChildTerminated()
// PluginTerminated removes the GMP from the GMPService.
// On shutdown we can have this case where it is already been
// removed so there is no harm in not trying to remove it again.
//LOGD("%s::%s: GMPThread() returned nullptr.", __CLASS__, __FUNCTION__);
LOGD("%s: GMPThread() returned nullptr.", __FUNCTION__);
LOGD("%s::%s: GMPThread() returned nullptr.", __CLASS__, __FUNCTION__);
} else {
gmpThread->Dispatch(NS_NewRunnableMethodWithArg<RefPtr<GMPParent>>(
mService,
@@ -865,8 +866,13 @@ GMPParent::ReadGMPMetaData()
}
}
if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR) ||
cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_COMPAT)) {
// We support the current GMPDecryptor version, and the previous.
// We Adapt the previous to the current in the GMPContentChild.
if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
cap->mAPIName.AssignLiteral(GMP_API_DECRYPTOR);
}
if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) {
mCanDecrypt = true;
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
@@ -893,19 +899,21 @@ GMPParent::ReadGMPMetaData()
bool
GMPParent::CanBeSharedCrossNodeIds() const
{
return mNodeId.IsEmpty() &&
// XXX bug 1159300 hack -- maybe remove after openh264 1.4
// We don't want to use CDM decoders for non-encrypted playback
// just yet; especially not for WebRTC. Don't allow CDMs to be used
// without a node ID.
!mCanDecrypt;
return !mAsyncShutdownInProgress &&
mNodeId.IsEmpty() &&
// XXX bug 1159300 hack -- maybe remove after openh264 1.4
// We don't want to use CDM decoders for non-encrypted playback
// just yet; especially not for WebRTC. Don't allow CDMs to be used
// without a node ID.
!mCanDecrypt;
}
bool
GMPParent::CanBeUsedFrom(const nsACString& aNodeId) const
{
return (mNodeId.IsEmpty() && State() == GMPStateNotLoaded) ||
mNodeId == aNodeId;
return !mAsyncShutdownInProgress &&
((mNodeId.IsEmpty() && State() == GMPStateNotLoaded) ||
mNodeId == aNodeId);
}
void
@@ -1064,6 +1072,12 @@ GMPParent::Bridge(GMPServiceParent* aGMPServiceParent)
return true;
}
nsString
GMPParent::GetPluginBaseName() const
{
return NS_LITERAL_STRING("gmp-") + mName;
}
} // namespace gmp
} // namespace mozilla
+2
View File
@@ -123,6 +123,7 @@ public:
const nsCString& GetDisplayName() const;
const nsCString& GetVersion() const;
const uint32_t GetPluginId() const;
nsString GetPluginBaseName() const;
// Returns true if a plugin can be or is being used across multiple NodeIds.
bool CanBeSharedCrossNodeIds() const;
@@ -183,6 +184,7 @@ private:
}
static void AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure);
nsresult EnsureAsyncShutdownTimeoutSet();
GMPState mState;
+105 -19
View File
@@ -37,6 +37,9 @@
#include "nsIFile.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/dom/PluginCrashedEvent.h"
#include "mozilla/EventDispatcher.h"
namespace mozilla {
#ifdef LOG
@@ -158,33 +161,115 @@ GeckoMediaPluginService::RemoveObsoletePluginCrashCallbacks()
{
MOZ_ASSERT(NS_IsMainThread());
for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
RefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
RefPtr<GMPCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
if (!callback->IsStillValid()) {
LOGD(("%s::%s - Removing obsolete callback for pluginId %i",
__CLASS__, __FUNCTION__, callback->PluginId()));
__CLASS__, __FUNCTION__, callback->GetPluginId()));
mPluginCrashCallbacks.RemoveElementAt(i - 1);
}
}
}
void
GeckoMediaPluginService::AddPluginCrashCallback(
RefPtr<PluginCrashCallback> aPluginCrashCallback)
GeckoMediaPluginService::GMPCrashCallback::GMPCrashCallback(const uint32_t aPluginId,
nsPIDOMWindow* aParentWindow,
nsIDocument* aDocument)
: mPluginId(aPluginId)
, mParentWindowWeakPtr(do_GetWeakReference(aParentWindow))
, mDocumentWeakPtr(do_GetWeakReference(aDocument))
{
RemoveObsoletePluginCrashCallbacks();
mPluginCrashCallbacks.AppendElement(aPluginCrashCallback);
MOZ_ASSERT(NS_IsMainThread());
}
void
GeckoMediaPluginService::RemovePluginCrashCallbacks(const uint32_t aPluginId)
GeckoMediaPluginService::GMPCrashCallback::Run(const nsACString& aPluginName)
{
dom::PluginCrashedEventInit init;
init.mPluginID = mPluginId;
init.mBubbles = true;
init.mCancelable = true;
init.mGmpPlugin = true;
CopyUTF8toUTF16(aPluginName, init.mPluginName);
init.mSubmittedCrashReport = false;
// The following PluginCrashedEvent fields stay empty:
// init.mBrowserDumpID
// init.mPluginFilename
// TODO: Can/should we fill them?
nsCOMPtr<nsPIDOMWindow> parentWindow;
nsCOMPtr<nsIDocument> document;
if (!GetParentWindowAndDocumentIfValid(parentWindow, document)) {
return;
}
RefPtr<dom::PluginCrashedEvent> event =
dom::PluginCrashedEvent::Constructor(document, NS_LITERAL_STRING("PluginCrashed"), init);
event->SetTrusted(true);
event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
EventDispatcher::DispatchDOMEvent(parentWindow, nullptr, event, nullptr, nullptr);
}
bool
GeckoMediaPluginService::GMPCrashCallback::IsStillValid()
{
nsCOMPtr<nsPIDOMWindow> parentWindow;
nsCOMPtr<nsIDocument> document;
return GetParentWindowAndDocumentIfValid(parentWindow, document);
}
bool
GeckoMediaPluginService::GMPCrashCallback::GetParentWindowAndDocumentIfValid(
nsCOMPtr<nsPIDOMWindow>& parentWindow,
nsCOMPtr<nsIDocument>& document)
{
parentWindow = do_QueryReferent(mParentWindowWeakPtr);
if (!parentWindow) {
return false;
}
document = do_QueryReferent(mDocumentWeakPtr);
if (!document) {
return false;
}
nsCOMPtr<nsIDocument> parentWindowDocument = parentWindow->GetExtantDoc();
if (!parentWindowDocument || document.get() != parentWindowDocument.get()) {
return false;
}
return true;
}
void
GeckoMediaPluginService::AddPluginCrashedEventTarget(const uint32_t aPluginId,
nsPIDOMWindow* aParentWindow)
{
LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
if (NS_WARN_IF(!aParentWindow)) {
return;
}
nsCOMPtr<nsIDocument> doc = aParentWindow->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
RefPtr<GMPCrashCallback> callback(new GMPCrashCallback(aPluginId, aParentWindow, doc));
RemoveObsoletePluginCrashCallbacks();
for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
RefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
if (callback->PluginId() == aPluginId) {
mPluginCrashCallbacks.RemoveElementAt(i - 1);
// If the plugin with that ID has already crashed without being handled,
// just run the handler now.
for (size_t i = mPluginCrashes.Length(); i != 0; --i) {
size_t index = i - 1;
const PluginCrash& crash = mPluginCrashes[index];
if (crash.mPluginId == aPluginId) {
LOGD(("%s::%s(%i) - added crash handler for crashed plugin, running handler #%u",
__CLASS__, __FUNCTION__, aPluginId, index));
callback->Run(crash.mPluginName);
mPluginCrashes.RemoveElementAt(index);
}
}
// Remember crash, so if a handler is added for it later, we report the
// crash to that window too.
mPluginCrashCallbacks.AppendElement(callback);
}
void
@@ -193,20 +278,21 @@ GeckoMediaPluginService::RunPluginCrashCallbacks(const uint32_t aPluginId,
{
MOZ_ASSERT(NS_IsMainThread());
LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
RemoveObsoletePluginCrashCallbacks();
for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
RefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
const uint32_t callbackPluginId = callback->PluginId();
if (!callback->IsStillValid()) {
LOGD(("%s::%s(%i) - Removing obsolete callback for pluginId %i",
__CLASS__, __FUNCTION__, aPluginId, callback->PluginId()));
mPluginCrashCallbacks.RemoveElementAt(i - 1);
} else if (callbackPluginId == aPluginId) {
RefPtr<GMPCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
if (callback->GetPluginId() == aPluginId) {
LOGD(("%s::%s(%i) - Running #%u",
__CLASS__, __FUNCTION__, aPluginId, i - 1));
callback->Run(aPluginName);
mPluginCrashCallbacks.RemoveElementAt(i - 1);
}
}
mPluginCrashes.AppendElement(PluginCrash(aPluginId, aPluginName));
if (mPluginCrashes.Length() > MAX_PLUGIN_CRASHES) {
mPluginCrashes.RemoveElementAt(0);
}
}
nsresult
+53 -26
View File
@@ -16,6 +16,9 @@
#include "nsCOMPtr.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
#include "nsPIDOMWindow.h"
#include "nsIDocument.h"
#include "nsIWeakReference.h"
template <class> struct already_AddRefed;
@@ -62,37 +65,21 @@ public:
int32_t AsyncShutdownTimeoutMs();
class PluginCrashCallback
{
public:
NS_INLINE_DECL_REFCOUNTING(PluginCrashCallback)
PluginCrashCallback(const uint32_t aPluginId)
: mPluginId(aPluginId)
{
MOZ_ASSERT(NS_IsMainThread());
}
const uint32_t PluginId() const { return mPluginId; }
virtual void Run(const nsACString& aPluginName) = 0;
virtual bool IsStillValid() = 0; // False if callback has become useless.
protected:
virtual ~PluginCrashCallback()
{
MOZ_ASSERT(NS_IsMainThread());
}
private:
const uint32_t mPluginId;
};
void RemoveObsoletePluginCrashCallbacks(); // Called from add/remove/run.
void AddPluginCrashCallback(RefPtr<PluginCrashCallback> aPluginCrashCallback);
void RemovePluginCrashCallbacks(const uint32_t aPluginId);
void RunPluginCrashCallbacks(const uint32_t aPluginId,
const nsACString& aPluginName);
// Sets the window to which 'PluginCrashed' chromeonly event is dispatched.
// Note: if the plugin has crashed before the target window has been set,
// the 'PluginCrashed' event is dispatched as soon as a target window is set.
void AddPluginCrashedEventTarget(const uint32_t aPluginId,
nsPIDOMWindow* aParentWindow);
protected:
GeckoMediaPluginService();
virtual ~GeckoMediaPluginService();
void RemoveObsoletePluginCrashCallbacks(); // Called from add/run.
virtual void InitializePlugins() = 0;
virtual bool GetContentParentFrom(const nsACString& aNodeId,
const nsCString& aAPI,
@@ -102,14 +89,54 @@ protected:
nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
void ShutdownGMPThread();
protected:
Mutex mMutex; // Protects mGMPThread and mGMPThreadShutdown and some members
// in derived classes.
nsCOMPtr<nsIThread> mGMPThread;
bool mGMPThreadShutdown;
bool mShuttingDownOnGMPThread;
nsTArray<RefPtr<PluginCrashCallback>> mPluginCrashCallbacks;
class GMPCrashCallback
{
public:
NS_INLINE_DECL_REFCOUNTING(GMPCrashCallback)
GMPCrashCallback(const uint32_t aPluginId,
nsPIDOMWindow* aParentWindow,
nsIDocument* aDocument);
void Run(const nsACString& aPluginName);
bool IsStillValid();
const uint32_t GetPluginId() const { return mPluginId; }
private:
virtual ~GMPCrashCallback() { MOZ_ASSERT(NS_IsMainThread()); }
bool GetParentWindowAndDocumentIfValid(nsCOMPtr<nsPIDOMWindow>& parentWindow,
nsCOMPtr<nsIDocument>& document);
const uint32_t mPluginId;
nsWeakPtr mParentWindowWeakPtr;
nsWeakPtr mDocumentWeakPtr;
};
struct PluginCrash
{
PluginCrash(uint32_t aPluginId,
const nsACString& aPluginName)
: mPluginId(aPluginId)
, mPluginName(aPluginName)
{
}
uint32_t mPluginId;
nsCString mPluginName;
bool operator==(const PluginCrash& aOther) const {
return mPluginId == aOther.mPluginId &&
mPluginName == aOther.mPluginName;
}
};
static const size_t MAX_PLUGIN_CRASHES = 100;
nsTArray<PluginCrash> mPluginCrashes;
nsTArray<RefPtr<GMPCrashCallback>> mPluginCrashCallbacks;
};
} // namespace gmp
-7
View File
@@ -863,13 +863,6 @@ GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
return clone;
}
if (aAPI.EqualsLiteral(GMP_API_DECRYPTOR)) {
// XXX to remove in bug 1147692
return SelectPluginForAPI(aNodeId,
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
aTags);
}
return nullptr;
}
+3 -4
View File
@@ -238,10 +238,9 @@ enum GMPSessionType {
kGMPSessionInvalid = 2 // Must always be last.
};
#define GMP_API_DECRYPTOR "eme-decrypt-v7"
// XXX remove in bug 1147692
#define GMP_API_DECRYPTOR_COMPAT "eme-decrypt-v6"
// Gecko supports the current GMPDecryptor version, and the previous.
#define GMP_API_DECRYPTOR "eme-decrypt-v8"
#define GMP_API_DECRYPTOR_BACKWARDS_COMPAT "eme-decrypt-v7"
// API exposed by plugin library to manage decryption sessions.
// When the Host requests this by calling GMPGetAPIFunc().
+11 -2
View File
@@ -180,7 +180,8 @@ function PushEndpointCallback(pushManager, resolve, reject) {
PushEndpointCallback.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]),
onPushEndpoint: function(ok, endpoint, keyLen, key) {
onPushEndpoint: function(ok, endpoint, keyLen, key,
authSecretLen, authSecretIn) {
let {pushManager} = this;
if (!Components.isSuccessCode(ok)) {
this.reject(new pushManager._window.DOMException(
@@ -202,9 +203,17 @@ PushEndpointCallback.prototype = {
keyView.set(key);
}
let authSecret = null;
if (authSecretLen) {
authSecret = new ArrayBuffer(authSecretLen);
let secretView = new Uint8Array(authSecret);
secretView.set(authSecretIn);
}
let sub = new pushManager._window.PushSubscription(endpoint,
pushManager._scope,
publicKey);
publicKey,
authSecret);
sub.setPrincipal(pushManager._principal);
this.resolve(sub);
},
+18 -11
View File
@@ -101,19 +101,26 @@ PushClient.prototype = {
_deliverPushEndpoint: function(request, registration) {
if (!registration) {
request.onPushEndpoint(Cr.NS_OK, "", 0, null);
return;
}
if (registration.p256dhKey) {
let key = new Uint8Array(registration.p256dhKey);
request.onPushEndpoint(Cr.NS_OK,
registration.pushEndpoint,
key.length,
key);
request.onPushEndpoint(Cr.NS_OK, "", 0, null, 0, null);
return;
}
request.onPushEndpoint(Cr.NS_OK, registration.pushEndpoint, 0, null);
let key;
if (registration.p256dhKey) {
key = new Uint8Array(registration.p256dhKey);
}
let authSecret;
if (registration.authSecret) {
authSecret = new Uint8Array(registration.authSecret);
}
request.onPushEndpoint(Cr.NS_OK,
registration.pushEndpoint,
key ? key.length : 0,
key,
authSecret ? authSecret.length : 0,
authSecret);
},
receiveMessage: function(aMessage) {
@@ -135,7 +142,7 @@ PushClient.prototype = {
case "PushService:Register:KO":
case "PushService:Registration:KO":
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null, 0, null);
break;
case "PushService:Unregister:OK":
+78 -26
View File
@@ -13,10 +13,16 @@ this.EXPORTED_SYMBOLS = ['PushCrypto', 'concatArray',
'getEncryptionKeyParams', 'getEncryptionParams',
'base64UrlDecode'];
var ENCRYPT_INFO = new TextEncoder('utf-8').encode('Content-Encoding: aesgcm128');
var NONCE_INFO = new TextEncoder('utf-8').encode('Content-Encoding: nonce');
var UTF8 = new TextEncoder('utf-8');
var ENCRYPT_INFO = UTF8.encode('Content-Encoding: aesgcm128');
var NONCE_INFO = UTF8.encode('Content-Encoding: nonce');
var AUTH_INFO = UTF8.encode('Content-Encoding: auth\0'); // note nul-terminus
var P256DH_INFO = UTF8.encode('P-256\0');
this.getEncryptionKeyParams = function(encryptKeyField) {
if (!encryptKeyField) {
return null;
}
var params = encryptKeyField.split(',');
return params.reduce((m, p) => {
var pmap = p.split(';').reduce(parseHeaderFieldParams, {});
@@ -41,7 +47,7 @@ var parseHeaderFieldParams = (m, v) => {
// A quoted string with internal quotes is invalid for all the possible
// values of this header field.
m[v.substring(0, i).trim()] = v.substring(i + 1).trim()
.replace(/^"(.*)"$/, '$1');
.replace(/^"(.*)"$/, '$1');
}
return m;
};
@@ -115,7 +121,7 @@ function hkdf(salt, ikm) {
.then(prk => new hmac(prk));
}
hkdf.prototype.generate = function(info, len) {
hkdf.prototype.extract = function(info, len) {
var input = concatArray([info, new Uint8Array([1])]);
return this.prkhPromise
.then(prkh => prkh.hash(input))
@@ -127,10 +133,10 @@ hkdf.prototype.generate = function(info, len) {
});
};
/* generate a 96-bit IV for use in GCM, 48-bits of which are populated */
/* generate a 96-bit nonce for use in GCM, 48-bits of which are populated */
function generateNonce(base, index) {
if (index >= Math.pow(2, 48)) {
throw new Error('Error generating IV - index is too large.');
throw new Error('Error generating nonce - index is too large.');
}
var nonce = base.slice(0, 12);
nonce = new Uint8Array(nonce);
@@ -142,7 +148,11 @@ function generateNonce(base, index) {
this.PushCrypto = {
generateKeys: function() {
generateAuthenticationSecret() {
return crypto.getRandomValues(new Uint8Array(12));
},
generateKeys() {
return crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256'},
true,
['deriveBits'])
@@ -154,7 +164,8 @@ this.PushCrypto = {
]));
},
decodeMsg: function(aData, aPrivateKey, aRemotePublicKey, aSalt, aRs) {
decodeMsg(aData, aPrivateKey, aPublicKey, aSenderPublicKey,
aSalt, aRs, aAuthenticationSecret) {
if (aData.byteLength === 0) {
// Zero length messages will be passed as null.
@@ -167,8 +178,9 @@ this.PushCrypto = {
return Promise.reject(new Error('Data truncated'));
}
let senderKey = base64UrlDecode(aSenderPublicKey)
return Promise.all([
crypto.subtle.importKey('raw', base64UrlDecode(aRemotePublicKey),
crypto.subtle.importKey('raw', senderKey,
{ name: 'ECDH', namedCurve: 'P-256' },
false,
['deriveBits']),
@@ -177,18 +189,12 @@ this.PushCrypto = {
false,
['deriveBits'])
])
.then(keys =>
crypto.subtle.deriveBits({ name: 'ECDH', public: keys[0] }, keys[1], 256))
.then(rawKey => {
var kdf = new hkdf(base64UrlDecode(aSalt), new Uint8Array(rawKey));
return Promise.all([
kdf.generate(ENCRYPT_INFO, 16)
.then(gcmBits =>
crypto.subtle.importKey('raw', gcmBits, 'AES-GCM', false,
['decrypt'])),
kdf.generate(NONCE_INFO, 12)
])
})
.then(keys => crypto.subtle.deriveBits({ name: 'ECDH', public: keys[0] }, keys[1], 256))
.then(ikm => this._deriveKeyAndNonce(new Uint8Array(ikm),
base64UrlDecode(aSalt),
base64UrlDecode(aPublicKey),
senderKey,
aAuthenticationSecret))
.then(r =>
// AEAD_AES_128_GCM expands ciphertext to be 16 octets longer.
Promise.all(chunkArray(aData, aRs + 16).map((slice, index) =>
@@ -196,11 +202,57 @@ this.PushCrypto = {
.then(r => concatArray(r));
},
_decodeChunk: function(aSlice, aIndex, aNonce, aKey) {
return crypto.subtle.decrypt({name: 'AES-GCM',
iv: generateNonce(aNonce, aIndex)
},
aKey, aSlice)
_deriveKeyAndNonce(ikm, salt, receiverKey, senderKey, authenticationSecret) {
var kdfPromise;
var context;
// The authenticationSecret, when present, is mixed with the ikm using HKDF.
// This is its primary purpose. However, since the authentication secret
// was added at the same time that the info string was changed, we also use
// its presence to change how the final info string is calculated:
//
// 1. When there is no authenticationSecret, the context string is simply
// "Content-Encoding: <blah>". This corresponds to old, deprecated versions
// of the content encoding. This should eventually be removed: bug 1230038.
//
// 2. When there is an authenticationSecret, the context string is:
// "Content-Encoding: <blah>\0P-256\0" then the length and value of both the
// receiver key and sender key.
if (authenticationSecret) {
// Since we are using an authentication secret, we need to run an extra
// round of HKDF with the authentication secret as salt.
var authKdf = new hkdf(authenticationSecret, ikm);
kdfPromise = authKdf.extract(AUTH_INFO, 32)
.then(ikm2 => new hkdf(salt, ikm2));
// We also use the presence of the authentication secret to indicate that
// we have extra context to add to the info parameter.
context = concatArray([
new Uint8Array([0]), P256DH_INFO,
this._encodeLength(receiverKey), receiverKey,
this._encodeLength(senderKey), senderKey
]);
} else {
kdfPromise = Promise.resolve(new hkdf(salt, ikm));
context = new Uint8Array(0);
}
return kdfPromise.then(kdf => Promise.all([
kdf.extract(concatArray([ENCRYPT_INFO, context]), 16)
.then(gcmBits => crypto.subtle.importKey('raw', gcmBits, 'AES-GCM', false,
['decrypt'])),
kdf.extract(concatArray([NONCE_INFO, context]), 12)
]));
},
_encodeLength(buffer) {
return new Uint8Array([0, buffer.byteLength]);
},
_decodeChunk(aSlice, aIndex, aNonce, aKey) {
let params = {
name: 'AES-GCM',
iv: generateNonce(aNonce, aIndex)
};
return crypto.subtle.decrypt(params, aKey, aSlice)
.then(decoded => {
decoded = new Uint8Array(decoded);
if (decoded.length == 0) {
+84 -29
View File
@@ -8,6 +8,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "mozilla/dom/PushManagerBinding.h"
#include "mozilla/dom/PushSubscriptionBinding.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
@@ -124,11 +125,13 @@ PushSubscription::Unsubscribe(ErrorResult& aRv)
PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
const nsAString& aEndpoint,
const nsAString& aScope,
const nsTArray<uint8_t>& aRawP256dhKey)
const nsTArray<uint8_t>& aRawP256dhKey,
const nsTArray<uint8_t>& aAuthSecret)
: mGlobal(aGlobal)
, mEndpoint(aEndpoint)
, mScope(aScope)
, mRawP256dhKey(aRawP256dhKey)
, mAuthSecret(aAuthSecret)
{
}
@@ -145,14 +148,18 @@ PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
void
PushSubscription::GetKey(JSContext* aCx,
PushEncryptionKeyName aType,
JS::MutableHandle<JSObject*> aP256dhKey)
JS::MutableHandle<JSObject*> aKey)
{
if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
aP256dhKey.set(ArrayBuffer::Create(aCx,
mRawP256dhKey.Length(),
mRawP256dhKey.Elements()));
aKey.set(ArrayBuffer::Create(aCx,
mRawP256dhKey.Length(),
mRawP256dhKey.Elements()));
} else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) {
aKey.set(ArrayBuffer::Create(aCx,
mAuthSecret.Length(),
mAuthSecret.Elements()));
} else {
aP256dhKey.set(nullptr);
aKey.set(nullptr);
}
}
@@ -169,6 +176,7 @@ PushSubscription::Constructor(GlobalObject& aGlobal,
const nsAString& aEndpoint,
const nsAString& aScope,
const Nullable<ArrayBuffer>& aP256dhKey,
const Nullable<ArrayBuffer>& aAuthSecret,
ErrorResult& aRv)
{
MOZ_ASSERT(!aEndpoint.IsEmpty());
@@ -180,13 +188,20 @@ PushSubscription::Constructor(GlobalObject& aGlobal,
if (!aP256dhKey.IsNull()) {
const ArrayBuffer& key = aP256dhKey.Value();
key.ComputeLengthAndData();
rawKey.SetLength(key.Length());
rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
rawKey.InsertElementsAt(0, key.Data(), key.Length());
}
nsTArray<uint8_t> authSecret;
if (!aAuthSecret.IsNull()) {
const ArrayBuffer& sekrit = aAuthSecret.Value();
sekrit.ComputeLengthAndData();
authSecret.InsertElementsAt(0, sekrit.Data(), sekrit.Length());
}
RefPtr<PushSubscription> sub = new PushSubscription(global,
aEndpoint,
aScope,
rawKey);
aEndpoint,
aScope,
rawKey,
authSecret);
return sub.forget();
}
@@ -259,8 +274,12 @@ NS_INTERFACE_MAP_END
WorkerPushSubscription::WorkerPushSubscription(const nsAString& aEndpoint,
const nsAString& aScope,
const nsTArray<uint8_t>& aRawP256dhKey)
: mEndpoint(aEndpoint), mScope(aScope), mRawP256dhKey(aRawP256dhKey)
const nsTArray<uint8_t>& aRawP256dhKey,
const nsTArray<uint8_t>& aAuthSecret)
: mEndpoint(aEndpoint)
, mScope(aScope)
, mRawP256dhKey(aRawP256dhKey)
, mAuthSecret(aAuthSecret)
{
MOZ_ASSERT(!aScope.IsEmpty());
MOZ_ASSERT(!aEndpoint.IsEmpty());
@@ -281,6 +300,7 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal,
const nsAString& aEndpoint,
const nsAString& aScope,
const Nullable<ArrayBuffer>& aP256dhKey,
const Nullable<ArrayBuffer>& aAuthSecret,
ErrorResult& aRv)
{
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
@@ -294,9 +314,19 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal,
rawKey.SetLength(key.Length());
rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
}
nsTArray<uint8_t> authSecret;
if (!aAuthSecret.IsNull()) {
const ArrayBuffer& sekrit = aAuthSecret.Value();
sekrit.ComputeLengthAndData();
authSecret.SetLength(sekrit.Length());
authSecret.ReplaceElementsAt(0, sekrit.Length(),
sekrit.Data(), sekrit.Length());
}
RefPtr<WorkerPushSubscription> sub = new WorkerPushSubscription(aEndpoint,
aScope,
rawKey);
aScope,
rawKey,
authSecret);
return sub.forget();
}
@@ -304,15 +334,20 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal,
void
WorkerPushSubscription::GetKey(JSContext* aCx,
PushEncryptionKeyName aType,
JS::MutableHandle<JSObject*> aP256dhKey)
JS::MutableHandle<JSObject*> aKey)
{
if (aType == mozilla::dom::PushEncryptionKeyName::P256dh &&
!mRawP256dhKey.IsEmpty()) {
aP256dhKey.set(ArrayBuffer::Create(aCx,
mRawP256dhKey.Length(),
mRawP256dhKey.Elements()));
aKey.set(ArrayBuffer::Create(aCx,
mRawP256dhKey.Length(),
mRawP256dhKey.Elements()));
} else if (aType == mozilla::dom::PushEncryptionKeyName::Auth &&
!mAuthSecret.IsEmpty()) {
aKey.set(ArrayBuffer::Create(aCx,
mAuthSecret.Length(),
mAuthSecret.Elements()));
} else {
aP256dhKey.set(nullptr);
aKey.set(nullptr);
}
}
@@ -500,13 +535,15 @@ public:
nsresult aStatus,
const nsAString& aEndpoint,
const nsAString& aScope,
const nsTArray<uint8_t>& aRawP256dhKey)
const nsTArray<uint8_t>& aRawP256dhKey,
const nsTArray<uint8_t>& aAuthSecret)
: WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
, mProxy(aProxy)
, mStatus(aStatus)
, mEndpoint(aEndpoint)
, mScope(aScope)
, mRawP256dhKey(aRawP256dhKey)
, mAuthSecret(aAuthSecret)
{ }
bool
@@ -518,7 +555,8 @@ public:
promise->MaybeResolve(JS::NullHandleValue);
} else {
RefPtr<WorkerPushSubscription> sub =
new WorkerPushSubscription(mEndpoint, mScope, mRawP256dhKey);
new WorkerPushSubscription(mEndpoint, mScope,
mRawP256dhKey, mAuthSecret);
promise->MaybeResolve(sub);
}
} else {
@@ -537,6 +575,7 @@ private:
nsString mEndpoint;
nsString mScope;
nsTArray<uint8_t> mRawP256dhKey;
nsTArray<uint8_t> mAuthSecret;
};
class GetSubscriptionCallback final : public nsIPushEndpointCallback
@@ -554,7 +593,9 @@ public:
OnPushEndpoint(nsresult aStatus,
const nsAString& aEndpoint,
uint32_t aKeyLen,
uint8_t* aKey) override
uint8_t* aKey,
uint32_t aAuthSecretLen,
uint8_t* aAuthSecret) override
{
AssertIsOnMainThread();
MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?");
@@ -572,16 +613,30 @@ public:
nsTArray<uint8_t> rawP256dhKey(aKeyLen);
rawP256dhKey.ReplaceElementsAt(0, aKeyLen, aKey, aKeyLen);
nsTArray<uint8_t> authSecret(aAuthSecretLen);
authSecret.ReplaceElementsAt(0, aAuthSecretLen,
aAuthSecret, aAuthSecretLen);
RefPtr<GetSubscriptionResultRunnable> r =
new GetSubscriptionResultRunnable(proxy,
aStatus,
aEndpoint,
mScope,
rawP256dhKey);
rawP256dhKey,
authSecret);
r->Dispatch(jsapi.cx());
return NS_OK;
}
// Convenience method for use in this file.
void
OnPushEndpointError(nsresult aStatus)
{
Unused << NS_WARN_IF(NS_FAILED(
OnPushEndpoint(aStatus, EmptyString(), 0, nullptr, 0, nullptr)));
}
protected:
~GetSubscriptionCallback()
{}
@@ -619,23 +674,23 @@ public:
PushPermissionState state;
nsresult rv = GetPermissionState(principal, state);
if (NS_FAILED(rv)) {
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
callback->OnPushEndpointError(NS_ERROR_FAILURE);
return NS_OK;
}
if (state != PushPermissionState::Granted) {
if (mAction == WorkerPushManager::GetSubscriptionAction) {
callback->OnPushEndpoint(NS_OK, EmptyString(), 0, nullptr);
callback->OnPushEndpointError(NS_OK);
return NS_OK;
}
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
callback->OnPushEndpointError(NS_ERROR_FAILURE);
return NS_OK;
}
nsCOMPtr<nsIPushClient> client =
do_CreateInstance("@mozilla.org/push/PushClient;1");
if (!client) {
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
callback->OnPushEndpointError(NS_ERROR_FAILURE);
return NS_OK;
}
@@ -647,7 +702,7 @@ public:
}
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
callback->OnPushEndpointError(NS_ERROR_FAILURE);
return NS_OK;
}
+9 -3
View File
@@ -67,7 +67,8 @@ public:
explicit PushSubscription(nsIGlobalObject* aGlobal,
const nsAString& aEndpoint,
const nsAString& aScope,
const nsTArray<uint8_t>& aP256dhKey);
const nsTArray<uint8_t>& aP256dhKey,
const nsTArray<uint8_t>& aAuthSecret);
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -87,13 +88,14 @@ public:
void
GetKey(JSContext* cx,
PushEncryptionKeyName aType,
JS::MutableHandle<JSObject*> aP256dhKey);
JS::MutableHandle<JSObject*> aKey);
static already_AddRefed<PushSubscription>
Constructor(GlobalObject& aGlobal,
const nsAString& aEndpoint,
const nsAString& aScope,
const Nullable<ArrayBuffer>& aP256dhKey,
const Nullable<ArrayBuffer>& aAuthSecret,
ErrorResult& aRv);
void
@@ -111,6 +113,7 @@ private:
nsString mEndpoint;
nsString mScope;
nsTArray<uint8_t> mRawP256dhKey;
nsTArray<uint8_t> mAuthSecret;
};
class PushManager final : public nsISupports
@@ -161,7 +164,8 @@ public:
explicit WorkerPushSubscription(const nsAString& aEndpoint,
const nsAString& aScope,
const nsTArray<uint8_t>& aRawP256dhKey);
const nsTArray<uint8_t>& aRawP256dhKey,
const nsTArray<uint8_t>& aAuthSecret);
nsIGlobalObject*
GetParentObject() const
@@ -177,6 +181,7 @@ public:
const nsAString& aEndpoint,
const nsAString& aScope,
const Nullable<ArrayBuffer>& aP256dhKey,
const Nullable<ArrayBuffer>& aAuthSecret,
ErrorResult& aRv);
void
@@ -199,6 +204,7 @@ private:
nsString mEndpoint;
nsString mScope;
nsTArray<uint8_t> mRawP256dhKey;
nsTArray<uint8_t> mAuthSecret;
};
class WorkerPushManager final : public nsISupports
+2
View File
@@ -38,6 +38,7 @@ function PushRecord(props) {
this.lastPush = props.lastPush || 0;
this.p256dhPublicKey = props.p256dhPublicKey;
this.p256dhPrivateKey = props.p256dhPrivateKey;
this.authenticationSecret = props.authenticationSecret;
this.setQuota(props.quota);
this.ctime = (typeof props.ctime === "number") ? props.ctime : 0;
}
@@ -230,6 +231,7 @@ PushRecord.prototype = {
lastPush: this.lastPush,
pushCount: this.pushCount,
p256dhKey: this.p256dhPublicKey,
authenticationSecret: this.authenticationSecret,
};
},
};
+24 -8
View File
@@ -278,7 +278,7 @@ this.PushService = {
break;
case "clear-origin-data":
this._clearOriginData(data).catch(error => {
this._clearOriginData(aData).catch(error => {
console.error("clearOriginData: Error clearing origin data:", error);
});
break;
@@ -787,17 +787,29 @@ this.PushService = {
});
},
ensureP256dhKey: function(record) {
if (record.p256dhPublicKey && record.p256dhPrivateKey) {
ensureCrypto: function(record) {
if (record.authenticationSecret &&
record.p256dhPublicKey &&
record.p256dhPrivateKey) {
return Promise.resolve(record);
}
let keygen = Promise.resolve([]);
if (!record.p256dhPublicKey || !record.p256dhPrivateKey) {
keygen = PushCrypto.generateKeys();
}
// We do not have a encryption key. so we need to generate it. This
// is only going to happen on db upgrade from version 4 to higher.
return PushCrypto.generateKeys()
.then(exportedKeys => {
return keygen
.then(([pubKey, privKey]) => {
return this.updateRecordAndNotifyApp(record.keyID, record => {
record.p256dhPublicKey = exportedKeys[0];
record.p256dhPrivateKey = exportedKeys[1];
if (!record.p256dhPublicKey || !record.p256dhPrivateKey) {
record.p256dhPublicKey = pubKey;
record.p256dhPrivateKey = privKey;
}
if (!record.authenticationSecret) {
record.authenticationSecret = PushCrypto.generateAuthenticationSecret();
}
return record;
});
}, error => {
@@ -873,9 +885,11 @@ this.PushService = {
decodedPromise = PushCrypto.decodeMsg(
message,
record.p256dhPrivateKey,
record.p256dhPublicKey,
cryptoParams.dh,
cryptoParams.salt,
cryptoParams.rs
cryptoParams.rs,
cryptoParams.auth ? record.authenticationSecret : null
);
} else {
decodedPromise = Promise.resolve(null);
@@ -889,6 +903,8 @@ this.PushService = {
setTimeout(() => this._updateQuota(keyID),
prefs.get("quotaUpdateDelay"));
return notified;
}, error => {
console.error("receivedPushMessage: Error decrypting message", error);
});
}).catch(error => {
console.error("receivedPushMessage: Error notifying app", error);
+25 -17
View File
@@ -151,10 +151,17 @@ PushChannelListener.prototype = {
if (Components.isSuccessCode(aStatusCode) &&
this._mainListener &&
this._mainListener._pushService) {
let requiresAuthenticationSecret = true;
var keymap = encryptKeyFieldParser(aRequest);
var keymap = encryptKeyFieldParser(aRequest, "Crypto-Key");
if (!keymap) {
return;
// Backward compatibility: use the absence of Crypto-Key to indicate
// that the authentication secret isn't used.
requiresAuthenticationSecret = false;
keymap = encryptKeyFieldParser(aRequest, "Encryption-Key");
if (!keymap) {
return;
}
}
var enc = encryptFieldParser(aRequest);
if (!enc || !enc.keyid) {
@@ -169,19 +176,24 @@ PushChannelListener.prototype = {
var msg = concatArray(this._message);
let cryptoParams = {
dh: dh,
salt: salt,
rs: rs,
auth: requiresAuthenticationSecret,
};
this._mainListener._pushService._pushChannelOnStop(this._mainListener.uri,
this._ackUri,
msg,
dh,
salt,
rs);
cryptoParams);
}
}
};
function encryptKeyFieldParser(aRequest) {
function encryptKeyFieldParser(aRequest, name) {
try {
var encryptKeyField = aRequest.getRequestHeader("Encryption-Key");
var encryptKeyField = aRequest.getRequestHeader(name);
return getEncryptionKeyParams(encryptKeyField);
} catch(e) {
// getRequestHeader can throw.
@@ -490,9 +502,10 @@ this.PushServiceHttp2 = {
})
.then(result =>
PushCrypto.generateKeys()
.then(exportedKeys => {
result.p256dhPublicKey = exportedKeys[0];
result.p256dhPrivateKey = exportedKeys[1];
.then(([publicKey, privateKey]) => {
result.p256dhPublicKey = publicKey;
result.p256dhPrivateKey = privateKey;
result.authenticationSecret = PushCrypto.generateAuthenticationSecret();
this._conns[result.subscriptionUri] = {
channel: null,
listener: null,
@@ -673,7 +686,7 @@ this.PushServiceHttp2 = {
for (let i = 0; i < aSubscriptions.length; i++) {
let record = aSubscriptions[i];
this._mainPushService.ensureP256dhKey(record).then(record => {
this._mainPushService.ensureCrypto(record).then(record => {
this._startSingleConnection(record);
}, error => {
console.error("startConnections: Error updating record",
@@ -800,14 +813,9 @@ this.PushServiceHttp2 = {
}
},
_pushChannelOnStop: function(aUri, aAckUri, aMessage, dh, salt, rs) {
_pushChannelOnStop: function(aUri, aAckUri, aMessage, cryptoParams) {
console.debug("pushChannelOnStop()");
let cryptoParams = {
dh: dh,
salt: salt,
rs: rs,
};
this._mainPushService.receivedPushMessage(
aUri, aMessage, cryptoParams, record => {
// Always update the stored record.
+10 -4
View File
@@ -63,9 +63,14 @@ function getCryptoParams(headers) {
if (!headers) {
return null;
}
var keymap = getEncryptionKeyParams(headers.encryption_key);
var requiresAuthenticationSecret = true;
var keymap = getEncryptionKeyParams(headers.crypto_key);
if (!keymap) {
return null;
requiresAuthenticationSecret = false;
keymap = getEncryptionKeyParams(headers.encryption_key);
if (!keymap) {
return null;
}
}
var enc = getEncryptionParams(headers.encryption);
if (!enc || !enc.keyid) {
@@ -77,7 +82,7 @@ function getCryptoParams(headers) {
if (!dh || !salt || isNaN(rs) || (rs <= 1)) {
return null;
}
return {dh, salt, rs};
return {dh, salt, rs, auth: requiresAuthenticationSecret};
}
/**
@@ -802,7 +807,7 @@ this.PushServiceWebSocket = {
if (this._dataEnabled) {
this._mainPushService.getAllUnexpired().then(records =>
Promise.all(records.map(record =>
this._mainPushService.ensureP256dhKey(record).catch(error => {
this._mainPushService.ensureCrypto(record).catch(error => {
console.error("finishHandshake: Error updating record",
record.keyID, error);
})
@@ -1014,6 +1019,7 @@ this.PushServiceWebSocket = {
.then(([publicKey, privateKey]) => {
record.p256dhPublicKey = publicKey;
record.p256dhPrivateKey = privateKey;
record.authenticationSecret = PushCrypto.generateAuthenticationSecret();
return record;
});
});
+4 -2
View File
@@ -11,11 +11,13 @@ interface Principal;
enum PushEncryptionKeyName
{
"p256dh"
"p256dh",
"auth"
};
[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled",
ChromeConstructor(DOMString pushEndpoint, DOMString scope, ArrayBuffer? key)]
ChromeConstructor(DOMString pushEndpoint, DOMString scope,
ArrayBuffer? key, ArrayBuffer? authSecret)]
interface PushSubscription
{
readonly attribute USVString endpoint;
+1
View File
@@ -694,6 +694,7 @@ public:
DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {}
virtual ~DrawTarget() {}
virtual bool IsValid() const { return true; };
virtual DrawTargetType GetType() const = 0;
virtual BackendType GetBackendType() const = 0;
+96 -11
View File
@@ -222,6 +222,7 @@ CopyToImageSurface(unsigned char *aData,
// investigation hasn't been done to determine the underlying cause. We
// will just handle the failure to allocate the surface to avoid a crash.
if (cairo_surface_status(surf)) {
gfxWarning() << "Invalid surface DTC " << cairo_surface_status(surf);
return nullptr;
}
@@ -590,6 +591,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
DrawTargetCairo::DrawTargetCairo()
: mContext(nullptr)
, mSurface(nullptr)
, mTransformSingular(false)
, mLockedBits(nullptr)
{
}
@@ -599,10 +601,17 @@ DrawTargetCairo::~DrawTargetCairo()
cairo_destroy(mContext);
if (mSurface) {
cairo_surface_destroy(mSurface);
mSurface = nullptr;
}
MOZ_ASSERT(!mLockedBits);
}
bool
DrawTargetCairo::IsValid() const
{
return mSurface && !cairo_surface_status(mSurface);
}
DrawTargetType
DrawTargetCairo::GetType() const
{
@@ -680,6 +689,10 @@ GfxFormatForCairoSurface(cairo_surface_t* surface)
already_AddRefed<SourceSurface>
DrawTargetCairo::Snapshot()
{
if (!IsValid()) {
gfxCriticalNote << "DrawTargetCairo::Snapshot with bad surface " << cairo_surface_status(mSurface);
return nullptr;
}
if (mSnapshot) {
RefPtr<SourceSurface> snapshot(mSnapshot);
return snapshot.forget();
@@ -775,6 +788,15 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions)
{
if (mTransformSingular) {
return;
}
if (!IsValid()) {
gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(mSurface);
return;
}
AutoPrepareForDrawing prep(this, mContext);
AutoClearDeviceOffset clear(aSurface);
@@ -968,6 +990,10 @@ DrawTargetCairo::FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
bool restoreTransform = false;
@@ -1022,7 +1048,7 @@ DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface,
const IntPoint &aDest)
{
if (cairo_surface_status(aSurface)) {
gfxWarning() << "Invalid surface";
gfxWarning() << "Invalid surface" << cairo_surface_status(aSurface);
return;
}
@@ -1043,6 +1069,10 @@ DrawTargetCairo::CopySurface(SourceSurface *aSurface,
const IntRect &aSource,
const IntPoint &aDest)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
AutoClearDeviceOffset clear(aSurface);
@@ -1065,6 +1095,10 @@ void
DrawTargetCairo::CopyRect(const IntRect &aSource,
const IntPoint &aDest)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
IntRect source = aSource;
@@ -1097,6 +1131,10 @@ DrawTargetCairo::CopyRect(const IntRect &aSource,
void
DrawTargetCairo::ClearRect(const Rect& aRect)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
if (!mContext || aRect.Width() <= 0 || aRect.Height() <= 0 ||
@@ -1119,6 +1157,10 @@ DrawTargetCairo::StrokeRect(const Rect &aRect,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
cairo_new_path(mContext);
@@ -1134,6 +1176,10 @@ DrawTargetCairo::StrokeLine(const Point &aStart,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
cairo_new_path(mContext);
@@ -1149,6 +1195,10 @@ DrawTargetCairo::Stroke(const Path *aPath,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext, aPath);
if (aPath->GetBackendType() != BackendType::CAIRO)
@@ -1165,6 +1215,10 @@ DrawTargetCairo::Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions /* = DrawOptions() */)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext, aPath);
if (aPath->GetBackendType() != BackendType::CAIRO)
@@ -1193,6 +1247,15 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
const DrawOptions &aOptions,
const GlyphRenderingOptions*)
{
if (mTransformSingular) {
return;
}
if (!IsValid()) {
gfxDebug() << "FillGlyphs bad surface " << cairo_surface_status(mSurface);
return;
}
AutoPrepareForDrawing prep(this, mContext);
AutoClearDeviceOffset clear(aPattern);
@@ -1225,6 +1288,10 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
}
cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
if (mSurface && cairo_surface_status(mSurface)) {
gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(mSurface);
}
}
void
@@ -1232,6 +1299,10 @@ DrawTargetCairo::Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions /* = DrawOptions() */)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
AutoClearDeviceOffset clearSource(aSource);
AutoClearDeviceOffset clearMask(aMask);
@@ -1270,6 +1341,10 @@ DrawTargetCairo::MaskSurface(const Pattern &aSource,
Point aOffset,
const DrawOptions &aOptions)
{
if (mTransformSingular) {
return;
}
AutoPrepareForDrawing prep(this, mContext);
AutoClearDeviceOffset clearSource(aSource);
AutoClearDeviceOffset clearMask(aMask);
@@ -1336,7 +1411,13 @@ DrawTargetCairo::PushClip(const Path *aPath)
cairo_save(mContext);
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
path->SetPathOnContext(mContext);
if (mTransformSingular) {
cairo_new_path(mContext);
cairo_rectangle(mContext, 0, 0, 0, 0);
} else {
path->SetPathOnContext(mContext);
}
cairo_clip_preserve(mContext);
}
@@ -1347,7 +1428,11 @@ DrawTargetCairo::PushClipRect(const Rect& aRect)
cairo_save(mContext);
cairo_new_path(mContext);
cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
if (mTransformSingular) {
cairo_rectangle(mContext, 0, 0, 0, 0);
} else {
cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
}
cairo_clip_preserve(mContext);
}
@@ -1364,9 +1449,6 @@ DrawTargetCairo::PopClip()
cairo_restore(mContext);
cairo_set_matrix(mContext, &mat);
MOZ_ASSERT(cairo_status(mContext) || GetTransform() == Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0),
"Transforms are out of sync");
}
already_AddRefed<PathBuilder>
@@ -1571,7 +1653,7 @@ bool
DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
{
if (cairo_surface_status(aSurface)) {
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
gfxCriticalNote
<< "Attempt to create DrawTarget for invalid surface. "
<< aSize << " Cairo Status: " << cairo_surface_status(aSurface);
cairo_surface_destroy(aSurface);
@@ -1709,11 +1791,14 @@ DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
void
DrawTargetCairo::SetTransform(const Matrix& aTransform)
{
mTransform = aTransform;
DrawTarget::SetTransform(aTransform);
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(mTransform, mat);
cairo_set_matrix(mContext, &mat);
mTransformSingular = aTransform.IsSingular();
if (!mTransformSingular) {
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(mTransform, mat);
cairo_set_matrix(mContext, &mat);
}
}
Rect
+2
View File
@@ -59,6 +59,7 @@ public:
DrawTargetCairo();
virtual ~DrawTargetCairo();
virtual bool IsValid() const override;
virtual DrawTargetType GetType() const override;
virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
virtual already_AddRefed<SourceSurface> Snapshot() override;
@@ -209,6 +210,7 @@ private: // data
cairo_t* mContext;
cairo_surface_t* mSurface;
IntSize mSize;
bool mTransformSingular;
uint8_t* mLockedBits;
+5
View File
@@ -366,6 +366,11 @@ DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
}
if (mSurface->CairoStatus()) {
gfxWarning() << "Bad Cairo surface internal update " << mSurface->CairoStatus();
mTextureSource = nullptr;
return;
}
RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat);
+9 -2
View File
@@ -162,7 +162,8 @@ BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
mContentClient->BeginPaintBuffer(this, flags);
mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state)) {
DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state);
if (target && target->IsValid()) {
// The area that became invalid and is visible needs to be repainted
// (this could be the whole visible area if our buffer switched
// from RGB to RGBA, because we might need to repaint with
@@ -183,16 +184,22 @@ BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
Mutated();
ctx = nullptr;
mContentClient->ReturnDrawTargetToBuffer(target);
target = nullptr;
RenderTraceInvalidateEnd(this, "FFFF00");
} else {
if (target) {
mContentClient->ReturnDrawTargetToBuffer(target);
target = nullptr;
}
// It's possible that state.mRegionToInvalidate is nonempty here,
// if we are shrinking the valid region to nothing. So use mRegionToDraw
// instead.
NS_WARN_IF_FALSE(state.mRegionToDraw.IsEmpty(),
"No context when we have something to draw, resource exhaustion?");
}
for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) {
ReadbackProcessor::Update& update = readbackUpdates[i];
nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+7
View File
@@ -80,6 +80,13 @@ ClientPaintedLayer::PaintThebes()
bool didUpdate = false;
RotatedContentBuffer::DrawIterator iter;
while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
if (!target || !target->IsValid()) {
if (target) {
mContentClient->ReturnDrawTargetToBuffer(target);
}
continue;
}
SetAntialiasingFlags(this, target);
RefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target);
+15 -9
View File
@@ -586,15 +586,21 @@ ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
// Restrict the DrawTargets and frontBuffer to a scope to make
// sure there is no more external references to the DrawTargets
// when we Unlock the TextureClients.
RefPtr<SourceSurface> surf = mFrontClient->BorrowDrawTarget()->Snapshot();
RefPtr<SourceSurface> surfOnWhite = mFrontClientOnWhite
? mFrontClientOnWhite->BorrowDrawTarget()->Snapshot()
: nullptr;
SourceRotatedBuffer frontBuffer(surf,
surfOnWhite,
mFrontBufferRect,
mFrontBufferRotation);
UpdateDestinationFrom(frontBuffer, updateRegion);
gfx::DrawTarget* dt = mFrontClient->BorrowDrawTarget();
gfx::DrawTarget* dtw = mFrontClientOnWhite ? mFrontClientOnWhite->BorrowDrawTarget() : nullptr;
if (dt && dt->IsValid()) {
RefPtr<SourceSurface> surf = dt->Snapshot();
RefPtr<SourceSurface> surfOnWhite = dtw ? dtw->Snapshot() : nullptr;
SourceRotatedBuffer frontBuffer(surf,
surfOnWhite,
mFrontBufferRect,
mFrontBufferRotation);
UpdateDestinationFrom(frontBuffer, updateRegion);
} else {
// We know this can happen, but we want to track it somewhat, in case it leads
// to other problems.
gfxCriticalNote << "Invalid draw target(s) " << hexa(dt) << " and " << hexa(dtw);
}
}
void
+5
View File
@@ -88,6 +88,11 @@ gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
/* static */ already_AddRefed<gfxContext>
gfxContext::ContextForDrawTarget(DrawTarget* aTarget)
{
if (!aTarget || !aTarget->IsValid()) {
gfxWarning() << "Invalid target in gfxContext::ContextForDrawTarget";
return nullptr;
}
Matrix transform = aTarget->GetTransform();
RefPtr<gfxContext> result = new gfxContext(aTarget);
result->SetMatrix(ThebesMatrix(transform));
+5
View File
@@ -29,6 +29,11 @@ gfxImageSurface::gfxImageSurface()
void
gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
{
if (!csurf || cairo_surface_status(csurf)) {
MakeInvalid();
return;
}
mSize.width = cairo_image_surface_get_width(csurf);
mSize.height = cairo_image_surface_get_height(csurf);
mData = cairo_image_surface_get_data(csurf);
+24 -5
View File
@@ -77,8 +77,18 @@ public:
gfxImageFormat Format() const { return mFormat; }
virtual const mozilla::gfx::IntSize GetSize() const override { return mSize; }
int32_t Width() const { return mSize.width; }
int32_t Height() const { return mSize.height; }
int32_t Width() const {
if (mSize.width < 0) {
return 0;
}
return mSize.width;
}
int32_t Height() const {
if (mSize.height < 0) {
return 0;
}
return mSize.height;
}
/**
* Distance in bytes between the start of a line and the start of the
@@ -93,7 +103,12 @@ public:
/**
* Returns the total size of the image data.
*/
int32_t GetDataSize() const { return mStride*mSize.height; }
int32_t GetDataSize() const {
if (mStride < 0 || mSize.height < 0) {
return 0;
}
return mStride*mSize.height;
}
/* Fast copy from another image surface; returns TRUE if successful, FALSE otherwise */
bool CopyFrom (gfxImageSurface *other);
@@ -144,8 +159,12 @@ protected:
void AllocateAndInit(long aStride, int32_t aMinimalAllocation, bool aClear);
void InitFromSurface(cairo_surface_t *csurf);
long ComputeStride() const { return ComputeStride(mSize, mFormat); }
long ComputeStride() const {
if (mSize.height < 0 || mSize.width < 0) {
return 0;
}
return ComputeStride(mSize, mFormat);
}
void MakeInvalid();
+5 -3
View File
@@ -65,9 +65,6 @@ public:
return mPresContext;
}
nsCSSFrameConstructor* FrameConstructor() const
{ return PresContext()->FrameConstructor(); }
// Should be called when a frame is going to be destroyed and
// WillDestroyFrameTree hasn't been called yet.
void NotifyDestroyingFrame(nsIFrame* aFrame);
@@ -138,6 +135,9 @@ public:
}
private:
nsCSSFrameConstructor* FrameConstructor() const
{ return PresContext()->FrameConstructor(); }
// Used when restyling an element with a frame.
void ComputeAndProcessStyleChange(nsIFrame* aFrame,
nsChangeHint aMinChange,
@@ -346,12 +346,14 @@ public:
// Returns whether there are any pending restyles.
bool HasPendingRestyles() { return mPendingRestyles.Count() != 0; }
private:
// ProcessPendingRestyles calls into one of our RestyleTracker
// objects. It then calls back to these functions at the beginning
// and end of its work.
void BeginProcessingRestyles(RestyleTracker& aRestyleTracker);
void EndProcessingRestyles();
public:
// Update styles for animations that are running on the compositor and
// whose updating is suppressed on the main thread (to save
// unnecessary work), while leaving all other aspects of style
+33
View File
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script>
function draw() {
var ctx = document.getElementById('c').getContext('2d');
ctx.beginPath();
ctx.arc(75, 75, 74, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(75, 75, 40, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.globalCompositeOperation = 'source-over';
ctx.lineWidth = 10;
ctx.strokeStyle = 'green';
ctx.beginPath();
ctx.arc(75, 75, 40, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
}
</script>
</head>
<body onload='draw()' style='background: white;'>
<canvas id='c' width='200' height='200'></canvas>
</body>
</html>
+36
View File
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script>
function draw() {
var ctx = document.getElementById('c').getContext('2d');
ctx.beginPath();
ctx.arc(75, 75, 74, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.globalCompositeOperation = 'destination-out';
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
ctx.shadowBlur = 3;
ctx.beginPath();
ctx.arc(75, 75, 40, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.shadowBlur = 0;
ctx.globalCompositeOperation = 'source-over';
ctx.lineWidth = 10;
ctx.strokeStyle = 'green';
ctx.beginPath();
ctx.arc(75, 75, 40, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
}
</script>
</head>
<body onload='draw()' style='background: white;'>
<canvas id='c' width='200' height='200'></canvas>
</body>
</html>
@@ -0,0 +1,2 @@
<!DOCTYPE HTML>
<div style="background:black; width:10px; height:10px"></div>
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<canvas id="c"></canvas>
<script>
var ctx = c.getContext('2d');
ctx.scale(0,1);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 10, 10);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillRect(0, 0, 10, 10);
</script>
+2
View File
@@ -106,3 +106,5 @@ fuzzy-if(azureQuartz,2,128) fuzzy-if(d2d,12,21) == 784573-1.html 784573-1-ref.ht
== 1074733-1.html 1074733-1-ref.html
fuzzy-if(Mulet,45,2) == 1107096-invisibles.html 1107096-invisibles-ref.html
== 1151821-1.html 1151821-1-ref.html
== 1201272-1.html 1201272-1-ref.html
== 1224976-1.html 1224976-1-ref.html
@@ -23,6 +23,7 @@
#include "gmp-api/gmp-decryption.h"
#include "Endian.h"
#include <assert.h>
#include <string.h>
using namespace mozilla;
+8
View File
@@ -24,6 +24,7 @@
#include "ClearKeyBase64.h"
#include "ArrayUtils.h"
#include <assert.h>
#include <memory.h>
#include "Endian.h"
#include "openaes/oaes_lib.h"
@@ -539,3 +540,10 @@ ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength)
}
return true;
}
GMPMutex* GMPCreateMutex() {
GMPMutex* mutex;
auto err = GetPlatform()->createmutex(&mutex);
assert(mutex);
return GMP_FAILED(err) ? nullptr : mutex;
}
+22
View File
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <string>
#include <vector>
#include <assert.h>
#include "gmp-decryption.h"
#define CLEARKEY_KEY_LEN ((size_t)16)
@@ -75,4 +76,25 @@ Contains(const Container& aContainer, const Element& aElement)
return aContainer.find(aElement) != aContainer.end();
}
class AutoLock {
public:
explicit AutoLock(GMPMutex* aMutex)
: mMutex(aMutex)
{
assert(aMutex);
if (mMutex) {
mMutex->Acquire();
}
}
~AutoLock() {
if (mMutex) {
mMutex->Release();
}
}
private:
GMPMutex* mMutex;
};
GMPMutex* GMPCreateMutex();
#endif // __ClearKeyUtils_h__
+38 -2
View File
@@ -18,8 +18,44 @@
#define __RefCount_h__
#include <stdint.h>
#include <atomic>
#include <assert.h>
#include "ClearKeyUtils.h"
#if defined(_MSC_VER)
#include <atomic>
typedef std::atomic<uint32_t> AtomicRefCount;
#else
class AtomicRefCount {
public:
explicit AtomicRefCount(uint32_t aValue)
: mCount(aValue)
, mMutex(GMPCreateMutex())
{
assert(mMutex);
}
~AtomicRefCount()
{
if (mMutex) {
mMutex->Destroy();
}
}
uint32_t operator--() {
AutoLock lock(mMutex);
return --mCount;
}
uint32_t operator++() {
AutoLock lock(mMutex);
return ++mCount;
}
operator uint32_t() {
AutoLock lock(mMutex);
return mCount;
}
private:
uint32_t mCount;
GMPMutex* mMutex;
};
#endif
// Note: Thread safe.
class RefCounted {
@@ -45,7 +81,7 @@ protected:
{
assert(!mRefCount);
}
std::atomic<uint32_t> mRefCount;
AtomicRefCount mRefCount;
};
template<class T>
-15
View File
@@ -243,21 +243,6 @@ inline uint32_t MicrosecondsToRTPTime(Microseconds us) {
return uint32_t(0xffffffff & (us * 90000) / 1000000);
}
class AutoLock {
public:
AutoLock(GMPMutex* aMutex)
: mMutex(aMutex)
{
assert(aMutex);
mMutex->Acquire();
}
~AutoLock() {
mMutex->Release();
}
private:
GMPMutex* mMutex;
};
void dump(const uint8_t* data, uint32_t len, const char* filename);
HRESULT
+2 -2
View File
@@ -2,9 +2,9 @@ Name: clearkey
Description: ClearKey Gecko Media Plugin
Version: 1
#ifdef ENABLE_WMF
APIs: eme-decrypt-v7[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey]
APIs: eme-decrypt-v8[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey]
Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll, evr.dll, mfheaacdec.dll, mfh264dec.dll, mfplat.dll
#else
APIs: eme-decrypt-v7[org.w3.clearkey]
APIs: eme-decrypt-v8[org.w3.clearkey]
Libraries:
#endif
-12
View File
@@ -1507,18 +1507,6 @@ NS_TryToMakeImmutable(nsIURI *uri,
return result.forget();
}
nsresult
NS_URIChainHasFlags(nsIURI *uri,
uint32_t flags,
bool *result)
{
nsresult rv;
nsCOMPtr<nsINetUtil> util = do_GetNetUtil(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return util->URIChainHasFlags(uri, flags, result);
}
already_AddRefed<nsIURI>
NS_GetInnermostURI(nsIURI *aURI)
{
+12
View File
@@ -88,6 +88,18 @@ net_EnsureIOService(nsIIOService **ios, nsCOMPtr<nsIIOService> &grip)
return rv;
}
INLINE_IF_EXTERN nsresult
NS_URIChainHasFlags(nsIURI *uri,
uint32_t flags,
bool *result)
{
nsresult rv;
nsCOMPtr<nsINetUtil> util = do_GetNetUtil(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return util->URIChainHasFlags(uri, flags, result);
}
INLINE_IF_EXTERN nsresult
NS_NewURI(nsIURI **result,
const nsACString &spec,