Commit 9b86811c authored by eroman@chromium.org's avatar eroman@chromium.org

[webcrypto] Allow crypto operations to be cancelled by the platform implementation.

When the ExecutionContext is destroyed, the outstanding request is marked as cancelled.

The corresponding chromium side of this change is https://codereview.chromium.org/341923004/

BUG=375430

Review URL: https://codereview.chromium.org/343723003

git-svn-id: svn://svn.chromium.org/blink/trunk@176502 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 1313fa4e
Tests cancelling of crypto operations when the context is destroyed.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Worker started
Successfully generated AES-CBC key
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script>
description("Tests cancelling of crypto operations when the context is destroyed.");
// Start a worker thread which starts a LOT of expensive
// webcrypto operations (generating RSA keys).
//
// Now from the main page try to generate an AES key.
//
// Lastly, the web worker closes itself. This should have the
// effect of aborting all of the web crypto operations it had
// started, and now the AES key generation will be able to complete.
//
// If the crypto tasks started by the web worker are NOT
// cancelled (and are merely orphaned), then the test is going to
// timeout.
jsTestIsAsync = true;
function startWorker() {
return new Promise(function(resolve, reject) {
var worker = new Worker("resources/worker-start-slow-operations.js");
worker.onmessage = function(event)
{
debug(event.data);
resolve();
};
worker.onerror = function(error)
{
debug(error.filename + ':' + error.lineno + ': ' + error.message);
reject('worker failed');
}
});
}
function generateTestKey() {
var algorithm = {name: "AES-CBC", length: 128};
return crypto.subtle.generateKey(algorithm, true, ['encrypt']).then(function() {
debug('Successfully generated AES-CBC key');
});
}
startWorker().then(generateTestKey).then(finishJSTest, failAndFinishJSTest);
</script>
</body>
</html>
function generateRsaKey()
{
var extractable = false;
var usages = ['sign', 'verify'];
var algorithm = {name: "RSASSA-PKCS1-v1_5", modulusLength: 512, publicExponent: new Uint8Array([1, 0, 1]), hash: {name: 'sha-1'}};
return crypto.subtle.generateKey(algorithm, extractable, usages).then(undefined, function(error) {
postMessage(error);
});
}
// Start many concurrent operations but don't do anything with the result, or
// even wait for them to complete.
for (var i = 0; i < 1000; ++i)
generateRsaKey();
// Inform the outer script that the worker started.
postMessage("Worker started");
close();
......@@ -47,27 +47,30 @@
namespace WebCore {
namespace {
class WeakResolver : public ScriptPromiseResolverWithContext {
class CryptoResultImpl::WeakResolver : public ScriptPromiseResolverWithContext {
public:
static WeakPtr<ScriptPromiseResolverWithContext> create(ScriptState* scriptState)
static WeakPtr<ScriptPromiseResolverWithContext> create(ScriptState* scriptState, CryptoResultImpl* result)
{
RefPtr<WeakResolver> p = adoptRef(new WeakResolver(scriptState));
RefPtr<WeakResolver> p = adoptRef(new WeakResolver(scriptState, result));
p->suspendIfNeeded();
p->keepAliveWhilePending();
return p->m_weakPtrFactory.createWeakPtr();
}
virtual ~WeakResolver()
{
m_result->cancel();
}
private:
explicit WeakResolver(ScriptState* scriptState)
WeakResolver(ScriptState* scriptState, CryptoResultImpl* result)
: ScriptPromiseResolverWithContext(scriptState)
, m_weakPtrFactory(this) { }
, m_weakPtrFactory(this)
, m_result(result) { }
WeakPtrFactory<ScriptPromiseResolverWithContext> m_weakPtrFactory;
RefPtr<CryptoResultImpl> m_result;
};
} // namespace
ExceptionCode webCryptoErrorToExceptionCode(blink::WebCryptoErrorType errorType)
{
switch (errorType) {
......@@ -155,8 +158,19 @@ void CryptoResultImpl::completeWithKeyPair(const blink::WebCryptoKey& publicKey,
m_resolver->resolve(KeyPair::create(publicKey, privateKey));
}
bool CryptoResultImpl::cancelled() const
{
return acquireLoad(&m_cancelled);
}
void CryptoResultImpl::cancel()
{
releaseStore(&m_cancelled, 1);
}
CryptoResultImpl::CryptoResultImpl(ScriptState* scriptState)
: m_resolver(WeakResolver::create(scriptState))
: m_resolver(WeakResolver::create(scriptState, this))
, m_cancelled(0)
{
}
......
......@@ -50,7 +50,7 @@ ExceptionCode webCryptoErrorToExceptionCode(blink::WebCryptoErrorType);
//
// * At creation time there must be an active ExecutionContext.
// * The CryptoResult interface must only be called from the origin thread.
// * addref() and deref() can be called from any thread.
// * ref(), deref(), cancelled() and cancel() can be called from any thread.
// * One of the completeWith***() functions must be called, or the
// m_resolver will be leaked until the ExecutionContext is destroyed.
class CryptoResultImpl FINAL : public CryptoResult {
......@@ -65,14 +65,19 @@ public:
virtual void completeWithBoolean(bool) OVERRIDE;
virtual void completeWithKey(const blink::WebCryptoKey&) OVERRIDE;
virtual void completeWithKeyPair(const blink::WebCryptoKey& publicKey, const blink::WebCryptoKey& privateKey) OVERRIDE;
virtual bool cancelled() const OVERRIDE;
// It is only valid to call this before completion.
ScriptPromise promise();
private:
class WeakResolver;
explicit CryptoResultImpl(ScriptState*);
void cancel();
WeakPtr<ScriptPromiseResolverWithContext> m_resolver;
volatile int m_cancelled;
};
} // namespace WebCore
......
......@@ -47,6 +47,7 @@ public:
virtual void completeWithBoolean(bool) = 0;
virtual void completeWithKey(const blink::WebCryptoKey&) = 0;
virtual void completeWithKeyPair(const blink::WebCryptoKey& publicKey, const blink::WebCryptoKey& privateKey) = 0;
virtual bool cancelled() const = 0;
blink::WebCryptoResult result()
{
......
......@@ -86,6 +86,11 @@ void WebCryptoResult::completeWithKeyPair(const WebCryptoKey& publicKey, const W
reset();
}
bool WebCryptoResult::cancelled() const
{
return m_impl->cancelled();
}
WebCryptoResult::WebCryptoResult(const PassRefPtr<WebCore::CryptoResult>& impl)
: m_impl(impl)
{
......
......@@ -44,6 +44,9 @@ namespace WebCore { class CryptoResult; }
namespace WTF { template <typename T> class PassRefPtr; }
#endif
// FIXME: Remove once chromium side rolls.
#define WEBCRYPTO_RESULT_HAS_CANCELLED 1
namespace blink {
class WebArrayBuffer;
......@@ -95,6 +98,10 @@ public:
BLINK_PLATFORM_EXPORT void completeWithKey(const WebCryptoKey&);
BLINK_PLATFORM_EXPORT void completeWithKeyPair(const WebCryptoKey& publicKey, const WebCryptoKey& privateKey);
// Returns true if the underlying operation was cancelled.
// This method can be called from any thread.
BLINK_PLATFORM_EXPORT bool cancelled() const;
#if INSIDE_BLINK
BLINK_PLATFORM_EXPORT explicit WebCryptoResult(const WTF::PassRefPtr<WebCore::CryptoResult>&);
#endif
......@@ -146,6 +153,9 @@ public:
// asynchronously make a copy of the WebCryptoResult and call it from the
// same thread that started the request.
//
// If the request was cancelled it is not necessary for implementations to
// set the result.
//
// -----------------------
// Threading
// -----------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment