Commit 3995eb71 authored by xianglu's avatar xianglu Committed by Commit bot

Shape Detection: Add two layout tests for face detection.

This CL includes security tests for empty image and undecodable image.

BUG=646083
TEST=third_party/WebKit/LayoutTests/fast/shapedetection/shapedetection-security-test.html

Review-Url: https://codereview.chromium.org/2441953002
Cr-Commit-Position: refs/heads/master@{#427234}
parent 2de862f7
<!DOCTYPE html>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script>
// TODO(xianglu): Consider adding tests for image with only one dimension
// equal to zero.
// Returns a Promise that is resolve()d if detect() fails.
function detectFaceAndExpectError(imageUrl) {
return new Promise(function(resolve, reject) {
var image = new Image();
var faceDetector = new FaceDetector();
var tryFaceDetection = function() {
faceDetector.detect(image)
.then(faceDetectionResult => {
reject("Promise for this test image should have been rejected.");
})
.catch(error => {
resolve(error);
});
};
image.onload = tryFaceDetection;
image.onerror = tryFaceDetection;
image.src = imageUrl;
});
}
// This test verifies that FaceDetector will reject an empty image.
promise_test(function(t) {
return detectFaceAndExpectError("")
.then(function(error) {
assert_equals(error.name, "InvalidStateError");
assert_equals(error.message, "HTMLImageElement is empty.");
});
}, "FaceDetector should reject empty images with InvalidStateError.");
// This test verifies that FaceDetector will reject an undecodable image.
promise_test(function(t) {
return detectFaceAndExpectError("../../imported/wpt/images/broken.png")
.then(function(error) {
assert_equals(error.name, "InvalidStateError");
assert_regexp_match(error.message, /Unable to decompress*/);
});
}, "FaceDetector should reject undecodable images with InvalidStateError.");
</script>
...@@ -21,16 +21,20 @@ namespace blink { ...@@ -21,16 +21,20 @@ namespace blink {
namespace { namespace {
mojo::ScopedSharedBufferHandle getSharedBufferHandle( mojo::ScopedSharedBufferHandle getSharedBufferHandle(
const HTMLImageElement* img) { const HTMLImageElement* img,
ScriptPromiseResolver* resolver) {
ImageResource* const imageResource = img->cachedImage(); ImageResource* const imageResource = img->cachedImage();
if (!imageResource) { // TODO(xianglu): Add test case for undecodable images.
DLOG(ERROR) << "Failed to convert HTMLImageElement to ImageSource."; if (!imageResource || imageResource->errorOccurred()) {
resolver->reject(DOMException::create(
InvalidStateError, "Failed to load or decode HTMLImageElement."));
return mojo::ScopedSharedBufferHandle(); return mojo::ScopedSharedBufferHandle();
} }
Image* const blinkImage = imageResource->getImage(); Image* const blinkImage = imageResource->getImage();
if (!blinkImage) { if (!blinkImage) {
DLOG(ERROR) << "Failed to convert ImageSource to blink::Image."; resolver->reject(DOMException::create(
InvalidStateError, "Failed to get image from resource."));
return mojo::ScopedSharedBufferHandle(); return mojo::ScopedSharedBufferHandle();
} }
...@@ -39,7 +43,8 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle( ...@@ -39,7 +43,8 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle(
DCHECK_EQ(img->naturalHeight(), image->height()); DCHECK_EQ(img->naturalHeight(), image->height());
if (!image) { if (!image) {
DLOG(ERROR) << "Failed to convert blink::Image to sk_sp<SkImage>."; resolver->reject(DOMException::create(
InvalidStateError, "Failed to get image from current frame."));
return mojo::ScopedSharedBufferHandle(); return mojo::ScopedSharedBufferHandle();
} }
...@@ -51,9 +56,13 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle( ...@@ -51,9 +56,13 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle(
mojo::ScopedSharedBufferHandle sharedBufferHandle = mojo::ScopedSharedBufferHandle sharedBufferHandle =
mojo::SharedBufferHandle::Create(allocationSize); mojo::SharedBufferHandle::Create(allocationSize);
if (!sharedBufferHandle.is_valid()) { if (!sharedBufferHandle.is_valid()) {
// TODO(xianglu): Do something when the image is too large. DLOG(ERROR) << "Requested allocation : " << allocationSize
DLOG(ERROR) << "Failed to create a sharedBufferHandle. allocationSize = " << "B, larger than |mojo::edk::kMaxSharedBufferSize| == 16MB ";
<< allocationSize << "bytes. limit = 16777216"; // TODO(xianglu): For now we reject the promise if the image is too large.
// But consider resizing the image to remove restriction on the user side.
// Also, add layouttest for this case later.
resolver->reject(
DOMException::create(InvalidStateError, "Image exceeds size limit."));
return mojo::ScopedSharedBufferHandle(); return mojo::ScopedSharedBufferHandle();
} }
...@@ -62,7 +71,9 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle( ...@@ -62,7 +71,9 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle(
const SkPixmap pixmap(skiaInfo, mappedBuffer.get(), skiaInfo.minRowBytes()); const SkPixmap pixmap(skiaInfo, mappedBuffer.get(), skiaInfo.minRowBytes());
if (!image->readPixels(pixmap, 0, 0)) { if (!image->readPixels(pixmap, 0, 0)) {
DLOG(ERROR) << "Failed to read pixels from sk_sp<SkImage>."; resolver->reject(DOMException::create(
InvalidStateError,
"Failed to read pixels: Unable to decompress or unsupported format."));
return mojo::ScopedSharedBufferHandle(); return mojo::ScopedSharedBufferHandle();
} }
...@@ -86,25 +97,28 @@ ScriptPromise FaceDetector::detect(ScriptState* scriptState, ...@@ -86,25 +97,28 @@ ScriptPromise FaceDetector::detect(ScriptState* scriptState,
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise(); ScriptPromise promise = resolver->promise();
if (!m_service) { // TODO(xianglu): Add test cases for cross-origin-images.
if (img->wouldTaintOrigin(
scriptState->getExecutionContext()->getSecurityOrigin())) {
resolver->reject(DOMException::create( resolver->reject(DOMException::create(
NotFoundError, "Face detection service unavailable.")); SecurityError, "Image source from a different origin."));
return promise; return promise;
} }
if (!img) { if (img->bitmapSourceSize().isZero()) {
resolver->reject(DOMException::create( resolver->reject(
SyntaxError, "The provided HTMLImageElement is empty.")); DOMException::create(InvalidStateError, "HTMLImageElement is empty."));
return promise; return promise;
} }
// TODO(xianglu): Add security check when the spec is ready.
// https://crbug.com/646083
mojo::ScopedSharedBufferHandle sharedBufferHandle = mojo::ScopedSharedBufferHandle sharedBufferHandle =
getSharedBufferHandle(img); getSharedBufferHandle(img, resolver);
if (!sharedBufferHandle->is_valid()) { if (!sharedBufferHandle->is_valid())
return promise;
if (!m_service) {
resolver->reject(DOMException::create( resolver->reject(DOMException::create(
SyntaxError, "Request for sharedBufferHandle failed.")); NotSupportedError, "Face detection service unavailable."));
return promise; return promise;
} }
......
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