Bug 882526 - Remove Gecko support for WBMP. r=joe, r=roc
@ -55,4 +55,3 @@ MOZ_TOOLKIT_SEARCH=
|
||||
MOZ_PLACES=
|
||||
MOZ_B2G=1
|
||||
MOZ_FOLD_LIBS=1
|
||||
MOZ_WBMP=1
|
||||
|
@ -8605,12 +8605,6 @@ if test -n "$MOZ_CAPTIVEDETECT"; then
|
||||
AC_DEFINE(MOZ_CAPTIVEDETECT)
|
||||
fi
|
||||
|
||||
dnl Build WBMP decoder if required
|
||||
AC_SUBST(MOZ_WBMP)
|
||||
if test -n "$MOZ_WBMP"; then
|
||||
AC_DEFINE(MOZ_WBMP)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
if test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC" -o "$MOZ_DMD"; then
|
||||
MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
|
||||
|
@ -73,9 +73,6 @@ static const mozilla::Module::CategoryEntry kImageCategories[] = {
|
||||
{ "Gecko-Content-Viewers", IMAGE_ICON_MS, "@mozilla.org/content/document-loader-factory;1" },
|
||||
{ "Gecko-Content-Viewers", IMAGE_PNG, "@mozilla.org/content/document-loader-factory;1" },
|
||||
{ "Gecko-Content-Viewers", IMAGE_X_PNG, "@mozilla.org/content/document-loader-factory;1" },
|
||||
#ifdef MOZ_WBMP
|
||||
{ "Gecko-Content-Viewers", IMAGE_WBMP, "@mozilla.org/content/document-loader-factory;1" },
|
||||
#endif
|
||||
{ "content-sniffing-services", "@mozilla.org/image/loader;1", "@mozilla.org/image/loader;1" },
|
||||
{ NULL }
|
||||
};
|
||||
|
@ -34,10 +34,5 @@ CPP_SOURCES += [
|
||||
'nsPNGDecoder.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WBMP']:
|
||||
CPP_SOURCES += [
|
||||
'nsWBMPDecoder.cpp',
|
||||
]
|
||||
|
||||
LIBRARY_NAME = 'imgdecoders_s'
|
||||
|
||||
|
@ -1,279 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* 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 "nsWBMPDecoder.h"
|
||||
#include "RasterImage.h"
|
||||
#include "nspr.h"
|
||||
#include "nsRect.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
#include "nsError.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
static inline void SetPixel(uint32_t*& aDecoded, bool aPixelWhite)
|
||||
{
|
||||
uint8_t pixelValue = aPixelWhite ? 255 : 0;
|
||||
|
||||
*aDecoded++ = gfxPackedPixel(0xFF, pixelValue, pixelValue, pixelValue);
|
||||
}
|
||||
|
||||
/** Parses a WBMP encoded int field. Returns IntParseInProgress (out of
|
||||
* data), IntParseSucceeded if the field was read OK or IntParseFailed
|
||||
* on an error.
|
||||
* The encoding used for WBMP ints is per byte. The high bit is a
|
||||
* continuation flag saying (when set) that the next byte is part of the
|
||||
* field, and the low seven bits are data. New data bits are added in the
|
||||
* low bit positions, i.e. the field is big-endian (ignoring the high bits).
|
||||
* @param aField Variable holds current value of field. When this function
|
||||
* returns IntParseInProgress, aField will hold the
|
||||
* intermediate result of the decoding, so this function can be
|
||||
* called repeatedly for new bytes on the same field and will
|
||||
* operate correctly.
|
||||
* @param aBuffer Points to encoded field data.
|
||||
* @param aCount Number of bytes in aBuffer. */
|
||||
static WbmpIntDecodeStatus DecodeEncodedInt (uint32_t& aField, const char*& aBuffer, uint32_t& aCount)
|
||||
{
|
||||
while (aCount > 0) {
|
||||
// Check if the result would overflow if another seven bits were added.
|
||||
// The actual test performed is AND to check if any of the top seven bits are set.
|
||||
if (aField & 0xFE000000) {
|
||||
// Overflow :(
|
||||
return IntParseFailed;
|
||||
}
|
||||
|
||||
// Get next encoded byte.
|
||||
char encodedByte = *aBuffer;
|
||||
|
||||
// Update buffer state variables now we have read this byte.
|
||||
aBuffer++;
|
||||
aCount--;
|
||||
|
||||
// Work out and store the new (valid) value of the encoded int with this byte added.
|
||||
aField = (aField << 7) + (uint32_t)(encodedByte & 0x7F);
|
||||
|
||||
if (!(encodedByte & 0x80)) {
|
||||
// No more bytes, value is complete.
|
||||
return IntParseSucceeded;
|
||||
}
|
||||
}
|
||||
|
||||
// Out of data but in the middle of an encoded int.
|
||||
return IntParseInProgress;
|
||||
}
|
||||
|
||||
nsWBMPDecoder::nsWBMPDecoder(RasterImage &aImage)
|
||||
: Decoder(aImage),
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mRow(nullptr),
|
||||
mRowBytes(0),
|
||||
mCurLine(0),
|
||||
mState(WbmpStateStart)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
nsWBMPDecoder::~nsWBMPDecoder()
|
||||
{
|
||||
moz_free(mRow);
|
||||
}
|
||||
|
||||
void
|
||||
nsWBMPDecoder::WriteInternal(const char *aBuffer, uint32_t aCount)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
|
||||
|
||||
// Loop until the input data is gone
|
||||
while (aCount > 0) {
|
||||
switch (mState) {
|
||||
case WbmpStateStart:
|
||||
{
|
||||
// Since we only accept a type 0 WBMP we can just check the first byte is 0.
|
||||
// (The specification says a well defined type 0 bitmap will start with a 0x00 byte).
|
||||
if (*aBuffer++ == 0x00) {
|
||||
// This is a type 0 WBMP.
|
||||
aCount--;
|
||||
mState = DecodingFixHeader;
|
||||
} else {
|
||||
// This is a new type of WBMP or a type 0 WBMP defined oddly (e.g. 0x80 0x00)
|
||||
PostDataError();
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DecodingFixHeader:
|
||||
{
|
||||
if ((*aBuffer++ & 0x9F) == 0x00) {
|
||||
// Fix header field is as expected
|
||||
aCount--;
|
||||
// For now, we skip the ext header field as it is not in a well-defined type 0 WBMP.
|
||||
mState = DecodingWidth;
|
||||
} else {
|
||||
// Can't handle this fix header field.
|
||||
PostDataError();
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DecodingWidth:
|
||||
{
|
||||
WbmpIntDecodeStatus widthReadResult = DecodeEncodedInt (mWidth, aBuffer, aCount);
|
||||
|
||||
if (widthReadResult == IntParseSucceeded) {
|
||||
mState = DecodingHeight;
|
||||
} else if (widthReadResult == IntParseFailed) {
|
||||
// Encoded width was bigger than a uint32_t or equal to 0.
|
||||
PostDataError();
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
} else {
|
||||
// We are still parsing the encoded int field.
|
||||
NS_ABORT_IF_FALSE((widthReadResult == IntParseInProgress),
|
||||
"nsWBMPDecoder got bad result from an encoded width field");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DecodingHeight:
|
||||
{
|
||||
WbmpIntDecodeStatus heightReadResult = DecodeEncodedInt (mHeight, aBuffer, aCount);
|
||||
|
||||
if (heightReadResult == IntParseSucceeded) {
|
||||
// The header has now been entirely read.
|
||||
const uint32_t k64KWidth = 0x0000FFFF;
|
||||
if (mWidth == 0 || mWidth > k64KWidth
|
||||
|| mHeight == 0 || mHeight > k64KWidth) {
|
||||
// consider 0 as an incorrect image size
|
||||
// reject the extremely wide/high images to keep the math sane
|
||||
PostDataError();
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Post our size to the superclass
|
||||
PostSize(mWidth, mHeight);
|
||||
if (HasError()) {
|
||||
// Setting the size led to an error.
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
}
|
||||
|
||||
// If We're doing a size decode, we're done
|
||||
if (IsSizeDecode()) {
|
||||
mState = WbmpStateFinished;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mImageData) {
|
||||
PostDecoderError(NS_ERROR_FAILURE);
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create mRow, the buffer that holds one line of the raw image data
|
||||
mRow = (uint8_t*)moz_malloc((mWidth + 7) / 8);
|
||||
if (!mRow) {
|
||||
PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
}
|
||||
|
||||
mState = DecodingImageData;
|
||||
|
||||
} else if (heightReadResult == IntParseFailed) {
|
||||
// Encoded height was bigger than a uint32_t.
|
||||
PostDataError();
|
||||
mState = DecodingFailed;
|
||||
return;
|
||||
} else {
|
||||
// We are still parsing the encoded int field.
|
||||
NS_ABORT_IF_FALSE((heightReadResult == IntParseInProgress),
|
||||
"nsWBMPDecoder got bad result from an encoded height field");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DecodingImageData:
|
||||
{
|
||||
uint32_t rowSize = (mWidth + 7) / 8; // +7 to round up to nearest byte
|
||||
uint32_t top = mCurLine;
|
||||
|
||||
// Process up to one row of data at a time until there is no more data.
|
||||
while ((aCount > 0) && (mCurLine < mHeight)) {
|
||||
// Calculate if we need to copy data to fill the next buffered row of raw data.
|
||||
uint32_t toCopy = rowSize - mRowBytes;
|
||||
|
||||
// If required, copy raw data to fill a buffered row of raw data.
|
||||
if (toCopy) {
|
||||
if (toCopy > aCount)
|
||||
toCopy = aCount;
|
||||
memcpy(mRow + mRowBytes, aBuffer, toCopy);
|
||||
aCount -= toCopy;
|
||||
aBuffer += toCopy;
|
||||
mRowBytes += toCopy;
|
||||
}
|
||||
|
||||
// If there is a filled buffered row of raw data, process the row.
|
||||
if (rowSize == mRowBytes) {
|
||||
uint8_t *p = mRow;
|
||||
uint32_t *d = reinterpret_cast<uint32_t*>(mImageData) + (mWidth * mCurLine); // position of the first pixel at mCurLine
|
||||
uint32_t lpos = 0;
|
||||
|
||||
while (lpos < mWidth) {
|
||||
for (int8_t bit = 7; bit >= 0; bit--) {
|
||||
if (lpos >= mWidth)
|
||||
break;
|
||||
bool pixelWhite = (*p >> bit) & 1;
|
||||
SetPixel(d, pixelWhite);
|
||||
++lpos;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
|
||||
mCurLine++;
|
||||
mRowBytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
nsIntRect r(0, top, mWidth, mCurLine - top);
|
||||
// Invalidate
|
||||
PostInvalidation(r);
|
||||
|
||||
// If we've got all the pixel bytes, we're finished
|
||||
if (mCurLine == mHeight) {
|
||||
PostFrameStop();
|
||||
PostDecodeDone();
|
||||
mState = WbmpStateFinished;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WbmpStateFinished:
|
||||
{
|
||||
// Consume all excess data silently
|
||||
aCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case DecodingFailed:
|
||||
{
|
||||
NS_ABORT_IF_FALSE(0, "Shouldn't process any data after decode failed!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
@ -1,62 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* 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 nsWBMPDecoder_h__
|
||||
#define nsWBMPDecoder_h__
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "imgDecoderObserver.h"
|
||||
#include "gfxColor.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
class RasterImage;
|
||||
|
||||
/* WBMP is a monochrome graphics file format optimized for mobile computing devices.
|
||||
* Format description from http://www.wapforum.org/what/technical/SPEC-WAESpec-19990524.pdf
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
WbmpStateStart,
|
||||
DecodingFixHeader,
|
||||
DecodingWidth,
|
||||
DecodingHeight,
|
||||
DecodingImageData,
|
||||
DecodingFailed,
|
||||
WbmpStateFinished
|
||||
} WbmpDecodingState;
|
||||
|
||||
typedef enum {
|
||||
IntParseSucceeded,
|
||||
IntParseFailed,
|
||||
IntParseInProgress
|
||||
} WbmpIntDecodeStatus;
|
||||
|
||||
class nsWBMPDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
nsWBMPDecoder(RasterImage &aImage);
|
||||
virtual ~nsWBMPDecoder();
|
||||
|
||||
virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
|
||||
|
||||
private:
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
|
||||
uint8_t* mRow; // Holds one raw line of the image
|
||||
uint32_t mRowBytes; // How many bytes of the row were already received
|
||||
uint32_t mCurLine; // The current line being decoded (0 to mHeight - 1)
|
||||
|
||||
WbmpDecodingState mState; // Describes what part of the file we are decoding now.
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // nsWBMPDecoder_h__
|
@ -86,12 +86,6 @@ Image::GetDecoderType(const char *aMimeType)
|
||||
else if (!strcmp(aMimeType, IMAGE_ICON_MS))
|
||||
rv = eDecoderType_icon;
|
||||
|
||||
#ifdef MOZ_WBMP
|
||||
// WBMP
|
||||
else if (!strcmp(aMimeType, IMAGE_WBMP))
|
||||
rv = eDecoderType_wbmp;
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,7 @@ public:
|
||||
eDecoderType_bmp = 3,
|
||||
eDecoderType_ico = 4,
|
||||
eDecoderType_icon = 5,
|
||||
eDecoderType_wbmp = 6,
|
||||
eDecoderType_unknown = 7
|
||||
eDecoderType_unknown = 6
|
||||
};
|
||||
static eDecoderType GetDecoderType(const char *aMimeType);
|
||||
|
||||
|
@ -33,10 +33,6 @@
|
||||
#include "nsICODecoder.h"
|
||||
#include "nsIconDecoder.h"
|
||||
|
||||
#ifdef MOZ_WBMP
|
||||
#include "nsWBMPDecoder.h"
|
||||
#endif
|
||||
|
||||
#include "gfxContext.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
@ -1997,11 +1993,6 @@ RasterImage::InitDecoder(bool aDoSizeDecode, bool aIsSynchronous /* = false */)
|
||||
case eDecoderType_icon:
|
||||
mDecoder = new nsIconDecoder(*this);
|
||||
break;
|
||||
#ifdef MOZ_WBMP
|
||||
case eDecoderType_wbmp:
|
||||
mDecoder = new nsWBMPDecoder(*this);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
|
||||
}
|
||||
|
@ -706,17 +706,6 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
|
||||
LOG_MSG(GetImgLog(), "imgRequest::OnDataAvailable", "Got content type from the channel");
|
||||
}
|
||||
|
||||
#ifdef MOZ_WBMP
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Only support WBMP in privileged app and certified app, do not support in browser app.
|
||||
if (newType.EqualsLiteral(IMAGE_WBMP) &&
|
||||
(!mLoadingPrincipal || mLoadingPrincipal->GetAppStatus() < nsIPrincipal::APP_STATUS_PRIVILEGED)) {
|
||||
this->Cancel(NS_ERROR_FAILURE);
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If we're a regular image and this is the first call to OnDataAvailable,
|
||||
// this will always be true. If we've resniffed our MIME type (i.e. we're a
|
||||
// multipart/x-mixed-replace image), we have to be able to switch our image
|
||||
|
@ -46,8 +46,5 @@ include color-management/reftest.list
|
||||
# Lossless encoders
|
||||
skip-if(Android||B2G) include encoders-lossless/reftest.list # bug 783621
|
||||
|
||||
# WBMP tests
|
||||
#skip-if(!B2G) include wbmp/reftest.list # bug 852053
|
||||
|
||||
# Animated image disposal and blending operator tests
|
||||
include animated/reftest.list
|
||||
|
@ -1,4 +0,0 @@
|
||||
# WBMP tests
|
||||
|
||||
include wbmp-normal/reftest.list
|
||||
include wbmp-corrupted/reftest.list
|
@ -1,25 +0,0 @@
|
||||
# Corrupted WBMP tests
|
||||
|
||||
# Tests for corrupted header value
|
||||
== wrapper.html?err_fh_b0.wbmp about:blank
|
||||
== wrapper.html?err_fh_b1.wbmp about:blank
|
||||
== wrapper.html?err_fh_b2.wbmp about:blank
|
||||
== wrapper.html?err_fh_b3.wbmp about:blank
|
||||
== wrapper.html?err_fh_b4.wbmp about:blank
|
||||
== wrapper.html?err_fh_b5.wbmp about:blank
|
||||
== wrapper.html?err_fh_b6.wbmp about:blank
|
||||
== wrapper.html?err_fh_b7_and_ext_header.wbmp about:blank
|
||||
# Tests for corrupted size value
|
||||
== wrapper.html?err_incomplete_height.wbmp about:blank
|
||||
== wrapper.html?err_incomplete_width.wbmp about:blank
|
||||
== wrapper.html?over_height.wbmp about:blank
|
||||
== wrapper.html?over_width.wbmp about:blank
|
||||
# Tests for unsupported type value
|
||||
== wrapper.html?err_type_1.wbmp about:blank
|
||||
== wrapper.html?err_type_fix_only.wbmp about:blank
|
||||
== wrapper.html?err_type_fix_width_only.wbmp about:blank
|
||||
== wrapper.html?err_type_header_only.wbmp about:blank
|
||||
# Test for empty size value
|
||||
== wrapper.html?height_0.wbmp about:blank
|
||||
== wrapper.html?width_0.wbmp about:blank
|
||||
== wrapper.html?width_0_height_0.wbmp about:blank
|
@ -1,27 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Image reftest wrapper</title>
|
||||
<style type="text/css">
|
||||
#image1 { background-color: rgb(10, 100, 250); }
|
||||
</style>
|
||||
<script>
|
||||
// The image is loaded async after the page loads
|
||||
// wait for it to finish loading
|
||||
function onImageLoad() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="image1">
|
||||
<script>
|
||||
// Use as "wrapper.html?image.png"
|
||||
var imgURL = document.location.search.substr(1);
|
||||
document.images[0].onload = onImageLoad;
|
||||
document.images[0].onerror = onImageLoad;
|
||||
document.images[0].src = imgURL;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 577 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 560 B |
@ -1,11 +0,0 @@
|
||||
# WBMP tests
|
||||
|
||||
== bmts.wbmp bmts.png
|
||||
== ok_fh_b5.wbmp ok_fh_b5.png
|
||||
== ok_fh_b6.wbmp ok_fh_b6.png
|
||||
== height_1.wbmp height_1.png
|
||||
== width_1.wbmp width_1.png
|
||||
== width_1_height_1.wbmp width_1_height_1.png
|
||||
== multi_byte_height.wbmp multi_byte_height.png
|
||||
== multi_byte_width.wbmp multi_byte_width.png
|
||||
== large.wbmp large.png
|
Before Width: | Height: | Size: 213 B |
Before Width: | Height: | Size: 200 B |
@ -102,7 +102,6 @@
|
||||
#define IMAGE_MNG "video/x-mng"
|
||||
#define IMAGE_JNG "image/x-jng"
|
||||
#define IMAGE_SVG_XML "image/svg+xml"
|
||||
#define IMAGE_WBMP "image/vnd.wap.wbmp"
|
||||
|
||||
#define MESSAGE_EXTERNAL_BODY "message/external-body"
|
||||
#define MESSAGE_NEWS "message/news"
|
||||
|
@ -480,9 +480,6 @@ static nsExtraMimeTypeEntry extraMimeEntries [] =
|
||||
{ IMAGE_PNG, "png", "PNG Image" },
|
||||
{ IMAGE_TIFF, "tiff,tif", "TIFF Image" },
|
||||
{ IMAGE_XBM, "xbm", "XBM Image" },
|
||||
#ifdef MOZ_WBMP
|
||||
{ IMAGE_WBMP, "wbmp", "WBMP Image" },
|
||||
#endif
|
||||
{ "image/svg+xml", "svg", "Scalable Vector Graphics" },
|
||||
{ MESSAGE_RFC822, "eml", "RFC-822 data" },
|
||||
{ TEXT_PLAIN, "txt,text", "Text File" },
|
||||
|