Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2014-08-06 13:43:41 +01:00
commit 2e6839e1e5
106 changed files with 2403 additions and 458 deletions

View File

@ -2651,7 +2651,7 @@ if test "$ac_cv_sockaddr_sa_len" = true ; then
AC_DEFINE(HAVE_SA_LEN)
fi
AC_ARG_ENABLE(dtrace,
MOZ_ARG_ENABLE_BOOL(dtrace,
[ --enable-dtrace build with dtrace support if available (default=no)],
[enable_dtrace="yes"],)
if test "x$enable_dtrace" = "xyes"; then

View File

@ -1520,6 +1520,13 @@ public:
return sScriptBlockerCount == 0;
}
/**
* Call this function if !IsSafeToRunScript() and we fail to run the script
* (rather than using AddScriptRunner as we usually do). |aDocument| is
* optional as it is only used for showing the URL in the console.
*/
static void WarnScriptWasIgnored(nsIDocument* aDocument);
/**
* Retrieve information about the viewport as a data structure.
* This will return information in the viewport META data section

View File

@ -3961,6 +3961,7 @@ nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
// than fire DOMNodeRemoved in all corner cases. We also rely on it for
// nsAutoScriptBlockerSuppressNodeRemoved.
if (!IsSafeToRunScript()) {
WarnScriptWasIgnored(aOwnerDoc);
return;
}
@ -5039,10 +5040,11 @@ nsContentUtils::GetAccessKeyCandidates(WidgetKeyboardEvent* aNativeKeyEvent,
void
nsContentUtils::AddScriptBlocker()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sScriptBlockerCount) {
NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
"Should not already have a count");
sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
"Should not already have a count");
sRunnersCountAtFirstBlocker = sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
}
++sScriptBlockerCount;
}
@ -5055,6 +5057,7 @@ static bool sRemovingScriptBlockers = false;
void
nsContentUtils::RemoveScriptBlocker()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sRemovingScriptBlockers);
NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
--sScriptBlockerCount;
@ -5062,6 +5065,10 @@ nsContentUtils::RemoveScriptBlocker()
return;
}
if (!sBlockedScriptRunners) {
return;
}
uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
uint32_t lastBlocker = sBlockedScriptRunners->Length();
uint32_t originalFirstBlocker = firstBlocker;
@ -5091,6 +5098,25 @@ nsContentUtils::RemoveScriptBlocker()
sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
}
/* static */
void
nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
{
nsAutoString msg;
if (aDocument) {
nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
if (uri) {
nsCString spec;
uri->GetSpec(spec);
msg.Append(NS_ConvertUTF8toUTF16(spec));
msg.AppendLiteral(" : ");
}
}
msg.AppendLiteral("Unable to run script because scripts are blocked internally.");
LogSimpleConsoleError(msg, "DOM");
}
/* static */
bool
nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)

View File

@ -634,8 +634,10 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
}
// It seems to be unsafe to fire an event handler during reflow (bug 393696)
if (!nsContentUtils::IsSafeToRunScript())
if (!nsContentUtils::IsSafeToRunScript()) {
nsContentUtils::WarnScriptWasIgnored(doc);
return false;
}
nsCOMPtr<nsIDocShell> docShell = piWindow->GetDocShell();
const bool chromeShell =

View File

@ -311,6 +311,7 @@ nsNodeUtils::CallUserDataHandlers(nsCOMArray<nsINode> &aNodesWithProperties,
"cloned nodes.");
if (!nsContentUtils::IsSafeToRunScript()) {
nsContentUtils::WarnScriptWasIgnored(aOwnerDocument);
if (nsContentUtils::IsChromeDoc(aOwnerDocument)) {
NS_WARNING("Fix the caller! Userdata callback disabled.");
} else {

View File

@ -3,4 +3,5 @@
[test_bug357450.js]
[test_copypaste.xul]
[test_messagemanager_principal.html]
[test_messagemanager_send_principal.html]
skip-if = buildapp == 'mulet'

View File

@ -0,0 +1,118 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Principal in MessageManager</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
var permManager = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
SimpleTest.waitForExplicitFinish();
const childFrameURL =
"data:text/html,<!DOCTYPE HTML><html><body></body></html>";
function childFrameScript() {
"use strict";
const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
getService(Ci.nsIScriptSecurityManager);
addMessageListener("test:content", function(message) {
sendAsyncMessage("test:result", "is nsIPrincipal: " +
(message.data instanceof Ci.nsIPrincipal ? "OK" : "KO"));
sendAsyncMessage("test:result", "principal.appId: " +
("appId" in message.data ? "OK" : "KO"));
sendAsyncMessage("test:result", "principal.origin: " +
("origin" in message.data ? "OK" : "KO"));
sendAsyncMessage("test:result", "principal.isInBrowserElement: " +
("isInBrowserElement" in message.data ? "OK" : "KO"));
});
addMessageListener("test:system", function(message) {
sendAsyncMessage("test:result", "isSystemPrincipal: " +
(secMan.isSystemPrincipal(message.data) ? "OK" : "KO"));
});
addMessageListener("test:null", function(message) {
sendAsyncMessage("test:result", "is nsIPrincipal: " +
(message.data instanceof Ci.nsIPrincipal ? "OK" : "KO"));
sendAsyncMessage("test:result", "isNullPrincipal: " +
(message.data.isNullPrincipal ? "OK" : "KO"));
sendAsyncMessage("test:result", "DONE");
});
}
function runTests() {
ok("Browser prefs set.");
let iframe = document.createElement("iframe");
SpecialPowers.wrap(iframe).mozbrowser = true;
iframe.id = "iframe";
iframe.src = childFrameURL;
iframe.addEventListener("mozbrowserloadend", function() {
ok(true, "Got iframe load event.");
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
mm.addMessageListener("test:result", function(message) {
// We need to wrap to access message.json, and unwrap to do the
// identity check.
var msg = SpecialPowers.unwrap(SpecialPowers.wrap(message).data);
if (/OK$/.exec(msg)) {
ok(true, msg);
} else if(/KO$/.exec(msg)) {
ok(true, false);
} else if (/DONE/.exec(msg)) {
permManager.removeFromPrincipal(window.document.nodePrincipal, "browser",
Ci.nsIPermissionManager.ALLOW_ACTION);
SimpleTest.finish();
}
});
mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
false);
mm.sendAsyncMessage("test:content", window.document.nodePrincipal);
let system = Cc["@mozilla.org/systemprincipal;1"].
createInstance(Ci.nsIPrincipal);
mm.sendAsyncMessage("test:system", system);
let nullP = Cc["@mozilla.org/nullprincipal;1"].
createInstance(Ci.nsIPrincipal);
mm.sendAsyncMessage("test:null", nullP);
});
document.body.appendChild(iframe);
}
addEventListener("load", function() {
info("Got load event.");
permManager.addFromPrincipal(window.document.nodePrincipal, "browser",
Ci.nsIPermissionManager.ALLOW_ACTION);
SpecialPowers.pushPrefEnv({
"set": [
["dom.mozBrowserFramesEnabled", true],
["browser.pagethumbnails.capturing_disabled", true]
]
}, runTests);
});
</script>
</body>
</html>

View File

@ -139,8 +139,9 @@ MediaEngineWebRTCVideoSource::NotifyPull(MediaStreamGraph* aGraph,
VideoSegment segment;
MonitorAutoLock lock(mMonitor);
if (mState != kStarted)
return;
// B2G does AddTrack, but holds kStarted until the hardware changes state.
// So mState could be kReleased here. We really don't care about the state,
// though.
// Note: we're not giving up mImage here
nsRefPtr<layers::Image> image = mImage;

View File

@ -821,7 +821,7 @@ this.DOMApplicationRegistry = {
root = aManifest.entry_points[aEntryPoint];
}
if (!root.activities) {
if (!root || !root.activities) {
return activitiesToRegister;
}
@ -2908,6 +2908,10 @@ this.DOMApplicationRegistry = {
});
let zipFile = yield this._getPackage(requestChannel, aId, aOldApp, aNewApp);
// After this point, it's too late to cancel the download.
AppDownloadManager.remove(aNewApp.manifestURL);
let hash = yield this._computeFileHash(zipFile.path);
let responseStatus = requestChannel.responseStatus;
@ -2926,8 +2930,6 @@ this.DOMApplicationRegistry = {
let newManifest = yield this._openAndReadPackage(zipFile, aOldApp, aNewApp,
isLocalFileInstall, aIsUpdate, aManifest, requestChannel, hash);
AppDownloadManager.remove(aNewApp.manifestURL);
return [aOldApp.id, newManifest];
}),

View File

@ -106,7 +106,7 @@ function handleRequest(request, response) {
response.write(resource);
aTimer.cancel();
response.finish();
}, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
}, 15000, Ci.nsITimer.TYPE_ONE_SHOT);
} else {
response.write(resource);
}

View File

@ -244,7 +244,7 @@ var steps = [
PackagedTestHelper.gApp.cancelDownload();
// And only do this once.
PackagedTestHelper.gApp.onprogress = null;
}, 40);
}, 10);
};
var alreadyCanceled = false;
@ -261,18 +261,24 @@ var steps = [
PackagedTestHelper.gApp.ondownloadsuccess = function() {
info("App downloaded");
// We could try also applying the download we just made.
// Sometimes we can't cancel in time (since setting a high timer on the
// sjs just doesn't work). Let's fail gracefully in that case, and just
// give a warning here.
if (!alreadyCanceled) {
todo(alreadyCanceled, "The app download wasn't cancelled in time!");
}
var expected = {
name: PackagedTestHelper.gAppName,
manifestURL: miniManifestURL,
installOrigin: PackagedTestHelper.gInstallOrigin,
progress: 0,
installState: "pending",
installState: alreadyCanceled?"pending":"installed",
downloadAvailable: false,
downloading: false,
downloadSize: 0,
size: 0,
readyToApplyDownload: true
readyToApplyDownload: alreadyCanceled
};
PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
true, false, function() {});

View File

@ -35,6 +35,10 @@ enum StructuredCloneTags {
// This tag is for WebCrypto keys
SCTAG_DOM_WEBCRYPTO_KEY,
SCTAG_DOM_NULL_PRINCIPAL,
SCTAG_DOM_SYSTEM_PRINCIPAL,
SCTAG_DOM_CONTENT_PRINCIPAL,
SCTAG_DOM_MAX
};

View File

@ -60,6 +60,8 @@
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/StructuredClone.h"
#include "mozilla/dom/SubtleCryptoBinding.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsAXPCNativeCallContext.h"
#include "mozilla/CycleCollectedJSRuntime.h"
@ -2830,6 +2832,46 @@ NS_DOMReadStructuredClone(JSContext* cx,
}
}
return result;
} else if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
mozilla::ipc::PrincipalInfo info;
if (tag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
info = mozilla::ipc::SystemPrincipalInfo();
} else if (tag == SCTAG_DOM_NULL_PRINCIPAL) {
info = mozilla::ipc::NullPrincipalInfo();
} else {
uint32_t appId = data;
uint32_t isInBrowserElement, specLength;
if (!JS_ReadUint32Pair(reader, &isInBrowserElement, &specLength)) {
return nullptr;
}
nsAutoCString spec;
spec.SetLength(specLength);
if (!JS_ReadBytes(reader, spec.BeginWriting(), specLength)) {
return nullptr;
}
info = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement, spec);
}
nsresult rv;
nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(info, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return nullptr;
}
JS::RootedValue result(cx);
rv = nsContentUtils::WrapNative(cx, principal, &NS_GET_IID(nsIPrincipal), &result);
if (NS_FAILED(rv)) {
xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return nullptr;
}
return result.toObjectOrNull();
}
// Don't know what this is. Bail.
@ -2856,6 +2898,31 @@ NS_DOMWriteStructuredClone(JSContext* cx,
key->WriteStructuredClone(writer);
}
if (xpc::IsReflector(obj)) {
nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
if (principal) {
mozilla::ipc::PrincipalInfo info;
if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
if (info.type() == mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
return JS_WriteUint32Pair(writer, SCTAG_DOM_NULL_PRINCIPAL, 0);
}
if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
return JS_WriteUint32Pair(writer, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
}
MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
return JS_WriteUint32Pair(writer, SCTAG_DOM_CONTENT_PRINCIPAL, cInfo.appId()) &&
JS_WriteUint32Pair(writer, cInfo.isInBrowserElement(), cInfo.spec().Length()) &&
JS_WriteBytes(writer, cInfo.spec().get(), cInfo.spec().Length());
}
}
// Don't know what this is
xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;

View File

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebGLBindableName.h"
#include "GLConsts.h"
#include "mozilla/Assertions.h"
using namespace mozilla;
WebGLBindableName::WebGLBindableName()
: mGLName(LOCAL_GL_NONE)
, mTarget(LOCAL_GL_NONE)
{ }
void
WebGLBindableName::BindTo(GLenum target)
{
MOZ_ASSERT(target != LOCAL_GL_NONE, "Can't bind to GL_NONE.");
MOZ_ASSERT(mTarget == LOCAL_GL_NONE || mTarget == target, "Rebinding is illegal.");
bool targetChanged = (target != mTarget);
mTarget = target;
if (targetChanged)
OnTargetChanged();
}

View File

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef WEBGLBINDABLENAME_H_
#define WEBGLBINDABLENAME_H_
#include "WebGLTypes.h"
namespace mozilla {
/** Represents a GL name that can be bound to a target.
*/
class WebGLBindableName
{
public:
WebGLBindableName();
void BindTo(GLenum target);
bool HasEverBeenBound() const { return mTarget != 0; }
GLuint GLName() const { return mGLName; }
GLenum Target() const { return mTarget; }
protected:
//! Called after mTarget has been changed by BindTo(target).
virtual void OnTargetChanged() {}
GLuint mGLName;
GLenum mTarget;
};
} // namespace mozilla
#endif // !WEBGLBINDABLENAME_H_

View File

@ -13,10 +13,9 @@
using namespace mozilla;
WebGLBuffer::WebGLBuffer(WebGLContext *context)
: WebGLContextBoundObject(context)
, mHasEverBeenBound(false)
: WebGLBindableName()
, WebGLContextBoundObject(context)
, mByteLength(0)
, mTarget(LOCAL_GL_NONE)
{
SetIsDOMBinding();
mContext->MakeContextCurrent();
@ -38,8 +37,7 @@ WebGLBuffer::Delete() {
}
void
WebGLBuffer::SetTarget(GLenum target) {
mTarget = target;
WebGLBuffer::OnTargetChanged() {
if (!mCache && mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
mCache = new WebGLElementArrayCache;
}

View File

@ -10,6 +10,7 @@
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "nsWrapperCache.h"
#include "WebGLBindableName.h"
#include "WebGLObjectModel.h"
#include "WebGLTypes.h"
@ -19,6 +20,7 @@ class WebGLElementArrayCache;
class WebGLBuffer MOZ_FINAL
: public nsWrapperCache
, public WebGLBindableName
, public WebGLRefCountedObject<WebGLBuffer>
, public LinkedListElement<WebGLBuffer>
, public WebGLContextBoundObject
@ -30,16 +32,10 @@ public:
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
bool HasEverBeenBound() { return mHasEverBeenBound; }
void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
GLuint GLName() const { return mGLName; }
WebGLsizeiptr ByteLength() const { return mByteLength; }
GLenum Target() const { return mTarget; }
void SetByteLength(WebGLsizeiptr byteLength) { mByteLength = byteLength; }
void SetTarget(GLenum target);
bool ElementArrayCacheBufferData(const void* ptr, size_t buffer_size_in_bytes);
void ElementArrayCacheBufferSubData(size_t pos, const void* ptr, size_t update_size_in_bytes);
@ -61,10 +57,9 @@ public:
protected:
~WebGLBuffer();
GLuint mGLName;
bool mHasEverBeenBound;
virtual void OnTargetChanged() MOZ_OVERRIDE;
WebGLsizeiptr mByteLength;
GLenum mTarget;
nsAutoPtr<WebGLElementArrayCache> mCache;
};

View File

@ -31,9 +31,8 @@ WebGLContext::BindBuffer(GLenum target, WebGLBuffer *buffer)
}
if (buffer) {
if (!buffer->Target()) {
buffer->SetTarget(target);
buffer->SetHasEverBeenBound(true);
if (!buffer->HasEverBeenBound()) {
buffer->BindTo(target);
} else if (target != buffer->Target()) {
return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
}
@ -67,12 +66,11 @@ WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
}
if (buffer) {
if (!buffer->Target()) {
buffer->SetTarget(target);
buffer->SetHasEverBeenBound(true);
} else if (target != buffer->Target()) {
if (!buffer->HasEverBeenBound())
buffer->BindTo(target);
if (target != buffer->Target())
return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
}
}
WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer");
@ -108,12 +106,12 @@ WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
}
if (buffer) {
if (!buffer->Target()) {
buffer->SetTarget(target);
buffer->SetHasEverBeenBound(true);
} else if (target != buffer->Target()) {
if (!buffer->HasEverBeenBound())
buffer->BindTo(target);
if (target != buffer->Target())
return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
}
CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + size;
if (!checked_neededByteLength.isValid() ||
checked_neededByteLength.value() > buffer->ByteLength())

View File

@ -174,9 +174,9 @@ WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer *wfb)
if (!wfb) {
gl->fBindFramebuffer(target, 0);
} else {
wfb->BindTo(target);
GLuint framebuffername = wfb->GLName();
gl->fBindFramebuffer(target, framebuffername);
wfb->SetHasEverBeenBound(true);
}
mBoundFramebuffer = wfb;
@ -199,7 +199,7 @@ WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer *wrb)
return;
if (wrb)
wrb->SetHasEverBeenBound(true);
wrb->BindTo(target);
MakeContextCurrent();

View File

@ -89,5 +89,3 @@ WebGLContext::IsVertexArray(WebGLVertexArray *array)
!array->IsDeleted() &&
array->HasEverBeenBound();
}

View File

@ -24,9 +24,9 @@ WebGLFramebuffer::WrapObject(JSContext* cx)
}
WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context)
: WebGLContextBoundObject(context)
: WebGLBindableName()
, WebGLContextBoundObject(context)
, mStatus(0)
, mHasEverBeenBound(false)
, mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
, mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT)
, mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
@ -79,8 +79,9 @@ WebGLFramebuffer::Attachment::HasAlpha() const
bool
WebGLFramebuffer::Attachment::IsReadableFloat() const
{
if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType();
const WebGLTexture* tex = Texture();
if (tex && tex->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
GLenum type = tex->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType();
switch (type) {
case LOCAL_GL_FLOAT:
case LOCAL_GL_HALF_FLOAT_OES:
@ -89,8 +90,9 @@ WebGLFramebuffer::Attachment::IsReadableFloat() const
return false;
}
if (Renderbuffer()) {
GLenum format = Renderbuffer()->InternalFormat();
const WebGLRenderbuffer* rb = Renderbuffer();
if (rb) {
GLenum format = rb->InternalFormat();
switch (format) {
case LOCAL_GL_RGB16F:
case LOCAL_GL_RGBA16F:
@ -101,6 +103,8 @@ WebGLFramebuffer::Attachment::IsReadableFloat() const
return false;
}
// If we arrive here Attachment isn't correct setup because it has
// no texture nor render buffer pointer.
MOZ_ASSERT(false, "Should not get here.");
return false;
}

View File

@ -6,6 +6,7 @@
#ifndef WEBGLFRAMEBUFFER_H_
#define WEBGLFRAMEBUFFER_H_
#include "WebGLBindableName.h"
#include "WebGLObjectModel.h"
#include "nsWrapperCache.h"
@ -23,6 +24,7 @@ namespace gl {
class WebGLFramebuffer MOZ_FINAL
: public nsWrapperCache
, public WebGLBindableName
, public WebGLRefCountedObject<WebGLFramebuffer>
, public LinkedListElement<WebGLFramebuffer>
, public WebGLContextBoundObject
@ -92,10 +94,6 @@ public:
void Delete();
bool HasEverBeenBound() { return mHasEverBeenBound; }
void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
GLuint GLName() { return mGLName; }
void FramebufferRenderbuffer(GLenum target,
GLenum attachment,
GLenum rbtarget,
@ -183,9 +181,6 @@ private:
mutable GLenum mStatus;
GLuint mGLName;
bool mHasEverBeenBound;
// we only store pointers to attached renderbuffers, not to attached textures, because
// we will only need to initialize renderbuffers. Textures are already initialized.
nsTArray<Attachment> mColorAttachments;

View File

@ -43,12 +43,12 @@ WebGLRenderbuffer::WrapObject(JSContext *cx) {
}
WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext *context)
: WebGLContextBoundObject(context)
: WebGLBindableName()
, WebGLContextBoundObject(context)
, mPrimaryRB(0)
, mSecondaryRB(0)
, mInternalFormat(0)
, mInternalFormatForGL(0)
, mHasEverBeenBound(false)
, mImageDataStatus(WebGLImageDataStatus::NoImageData)
{
SetIsDOMBinding();

View File

@ -6,6 +6,7 @@
#ifndef WEBGLRENDERBUFFER_H_
#define WEBGLRENDERBUFFER_H_
#include "WebGLBindableName.h"
#include "WebGLObjectModel.h"
#include "WebGLFramebufferAttachable.h"
@ -17,6 +18,7 @@ namespace mozilla {
class WebGLRenderbuffer MOZ_FINAL
: public nsWrapperCache
, public WebGLBindableName
, public WebGLRefCountedObject<WebGLRenderbuffer>
, public LinkedListElement<WebGLRenderbuffer>
, public WebGLRectangleObject
@ -28,9 +30,6 @@ public:
void Delete();
bool HasEverBeenBound() { return mHasEverBeenBound; }
void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
bool HasUninitializedImageData() const { return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData; }
void SetImageDataStatus(WebGLImageDataStatus x) {
// there is no way to go from having image data to not having any
@ -71,7 +70,6 @@ protected:
GLuint mSecondaryRB;
GLenum mInternalFormat;
GLenum mInternalFormatForGL;
bool mHasEverBeenBound;
WebGLImageDataStatus mImageDataStatus;
friend class WebGLFramebuffer;

View File

@ -23,9 +23,8 @@ WebGLTexture::WrapObject(JSContext *cx) {
}
WebGLTexture::WebGLTexture(WebGLContext *context)
: WebGLContextBoundObject(context)
, mHasEverBeenBound(false)
, mTarget(0)
: WebGLBindableName()
, WebGLContextBoundObject(context)
, mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
, mMagFilter(LOCAL_GL_LINEAR)
, mWrapS(LOCAL_GL_REPEAT)
@ -108,18 +107,21 @@ WebGLTexture::Bind(GLenum aTarget) {
// this function should only be called by bindTexture().
// it assumes that the GL context is already current.
bool firstTimeThisTextureIsBound = !mHasEverBeenBound;
bool firstTimeThisTextureIsBound = !HasEverBeenBound();
if (!firstTimeThisTextureIsBound && aTarget != mTarget) {
if (firstTimeThisTextureIsBound) {
BindTo(aTarget);
} else if (aTarget != Target()) {
mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
// very important to return here before modifying texture state! This was the place when I lost a whole day figuring
// very strange 'invalid write' crashes.
return;
}
mTarget = aTarget;
GLuint name = GLName();
GLenum target = Target();
mContext->gl->fBindTexture(mTarget, mGLName);
mContext->gl->fBindTexture(target, name);
if (firstTimeThisTextureIsBound) {
mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
@ -132,8 +134,6 @@ WebGLTexture::Bind(GLenum aTarget) {
if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES())
mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
}
mHasEverBeenBound = true;
}
void
@ -141,8 +141,14 @@ WebGLTexture::SetImageInfo(GLenum aTarget, GLint aLevel,
GLsizei aWidth, GLsizei aHeight,
GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus)
{
if ( (aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D) )
// TODO(djg): I suspected the following ASSERT and check are
// trying to express more than they're saying, probably
// to do with cubemap targets. We should do this
// properly. https://bugzilla.mozilla.org/show_bug.cgi?id=1006908
MOZ_ASSERT((aTarget == LOCAL_GL_TEXTURE_2D) == (mTarget == LOCAL_GL_TEXTURE_2D));
if ((aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D)) {
return;
}
EnsureMaxLevelWithCustomImagesAtLeast(aLevel);

View File

@ -6,8 +6,9 @@
#ifndef WEBGLTEXTURE_H_
#define WEBGLTEXTURE_H_
#include "WebGLObjectModel.h"
#include "WebGLBindableName.h"
#include "WebGLFramebufferAttachable.h"
#include "WebGLObjectModel.h"
#include "nsWrapperCache.h"
@ -27,6 +28,7 @@ inline bool is_pot_assuming_nonnegative(GLsizei x)
// WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
class WebGLTexture MOZ_FINAL
: public nsWrapperCache
, public WebGLBindableName
, public WebGLRefCountedObject<WebGLTexture>
, public LinkedListElement<WebGLTexture>
, public WebGLContextBoundObject
@ -37,11 +39,6 @@ public:
void Delete();
bool HasEverBeenBound() const { return mHasEverBeenBound; }
void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
GLuint GLName() const { return mGLName; }
GLenum Target() const { return mTarget; }
WebGLContext *GetParentObject() const {
return Context();
}
@ -59,9 +56,6 @@ protected:
friend class WebGLContext;
friend class WebGLFramebuffer;
bool mHasEverBeenBound;
GLuint mGLName;
// we store information about the various images that are part of
// this texture (cubemap faces, mipmap levels)
@ -205,7 +199,6 @@ public:
protected:
GLenum mTarget;
GLenum mMinFilter, mMagFilter, mWrapS, mWrapT;
size_t mFacesCount, mMaxLevelWithCustomImages;
@ -278,7 +271,7 @@ public:
bool IsMipmapCubeComplete() const;
void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
// Returns the current fake-black-status, except if it was Unknown,
// in which case this function resolves it first, so it never returns Unknown.
WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();

View File

@ -20,9 +20,8 @@ WebGLVertexArray::WrapObject(JSContext *cx) {
}
WebGLVertexArray::WebGLVertexArray(WebGLContext* context)
: WebGLContextBoundObject(context)
, mGLName(0)
, mHasEverBeenBound(false)
: WebGLBindableName()
, WebGLContextBoundObject(context)
{
SetIsDOMBinding();
context->mVertexArrays.insertBack(this);

View File

@ -6,6 +6,7 @@
#ifndef WEBGLVERTEXARRAY_H_
#define WEBGLVERTEXARRAY_H_
#include "WebGLBindableName.h"
#include "WebGLObjectModel.h"
#include "WebGLBuffer.h"
#include "WebGLVertexAttribData.h"
@ -20,6 +21,7 @@ class WebGLVertexArrayFake;
class WebGLVertexArray
: public nsWrapperCache
, public WebGLBindableName
, public WebGLRefCountedObject<WebGLVertexArray>
, public LinkedListElement<WebGLVertexArray>
, public WebGLContextBoundObject
@ -30,7 +32,9 @@ public:
static WebGLVertexArray* Create(WebGLContext* context);
void BindVertexArray() {
SetHasEverBeenBound(true);
/* Bind to dummy value to signal that this vertex array has
ever been bound */
BindTo(LOCAL_GL_VERTEX_ARRAY_BINDING);
BindVertexArrayImpl();
};
@ -58,9 +62,6 @@ public:
// -------------------------------------------------------------------------
// MEMBER FUNCTIONS
bool HasEverBeenBound() { return mHasEverBeenBound; }
void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
bool EnsureAttrib(GLuint index, const char *info);
bool HasAttrib(GLuint index) {
return index < mAttribs.Length();
@ -82,8 +83,6 @@ protected:
// -------------------------------------------------------------------------
// MEMBERS
GLuint mGLName;
bool mHasEverBeenBound;
nsTArray<WebGLVertexAttribData> mAttribs;
WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;

View File

@ -41,6 +41,7 @@ if CONFIG['MOZ_WEBGL']:
'WebGL1Context.cpp',
'WebGL2Context.cpp',
'WebGLActiveInfo.cpp',
'WebGLBindableName.cpp',
'WebGLBuffer.cpp',
'WebGLContext.cpp',
'WebGLContextAsyncQueries.cpp',

View File

@ -322,6 +322,23 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
return nullptr;
}
// Check for id-ecDH. Per the WebCrypto spec we must support it but NSS
// does unfortunately not know about it. Let's change the algorithm to
// id-ecPublicKey to make NSS happy.
if (SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm)) {
// Retrieve OID data for id-ecPublicKey (1.2.840.10045.2.1).
SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PUBLIC_KEY);
if (!oidData) {
return nullptr;
}
SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
&oidData->oid);
if (rv != SECSuccess) {
return nullptr;
}
}
return SECKEY_ExtractPublicKey(spki.get());
}
@ -343,66 +360,58 @@ CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
CryptoBuffer& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
ScopedSECItem spkiItem(PK11_DEREncodePublicKey(aPubKey));
if (!spkiItem.get()) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
ScopedCERTSubjectPublicKeyInfo spki(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
if (!spki) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// Per WebCrypto spec we must export ECDH SPKIs with the algorithm OID
// id-ecDH (1.3.132.112). NSS doesn't know about that OID and there is
// no way to specify the algorithm to use when exporting a public key.
if (aPubKey->keyType == ecKey) {
SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
&SEC_OID_DATA_EC_DH);
if (rv != SECSuccess) {
return NS_ERROR_DOM_OPERATION_ERR;
}
}
const SEC_ASN1Template* tpl = SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate);
ScopedSECItem spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki, tpl));
aRetVal.Assign(spkiItem.get());
return NS_OK;
}
SECKEYPrivateKey*
CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
SECItem*
CreateECPointForCoordinates(const CryptoBuffer& aX,
const CryptoBuffer& aY,
PLArenaPool* aArena)
{
if (!aJwk.mKty.WasPassed() || !aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
// Check that both points have the same length.
if (aX.Length() != aY.Length()) {
return nullptr;
}
// Verify that all of the required parameters are present
CryptoBuffer n, e, d, p, q, dp, dq, qi;
if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
!aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
!aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
!aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
!aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
!aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
!aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) ||
!aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) {
// Create point.
SECItem* point = ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
if (!point) {
return nullptr;
}
// Compute the ID for this key
// This is generated with a SHA-1 hash, so unlikely to collide
ScopedSECItem nItem(n.ToSECItem());
ScopedSECItem objID(PK11_MakeIDFromPubKey(nItem.get()));
if (!nItem.get() || !objID.get()) {
return nullptr;
}
// Populate template from parameters
CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
CK_KEY_TYPE rsaValue = CKK_RSA;
CK_BBOOL falseValue = CK_FALSE;
CK_ATTRIBUTE keyTemplate[14] = {
{ CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
{ CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) },
{ CKA_TOKEN, &falseValue, sizeof(falseValue) },
{ CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
{ CKA_PRIVATE, &falseValue, sizeof(falseValue) },
{ CKA_ID, objID->data, objID->len },
{ CKA_MODULUS, (void*) n.Elements(), n.Length() },
{ CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() },
{ CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() },
{ CKA_PRIME_1, (void*) p.Elements(), p.Length() },
{ CKA_PRIME_2, (void*) q.Elements(), q.Length() },
{ CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() },
{ CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() },
{ CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() },
};
// Set point data.
point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
memcpy(point->data + 1, aX.Elements(), aX.Length());
memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
return point;
}
SECKEYPrivateKey*
PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID,
CK_ATTRIBUTE* aTemplate,
CK_ULONG aTemplateSize)
{
// Create a generic object with the contents of the key
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot.get()) {
@ -410,8 +419,8 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
}
ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
keyTemplate,
PR_ARRAY_SIZE(keyTemplate),
aTemplate,
aTemplateSize,
PR_FALSE));
if (!obj.get()) {
return nullptr;
@ -419,14 +428,134 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
// Have NSS translate the object to a private key by inspection
// and make a copy we can own
ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), objID.get(),
ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), aObjID,
nullptr));
if (!privKey.get()) {
return nullptr;
}
return SECKEY_CopyPrivateKey(privKey.get());
}
SECKEYPrivateKey*
CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
if (!aJwk.mKty.WasPassed()) {
return nullptr;
}
CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
CK_BBOOL falseValue = CK_FALSE;
if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) {
// Verify that all of the required parameters are present
CryptoBuffer x, y, d;
if (!aJwk.mCrv.WasPassed() ||
!aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
!aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) ||
!aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) {
return nullptr;
}
nsString namedCurve;
if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) {
return nullptr;
}
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
return nullptr;
}
// Create parameters.
SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
if (!params) {
return nullptr;
}
SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get());
if (!ecPoint) {
return nullptr;
}
// Compute the ID for this key
// This is generated with a SHA-1 hash, so unlikely to collide
ScopedSECItem objID(PK11_MakeIDFromPubKey(ecPoint));
if (!objID.get()) {
return nullptr;
}
// Populate template from parameters
CK_KEY_TYPE ecValue = CKK_EC;
CK_ATTRIBUTE keyTemplate[9] = {
{ CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
{ CKA_KEY_TYPE, &ecValue, sizeof(ecValue) },
{ CKA_TOKEN, &falseValue, sizeof(falseValue) },
{ CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
{ CKA_PRIVATE, &falseValue, sizeof(falseValue) },
{ CKA_ID, objID->data, objID->len },
{ CKA_EC_PARAMS, params->data, params->len },
{ CKA_EC_POINT, ecPoint->data, ecPoint->len },
{ CKA_VALUE, (void*) d.Elements(), d.Length() },
};
return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
PR_ARRAY_SIZE(keyTemplate));
}
if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
// Verify that all of the required parameters are present
CryptoBuffer n, e, d, p, q, dp, dq, qi;
if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
!aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
!aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
!aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
!aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
!aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
!aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) ||
!aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) {
return nullptr;
}
// Compute the ID for this key
// This is generated with a SHA-1 hash, so unlikely to collide
ScopedSECItem nItem(n.ToSECItem());
if (!nItem.get()) {
return nullptr;
}
ScopedSECItem objID(PK11_MakeIDFromPubKey(nItem.get()));
if (!objID.get()) {
return nullptr;
}
// Populate template from parameters
CK_KEY_TYPE rsaValue = CKK_RSA;
CK_ATTRIBUTE keyTemplate[14] = {
{ CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
{ CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) },
{ CKA_TOKEN, &falseValue, sizeof(falseValue) },
{ CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
{ CKA_PRIVATE, &falseValue, sizeof(falseValue) },
{ CKA_ID, objID->data, objID->len },
{ CKA_MODULUS, (void*) n.Elements(), n.Length() },
{ CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() },
{ CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() },
{ CKA_PRIME_1, (void*) p.Elements(), p.Length() },
{ CKA_PRIME_2, (void*) q.Elements(), q.Length() },
{ CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() },
{ CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() },
{ CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() },
};
return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
PR_ARRAY_SIZE(keyTemplate));
}
return nullptr;
}
bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey,
CK_ATTRIBUTE_TYPE aAttribute,
Optional<nsString>& aDst)
@ -449,6 +578,71 @@ bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey,
return true;
}
bool
ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey, const SECItem* aEcParams,
const SECItem* aPublicValue, JsonWebKey& aRetVal)
{
aRetVal.mX.Construct();
aRetVal.mY.Construct();
// Check that the given EC parameters are valid.
if (!CheckEncodedECParameters(aEcParams)) {
return false;
}
// Construct the OID tag.
SECItem oid = { siBuffer, nullptr, 0 };
oid.len = aEcParams->data[1];
oid.data = aEcParams->data + 2;
uint32_t flen;
switch (SECOID_FindOIDTag(&oid)) {
case SEC_OID_SECG_EC_SECP256R1:
flen = 32; // bytes
aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P256));
break;
case SEC_OID_SECG_EC_SECP384R1:
flen = 48; // bytes
aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P384));
break;
case SEC_OID_SECG_EC_SECP521R1:
flen = 66; // bytes
aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P521));
break;
default:
return false;
}
// No support for compressed points.
if (aPublicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
return false;
}
// Check length of uncompressed point coordinates.
if (aPublicValue->len != (2 * flen + 1)) {
return false;
}
ScopedSECItem ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen));
ScopedSECItem ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen));
if (!ecPointX || !ecPointY) {
return false;
}
// Extract point data.
memcpy(ecPointX->data, aPublicValue->data + 1, flen);
memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
CryptoBuffer x, y;
if (!x.Assign(ecPointX) || NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value())) ||
!y.Assign(ecPointY) || NS_FAILED(y.ToJwkBase64(aRetVal.mY.Value()))) {
return false;
}
aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_EC));
return true;
}
nsresult
CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
JsonWebKey& aRetVal,
@ -479,7 +673,36 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA));
return NS_OK;
}
case ecKey: // TODO: Bug 1034855
case ecKey: {
// Read EC params.
ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0));
SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
CKA_EC_PARAMS, params);
if (rv != SECSuccess) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// Read public point Q.
ScopedSECItem ecPoint(::SECITEM_AllocItem(nullptr, nullptr, 0));
rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_POINT,
ecPoint);
if (rv != SECSuccess) {
return NS_ERROR_DOM_OPERATION_ERR;
}
if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, params, ecPoint, aRetVal)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
aRetVal.mD.Construct();
// Read private value.
if (!ReadAndEncodeAttribute(aPrivKey, CKA_VALUE, aRetVal.mD)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
return NS_OK;
}
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
@ -489,40 +712,89 @@ SECKEYPublicKey*
CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
if (!aJwk.mKty.WasPassed() || !aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
if (!aJwk.mKty.WasPassed()) {
return nullptr;
}
// Verify that all of the required parameters are present
CryptoBuffer n, e;
if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
!aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
return nullptr;
if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
// Verify that all of the required parameters are present
CryptoBuffer n, e;
if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
!aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
return nullptr;
}
// Transcode to a DER RSAPublicKey structure
struct RSAPublicKeyData {
SECItem n;
SECItem e;
};
const RSAPublicKeyData input = {
{ siUnsignedInteger, n.Elements(), (unsigned int) n.Length() },
{ siUnsignedInteger, e.Elements(), (unsigned int) e.Length() }
};
const SEC_ASN1Template rsaPublicKeyTemplate[] = {
{SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)},
{SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, n),},
{SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, e),},
{0,}
};
ScopedSECItem pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input,
rsaPublicKeyTemplate));
if (!pkDer.get()) {
return nullptr;
}
return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA);
}
// Transcode to a DER RSAPublicKey structure
struct RSAPublicKeyData {
SECItem n;
SECItem e;
};
const RSAPublicKeyData input = {
{ siUnsignedInteger, n.Elements(), (unsigned int) n.Length() },
{ siUnsignedInteger, e.Elements(), (unsigned int) e.Length() }
};
const SEC_ASN1Template rsaPublicKeyTemplate[] = {
{SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)},
{SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, n),},
{SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, e),},
{0,}
};
if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) {
// Verify that all of the required parameters are present
CryptoBuffer x, y;
if (!aJwk.mCrv.WasPassed() ||
!aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
!aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) {
return nullptr;
}
ScopedSECItem pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input,
rsaPublicKeyTemplate));
if (!pkDer.get()) {
return nullptr;
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
return nullptr;
}
SECKEYPublicKey* key = PORT_ArenaZNew(arena, SECKEYPublicKey);
if (!key) {
return nullptr;
}
key->keyType = ecKey;
key->pkcs11Slot = nullptr;
key->pkcs11ID = CK_INVALID_HANDLE;
nsString namedCurve;
if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) {
return nullptr;
}
// Create parameters.
SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
if (!params) {
return nullptr;
}
key->u.ec.DEREncodedParams = *params;
// Create point.
SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
if (!point) {
return nullptr;
}
key->u.ec.publicValue = *point;
return SECKEY_CopyPublicKey(key);
}
return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA);
return nullptr;
}
nsresult
@ -546,7 +818,12 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA));
return NS_OK;
}
case ecKey: // TODO
case ecKey:
if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
&aPubKey->u.ec.publicValue, aRetVal)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
return NS_OK;
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}

View File

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/EcKeyAlgorithm.h"
#include "mozilla/dom/SubtleCryptoBinding.h"
#include "mozilla/dom/WebCryptoCommon.h"
namespace mozilla {
namespace dom {
JSObject*
EcKeyAlgorithm::WrapObject(JSContext* aCx)
{
return EcKeyAlgorithmBinding::Wrap(aCx, this);
}
bool
EcKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
{
return JS_WriteUint32Pair(aWriter, SCTAG_ECKEYALG, 0) &&
WriteString(aWriter, mNamedCurve) &&
WriteString(aWriter, mName);
}
KeyAlgorithm*
EcKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
{
nsString name;
nsString namedCurve;
bool read = ReadString(aReader, namedCurve) &&
ReadString(aReader, name);
if (!read) {
return nullptr;
}
return new EcKeyAlgorithm(aGlobal, name, namedCurve);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_EcKeyAlgorithm_h
#define mozilla_dom_EcKeyAlgorithm_h
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/KeyAlgorithm.h"
#include "js/TypeDecls.h"
namespace mozilla {
namespace dom {
class EcKeyAlgorithm : public KeyAlgorithm
{
public:
EcKeyAlgorithm(nsIGlobalObject* aGlobal,
const nsString& aName,
const nsString& aNamedCurve)
: KeyAlgorithm(aGlobal, aName)
, mNamedCurve(aNamedCurve)
{}
~EcKeyAlgorithm()
{}
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void GetNamedCurve(nsString& aRetVal) const
{
aRetVal.Assign(mNamedCurve);
}
virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
JSStructuredCloneReader* aReader);
protected:
nsString mNamedCurve;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_EcKeyAlgorithm_h

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/KeyAlgorithm.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/dom/AesKeyAlgorithm.h"
#include "mozilla/dom/EcKeyAlgorithm.h"
#include "mozilla/dom/HmacKeyAlgorithm.h"
#include "mozilla/dom/RsaKeyAlgorithm.h"
#include "mozilla/dom/RsaHashedKeyAlgorithm.h"
@ -89,6 +90,10 @@ KeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader)
algorithm = AesKeyAlgorithm::Create(aGlobal, aReader);
break;
}
case SCTAG_ECKEYALG: {
algorithm = EcKeyAlgorithm::Create(aGlobal, aReader);
break;
}
case SCTAG_HMACKEYALG: {
algorithm = HmacKeyAlgorithm::Create(aGlobal, aReader);
break;

View File

@ -24,6 +24,7 @@ class KeyAlgorithm;
enum KeyAlgorithmStructuredCloneTags {
SCTAG_KEYALG,
SCTAG_AESKEYALG,
SCTAG_ECKEYALG,
SCTAG_HMACKEYALG,
SCTAG_RSAKEYALG,
SCTAG_RSAHASHEDKEYALG

View File

@ -26,6 +26,7 @@
#define WEBCRYPTO_ALG_RSAES_PKCS1 "RSAES-PKCS1-v1_5"
#define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5"
#define WEBCRYPTO_ALG_RSA_OAEP "RSA-OAEP"
#define WEBCRYPTO_ALG_ECDH "ECDH"
// WebCrypto key formats
#define WEBCRYPTO_KEY_FORMAT_RAW "raw"
@ -48,6 +49,11 @@
#define WEBCRYPTO_KEY_USAGE_WRAPKEY "wrapKey"
#define WEBCRYPTO_KEY_USAGE_UNWRAPKEY "unwrapKey"
// WebCrypto named curves
#define WEBCRYPTO_NAMED_CURVE_P256 "P-256"
#define WEBCRYPTO_NAMED_CURVE_P384 "P-384"
#define WEBCRYPTO_NAMED_CURVE_P521 "P-521"
// JWK key types
#define JWK_TYPE_SYMMETRIC "oct"
#define JWK_TYPE_RSA "RSA"
@ -86,6 +92,11 @@
// Define an unknown mechanism type
#define UNKNOWN_CK_MECHANISM CKM_VENDOR_DEFINED+1
// python security/pkix/tools/DottedOIDToCode.py id-ecDH 1.3.132.112
static const uint8_t id_ecDH[] = { 0x2b, 0x81, 0x04, 0x70 };
const SECItem SEC_OID_DATA_EC_DH = { siBuffer, (unsigned char*)id_ecDH,
PR_ARRAY_SIZE(id_ecDH) };
namespace mozilla {
namespace dom {
@ -176,11 +187,95 @@ MapAlgorithmNameToMechanism(const nsString& aName)
mechanism = CKM_RSA_PKCS;
} else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
mechanism = CKM_RSA_PKCS_OAEP;
} else if (aName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
mechanism = CKM_ECDH1_DERIVE;
}
return mechanism;
}
inline bool
NormalizeNamedCurveValue(const nsString& aNamedCurve, nsString& aDest)
{
if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P256)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
} else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P384)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
} else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P521)) {
aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
} else {
return false;
}
return true;
}
inline bool
CheckEncodedECParameters(const SECItem* aEcParams)
{
// Need at least two bytes for a valid ASN.1 encoding.
if (aEcParams->len < 2) {
return false;
}
// Check the ASN.1 tag.
if (aEcParams->data[0] != SEC_ASN1_OBJECT_ID) {
return false;
}
// OID tags are short, we never need more than one length byte.
if (aEcParams->data[1] >= 128) {
return false;
}
// Check that the SECItem's length is correct.
if (aEcParams->len != (unsigned)aEcParams->data[1] + 2) {
return false;
}
return true;
}
inline SECItem*
CreateECParamsForCurve(const nsString& aNamedCurve, PLArenaPool* aArena)
{
SECOidTag curveOIDTag;
if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
curveOIDTag = SEC_OID_SECG_EC_SECP256R1;
} else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
curveOIDTag = SEC_OID_SECG_EC_SECP384R1;
} else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
curveOIDTag = SEC_OID_SECG_EC_SECP521R1;
} else {
return nullptr;
}
// Retrieve curve data by OID tag.
SECOidData* oidData = SECOID_FindOIDByTag(curveOIDTag);
if (!oidData) {
return nullptr;
}
// Create parameters.
SECItem* params = ::SECITEM_AllocItem(aArena, nullptr, 2 + oidData->oid.len);
if (!params) {
return nullptr;
}
// Set parameters.
params->data[0] = SEC_ASN1_OBJECT_ID;
params->data[1] = oidData->oid.len;
memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
// Sanity check the params we just created.
if (!CheckEncodedECParameters(params)) {
return nullptr;
}
return params;
}
} // namespace dom
} // namespace mozilla

View File

@ -15,6 +15,7 @@
#include "mozilla/dom/CryptoBuffer.h"
#include "mozilla/dom/CryptoKey.h"
#include "mozilla/dom/CryptoKeyPair.h"
#include "mozilla/dom/EcKeyAlgorithm.h"
#include "mozilla/dom/HmacKeyAlgorithm.h"
#include "mozilla/dom/KeyAlgorithm.h"
#include "mozilla/dom/RsaHashedKeyAlgorithm.h"
@ -159,6 +160,8 @@ GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, nsString& aName)
aName.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1);
} else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSA_OAEP)) {
aName.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP);
} else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_ECDH)) {
aName.AssignLiteral(WEBCRYPTO_ALG_ECDH);
}
return NS_OK;
@ -255,6 +258,26 @@ GetKeySizeForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm,
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
inline bool
MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult)
{
switch (aOIDTag) {
case SEC_OID_SECG_EC_SECP256R1:
aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
break;
case SEC_OID_SECG_EC_SECP384R1:
aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
break;
case SEC_OID_SECG_EC_SECP521R1:
aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
break;
default:
return false;
}
return true;
}
// Helper function to clone data from an ArrayBuffer or ArrayBufferView object
inline bool
CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle<JSObject*> aSrc)
@ -1651,6 +1674,113 @@ private:
}
};
class ImportEcKeyTask : public ImportKeyTask
{
public:
ImportEcKeyTask(JSContext* aCx, const nsAString& aFormat,
const ObjectOrString& aAlgorithm, bool aExtractable,
const Sequence<nsString>& aKeyUsages)
{
ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
}
ImportEcKeyTask(JSContext* aCx, const nsAString& aFormat,
JS::Handle<JSObject*> aKeyData,
const ObjectOrString& aAlgorithm, bool aExtractable,
const Sequence<nsString>& aKeyUsages)
{
ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
if (NS_FAILED(mEarlyRv)) {
return;
}
SetKeyData(aCx, aKeyData);
}
private:
nsString mNamedCurve;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
// Import the key data itself
ScopedSECKEYPublicKey pubKey;
ScopedSECKEYPrivateKey privKey;
nsNSSShutDownPreventionLock locker;
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) && mJwk.mD.WasPassed()) {
// Private key import
privKey = CryptoKey::PrivateKeyFromJwk(mJwk, locker);
if (!privKey) {
return NS_ERROR_DOM_DATA_ERR;
}
mKey->SetPrivateKey(privKey.get());
mKey->SetType(CryptoKey::PRIVATE);
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
(mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
!mJwk.mD.WasPassed())) {
// Public key import
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
pubKey = CryptoKey::PublicKeyFromSpki(mKeyData, locker);
} else {
pubKey = CryptoKey::PublicKeyFromJwk(mJwk, locker);
}
if (!pubKey) {
return NS_ERROR_DOM_DATA_ERR;
}
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// Construct the OID tag.
SECItem oid = { siBuffer, nullptr, 0 };
oid.len = pubKey->u.ec.DEREncodedParams.data[1];
oid.data = pubKey->u.ec.DEREncodedParams.data + 2;
// Find a matching and supported named curve.
if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
}
mKey->SetPublicKey(pubKey.get());
mKey->SetType(CryptoKey::PUBLIC);
} else {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
// Extract 'crv' parameter from JWKs.
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
}
return NS_OK;
}
virtual nsresult AfterCrypto() MOZ_OVERRIDE
{
// Check permissions for the requested operation
if (mKey->GetKeyType() == CryptoKey::PRIVATE &&
mKey->HasUsageOtherThan(CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY)) {
return NS_ERROR_DOM_DATA_ERR;
}
nsIGlobalObject* global = mKey->GetParentObject();
mKey->SetAlgorithm(new EcKeyAlgorithm(global, mAlgName, mNamedCurve));
if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
return NS_ERROR_DOM_DATA_ERR;
}
return NS_OK;
}
};
class ExportKeyTask : public WebCryptoTask
{
public:
@ -1997,6 +2127,24 @@ public:
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
RootedDictionary<EcKeyGenParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
if (!NormalizeNamedCurveValue(params.mNamedCurve.Value(), mNamedCurve)) {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
// Create algorithm.
algorithm = new EcKeyAlgorithm(global, algName, mNamedCurve);
mKeyPair->PublicKey()->SetAlgorithm(algorithm);
mKeyPair->PrivateKey()->SetAlgorithm(algorithm);
mMechanism = CKM_EC_KEY_PAIR_GEN;
} else {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
@ -2010,6 +2158,9 @@ public:
algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
publicAllowedUsages = 0;
}
mKeyPair->PrivateKey()->SetExtractable(aExtractable);
@ -2041,6 +2192,7 @@ private:
PK11RSAGenParams mRsaParams;
ScopedSECKEYPublicKey mPublicKey;
ScopedSECKEYPrivateKey mPrivateKey;
nsString mNamedCurve;
virtual void ReleaseNSSResources() MOZ_OVERRIDE
{
@ -2054,9 +2206,26 @@ private:
MOZ_ASSERT(slot.get());
void* param;
ScopedPLArenaPool arena;
switch (mMechanism) {
case CKM_RSA_PKCS_KEY_PAIR_GEN: param = &mRsaParams; break;
default: return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
case CKM_RSA_PKCS_KEY_PAIR_GEN:
param = &mRsaParams;
break;
case CKM_EC_KEY_PAIR_GEN: {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
param = CreateECParamsForCurve(mNamedCurve, arena.get());
if (!param) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
break;
}
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
SECKEYPublicKey* pubKey = nullptr;
@ -2202,17 +2371,18 @@ private:
}
};
class DerivePbkdfKeyTask : public DerivePbkdfBitsTask
template<class DeriveBitsTask>
class DeriveKeyTask : public DeriveBitsTask
{
public:
DerivePbkdfKeyTask(JSContext* aCx,
const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
const ObjectOrString& aDerivedKeyType, bool aExtractable,
const Sequence<nsString>& aKeyUsages)
: DerivePbkdfBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
DeriveKeyTask(JSContext* aCx,
const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
const ObjectOrString& aDerivedKeyType, bool aExtractable,
const Sequence<nsString>& aKeyUsages)
: DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
, mResolved(false)
{
if (NS_FAILED(mEarlyRv)) {
if (NS_FAILED(this->mEarlyRv)) {
return;
}
@ -2227,8 +2397,8 @@ protected:
private:
virtual void Resolve() MOZ_OVERRIDE {
mTask->SetKeyData(mResult);
mTask->DispatchWithPromise(mResultPromise);
mTask->SetKeyData(this->mResult);
mTask->DispatchWithPromise(this->mResultPromise);
mResolved = true;
}
@ -2241,6 +2411,116 @@ private:
}
};
class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask
{
public:
DeriveEcdhBitsTask(JSContext* aCx,
const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
: mLength(aLength),
mPrivKey(aKey.GetPrivateKey())
{
Init(aCx, aAlgorithm, aKey);
}
DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
: mPrivKey(aKey.GetPrivateKey())
{
mEarlyRv = GetKeySizeForAlgorithm(aCx, aTargetAlgorithm, mLength);
if (NS_SUCCEEDED(mEarlyRv)) {
Init(aCx, aAlgorithm, aKey);
}
}
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey)
{
// Check that we have a private key.
if (!mPrivKey) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
// Length must be a multiple of 8 bigger than zero.
if (mLength == 0 || mLength % 8) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
mLength = mLength >> 3; // bits to bytes
// Retrieve the peer's public key.
RootedDictionary<EcdhKeyDeriveParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv) || !params.mPublic.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
CryptoKey* publicKey = params.mPublic.Value();
mPubKey = publicKey->GetPublicKey();
if (!mPubKey) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
nsRefPtr<KeyAlgorithm> publicAlgorithm = publicKey->Algorithm();
// Given public key must be an ECDH key.
nsString algName;
publicAlgorithm->GetName(algName);
if (!algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
// Both keys must use the same named curve.
nsString curve1, curve2;
static_cast<EcKeyAlgorithm*>(aKey.Algorithm())->GetNamedCurve(curve1);
static_cast<EcKeyAlgorithm*>(publicAlgorithm.get())->GetNamedCurve(curve2);
if (!curve1.Equals(curve2)) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
}
private:
size_t mLength;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF(
mPrivKey, mPubKey, PR_FALSE, nullptr, nullptr, CKM_ECDH1_DERIVE,
CKM_CONCATENATE_DATA_AND_BASE, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
if (!symKey.get()) {
return NS_ERROR_DOM_OPERATION_ERR;
}
nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey));
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// This doesn't leak, because the SECItem* returned by PK11_GetKeyData
// just refers to a buffer managed by symKey. The assignment copies the
// data, so mResult manages one copy, while symKey manages another.
ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey));
if (mLength > mResult.Length()) {
return NS_ERROR_DOM_DATA_ERR;
}
if (!mResult.SetLength(mLength)) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
return NS_OK;
}
};
template<class KeyEncryptTask>
class WrapKeyTask : public ExportKeyTask
{
@ -2437,6 +2717,9 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx,
algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
return new ImportEcKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
@ -2474,7 +2757,8 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx,
return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP)) {
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
@ -2498,8 +2782,15 @@ WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
}
if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
return new DerivePbkdfKeyTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType,
aExtractable, aKeyUsages);
return new DeriveKeyTask<DerivePbkdfBitsTask>(aCx, aAlgorithm, aBaseKey,
aDerivedKeyType, aExtractable,
aKeyUsages);
}
if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
return new DeriveKeyTask<DeriveEcdhBitsTask>(aCx, aAlgorithm, aBaseKey,
aDerivedKeyType, aExtractable,
aKeyUsages);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
@ -2523,6 +2814,10 @@ WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx,
return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
}
if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}

View File

@ -10,6 +10,7 @@ EXPORTS.mozilla.dom += [
'CryptoBuffer.h',
'CryptoKey.h',
'CryptoKeyPair.h',
'EcKeyAlgorithm.h',
'HmacKeyAlgorithm.h',
'KeyAlgorithm.h',
'RsaHashedKeyAlgorithm.h',
@ -23,6 +24,7 @@ UNIFIED_SOURCES += [
'CryptoBuffer.cpp',
'CryptoKey.cpp',
'CryptoKeyPair.cpp',
'EcKeyAlgorithm.cpp',
'HmacKeyAlgorithm.cpp',
'KeyAlgorithm.cpp',
'RsaHashedKeyAlgorithm.cpp',

View File

@ -452,5 +452,128 @@ tv = {
"c026b58a21e1e5ec412ff241b436043e29173f1dc6cb943c09742de989547288" +
"0416021442c6ee70beb7465928a1efe692d2281b8f7b53d6"
)
}
},
// KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init.fax [EC]
// <http://csrc.nist.gov/groups/STM/cavp/documents/keymgmt/kastestvectors.zip>
ecdh_p256: {
jwk_pub: {
kty: "EC",
crv: "P-256",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8"
},
jwk_priv: {
kty: "EC",
crv: "P-256",
d: "qq_LEzeJpR00KM5DQvL2MNtJcbi0KcGVcoPIHNnwm2A",
x: "FNwJHA-FwnSx5tKXFV_iLN408gbKUHRV06WnQlzTdN4",
y: "is9pWAaneK4RdxmdLfsq5IwizDmUS2w8OGS99sKm3ek"
},
// vector with algorithm = id-ecDH
spki: util.hex2abv(
"3056301006042b81047006082a8648ce3d030107034200045ce7b86e3b326604" +
"03e63712ef0998deae1027faec3c1be9f76f934dfeb58e98f4cf075b39405dd1" +
"f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
),
// vector with algorithm = id-ecPublicKey
spki_id_ecpk: util.hex2abv(
"3059301306072a8648ce3d020106082a8648ce3d030107034200045ce7b86e3b" +
"32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e98f4cf075b39" +
"405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
),
secret: util.hex2abv(
"35669cd5c244ba6c1ea89b8802c3d1db815cd769979072e6556eb98548c65f7d"
)
},
// KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init.fax [ED]
// <http://csrc.nist.gov/groups/STM/cavp/documents/keymgmt/kastestvectors.zip>
ecdh_p384: {
jwk_pub: {
kty: "EC",
crv: "P-384",
x: "YoV6fhCph4kyt7sUkqiZOtbRs0rF6etPqlnrn1nzSB95NElaw4uTK7Pn2nlFFqqH",
y: "bf3tRz6icq3-W6hhmoqDTBKjdOQUJ5xHr5kX4X-h5MZk_P_nCrG3IUVl1SAbhWDw"
},
jwk_priv: {
kty: "EC",
crv: "P-384",
d: "RT8f0pRw4CL1Tgk4rwuNnNbFoQBNTTBkr7WVLLm4fDA3boYZpNB_t-rbMVLx0CRp",
x: "_XwhXRnOzEfCsWIRCz3QLClaDkigQFvXmqYNdh/7vJdADykPbfGi1VgAu3XJdXoD",
y: "S1P_FBCXYGE-5VPvTCRnFT7bPIPmUPV9qKTM24TQFYEUgIDfzCLsyGCWK-rhP6jU"
},
secret: util.hex2abv(
"a3d28aa18f905a48a5f166b4ddbf5f6b499e43858ccdd80b869946aba2c5d461" +
"db6a1e5b1137687801878ff0f8d9a7b3"
)
},
// KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init.fax [EE]
// <http://csrc.nist.gov/groups/STM/cavp/documents/keymgmt/kastestvectors.zip>
ecdh_p521: {
jwk_pub: {
kty: "EC",
crv: "P-521",
x: "AeCLgRZ-BPqfhq4jt409-E26VHW5l29q74cHbIbQiS_-Gcqdo-087jHdPXUksGpr" +
"Nyp_RcTZd94t3peXzQziQIqo",
y: "AZIAp8QVnU9hBOkLScv0dy540uGtBWHkWj4DGh-Exh4iWZ0E-YBS8-HVx2eB-nfG" +
"AGEy4-BzfpFFlfidOS1Tg77J"
},
jwk_priv: {
kty: "EC",
crv: "P-521",
d: "ABtsfkDGFarQU4kb7e2gPszGCTT8GLDaaJbFQenFZce3qp_dh0qZarXHKBZ-BVic" +
"NeIW5Sk661UoNfwykSvmh77S",
x: "AcD_6Eb4A-8QdUM70c6F0WthN1kvV4fohS8QHbod6B4y1ZDU54mQuCR-3IBjcV1c" +
"oh18uxbyUn5szMuCgjZUiD0y",
y: "AU3WKJffztkhAQetBXaLvUSIHa87HMn8vZFB04lWipH-SrsrAu_4N-6iam0OD4EJ" +
"0kOMH8iEh7yuivaKsFRzm2-m"
},
secret: util.hex2abv(
"00561eb17d856552c21b8cbe7d3d60d1ea0db738b77d4050fa2dbd0773edc395" +
"09854d9e30e843964ed3fd303339e338f31289120a38f94e9dc9ff7d4b3ea8f2" +
"5e01"
)
},
// Some test vectors that we should fail to import.
ecdh_p256_negative: {
// The given curve doesn't exist / isn't supported.
jwk_bad_crv: {
kty: "EC",
crv: "P-123",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8"
},
// The crv parameter is missing.
jwk_missing_crv: {
kty: "EC",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8"
},
// The X coordinate is missing.
jwk_missing_x: {
kty: "EC",
crv: "P-256",
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8"
},
// The Y coordinate is missing.
jwk_missing_y: {
kty: "EC",
crv: "P-256",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
}
},
}

View File

@ -1867,3 +1867,389 @@ TestArray.addTest(
.then(complete(that), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Generate an ECDH key for named curve P-256",
function() {
var that = this;
var alg = { name: "ECDH", namedCurve: "P-256" };
crypto.subtle.generateKey(alg, false, ["deriveKey", "deriveBits"]).then(
complete(that, function(x) {
return exists(x.publicKey) &&
(x.publicKey.algorithm.name == alg.name) &&
(x.publicKey.algorithm.namedCurve == alg.namedCurve) &&
(x.publicKey.type == "public") &&
x.publicKey.extractable &&
(x.publicKey.usages.length == 0) &&
exists(x.privateKey) &&
(x.privateKey.algorithm.name == alg.name) &&
(x.privateKey.algorithm.namedCurve == alg.namedCurve) &&
(x.privateKey.type == "private") &&
!x.privateKey.extractable &&
(x.privateKey.usages.length == 2) &&
(x.privateKey.usages[0] == "deriveKey") &&
(x.privateKey.usages[1] == "deriveBits");
}),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Generate an ECDH key and derive some bits",
function() {
var that = this;
var alg = { name: "ECDH", namedCurve: "P-256" };
var pair;
function setKeyPair(x) { pair = x; }
function doDerive(n) {
return function (x) {
var alg = { name: "ECDH", public: pair.publicKey };
return crypto.subtle.deriveBits(alg, pair.privateKey, n * 8);
}
}
crypto.subtle.generateKey(alg, false, ["deriveBits"])
.then(setKeyPair, error(that))
.then(doDerive(2), error(that))
.then(function (x) {
// Deriving less bytes works.
if (x.byteLength != 2) {
throw "should have derived two bytes";
}
})
// Deriving more than the curve yields doesn't.
.then(doDerive(33), error(that))
.then(error(that), complete(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Test that ECDH deriveBits() fails when the public key is not an ECDH key",
function() {
var that = this;
var pubKey, privKey;
function setPub(x) { pubKey = x.publicKey; }
function setPriv(x) { privKey = x.privateKey; }
function doGenerateP256() {
var alg = { name: "ECDH", namedCurve: "P-256" };
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
}
function doGenerateRSA() {
var alg = {
name: "RSA-OAEP",
hash: "SHA-256",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01])
};
return crypto.subtle.generateKey(alg, false, ["encrypt"])
}
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, 16);
}
doGenerateP256()
.then(setPriv, error(that))
.then(doGenerateRSA, error(that))
.then(setPub, error(that))
.then(doDerive, error(that))
.then(error(that), complete(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Test that ECDH deriveBits() fails when the given keys' curves don't match",
function() {
var that = this;
var pubKey, privKey;
function setPub(x) { pubKey = x.publicKey; }
function setPriv(x) { privKey = x.privateKey; }
function doGenerateP256() {
var alg = { name: "ECDH", namedCurve: "P-256" };
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
}
function doGenerateP384() {
var alg = { name: "ECDH", namedCurve: "P-384" };
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
}
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, 16);
}
doGenerateP256()
.then(setPriv, error(that))
.then(doGenerateP384, error(that))
.then(setPub, error(that))
.then(doDerive, error(that))
.then(error(that), complete(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"JWK import an ECDH public and private key and derive bits (P-256)",
function () {
var that = this;
var alg = { name: "ECDH" };
var pubKey, privKey;
function setPub(x) { pubKey = x; }
function setPriv(x) { privKey = x; }
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p256.secret.byteLength * 8);
}
Promise.all([
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveBits"])
.then(setPriv, error(that)),
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, alg, false, ["deriveBits"])
.then(setPub, error(that))
]).then(doDerive, error(that))
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"JWK import an ECDH public and private key and derive bits (P-384)",
function () {
var that = this;
var alg = { name: "ECDH" };
var pubKey, privKey;
function setPub(x) { pubKey = x; }
function setPriv(x) { privKey = x; }
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p384.secret.byteLength * 8);
}
Promise.all([
crypto.subtle.importKey("jwk", tv.ecdh_p384.jwk_priv, alg, false, ["deriveBits"])
.then(setPriv, error(that)),
crypto.subtle.importKey("jwk", tv.ecdh_p384.jwk_pub, alg, false, ["deriveBits"])
.then(setPub, error(that))
]).then(doDerive, error(that))
.then(memcmp_complete(that, tv.ecdh_p384.secret), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"JWK import an ECDH public and private key and derive bits (P-521)",
function () {
var that = this;
var alg = { name: "ECDH" };
var pubKey, privKey;
function setPub(x) { pubKey = x; }
function setPriv(x) { privKey = x; }
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p521.secret.byteLength * 8);
}
Promise.all([
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"])
.then(setPriv, error(that)),
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"])
.then(setPub, error(that))
]).then(doDerive, error(that))
.then(memcmp_complete(that, tv.ecdh_p521.secret), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"JWK import/export roundtrip with ECDH (P-256)",
function () {
var that = this;
var alg = { name: "ECDH" };
var pubKey, privKey;
function setPub(x) { pubKey = x; }
function setPriv(x) { privKey = x; }
function doExportPub() {
return crypto.subtle.exportKey("jwk", pubKey);
}
function doExportPriv() {
return crypto.subtle.exportKey("jwk", privKey);
}
Promise.all([
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, true, ["deriveBits"])
.then(setPriv, error(that)),
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, alg, true, ["deriveBits"])
.then(setPub, error(that))
]).then(doExportPub, error(that))
.then(function (x) {
var tp = tv.ecdh_p256.jwk_pub;
if ((tp.kty != x.kty) &&
(tp.crv != x.crv) &&
(tp.x != x.x) &&
(tp.y != x.y)) {
throw "exported public key doesn't match";
}
}, error(that))
.then(doExportPriv, error(that))
.then(complete(that, function (x) {
var tp = tv.ecdh_p256.jwk_priv;
return (tp.kty == x.kty) &&
(tp.crv == x.crv) &&
(tp.d == x.d) &&
(tp.x == x.x) &&
(tp.y == x.y);
}), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Test that importing bad JWKs fails",
function () {
var that = this;
var alg = { name: "ECDH" };
var tvs = tv.ecdh_p256_negative;
function doTryImport(jwk) {
return function () {
return crypto.subtle.importKey("jwk", jwk, alg, false, ["deriveBits"]);
}
}
doTryImport(tvs.jwk_bad_crv)()
.then(error(that), doTryImport(tvs.jwk_missing_crv))
.then(error(that), doTryImport(tvs.jwk_missing_x))
.then(error(that), doTryImport(tvs.jwk_missing_y))
.then(error(that), complete(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Derive an HMAC key from two ECDH keys and test sign/verify",
function() {
var that = this;
var alg = { name: "ECDH" };
var algDerived = { name: "HMAC", hash: {name: "SHA-1"} };
var pubKey, privKey;
function setPub(x) { pubKey = x; }
function setPriv(x) { privKey = x; }
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveKey(alg, privKey, algDerived, false, ["sign", "verify"])
.then(function (x) {
if (!hasKeyFields(x)) {
throw "Invalid key; missing field(s)";
}
// 512 bit is the default for HMAC-SHA1.
if (x.algorithm.length != 512) {
throw "Invalid key; incorrect length";
}
return x;
});
}
function doSignAndVerify(x) {
var data = crypto.getRandomValues(new Uint8Array(1024));
return crypto.subtle.sign("HMAC", x, data)
.then(function (sig) {
return crypto.subtle.verify("HMAC", x, sig, data);
});
}
Promise.all([
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"])
.then(setPriv, error(that)),
crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"])
.then(setPub, error(that))
]).then(doDerive, error(that))
.then(doSignAndVerify, error(that))
.then(complete(that), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"SPKI import/export of public ECDH keys (P-256)",
function () {
var that = this;
var alg = { name: "ECDH" };
var keys = ["spki", "spki_id_ecpk"];
function doImport(key) {
return crypto.subtle.importKey("spki", tv.ecdh_p256[key], alg, true, ["deriveBits"]);
}
function doExport(x) {
return crypto.subtle.exportKey("spki", x);
}
function nextKey() {
var key = keys.shift();
var imported = doImport(key);
var derived = imported.then(doExport);
return derived.then(function (x) {
if (!util.memcmp(x, tv.ecdh_p256.spki)) {
throw "exported key is invalid";
}
if (keys.length) {
return nextKey();
}
});
}
nextKey().then(complete(that), error(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"SPKI/JWK import ECDH keys (P-256) and derive a known secret",
function () {
var that = this;
var alg = { name: "ECDH" };
var pubKey, privKey;
function setPub(x) { pubKey = x; }
function setPriv(x) { privKey = x; }
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p256.secret.byteLength * 8);
}
Promise.all([
crypto.subtle.importKey("spki", tv.ecdh_p256.spki, alg, false, ["deriveBits"])
.then(setPub),
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveBits"])
.then(setPriv)
]).then(doDerive)
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
}
);

View File

@ -25,6 +25,7 @@
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TextEvents.h"
@ -400,6 +401,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
NS_ERROR_DOM_INVALID_STATE_ERR);
NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
#ifdef NIGHTLY_BUILD
MOZ_RELEASE_ASSERT(!mozilla::ipc::ProcessingUrgentMessages());
#endif
// If we're dispatching an already created DOMEvent object, make
// sure it is initialized!
// If aTargets is non-null, the event isn't going to be dispatched.

View File

@ -565,6 +565,10 @@ ContentChild::Init(MessageLoop* aIOLoop,
Open(aChannel, aParentHandle, aIOLoop);
sSingleton = this;
// Make sure there's an nsAutoScriptBlocker on the stack when dispatching
// urgent messages.
GetIPCChannel()->BlockScripts();
#ifdef MOZ_X11
// Send the parent our X socket to act as a proxy reference for our X
// resources.

View File

@ -494,7 +494,7 @@ parent:
PNecko();
sync PScreenManager()
rpc PScreenManager()
returns (uint32_t numberOfScreens,
float systemDefaultScale,
bool success);

View File

@ -20,32 +20,32 @@ struct ScreenDetails {
double contentsScaleFactor;
};
sync protocol PScreenManager
rpc protocol PScreenManager
{
manager PContent;
parent:
sync Refresh()
rpc Refresh()
returns (uint32_t numberOfScreens,
float systemDefaultScale,
bool success);
sync ScreenRefresh(uint32_t aId)
rpc ScreenRefresh(uint32_t aId)
returns (ScreenDetails screen,
bool success);
sync GetPrimaryScreen()
rpc GetPrimaryScreen()
returns (ScreenDetails screen,
bool success);
sync ScreenForRect(int32_t aLeft,
rpc ScreenForRect(int32_t aLeft,
int32_t aTop,
int32_t aWidth,
int32_t aHeight)
returns (ScreenDetails screen,
bool success);
sync ScreenForBrowser(PBrowser aBrowser)
rpc ScreenForBrowser(PBrowser aBrowser)
returns (ScreenDetails screen,
bool success);

View File

@ -24,13 +24,13 @@ ScreenManagerParent::ScreenManagerParent(uint32_t* aNumberOfScreens,
MOZ_CRASH("Couldn't get nsIScreenManager from ScreenManagerParent.");
}
unused << RecvRefresh(aNumberOfScreens, aSystemDefaultScale, aSuccess);
unused << AnswerRefresh(aNumberOfScreens, aSystemDefaultScale, aSuccess);
}
bool
ScreenManagerParent::RecvRefresh(uint32_t* aNumberOfScreens,
float* aSystemDefaultScale,
bool* aSuccess)
ScreenManagerParent::AnswerRefresh(uint32_t* aNumberOfScreens,
float* aSystemDefaultScale,
bool* aSuccess)
{
*aSuccess = false;
@ -49,9 +49,9 @@ ScreenManagerParent::RecvRefresh(uint32_t* aNumberOfScreens,
}
bool
ScreenManagerParent::RecvScreenRefresh(const uint32_t& aId,
ScreenDetails* aRetVal,
bool* aSuccess)
ScreenManagerParent::AnswerScreenRefresh(const uint32_t& aId,
ScreenDetails* aRetVal,
bool* aSuccess)
{
*aSuccess = false;
@ -70,8 +70,8 @@ ScreenManagerParent::RecvScreenRefresh(const uint32_t& aId,
}
bool
ScreenManagerParent::RecvGetPrimaryScreen(ScreenDetails* aRetVal,
bool* aSuccess)
ScreenManagerParent::AnswerGetPrimaryScreen(ScreenDetails* aRetVal,
bool* aSuccess)
{
*aSuccess = false;
@ -91,12 +91,12 @@ ScreenManagerParent::RecvGetPrimaryScreen(ScreenDetails* aRetVal,
}
bool
ScreenManagerParent::RecvScreenForRect(const int32_t& aLeft,
const int32_t& aTop,
const int32_t& aWidth,
const int32_t& aHeight,
ScreenDetails* aRetVal,
bool* aSuccess)
ScreenManagerParent::AnswerScreenForRect(const int32_t& aLeft,
const int32_t& aTop,
const int32_t& aWidth,
const int32_t& aHeight,
ScreenDetails* aRetVal,
bool* aSuccess)
{
*aSuccess = false;
@ -116,9 +116,9 @@ ScreenManagerParent::RecvScreenForRect(const int32_t& aLeft,
}
bool
ScreenManagerParent::RecvScreenForBrowser(PBrowserParent* aBrowser,
ScreenDetails* aRetVal,
bool* aSuccess)
ScreenManagerParent::AnswerScreenForBrowser(PBrowserParent* aBrowser,
ScreenDetails* aRetVal,
bool* aSuccess)
{
*aSuccess = false;

View File

@ -21,29 +21,29 @@ class ScreenManagerParent : public PScreenManagerParent
bool* aSuccess);
~ScreenManagerParent() {};
virtual bool RecvRefresh(uint32_t* aNumberOfScreens,
float* aSystemDefaultScale,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool AnswerRefresh(uint32_t* aNumberOfScreens,
float* aSystemDefaultScale,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool RecvScreenRefresh(const uint32_t& aId,
ScreenDetails* aRetVal,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool AnswerScreenRefresh(const uint32_t& aId,
ScreenDetails* aRetVal,
bool* aSuccess) MOZ_OVERRIDE;
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
virtual bool RecvGetPrimaryScreen(ScreenDetails* aRetVal,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool AnswerGetPrimaryScreen(ScreenDetails* aRetVal,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool RecvScreenForRect(const int32_t& aLeft,
const int32_t& aTop,
const int32_t& aWidth,
const int32_t& aHeight,
ScreenDetails* aRetVal,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool AnswerScreenForRect(const int32_t& aLeft,
const int32_t& aTop,
const int32_t& aWidth,
const int32_t& aHeight,
ScreenDetails* aRetVal,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool RecvScreenForBrowser(PBrowserParent* aBrowser,
ScreenDetails* aRetVal,
bool* aSuccess);
virtual bool AnswerScreenForBrowser(PBrowserParent* aBrowser,
ScreenDetails* aRetVal,
bool* aSuccess);
private:
bool ExtractScreenDetails(nsIScreen* aScreen, ScreenDetails &aDetails);

View File

@ -41,6 +41,11 @@ interface RsaHashedKeyAlgorithm : RsaKeyAlgorithm {
readonly attribute KeyAlgorithm hash;
};
[NoInterfaceObject]
interface EcKeyAlgorithm : KeyAlgorithm {
readonly attribute NamedCurve namedCurve;
};
/***** Algorithm dictionaries *****/
@ -109,6 +114,10 @@ dictionary EcKeyGenParams : Algorithm {
NamedCurve namedCurve;
};
dictionary EcdhKeyDeriveParams : Algorithm {
CryptoKey public;
};
/***** JWK *****/

View File

@ -455,6 +455,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
*_retval = 0;
if (!nsContentUtils::IsSafeToRunScript()) {
nsContentUtils::WarnScriptWasIgnored(nullptr);
return NS_ERROR_FAILURE;
}

View File

@ -976,7 +976,8 @@ public:
*/
virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
virtual bool IsDualDrawTarget() { return false; }
virtual bool IsDualDrawTarget() const { return false; }
virtual bool IsTiledDrawTarget() const { return false; }
void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
mUserData.Add(key, userData, destroy);

View File

@ -1594,8 +1594,9 @@ DrawTargetCG::MarkChanged()
CGContextRef
BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
{
if (aDT->GetBackendType() == BackendType::COREGRAPHICS ||
aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) {
if ((aDT->GetBackendType() == BackendType::COREGRAPHICS ||
aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) &&
!aDT->IsTiledDrawTarget() && !aDT->IsDualDrawTarget()) {
DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
cgDT->MarkChanged();

View File

@ -1423,7 +1423,8 @@ cairo_t*
BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT)
{
if (aDT->GetBackendType() != BackendType::CAIRO ||
aDT->IsDualDrawTarget()) {
aDT->IsDualDrawTarget() ||
aDT->IsTiledDrawTarget()) {
return nullptr;
}
DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
@ -1445,7 +1446,8 @@ BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT,
cairo_t* aCairo)
{
if (aDT->GetBackendType() != BackendType::CAIRO ||
aDT->IsDualDrawTarget()) {
aDT->IsDualDrawTarget() ||
aDT->IsTiledDrawTarget()) {
return;
}
DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);

View File

@ -149,7 +149,7 @@ public:
return nullptr;
}
virtual bool IsDualDrawTarget()
virtual bool IsDualDrawTarget() const
{
return true;
}

View File

@ -21,12 +21,14 @@ DrawTargetTiled::Init(const TileSet& aTiles)
return false;
}
mTiles.resize(aTiles.mTileCount);
memcpy(&mTiles.front(), aTiles.mTiles, aTiles.mTileCount * sizeof(Tile));
for (size_t i = 0; i < mTiles.size(); i++) {
if (mTiles[0].mDrawTarget->GetFormat() != mTiles[i].mDrawTarget->GetFormat() ||
mTiles[0].mDrawTarget->GetBackendType() != mTiles[i].mDrawTarget->GetBackendType()) {
mTiles.reserve(aTiles.mTileCount);
for (size_t i = 0; i < aTiles.mTileCount; ++i) {
mTiles.push_back(aTiles.mTiles[i]);
if (!aTiles.mTiles[i].mDrawTarget) {
return false;
}
if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() ||
mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) {
return false;
}
uint32_t newXMost = max(mRect.XMost(),

View File

@ -21,6 +21,8 @@ public:
bool Init(const TileSet& mTiles);
virtual bool IsTiledDrawTarget() const { return true; }
virtual DrawTargetType GetType() const MOZ_OVERRIDE { return mTiles[0].mDrawTarget->GetType(); }
virtual BackendType GetBackendType() const { return mTiles[0].mDrawTarget->GetBackendType(); }
virtual TemporaryRef<SourceSurface> Snapshot();

View File

@ -128,6 +128,10 @@ D2D1_CHANNEL_SELECTOR D2DChannelSelector(uint32_t aMode)
TemporaryRef<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
{
if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
MOZ_CRASH("Incompatible draw target type!");
return nullptr;
}
switch (aDT->GetBackendType()) {
case BackendType::DIRECT2D1_1:
return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);

View File

@ -27,11 +27,11 @@ DIBTextureClient::~DIBTextureClient()
TemporaryRef<TextureClient>
DIBTextureClient::CreateSimilar(TextureFlags aFlags,
TextureAllocationFlags aAllocFlags) const
TextureAllocationFlags aAllocFlags) const
{
RefPtr<TextureClient> tex = new DIBTextureClient(mFormat, mFlags | aFlags);
if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) {
if (!tex->AllocateForSurface(mSize, aAllocFlags)) {
return nullptr;
}

View File

@ -506,6 +506,11 @@ TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion,
x += width;
}
AsDerived().PostValidate(aPaintRegion);
for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
AsDerived().UnlockTile(newRetainedTiles[i]);
}
// At this point, oldTileCount should be zero
NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");

View File

@ -130,6 +130,9 @@ private:
void ReleaseTile(SimpleTiledLayerTile aTile) { aTile.Release(); }
void SwapTiles(SimpleTiledLayerTile& aTileA, SimpleTiledLayerTile& aTileB) { std::swap(aTileA, aTileB); }
void PostValidate(const nsIntRegion& aPaintRegion) {}
void UnlockTile(SimpleTiledLayerTile aTile) {}
};
class SimpleTiledContentClient : public CompositableClient

View File

@ -547,13 +547,11 @@ TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
NS_WARNING("Failed to lock the tile's front buffer");
return;
}
TextureClientAutoUnlock autoFront(mFrontBuffer);
if (!mBackBuffer->Lock(OpenMode::OPEN_WRITE)) {
NS_WARNING("Failed to lock the tile's back buffer");
return;
}
TextureClientAutoUnlock autoBack(mBackBuffer);
// Copy the bounding rect of regionToCopy. As tiles are quite small, it
// is unlikely that we'd save much by copying each individual rect of the
@ -589,6 +587,9 @@ TileClient::DiscardFrontBuffer()
#endif
mManager->GetTexturePool(mFrontBuffer->GetFormat())->ReturnTextureClientDeferred(mFrontBuffer);
mFrontLock->ReadUnlock();
if (mFrontBuffer->IsLocked()) {
mFrontBuffer->Unlock();
}
mFrontBuffer = nullptr;
mFrontLock = nullptr;
}
@ -626,6 +627,9 @@ TileClient::DiscardBackBuffer()
#endif
}
mBackLock->ReadUnlock();
if (mBackBuffer->IsLocked()) {
mBackBuffer->Unlock();
}
mBackBuffer.Set(this, nullptr);
mBackLock = nullptr;
}
@ -784,8 +788,7 @@ ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
GetTileStart(paintBounds.YMost() - 1);
}
*/
if (useSinglePaintBuffer) {
if (useSinglePaintBuffer && !gfxPrefs::TiledDrawTargetEnabled()) {
nsRefPtr<gfxContext> ctxt;
const nsIntRect bounds = aPaintRegion.GetBounds();
@ -921,6 +924,35 @@ void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion &regi
}
}
void
ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion)
{
if (gfxPrefs::TiledDrawTargetEnabled() && mMoz2DTiles.size() > 0) {
gfx::TileSet tileset;
tileset.mTiles = &mMoz2DTiles[0];
tileset.mTileCount = mMoz2DTiles.size();
RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
drawTarget->SetTransform(Matrix());
RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
mCallback(mThebesLayer, ctx, aPaintRegion, DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
mMoz2DTiles.clear();
}
}
void
ClientTiledLayerBuffer::UnlockTile(TileClient aTile)
{
// We locked the back buffer, and flipped so we now need to unlock the front
if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
aTile.mFrontBuffer->Unlock();
}
if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
aTile.mBackBuffer->Unlock();
}
}
TileClient
ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
const nsIntPoint& aTileOrigin,
@ -958,10 +990,64 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())),
&createdTextureClient, !usingSinglePaintBuffer);
if (!backBuffer || !backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
NS_WARNING("Failed to lock tile TextureClient for updating.");
aTile.DiscardFrontBuffer();
aTile.DiscardBackBuffer();
// the back buffer may have been already locked in ValidateBackBufferFromFront
if (!backBuffer->IsLocked()) {
if (!backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
NS_WARNING("Failed to lock tile TextureClient for updating.");
aTile.DiscardFrontBuffer();
return aTile;
}
}
if (gfxPrefs::TiledDrawTargetEnabled()) {
nsIntRegionRectIterator it(aDirtyRegion);
for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) {
gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x,
dirtyRect->y - aTileOrigin.y,
dirtyRect->width,
dirtyRect->height);
drawRect.Scale(mResolution);
gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
drawRect.width,
drawRect.height);
gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
// Mark the newly updated area as invalid in the front buffer
aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
}
// The new buffer is now validated, remove the dirty region from it.
aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
offsetScaledDirtyRegion);
aTile.Flip();
if (createdTextureClient) {
if (!mCompositableClient->AddTextureClient(backBuffer)) {
NS_WARNING("Failed to add tile TextureClient.");
aTile.DiscardFrontBuffer();
aTile.DiscardBackBuffer();
return aTile;
}
}
if (backBuffer->HasInternalBuffer()) {
// If our new buffer has an internal buffer, we don't want to keep another
// TextureClient around unnecessarily, so discard the back-buffer.
aTile.DiscardBackBuffer();
}
// prepare an array of Moz2D tiles that will be painted into in PostValidate
gfx::Tile moz2DTile;
moz2DTile.mDrawTarget = backBuffer->BorrowDrawTarget();
moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
if (!moz2DTile.mDrawTarget) {
aTile.DiscardFrontBuffer();
return aTile;
}
mMoz2DTiles.push_back(moz2DTile);
return aTile;
}

View File

@ -424,6 +424,10 @@ protected:
const nsIntPoint& aTileRect,
const nsIntRegion& dirtyRect);
void PostValidate(const nsIntRegion& aPaintRegion);
void UnlockTile(TileClient aTile);
// If this returns true, we perform the paint operation into a single large
// buffer and copy it out to the tiles instead of calling PaintThebes() on
// each tile individually. Somewhat surprisingly, this turns out to be faster
@ -450,7 +454,8 @@ private:
RefPtr<gfx::DrawTarget> mSinglePaintDrawTarget;
nsIntPoint mSinglePaintBufferOffset;
SharedFrameMetricsHelper* mSharedFrameMetricsHelper;
// When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
std::vector<gfx::Tile> mMoz2DTiles;
/**
* Calculates the region to update in a single progressive update transaction.
* This employs some heuristics to update the most 'sensible' region to

View File

@ -154,6 +154,8 @@ protected:
void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
void UnlockTile(TileHost aTile) {}
void PostValidate(const nsIntRegion& aPaintRegion) {}
private:
CSSToParentLayerScale mFrameResolution;
bool mHasDoubleBufferedTiles;

View File

@ -192,7 +192,7 @@ TextureClientD3D11::CreateSimilar(TextureFlags aFlags,
{
RefPtr<TextureClient> tex = new TextureClientD3D11(mFormat, mFlags | aFlags);
if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) {
if (!tex->AllocateForSurface(mSize, aAllocFlags)) {
return nullptr;
}

View File

@ -89,7 +89,10 @@ public:
IDirect3DDevice9* device() const
{
return mDeviceManager
// If the reset counts don't match it means the device was lost and we are
// in the process of recreating a new one or will be soon.
// cf. comment in EnsureSwapChain.
return mDeviceManager && mDeviceResetCount == mDeviceManager->GetDeviceResetCount()
? mDeviceManager->device()
: nullptr;
}

View File

@ -52,6 +52,9 @@ public:
{
Update(aNewValidRegion, aPaintRegion);
}
void UnlockTile(TestTiledLayerTile aTile) {}
void PostValidate(const nsIntRegion& aPaintRegion) {}
};
TEST(TiledLayerBuffer, TileConstructor) {

View File

@ -225,6 +225,7 @@ private:
DECL_GFX_PREF(Once, "layers.enable-tiles", LayersTilesEnabled, bool, false);
DECL_GFX_PREF(Once, "layers.simple-tiles", LayersUseSimpleTiles, bool, false);
DECL_GFX_PREF(Once, "layers.force-per-tile-drawing", PerTileDrawing, bool, false);
DECL_GFX_PREF(Once, "layers.tiled-drawtarget.enabled", TiledDrawTargetEnabled, bool, false);
// We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
// screen size does not align nicely to the default tile size. Although layers can be any size,
// they are often the same size as the screen, especially for width.

View File

@ -13,6 +13,7 @@
#include "mozilla/Move.h"
#include "nsDebug.h"
#include "nsISupportsImpl.h"
#include "nsContentUtils.h"
// Undo the damage done by mozzconf.h
#undef compress
@ -36,6 +37,8 @@ struct RunnableMethodTraits<mozilla::ipc::MessageChannel>
DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__); \
} while (0)
static uintptr_t gDispatchingUrgentMessageCount;
namespace mozilla {
namespace ipc {
@ -186,6 +189,31 @@ private:
CxxStackFrame& operator=(const CxxStackFrame&) MOZ_DELETE;
};
namespace {
class MOZ_STACK_CLASS MaybeScriptBlocker {
public:
MaybeScriptBlocker(MessageChannel *aChannel
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mBlocked(aChannel->ShouldBlockScripts())
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (mBlocked) {
nsContentUtils::AddScriptBlocker();
}
}
~MaybeScriptBlocker() {
if (mBlocked) {
nsContentUtils::RemoveScriptBlocker();
}
}
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
bool mBlocked;
};
} /* namespace {} */
MessageChannel::MessageChannel(MessageListener *aListener)
: mListener(aListener),
mChannelState(ChannelClosed),
@ -205,7 +233,8 @@ MessageChannel::MessageChannel(MessageListener *aListener)
mDispatchingUrgentMessageCount(0),
mRemoteStackDepthGuess(false),
mSawInterruptOutMsg(false),
mAbortOnError(false)
mAbortOnError(false),
mBlockScripts(false)
{
MOZ_COUNT_CTOR(ipc::MessageChannel);
@ -566,6 +595,9 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
bool
MessageChannel::Send(Message* aMsg, Message* aReply)
{
// See comment in DispatchUrgentMessage.
MaybeScriptBlocker scriptBlocker(this);
// Sanity checks.
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
@ -594,6 +626,9 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
bool
MessageChannel::UrgentCall(Message* aMsg, Message* aReply)
{
// See comment in DispatchUrgentMessage.
MaybeScriptBlocker scriptBlocker(this);
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
IPC_ASSERT(mSide == ParentSide, "cannot send urgent requests from child");
@ -623,6 +658,9 @@ MessageChannel::UrgentCall(Message* aMsg, Message* aReply)
bool
MessageChannel::RPCCall(Message* aMsg, Message* aReply)
{
// See comment in DispatchUrgentMessage.
MaybeScriptBlocker scriptBlocker(this);
AssertWorkerThread();
mMonitor->AssertNotCurrentThreadOwns();
IPC_ASSERT(mSide == ChildSide, "cannot send rpc messages from parent");
@ -1099,9 +1137,41 @@ MessageChannel::DispatchUrgentMessage(const Message& aMsg)
Message *reply = nullptr;
MOZ_ASSERT(NS_IsMainThread());
// We don't want to run any code that might run a nested event loop here, so
// we avoid running event handlers. Once we've sent the response to the
// urgent message, it's okay to run event handlers again since the parent is
// no longer blocked.
//
// We also put script blockers at the start of every synchronous send
// call. That way we won't run any scripts while waiting for a response to
// another message. Running scripts could cause us to send more sync
// messages, and the other side wouldn't know what to do if it received a
// sync message while dispatching another sync message. (In practice, the
// other side would queue the second sync message, while we would need it to
// dispatch that message before sending the reply to the original sync
// message. Otherwise the replies would come out of order.)
//
// We omit the script blocker for InterruptCall since interrupt messages are
// designed to handle this sort of re-entry. (For example, if the child
// sends an intr message to the parent, the child will process any queued
// async messages from the parent while waiting for the intr response. In
// doing so, the child could trigger sync messages to be sent to the parent
// while the parent is still dispatching the intr message. If the parent
// sends an intr reply while the child is waiting for a sync response, the
// intr reply will be queued in mPending. Once the sync reply is received,
// InterruptCall will find the intr reply in mPending and run it.) The
// situation where we run event handlers while waiting for an intr reply is
// no different than the one where we process async messages while waiting
// for an intr reply.
MaybeScriptBlocker scriptBlocker(this);
gDispatchingUrgentMessageCount++;
mDispatchingUrgentMessageCount++;
Result rv = mListener->OnCallReceived(aMsg, reply);
mDispatchingUrgentMessageCount--;
gDispatchingUrgentMessageCount--;
if (!MaybeHandleError(rv, "DispatchUrgentMessage")) {
delete reply;
@ -1640,6 +1710,13 @@ MessageChannel::CloseWithError()
PostErrorNotifyTask();
}
void
MessageChannel::BlockScripts()
{
MOZ_ASSERT(NS_IsMainThread());
mBlockScripts = true;
}
void
MessageChannel::Close()
{
@ -1752,5 +1829,11 @@ MessageChannel::DumpInterruptStack(const char* const pfx) const
}
}
bool
ProcessingUrgentMessages()
{
return gDispatchingUrgentMessageCount > 0;
}
} // ipc
} // mozilla

View File

@ -89,6 +89,13 @@ class MessageChannel : HasResultCodes
mAbortOnError = true;
}
void BlockScripts();
bool ShouldBlockScripts() const
{
return mBlockScripts;
}
// Asynchronously send a message to the other side of the channel
bool Send(Message* aMsg);
@ -639,8 +646,14 @@ class MessageChannel : HasResultCodes
// Should the channel abort the process from the I/O thread when
// a channel error occurs?
bool mAbortOnError;
// Should we prevent scripts from running while dispatching urgent messages?
bool mBlockScripts;
};
bool
ProcessingUrgentMessages();
} // namespace ipc
} // namespace mozilla

View File

@ -8,10 +8,12 @@
#include "JavaScriptChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/ipc/MessageChannel.h"
#include "nsContentUtils.h"
#include "xpcprivate.h"
#include "jsfriendapi.h"
#include "nsCxPusher.h"
#include "AccessCheck.h"
using namespace JS;
using namespace mozilla;
@ -19,6 +21,17 @@ using namespace mozilla::jsipc;
using mozilla::AutoSafeJSContext;
#ifdef NIGHTLY_BUILD
static void
UrgentMessageCheck(JSContext *cx, HandleScript script)
{
// We're only allowed to enter chrome JS code while processing urgent
// messages.
if (ipc::ProcessingUrgentMessages())
MOZ_RELEASE_ASSERT(xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)));
}
#endif
static void
FinalizeChild(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data)
{
@ -31,6 +44,9 @@ JavaScriptChild::JavaScriptChild(JSRuntime *rt)
: JavaScriptShared(rt),
JavaScriptBase<PJavaScriptChild>(rt)
{
#ifdef NIGHTLY_BUILD
js::SetAssertOnScriptEntryHook(rt, UrgentMessageCheck);
#endif
}
JavaScriptChild::~JavaScriptChild()

View File

@ -30,5 +30,6 @@ LOCAL_INCLUDES += [
'/js/ipc',
'/js/public',
'/js/xpconnect/src',
'/js/xpconnect/wrappers',
]

View File

@ -43,7 +43,7 @@ namespace JS {
D(TOO_MUCH_MALLOC) \
D(ALLOC_TRIGGER) \
D(DEBUG_GC) \
D(TRANSPLANT) \
D(COMPARTMENT_REVIVED) \
D(RESET) \
D(OUT_OF_NURSERY) \
D(EVICT_NURSERY) \

View File

@ -2153,7 +2153,7 @@ dnl Quota support
MOZ_CHECK_HEADERS(sys/quota.h)
MOZ_CHECK_HEADERS(linux/quota.h)
AC_ARG_ENABLE(dtrace,
MOZ_ARG_ENABLE_BOOL(dtrace,
[ --enable-dtrace build with dtrace support if available (default=no)],
[enable_dtrace="yes"],)
if test "x$enable_dtrace" = "xyes"; then

View File

@ -192,8 +192,6 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
DebugOnly<JSRuntime *> rt = trc->runtime();
bool isGcMarkingTracer = IS_GC_MARKING_TRACER(trc);
JS_ASSERT_IF(isGcMarkingTracer && rt->gc.isManipulatingDeadZones(),
!thing->zone()->scheduledForDestruction);
JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
@ -225,6 +223,33 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
#endif
}
/*
* We only set the maybeAlive flag for objects and scripts. It's assumed that,
* if a compartment is alive, then it will have at least some live object or
* script it in. Even if we get this wrong, the worst that will happen is that
* scheduledForDestruction will be set on the compartment, which will cause some
* extra GC activity to try to free the compartment.
*/
template<typename T>
static inline void
SetMaybeAliveFlag(T *thing)
{
}
template<>
void
SetMaybeAliveFlag(JSObject *thing)
{
thing->compartment()->maybeAlive = true;
}
template<>
void
SetMaybeAliveFlag(JSScript *thing)
{
thing->compartment()->maybeAlive = true;
}
template<typename T>
static void
MarkInternal(JSTracer *trc, T **thingp)
@ -268,7 +293,7 @@ MarkInternal(JSTracer *trc, T **thingp)
return;
PushMarkStack(AsGCMarker(trc), thing);
thing->zone()->maybeAlive = true;
SetMaybeAliveFlag(thing);
} else {
trc->callback(trc, (void **)thingp, MapTypeToTraceKind<T>::kind);
trc->unsetTracingLocation();

View File

@ -662,7 +662,20 @@ GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
if (zone->isCollecting()) {
zone->maybeAlive = true;
// See the comment on SetMaybeAliveFlag to see why we only do this for
// objects and scripts. We rely on gray root buffering for this to work,
// but we only need to worry about uncollected dead compartments during
// incremental GCs (when we do gray root buffering).
switch (kind) {
case JSTRACE_OBJECT:
static_cast<JSObject *>(thing)->compartment()->maybeAlive = true;
break;
case JSTRACE_SCRIPT:
static_cast<JSScript *>(thing)->compartment()->maybeAlive = true;
break;
default:
break;
}
if (!zone->gcGrayRoots.append(root)) {
resetBufferedGrayRoots();
grayBufferState = GRAY_BUFFER_FAILED;

View File

@ -31,8 +31,6 @@ JS::Zone::Zone(JSRuntime *rt)
data(nullptr),
isSystem(false),
usedByExclusiveThread(false),
scheduledForDestruction(false),
maybeAlive(true),
active(false),
jitZone_(nullptr),
gcState_(NoGC),

View File

@ -286,11 +286,6 @@ struct Zone : public JS::shadow::Zone,
bool usedByExclusiveThread;
// These flags help us to discover if a compartment that shouldn't be alive
// manages to outlive a GC.
bool scheduledForDestruction;
bool maybeAlive;
// True when there are active frames.
bool active;

View File

@ -1092,8 +1092,6 @@ JS_TransplantObject(JSContext *cx, HandleObject origobj, HandleObject target)
RootedObject newIdentity(cx);
{
// Scope to make ~AutoMaybeTouchDeadZones do its GC before the return value is on the stack.
AutoMaybeTouchDeadZones agc(cx);
AutoDisableProxyCheck adpc(cx->runtime());
JSCompartment *destination = target->compartment();

View File

@ -65,6 +65,8 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options =
debugScopes(nullptr),
enumerators(nullptr),
compartmentStats(nullptr),
scheduledForDestruction(false),
maybeAlive(true),
jitCompartment_(nullptr)
{
runtime_->numCompartments++;

View File

@ -451,6 +451,11 @@ struct JSCompartment
/* Used by memory reporters and invalid otherwise. */
void *compartmentStats;
// These flags help us to discover if a compartment that shouldn't be alive
// manages to outlive a GC.
bool scheduledForDestruction;
bool maybeAlive;
private:
js::jit::JitCompartment *jitCompartment_;

View File

@ -63,6 +63,14 @@ js::ForgetSourceHook(JSRuntime *rt)
return Move(rt->sourceHook);
}
#ifdef NIGHTLY_BUILD
JS_FRIEND_API(void)
js::SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook)
{
rt->assertOnScriptEntryHook_ = hook;
}
#endif
JS_FRIEND_API(void)
JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
@ -942,8 +950,6 @@ JS::IncrementalObjectBarrier(JSObject *obj)
JS_ASSERT(!obj->zone()->runtimeFromMainThread()->isHeapMajorCollecting());
AutoMarkInDeadZone amn(obj->zone());
JSObject::writeBarrierPre(obj);
}
@ -957,13 +963,13 @@ JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
return;
gc::Cell *cell = static_cast<gc::Cell *>(ptr);
#ifdef DEBUG
Zone *zone = kind == JSTRACE_OBJECT
? static_cast<JSObject *>(cell)->zone()
: cell->tenuredZone();
JS_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting());
AutoMarkInDeadZone amn(zone);
#endif
if (kind == JSTRACE_OBJECT)
JSObject::writeBarrierPre(static_cast<JSObject*>(cell));

View File

@ -422,6 +422,13 @@ SetSourceHook(JSRuntime *rt, mozilla::UniquePtr<SourceHook> hook);
extern JS_FRIEND_API(mozilla::UniquePtr<SourceHook>)
ForgetSourceHook(JSRuntime *rt);
#ifdef NIGHTLY_BUILD
typedef void (*AssertOnScriptEntryHook)(JSContext *cx, JS::HandleScript script);
extern JS_FRIEND_API(void)
SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook);
#endif
extern JS_FRIEND_API(JS::Zone *)
GetCompartmentZone(JSCompartment *comp);

View File

@ -3149,14 +3149,14 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason)
isFull = false;
}
zone->scheduledForDestruction = false;
zone->maybeAlive = false;
zone->setPreservingCode(false);
}
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
JS_ASSERT(c->gcLiveArrayBuffers.empty());
c->marked = false;
c->scheduledForDestruction = false;
c->maybeAlive = false;
if (shouldPreserveJITCode(c, currentTime, reason))
c->zone()->setPreservingCode(true);
}
@ -3255,40 +3255,50 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason)
bufferGrayRoots();
/*
* This code ensures that if a zone is "dead", then it will be
* collected in this GC. A zone is considered dead if its maybeAlive
* This code ensures that if a compartment is "dead", then it will be
* collected in this GC. A compartment is considered dead if its maybeAlive
* flag is false. The maybeAlive flag is set if:
* (1) the zone has incoming cross-compartment edges, or
* (2) an object in the zone was marked during root marking, either
* (1) the compartment has incoming cross-compartment edges, or
* (2) an object in the compartment was marked during root marking, either
* as a black root or a gray root.
* If the maybeAlive is false, then we set the scheduledForDestruction flag.
* At any time later in the GC, if we try to mark an object whose
* zone is scheduled for destruction, we will assert.
* NOTE: Due to bug 811587, we only assert if gcManipulatingDeadCompartments
* is true (e.g., if we're doing a brain transplant).
* At the end of the GC, we look for compartments where
* scheduledForDestruction is true. These are compartments that were somehow
* "revived" during the incremental GC. If any are found, we do a special,
* non-incremental GC of those compartments to try to collect them.
*
* The purpose of this check is to ensure that a zone that we would
* normally destroy is not resurrected by a read barrier or an
* allocation. This might happen during a function like JS_TransplantObject,
* which iterates over all compartments, live or dead, and operates on their
* objects. See bug 803376 for details on this problem. To avoid the
* problem, we are very careful to avoid allocation and read barriers during
* JS_TransplantObject and the like. The code here ensures that we don't
* regress.
* Compartments can be revived for a variety of reasons. On reason is bug
* 811587, where a reflector that was dead can be revived by DOM code that
* still refers to the underlying DOM node.
*
* Note that there are certain cases where allocations or read barriers in
* dead zone are difficult to avoid. We detect such cases (via the
* gcObjectsMarkedInDeadCompartment counter) and redo any ongoing GCs after
* the JS_TransplantObject function has finished. This ensures that the dead
* zones will be cleaned up. See AutoMarkInDeadZone and
* AutoMaybeTouchDeadZones for details.
* Read barriers and allocations can also cause revival. This might happen
* during a function like JS_TransplantObject, which iterates over all
* compartments, live or dead, and operates on their objects. See bug 803376
* for details on this problem. To avoid the problem, we try to avoid
* allocation and read barriers during JS_TransplantObject and the like.
*/
/* Set the maybeAlive flag based on cross-compartment edges. */
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
Cell *dst = e.front().key().wrapped;
dst->tenuredZone()->maybeAlive = true;
const CrossCompartmentKey &key = e.front().key();
JSCompartment *dest;
switch (key.kind) {
case CrossCompartmentKey::ObjectWrapper:
case CrossCompartmentKey::DebuggerObject:
case CrossCompartmentKey::DebuggerSource:
case CrossCompartmentKey::DebuggerEnvironment:
dest = static_cast<JSObject *>(key.wrapped)->compartment();
break;
case CrossCompartmentKey::DebuggerScript:
dest = static_cast<JSScript *>(key.wrapped)->compartment();
break;
default:
dest = nullptr;
break;
}
if (dest)
dest->maybeAlive = true;
}
}
@ -3297,9 +3307,9 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason)
* during MarkRuntime.
*/
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
if (!zone->maybeAlive && !rt->isAtomsZone(zone))
zone->scheduledForDestruction = true;
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
if (!c->maybeAlive && !rt->isAtomsCompartment(c))
c->scheduledForDestruction = true;
}
foundBlackGrayEdges = false;
@ -4631,8 +4641,8 @@ GCRuntime::resetIncrementalGC(const char *reason)
case SWEEP:
marker.reset();
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
zone->scheduledForDestruction = false;
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
c->scheduledForDestruction = false;
/* Finish sweeping the current zone group, then abort. */
abortSweepAfterCurrentGroup = true;
@ -5151,13 +5161,30 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
if (poked && cleanUpEverything)
JS::PrepareForFullGC(rt);
/*
* This code makes an extra effort to collect compartments that we
* thought were dead at the start of the GC. See the large comment in
* beginMarkPhase.
*/
bool repeatForDeadZone = false;
if (incremental && incrementalState == NO_INCREMENTAL) {
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
if (c->scheduledForDestruction) {
incremental = false;
repeatForDeadZone = true;
reason = JS::gcreason::COMPARTMENT_REVIVED;
c->zone()->scheduleGC();
}
}
}
/*
* If we reset an existing GC, we need to start a new one. Also, we
* repeat GCs that happen during shutdown (the gcShouldCleanUpEverything
* case) until we can be sure that no additional garbage is created
* (which typically happens if roots are dropped during finalizers).
*/
repeat = (poked && cleanUpEverything) || wasReset;
repeat = (poked && cleanUpEverything) || wasReset || repeatForDeadZone;
} while (repeat);
if (incrementalState == NO_INCREMENTAL)
@ -5717,34 +5744,6 @@ ArenaLists::containsArena(JSRuntime *rt, ArenaHeader *needle)
}
AutoMaybeTouchDeadZones::AutoMaybeTouchDeadZones(JSContext *cx)
: runtime(cx->runtime()),
markCount(runtime->gc.objectsMarkedInDeadZonesCount()),
inIncremental(JS::IsIncrementalGCInProgress(runtime)),
manipulatingDeadZones(runtime->gc.isManipulatingDeadZones())
{
runtime->gc.setManipulatingDeadZones(true);
}
AutoMaybeTouchDeadZones::AutoMaybeTouchDeadZones(JSObject *obj)
: runtime(obj->compartment()->runtimeFromMainThread()),
markCount(runtime->gc.objectsMarkedInDeadZonesCount()),
inIncremental(JS::IsIncrementalGCInProgress(runtime)),
manipulatingDeadZones(runtime->gc.isManipulatingDeadZones())
{
runtime->gc.setManipulatingDeadZones(true);
}
AutoMaybeTouchDeadZones::~AutoMaybeTouchDeadZones()
{
runtime->gc.setManipulatingDeadZones(manipulatingDeadZones);
if (inIncremental && runtime->gc.objectsMarkedInDeadZonesCount() != markCount) {
JS::PrepareForFullGC(runtime);
js::GC(runtime, GC_NORMAL, JS::gcreason::TRANSPLANT);
}
}
AutoSuppressGC::AutoSuppressGC(ExclusiveContext *cx)
: suppressGC_(cx->perThreadData->suppressGC)
{

View File

@ -17,33 +17,6 @@ namespace js {
class Shape;
/*
* This auto class should be used around any code that might cause a mark bit to
* be set on an object in a dead zone. See AutoMaybeTouchDeadZones
* for more details.
*/
struct AutoMarkInDeadZone
{
explicit AutoMarkInDeadZone(JS::Zone *zone)
: zone(zone),
scheduled(zone->scheduledForDestruction)
{
gc::GCRuntime &gc = zone->runtimeFromMainThread()->gc;
if (gc.isManipulatingDeadZones() && zone->scheduledForDestruction) {
gc.incObjectsMarkedInDeadZone();
zone->scheduledForDestruction = false;
}
}
~AutoMarkInDeadZone() {
zone->scheduledForDestruction = scheduled;
}
private:
JS::Zone *zone;
bool scheduled;
};
inline Allocator *
ThreadSafeContext::allocator() const
{

View File

@ -2530,9 +2530,6 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
bool
JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
{
AutoMarkInDeadZone adc1(a->zone());
AutoMarkInDeadZone adc2(b->zone());
// Ensure swap doesn't cause a finalizer to not be run.
JS_ASSERT(IsBackgroundFinalized(a->tenuredGetAllocKind()) ==
IsBackgroundFinalized(b->tenuredGetAllocKind()));

View File

@ -44,8 +44,6 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, const Wrapper *hand
{
JS_ASSERT(parent);
AutoMarkInDeadZone amd(cx->zone());
RootedValue priv(cx, ObjectValue(*obj));
mozilla::Maybe<WrapperOptions> opts;
if (!options) {
@ -1035,8 +1033,6 @@ JS_FRIEND_API(bool)
js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
const CompartmentFilter &targetFilter)
{
AutoMaybeTouchDeadZones agc(cx);
AutoWrapperVector toRecompute(cx);
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {

View File

@ -304,34 +304,6 @@ JS_FRIEND_API(bool)
RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
const CompartmentFilter &targetFilter);
/*
* This auto class should be used around any code, such as brain transplants,
* that may touch dead zones. Brain transplants can cause problems
* because they operate on all compartments, whether live or dead. A brain
* transplant can cause a formerly dead object to be "reanimated" by causing a
* read or write barrier to be invoked on it during the transplant. In this way,
* a zone becomes a zombie, kept alive by repeatedly consuming
* (transplanted) brains.
*
* To work around this issue, we observe when mark bits are set on objects in
* dead zones. If this happens during a brain transplant, we do a full,
* non-incremental GC at the end of the brain transplant. This will clean up any
* objects that were improperly marked.
*/
struct JS_FRIEND_API(AutoMaybeTouchDeadZones)
{
// The version that takes an object just uses it for its runtime.
explicit AutoMaybeTouchDeadZones(JSContext *cx);
explicit AutoMaybeTouchDeadZones(JSObject *obj);
~AutoMaybeTouchDeadZones();
private:
JSRuntime *runtime;
unsigned markCount;
bool inIncremental;
bool manipulatingDeadZones;
};
} /* namespace js */
#endif /* jswrapper_h */

View File

@ -2047,7 +2047,7 @@ Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp)
for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
continue;
c->zone()->scheduledForDestruction = false;
c->scheduledForDestruction = false;
GlobalObject *global = c->maybeGlobal();
if (global) {
Rooted<GlobalObject*> rg(cx, global);
@ -2861,7 +2861,7 @@ Debugger::findAllGlobals(JSContext *cx, unsigned argc, Value *vp)
if (c->options().invisibleToDebugger())
continue;
c->zone()->scheduledForDestruction = false;
c->scheduledForDestruction = false;
GlobalObject *global = c->maybeGlobal();

View File

@ -374,6 +374,11 @@ js::RunScript(JSContext *cx, RunState &state)
{
JS_CHECK_RECURSION(cx, return false);
#ifdef NIGHTLY_BUILD
if (AssertOnScriptEntryHook hook = cx->runtime()->assertOnScriptEntryHook_)
(*hook)(cx, state.script());
#endif
SPSEntryMarker marker(cx->runtime(), state.script());
state.script()->ensureNonLazyCanonicalFunction(cx);

View File

@ -203,7 +203,7 @@ class RunState
return (GeneratorState *)this;
}
JSScript *script() const { return script_; }
JS::HandleScript script() const { return script_; }
virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx) = 0;
virtual void setReturnValue(Value v) = 0;

View File

@ -83,14 +83,7 @@ ProxyObject::initHandler(const BaseProxyHandler *handler)
static void
NukeSlot(ProxyObject *proxy, uint32_t slot)
{
Value old = proxy->getSlot(slot);
if (old.isMarkable()) {
Zone *zone = ZoneOfValue(old);
AutoMarkInDeadZone amd(zone);
proxy->setReservedSlot(slot, NullValue());
} else {
proxy->setReservedSlot(slot, NullValue());
}
proxy->setReservedSlot(slot, NullValue());
}
void

View File

@ -172,6 +172,9 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
emptyString(nullptr),
#ifdef NIGHTLY_BUILD
assertOnScriptEntryHook_(nullptr),
#endif
debugMode(false),
spsProfiler(thisFromCtor()),
profilingScripts(false),

View File

@ -993,6 +993,10 @@ struct JSRuntime : public JS::shadow::Runtime,
mozilla::UniquePtr<js::SourceHook> sourceHook;
#ifdef NIGHTLY_BUILD
js::AssertOnScriptEntryHook assertOnScriptEntryHook_;
#endif
/* If true, new compartments are initially in debug mode. */
bool debugMode;

View File

@ -356,9 +356,6 @@ XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
mozilla::Maybe<JSAutoCompartment> ac;
if (sciWrapper.GetFlags().WantPreCreate()) {
// PreCreate may touch dead compartments.
js::AutoMaybeTouchDeadZones agc(parent);
RootedObject plannedParent(cx, parent);
nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx,
parent, parent.address());
@ -1285,9 +1282,6 @@ RescueOrphans(HandleObject obj)
return NS_OK; // Global object. We're done.
parentObj = js::UncheckedUnwrap(parentObj, /* stopAtOuter = */ false);
// PreCreate may touch dead compartments.
js::AutoMaybeTouchDeadZones agc(parentObj);
// Recursively fix up orphans on the parent chain.
rv = RescueOrphans(parentObj);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -1896,13 +1896,13 @@ nsPresContext::MediaFeatureValuesChanged(StyleRebuildType aShouldRebuild,
mPendingViewportChange = false;
if (!nsContentUtils::IsSafeToRunScript()) {
NS_ABORT_IF_FALSE(mDocument->IsBeingUsedAsImage(),
"How did we get here? Are we failing to notify "
"listeners that we should notify?");
if (mDocument->IsBeingUsedAsImage()) {
MOZ_ASSERT(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists));
return;
}
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
// Media query list listeners should be notified from a queued task
// (in HTML5 terms), although we also want to notify them on certain
// flushes. (We're already running off an event.)

View File

@ -6896,6 +6896,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
NS_WARNING(warning.get());
}
#endif
nsContentUtils::WarnScriptWasIgnored(GetDocument());
return NS_OK;
}

View File

@ -454,8 +454,13 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
Element* docElement = aPresContext->Document()->GetRootElement();
if (docElement) {
rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement,
nullptr);
nsIFrame* rootFrame = docElement->GetPrimaryFrame();
if (rootFrame) {
rootStyle = rootFrame->StyleContext();
} else {
rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement,
nullptr);
}
rootStyleFont = rootStyle->StyleFont();
}

View File

@ -138,6 +138,7 @@ PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
: mParent(parent),
mParentHandle(parent->GetHandle()),
mIceCtx(nullptr),
mDNSResolver(new mozilla::NrIceResolver()),
mMainThread(mParent->GetMainThread()),
@ -535,19 +536,31 @@ PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream)
void
PeerConnectionMedia::DtlsConnected(TransportLayer *dtlsLayer,
TransportLayer::State state)
PeerConnectionMedia::DtlsConnected_s(TransportLayer *dtlsLayer,
TransportLayer::State state)
{
dtlsLayer->SignalStateChange.disconnect(this);
bool privacyRequested = false;
// TODO (Bug 952678) set privacy mode, ask the DTLS layer about that
// This has to be a dispatch to a static method, we could be going away
GetMainThread()->Dispatch(
WrapRunnable(nsRefPtr<PeerConnectionImpl>(mParent),
&PeerConnectionImpl::SetDtlsConnected, privacyRequested),
WrapRunnableNM(&PeerConnectionMedia::DtlsConnected_m,
mParentHandle, privacyRequested),
NS_DISPATCH_NORMAL);
}
void
PeerConnectionMedia::DtlsConnected_m(const std::string& aParentHandle,
bool aPrivacyRequested)
{
PeerConnectionWrapper pcWrapper(aParentHandle);
PeerConnectionImpl* pc = pcWrapper.impl();
if (pc) {
pc->SetDtlsConnected(aPrivacyRequested);
}
}
void
PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
const RefPtr<TransportFlow> &aFlow)
@ -567,7 +580,7 @@ PeerConnectionMedia::ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow)
{
TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID());
if (dtls) {
dtls->SignalStateChange.connect(this, &PeerConnectionMedia::DtlsConnected);
dtls->SignalStateChange.connect(this, &PeerConnectionMedia::DtlsConnected_s);
}
}

View File

@ -362,8 +362,10 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
void AddTransportFlow(int aIndex, bool aRtcp,
const mozilla::RefPtr<mozilla::TransportFlow> &aFlow);
void ConnectDtlsListener_s(const mozilla::RefPtr<mozilla::TransportFlow>& aFlow);
void DtlsConnected(mozilla::TransportLayer* aFlow,
mozilla::TransportLayer::State state);
void DtlsConnected_s(mozilla::TransportLayer* aFlow,
mozilla::TransportLayer::State state);
static void DtlsConnected_m(const std::string& aParentHandle,
bool aPrivacyRequested);
mozilla::RefPtr<mozilla::MediaSessionConduit> GetConduit(int aStreamIndex, bool aReceive) {
int index_inner = aStreamIndex * 2 + (aReceive ? 0 : 1);
@ -406,6 +408,8 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
// The parent PC
PeerConnectionImpl *mParent;
// and a loose handle on it for event driven stuff
std::string mParentHandle;
// A list of streams returned from GetUserMedia
// This is only accessed on the main thread (with one special exception)

View File

@ -3732,6 +3732,7 @@ pref("layers.draw-tile-borders", false);
pref("layers.draw-bigimage-borders", false);
pref("layers.frame-counter", false);
pref("layers.enable-tiles", false);
pref("layers.tiled-drawtarget.enabled", false);
pref("layers.low-precision-buffer", false);
pref("layers.tile-width", 256);
pref("layers.tile-height", 256);

View File

@ -1569,8 +1569,13 @@ nsStandardURL::SetHost(const nsACString &input)
buf.AppendInt(mPort);
port_length = buf.Length();
}
mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
mHost.mLen = 0;
if (mAuthority.mLen > 0) {
mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
mHost.mLen = 0;
} else if (mScheme.mLen > 0) {
mHost.mPos = mScheme.mPos + mScheme.mLen + 3;
mHost.mLen = 0;
}
}
int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
@ -1660,7 +1665,6 @@ nsStandardURL::SetPath(const nsACString &input)
ENSURE_MUTABLE();
const nsPromiseFlatCString &path = PromiseFlatCString(input);
LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
InvalidateCache();

View File

@ -528,7 +528,7 @@ this.ProviderManager.prototype = Object.freeze({
* Record an error that occurred operating on a provider.
*/
_recordProviderError: function (name, msg, ex) {
let msg = "Provider error: " + name + ": " + msg;
msg = "Provider error: " + name + ": " + msg;
if (ex) {
msg += ": " + CommonUtils.exceptionStr(ex);
}

View File

@ -5,6 +5,7 @@
import sys
import optparse
from collections import defaultdict
from structuredlog import StructuredLogger, set_default_logger
import handlers
import formatters
@ -18,6 +19,16 @@ log_formatters = {
'tbpl': (formatters.TbplFormatter, "TBPL style log format"),
}
def level_filter_wrapper(formatter, level):
return handlers.LogLevelFilter(formatter, level)
fmt_options = {
# <option name>: (<wrapper function>, description, <applicable formatters>)
'level': (level_filter_wrapper,
"A least log level to subscribe to for the given formatter (debug, info, error, etc.)",
["mach", "tbpl"]),
}
def log_file(name):
if name == "-":
@ -50,7 +61,10 @@ def add_logging_group(parser):
for name, (cls, help_str) in log_formatters.iteritems():
group.add_option("--log-" + name, action="append", type="str",
help=help_str)
for optname, (cls, help_str, formatters) in fmt_options.iteritems():
for fmt in formatters:
group.add_option("--log-%s-%s" % (fmt, optname), action="store",
type="str", help=help_str)
parser.add_option_group(group)
else:
group = parser.add_argument_group(group_name,
@ -59,6 +73,38 @@ def add_logging_group(parser):
group.add_argument("--log-" + name, action="append", type=log_file,
help=help_str)
for optname, (cls, help_str, formatters) in fmt_options.iteritems():
for fmt in formatters:
group.add_argument("--log-%s-%s" % (fmt, optname), action="store",
type=str, help=help_str)
def setup_handlers(logger, formatters, formatter_options):
"""
Add handlers to the given logger according to the formatters and
options provided.
:param logger: The logger configured by this function.
:param formatters: A dict of {formatter, [streams]} to use in handlers.
:param formatter_options: a dict of {formatter: {option: value}} to
to use when configuring formatters.
"""
unused_options = set(formatter_options.keys()) - set(formatters.keys())
if unused_options:
msg = ("Options specified for unused formatter(s) (%s) have no effect" %
list(unused_options))
raise ValueError(msg)
for fmt, streams in formatters.iteritems():
formatter_cls = log_formatters[fmt][0]
formatter = formatter_cls()
for option, value in formatter_options[fmt].iteritems():
formatter = fmt_options[option][0](formatter, value)
for value in streams:
logger.add_handler(handlers.StreamHandler(stream=value,
formatter=formatter))
def setup_logging(suite, args, defaults=None):
"""
@ -78,8 +124,16 @@ def setup_logging(suite, args, defaults=None):
:rtype: StructuredLogger
"""
_option_defaults = {
'level': 'info',
}
logger = StructuredLogger(suite)
prefix = "log_"
# Keep track of any options passed for formatters.
formatter_options = defaultdict(lambda: _option_defaults.copy())
# Keep track of formatters and list of streams specified.
formatters = {}
found = False
found_stdout_logger = False
if not hasattr(args, 'iteritems'):
@ -92,31 +146,38 @@ def setup_logging(suite, args, defaults=None):
defaults = {"raw": sys.stdout}
for name, values in args.iteritems():
if name.startswith(prefix) and values is not None:
for value in values:
found = True
if isinstance(value, str):
value = log_file(value)
if value == sys.stdout:
found_stdout_logger = True
formatter_cls = log_formatters[name[len(prefix):]][0]
logger.add_handler(handlers.StreamHandler(stream=value,
formatter=formatter_cls()))
parts = name.split('_')
if len(parts) > 3:
continue
# Our args will be ['log', <formatter>] or ['log', <formatter>, <option>].
if parts[0] == 'log' and values is not None:
if len(parts) == 1 or parts[1] not in log_formatters:
continue
if len(parts) == 2:
_, formatter = parts
formatters[formatter] = []
for value in values:
found = True
if isinstance(value, str):
value = log_file(value)
if value == sys.stdout:
found_stdout_logger = True
formatters[formatter].append(value)
if len(parts) == 3:
_, formatter, opt = parts
formatter_options[formatter][opt] = values
#If there is no user-specified logging, go with the default options
if not found:
for name, value in defaults.iteritems():
formatter_cls = log_formatters[name][0]
logger.add_handler(handlers.StreamHandler(stream=value,
formatter=formatter_cls()))
formatters[name] = [value]
elif not found_stdout_logger and sys.stdout in defaults.values():
for name, value in defaults.iteritems():
if value == sys.stdout:
formatter_cls = log_formatters[name][0]
logger.add_handler(handlers.StreamHandler(stream=value,
formatter=formatter_cls()))
formatters[name] = [value]
setup_handlers(logger, formatters, formatter_options)
set_default_logger(logger)
return logger

Some files were not shown because too many files have changed in this diff Show More