fix: add missing buffer size check in nativeImage (#17465)
This commit is contained in:
parent
6c20c6e668
commit
95df531b33
2 changed files with 97 additions and 56 deletions
|
@ -80,19 +80,26 @@ float GetScaleFactorFromOptions(mate::Arguments* args) {
|
||||||
return scale_factor;
|
return scale_factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddImageSkiaRep(gfx::ImageSkia* image,
|
bool AddImageSkiaRepFromPNG(gfx::ImageSkia* image,
|
||||||
const unsigned char* data,
|
const unsigned char* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
double scale_factor) {
|
double scale_factor) {
|
||||||
auto decoded = std::make_unique<SkBitmap>();
|
SkBitmap bitmap;
|
||||||
|
if (!gfx::PNGCodec::Decode(data, size, &bitmap))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddImageSkiaRepFromJPEG(gfx::ImageSkia* image,
|
||||||
|
const unsigned char* data,
|
||||||
|
size_t size,
|
||||||
|
double scale_factor) {
|
||||||
|
auto bitmap = gfx::JPEGCodec::Decode(data, size);
|
||||||
|
if (!bitmap)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Try PNG first.
|
|
||||||
if (!gfx::PNGCodec::Decode(data, size, decoded.get())) {
|
|
||||||
// Try JPEG.
|
|
||||||
decoded = gfx::JPEGCodec::Decode(data, size);
|
|
||||||
if (decoded) {
|
|
||||||
// `JPEGCodec::Decode()` doesn't tell `SkBitmap` instance it creates
|
// `JPEGCodec::Decode()` doesn't tell `SkBitmap` instance it creates
|
||||||
// that all of its pixels are opaque, that's why the bitmap gets
|
// that all of its pixels are opaque, that's why the bitmap gets
|
||||||
// an alpha type `kPremul_SkAlphaType` instead of `kOpaque_SkAlphaType`.
|
// an alpha type `kPremul_SkAlphaType` instead of `kOpaque_SkAlphaType`.
|
||||||
|
@ -100,27 +107,42 @@ bool AddImageSkiaRep(gfx::ImageSkia* image,
|
||||||
// TODO(alexeykuzmin): This workaround should be removed
|
// TODO(alexeykuzmin): This workaround should be removed
|
||||||
// when the `JPEGCodec::Decode()` code is fixed.
|
// when the `JPEGCodec::Decode()` code is fixed.
|
||||||
// See https://github.com/electron/electron/issues/11294.
|
// See https://github.com/electron/electron/issues/11294.
|
||||||
decoded->setAlphaType(SkAlphaType::kOpaque_SkAlphaType);
|
bitmap->setAlphaType(SkAlphaType::kOpaque_SkAlphaType);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!decoded) {
|
image->AddRepresentation(gfx::ImageSkiaRep(*bitmap, scale_factor));
|
||||||
// Try Bitmap
|
|
||||||
if (width > 0 && height > 0) {
|
|
||||||
decoded.reset(new SkBitmap);
|
|
||||||
decoded->allocN32Pixels(width, height, false);
|
|
||||||
decoded->setPixels(
|
|
||||||
const_cast<void*>(reinterpret_cast<const void*>(data)));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
image->AddRepresentation(gfx::ImageSkiaRep(*decoded, scale_factor));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddImageSkiaRep(gfx::ImageSkia* image,
|
bool AddImageSkiaRepFromBuffer(gfx::ImageSkia* image,
|
||||||
|
const unsigned char* data,
|
||||||
|
size_t size,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
double scale_factor) {
|
||||||
|
// Try PNG first.
|
||||||
|
if (AddImageSkiaRepFromPNG(image, data, size, scale_factor))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Try JPEG second.
|
||||||
|
if (AddImageSkiaRepFromJPEG(image, data, size, scale_factor))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (width == 0 || height == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
|
||||||
|
if (size < info.computeMinByteSize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SkBitmap bitmap;
|
||||||
|
bitmap.allocN32Pixels(width, height, false);
|
||||||
|
bitmap.setPixels(const_cast<void*>(reinterpret_cast<const void*>(data)));
|
||||||
|
|
||||||
|
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddImageSkiaRepFromPath(gfx::ImageSkia* image,
|
||||||
const base::FilePath& path,
|
const base::FilePath& path,
|
||||||
double scale_factor) {
|
double scale_factor) {
|
||||||
std::string file_contents;
|
std::string file_contents;
|
||||||
|
@ -133,7 +155,8 @@ bool AddImageSkiaRep(gfx::ImageSkia* image,
|
||||||
const unsigned char* data =
|
const unsigned char* data =
|
||||||
reinterpret_cast<const unsigned char*>(file_contents.data());
|
reinterpret_cast<const unsigned char*>(file_contents.data());
|
||||||
size_t size = file_contents.size();
|
size_t size = file_contents.size();
|
||||||
return AddImageSkiaRep(image, data, size, 0, 0, scale_factor);
|
|
||||||
|
return AddImageSkiaRepFromBuffer(image, data, size, 0, 0, scale_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
|
bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
|
||||||
|
@ -142,12 +165,12 @@ bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
|
||||||
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
|
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
|
||||||
if (base::MatchPattern(filename, "*@*x"))
|
if (base::MatchPattern(filename, "*@*x"))
|
||||||
// Don't search for other representations if the DPI has been specified.
|
// Don't search for other representations if the DPI has been specified.
|
||||||
return AddImageSkiaRep(image, path, GetScaleFactorFromPath(path));
|
return AddImageSkiaRepFromPath(image, path, GetScaleFactorFromPath(path));
|
||||||
else
|
else
|
||||||
succeed |= AddImageSkiaRep(image, path, 1.0f);
|
succeed |= AddImageSkiaRepFromPath(image, path, 1.0f);
|
||||||
|
|
||||||
for (const ScaleFactorPair& pair : kScaleFactorPairs)
|
for (const ScaleFactorPair& pair : kScaleFactorPairs)
|
||||||
succeed |= AddImageSkiaRep(
|
succeed |= AddImageSkiaRepFromPath(
|
||||||
image, path.InsertBeforeExtensionASCII(pair.name), pair.scale);
|
image, path.InsertBeforeExtensionASCII(pair.name), pair.scale);
|
||||||
return succeed;
|
return succeed;
|
||||||
}
|
}
|
||||||
|
@ -422,19 +445,20 @@ void NativeImage::AddRepresentation(const mate::Dictionary& options) {
|
||||||
v8::Local<v8::Value> buffer;
|
v8::Local<v8::Value> buffer;
|
||||||
GURL url;
|
GURL url;
|
||||||
if (options.Get("buffer", &buffer) && node::Buffer::HasInstance(buffer)) {
|
if (options.Get("buffer", &buffer) && node::Buffer::HasInstance(buffer)) {
|
||||||
AddImageSkiaRep(
|
auto* data = reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer));
|
||||||
&image_skia,
|
auto size = node::Buffer::Length(buffer);
|
||||||
reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
|
skia_rep_added = AddImageSkiaRepFromBuffer(&image_skia, data, size, width,
|
||||||
node::Buffer::Length(buffer), width, height, scale_factor);
|
height, scale_factor);
|
||||||
skia_rep_added = true;
|
|
||||||
} else if (options.Get("dataURL", &url)) {
|
} else if (options.Get("dataURL", &url)) {
|
||||||
std::string mime_type, charset, data;
|
std::string mime_type, charset, data;
|
||||||
if (net::DataURL::Parse(url, &mime_type, &charset, &data)) {
|
if (net::DataURL::Parse(url, &mime_type, &charset, &data)) {
|
||||||
if (mime_type == "image/png" || mime_type == "image/jpeg") {
|
auto* data_ptr = reinterpret_cast<const unsigned char*>(data.c_str());
|
||||||
AddImageSkiaRep(&image_skia,
|
if (mime_type == "image/png") {
|
||||||
reinterpret_cast<const unsigned char*>(data.c_str()),
|
skia_rep_added = AddImageSkiaRepFromPNG(&image_skia, data_ptr,
|
||||||
data.size(), width, height, scale_factor);
|
data.size(), scale_factor);
|
||||||
skia_rep_added = true;
|
} else if (mime_type == "image/jpeg") {
|
||||||
|
skia_rep_added = AddImageSkiaRepFromJPEG(&image_skia, data_ptr,
|
||||||
|
data.size(), scale_factor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,8 +597,8 @@ mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx::ImageSkia image_skia;
|
gfx::ImageSkia image_skia;
|
||||||
AddImageSkiaRep(&image_skia,
|
AddImageSkiaRepFromBuffer(
|
||||||
reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
|
&image_skia, reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
|
||||||
node::Buffer::Length(buffer), width, height, scale_factor);
|
node::Buffer::Length(buffer), width, height, scale_factor);
|
||||||
return Create(args->isolate(), gfx::Image(image_skia));
|
return Create(args->isolate(), gfx::Image(image_skia));
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,11 @@ describe('nativeImage module', () => {
|
||||||
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true()
|
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('returns an empty image when the buffer is too small', () => {
|
||||||
|
const image = nativeImage.createFromBuffer(Buffer.from([1, 2, 3, 4]), { width: 100, height: 100 })
|
||||||
|
expect(image.isEmpty()).to.be.true()
|
||||||
|
})
|
||||||
|
|
||||||
it('returns an image created from the given buffer', () => {
|
it('returns an image created from the given buffer', () => {
|
||||||
const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
||||||
|
|
||||||
|
@ -476,6 +481,18 @@ describe('nativeImage module', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('addRepresentation()', () => {
|
describe('addRepresentation()', () => {
|
||||||
|
it('does not add representation when the buffer is too small', () => {
|
||||||
|
const image = nativeImage.createEmpty()
|
||||||
|
|
||||||
|
image.addRepresentation({
|
||||||
|
buffer: Buffer.from([1, 2, 3, 4]),
|
||||||
|
width: 100,
|
||||||
|
height: 100
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(image.isEmpty()).to.be.true()
|
||||||
|
})
|
||||||
|
|
||||||
it('supports adding a buffer representation for a scale factor', () => {
|
it('supports adding a buffer representation for a scale factor', () => {
|
||||||
const image = nativeImage.createEmpty()
|
const image = nativeImage.createEmpty()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue