зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1201796 (Part 4) - Add downscale-during-decode support for the ICO decoder. r=tn
This commit is contained in:
Родитель
ba6ea7f0cc
Коммит
93cbaca5e0
|
@ -38,6 +38,7 @@ ShouldDownscaleDuringDecode(const nsCString& aMimeType)
|
||||||
DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
|
DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
|
||||||
return type == DecoderType::JPEG ||
|
return type == DecoderType::JPEG ||
|
||||||
type == DecoderType::ICON ||
|
type == DecoderType::ICON ||
|
||||||
|
type == DecoderType::ICO ||
|
||||||
type == DecoderType::PNG ||
|
type == DecoderType::PNG ||
|
||||||
type == DecoderType::BMP ||
|
type == DecoderType::BMP ||
|
||||||
type == DecoderType::GIF;
|
type == DecoderType::GIF;
|
||||||
|
|
|
@ -14,13 +14,14 @@
|
||||||
|
|
||||||
#include "RasterImage.h"
|
#include "RasterImage.h"
|
||||||
|
|
||||||
|
using namespace mozilla::gfx;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace image {
|
namespace image {
|
||||||
|
|
||||||
// Constants.
|
// Constants.
|
||||||
static const uint32_t ICOHEADERSIZE = 6;
|
static const uint32_t ICOHEADERSIZE = 6;
|
||||||
static const uint32_t BITMAPINFOSIZE = 40;
|
static const uint32_t BITMAPINFOSIZE = 40;
|
||||||
static const uint32_t PREFICONSIZE = 16;
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Actual Data Processing
|
// Actual Data Processing
|
||||||
|
@ -60,6 +61,7 @@ nsICODecoder::GetNumColors()
|
||||||
nsICODecoder::nsICODecoder(RasterImage* aImage)
|
nsICODecoder::nsICODecoder(RasterImage* aImage)
|
||||||
: Decoder(aImage)
|
: Decoder(aImage)
|
||||||
, mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE))
|
, mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE))
|
||||||
|
, mBiggestResourceColorDepth(0)
|
||||||
, mBestResourceDelta(INT_MIN)
|
, mBestResourceDelta(INT_MIN)
|
||||||
, mBestResourceColorDepth(0)
|
, mBestResourceColorDepth(0)
|
||||||
, mNumIcons(0)
|
, mNumIcons(0)
|
||||||
|
@ -69,6 +71,20 @@ nsICODecoder::nsICODecoder(RasterImage* aImage)
|
||||||
, mCurrMaskLine(0)
|
, mCurrMaskLine(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsICODecoder::SetTargetSize(const nsIntSize& aSize)
|
||||||
|
{
|
||||||
|
// Make sure the size is reasonable.
|
||||||
|
if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a downscaler that we'll filter our output through.
|
||||||
|
mDownscaler.emplace(aSize);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsICODecoder::FinishInternal()
|
nsICODecoder::FinishInternal()
|
||||||
{
|
{
|
||||||
|
@ -222,16 +238,6 @@ nsICODecoder::ReadBIHSize(const char* aBIH)
|
||||||
return headerSize;
|
return headerSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
nsICODecoder::SetHotSpotIfCursor()
|
|
||||||
{
|
|
||||||
if (!mIsCursor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mImageMetadata.SetHotspot(mDirEntry.mXHotspot, mDirEntry.mYHotspot);
|
|
||||||
}
|
|
||||||
|
|
||||||
LexerTransition<ICOState>
|
LexerTransition<ICOState>
|
||||||
nsICODecoder::ReadHeader(const char* aData)
|
nsICODecoder::ReadHeader(const char* aData)
|
||||||
{
|
{
|
||||||
|
@ -248,10 +254,13 @@ nsICODecoder::ReadHeader(const char* aData)
|
||||||
return Transition::Terminate(ICOState::SUCCESS); // Nothing to do.
|
return Transition::Terminate(ICOState::SUCCESS); // Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we didn't get a #-moz-resolution, default to PREFICONSIZE.
|
// Downscale-during-decode can end up decoding different resources in the ICO
|
||||||
if (mResolution.width == 0 && mResolution.height == 0) {
|
// file depending on the target size. Since the resources are not necessarily
|
||||||
mResolution.SizeTo(PREFICONSIZE, PREFICONSIZE);
|
// scaled versions of the same image, some may be transparent and some may not
|
||||||
}
|
// be. We could be precise about transparency if we decoded the metadata of
|
||||||
|
// every resource, but for now we don't and it's safest to assume that
|
||||||
|
// transparency could be present.
|
||||||
|
PostHasTransparency();
|
||||||
|
|
||||||
return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
|
return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
|
||||||
}
|
}
|
||||||
|
@ -288,35 +297,69 @@ nsICODecoder::ReadDirEntry(const char* aData)
|
||||||
memcpy(&e.mImageOffset, aData + 12, sizeof(e.mImageOffset));
|
memcpy(&e.mImageOffset, aData + 12, sizeof(e.mImageOffset));
|
||||||
e.mImageOffset = LittleEndian::readUint32(&e.mImageOffset);
|
e.mImageOffset = LittleEndian::readUint32(&e.mImageOffset);
|
||||||
|
|
||||||
// Calculate the delta between this image's size and the desired size, so we
|
// Determine if this is the biggest resource we've seen so far. We always use
|
||||||
// can see if it is better than our current-best option. In the case of
|
// the biggest resource for the intrinsic size, and if we're not downscaling,
|
||||||
// several equally-good images, we use the last one. "Better" in this case is
|
// we select it as the best resource as well.
|
||||||
// determined by |delta|, a measure of the difference in size between the
|
IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
|
||||||
// entry we've found and the requested size. We will choose the smallest image
|
if (e.mBitCount >= mBiggestResourceColorDepth &&
|
||||||
// that is >= requested size (i.e. we assume it's better to downscale a larger
|
entrySize.width * entrySize.height >=
|
||||||
// icon than to upscale a smaller one).
|
mBiggestResourceSize.width * mBiggestResourceSize.height) {
|
||||||
int32_t delta = GetRealWidth(e) - mResolution.width +
|
mBiggestResourceSize = entrySize;
|
||||||
GetRealHeight(e) - mResolution.height;
|
mBiggestResourceColorDepth = e.mBitCount;
|
||||||
if (e.mBitCount >= mBestResourceColorDepth &&
|
mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
|
||||||
((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
|
|
||||||
(delta >= 0 && delta <= mBestResourceDelta))) {
|
|
||||||
mBestResourceDelta = delta;
|
|
||||||
|
|
||||||
// Ensure mImageOffset is >= size of the direntry headers (bug #245631).
|
if (!mDownscaler) {
|
||||||
if (e.mImageOffset < FirstResourceOffset()) {
|
mDirEntry = e;
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mBestResourceColorDepth = e.mBitCount;
|
if (mDownscaler) {
|
||||||
mDirEntry = e;
|
// Calculate the delta between this resource's size and the desired size, so
|
||||||
|
// we can see if it is better than our current-best option. In the case of
|
||||||
|
// several equally-good resources, we use the last one. "Better" in this
|
||||||
|
// case is determined by |delta|, a measure of the difference in size
|
||||||
|
// between the entry we've found and the downscaler's target size. We will
|
||||||
|
// choose the smallest resource that is >= the target size (i.e. we assume
|
||||||
|
// it's better to downscale a larger icon than to upscale a smaller one).
|
||||||
|
IntSize desiredSize = mDownscaler->TargetSize();
|
||||||
|
int32_t delta = entrySize.width - desiredSize.width +
|
||||||
|
entrySize.height - desiredSize.height;
|
||||||
|
if (e.mBitCount >= mBestResourceColorDepth &&
|
||||||
|
((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
|
||||||
|
(delta >= 0 && delta <= mBestResourceDelta))) {
|
||||||
|
mBestResourceDelta = delta;
|
||||||
|
mBestResourceColorDepth = e.mBitCount;
|
||||||
|
mDirEntry = e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCurrIcon == mNumIcons) {
|
if (mCurrIcon == mNumIcons) {
|
||||||
PostSize(GetRealWidth(mDirEntry), GetRealHeight(mDirEntry));
|
// Ensure the resource we selected has an offset past the ICO headers.
|
||||||
|
if (mDirEntry.mImageOffset < FirstResourceOffset()) {
|
||||||
|
return Transition::Terminate(ICOState::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a cursor, set the hotspot. We use the hotspot from the biggest
|
||||||
|
// resource since we also use that resource for the intrinsic size.
|
||||||
|
if (mIsCursor) {
|
||||||
|
mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
|
||||||
|
mBiggestResourceHotSpot.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always report the biggest resource's size as the intrinsic size; this
|
||||||
|
// is necessary for downscale-during-decode to work since we won't even
|
||||||
|
// attempt to *upscale* while decoding.
|
||||||
|
PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
|
||||||
if (IsMetadataDecode()) {
|
if (IsMetadataDecode()) {
|
||||||
return Transition::Terminate(ICOState::SUCCESS);
|
return Transition::Terminate(ICOState::SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the resource we selected matches the downscaler's target size
|
||||||
|
// perfectly, we don't need to do any downscaling.
|
||||||
|
if (mDownscaler && GetRealSize() == mDownscaler->TargetSize()) {
|
||||||
|
mDownscaler.reset();
|
||||||
|
}
|
||||||
|
|
||||||
size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
|
size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
|
||||||
return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
|
return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
|
||||||
ICOState::SKIP_TO_RESOURCE,
|
ICOState::SKIP_TO_RESOURCE,
|
||||||
|
@ -339,6 +382,9 @@ nsICODecoder::SniffResource(const char* aData)
|
||||||
mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
|
mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
|
||||||
mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
|
mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
|
||||||
mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
|
mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
|
||||||
|
if (mDownscaler) {
|
||||||
|
mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
|
||||||
|
}
|
||||||
mContainedDecoder->Init();
|
mContainedDecoder->Init();
|
||||||
|
|
||||||
if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
|
if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
|
||||||
|
@ -363,6 +409,9 @@ nsICODecoder::SniffResource(const char* aData)
|
||||||
mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
|
mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
|
||||||
mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
|
mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
|
||||||
mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
|
mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
|
||||||
|
if (mDownscaler) {
|
||||||
|
mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
|
||||||
|
}
|
||||||
mContainedDecoder->Init();
|
mContainedDecoder->Init();
|
||||||
|
|
||||||
// Make sure we have a sane size for the bitmap information header.
|
// Make sure we have a sane size for the bitmap information header.
|
||||||
|
@ -389,8 +438,7 @@ nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
|
||||||
|
|
||||||
// Raymond Chen says that 32bpp only are valid PNG ICOs
|
// Raymond Chen says that 32bpp only are valid PNG ICOs
|
||||||
// http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
|
// http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
|
||||||
if (!IsMetadataDecode() &&
|
if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
|
||||||
!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
|
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::Terminate(ICOState::FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,9 +468,6 @@ nsICODecoder::ReadBIH(const char* aData)
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::Terminate(ICOState::FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the cursor hot spot if one is present.
|
|
||||||
SetHotSpotIfCursor();
|
|
||||||
|
|
||||||
// Fix the ICO height from the BIH. It needs to be halved so our BMP decoder
|
// Fix the ICO height from the BIH. It needs to be halved so our BMP decoder
|
||||||
// will understand, because the BMP decoder doesn't expect the alpha mask that
|
// will understand, because the BMP decoder doesn't expect the alpha mask that
|
||||||
// follows the BMP data in an ICO.
|
// follows the BMP data in an ICO.
|
||||||
|
@ -567,9 +612,8 @@ nsICODecoder::FinishResource()
|
||||||
{
|
{
|
||||||
// Make sure the actual size of the resource matches the size in the directory
|
// Make sure the actual size of the resource matches the size in the directory
|
||||||
// entry. If not, we consider the image corrupt.
|
// entry. If not, we consider the image corrupt.
|
||||||
IntSize expectedSize(GetRealWidth(mDirEntry), GetRealHeight(mDirEntry));
|
|
||||||
if (mContainedDecoder->HasSize() &&
|
if (mContainedDecoder->HasSize() &&
|
||||||
mContainedDecoder->GetSize() != expectedSize) {
|
mContainedDecoder->GetSize() != GetRealSize()) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::Terminate(ICOState::FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@ class nsICODecoder : public Decoder
|
||||||
public:
|
public:
|
||||||
virtual ~nsICODecoder() { }
|
virtual ~nsICODecoder() { }
|
||||||
|
|
||||||
|
nsresult SetTargetSize(const nsIntSize& aSize) override;
|
||||||
|
|
||||||
/// @return the width of the icon directory entry @aEntry.
|
/// @return the width of the icon directory entry @aEntry.
|
||||||
static uint32_t GetRealWidth(const IconDirEntry& aEntry)
|
static uint32_t GetRealWidth(const IconDirEntry& aEntry)
|
||||||
{
|
{
|
||||||
|
@ -61,9 +63,10 @@ public:
|
||||||
/// @return the height of the selected directory entry (mDirEntry).
|
/// @return the height of the selected directory entry (mDirEntry).
|
||||||
uint32_t GetRealHeight() const { return GetRealHeight(mDirEntry); }
|
uint32_t GetRealHeight() const { return GetRealHeight(mDirEntry); }
|
||||||
|
|
||||||
virtual void SetResolution(const gfx::IntSize& aResolution) override
|
/// @return the size of the selected directory entry (mDirEntry).
|
||||||
|
gfx::IntSize GetRealSize() const
|
||||||
{
|
{
|
||||||
mResolution = aResolution;
|
return gfx::IntSize(GetRealWidth(), GetRealHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return The offset from the beginning of the ICO to the first resource.
|
/// @return The offset from the beginning of the ICO to the first resource.
|
||||||
|
@ -86,8 +89,6 @@ private:
|
||||||
// Gets decoder state from the contained decoder so it's visible externally.
|
// Gets decoder state from the contained decoder so it's visible externally.
|
||||||
void GetFinalStateFromContainedDecoder();
|
void GetFinalStateFromContainedDecoder();
|
||||||
|
|
||||||
// Sets the hotspot property of if we have a cursor
|
|
||||||
void SetHotSpotIfCursor();
|
|
||||||
// Creates a bitmap file header buffer, returns true if successful
|
// Creates a bitmap file header buffer, returns true if successful
|
||||||
bool FillBitmapFileHeaderBuffer(int8_t* bfh);
|
bool FillBitmapFileHeaderBuffer(int8_t* bfh);
|
||||||
// Fixes the ICO height to match that of the BIH.
|
// Fixes the ICO height to match that of the BIH.
|
||||||
|
@ -118,10 +119,13 @@ private:
|
||||||
LexerTransition<ICOState> FinishResource();
|
LexerTransition<ICOState> FinishResource();
|
||||||
|
|
||||||
StreamingLexer<ICOState, 32> mLexer; // The lexer.
|
StreamingLexer<ICOState, 32> mLexer; // The lexer.
|
||||||
|
Maybe<Downscaler> mDownscaler; // Our downscaler, if we're downscaling.
|
||||||
nsRefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
|
nsRefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
|
||||||
gfx::IntSize mResolution; // The requested -moz-resolution.
|
|
||||||
char mBIHraw[40]; // The bitmap information header.
|
char mBIHraw[40]; // The bitmap information header.
|
||||||
IconDirEntry mDirEntry; // The dir entry for the selected resource.
|
IconDirEntry mDirEntry; // The dir entry for the selected resource.
|
||||||
|
IntSize mBiggestResourceSize; // Used to select the intrinsic size.
|
||||||
|
IntSize mBiggestResourceHotSpot; // Used to select the intrinsic size.
|
||||||
|
uint16_t mBiggestResourceColorDepth; // Used to select the intrinsic size.
|
||||||
int32_t mBestResourceDelta; // Used to select the best resource.
|
int32_t mBestResourceDelta; // Used to select the best resource.
|
||||||
uint16_t mBestResourceColorDepth; // Used to select the best resource.
|
uint16_t mBestResourceColorDepth; // Used to select the best resource.
|
||||||
uint16_t mNumIcons; // Stores the number of icons in the ICO file.
|
uint16_t mNumIcons; // Stores the number of icons in the ICO file.
|
||||||
|
|
|
@ -57,8 +57,9 @@ function testFiles() {
|
||||||
yield ["opaque.bmp", false];
|
yield ["opaque.bmp", false];
|
||||||
|
|
||||||
// ICO files which contain BMPs have an additional type of transparency - the
|
// ICO files which contain BMPs have an additional type of transparency - the
|
||||||
// AND mask - that warrants separate testing.
|
// AND mask - that warrants separate testing. (Although, after bug 1201796,
|
||||||
yield ["ico-bmp-opaque.ico", false];
|
// all ICOs are considered transparent.)
|
||||||
|
yield ["ico-bmp-opaque.ico", true];
|
||||||
yield ["ico-bmp-transparent.ico", true];
|
yield ["ico-bmp-transparent.ico", true];
|
||||||
|
|
||||||
// SVGs are always transparent.
|
// SVGs are always transparent.
|
||||||
|
|
Двоичные данные
image/test/reftest/ico/cur/pointer.cur
Двоичные данные
image/test/reftest/ico/cur/pointer.cur
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 14 KiB После Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -1,14 +1,3 @@
|
||||||
# ICO BMP and PNG mixed tests
|
# ICO BMP and PNG mixed tests
|
||||||
|
|
||||||
== mixed-bmp-png.ico mixed-bmp-png.png
|
== mixed-bmp-png.ico mixed-bmp-png48.png
|
||||||
|
|
||||||
# Using media fragments to select different resolutions
|
|
||||||
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=8,8 mixed-bmp-png.png
|
|
||||||
== mixed-bmp-png.ico#test=true&-moz-resolution=8,8&other mixed-bmp-png.png
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=32,32 mixed-bmp-png32.png
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=39,39 mixed-bmp-png48.png
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=40,40 mixed-bmp-png48.png
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=48,48 mixed-bmp-png48.png
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=64,64 mixed-bmp-png48.png
|
|
||||||
== mixed-bmp-png.ico#-moz-resolution=64 mixed-bmp-png.png # Bad syntax will fall back to lowest resolution
|
|
||||||
|
|
Двоичные данные
image/test/reftest/ico/ico-png/transparent-png.ico
Двоичные данные
image/test/reftest/ico/ico-png/transparent-png.ico
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 3.0 KiB После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -691,8 +691,10 @@ var errsrc = "none";
|
||||||
try {
|
try {
|
||||||
container = imgTools.decodeImage(istream, inMimeType);
|
container = imgTools.decodeImage(istream, inMimeType);
|
||||||
|
|
||||||
// We should never hit this - decodeImage throws an assertion because the
|
// We expect to hit an error during encoding because the ICO header of the
|
||||||
// image decoded doesn't have enough frames.
|
// image is fine, but the actual resources are corrupt. Since decodeImage()
|
||||||
|
// only performs a metadata decode, it doesn't decode far enough to realize
|
||||||
|
// this, but we'll find out when we do a full decode during encodeImage().
|
||||||
try {
|
try {
|
||||||
istream = imgTools.encodeImage(container, "image/png");
|
istream = imgTools.encodeImage(container, "image/png");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -704,7 +706,7 @@ try {
|
||||||
errsrc = "decode";
|
errsrc = "decode";
|
||||||
}
|
}
|
||||||
|
|
||||||
do_check_eq(errsrc, "decode");
|
do_check_eq(errsrc, "encode");
|
||||||
checkExpectedError(/NS_ERROR_FAILURE/, err);
|
checkExpectedError(/NS_ERROR_FAILURE/, err);
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче