image/decoders/icon/win/nsIconChannel.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/decoders/icon/win/nsIconChannel.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,725 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/ArrayUtils.h"
    1.11 +
    1.12 +#include "nsIconChannel.h"
    1.13 +#include "nsIIconURI.h"
    1.14 +#include "nsIServiceManager.h"
    1.15 +#include "nsIInterfaceRequestor.h"
    1.16 +#include "nsIInterfaceRequestorUtils.h"
    1.17 +#include "nsXPIDLString.h"
    1.18 +#include "nsReadableUtils.h"
    1.19 +#include "nsMimeTypes.h"
    1.20 +#include "nsMemory.h"
    1.21 +#include "nsIStringStream.h"
    1.22 +#include "nsIURL.h"
    1.23 +#include "nsNetUtil.h"
    1.24 +#include "nsIFile.h"
    1.25 +#include "nsIFileURL.h"
    1.26 +#include "nsIMIMEService.h"
    1.27 +#include "nsCExternalHandlerService.h"
    1.28 +#include "nsDirectoryServiceDefs.h"
    1.29 +
    1.30 +#ifdef _WIN32_WINNT
    1.31 +#undef _WIN32_WINNT
    1.32 +#endif
    1.33 +#define _WIN32_WINNT 0x0600
    1.34 +
    1.35 +// we need windows.h to read out registry information...
    1.36 +#include <windows.h>
    1.37 +#include <shellapi.h>
    1.38 +#include <shlobj.h>
    1.39 +#include <objbase.h>
    1.40 +#include <wchar.h>
    1.41 +
    1.42 +using namespace mozilla;
    1.43 +
    1.44 +struct ICONFILEHEADER {
    1.45 +  uint16_t ifhReserved;
    1.46 +  uint16_t ifhType;
    1.47 +  uint16_t ifhCount;
    1.48 +};
    1.49 +
    1.50 +struct ICONENTRY {
    1.51 +  int8_t ieWidth;
    1.52 +  int8_t ieHeight;
    1.53 +  uint8_t ieColors;
    1.54 +  uint8_t ieReserved;
    1.55 +  uint16_t iePlanes;
    1.56 +  uint16_t ieBitCount;
    1.57 +  uint32_t ieSizeImage;
    1.58 +  uint32_t ieFileOffset;
    1.59 +};
    1.60 +
    1.61 +// Match stock icons with names
    1.62 +static SHSTOCKICONID GetStockIconIDForName(const nsACString &aStockName)
    1.63 +{
    1.64 +  // UAC shield icon
    1.65 +  if (aStockName == NS_LITERAL_CSTRING("uac-shield"))
    1.66 +    return SIID_SHIELD;
    1.67 +
    1.68 +  return SIID_INVALID;
    1.69 +}
    1.70 +
    1.71 +// nsIconChannel methods
    1.72 +nsIconChannel::nsIconChannel()
    1.73 +{
    1.74 +}
    1.75 +
    1.76 +nsIconChannel::~nsIconChannel() 
    1.77 +{}
    1.78 +
    1.79 +NS_IMPL_ISUPPORTS(nsIconChannel, 
    1.80 +                  nsIChannel, 
    1.81 +                  nsIRequest,
    1.82 +                  nsIRequestObserver,
    1.83 +                  nsIStreamListener)
    1.84 +
    1.85 +nsresult nsIconChannel::Init(nsIURI* uri)
    1.86 +{
    1.87 +  NS_ASSERTION(uri, "no uri");
    1.88 +  mUrl = uri;
    1.89 +  mOriginalURI = uri;
    1.90 +  nsresult rv;
    1.91 +  mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
    1.92 +  return rv;
    1.93 +}
    1.94 +
    1.95 +////////////////////////////////////////////////////////////////////////////////
    1.96 +// nsIRequest methods:
    1.97 +
    1.98 +NS_IMETHODIMP nsIconChannel::GetName(nsACString &result)
    1.99 +{
   1.100 +  return mUrl->GetSpec(result);
   1.101 +}
   1.102 +
   1.103 +NS_IMETHODIMP nsIconChannel::IsPending(bool *result)
   1.104 +{
   1.105 +  return mPump->IsPending(result);
   1.106 +}
   1.107 +
   1.108 +NS_IMETHODIMP nsIconChannel::GetStatus(nsresult *status)
   1.109 +{
   1.110 +  return mPump->GetStatus(status);
   1.111 +}
   1.112 +
   1.113 +NS_IMETHODIMP nsIconChannel::Cancel(nsresult status)
   1.114 +{
   1.115 +  return mPump->Cancel(status);
   1.116 +}
   1.117 +
   1.118 +NS_IMETHODIMP nsIconChannel::Suspend(void)
   1.119 +{
   1.120 +  return mPump->Suspend();
   1.121 +}
   1.122 +
   1.123 +NS_IMETHODIMP nsIconChannel::Resume(void)
   1.124 +{
   1.125 +  return mPump->Resume();
   1.126 +}
   1.127 +NS_IMETHODIMP nsIconChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
   1.128 +{
   1.129 +  *aLoadGroup = mLoadGroup;
   1.130 +  NS_IF_ADDREF(*aLoadGroup);
   1.131 +  return NS_OK;
   1.132 +}
   1.133 +
   1.134 +NS_IMETHODIMP nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
   1.135 +{
   1.136 +  mLoadGroup = aLoadGroup;
   1.137 +  return NS_OK;
   1.138 +}
   1.139 +
   1.140 +NS_IMETHODIMP nsIconChannel::GetLoadFlags(uint32_t *aLoadAttributes)
   1.141 +{
   1.142 +  return mPump->GetLoadFlags(aLoadAttributes);
   1.143 +}
   1.144 +
   1.145 +NS_IMETHODIMP nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes)
   1.146 +{
   1.147 +  return mPump->SetLoadFlags(aLoadAttributes);
   1.148 +}
   1.149 +
   1.150 +////////////////////////////////////////////////////////////////////////////////
   1.151 +// nsIChannel methods:
   1.152 +
   1.153 +NS_IMETHODIMP nsIconChannel::GetOriginalURI(nsIURI* *aURI)
   1.154 +{
   1.155 +  *aURI = mOriginalURI;
   1.156 +  NS_ADDREF(*aURI);
   1.157 +  return NS_OK;
   1.158 +}
   1.159 +
   1.160 +NS_IMETHODIMP nsIconChannel::SetOriginalURI(nsIURI* aURI)
   1.161 +{
   1.162 +  NS_ENSURE_ARG_POINTER(aURI);
   1.163 +  mOriginalURI = aURI;
   1.164 +  return NS_OK;
   1.165 +}
   1.166 +
   1.167 +NS_IMETHODIMP nsIconChannel::GetURI(nsIURI* *aURI)
   1.168 +{
   1.169 +  *aURI = mUrl;
   1.170 +  NS_IF_ADDREF(*aURI);
   1.171 +  return NS_OK;
   1.172 +}
   1.173 +
   1.174 +NS_IMETHODIMP
   1.175 +nsIconChannel::Open(nsIInputStream **_retval)
   1.176 +{
   1.177 +  return MakeInputStream(_retval, false);
   1.178 +}
   1.179 +
   1.180 +nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile ** aLocalFile, uint32_t * aDesiredImageSize, nsCString &aContentType, nsCString &aFileExtension)
   1.181 +{
   1.182 +  nsresult rv = NS_OK;
   1.183 +  nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
   1.184 +  NS_ENSURE_SUCCESS(rv, rv);
   1.185 +
   1.186 +  iconURI->GetImageSize(aDesiredImageSize);
   1.187 +  iconURI->GetContentType(aContentType);
   1.188 +  iconURI->GetFileExtension(aFileExtension);
   1.189 +
   1.190 +  nsCOMPtr<nsIURL> url;
   1.191 +  rv = iconURI->GetIconURL(getter_AddRefs(url));
   1.192 +  if (NS_FAILED(rv) || !url) return NS_OK;
   1.193 +
   1.194 +  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
   1.195 +  if (NS_FAILED(rv) || !fileURL) return NS_OK;
   1.196 +
   1.197 +  nsCOMPtr<nsIFile> file;
   1.198 +  rv = fileURL->GetFile(getter_AddRefs(file));
   1.199 +  if (NS_FAILED(rv) || !file) return NS_OK;
   1.200 +
   1.201 +  return file->Clone(aLocalFile);
   1.202 +}
   1.203 +
   1.204 +NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
   1.205 +{
   1.206 +  nsCOMPtr<nsIInputStream> inStream;
   1.207 +  nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
   1.208 +  if (NS_FAILED(rv))
   1.209 +    return rv;
   1.210 +
   1.211 +  // Init our streampump
   1.212 +  rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
   1.213 +  if (NS_FAILED(rv))
   1.214 +    return rv;
   1.215 +
   1.216 +  rv = mPump->AsyncRead(this, ctxt);
   1.217 +  if (NS_SUCCEEDED(rv)) {
   1.218 +    // Store our real listener
   1.219 +    mListener = aListener;
   1.220 +    // Add ourself to the load group, if available
   1.221 +    if (mLoadGroup)
   1.222 +      mLoadGroup->AddRequest(this, nullptr);
   1.223 +  }
   1.224 +  return rv;
   1.225 +}
   1.226 +
   1.227 +static DWORD GetSpecialFolderIcon(nsIFile* aFile, int aFolder, SHFILEINFOW* aSFI, UINT aInfoFlags)
   1.228 +{
   1.229 +  DWORD shellResult = 0;
   1.230 +
   1.231 +  if (!aFile)
   1.232 +    return shellResult;
   1.233 +
   1.234 +  wchar_t fileNativePath[MAX_PATH];
   1.235 +  nsAutoString fileNativePathStr;
   1.236 +  aFile->GetPath(fileNativePathStr);
   1.237 +  ::GetShortPathNameW(fileNativePathStr.get(), fileNativePath, ArrayLength(fileNativePath));
   1.238 +
   1.239 +  LPITEMIDLIST idList;
   1.240 +  HRESULT hr = ::SHGetSpecialFolderLocation(nullptr, aFolder, &idList);
   1.241 +  if (SUCCEEDED(hr)) {
   1.242 +    wchar_t specialNativePath[MAX_PATH];
   1.243 +    ::SHGetPathFromIDListW(idList, specialNativePath);
   1.244 +    ::GetShortPathNameW(specialNativePath, specialNativePath, ArrayLength(specialNativePath));
   1.245 +  
   1.246 +    if (!wcsicmp(fileNativePath, specialNativePath)) {
   1.247 +      aInfoFlags |= (SHGFI_PIDL | SHGFI_SYSICONINDEX);
   1.248 +      shellResult = ::SHGetFileInfoW((LPCWSTR)(LPCITEMIDLIST)idList, 0, aSFI,
   1.249 +                                     sizeof(*aSFI), aInfoFlags);
   1.250 +    }
   1.251 +  }
   1.252 +  CoTaskMemFree(idList);
   1.253 +  return shellResult;
   1.254 +}
   1.255 +
   1.256 +static UINT GetSizeInfoFlag(uint32_t aDesiredImageSize)
   1.257 +{
   1.258 +  UINT infoFlag;
   1.259 +  if (aDesiredImageSize > 16)
   1.260 +    infoFlag = SHGFI_SHELLICONSIZE;
   1.261 +  else
   1.262 +    infoFlag = SHGFI_SMALLICON;
   1.263 +
   1.264 +  return infoFlag;
   1.265 +}
   1.266 +
   1.267 +nsresult nsIconChannel::GetHIconFromFile(HICON *hIcon)
   1.268 +{
   1.269 +  nsXPIDLCString contentType;
   1.270 +  nsCString fileExt;
   1.271 +  nsCOMPtr<nsIFile> localFile; // file we want an icon for
   1.272 +  uint32_t desiredImageSize;
   1.273 +  nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(localFile), &desiredImageSize, contentType, fileExt);
   1.274 +  NS_ENSURE_SUCCESS(rv, rv);
   1.275 +
   1.276 +  // if the file exists, we are going to use it's real attributes...otherwise we only want to use it for it's extension...
   1.277 +  SHFILEINFOW      sfi;
   1.278 +  UINT infoFlags = SHGFI_ICON;
   1.279 +  
   1.280 +  bool fileExists = false;
   1.281 + 
   1.282 +  nsAutoString filePath;
   1.283 +  CopyASCIItoUTF16(fileExt, filePath);
   1.284 +  if (localFile)
   1.285 +  {
   1.286 +    rv = localFile->Normalize();
   1.287 +    NS_ENSURE_SUCCESS(rv, rv);
   1.288 +
   1.289 +    localFile->GetPath(filePath);
   1.290 +    if (filePath.Length() < 2 || filePath[1] != ':')
   1.291 +      return NS_ERROR_MALFORMED_URI; // UNC
   1.292 +
   1.293 +    if (filePath.Last() == ':')
   1.294 +      filePath.Append('\\');
   1.295 +    else {
   1.296 +      localFile->Exists(&fileExists);
   1.297 +      if (!fileExists)
   1.298 +       localFile->GetLeafName(filePath);
   1.299 +    }
   1.300 +  }
   1.301 +
   1.302 +  if (!fileExists)
   1.303 +   infoFlags |= SHGFI_USEFILEATTRIBUTES;
   1.304 +
   1.305 +  infoFlags |= GetSizeInfoFlag(desiredImageSize);
   1.306 +
   1.307 +  // if we have a content type... then use it! but for existing files, we want
   1.308 +  // to show their real icon.
   1.309 +  if (!fileExists && !contentType.IsEmpty())
   1.310 +  {
   1.311 +    nsCOMPtr<nsIMIMEService> mimeService (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
   1.312 +    NS_ENSURE_SUCCESS(rv, rv);
   1.313 +
   1.314 +    nsAutoCString defFileExt;
   1.315 +    mimeService->GetPrimaryExtension(contentType, fileExt, defFileExt);
   1.316 +    // If the mime service does not know about this mime type, we show
   1.317 +    // the generic icon.
   1.318 +    // In any case, we need to insert a '.' before the extension.
   1.319 +    filePath = NS_LITERAL_STRING(".") + NS_ConvertUTF8toUTF16(defFileExt);
   1.320 +  }
   1.321 +
   1.322 +  // Is this the "Desktop" folder?
   1.323 +  DWORD shellResult = GetSpecialFolderIcon(localFile, CSIDL_DESKTOP, &sfi, infoFlags);
   1.324 +  if (!shellResult) {
   1.325 +    // Is this the "My Documents" folder?
   1.326 +    shellResult = GetSpecialFolderIcon(localFile, CSIDL_PERSONAL, &sfi, infoFlags);
   1.327 +  }
   1.328 +
   1.329 +  // There are other "Special Folders" and Namespace entities that we are not 
   1.330 +  // fetching icons for, see: 
   1.331 +  // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp
   1.332 +  // If we ever need to get them, code to do so would be inserted here. 
   1.333 +
   1.334 +  // Not a special folder, or something else failed above.
   1.335 +  if (!shellResult)
   1.336 +    shellResult = ::SHGetFileInfoW(filePath.get(),
   1.337 +                                   FILE_ATTRIBUTE_ARCHIVE, &sfi, sizeof(sfi), infoFlags);
   1.338 +
   1.339 +  if (shellResult && sfi.hIcon)
   1.340 +    *hIcon = sfi.hIcon;
   1.341 +  else
   1.342 +    rv = NS_ERROR_NOT_AVAILABLE;
   1.343 +
   1.344 +  return rv;
   1.345 +}
   1.346 +
   1.347 +nsresult nsIconChannel::GetStockHIcon(nsIMozIconURI *aIconURI, HICON *hIcon)
   1.348 +{
   1.349 +  nsresult rv = NS_OK;
   1.350 +
   1.351 +  // We can only do this on Vista or above
   1.352 +  HMODULE hShellDLL = ::LoadLibraryW(L"shell32.dll");
   1.353 +  decltype(SHGetStockIconInfo)* pSHGetStockIconInfo =
   1.354 +    (decltype(SHGetStockIconInfo)*) ::GetProcAddress(hShellDLL, "SHGetStockIconInfo");
   1.355 +
   1.356 +  if (pSHGetStockIconInfo)
   1.357 +  {
   1.358 +    uint32_t desiredImageSize;
   1.359 +    aIconURI->GetImageSize(&desiredImageSize);
   1.360 +    nsAutoCString stockIcon;
   1.361 +    aIconURI->GetStockIcon(stockIcon);
   1.362 +
   1.363 +    SHSTOCKICONID stockIconID = GetStockIconIDForName(stockIcon);
   1.364 +    if (stockIconID == SIID_INVALID)
   1.365 +      return NS_ERROR_NOT_AVAILABLE;
   1.366 +
   1.367 +    UINT infoFlags = SHGSI_ICON;
   1.368 +    infoFlags |= GetSizeInfoFlag(desiredImageSize);
   1.369 +
   1.370 +    SHSTOCKICONINFO sii = {0};
   1.371 +    sii.cbSize = sizeof(sii);
   1.372 +    HRESULT hr = pSHGetStockIconInfo(stockIconID, infoFlags, &sii);
   1.373 +
   1.374 +    if (SUCCEEDED(hr))
   1.375 +      *hIcon = sii.hIcon;
   1.376 +    else
   1.377 +      rv = NS_ERROR_FAILURE;
   1.378 +  }
   1.379 +  else
   1.380 +  {
   1.381 +    rv = NS_ERROR_NOT_AVAILABLE;
   1.382 +  }
   1.383 +
   1.384 +  if (hShellDLL)
   1.385 +    ::FreeLibrary(hShellDLL);
   1.386 +
   1.387 +  return rv;
   1.388 +}
   1.389 +
   1.390 +// Given a BITMAPINFOHEADER, returns the size of the color table.
   1.391 +static int GetColorTableSize(BITMAPINFOHEADER* aHeader)
   1.392 +{
   1.393 +  int colorTableSize = -1;
   1.394 +
   1.395 +  // http://msdn.microsoft.com/en-us/library/dd183376%28v=VS.85%29.aspx
   1.396 +  switch (aHeader->biBitCount) {
   1.397 +  case 0:
   1.398 +    colorTableSize = 0;
   1.399 +    break;
   1.400 +  case 1:
   1.401 +    colorTableSize = 2 * sizeof(RGBQUAD);
   1.402 +    break;
   1.403 +  case 4:
   1.404 +  case 8:
   1.405 +  {
   1.406 +    // The maximum possible size for the color table is 2**bpp, so check for
   1.407 +    // that and fail if we're not in those bounds
   1.408 +    unsigned int maxEntries = 1 << (aHeader->biBitCount);
   1.409 +    if (aHeader->biClrUsed > 0 && aHeader->biClrUsed <= maxEntries)
   1.410 +      colorTableSize = aHeader->biClrUsed * sizeof(RGBQUAD);
   1.411 +    else if (aHeader->biClrUsed == 0)
   1.412 +      colorTableSize = maxEntries * sizeof(RGBQUAD);
   1.413 +    break;
   1.414 +  }
   1.415 +  case 16:
   1.416 +  case 32:
   1.417 +    // If we have BI_BITFIELDS compression, we would normally need 3 DWORDS for
   1.418 +    // the bitfields mask which would be stored in the color table; However, 
   1.419 +    // we instead force the bitmap to request data of type BI_RGB so the color
   1.420 +    // table should be of size 0.  
   1.421 +    // Setting aHeader->biCompression = BI_RGB forces the later call to 
   1.422 +    // GetDIBits to return to us BI_RGB data.
   1.423 +    if (aHeader->biCompression == BI_BITFIELDS) {
   1.424 +      aHeader->biCompression = BI_RGB;
   1.425 +    }
   1.426 +    colorTableSize = 0;
   1.427 +    break;
   1.428 +  case 24:
   1.429 +    colorTableSize = 0;
   1.430 +    break;
   1.431 +  }
   1.432 +
   1.433 +  if (colorTableSize < 0)
   1.434 +    NS_WARNING("Unable to figure out the color table size for this bitmap");
   1.435 +
   1.436 +  return colorTableSize;
   1.437 +}
   1.438 +
   1.439 +// Given a header and a size, creates a freshly allocated BITMAPINFO structure.
   1.440 +// It is the caller's responsibility to null-check and delete the structure.
   1.441 +static BITMAPINFO* CreateBitmapInfo(BITMAPINFOHEADER* aHeader,
   1.442 +                                    size_t aColorTableSize)
   1.443 +{
   1.444 +  BITMAPINFO* bmi = (BITMAPINFO*) ::operator new(sizeof(BITMAPINFOHEADER) +
   1.445 +                                                 aColorTableSize,
   1.446 +                                                 mozilla::fallible_t());
   1.447 +  if (bmi) {
   1.448 +    memcpy(bmi, aHeader, sizeof(BITMAPINFOHEADER));
   1.449 +    memset(bmi->bmiColors, 0, aColorTableSize);
   1.450 +  }
   1.451 +  return bmi;
   1.452 +}
   1.453 +
   1.454 +nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool nonBlocking)
   1.455 +{
   1.456 +  // Check whether the icon requested's a file icon or a stock icon
   1.457 +  nsresult rv = NS_ERROR_NOT_AVAILABLE;
   1.458 +
   1.459 +  // GetDIBits does not exist on windows mobile.
   1.460 +  HICON hIcon = nullptr;
   1.461 +
   1.462 +  nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
   1.463 +  NS_ENSURE_SUCCESS(rv, rv);
   1.464 +
   1.465 +  nsAutoCString stockIcon;
   1.466 +  iconURI->GetStockIcon(stockIcon);
   1.467 +  if (!stockIcon.IsEmpty())
   1.468 +    rv = GetStockHIcon(iconURI, &hIcon);
   1.469 +  else
   1.470 +    rv = GetHIconFromFile(&hIcon);
   1.471 +
   1.472 +  NS_ENSURE_SUCCESS(rv, rv);
   1.473 +
   1.474 +  if (hIcon)
   1.475 +  {
   1.476 +    // we got a handle to an icon. Now we want to get a bitmap for the icon using GetIconInfo....
   1.477 +    ICONINFO iconInfo;
   1.478 +    if (GetIconInfo(hIcon, &iconInfo))
   1.479 +    {
   1.480 +      // we got the bitmaps, first find out their size
   1.481 +      HDC hDC = CreateCompatibleDC(nullptr); // get a device context for the screen.
   1.482 +      BITMAPINFOHEADER maskHeader  = {sizeof(BITMAPINFOHEADER)};
   1.483 +      BITMAPINFOHEADER colorHeader = {sizeof(BITMAPINFOHEADER)};
   1.484 +      int colorTableSize, maskTableSize;
   1.485 +      if (GetDIBits(hDC, iconInfo.hbmMask,  0, 0, nullptr, (BITMAPINFO*)&maskHeader,  DIB_RGB_COLORS) &&
   1.486 +          GetDIBits(hDC, iconInfo.hbmColor, 0, 0, nullptr, (BITMAPINFO*)&colorHeader, DIB_RGB_COLORS) &&
   1.487 +          maskHeader.biHeight == colorHeader.biHeight &&
   1.488 +          maskHeader.biWidth  == colorHeader.biWidth  &&
   1.489 +          colorHeader.biBitCount > 8 &&
   1.490 +          colorHeader.biSizeImage > 0 &&
   1.491 +          colorHeader.biWidth >= 0 && colorHeader.biWidth <= 255 &&
   1.492 +          colorHeader.biHeight >= 0 && colorHeader.biHeight <= 255 &&
   1.493 +          maskHeader.biSizeImage > 0  &&
   1.494 +          (colorTableSize = GetColorTableSize(&colorHeader)) >= 0 &&
   1.495 +          (maskTableSize  = GetColorTableSize(&maskHeader))  >= 0) {
   1.496 +        uint32_t iconSize = sizeof(ICONFILEHEADER) +
   1.497 +                            sizeof(ICONENTRY) +
   1.498 +                            sizeof(BITMAPINFOHEADER) +
   1.499 +                            colorHeader.biSizeImage +
   1.500 +                            maskHeader.biSizeImage;
   1.501 +
   1.502 +        char *buffer = new char[iconSize];
   1.503 +        if (!buffer)
   1.504 +          rv = NS_ERROR_OUT_OF_MEMORY;
   1.505 +        else {
   1.506 +          char *whereTo = buffer;
   1.507 +          int howMuch;
   1.508 +
   1.509 +          // the data starts with an icon file header
   1.510 +          ICONFILEHEADER iconHeader;
   1.511 +          iconHeader.ifhReserved = 0;
   1.512 +          iconHeader.ifhType = 1;
   1.513 +          iconHeader.ifhCount = 1;
   1.514 +          howMuch = sizeof(ICONFILEHEADER);
   1.515 +          memcpy(whereTo, &iconHeader, howMuch);
   1.516 +          whereTo += howMuch;
   1.517 +
   1.518 +          // followed by the single icon entry
   1.519 +          ICONENTRY iconEntry;
   1.520 +          iconEntry.ieWidth = static_cast<int8_t>(colorHeader.biWidth);
   1.521 +          iconEntry.ieHeight = static_cast<int8_t>(colorHeader.biHeight);
   1.522 +          iconEntry.ieColors = 0;
   1.523 +          iconEntry.ieReserved = 0;
   1.524 +          iconEntry.iePlanes = 1;
   1.525 +          iconEntry.ieBitCount = colorHeader.biBitCount;
   1.526 +          iconEntry.ieSizeImage = sizeof(BITMAPINFOHEADER) +
   1.527 +                                  colorHeader.biSizeImage +
   1.528 +                                  maskHeader.biSizeImage;
   1.529 +          iconEntry.ieFileOffset = sizeof(ICONFILEHEADER) + sizeof(ICONENTRY);
   1.530 +          howMuch = sizeof(ICONENTRY);
   1.531 +          memcpy(whereTo, &iconEntry, howMuch);
   1.532 +          whereTo += howMuch;
   1.533 +
   1.534 +          // followed by the bitmap info header
   1.535 +          // (doubling the height because icons have two bitmaps)
   1.536 +          colorHeader.biHeight *= 2;
   1.537 +          colorHeader.biSizeImage += maskHeader.biSizeImage;
   1.538 +          howMuch = sizeof(BITMAPINFOHEADER);
   1.539 +          memcpy(whereTo, &colorHeader, howMuch);
   1.540 +          whereTo += howMuch;
   1.541 +          colorHeader.biHeight /= 2;
   1.542 +          colorHeader.biSizeImage -= maskHeader.biSizeImage;
   1.543 +
   1.544 +          // followed by the XOR bitmap data (colorHeader)
   1.545 +          // (you'd expect the color table to come here, but it apparently doesn't)
   1.546 +          BITMAPINFO* colorInfo = CreateBitmapInfo(&colorHeader, colorTableSize);
   1.547 +          if (colorInfo && GetDIBits(hDC, iconInfo.hbmColor, 0,
   1.548 +                                     colorHeader.biHeight, whereTo, colorInfo,
   1.549 +                                     DIB_RGB_COLORS)) {
   1.550 +            whereTo += colorHeader.biSizeImage;
   1.551 +
   1.552 +            // and finally the AND bitmap data (maskHeader)
   1.553 +            BITMAPINFO* maskInfo = CreateBitmapInfo(&maskHeader, maskTableSize);
   1.554 +            if (maskInfo && GetDIBits(hDC, iconInfo.hbmMask, 0,
   1.555 +                                      maskHeader.biHeight, whereTo, maskInfo,
   1.556 +                                      DIB_RGB_COLORS)) {
   1.557 +              // Now, create a pipe and stuff our data into it
   1.558 +              nsCOMPtr<nsIInputStream> inStream;
   1.559 +              nsCOMPtr<nsIOutputStream> outStream;
   1.560 +              rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
   1.561 +                              iconSize, iconSize, nonBlocking);
   1.562 +              if (NS_SUCCEEDED(rv)) {
   1.563 +                uint32_t written;
   1.564 +                rv = outStream->Write(buffer, iconSize, &written);
   1.565 +                if (NS_SUCCEEDED(rv)) {
   1.566 +                  NS_ADDREF(*_retval = inStream);
   1.567 +                }
   1.568 +              }
   1.569 +
   1.570 +            } // if we got bitmap bits
   1.571 +            delete maskInfo;
   1.572 +          } // if we got mask bits
   1.573 +          delete colorInfo;
   1.574 +          delete [] buffer;
   1.575 +        } // if we allocated the buffer
   1.576 +      } // if we got mask size
   1.577 +
   1.578 +      DeleteDC(hDC);
   1.579 +      DeleteObject(iconInfo.hbmColor);
   1.580 +      DeleteObject(iconInfo.hbmMask);
   1.581 +    } // if we got icon info
   1.582 +    DestroyIcon(hIcon);
   1.583 +  } // if we got an hIcon
   1.584 +
   1.585 +  // If we didn't make a stream, then fail.
   1.586 +  if (!*_retval && NS_SUCCEEDED(rv))
   1.587 +    rv = NS_ERROR_NOT_AVAILABLE;
   1.588 +  return rv;
   1.589 +}
   1.590 +
   1.591 +NS_IMETHODIMP nsIconChannel::GetContentType(nsACString &aContentType) 
   1.592 +{
   1.593 +  aContentType.AssignLiteral(IMAGE_ICO);
   1.594 +  return NS_OK;
   1.595 +}
   1.596 +
   1.597 +NS_IMETHODIMP
   1.598 +nsIconChannel::SetContentType(const nsACString &aContentType)
   1.599 +{
   1.600 +  // It doesn't make sense to set the content-type on this type
   1.601 +  // of channel...
   1.602 +  return NS_ERROR_FAILURE;
   1.603 +}
   1.604 +
   1.605 +NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString &aContentCharset) 
   1.606 +{
   1.607 +  aContentCharset.Truncate();
   1.608 +  return NS_OK;
   1.609 +}
   1.610 +
   1.611 +NS_IMETHODIMP
   1.612 +nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
   1.613 +{
   1.614 +  // It doesn't make sense to set the content-charset on this type
   1.615 +  // of channel...
   1.616 +  return NS_ERROR_FAILURE;
   1.617 +}
   1.618 +
   1.619 +NS_IMETHODIMP
   1.620 +nsIconChannel::GetContentDisposition(uint32_t *aContentDisposition)
   1.621 +{
   1.622 +  return NS_ERROR_NOT_AVAILABLE;
   1.623 +}
   1.624 +
   1.625 +NS_IMETHODIMP
   1.626 +nsIconChannel::SetContentDisposition(uint32_t aContentDisposition)
   1.627 +{
   1.628 +  return NS_ERROR_NOT_AVAILABLE;
   1.629 +}
   1.630 +
   1.631 +NS_IMETHODIMP
   1.632 +nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
   1.633 +{
   1.634 +  return NS_ERROR_NOT_AVAILABLE;
   1.635 +}
   1.636 +
   1.637 +NS_IMETHODIMP
   1.638 +nsIconChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
   1.639 +{
   1.640 +  return NS_ERROR_NOT_AVAILABLE;
   1.641 +}
   1.642 +
   1.643 +NS_IMETHODIMP
   1.644 +nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
   1.645 +{
   1.646 +  return NS_ERROR_NOT_AVAILABLE;
   1.647 +}
   1.648 +
   1.649 +NS_IMETHODIMP nsIconChannel::GetContentLength(int64_t *aContentLength)
   1.650 +{
   1.651 +  *aContentLength = mContentLength;
   1.652 +  return NS_OK;
   1.653 +}
   1.654 +
   1.655 +NS_IMETHODIMP nsIconChannel::SetContentLength(int64_t aContentLength)
   1.656 +{
   1.657 +  NS_NOTREACHED("nsIconChannel::SetContentLength");
   1.658 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.659 +}
   1.660 +
   1.661 +NS_IMETHODIMP nsIconChannel::GetOwner(nsISupports* *aOwner)
   1.662 +{
   1.663 +  *aOwner = mOwner.get();
   1.664 +  NS_IF_ADDREF(*aOwner);
   1.665 +  return NS_OK;
   1.666 +}
   1.667 +
   1.668 +NS_IMETHODIMP nsIconChannel::SetOwner(nsISupports* aOwner)
   1.669 +{
   1.670 +  mOwner = aOwner;
   1.671 +  return NS_OK;
   1.672 +}
   1.673 +
   1.674 +NS_IMETHODIMP nsIconChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
   1.675 +{
   1.676 +  *aNotificationCallbacks = mCallbacks.get();
   1.677 +  NS_IF_ADDREF(*aNotificationCallbacks);
   1.678 +  return NS_OK;
   1.679 +}
   1.680 +
   1.681 +NS_IMETHODIMP nsIconChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
   1.682 +{
   1.683 +  mCallbacks = aNotificationCallbacks;
   1.684 +  return NS_OK;
   1.685 +}
   1.686 +
   1.687 +NS_IMETHODIMP nsIconChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
   1.688 +{
   1.689 +  *aSecurityInfo = nullptr;
   1.690 +  return NS_OK;
   1.691 +}
   1.692 +
   1.693 +// nsIRequestObserver methods
   1.694 +NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
   1.695 +{
   1.696 +  if (mListener)
   1.697 +    return mListener->OnStartRequest(this, aContext);
   1.698 +  return NS_OK;
   1.699 +}
   1.700 +
   1.701 +NS_IMETHODIMP nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
   1.702 +{
   1.703 +  if (mListener) {
   1.704 +    mListener->OnStopRequest(this, aContext, aStatus);
   1.705 +    mListener = nullptr;
   1.706 +  }
   1.707 +
   1.708 +  // Remove from load group
   1.709 +  if (mLoadGroup)
   1.710 +    mLoadGroup->RemoveRequest(this, nullptr, aStatus);
   1.711 +
   1.712 +  // Drop notification callbacks to prevent cycles.
   1.713 +  mCallbacks = nullptr;
   1.714 +
   1.715 +  return NS_OK;
   1.716 +}
   1.717 +
   1.718 +// nsIStreamListener methods
   1.719 +NS_IMETHODIMP nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
   1.720 +                                             nsISupports* aContext,
   1.721 +                                             nsIInputStream* aStream,
   1.722 +                                             uint64_t aOffset,
   1.723 +                                             uint32_t aCount)
   1.724 +{
   1.725 +  if (mListener)
   1.726 +    return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
   1.727 +  return NS_OK;
   1.728 +}

mercurial