Bug 389273 - large favicons (>32 KB) won't show up in url bar autocomplete, history / bookmarks menu, bm organizer. r=sspitzer, stuart. a1.9=schrep

This commit is contained in:
dolske@mozilla.com 2008-01-12 20:15:20 -08:00
parent 885d5db188
commit 97a57a7769
37 changed files with 1211 additions and 14 deletions

View File

@ -58,6 +58,7 @@
#include "imgLoader.h"
#include "imgRequest.h"
#include "imgRequestProxy.h"
#include "imgTools.h"
#ifdef IMG_BUILD_DECODER_gif
// gif
@ -102,6 +103,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(imgCache)
NS_GENERIC_FACTORY_CONSTRUCTOR(imgContainer)
NS_GENERIC_FACTORY_CONSTRUCTOR(imgLoader)
NS_GENERIC_FACTORY_CONSTRUCTOR(imgRequestProxy)
NS_GENERIC_FACTORY_CONSTRUCTOR(imgTools)
#ifdef IMG_BUILD_DECODER_gif
// gif
@ -217,6 +219,10 @@ static const nsModuleComponentInfo components[] =
NS_IMGREQUESTPROXY_CID,
"@mozilla.org/image/request;1",
imgRequestProxyConstructor, },
{ "image tools",
NS_IMGTOOLS_CID,
"@mozilla.org/image/tools;1",
imgToolsConstructor, },
#ifdef IMG_BUILD_DECODER_gif
// gif

View File

@ -49,4 +49,5 @@
#define NS_IMAGELIB_ERROR_NO_DECODER NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 6)
#define NS_IMAGELIB_ERROR_NOT_FINISHED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 7)
#define NS_IMAGELIB_ERROR_LOAD_ABORTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 8)
#define NS_IMAGELIB_ERROR_NO_ENCODER NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 9)

View File

@ -52,11 +52,12 @@ XPIDLSRCS = \
imgIContainer.idl \
imgIContainerObserver.idl \
imgIDecoder.idl \
imgIDecoderObserver.idl \
imgIDecoderObserver.idl \
imgIEncoder.idl \
imgILoad.idl \
imgILoader.idl \
imgIRequest.idl \
imgIRequest.idl \
imgITools.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIInputStream;
interface imgIContainer;
[scriptable, uuid(c395d8f1-c616-4a1b-adfd-747b4b1b2cbe)]
interface imgITools : nsISupports
{
/**
* decodeImageData
* Caller provides an input stream and mimetype. We read from the stream
* and decompress it (according to the specified mime type) and return
* the resulting imgIContainer. (If the caller already has a container,
* it can be provided as input to be reused).
*
* @param aStream
* An input stream for an encoded image file.
* @param aMimeType
* Type of image in the stream.
* @param aContainer
* An imgIContainer holding the decoded image. Specify |null| when
* calling to have one created, otherwise specify a container to
* be reused.
*/
void decodeImageData(in nsIInputStream aStream,
in ACString aMimeType,
inout imgIContainer aContainer);
/**
* encodeImage
* Caller provides an image container, and the mime type it should be
* encoded to. We return an input stream for the encoded image data.
*
* @param aContainer
* An image container.
* @param aMimeType
* Type of encoded image desired (eg "image/png").
*/
nsIInputStream encodeImage(in imgIContainer aContainer,
in ACString aMimeType);
/**
* encodeScaledImage
* Caller provides an image container, and the mime type it should be
* encoded to. We return an input stream for the encoded image data.
* The encoded image is scaled to the specified dimensions.
*
* @param aContainer
* An image container.
* @param aMimeType
* Type of encoded image desired (eg "image/png").
* @param aWidth, aHeight
* The size (in pixels) desired for the resulting image.
*/
nsIInputStream encodeScaledImage(in imgIContainer aContainer,
in ACString aMimeType,
in long aWidth,
in long aHeight);
};

View File

@ -66,7 +66,8 @@ CPPSRCS = \
imgContainer.cpp \
imgLoader.cpp \
imgRequest.cpp \
imgRequestProxy.cpp
imgRequestProxy.cpp \
imgTools.cpp
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,356 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "imgTools.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "ImageErrors.h"
#include "imgIContainer.h"
#include "imgILoad.h"
#include "imgIDecoder.h"
#include "imgIEncoder.h"
#include "imgIDecoderObserver.h"
#include "imgIContainerObserver.h"
#include "nsIImage.h"
#include "gfxIImageFrame.h"
#include "gfxImageSurface.h"
#include "gfxContext.h"
#include "nsStringStream.h"
#include "nsComponentManagerUtils.h"
#include "nsWeakReference.h"
#include "nsIInterfaceRequestorUtils.h"
/* ========== Utility classes ========== */
class HelperLoader : public imgILoad,
public imgIDecoderObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGILOAD
NS_DECL_IMGIDECODEROBSERVER
NS_DECL_IMGICONTAINEROBSERVER
HelperLoader(void);
private:
nsCOMPtr<imgIContainer> mContainer;
};
NS_IMPL_ISUPPORTS4 (HelperLoader, imgILoad, imgIDecoderObserver, imgIContainerObserver, nsISupportsWeakReference)
HelperLoader::HelperLoader (void)
{
}
/* Implement imgILoad::image getter */
NS_IMETHODIMP
HelperLoader::GetImage(imgIContainer **aImage)
{
*aImage = mContainer;
NS_IF_ADDREF (*aImage);
return NS_OK;
}
/* Implement imgILoad::image setter */
NS_IMETHODIMP
HelperLoader::SetImage(imgIContainer *aImage)
{
mContainer = aImage;
return NS_OK;
}
/* Implement imgILoad::isMultiPartChannel getter */
NS_IMETHODIMP
HelperLoader::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
{
*aIsMultiPartChannel = PR_FALSE;
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartRequest() */
NS_IMETHODIMP
HelperLoader::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartDecode() */
NS_IMETHODIMP
HelperLoader::OnStartDecode(imgIRequest *aRequest)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartContainer() */
NS_IMETHODIMP
HelperLoader::OnStartContainer(imgIRequest *aRequest, imgIContainer
*aContainer)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartFrame() */
NS_IMETHODIMP
HelperLoader::OnStartFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onDataAvailable() */
NS_IMETHODIMP
HelperLoader::OnDataAvailable(imgIRequest *aRequest, gfxIImageFrame
*aFrame, const nsIntRect * aRect)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopFrame() */
NS_IMETHODIMP
HelperLoader::OnStopFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopContainer() */
NS_IMETHODIMP
HelperLoader::OnStopContainer(imgIRequest *aRequest, imgIContainer
*aContainer)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopDecode() */
NS_IMETHODIMP
HelperLoader::OnStopDecode(imgIRequest *aRequest, nsresult status, const
PRUnichar *statusArg)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopRequest() */
NS_IMETHODIMP
HelperLoader::OnStopRequest(imgIRequest *aRequest, PRBool aIsLastPart)
{
return NS_OK;
}
/* implement imgIContainerObserver::frameChanged() */
NS_IMETHODIMP
HelperLoader::FrameChanged(imgIContainer *aContainer,
gfxIImageFrame *aFrame, nsIntRect * aDirtyRect)
{
return NS_OK;
}
/* ========== imgITools implementation ========== */
NS_IMPL_ISUPPORTS1(imgTools, imgITools)
imgTools::imgTools()
{
/* member initializers and constructor code */
}
imgTools::~imgTools()
{
/* destructor code */
}
NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
const nsACString& aMimeType,
imgIContainer **aContainer)
{
nsresult rv;
// Get an image decoder for our media type
nsCAutoString decoderCID(
NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + aMimeType);
nsCOMPtr<imgIDecoder> decoder = do_CreateInstance(decoderCID.get());
if (!decoder)
return NS_IMAGELIB_ERROR_NO_DECODER;
// Init the decoder, we use a small utility class here.
nsCOMPtr<imgILoad> loader = new HelperLoader();
if (!loader)
return NS_ERROR_OUT_OF_MEMORY;
// If caller provided an existing container, use it.
if (*aContainer)
loader->SetImage(*aContainer);
rv = decoder->Init(loader);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 length;
rv = aInStr->Available(&length);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 written;
NS_ENSURE_SUCCESS(rv, rv);
rv = decoder->WriteFrom(aInStr, length, &written);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(written == length, "decoder didn't eat all of its vegetables");
rv = decoder->Flush();
NS_ENSURE_SUCCESS(rv, rv);
rv = decoder->Close();
NS_ENSURE_SUCCESS(rv, rv);
// If caller didn't provide an existing container, return the new one.
if (!*aContainer)
loader->GetImage(aContainer);
return NS_OK;
}
NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
const nsACString& aMimeType,
nsIInputStream **aStream)
{
return EncodeScaledImage(aContainer, aMimeType, 0, 0, aStream);
}
NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer,
const nsACString& aMimeType,
PRInt32 aScaledWidth,
PRInt32 aScaledHeight,
nsIInputStream **aStream)
{
nsresult rv;
PRBool doScaling = PR_TRUE;
PRUint8 *bitmapData;
PRUint32 bitmapDataLength, strideSize;
// If no scaled size is specified, we'll just encode the image at its
// original size (no scaling).
if (aScaledWidth == 0 && aScaledHeight == 0) {
doScaling = PR_FALSE;
} else {
NS_ENSURE_ARG(aScaledWidth > 0);
NS_ENSURE_ARG(aScaledHeight > 0);
}
// Get an image encoder for the media type
nsCAutoString encoderCID(
NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
if (!encoder)
return NS_IMAGELIB_ERROR_NO_ENCODER;
// Use frame 0 from the image container.
nsCOMPtr<gfxIImageFrame> frame;
rv = aContainer->GetFrameAt(0, getter_AddRefs(frame));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 w,h;
frame->GetWidth(&w);
frame->GetHeight(&h);
if (!w || !h)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIImage> img(do_GetInterface(frame));
nsRefPtr<gfxImageSurface> dest;
if (!doScaling) {
// If we're not scaling the image, use the actual width/height.
aScaledWidth = w;
aScaledHeight = h;
img->LockImagePixels(PR_FALSE);
bitmapData = img->GetBits();
if (!bitmapData) {
img->UnlockImagePixels(PR_FALSE);
return NS_ERROR_FAILURE;
}
frame->GetImageBytesPerRow(&strideSize);
bitmapDataLength = aScaledHeight * strideSize;
} else {
// Prepare to draw a scaled version of the image to a temporary surface...
// Get the source image surface
nsRefPtr<gfxASurface> gfxsurf;
rv = img->GetSurface(getter_AddRefs(gfxsurf));
NS_ENSURE_SUCCESS(rv, rv);
// Create a temporary image surface
dest = new gfxImageSurface(gfxIntSize(aScaledWidth, aScaledHeight),
gfxASurface::ImageFormatARGB32);
if (!dest)
return NS_ERROR_OUT_OF_MEMORY;
gfxContext ctx(dest);
// Set scaling
gfxFloat sw = (double) aScaledWidth / w;
gfxFloat sh = (double) aScaledHeight / h;
ctx.Scale(sw, sh);
// Paint a scaled image
ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
ctx.SetSource(gfxsurf);
ctx.Paint();
bitmapData = dest->Data();
strideSize = dest->Stride();
bitmapDataLength = aScaledHeight * strideSize;
}
// Encode the bitmap
rv = encoder->InitFromData(bitmapData, bitmapDataLength,
aScaledWidth, aScaledHeight, strideSize,
imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
if (!doScaling)
img->UnlockImagePixels(PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(encoder, aStream);
}

View File

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "imgITools.h"
#define NS_IMGTOOLS_CID \
{ /* fd9a9e8a-a77b-496a-b7bb-263df9715149 */ \
0xfd9a9e8a, \
0xa77b, \
0x496a, \
{0xb7, 0xbb, 0x26, 0x3d, 0xf9, 0x71, 0x51, 0x49} \
}
class imgTools : public imgITools
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGITOOLS
imgTools();
virtual ~imgTools();
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,294 @@
/*
* Tests for imgITools
*/
const TESTDIR = "modules/libpr0n/test/unit/";
const Ci = Components.interfaces;
const Cc = Components.classes;
/*
* dumpToFile()
*
* For test development, dumps the specified array to a file.
* Call |dumpToFile(outData);| in a test to file to a file.
*/
function dumpToFile(aData) {
const path = "/tmp";
var outputFile = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsILocalFile);
outputFile.initWithPath(path);
outputFile.append("testdump.png");
var outputStream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
// WR_ONLY|CREAT|TRUNC
outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0644, null);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].
createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(outputStream);
bos.writeByteArray(aData, aData.length);
outputStream.close();
}
/*
* getFileInputStream()
*
* Returns an input stream for the specified file.
*/
function getFileInputStream(aFile) {
var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
// init the stream as RD_ONLY, -1 == default permissions.
inputStream.init(aFile, 0x01, -1, null);
// Blah. The image decoders use ReadSegments, which isn't implemented on
// file input streams. Use a buffered stream to make it work.
var bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
createInstance(Ci.nsIBufferedInputStream);
bis.init(inputStream, 1024);
return bis;
}
/*
* streamToArray()
*
* Consumes an input stream, and returns its bytes as an array.
*/
function streamToArray(aStream) {
var size = aStream.available();
// use a binary input stream to grab the bytes.
var bis = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(aStream);
var bytes = bis.readByteArray(size);
if (size != bytes.length)
throw "Didn't read expected number of bytes";
return bytes;
}
/*
* compareArrays
*
* Compares two arrays, and throws if there's a difference.
*/
function compareArrays(aArray1, aArray2) {
do_check_eq(aArray1.length, aArray2.length);
for (var i = 0; i < aArray1.length; i++)
if (aArray1[i] != aArray2[i])
throw "arrays differ at index " + i;
}
function run_test() {
try {
/* ========== 0 ========== */
var testnum = 0;
var testdesc = "imgITools setup";
var imgTools = Cc["@mozilla.org/image/tools;1"].
getService(Ci.imgITools);
if (!imgTools)
throw "Couldn't get imgITools service"
/* ========== 1 ========== */
testnum++;
testdesc = "test decoding a PNG";
// 64x64 png, 10698 bytes.
var imgName = "image1.png";
var inMimeType = "image/png";
var imgFile = do_get_file(TESTDIR + imgName);
var istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 10698);
var outParam = { value: null };
imgTools.decodeImageData(istream, inMimeType, outParam);
var container = outParam.value;
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 64);
do_check_eq(container.height, 64);
/* ========== 2 ========== */
testnum++;
testdesc = "test encoding a scaled JPEG";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 16);
var encodedBytes = streamToArray(istream);
// Get bytes for exected result
var refName = "image1png16x16.jpg";
var refFile = do_get_file(TESTDIR + refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 733);
var referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 3 ========== */
testnum++;
testdesc = "test encoding an unscaled JPEG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/jpeg");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image1png64x64.jpg";
refFile = do_get_file(TESTDIR + refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1593);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 4 ========== */
testnum++;
testdesc = "test decoding a JPEG";
// 32x32 jpeg, 3494 bytes.
imgName = "image2.jpg";
inMimeType = "image/jpeg";
imgFile = do_get_file(TESTDIR + imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 3494);
outParam = {};
imgTools.decodeImageData(istream, inMimeType, outParam);
container = outParam.value;
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 32);
do_check_eq(container.height, 32);
/* ========== 5 ========== */
testnum++;
testdesc = "test encoding a scaled PNG";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/png", 16, 16);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg16x16.png";
refFile = do_get_file(TESTDIR + refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 948);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 6 ========== */
testnum++;
testdesc = "test encoding an unscaled PNG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/png");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg32x32.png";
refFile = do_get_file(TESTDIR + refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 3105);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 7 ========== */
testnum++;
testdesc = "test decoding a ICO";
// 16x16 ico, 1406 bytes.
imgName = "image3.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(TESTDIR + imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 1406);
outParam = { value: null };
imgTools.decodeImageData(istream, inMimeType, outParam);
container = outParam.value;
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 16);
do_check_eq(container.height, 16);
/* ========== 8 ========== */
testnum++;
testdesc = "test encoding a scaled PNG"; // note that we're scaling UP
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/png", 32, 32);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image3ico32x32.png";
refFile = do_get_file(TESTDIR + refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 2281);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 9 ========== */
testnum++;
testdesc = "test encoding an unscaled PNG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/png");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image3ico16x16.png";
refFile = do_get_file(TESTDIR + refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 330);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== end ========== */
} catch (e) {
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
}
};

View File

@ -59,6 +59,8 @@ REQUIRES = xpcom \
morkreader \
pref \
necko \
imglib2 \
gfx \
intl \
layout \
locale \

View File

@ -59,14 +59,12 @@
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "mozStorageHelper.h"
// This is the maximum favicon size that we will bother storing. Most icons
// are about 4K. Some people add 32x32 versions at different bit depths,
// making them much bigger. It would be nice if could extract just the 16x16
// version that we need. Instead, we'll just store everything below this
// sanity threshold.
#define MAX_FAVICON_SIZE 32768
// For favicon optimization
#include "imgITools.h"
#include "imgIContainer.h"
#define FAVICON_BUFFER_INCREMENT 8192
@ -553,6 +551,24 @@ nsFaviconService::SetFaviconData(nsIURI* aFavicon, const PRUint8* aData,
PRTime aExpiration)
{
nsresult rv;
PRUint32 dataLen = aDataLen;
const PRUint8* data = aData;
const nsACString* mimeType = &aMimeType;
nsCString newData, newMimeType;
// If the page provided a large image for the favicon (eg, a highres image
// or a multiresolution .ico file), we don't want to store more data than
// needed. An uncompressed 16x16 RGBA image is 1024 bytes, and almost all
// sensible 16x16 icons are under 1024 bytes.
if (aDataLen > 1024) {
rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, newData, newMimeType);
if (NS_SUCCEEDED(rv) && newData.Length() < aDataLen) {
data = reinterpret_cast<PRUint8*>(const_cast<char*>(newData.get())),
dataLen = newData.Length();
mimeType = &newMimeType;
}
}
mozIStorageStatement* statement;
{
// this block forces the scoper to reset our statement: necessary for the
@ -583,9 +599,9 @@ nsFaviconService::SetFaviconData(nsIURI* aFavicon, const PRUint8* aData,
mozStorageStatementScoper scoper(statement);
// the insert and update statements share all but the 0th parameter
rv = statement->BindBlobParameter(1, aData, aDataLen);
rv = statement->BindBlobParameter(1, data, dataLen);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(2, aMimeType);
rv = statement->BindUTF8StringParameter(2, *mimeType);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(3, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
@ -800,6 +816,48 @@ nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec, nsACString
}
// nsFaviconService::OptimizeFaviconImage
//
// Given a blob of data (a image file already read into a buffer), optimize
// its size by recompressing it as a 16x16 PNG.
nsresult
nsFaviconService::OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen,
const nsACString& aMimeType,
nsACString& aNewData,
nsACString& aNewMimeType)
{
nsresult rv;
nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewByteInputStream(getter_AddRefs(stream),
reinterpret_cast<const char*>(aData), aDataLen,
NS_ASSIGNMENT_DEPEND);
NS_ENSURE_SUCCESS(rv, rv);
// decode image
nsCOMPtr<imgIContainer> container;
rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
aNewMimeType.AssignLiteral("image/png");
// scale and recompress
nsCOMPtr<nsIInputStream> iconStream;
rv = imgtool->EncodeScaledImage(container, aNewMimeType, 16, 16,
getter_AddRefs(iconStream));
NS_ENSURE_SUCCESS(rv, rv);
// Read the stream into a new buffer.
rv = NS_ConsumeStream(iconStream, PR_UINT32_MAX, aNewData);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMPL_ISUPPORTS4(FaviconLoadListener,
nsIRequestObserver,
nsIStreamListener,
@ -926,9 +984,6 @@ FaviconLoadListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext
nsIInputStream *aInputStream,
PRUint32 aOffset, PRUint32 aCount)
{
if (aOffset + aCount > MAX_FAVICON_SIZE)
return NS_ERROR_FAILURE; // too big
nsCString buffer;
nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv))

View File

@ -87,6 +87,9 @@ public:
nsresult GetFaviconLinkForIconString(const nsCString& aIcon, nsIURI** aOutput);
void GetFaviconSpecForIconString(const nsCString& aIcon, nsACString& aOutput);
static nsresult OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen,
const nsACString& aMimeType,
nsACString& aNewData, nsACString& aNewMimeType);
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONSERVICE

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,324 @@
/*
* Tests for nsIFaviconService
*/
const TESTDIR = "toolkit/components/places/tests/unit/";
/*
* dumpToFile()
*
* For test development, dumps the specified array to a file.
* Call |dumpToFile(outData);| in a test to file to a file.
*/
function dumpToFile(aData) {
const path = "/tmp";
var outputFile = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsILocalFile);
outputFile.initWithPath(path);
outputFile.append("testdump.png");
var outputStream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
// WR_ONLY|CREAT|TRUNC
outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0644, null);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].
createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(outputStream);
bos.writeByteArray(aData, aData.length);
outputStream.close();
}
/*
* readFileData()
*
* Reads the data from the specified nsIFile, and returns an array of bytes.
*/
function readFileData(aFile) {
var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
// init the stream as RD_ONLY, -1 == default permissions.
inputStream.init(aFile, 0x01, -1, null);
var size = inputStream.available();
// use a binary input stream to grab the bytes.
var bis = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(inputStream);
var bytes = bis.readByteArray(size);
if (size != bytes.length)
throw "Didn't read expected number of bytes";
return bytes;
}
/*
* setAndGetFaviconData()
*
* Calls setFaviconData() with the specified image data,
* and then retrieves it with getFaviconData(). Returns
* and array of bytes and mimetype.
*/
function setAndGetFaviconData(aFilename, aData, aMimeType) {
var iconURI = uri("http://places.test/" + aFilename);
iconsvc.setFaviconData(iconURI,
aData, aData.length, aMimeType,
Number.MAX_VALUE);
var mimeTypeOutparam = {};
var outData = iconsvc.getFaviconData(iconURI,
mimeTypeOutparam, {});
return [outData, mimeTypeOutparam.value];
}
/*
* compareArrays
*
* Compares two arrays, and throws if there's a difference.
*/
function compareArrays(aArray1, aArray2) {
do_check_eq(aArray1.length, aArray2.length);
for (var i = 0; i < aArray1.length; i++)
if (aArray1[i] != aArray2[i])
throw "arrays differ at index " + i;
}
var iconsvc;
function run_test() {
try {
/* ========== 0 ========== */
var testnum = 0;
var testdesc = "nsIFaviconService setup";
iconsvc = Cc["@mozilla.org/browser/favicon-service;1"].
getService(Ci.nsIFaviconService);
if (!iconsvc)
throw "Couldn't get nsIFaviconService service"
/* ========== 1 ========== */
testnum++;
testdesc = "test storing a normal 16x16 icon";
// 16x16 png, 286 bytes.
var iconName = "favicon-normal16.png";
var inMimeType = "image/png";
var iconFile = do_get_file(TESTDIR + iconName);
var inData = readFileData(iconFile);
do_check_eq(inData.length, 286);
var [outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Ensure input and output are identical
do_check_eq(inMimeType, outMimeType);
compareArrays(inData, outData);
/* ========== 2 ========== */
testnum++;
testdesc = "test storing a normal 32x32 icon";
// 32x32 png, 344 bytes.
iconName = "favicon-normal32.png";
inMimeType = "image/png";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 344);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Ensure input and output are identical
do_check_eq(inMimeType, outMimeType);
compareArrays(inData, outData);
/* ========== 3 ========== */
testnum++;
testdesc = "test storing an oversize 16x16 icon ";
// in: 16x16 ico, 1406 bytes.
// out: 16x16 png
iconName = "favicon-big16.ico";
inMimeType = "image/x-icon";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 1406);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== 4 ========== */
testnum++;
testdesc = "test storing an oversize 4x4 icon ";
// in: 4x4 jpg, 4751 bytes.
// out: 16x16 png
iconName = "favicon-big4.jpg";
inMimeType = "image/jpeg";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 4751);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== 5 ========== */
testnum++;
testdesc = "test storing an oversize 32x32 icon ";
// in: 32x32 jpg, 3494 bytes.
// out: 16x16 png
iconName = "favicon-big32.jpg";
inMimeType = "image/jpeg";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 3494);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== 6 ========== */
testnum++;
testdesc = "test storing an oversize 48x48 icon ";
// in: 48x48 ico, 56646 bytes.
// (howstuffworks.com icon, contains 13 icons with sizes from 16x16 to
// 48x48 in varying depths)
// out: 16x16 png
iconName = "favicon-big48.ico";
inMimeType = "image/x-icon";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 56646);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== 7 ========== */
testnum++;
testdesc = "test storing an oversize 64x64 icon ";
// in: 64x64 png, 10698 bytes.
// out: 16x16 png
iconName = "favicon-big64.png";
inMimeType = "image/png";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 10698);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== 8 ========== */
testnum++;
testdesc = "test scaling an oversize 160x3 icon ";
// in: 160x3 jpg, 5095 bytes.
// out: 16x16 png
iconName = "favicon-scale160x3.jpg";
inMimeType = "image/jpeg";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 5095);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== 9 ========== */
testnum++;
testdesc = "test scaling an oversize 3x160 icon ";
// in: 3x160 jpg, 5059 bytes.
// out: 16x16 png
iconName = "favicon-scale3x160.jpg";
inMimeType = "image/jpeg";
iconFile = do_get_file(TESTDIR + iconName);
inData = readFileData(iconFile);
do_check_eq(inData.length, 5059);
[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
// Read in the expected output.
var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png");
var expectedData = readFileData(expectedFile);
// Compare thet expected data to the actual data.
do_check_eq("image/png", outMimeType);
compareArrays(expectedData, outData);
/* ========== end ========== */
} catch (e) {
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
}
};