mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:25:44 +00:00
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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user