From fa20dc97b8487604ddbe01b164b276399394709c Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 27 Jun 2014 12:19:40 +0100 Subject: [PATCH] Bug 986526 - Create helper functions to encode DrawTargets/SourceSurfaces to PNG/JPEG/BMP/ICO/etc. and save them, dump them, or copy them to the clipboard, either as binary or as data: URIs. r=mattwoodrow --- gfx/thebes/gfxUtils.cpp | 301 +++++++++++++++++++++++++++++------- gfx/thebes/gfxUtils.h | 102 ++++++++---- layout/base/nsPresShell.cpp | 69 +-------- widget/windows/WinUtils.cpp | 94 +++++------ 4 files changed, 355 insertions(+), 211 deletions(-) diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index 78637a85940..7e45e7a6128 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -10,9 +10,19 @@ #include "gfxImageSurface.h" #include "gfxPlatform.h" #include "gfxDrawable.h" +#include "imgIEncoder.h" +#include "mozilla/Base64.h" #include "mozilla/gfx/2D.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" #include "mozilla/RefPtr.h" +#include "mozilla/Vector.h" +#include "nsComponentManagerUtils.h" +#include "nsIClipboardHelper.h" +#include "nsIFile.h" +#include "nsIPresShell.h" +#include "nsPresContext.h" #include "nsRegion.h" +#include "nsServiceManagerUtils.h" #include "yuv_convert.h" #include "ycbcr_to_rgb565.h" #include "GeckoProfiler.h" @@ -1105,83 +1115,258 @@ gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber) return colors[aFrameNumber % sNumFrameColors]; } -#ifdef MOZ_DUMP_PAINTING +/* static */ nsresult +gfxUtils::EncodeSourceSurface(SourceSurface* aSurface, + const nsACString& aMimeType, + const nsAString& aOutputOptions, + BinaryOrData aBinaryOrData, + FILE* aFile) +{ + MOZ_ASSERT(aBinaryOrData == eDataURIEncode || aFile, + "Copying binary encoding to clipboard not currently supported"); + + const IntSize size = aSurface->GetSize(); + if (size.IsEmpty()) { + return NS_ERROR_INVALID_ARG; + } + const Size floatSize(size.width, size.height); + + RefPtr dataSurface; + if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { + // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5) + dataSurface = + CopySurfaceToDataSourceSurfaceWithFormat(aSurface, + SurfaceFormat::B8G8R8A8); + } else { + dataSurface = aSurface->GetDataSurface(); + } + if (!dataSurface) { + return NS_ERROR_FAILURE; + } + + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return NS_ERROR_FAILURE; + } + + nsAutoCString encoderCID( + NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType); + nsCOMPtr encoder = do_CreateInstance(encoderCID.get()); + if (!encoder) { +#ifdef DEBUG + int32_t w = std::min(size.width, 8); + int32_t h = std::min(size.height, 8); + printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h); + for (int32_t y = 0; y < h; ++y) { + for (int32_t x = 0; x < w; ++x) { + printf("%x ", reinterpret_cast(map.mData)[y*map.mStride+x]); + } + } +#endif + dataSurface->Unmap(); + return NS_ERROR_FAILURE; + } + + nsresult rv = encoder->InitFromData(map.mData, + BufferSizeFromStrideAndHeight(map.mStride, size.height), + size.width, + size.height, + map.mStride, + imgIEncoder::INPUT_FORMAT_HOSTARGB, + aOutputOptions); + dataSurface->Unmap(); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr imgStream; + CallQueryInterface(encoder.get(), getter_AddRefs(imgStream)); + if (!imgStream) { + return NS_ERROR_FAILURE; + } + + uint64_t bufSize64; + rv = imgStream->Available(&bufSize64); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE); + + uint32_t bufSize = (uint32_t)bufSize64; + + // ...leave a little extra room so we can call read again and make sure we + // got everything. 16 bytes for better padding (maybe) + bufSize += 16; + uint32_t imgSize = 0; + Vector imgData; + if (!imgData.initCapacity(bufSize)) { + return NS_ERROR_OUT_OF_MEMORY; + } + uint32_t numReadThisTime = 0; + while ((rv = imgStream->Read(imgData.begin() + imgSize, + bufSize - imgSize, + &numReadThisTime)) == NS_OK && numReadThisTime > 0) + { + imgSize += numReadThisTime; + if (imgSize == bufSize) { + // need a bigger buffer, just double + bufSize *= 2; + if (!imgData.resizeUninitialized(bufSize)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + } + + if (aBinaryOrData == eBinaryEncode) { + if (aFile) { + fwrite(imgData.begin(), 1, imgSize, aFile); + } + return NS_OK; + } + + // base 64, result will be null-terminated + nsCString encodedImg; + rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString string("data:"); + string.Append(aMimeType); + string.Append(";base64,"); + string.Append(encodedImg); + + if (aFile) { +#ifdef ANDROID + if (aFile == stdout || aFile == stderr) { + // ADB logcat cuts off long strings so we will break it down + const char* cStr = string.BeginReading(); + size_t len = strlen(cStr); + while (true) { + printf_stderr("IMG: %.140s\n", cStr); + if (len <= 140) + break; + len -= 140; + cStr += 140; + } + } +#endif + fprintf(aFile, "%s", string.BeginReading()); + } else { + nsCOMPtr clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv)); + if (clipboard) { + clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr); + } + } + return NS_OK; +} + +/* static */ void +gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile) +{ + WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get()); +} + +/* static */ void +gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile) +{ + FILE* file = fopen(aFile, "wb"); + + if (!file) { + // Maybe the directory doesn't exist; try creating it, then fopen again. + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr comFile = do_CreateInstance("@mozilla.org/file/local;1"); + if (comFile) { + NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile))); + rv = comFile->InitWithPath(utf16path); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr dirPath; + comFile->GetParent(getter_AddRefs(dirPath)); + if (dirPath) { + rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) { + file = fopen(aFile, "wb"); + } + } + } + } + if (!file) { + NS_WARNING("Failed to open file to create PNG!\n"); + return; + } + } + + EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"), + EmptyString(), eBinaryEncode, file); + fclose(file); +} + +/* static */ void +gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile) +{ + WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get()); +} + /* static */ void gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile) { - aDT->Flush(); - nsRefPtr surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); - if (surf) { - surf->WriteAsPNG(aFile); + RefPtr surface = aDT->Snapshot(); + if (surface) { + WriteAsPNG(surface, aFile); } else { - NS_WARNING("Failed to get Thebes surface!"); + NS_WARNING("Failed to get surface!"); } } /* static */ void -gfxUtils::DumpAsDataURL(DrawTarget* aDT) +gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile) { - aDT->Flush(); - nsRefPtr surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); - if (surf) { - surf->DumpAsDataURL(); + int32_t width = 1000, height = 1000; + nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width), + aShell->GetPresContext()->DevPixelsToAppUnits(height)); + + RefPtr dt = gfxPlatform::GetPlatform()-> + CreateOffscreenContentDrawTarget(IntSize(width, height), + SurfaceFormat::B8G8R8A8); + NS_ENSURE_TRUE(dt, /*void*/); + + nsRefPtr context = new gfxContext(dt); + aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context); + WriteAsPNG(dt.get(), aFile); +} + +/* static */ void +gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile) +{ + EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"), + EmptyString(), eDataURIEncode, aFile); +} + +/* static */ void +gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile) +{ + RefPtr surface = aDT->Snapshot(); + if (surface) { + DumpAsDataURI(surface, aFile); } else { - NS_WARNING("Failed to get Thebes surface!"); + NS_WARNING("Failed to get surface!"); } } /* static */ void -gfxUtils::CopyAsDataURL(DrawTarget* aDT) +gfxUtils::CopyAsDataURI(SourceSurface* aSurface) { - aDT->Flush(); - nsRefPtr surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); - if (surf) { - surf->CopyAsDataURL(); + EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"), + EmptyString(), eDataURIEncode, nullptr); +} + +/* static */ void +gfxUtils::CopyAsDataURI(DrawTarget* aDT) +{ + RefPtr surface = aDT->Snapshot(); + if (surface) { + CopyAsDataURI(surface); } else { - NS_WARNING("Failed to get Thebes surface!"); + NS_WARNING("Failed to get surface!"); } } -/* static */ void -gfxUtils::WriteAsPNG(gfx::SourceSurface* aSourceSurface, const char* aFile) -{ - RefPtr dataSurface = aSourceSurface->GetDataSurface(); - RefPtr dt - = gfxPlatform::GetPlatform() - ->CreateDrawTargetForData(dataSurface->GetData(), - dataSurface->GetSize(), - dataSurface->Stride(), - aSourceSurface->GetFormat()); - gfxUtils::WriteAsPNG(dt.get(), aFile); -} - -/* static */ void -gfxUtils::DumpAsDataURL(gfx::SourceSurface* aSourceSurface) -{ - RefPtr dataSurface = aSourceSurface->GetDataSurface(); - RefPtr dt - = gfxPlatform::GetPlatform() - ->CreateDrawTargetForData(dataSurface->GetData(), - dataSurface->GetSize(), - dataSurface->Stride(), - aSourceSurface->GetFormat()); - gfxUtils::DumpAsDataURL(dt.get()); -} - -/* static */ void -gfxUtils::CopyAsDataURL(gfx::SourceSurface* aSourceSurface) -{ - RefPtr dataSurface = aSourceSurface->GetDataSurface(); - RefPtr dt - = gfxPlatform::GetPlatform() - ->CreateDrawTargetForData(dataSurface->GetData(), - dataSurface->GetSize(), - dataSurface->Stride(), - aSourceSurface->GetFormat()); - - gfxUtils::CopyAsDataURL(dt.get()); -} - +#ifdef MOZ_DUMP_PAINTING static bool sDumpPaintList = getenv("MOZ_DUMP_PAINT_LIST") != 0; /* static */ bool diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 222347121e7..8b823bb84dc 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -16,6 +16,8 @@ class gfxASurface; class gfxDrawable; class nsIntRegion; +class nsIPresShell; + struct nsIntRect; namespace mozilla { @@ -27,6 +29,7 @@ struct PlanarYCbCrData; class gfxUtils { public: typedef mozilla::gfx::DataSourceSurface DataSourceSurface; + typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::IntPoint IntPoint; typedef mozilla::gfx::Matrix Matrix; typedef mozilla::gfx::SourceSurface SourceSurface; @@ -228,45 +231,78 @@ public: static const mozilla::gfx::Color& GetColorForFrameNumber(uint64_t aFrameNumber); static const uint32_t sNumFrameColors; + + enum BinaryOrData { + eBinaryEncode, + eDataURIEncode + }; + + /** + * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder. + * + * @param aMimeType The MIME-type of the image type that the surface is to + * be encoded to. Used to create an appropriate imgIEncoder instance to + * do the encoding. + * + * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as + * the value of the |outputOptions| parameter. Callers are responsible + * for making sure that this is a sane value for the passed MIME-type + * (i.e. for the type of encoder that will be created). + * + * @aBinaryOrData Flag used to determine if the surface is simply encoded + * to the requested binary image format, or if the binary image is + * further converted to base-64 and written out as a 'data:' URI. + * + * @aFile If specified, the encoded data is written out to aFile, otherwise + * it is copied to the clipboard. + * + * TODO: Copying to the clipboard as a binary file is not currently + * supported. + */ + static nsresult + EncodeSourceSurface(SourceSurface* aSurface, + const nsACString& aMimeType, + const nsAString& aOutputOptions, + BinaryOrData aBinaryOrData, + FILE* aFile); + + /** + * Write as a PNG file to the path aFile. + */ + static void WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile); + static void WriteAsPNG(SourceSurface* aSurface, const char* aFile); + static void WriteAsPNG(DrawTarget* aDT, const nsAString& aFile); + static void WriteAsPNG(DrawTarget* aDT, const char* aFile); + static void WriteAsPNG(nsIPresShell* aShell, const char* aFile); + + /** + * Dump as a PNG encoded Data URL to a FILE stream (using stdout by + * default). + * + * Rather than giving aFile a default argument we have separate functions + * to make them easier to use from a debugger. + */ + static void DumpAsDataURI(SourceSurface* aSourceSurface, FILE* aFile); + static inline void DumpAsDataURI(SourceSurface* aSourceSurface) { + DumpAsDataURI(aSourceSurface, stdout); + } + static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile); + static inline void DumpAsDataURI(DrawTarget* aDT) { + DumpAsDataURI(aDT, stdout); + } + + /** + * Copy to the clipboard as a PNG encoded Data URL. + */ + static void CopyAsDataURI(SourceSurface* aSourceSurface); + static void CopyAsDataURI(DrawTarget* aDT); + #ifdef MOZ_DUMP_PAINTING - /** - * Writes a binary PNG file. - */ - static void WriteAsPNG(mozilla::gfx::DrawTarget* aDT, const char* aFile); - - /** - * Write as a PNG encoded Data URL to stdout. - */ - static void DumpAsDataURL(mozilla::gfx::DrawTarget* aDT); - - /** - * Copy a PNG encoded Data URL to the clipboard. - */ - static void CopyAsDataURL(mozilla::gfx::DrawTarget* aDT); - static bool DumpPaintList(); static bool sDumpPainting; static bool sDumpPaintingToFile; static FILE* sDumpPaintFile; - - /** - * Writes a binary PNG file. - * Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads - */ - static void WriteAsPNG(mozilla::gfx::SourceSurface* aSourceSurface, const char* aFile); - - /** - * Write as a PNG encoded Data URL to stdout. - * Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads - */ - static void DumpAsDataURL(mozilla::gfx::SourceSurface* aSourceSurface); - - /** - * Copy a PNG encoded Data URL to the clipboard. - * Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads - */ - static void CopyAsDataURL(mozilla::gfx::SourceSurface* aSourceSurface); #endif }; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ad71ab1f327..d1b4b868959 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -114,6 +114,7 @@ #include "nsStyleSheetService.h" #include "gfxImageSurface.h" #include "gfxContext.h" +#include "gfxUtils.h" #include "nsSMILAnimationController.h" #include "SVGContentUtils.h" #include "nsSVGEffects.h" @@ -9522,68 +9523,6 @@ PresShell::CloneStyleSet(nsStyleSet* aSet) return clone; } -#ifdef DEBUG_Eli -static nsresult -DumpToPNG(nsIPresShell* shell, nsAString& name) { - int32_t width=1000, height=1000; - nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width), - shell->GetPresContext()->DevPixelsToAppUnits(height)); - - nsRefPtr imgSurface = - new gfxImageSurface(gfxIntSize(width, height), - gfxImageFormat::ARGB32); - - nsRefPtr imgContext = new gfxContext(imgSurface); - - nsRefPtr surface = - gfxPlatform::GetPlatform()-> - CreateOffscreenSurface(IntSize(width, height), - gfxASurface::ContentFromFormat(gfxImageFormat::ARGB32)); - NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY); - - nsRefPtr context = new gfxContext(surface); - - shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context); - - imgContext->DrawSurface(surface, gfxSize(width, height)); - - nsCOMPtr encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png"); - NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE); - encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height, - width, height, imgSurface->Stride(), - imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString()); - - // XXX not sure if this is the right way to write to a file - nsCOMPtr file = do_CreateInstance("@mozilla.org/file/local;1"); - NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); - rv = file->InitWithPath(name); - NS_ENSURE_SUCCESS(rv, rv); - - uint64_t length64; - rv = encoder->Available(&length64); - NS_ENSURE_SUCCESS(rv, rv); - if (length64 > UINT32_MAX) - return NS_ERROR_FILE_TOO_BIG; - - uint32_t length = (uint32_t)length64; - - nsCOMPtr outputStream; - rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr bufferedOutputStream; - rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), - outputStream, length); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t numWritten; - rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} -#endif - // After an incremental reflow, we verify the correctness by doing a // full reflow into a fresh frame tree. bool @@ -9668,7 +9607,7 @@ PresShell::VerifyIncrementalReflow() root2->List(stdout, 0); } -#ifdef DEBUG_Eli +#if 0 // Sample code for dumping page to png // XXX Needs to be made more flexible if (!ok) { @@ -9677,12 +9616,12 @@ PresShell::VerifyIncrementalReflow() stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea"); stra.AppendInt(num); stra.AppendLiteral(".png"); - DumpToPNG(sh, stra); + gfxUtils::WriteAsPNG(sh, stra); nsString strb; strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb"); strb.AppendInt(num); strb.AppendLiteral(".png"); - DumpToPNG(this, strb); + gfxUtils::WriteAsPNG(sh, strb); ++num; } #endif diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index a6c852e2b58..8e2197c7274 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -7,6 +7,7 @@ #include "WinUtils.h" #include "gfxPlatform.h" +#include "gfxUtils.h" #include "nsWindow.h" #include "nsWindowDefs.h" #include "KeyboardLayout.h" @@ -851,65 +852,48 @@ NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() { NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread."); - nsCOMPtr iconStream; - nsRefPtr encoder = - do_CreateInstance("@mozilla.org/image/encoder;2?" - "type=image/vnd.microsoft.icon"); - NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE); - nsresult rv = encoder->InitFromData(mBuffer, mBufferLength, - mWidth, mHeight, - mStride, - imgIEncoder::INPUT_FORMAT_HOSTARGB, - EmptyString()); - NS_ENSURE_SUCCESS(rv, rv); - CallQueryInterface(encoder.get(), getter_AddRefs(iconStream)); - if (!iconStream) { - return NS_ERROR_FAILURE; + RefPtr dt = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr surface = + dt->CreateSourceSurfaceFromData(mBuffer, IntSize(mWidth, mHeight), mStride, + SurfaceFormat::B8G8R8A8); + + FILE* file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb"); + if (!file) { + // Maybe the directory doesn't exist; try creating it, then fopen again. + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr comFile = do_CreateInstance("@mozilla.org/file/local;1"); + if (comFile) { + //NS_ConvertUTF8toUTF16 utf16path(mIconPath); + rv = comFile->InitWithPath(mIconPath); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr dirPath; + comFile->GetParent(getter_AddRefs(dirPath)); + if (dirPath) { + rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) { + file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb"); + if (!file) { + rv = NS_ERROR_FAILURE; + } + } + } + } + } + if (!file) { + return rv; + } } - + nsresult rv = + gfxUtils::EncodeSourceSurface(surface, + NS_LITERAL_CSTRING("image/vnd.microsoft.icon"), + EmptyString(), + gfxUtils::eBinaryEncode, + file); + fclose(file); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr icoFile - = do_CreateInstance("@mozilla.org/file/local;1"); - NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE); - rv = icoFile->InitWithPath(mIconPath); - - // Try to create the directory if it's not there yet - nsCOMPtr dirPath; - icoFile->GetParent(getter_AddRefs(dirPath)); - rv = (dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777)); - if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { - return rv; - } - - // Setup the output stream for the ICO file on disk - nsCOMPtr outputStream; - rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), icoFile); - NS_ENSURE_SUCCESS(rv, rv); - - // Obtain the ICO buffer size from the re-encoded ICO stream - uint64_t bufSize64; - rv = iconStream->Available(&bufSize64); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(bufSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); - - uint32_t bufSize = (uint32_t)bufSize64; - - // Setup a buffered output stream from the stream object - // so that we can simply use WriteFrom with the stream object - nsCOMPtr bufferedOutputStream; - rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), - outputStream, bufSize); - NS_ENSURE_SUCCESS(rv, rv); - - // Write out the icon stream to disk and make sure we wrote everything - uint32_t wrote; - rv = bufferedOutputStream->WriteFrom(iconStream, bufSize, &wrote); - NS_ASSERTION(bufSize == wrote, - "Icon wrote size should be equal to requested write size"); // Cleanup - bufferedOutputStream->Close(); - outputStream->Close(); if (mURLShortcut) { SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0); }