mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and inbound
This commit is contained in:
commit
2e6839e1e5
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
|
||||
"Should not already have a count");
|
||||
sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
|
||||
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)
|
||||
|
@ -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 =
|
||||
|
@ -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 {
|
||||
|
@ -3,4 +3,5 @@
|
||||
[test_bug357450.js]
|
||||
[test_copypaste.xul]
|
||||
[test_messagemanager_principal.html]
|
||||
[test_messagemanager_send_principal.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
|
118
content/base/test/test_messagemanager_send_principal.html
Normal file
118
content/base/test/test_messagemanager_send_principal.html
Normal 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>
|
@ -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;
|
||||
|
@ -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];
|
||||
|
||||
}),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() {});
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
27
dom/canvas/WebGLBindableName.cpp
Normal file
27
dom/canvas/WebGLBindableName.cpp
Normal 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();
|
||||
}
|
36
dom/canvas/WebGLBindableName.h
Normal file
36
dom/canvas/WebGLBindableName.h
Normal 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_
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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,13 +66,12 @@ 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())
|
||||
|
@ -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();
|
||||
|
||||
|
@ -89,5 +89,3 @@ WebGLContext::IsVertexArray(WebGLVertexArray *array)
|
||||
!array->IsDeleted() &&
|
||||
array->HasEverBeenBound();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -41,6 +41,7 @@ if CONFIG['MOZ_WEBGL']:
|
||||
'WebGL1Context.cpp',
|
||||
'WebGL2Context.cpp',
|
||||
'WebGLActiveInfo.cpp',
|
||||
'WebGLBindableName.cpp',
|
||||
'WebGLBuffer.cpp',
|
||||
'WebGLContext.cpp',
|
||||
'WebGLContextAsyncQueries.cpp',
|
||||
|
@ -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,23 +360,151 @@ 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;
|
||||
}
|
||||
|
||||
SECItem*
|
||||
CreateECPointForCoordinates(const CryptoBuffer& aX,
|
||||
const CryptoBuffer& aY,
|
||||
PLArenaPool* aArena)
|
||||
{
|
||||
// Check that both points have the same length.
|
||||
if (aX.Length() != aY.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create point.
|
||||
SECItem* point = ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
|
||||
if (!point) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
|
||||
aTemplate,
|
||||
aTemplateSize,
|
||||
PR_FALSE));
|
||||
if (!obj.get()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Have NSS translate the object to a private key by inspection
|
||||
// and make a copy we can own
|
||||
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() || !aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) {
|
||||
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())) ||
|
||||
@ -376,15 +521,17 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
|
||||
// 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 (!nItem.get() || !objID.get()) {
|
||||
if (!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) },
|
||||
@ -402,31 +549,13 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
|
||||
{ CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() },
|
||||
};
|
||||
|
||||
|
||||
// Create a generic object with the contents of the key
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot.get()) {
|
||||
return nullptr;
|
||||
return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
|
||||
PR_ARRAY_SIZE(keyTemplate));
|
||||
}
|
||||
|
||||
ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
|
||||
keyTemplate,
|
||||
PR_ARRAY_SIZE(keyTemplate),
|
||||
PR_FALSE));
|
||||
if (!obj.get()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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(),
|
||||
nullptr));
|
||||
if (!privKey.get()) {
|
||||
return nullptr;
|
||||
}
|
||||
return SECKEY_CopyPrivateKey(privKey.get());
|
||||
}
|
||||
|
||||
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,10 +712,11 @@ 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;
|
||||
}
|
||||
|
||||
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())) ||
|
||||
@ -525,6 +749,54 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
|
||||
return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
|
||||
JsonWebKey& aRetVal,
|
||||
@ -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;
|
||||
}
|
||||
|
43
dom/crypto/EcKeyAlgorithm.cpp
Normal file
43
dom/crypto/EcKeyAlgorithm.cpp
Normal 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
|
48
dom/crypto/EcKeyAlgorithm.h
Normal file
48
dom/crypto/EcKeyAlgorithm.h
Normal 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
|
@ -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;
|
||||
|
@ -24,6 +24,7 @@ class KeyAlgorithm;
|
||||
enum KeyAlgorithmStructuredCloneTags {
|
||||
SCTAG_KEYALG,
|
||||
SCTAG_AESKEYALG,
|
||||
SCTAG_ECKEYALG,
|
||||
SCTAG_HMACKEYALG,
|
||||
SCTAG_RSAKEYALG,
|
||||
SCTAG_RSAHASHEDKEYALG
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
DeriveKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
|
||||
const ObjectOrString& aDerivedKeyType, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
: DerivePbkdfBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
|
||||
: 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);
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
);
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -494,7 +494,7 @@ parent:
|
||||
|
||||
PNecko();
|
||||
|
||||
sync PScreenManager()
|
||||
rpc PScreenManager()
|
||||
returns (uint32_t numberOfScreens,
|
||||
float systemDefaultScale,
|
||||
bool success);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -24,11 +24,11 @@ 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,
|
||||
ScreenManagerParent::AnswerRefresh(uint32_t* aNumberOfScreens,
|
||||
float* aSystemDefaultScale,
|
||||
bool* aSuccess)
|
||||
{
|
||||
@ -49,7 +49,7 @@ ScreenManagerParent::RecvRefresh(uint32_t* aNumberOfScreens,
|
||||
}
|
||||
|
||||
bool
|
||||
ScreenManagerParent::RecvScreenRefresh(const uint32_t& aId,
|
||||
ScreenManagerParent::AnswerScreenRefresh(const uint32_t& aId,
|
||||
ScreenDetails* aRetVal,
|
||||
bool* aSuccess)
|
||||
{
|
||||
@ -70,7 +70,7 @@ ScreenManagerParent::RecvScreenRefresh(const uint32_t& aId,
|
||||
}
|
||||
|
||||
bool
|
||||
ScreenManagerParent::RecvGetPrimaryScreen(ScreenDetails* aRetVal,
|
||||
ScreenManagerParent::AnswerGetPrimaryScreen(ScreenDetails* aRetVal,
|
||||
bool* aSuccess)
|
||||
{
|
||||
*aSuccess = false;
|
||||
@ -91,7 +91,7 @@ ScreenManagerParent::RecvGetPrimaryScreen(ScreenDetails* aRetVal,
|
||||
}
|
||||
|
||||
bool
|
||||
ScreenManagerParent::RecvScreenForRect(const int32_t& aLeft,
|
||||
ScreenManagerParent::AnswerScreenForRect(const int32_t& aLeft,
|
||||
const int32_t& aTop,
|
||||
const int32_t& aWidth,
|
||||
const int32_t& aHeight,
|
||||
@ -116,7 +116,7 @@ ScreenManagerParent::RecvScreenForRect(const int32_t& aLeft,
|
||||
}
|
||||
|
||||
bool
|
||||
ScreenManagerParent::RecvScreenForBrowser(PBrowserParent* aBrowser,
|
||||
ScreenManagerParent::AnswerScreenForBrowser(PBrowserParent* aBrowser,
|
||||
ScreenDetails* aRetVal,
|
||||
bool* aSuccess)
|
||||
{
|
||||
|
@ -21,27 +21,27 @@ class ScreenManagerParent : public PScreenManagerParent
|
||||
bool* aSuccess);
|
||||
~ScreenManagerParent() {};
|
||||
|
||||
virtual bool RecvRefresh(uint32_t* aNumberOfScreens,
|
||||
virtual bool AnswerRefresh(uint32_t* aNumberOfScreens,
|
||||
float* aSystemDefaultScale,
|
||||
bool* aSuccess) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvScreenRefresh(const uint32_t& aId,
|
||||
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,
|
||||
virtual bool AnswerGetPrimaryScreen(ScreenDetails* aRetVal,
|
||||
bool* aSuccess) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvScreenForRect(const int32_t& aLeft,
|
||||
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,
|
||||
virtual bool AnswerScreenForBrowser(PBrowserParent* aBrowser,
|
||||
ScreenDetails* aRetVal,
|
||||
bool* aSuccess);
|
||||
|
||||
|
@ -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 *****/
|
||||
|
||||
|
@ -455,6 +455,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
|
||||
*_retval = 0;
|
||||
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::WarnScriptWasIgnored(nullptr);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -149,7 +149,7 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool IsDualDrawTarget()
|
||||
virtual bool IsDualDrawTarget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -31,7 +31,7 @@ DIBTextureClient::CreateSimilar(TextureFlags aFlags,
|
||||
{
|
||||
RefPtr<TextureClient> tex = new DIBTextureClient(mFormat, mFlags | aFlags);
|
||||
|
||||
if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) {
|
||||
if (!tex->AllocateForSurface(mSize, aAllocFlags)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 ®i
|
||||
}
|
||||
}
|
||||
|
||||
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,12 +990,66 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
|
||||
mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())),
|
||||
&createdTextureClient, !usingSinglePaintBuffer);
|
||||
|
||||
if (!backBuffer || !backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// We must not keep a reference to the DrawTarget after it has been unlocked,
|
||||
// make sure these are null'd before unlocking as destruction of the context
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -52,6 +52,9 @@ public:
|
||||
{
|
||||
Update(aNewValidRegion, aPaintRegion);
|
||||
}
|
||||
|
||||
void UnlockTile(TestTiledLayerTile aTile) {}
|
||||
void PostValidate(const nsIntRegion& aPaintRegion) {}
|
||||
};
|
||||
|
||||
TEST(TiledLayerBuffer, TileConstructor) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -30,5 +30,6 @@ LOCAL_INCLUDES += [
|
||||
'/js/ipc',
|
||||
'/js/public',
|
||||
'/js/xpconnect/src',
|
||||
'/js/xpconnect/wrappers',
|
||||
]
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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++;
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
119
js/src/jsgc.cpp
119
js/src/jsgc.cpp
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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()));
|
||||
|
@ -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()) {
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.)
|
||||
|
@ -6896,6 +6896,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
||||
NS_WARNING(warning.get());
|
||||
}
|
||||
#endif
|
||||
nsContentUtils::WarnScriptWasIgnored(GetDocument());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -454,8 +454,13 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
|
||||
Element* docElement = aPresContext->Document()->GetRootElement();
|
||||
|
||||
if (docElement) {
|
||||
nsIFrame* rootFrame = docElement->GetPrimaryFrame();
|
||||
if (rootFrame) {
|
||||
rootStyle = rootFrame->StyleContext();
|
||||
} else {
|
||||
rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement,
|
||||
nullptr);
|
||||
}
|
||||
rootStyleFont = rootStyle->StyleFont();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
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)
|
||||
|
@ -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);
|
||||
|
@ -1569,8 +1569,13 @@ nsStandardURL::SetHost(const nsACString &input)
|
||||
buf.AppendInt(mPort);
|
||||
port_length = buf.Length();
|
||||
}
|
||||
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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
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
|
||||
formatter_cls = log_formatters[name[len(prefix):]][0]
|
||||
logger.add_handler(handlers.StreamHandler(stream=value,
|
||||
formatter=formatter_cls()))
|
||||
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
Loading…
Reference in New Issue
Block a user