nsIconChannel.cpp 22 KB

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