image/decoders/icon/win/nsIconChannel.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/ArrayUtils.h"
michael@0 8
michael@0 9 #include "nsIconChannel.h"
michael@0 10 #include "nsIIconURI.h"
michael@0 11 #include "nsIServiceManager.h"
michael@0 12 #include "nsIInterfaceRequestor.h"
michael@0 13 #include "nsIInterfaceRequestorUtils.h"
michael@0 14 #include "nsXPIDLString.h"
michael@0 15 #include "nsReadableUtils.h"
michael@0 16 #include "nsMimeTypes.h"
michael@0 17 #include "nsMemory.h"
michael@0 18 #include "nsIStringStream.h"
michael@0 19 #include "nsIURL.h"
michael@0 20 #include "nsNetUtil.h"
michael@0 21 #include "nsIFile.h"
michael@0 22 #include "nsIFileURL.h"
michael@0 23 #include "nsIMIMEService.h"
michael@0 24 #include "nsCExternalHandlerService.h"
michael@0 25 #include "nsDirectoryServiceDefs.h"
michael@0 26
michael@0 27 #ifdef _WIN32_WINNT
michael@0 28 #undef _WIN32_WINNT
michael@0 29 #endif
michael@0 30 #define _WIN32_WINNT 0x0600
michael@0 31
michael@0 32 // we need windows.h to read out registry information...
michael@0 33 #include <windows.h>
michael@0 34 #include <shellapi.h>
michael@0 35 #include <shlobj.h>
michael@0 36 #include <objbase.h>
michael@0 37 #include <wchar.h>
michael@0 38
michael@0 39 using namespace mozilla;
michael@0 40
michael@0 41 struct ICONFILEHEADER {
michael@0 42 uint16_t ifhReserved;
michael@0 43 uint16_t ifhType;
michael@0 44 uint16_t ifhCount;
michael@0 45 };
michael@0 46
michael@0 47 struct ICONENTRY {
michael@0 48 int8_t ieWidth;
michael@0 49 int8_t ieHeight;
michael@0 50 uint8_t ieColors;
michael@0 51 uint8_t ieReserved;
michael@0 52 uint16_t iePlanes;
michael@0 53 uint16_t ieBitCount;
michael@0 54 uint32_t ieSizeImage;
michael@0 55 uint32_t ieFileOffset;
michael@0 56 };
michael@0 57
michael@0 58 // Match stock icons with names
michael@0 59 static SHSTOCKICONID GetStockIconIDForName(const nsACString &aStockName)
michael@0 60 {
michael@0 61 // UAC shield icon
michael@0 62 if (aStockName == NS_LITERAL_CSTRING("uac-shield"))
michael@0 63 return SIID_SHIELD;
michael@0 64
michael@0 65 return SIID_INVALID;
michael@0 66 }
michael@0 67
michael@0 68 // nsIconChannel methods
michael@0 69 nsIconChannel::nsIconChannel()
michael@0 70 {
michael@0 71 }
michael@0 72
michael@0 73 nsIconChannel::~nsIconChannel()
michael@0 74 {}
michael@0 75
michael@0 76 NS_IMPL_ISUPPORTS(nsIconChannel,
michael@0 77 nsIChannel,
michael@0 78 nsIRequest,
michael@0 79 nsIRequestObserver,
michael@0 80 nsIStreamListener)
michael@0 81
michael@0 82 nsresult nsIconChannel::Init(nsIURI* uri)
michael@0 83 {
michael@0 84 NS_ASSERTION(uri, "no uri");
michael@0 85 mUrl = uri;
michael@0 86 mOriginalURI = uri;
michael@0 87 nsresult rv;
michael@0 88 mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
michael@0 89 return rv;
michael@0 90 }
michael@0 91
michael@0 92 ////////////////////////////////////////////////////////////////////////////////
michael@0 93 // nsIRequest methods:
michael@0 94
michael@0 95 NS_IMETHODIMP nsIconChannel::GetName(nsACString &result)
michael@0 96 {
michael@0 97 return mUrl->GetSpec(result);
michael@0 98 }
michael@0 99
michael@0 100 NS_IMETHODIMP nsIconChannel::IsPending(bool *result)
michael@0 101 {
michael@0 102 return mPump->IsPending(result);
michael@0 103 }
michael@0 104
michael@0 105 NS_IMETHODIMP nsIconChannel::GetStatus(nsresult *status)
michael@0 106 {
michael@0 107 return mPump->GetStatus(status);
michael@0 108 }
michael@0 109
michael@0 110 NS_IMETHODIMP nsIconChannel::Cancel(nsresult status)
michael@0 111 {
michael@0 112 return mPump->Cancel(status);
michael@0 113 }
michael@0 114
michael@0 115 NS_IMETHODIMP nsIconChannel::Suspend(void)
michael@0 116 {
michael@0 117 return mPump->Suspend();
michael@0 118 }
michael@0 119
michael@0 120 NS_IMETHODIMP nsIconChannel::Resume(void)
michael@0 121 {
michael@0 122 return mPump->Resume();
michael@0 123 }
michael@0 124 NS_IMETHODIMP nsIconChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
michael@0 125 {
michael@0 126 *aLoadGroup = mLoadGroup;
michael@0 127 NS_IF_ADDREF(*aLoadGroup);
michael@0 128 return NS_OK;
michael@0 129 }
michael@0 130
michael@0 131 NS_IMETHODIMP nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
michael@0 132 {
michael@0 133 mLoadGroup = aLoadGroup;
michael@0 134 return NS_OK;
michael@0 135 }
michael@0 136
michael@0 137 NS_IMETHODIMP nsIconChannel::GetLoadFlags(uint32_t *aLoadAttributes)
michael@0 138 {
michael@0 139 return mPump->GetLoadFlags(aLoadAttributes);
michael@0 140 }
michael@0 141
michael@0 142 NS_IMETHODIMP nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes)
michael@0 143 {
michael@0 144 return mPump->SetLoadFlags(aLoadAttributes);
michael@0 145 }
michael@0 146
michael@0 147 ////////////////////////////////////////////////////////////////////////////////
michael@0 148 // nsIChannel methods:
michael@0 149
michael@0 150 NS_IMETHODIMP nsIconChannel::GetOriginalURI(nsIURI* *aURI)
michael@0 151 {
michael@0 152 *aURI = mOriginalURI;
michael@0 153 NS_ADDREF(*aURI);
michael@0 154 return NS_OK;
michael@0 155 }
michael@0 156
michael@0 157 NS_IMETHODIMP nsIconChannel::SetOriginalURI(nsIURI* aURI)
michael@0 158 {
michael@0 159 NS_ENSURE_ARG_POINTER(aURI);
michael@0 160 mOriginalURI = aURI;
michael@0 161 return NS_OK;
michael@0 162 }
michael@0 163
michael@0 164 NS_IMETHODIMP nsIconChannel::GetURI(nsIURI* *aURI)
michael@0 165 {
michael@0 166 *aURI = mUrl;
michael@0 167 NS_IF_ADDREF(*aURI);
michael@0 168 return NS_OK;
michael@0 169 }
michael@0 170
michael@0 171 NS_IMETHODIMP
michael@0 172 nsIconChannel::Open(nsIInputStream **_retval)
michael@0 173 {
michael@0 174 return MakeInputStream(_retval, false);
michael@0 175 }
michael@0 176
michael@0 177 nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile ** aLocalFile, uint32_t * aDesiredImageSize, nsCString &aContentType, nsCString &aFileExtension)
michael@0 178 {
michael@0 179 nsresult rv = NS_OK;
michael@0 180 nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
michael@0 181 NS_ENSURE_SUCCESS(rv, rv);
michael@0 182
michael@0 183 iconURI->GetImageSize(aDesiredImageSize);
michael@0 184 iconURI->GetContentType(aContentType);
michael@0 185 iconURI->GetFileExtension(aFileExtension);
michael@0 186
michael@0 187 nsCOMPtr<nsIURL> url;
michael@0 188 rv = iconURI->GetIconURL(getter_AddRefs(url));
michael@0 189 if (NS_FAILED(rv) || !url) return NS_OK;
michael@0 190
michael@0 191 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
michael@0 192 if (NS_FAILED(rv) || !fileURL) return NS_OK;
michael@0 193
michael@0 194 nsCOMPtr<nsIFile> file;
michael@0 195 rv = fileURL->GetFile(getter_AddRefs(file));
michael@0 196 if (NS_FAILED(rv) || !file) return NS_OK;
michael@0 197
michael@0 198 return file->Clone(aLocalFile);
michael@0 199 }
michael@0 200
michael@0 201 NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
michael@0 202 {
michael@0 203 nsCOMPtr<nsIInputStream> inStream;
michael@0 204 nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
michael@0 205 if (NS_FAILED(rv))
michael@0 206 return rv;
michael@0 207
michael@0 208 // Init our streampump
michael@0 209 rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
michael@0 210 if (NS_FAILED(rv))
michael@0 211 return rv;
michael@0 212
michael@0 213 rv = mPump->AsyncRead(this, ctxt);
michael@0 214 if (NS_SUCCEEDED(rv)) {
michael@0 215 // Store our real listener
michael@0 216 mListener = aListener;
michael@0 217 // Add ourself to the load group, if available
michael@0 218 if (mLoadGroup)
michael@0 219 mLoadGroup->AddRequest(this, nullptr);
michael@0 220 }
michael@0 221 return rv;
michael@0 222 }
michael@0 223
michael@0 224 static DWORD GetSpecialFolderIcon(nsIFile* aFile, int aFolder, SHFILEINFOW* aSFI, UINT aInfoFlags)
michael@0 225 {
michael@0 226 DWORD shellResult = 0;
michael@0 227
michael@0 228 if (!aFile)
michael@0 229 return shellResult;
michael@0 230
michael@0 231 wchar_t fileNativePath[MAX_PATH];
michael@0 232 nsAutoString fileNativePathStr;
michael@0 233 aFile->GetPath(fileNativePathStr);
michael@0 234 ::GetShortPathNameW(fileNativePathStr.get(), fileNativePath, ArrayLength(fileNativePath));
michael@0 235
michael@0 236 LPITEMIDLIST idList;
michael@0 237 HRESULT hr = ::SHGetSpecialFolderLocation(nullptr, aFolder, &idList);
michael@0 238 if (SUCCEEDED(hr)) {
michael@0 239 wchar_t specialNativePath[MAX_PATH];
michael@0 240 ::SHGetPathFromIDListW(idList, specialNativePath);
michael@0 241 ::GetShortPathNameW(specialNativePath, specialNativePath, ArrayLength(specialNativePath));
michael@0 242
michael@0 243 if (!wcsicmp(fileNativePath, specialNativePath)) {
michael@0 244 aInfoFlags |= (SHGFI_PIDL | SHGFI_SYSICONINDEX);
michael@0 245 shellResult = ::SHGetFileInfoW((LPCWSTR)(LPCITEMIDLIST)idList, 0, aSFI,
michael@0 246 sizeof(*aSFI), aInfoFlags);
michael@0 247 }
michael@0 248 }
michael@0 249 CoTaskMemFree(idList);
michael@0 250 return shellResult;
michael@0 251 }
michael@0 252
michael@0 253 static UINT GetSizeInfoFlag(uint32_t aDesiredImageSize)
michael@0 254 {
michael@0 255 UINT infoFlag;
michael@0 256 if (aDesiredImageSize > 16)
michael@0 257 infoFlag = SHGFI_SHELLICONSIZE;
michael@0 258 else
michael@0 259 infoFlag = SHGFI_SMALLICON;
michael@0 260
michael@0 261 return infoFlag;
michael@0 262 }
michael@0 263
michael@0 264 nsresult nsIconChannel::GetHIconFromFile(HICON *hIcon)
michael@0 265 {
michael@0 266 nsXPIDLCString contentType;
michael@0 267 nsCString fileExt;
michael@0 268 nsCOMPtr<nsIFile> localFile; // file we want an icon for
michael@0 269 uint32_t desiredImageSize;
michael@0 270 nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(localFile), &desiredImageSize, contentType, fileExt);
michael@0 271 NS_ENSURE_SUCCESS(rv, rv);
michael@0 272
michael@0 273 // if the file exists, we are going to use it's real attributes...otherwise we only want to use it for it's extension...
michael@0 274 SHFILEINFOW sfi;
michael@0 275 UINT infoFlags = SHGFI_ICON;
michael@0 276
michael@0 277 bool fileExists = false;
michael@0 278
michael@0 279 nsAutoString filePath;
michael@0 280 CopyASCIItoUTF16(fileExt, filePath);
michael@0 281 if (localFile)
michael@0 282 {
michael@0 283 rv = localFile->Normalize();
michael@0 284 NS_ENSURE_SUCCESS(rv, rv);
michael@0 285
michael@0 286 localFile->GetPath(filePath);
michael@0 287 if (filePath.Length() < 2 || filePath[1] != ':')
michael@0 288 return NS_ERROR_MALFORMED_URI; // UNC
michael@0 289
michael@0 290 if (filePath.Last() == ':')
michael@0 291 filePath.Append('\\');
michael@0 292 else {
michael@0 293 localFile->Exists(&fileExists);
michael@0 294 if (!fileExists)
michael@0 295 localFile->GetLeafName(filePath);
michael@0 296 }
michael@0 297 }
michael@0 298
michael@0 299 if (!fileExists)
michael@0 300 infoFlags |= SHGFI_USEFILEATTRIBUTES;
michael@0 301
michael@0 302 infoFlags |= GetSizeInfoFlag(desiredImageSize);
michael@0 303
michael@0 304 // if we have a content type... then use it! but for existing files, we want
michael@0 305 // to show their real icon.
michael@0 306 if (!fileExists && !contentType.IsEmpty())
michael@0 307 {
michael@0 308 nsCOMPtr<nsIMIMEService> mimeService (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
michael@0 309 NS_ENSURE_SUCCESS(rv, rv);
michael@0 310
michael@0 311 nsAutoCString defFileExt;
michael@0 312 mimeService->GetPrimaryExtension(contentType, fileExt, defFileExt);
michael@0 313 // If the mime service does not know about this mime type, we show
michael@0 314 // the generic icon.
michael@0 315 // In any case, we need to insert a '.' before the extension.
michael@0 316 filePath = NS_LITERAL_STRING(".") + NS_ConvertUTF8toUTF16(defFileExt);
michael@0 317 }
michael@0 318
michael@0 319 // Is this the "Desktop" folder?
michael@0 320 DWORD shellResult = GetSpecialFolderIcon(localFile, CSIDL_DESKTOP, &sfi, infoFlags);
michael@0 321 if (!shellResult) {
michael@0 322 // Is this the "My Documents" folder?
michael@0 323 shellResult = GetSpecialFolderIcon(localFile, CSIDL_PERSONAL, &sfi, infoFlags);
michael@0 324 }
michael@0 325
michael@0 326 // There are other "Special Folders" and Namespace entities that we are not
michael@0 327 // fetching icons for, see:
michael@0 328 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp
michael@0 329 // If we ever need to get them, code to do so would be inserted here.
michael@0 330
michael@0 331 // Not a special folder, or something else failed above.
michael@0 332 if (!shellResult)
michael@0 333 shellResult = ::SHGetFileInfoW(filePath.get(),
michael@0 334 FILE_ATTRIBUTE_ARCHIVE, &sfi, sizeof(sfi), infoFlags);
michael@0 335
michael@0 336 if (shellResult && sfi.hIcon)
michael@0 337 *hIcon = sfi.hIcon;
michael@0 338 else
michael@0 339 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 340
michael@0 341 return rv;
michael@0 342 }
michael@0 343
michael@0 344 nsresult nsIconChannel::GetStockHIcon(nsIMozIconURI *aIconURI, HICON *hIcon)
michael@0 345 {
michael@0 346 nsresult rv = NS_OK;
michael@0 347
michael@0 348 // We can only do this on Vista or above
michael@0 349 HMODULE hShellDLL = ::LoadLibraryW(L"shell32.dll");
michael@0 350 decltype(SHGetStockIconInfo)* pSHGetStockIconInfo =
michael@0 351 (decltype(SHGetStockIconInfo)*) ::GetProcAddress(hShellDLL, "SHGetStockIconInfo");
michael@0 352
michael@0 353 if (pSHGetStockIconInfo)
michael@0 354 {
michael@0 355 uint32_t desiredImageSize;
michael@0 356 aIconURI->GetImageSize(&desiredImageSize);
michael@0 357 nsAutoCString stockIcon;
michael@0 358 aIconURI->GetStockIcon(stockIcon);
michael@0 359
michael@0 360 SHSTOCKICONID stockIconID = GetStockIconIDForName(stockIcon);
michael@0 361 if (stockIconID == SIID_INVALID)
michael@0 362 return NS_ERROR_NOT_AVAILABLE;
michael@0 363
michael@0 364 UINT infoFlags = SHGSI_ICON;
michael@0 365 infoFlags |= GetSizeInfoFlag(desiredImageSize);
michael@0 366
michael@0 367 SHSTOCKICONINFO sii = {0};
michael@0 368 sii.cbSize = sizeof(sii);
michael@0 369 HRESULT hr = pSHGetStockIconInfo(stockIconID, infoFlags, &sii);
michael@0 370
michael@0 371 if (SUCCEEDED(hr))
michael@0 372 *hIcon = sii.hIcon;
michael@0 373 else
michael@0 374 rv = NS_ERROR_FAILURE;
michael@0 375 }
michael@0 376 else
michael@0 377 {
michael@0 378 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 379 }
michael@0 380
michael@0 381 if (hShellDLL)
michael@0 382 ::FreeLibrary(hShellDLL);
michael@0 383
michael@0 384 return rv;
michael@0 385 }
michael@0 386
michael@0 387 // Given a BITMAPINFOHEADER, returns the size of the color table.
michael@0 388 static int GetColorTableSize(BITMAPINFOHEADER* aHeader)
michael@0 389 {
michael@0 390 int colorTableSize = -1;
michael@0 391
michael@0 392 // http://msdn.microsoft.com/en-us/library/dd183376%28v=VS.85%29.aspx
michael@0 393 switch (aHeader->biBitCount) {
michael@0 394 case 0:
michael@0 395 colorTableSize = 0;
michael@0 396 break;
michael@0 397 case 1:
michael@0 398 colorTableSize = 2 * sizeof(RGBQUAD);
michael@0 399 break;
michael@0 400 case 4:
michael@0 401 case 8:
michael@0 402 {
michael@0 403 // The maximum possible size for the color table is 2**bpp, so check for
michael@0 404 // that and fail if we're not in those bounds
michael@0 405 unsigned int maxEntries = 1 << (aHeader->biBitCount);
michael@0 406 if (aHeader->biClrUsed > 0 && aHeader->biClrUsed <= maxEntries)
michael@0 407 colorTableSize = aHeader->biClrUsed * sizeof(RGBQUAD);
michael@0 408 else if (aHeader->biClrUsed == 0)
michael@0 409 colorTableSize = maxEntries * sizeof(RGBQUAD);
michael@0 410 break;
michael@0 411 }
michael@0 412 case 16:
michael@0 413 case 32:
michael@0 414 // If we have BI_BITFIELDS compression, we would normally need 3 DWORDS for
michael@0 415 // the bitfields mask which would be stored in the color table; However,
michael@0 416 // we instead force the bitmap to request data of type BI_RGB so the color
michael@0 417 // table should be of size 0.
michael@0 418 // Setting aHeader->biCompression = BI_RGB forces the later call to
michael@0 419 // GetDIBits to return to us BI_RGB data.
michael@0 420 if (aHeader->biCompression == BI_BITFIELDS) {
michael@0 421 aHeader->biCompression = BI_RGB;
michael@0 422 }
michael@0 423 colorTableSize = 0;
michael@0 424 break;
michael@0 425 case 24:
michael@0 426 colorTableSize = 0;
michael@0 427 break;
michael@0 428 }
michael@0 429
michael@0 430 if (colorTableSize < 0)
michael@0 431 NS_WARNING("Unable to figure out the color table size for this bitmap");
michael@0 432
michael@0 433 return colorTableSize;
michael@0 434 }
michael@0 435
michael@0 436 // Given a header and a size, creates a freshly allocated BITMAPINFO structure.
michael@0 437 // It is the caller's responsibility to null-check and delete the structure.
michael@0 438 static BITMAPINFO* CreateBitmapInfo(BITMAPINFOHEADER* aHeader,
michael@0 439 size_t aColorTableSize)
michael@0 440 {
michael@0 441 BITMAPINFO* bmi = (BITMAPINFO*) ::operator new(sizeof(BITMAPINFOHEADER) +
michael@0 442 aColorTableSize,
michael@0 443 mozilla::fallible_t());
michael@0 444 if (bmi) {
michael@0 445 memcpy(bmi, aHeader, sizeof(BITMAPINFOHEADER));
michael@0 446 memset(bmi->bmiColors, 0, aColorTableSize);
michael@0 447 }
michael@0 448 return bmi;
michael@0 449 }
michael@0 450
michael@0 451 nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool nonBlocking)
michael@0 452 {
michael@0 453 // Check whether the icon requested's a file icon or a stock icon
michael@0 454 nsresult rv = NS_ERROR_NOT_AVAILABLE;
michael@0 455
michael@0 456 // GetDIBits does not exist on windows mobile.
michael@0 457 HICON hIcon = nullptr;
michael@0 458
michael@0 459 nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
michael@0 460 NS_ENSURE_SUCCESS(rv, rv);
michael@0 461
michael@0 462 nsAutoCString stockIcon;
michael@0 463 iconURI->GetStockIcon(stockIcon);
michael@0 464 if (!stockIcon.IsEmpty())
michael@0 465 rv = GetStockHIcon(iconURI, &hIcon);
michael@0 466 else
michael@0 467 rv = GetHIconFromFile(&hIcon);
michael@0 468
michael@0 469 NS_ENSURE_SUCCESS(rv, rv);
michael@0 470
michael@0 471 if (hIcon)
michael@0 472 {
michael@0 473 // we got a handle to an icon. Now we want to get a bitmap for the icon using GetIconInfo....
michael@0 474 ICONINFO iconInfo;
michael@0 475 if (GetIconInfo(hIcon, &iconInfo))
michael@0 476 {
michael@0 477 // we got the bitmaps, first find out their size
michael@0 478 HDC hDC = CreateCompatibleDC(nullptr); // get a device context for the screen.
michael@0 479 BITMAPINFOHEADER maskHeader = {sizeof(BITMAPINFOHEADER)};
michael@0 480 BITMAPINFOHEADER colorHeader = {sizeof(BITMAPINFOHEADER)};
michael@0 481 int colorTableSize, maskTableSize;
michael@0 482 if (GetDIBits(hDC, iconInfo.hbmMask, 0, 0, nullptr, (BITMAPINFO*)&maskHeader, DIB_RGB_COLORS) &&
michael@0 483 GetDIBits(hDC, iconInfo.hbmColor, 0, 0, nullptr, (BITMAPINFO*)&colorHeader, DIB_RGB_COLORS) &&
michael@0 484 maskHeader.biHeight == colorHeader.biHeight &&
michael@0 485 maskHeader.biWidth == colorHeader.biWidth &&
michael@0 486 colorHeader.biBitCount > 8 &&
michael@0 487 colorHeader.biSizeImage > 0 &&
michael@0 488 colorHeader.biWidth >= 0 && colorHeader.biWidth <= 255 &&
michael@0 489 colorHeader.biHeight >= 0 && colorHeader.biHeight <= 255 &&
michael@0 490 maskHeader.biSizeImage > 0 &&
michael@0 491 (colorTableSize = GetColorTableSize(&colorHeader)) >= 0 &&
michael@0 492 (maskTableSize = GetColorTableSize(&maskHeader)) >= 0) {
michael@0 493 uint32_t iconSize = sizeof(ICONFILEHEADER) +
michael@0 494 sizeof(ICONENTRY) +
michael@0 495 sizeof(BITMAPINFOHEADER) +
michael@0 496 colorHeader.biSizeImage +
michael@0 497 maskHeader.biSizeImage;
michael@0 498
michael@0 499 char *buffer = new char[iconSize];
michael@0 500 if (!buffer)
michael@0 501 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 502 else {
michael@0 503 char *whereTo = buffer;
michael@0 504 int howMuch;
michael@0 505
michael@0 506 // the data starts with an icon file header
michael@0 507 ICONFILEHEADER iconHeader;
michael@0 508 iconHeader.ifhReserved = 0;
michael@0 509 iconHeader.ifhType = 1;
michael@0 510 iconHeader.ifhCount = 1;
michael@0 511 howMuch = sizeof(ICONFILEHEADER);
michael@0 512 memcpy(whereTo, &iconHeader, howMuch);
michael@0 513 whereTo += howMuch;
michael@0 514
michael@0 515 // followed by the single icon entry
michael@0 516 ICONENTRY iconEntry;
michael@0 517 iconEntry.ieWidth = static_cast<int8_t>(colorHeader.biWidth);
michael@0 518 iconEntry.ieHeight = static_cast<int8_t>(colorHeader.biHeight);
michael@0 519 iconEntry.ieColors = 0;
michael@0 520 iconEntry.ieReserved = 0;
michael@0 521 iconEntry.iePlanes = 1;
michael@0 522 iconEntry.ieBitCount = colorHeader.biBitCount;
michael@0 523 iconEntry.ieSizeImage = sizeof(BITMAPINFOHEADER) +
michael@0 524 colorHeader.biSizeImage +
michael@0 525 maskHeader.biSizeImage;
michael@0 526 iconEntry.ieFileOffset = sizeof(ICONFILEHEADER) + sizeof(ICONENTRY);
michael@0 527 howMuch = sizeof(ICONENTRY);
michael@0 528 memcpy(whereTo, &iconEntry, howMuch);
michael@0 529 whereTo += howMuch;
michael@0 530
michael@0 531 // followed by the bitmap info header
michael@0 532 // (doubling the height because icons have two bitmaps)
michael@0 533 colorHeader.biHeight *= 2;
michael@0 534 colorHeader.biSizeImage += maskHeader.biSizeImage;
michael@0 535 howMuch = sizeof(BITMAPINFOHEADER);
michael@0 536 memcpy(whereTo, &colorHeader, howMuch);
michael@0 537 whereTo += howMuch;
michael@0 538 colorHeader.biHeight /= 2;
michael@0 539 colorHeader.biSizeImage -= maskHeader.biSizeImage;
michael@0 540
michael@0 541 // followed by the XOR bitmap data (colorHeader)
michael@0 542 // (you'd expect the color table to come here, but it apparently doesn't)
michael@0 543 BITMAPINFO* colorInfo = CreateBitmapInfo(&colorHeader, colorTableSize);
michael@0 544 if (colorInfo && GetDIBits(hDC, iconInfo.hbmColor, 0,
michael@0 545 colorHeader.biHeight, whereTo, colorInfo,
michael@0 546 DIB_RGB_COLORS)) {
michael@0 547 whereTo += colorHeader.biSizeImage;
michael@0 548
michael@0 549 // and finally the AND bitmap data (maskHeader)
michael@0 550 BITMAPINFO* maskInfo = CreateBitmapInfo(&maskHeader, maskTableSize);
michael@0 551 if (maskInfo && GetDIBits(hDC, iconInfo.hbmMask, 0,
michael@0 552 maskHeader.biHeight, whereTo, maskInfo,
michael@0 553 DIB_RGB_COLORS)) {
michael@0 554 // Now, create a pipe and stuff our data into it
michael@0 555 nsCOMPtr<nsIInputStream> inStream;
michael@0 556 nsCOMPtr<nsIOutputStream> outStream;
michael@0 557 rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
michael@0 558 iconSize, iconSize, nonBlocking);
michael@0 559 if (NS_SUCCEEDED(rv)) {
michael@0 560 uint32_t written;
michael@0 561 rv = outStream->Write(buffer, iconSize, &written);
michael@0 562 if (NS_SUCCEEDED(rv)) {
michael@0 563 NS_ADDREF(*_retval = inStream);
michael@0 564 }
michael@0 565 }
michael@0 566
michael@0 567 } // if we got bitmap bits
michael@0 568 delete maskInfo;
michael@0 569 } // if we got mask bits
michael@0 570 delete colorInfo;
michael@0 571 delete [] buffer;
michael@0 572 } // if we allocated the buffer
michael@0 573 } // if we got mask size
michael@0 574
michael@0 575 DeleteDC(hDC);
michael@0 576 DeleteObject(iconInfo.hbmColor);
michael@0 577 DeleteObject(iconInfo.hbmMask);
michael@0 578 } // if we got icon info
michael@0 579 DestroyIcon(hIcon);
michael@0 580 } // if we got an hIcon
michael@0 581
michael@0 582 // If we didn't make a stream, then fail.
michael@0 583 if (!*_retval && NS_SUCCEEDED(rv))
michael@0 584 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 585 return rv;
michael@0 586 }
michael@0 587
michael@0 588 NS_IMETHODIMP nsIconChannel::GetContentType(nsACString &aContentType)
michael@0 589 {
michael@0 590 aContentType.AssignLiteral(IMAGE_ICO);
michael@0 591 return NS_OK;
michael@0 592 }
michael@0 593
michael@0 594 NS_IMETHODIMP
michael@0 595 nsIconChannel::SetContentType(const nsACString &aContentType)
michael@0 596 {
michael@0 597 // It doesn't make sense to set the content-type on this type
michael@0 598 // of channel...
michael@0 599 return NS_ERROR_FAILURE;
michael@0 600 }
michael@0 601
michael@0 602 NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString &aContentCharset)
michael@0 603 {
michael@0 604 aContentCharset.Truncate();
michael@0 605 return NS_OK;
michael@0 606 }
michael@0 607
michael@0 608 NS_IMETHODIMP
michael@0 609 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
michael@0 610 {
michael@0 611 // It doesn't make sense to set the content-charset on this type
michael@0 612 // of channel...
michael@0 613 return NS_ERROR_FAILURE;
michael@0 614 }
michael@0 615
michael@0 616 NS_IMETHODIMP
michael@0 617 nsIconChannel::GetContentDisposition(uint32_t *aContentDisposition)
michael@0 618 {
michael@0 619 return NS_ERROR_NOT_AVAILABLE;
michael@0 620 }
michael@0 621
michael@0 622 NS_IMETHODIMP
michael@0 623 nsIconChannel::SetContentDisposition(uint32_t aContentDisposition)
michael@0 624 {
michael@0 625 return NS_ERROR_NOT_AVAILABLE;
michael@0 626 }
michael@0 627
michael@0 628 NS_IMETHODIMP
michael@0 629 nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
michael@0 630 {
michael@0 631 return NS_ERROR_NOT_AVAILABLE;
michael@0 632 }
michael@0 633
michael@0 634 NS_IMETHODIMP
michael@0 635 nsIconChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
michael@0 636 {
michael@0 637 return NS_ERROR_NOT_AVAILABLE;
michael@0 638 }
michael@0 639
michael@0 640 NS_IMETHODIMP
michael@0 641 nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
michael@0 642 {
michael@0 643 return NS_ERROR_NOT_AVAILABLE;
michael@0 644 }
michael@0 645
michael@0 646 NS_IMETHODIMP nsIconChannel::GetContentLength(int64_t *aContentLength)
michael@0 647 {
michael@0 648 *aContentLength = mContentLength;
michael@0 649 return NS_OK;
michael@0 650 }
michael@0 651
michael@0 652 NS_IMETHODIMP nsIconChannel::SetContentLength(int64_t aContentLength)
michael@0 653 {
michael@0 654 NS_NOTREACHED("nsIconChannel::SetContentLength");
michael@0 655 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 656 }
michael@0 657
michael@0 658 NS_IMETHODIMP nsIconChannel::GetOwner(nsISupports* *aOwner)
michael@0 659 {
michael@0 660 *aOwner = mOwner.get();
michael@0 661 NS_IF_ADDREF(*aOwner);
michael@0 662 return NS_OK;
michael@0 663 }
michael@0 664
michael@0 665 NS_IMETHODIMP nsIconChannel::SetOwner(nsISupports* aOwner)
michael@0 666 {
michael@0 667 mOwner = aOwner;
michael@0 668 return NS_OK;
michael@0 669 }
michael@0 670
michael@0 671 NS_IMETHODIMP nsIconChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
michael@0 672 {
michael@0 673 *aNotificationCallbacks = mCallbacks.get();
michael@0 674 NS_IF_ADDREF(*aNotificationCallbacks);
michael@0 675 return NS_OK;
michael@0 676 }
michael@0 677
michael@0 678 NS_IMETHODIMP nsIconChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
michael@0 679 {
michael@0 680 mCallbacks = aNotificationCallbacks;
michael@0 681 return NS_OK;
michael@0 682 }
michael@0 683
michael@0 684 NS_IMETHODIMP nsIconChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
michael@0 685 {
michael@0 686 *aSecurityInfo = nullptr;
michael@0 687 return NS_OK;
michael@0 688 }
michael@0 689
michael@0 690 // nsIRequestObserver methods
michael@0 691 NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
michael@0 692 {
michael@0 693 if (mListener)
michael@0 694 return mListener->OnStartRequest(this, aContext);
michael@0 695 return NS_OK;
michael@0 696 }
michael@0 697
michael@0 698 NS_IMETHODIMP nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
michael@0 699 {
michael@0 700 if (mListener) {
michael@0 701 mListener->OnStopRequest(this, aContext, aStatus);
michael@0 702 mListener = nullptr;
michael@0 703 }
michael@0 704
michael@0 705 // Remove from load group
michael@0 706 if (mLoadGroup)
michael@0 707 mLoadGroup->RemoveRequest(this, nullptr, aStatus);
michael@0 708
michael@0 709 // Drop notification callbacks to prevent cycles.
michael@0 710 mCallbacks = nullptr;
michael@0 711
michael@0 712 return NS_OK;
michael@0 713 }
michael@0 714
michael@0 715 // nsIStreamListener methods
michael@0 716 NS_IMETHODIMP nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
michael@0 717 nsISupports* aContext,
michael@0 718 nsIInputStream* aStream,
michael@0 719 uint64_t aOffset,
michael@0 720 uint32_t aCount)
michael@0 721 {
michael@0 722 if (mListener)
michael@0 723 return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
michael@0 724 return NS_OK;
michael@0 725 }

mercurial