tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsIconChannel.cpp (30810B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      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 
      7 #include "mozilla/BasePrincipal.h"
      8 #include "mozilla/Monitor.h"
      9 #include "mozilla/SyncRunnable.h"
     10 #include "mozilla/UniquePtr.h"
     11 #include "mozilla/WindowsProcessMitigations.h"
     12 #include "mozilla/dom/ContentChild.h"
     13 #include "mozilla/ipc/ByteBuf.h"
     14 
     15 #include "nsComponentManagerUtils.h"
     16 #include "nsIconChannel.h"
     17 #include "nsIIconURI.h"
     18 #include "nsIInterfaceRequestor.h"
     19 #include "nsIInterfaceRequestorUtils.h"
     20 #include "nsString.h"
     21 #include "nsReadableUtils.h"
     22 #include "nsMimeTypes.h"
     23 #include "nsIURL.h"
     24 #include "nsIPipe.h"
     25 #include "nsNetCID.h"
     26 #include "nsIFile.h"
     27 #include "nsIFileURL.h"
     28 #include "nsIIconURI.h"
     29 #include "nsIAsyncInputStream.h"
     30 #include "nsIAsyncOutputStream.h"
     31 #include "nsIMIMEService.h"
     32 #include "nsCExternalHandlerService.h"
     33 #include "nsDirectoryServiceDefs.h"
     34 #include "nsProxyRelease.h"
     35 #include "nsContentSecurityManager.h"
     36 #include "nsContentUtils.h"
     37 #include "nsNetUtil.h"
     38 #include "nsThreadUtils.h"
     39 
     40 #include "Decoder.h"
     41 #include "DecodePool.h"
     42 
     43 // we need windows.h to read out registry information...
     44 #include <windows.h>
     45 #include <shellapi.h>
     46 #include <shlobj.h>
     47 #include <objbase.h>
     48 #include <wchar.h>
     49 
     50 using namespace mozilla;
     51 using namespace mozilla::image;
     52 
     53 using mozilla::ipc::ByteBuf;
     54 
     55 struct ICONFILEHEADER {
     56  uint16_t ifhReserved;
     57  uint16_t ifhType;
     58  uint16_t ifhCount;
     59 };
     60 
     61 struct ICONENTRY {
     62  int8_t ieWidth;
     63  int8_t ieHeight;
     64  uint8_t ieColors;
     65  uint8_t ieReserved;
     66  uint16_t iePlanes;
     67  uint16_t ieBitCount;
     68  uint32_t ieSizeImage;
     69  uint32_t ieFileOffset;
     70 };
     71 
     72 struct IconPathInfo {
     73  nsCOMPtr<nsIFile> localFile;
     74  nsAutoString filePath;
     75  UINT infoFlags = 0;
     76 };
     77 
     78 using HIconPromise = MozPromise<HICON, nsresult, true>;
     79 
     80 static UINT GetSizeInfoFlag(uint32_t aDesiredImageSize) {
     81  return aDesiredImageSize > 16 ? SHGFI_SHELLICONSIZE : SHGFI_SMALLICON;
     82 }
     83 
     84 static nsresult ExtractIconInfoFromUrl(nsIURI* aUrl, nsIFile** aLocalFile,
     85                                       uint32_t* aDesiredImageSize,
     86                                       nsCString& aContentType,
     87                                       nsCString& aFileExtension) {
     88  nsresult rv = NS_OK;
     89  nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(aUrl, &rv));
     90  NS_ENSURE_SUCCESS(rv, rv);
     91 
     92  iconURI->GetImageSize(aDesiredImageSize);
     93  iconURI->GetContentType(aContentType);
     94  iconURI->GetFileExtension(aFileExtension);
     95 
     96  nsCOMPtr<nsIURL> url;
     97  rv = iconURI->GetIconURL(getter_AddRefs(url));
     98  if (NS_FAILED(rv) || !url) return NS_OK;
     99 
    100  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
    101  if (NS_FAILED(rv) || !fileURL) return NS_OK;
    102 
    103  nsCOMPtr<nsIFile> file;
    104  rv = fileURL->GetFile(getter_AddRefs(file));
    105  if (NS_FAILED(rv) || !file) return NS_OK;
    106 
    107  return file->Clone(aLocalFile);
    108 }
    109 
    110 static nsresult ExtractIconPathInfoFromUrl(nsIURI* aUrl,
    111                                           IconPathInfo* aIconPathInfo) {
    112  nsCString contentType;
    113  nsCString fileExt;
    114  nsCOMPtr<nsIFile> localFile;  // file we want an icon for
    115  uint32_t desiredImageSize;
    116  nsresult rv = ExtractIconInfoFromUrl(aUrl, getter_AddRefs(localFile),
    117                                       &desiredImageSize, contentType, fileExt);
    118  NS_ENSURE_SUCCESS(rv, rv);
    119 
    120  // if the file exists, we are going to use it's real attributes...
    121  // otherwise we only want to use it for it's extension...
    122  UINT infoFlags = SHGFI_ICON;
    123 
    124  bool fileExists = false;
    125 
    126  nsAutoString filePath;
    127  CopyASCIItoUTF16(fileExt, filePath);
    128  if (localFile) {
    129    rv = localFile->Normalize();
    130    NS_ENSURE_SUCCESS(rv, rv);
    131 
    132    localFile->GetPath(filePath);
    133    if (filePath.Length() < 2 || filePath[1] != ':') {
    134      return NS_ERROR_MALFORMED_URI;  // UNC
    135    }
    136 
    137    if (filePath.Last() == ':') {
    138      filePath.Append('\\');
    139    } else {
    140      localFile->Exists(&fileExists);
    141      if (!fileExists) {
    142        localFile->GetLeafName(filePath);
    143      }
    144    }
    145  }
    146 
    147  if (!fileExists) {
    148    infoFlags |= SHGFI_USEFILEATTRIBUTES;
    149  }
    150 
    151  infoFlags |= GetSizeInfoFlag(desiredImageSize);
    152 
    153  // if we have a content type... then use it! but for existing files,
    154  // we want to show their real icon.
    155  if (!fileExists && !contentType.IsEmpty()) {
    156    nsCOMPtr<nsIMIMEService> mimeService(
    157        do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
    158    NS_ENSURE_SUCCESS(rv, rv);
    159 
    160    nsAutoCString defFileExt;
    161    mimeService->GetPrimaryExtension(contentType, fileExt, defFileExt);
    162    // If the mime service does not know about this mime type, we show
    163    // the generic icon.
    164    // In any case, we need to insert a '.' before the extension.
    165    filePath = u"."_ns + NS_ConvertUTF8toUTF16(defFileExt);
    166  }
    167 
    168  if (!localFile && !fileExists &&
    169      ((filePath.Length() == 1 && filePath.Last() == '.') ||
    170       filePath.Length() == 0)) {
    171    filePath = u".MozBogusExtensionMoz"_ns;
    172  }
    173 
    174  aIconPathInfo->localFile = std::move(localFile);
    175  aIconPathInfo->filePath = std::move(filePath);
    176  aIconPathInfo->infoFlags = infoFlags;
    177 
    178  return NS_OK;
    179 }
    180 
    181 static bool GetSpecialFolderIcon(nsIFile* aFile, int aFolder, UINT aInfoFlags,
    182                                 HICON* aIcon) {
    183  if (!aFile) {
    184    return false;
    185  }
    186 
    187  wchar_t fileNativePath[MAX_PATH];
    188  nsAutoString fileNativePathStr;
    189  aFile->GetPath(fileNativePathStr);
    190  ::GetShortPathNameW(fileNativePathStr.get(), fileNativePath,
    191                      std::size(fileNativePath));
    192 
    193  struct IdListDeleter {
    194    void operator()(ITEMIDLIST* ptr) { ::CoTaskMemFree(ptr); }
    195  };
    196 
    197  UniquePtr<ITEMIDLIST, IdListDeleter> idList;
    198  HRESULT hr =
    199      ::SHGetSpecialFolderLocation(nullptr, aFolder, getter_Transfers(idList));
    200  if (FAILED(hr)) {
    201    return false;
    202  }
    203 
    204  wchar_t specialNativePath[MAX_PATH];
    205  ::SHGetPathFromIDListW(idList.get(), specialNativePath);
    206  ::GetShortPathNameW(specialNativePath, specialNativePath,
    207                      std::size(specialNativePath));
    208 
    209  if (wcsicmp(fileNativePath, specialNativePath) != 0) {
    210    return false;
    211  }
    212 
    213  SHFILEINFOW sfi = {};
    214  aInfoFlags |= (SHGFI_PIDL | SHGFI_SYSICONINDEX);
    215  if (::SHGetFileInfoW((LPCWSTR)(LPCITEMIDLIST)idList.get(), 0, &sfi,
    216                       sizeof(sfi), aInfoFlags) == 0) {
    217    return false;
    218  }
    219 
    220  *aIcon = sfi.hIcon;
    221  return true;
    222 }
    223 
    224 static nsresult GetIconHandleFromPathInfo(const IconPathInfo& aPathInfo,
    225                                          HICON* aIcon) {
    226  MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
    227 
    228  // Is this the "Desktop" folder?
    229  if (GetSpecialFolderIcon(aPathInfo.localFile, CSIDL_DESKTOP,
    230                           aPathInfo.infoFlags, aIcon)) {
    231    return NS_OK;
    232  }
    233 
    234  // Is this the "My Documents" folder?
    235  if (GetSpecialFolderIcon(aPathInfo.localFile, CSIDL_PERSONAL,
    236                           aPathInfo.infoFlags, aIcon)) {
    237    return NS_OK;
    238  }
    239 
    240  // There are other "Special Folders" and Namespace entities that we
    241  // are not fetching icons for, see:
    242  // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
    243  //        shellcc/platform/shell/reference/enums/csidl.asp
    244  // If we ever need to get them, code to do so would be inserted here.
    245 
    246  // Not a special folder, or something else failed above.
    247  SHFILEINFOW sfi = {};
    248  if (::SHGetFileInfoW(aPathInfo.filePath.get(), FILE_ATTRIBUTE_ARCHIVE, &sfi,
    249                       sizeof(sfi), aPathInfo.infoFlags) != 0) {
    250    *aIcon = sfi.hIcon;
    251    return NS_OK;
    252  }
    253 
    254  return NS_ERROR_NOT_AVAILABLE;
    255 }
    256 
    257 // Match stock icons with names
    258 static mozilla::Maybe<SHSTOCKICONID> GetStockIconIDForName(
    259    const nsACString& aStockName) {
    260  return aStockName.EqualsLiteral("uac-shield") ? Some(SIID_SHIELD) : Nothing();
    261 }
    262 
    263 // Specific to Vista and above
    264 static nsresult GetStockHIcon(nsIMozIconURI* aIconURI, HICON* aIcon) {
    265  uint32_t desiredImageSize;
    266  aIconURI->GetImageSize(&desiredImageSize);
    267  nsAutoCString stockIcon;
    268  aIconURI->GetStockIcon(stockIcon);
    269 
    270  Maybe<SHSTOCKICONID> stockIconID = GetStockIconIDForName(stockIcon);
    271  if (stockIconID.isNothing()) {
    272    return NS_ERROR_NOT_AVAILABLE;
    273  }
    274 
    275  UINT infoFlags = SHGSI_ICON;
    276  infoFlags |= GetSizeInfoFlag(desiredImageSize);
    277 
    278  SHSTOCKICONINFO sii = {0};
    279  sii.cbSize = sizeof(sii);
    280  HRESULT hr = SHGetStockIconInfo(*stockIconID, infoFlags, &sii);
    281  if (FAILED(hr)) {
    282    return NS_ERROR_FAILURE;
    283  }
    284 
    285  *aIcon = sii.hIcon;
    286 
    287  return NS_OK;
    288 }
    289 
    290 // Given a BITMAPINFOHEADER, returns the size of the color table.
    291 static int GetColorTableSize(BITMAPINFOHEADER* aHeader) {
    292  int colorTableSize = -1;
    293 
    294  // http://msdn.microsoft.com/en-us/library/dd183376%28v=VS.85%29.aspx
    295  switch (aHeader->biBitCount) {
    296    case 0:
    297      colorTableSize = 0;
    298      break;
    299    case 1:
    300      colorTableSize = 2 * sizeof(RGBQUAD);
    301      break;
    302    case 4:
    303    case 8: {
    304      // The maximum possible size for the color table is 2**bpp, so check for
    305      // that and fail if we're not in those bounds
    306      unsigned int maxEntries = 1 << (aHeader->biBitCount);
    307      if (aHeader->biClrUsed > 0 && aHeader->biClrUsed <= maxEntries) {
    308        colorTableSize = aHeader->biClrUsed * sizeof(RGBQUAD);
    309      } else if (aHeader->biClrUsed == 0) {
    310        colorTableSize = maxEntries * sizeof(RGBQUAD);
    311      }
    312      break;
    313    }
    314    case 16:
    315    case 32:
    316      // If we have BI_BITFIELDS compression, we would normally need 3 DWORDS
    317      // for the bitfields mask which would be stored in the color table;
    318      // However, we instead force the bitmap to request data of type BI_RGB so
    319      // the color table should be of size 0. Setting aHeader->biCompression =
    320      // BI_RGB forces the later call to GetDIBits to return to us BI_RGB data.
    321      if (aHeader->biCompression == BI_BITFIELDS) {
    322        aHeader->biCompression = BI_RGB;
    323      }
    324      colorTableSize = 0;
    325      break;
    326    case 24:
    327      colorTableSize = 0;
    328      break;
    329  }
    330 
    331  if (colorTableSize < 0) {
    332    NS_WARNING("Unable to figure out the color table size for this bitmap");
    333  }
    334 
    335  return colorTableSize;
    336 }
    337 
    338 // Given a header and a size, creates a freshly allocated BITMAPINFO structure.
    339 // It is the caller's responsibility to null-check and delete the structure.
    340 static BITMAPINFO* CreateBitmapInfo(BITMAPINFOHEADER* aHeader,
    341                                    size_t aColorTableSize) {
    342  BITMAPINFO* bmi = (BITMAPINFO*)::operator new(
    343      sizeof(BITMAPINFOHEADER) + aColorTableSize, mozilla::fallible);
    344  if (bmi) {
    345    memcpy(bmi, aHeader, sizeof(BITMAPINFOHEADER));
    346    memset(bmi->bmiColors, 0, aColorTableSize);
    347  }
    348  return bmi;
    349 }
    350 
    351 static nsresult MakeIconBuffer(HICON aIcon, ByteBuf* aOutBuffer) {
    352  nsresult rv = NS_ERROR_FAILURE;
    353 
    354  if (aIcon) {
    355    // we got a handle to an icon. Now we want to get a bitmap for the icon
    356    // using GetIconInfo....
    357    ICONINFO iconInfo;
    358    if (GetIconInfo(aIcon, &iconInfo)) {
    359      // we got the bitmaps, first find out their size
    360      HDC hDC = CreateCompatibleDC(nullptr);  // get a device context for
    361                                              // the screen.
    362      BITMAPINFOHEADER maskHeader = {sizeof(BITMAPINFOHEADER)};
    363      BITMAPINFOHEADER colorHeader = {sizeof(BITMAPINFOHEADER)};
    364      int colorTableSize, maskTableSize;
    365      if (GetDIBits(hDC, iconInfo.hbmMask, 0, 0, nullptr,
    366                    (BITMAPINFO*)&maskHeader, DIB_RGB_COLORS) &&
    367          GetDIBits(hDC, iconInfo.hbmColor, 0, 0, nullptr,
    368                    (BITMAPINFO*)&colorHeader, DIB_RGB_COLORS) &&
    369          maskHeader.biHeight == colorHeader.biHeight &&
    370          maskHeader.biWidth == colorHeader.biWidth &&
    371          colorHeader.biBitCount > 8 && colorHeader.biSizeImage > 0 &&
    372          colorHeader.biWidth >= 0 && colorHeader.biWidth <= 255 &&
    373          colorHeader.biHeight >= 0 && colorHeader.biHeight <= 255 &&
    374          maskHeader.biSizeImage > 0 &&
    375          (colorTableSize = GetColorTableSize(&colorHeader)) >= 0 &&
    376          (maskTableSize = GetColorTableSize(&maskHeader)) >= 0) {
    377        uint32_t iconSize = sizeof(ICONFILEHEADER) + sizeof(ICONENTRY) +
    378                            sizeof(BITMAPINFOHEADER) + colorHeader.biSizeImage +
    379                            maskHeader.biSizeImage;
    380 
    381        if (!aOutBuffer->Allocate(iconSize)) {
    382          rv = NS_ERROR_OUT_OF_MEMORY;
    383        } else {
    384          uint8_t* whereTo = aOutBuffer->mData;
    385          int howMuch;
    386 
    387          // the data starts with an icon file header
    388          ICONFILEHEADER iconHeader;
    389          iconHeader.ifhReserved = 0;
    390          iconHeader.ifhType = 1;
    391          iconHeader.ifhCount = 1;
    392          howMuch = sizeof(ICONFILEHEADER);
    393          memcpy(whereTo, &iconHeader, howMuch);
    394          whereTo += howMuch;
    395 
    396          // followed by the single icon entry
    397          ICONENTRY iconEntry;
    398          iconEntry.ieWidth = static_cast<int8_t>(colorHeader.biWidth);
    399          iconEntry.ieHeight = static_cast<int8_t>(colorHeader.biHeight);
    400          iconEntry.ieColors = 0;
    401          iconEntry.ieReserved = 0;
    402          iconEntry.iePlanes = 1;
    403          iconEntry.ieBitCount = colorHeader.biBitCount;
    404          iconEntry.ieSizeImage = sizeof(BITMAPINFOHEADER) +
    405                                  colorHeader.biSizeImage +
    406                                  maskHeader.biSizeImage;
    407          iconEntry.ieFileOffset = sizeof(ICONFILEHEADER) + sizeof(ICONENTRY);
    408          howMuch = sizeof(ICONENTRY);
    409          memcpy(whereTo, &iconEntry, howMuch);
    410          whereTo += howMuch;
    411 
    412          // followed by the bitmap info header
    413          // (doubling the height because icons have two bitmaps)
    414          colorHeader.biHeight *= 2;
    415          colorHeader.biSizeImage += maskHeader.biSizeImage;
    416          howMuch = sizeof(BITMAPINFOHEADER);
    417          memcpy(whereTo, &colorHeader, howMuch);
    418          whereTo += howMuch;
    419          colorHeader.biHeight /= 2;
    420          colorHeader.biSizeImage -= maskHeader.biSizeImage;
    421 
    422          // followed by the XOR bitmap data (colorHeader)
    423          // (you'd expect the color table to come here, but it apparently
    424          // doesn't)
    425          BITMAPINFO* colorInfo =
    426              CreateBitmapInfo(&colorHeader, colorTableSize);
    427          if (colorInfo &&
    428              GetDIBits(hDC, iconInfo.hbmColor, 0, colorHeader.biHeight,
    429                        whereTo, colorInfo, DIB_RGB_COLORS)) {
    430            whereTo += colorHeader.biSizeImage;
    431 
    432            // and finally the AND bitmap data (maskHeader)
    433            BITMAPINFO* maskInfo = CreateBitmapInfo(&maskHeader, maskTableSize);
    434            if (maskInfo &&
    435                GetDIBits(hDC, iconInfo.hbmMask, 0, maskHeader.biHeight,
    436                          whereTo, maskInfo, DIB_RGB_COLORS)) {
    437              rv = NS_OK;
    438            }  // if we got bitmap bits
    439            delete maskInfo;
    440          }  // if we got mask bits
    441          delete colorInfo;
    442        }  // if we allocated the buffer
    443      }  // if we got mask size
    444 
    445      DeleteDC(hDC);
    446      DeleteObject(iconInfo.hbmColor);
    447      DeleteObject(iconInfo.hbmMask);
    448    }  // if we got icon info
    449    DestroyIcon(aIcon);
    450  }  // if we got an hIcon
    451 
    452  return rv;
    453 }
    454 
    455 static nsresult GetIconHandleFromURLBlocking(nsIMozIconURI* aUrl,
    456                                             HICON* aIcon) {
    457  nsAutoCString stockIcon;
    458  aUrl->GetStockIcon(stockIcon);
    459  if (!stockIcon.IsEmpty()) {
    460    return GetStockHIcon(aUrl, aIcon);
    461  }
    462 
    463  IconPathInfo iconPathInfo;
    464  nsresult rv = ExtractIconPathInfoFromUrl(aUrl, &iconPathInfo);
    465  NS_ENSURE_SUCCESS(rv, rv);
    466 
    467  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
    468      "GetIconHandleFromURLBlocking",
    469      [&] { rv = GetIconHandleFromPathInfo(iconPathInfo, aIcon); });
    470 
    471  RefPtr<nsIEventTarget> target = DecodePool::Singleton()->GetIOEventTarget();
    472 
    473  nsresult dispatchResult = SyncRunnable::DispatchToThread(target, task);
    474  NS_ENSURE_SUCCESS(dispatchResult, dispatchResult);
    475 
    476  return rv;
    477 }
    478 
    479 static RefPtr<HIconPromise> GetIconHandleFromURLAsync(nsIMozIconURI* aUrl) {
    480  RefPtr<HIconPromise::Private> promise = new HIconPromise::Private(__func__);
    481 
    482  nsAutoCString stockIcon;
    483  aUrl->GetStockIcon(stockIcon);
    484  if (!stockIcon.IsEmpty()) {
    485    HICON hIcon = nullptr;
    486    nsresult rv = GetStockHIcon(aUrl, &hIcon);
    487    if (NS_SUCCEEDED(rv)) {
    488      promise->Resolve(hIcon, __func__);
    489    } else {
    490      promise->Reject(rv, __func__);
    491    }
    492    return promise;
    493  }
    494 
    495  IconPathInfo iconPathInfo;
    496  nsresult rv = ExtractIconPathInfoFromUrl(aUrl, &iconPathInfo);
    497  if (NS_FAILED(rv)) {
    498    promise->Reject(rv, __func__);
    499    return promise;
    500  }
    501 
    502  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
    503      "GetIconHandleFromURLAsync", [iconPathInfo, promise] {
    504        HICON hIcon = nullptr;
    505        nsresult rv = GetIconHandleFromPathInfo(iconPathInfo, &hIcon);
    506        if (NS_SUCCEEDED(rv)) {
    507          promise->Resolve(hIcon, __func__);
    508        } else {
    509          promise->Reject(rv, __func__);
    510        }
    511      });
    512 
    513  RefPtr<nsIEventTarget> target = DecodePool::Singleton()->GetIOEventTarget();
    514 
    515  rv = target->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
    516  if (NS_FAILED(rv)) {
    517    promise->Reject(rv, __func__);
    518  }
    519 
    520  return promise;
    521 }
    522 
    523 static RefPtr<nsIconChannel::ByteBufPromise> GetIconBufferFromURLAsync(
    524    nsIMozIconURI* aUrl) {
    525  RefPtr<nsIconChannel::ByteBufPromise::Private> promise =
    526      new nsIconChannel::ByteBufPromise::Private(__func__);
    527 
    528  GetIconHandleFromURLAsync(aUrl)->Then(
    529      GetCurrentSerialEventTarget(), __func__,
    530      [promise](HICON aIcon) {
    531        ByteBuf iconBuffer;
    532        nsresult rv = MakeIconBuffer(aIcon, &iconBuffer);
    533        if (NS_SUCCEEDED(rv)) {
    534          promise->Resolve(std::move(iconBuffer), __func__);
    535        } else {
    536          promise->Reject(rv, __func__);
    537        }
    538      },
    539      [promise](nsresult rv) { promise->Reject(rv, __func__); });
    540 
    541  return promise;
    542 }
    543 
    544 static nsresult WriteByteBufToOutputStream(const ByteBuf& aBuffer,
    545                                           nsIAsyncOutputStream* aStream) {
    546  uint32_t written = 0;
    547  nsresult rv = aStream->Write(reinterpret_cast<const char*>(aBuffer.mData),
    548                               aBuffer.mLen, &written);
    549  NS_ENSURE_SUCCESS(rv, rv);
    550 
    551  return (written == aBuffer.mLen) ? NS_OK : NS_ERROR_UNEXPECTED;
    552 }
    553 
    554 NS_IMPL_ISUPPORTS(nsIconChannel, nsIChannel, nsIRequest, nsIRequestObserver,
    555                  nsIStreamListener)
    556 
    557 // nsIconChannel methods
    558 nsIconChannel::nsIconChannel() {}
    559 
    560 nsIconChannel::~nsIconChannel() {
    561  if (mLoadInfo) {
    562    NS_ReleaseOnMainThread("nsIconChannel::mLoadInfo", mLoadInfo.forget());
    563  }
    564  if (mLoadGroup) {
    565    NS_ReleaseOnMainThread("nsIconChannel::mLoadGroup", mLoadGroup.forget());
    566  }
    567 }
    568 
    569 nsresult nsIconChannel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo) {
    570  NS_ASSERTION(uri, "no uri");
    571  mUrl = uri;
    572  mOriginalURI = uri;
    573  mLoadInfo = aLoadInfo;
    574  nsresult rv;
    575  mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
    576  return rv;
    577 }
    578 
    579 ////////////////////////////////////////////////////////////////////////////////
    580 // nsIRequest methods:
    581 
    582 NS_IMETHODIMP
    583 nsIconChannel::GetName(nsACString& result) { return mUrl->GetSpec(result); }
    584 
    585 NS_IMETHODIMP
    586 nsIconChannel::IsPending(bool* result) { return mPump->IsPending(result); }
    587 
    588 NS_IMETHODIMP
    589 nsIconChannel::GetStatus(nsresult* status) { return mPump->GetStatus(status); }
    590 
    591 NS_IMETHODIMP nsIconChannel::SetCanceledReason(const nsACString& aReason) {
    592  return SetCanceledReasonImpl(aReason);
    593 }
    594 
    595 NS_IMETHODIMP nsIconChannel::GetCanceledReason(nsACString& aReason) {
    596  return GetCanceledReasonImpl(aReason);
    597 }
    598 
    599 NS_IMETHODIMP nsIconChannel::CancelWithReason(nsresult aStatus,
    600                                              const nsACString& aReason) {
    601  return CancelWithReasonImpl(aStatus, aReason);
    602 }
    603 
    604 NS_IMETHODIMP
    605 nsIconChannel::Cancel(nsresult status) {
    606  mCanceled = true;
    607  return mPump->Cancel(status);
    608 }
    609 
    610 NS_IMETHODIMP
    611 nsIconChannel::GetCanceled(bool* result) {
    612  *result = mCanceled;
    613  return NS_OK;
    614 }
    615 
    616 NS_IMETHODIMP
    617 nsIconChannel::Suspend(void) { return mPump->Suspend(); }
    618 
    619 NS_IMETHODIMP
    620 nsIconChannel::Resume(void) { return mPump->Resume(); }
    621 NS_IMETHODIMP
    622 nsIconChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    623  *aLoadGroup = mLoadGroup;
    624  NS_IF_ADDREF(*aLoadGroup);
    625  return NS_OK;
    626 }
    627 
    628 NS_IMETHODIMP
    629 nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
    630  mLoadGroup = aLoadGroup;
    631  return NS_OK;
    632 }
    633 
    634 NS_IMETHODIMP
    635 nsIconChannel::GetLoadFlags(uint32_t* aLoadAttributes) {
    636  return mPump->GetLoadFlags(aLoadAttributes);
    637 }
    638 
    639 NS_IMETHODIMP
    640 nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes) {
    641  return mPump->SetLoadFlags(aLoadAttributes);
    642 }
    643 
    644 NS_IMETHODIMP
    645 nsIconChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    646  return GetTRRModeImpl(aTRRMode);
    647 }
    648 
    649 NS_IMETHODIMP
    650 nsIconChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    651  return SetTRRModeImpl(aTRRMode);
    652 }
    653 
    654 NS_IMETHODIMP
    655 nsIconChannel::GetIsDocument(bool* aIsDocument) {
    656  return NS_GetIsDocumentChannel(this, aIsDocument);
    657 }
    658 
    659 ////////////////////////////////////////////////////////////////////////////////
    660 // nsIChannel methods:
    661 
    662 NS_IMETHODIMP
    663 nsIconChannel::GetOriginalURI(nsIURI** aURI) {
    664  *aURI = mOriginalURI;
    665  NS_ADDREF(*aURI);
    666  return NS_OK;
    667 }
    668 
    669 NS_IMETHODIMP
    670 nsIconChannel::SetOriginalURI(nsIURI* aURI) {
    671  NS_ENSURE_ARG_POINTER(aURI);
    672  mOriginalURI = aURI;
    673  return NS_OK;
    674 }
    675 
    676 NS_IMETHODIMP
    677 nsIconChannel::GetURI(nsIURI** aURI) {
    678  *aURI = mUrl;
    679  NS_IF_ADDREF(*aURI);
    680  return NS_OK;
    681 }
    682 
    683 // static
    684 RefPtr<nsIconChannel::ByteBufPromise> nsIconChannel::GetIconAsync(
    685    nsIURI* aURI) {
    686  MOZ_ASSERT(XRE_IsParentProcess());
    687 
    688  nsresult rv = NS_OK;
    689  nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(aURI, &rv));
    690  if (NS_FAILED(rv)) {
    691    return ByteBufPromise::CreateAndReject(rv, __func__);
    692  }
    693 
    694  return GetIconBufferFromURLAsync(iconURI);
    695 }
    696 
    697 NS_IMETHODIMP
    698 nsIconChannel::Open(nsIInputStream** aStream) {
    699  nsCOMPtr<nsIStreamListener> listener;
    700  nsresult rv =
    701      nsContentSecurityManager::doContentSecurityCheck(this, listener);
    702  NS_ENSURE_SUCCESS(rv, rv);
    703 
    704  MOZ_ASSERT(
    705      mLoadInfo->GetSecurityMode() == 0 ||
    706          mLoadInfo->GetInitialSecurityCheckDone() ||
    707          (mLoadInfo->GetSecurityMode() ==
    708               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
    709           mLoadInfo->GetLoadingPrincipal() &&
    710           mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
    711      "security flags in loadInfo but doContentSecurityCheck() not called");
    712 
    713  // Double-check that we are actually an icon URL
    714  nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
    715  NS_ENSURE_SUCCESS(rv, rv);
    716 
    717  // Get the handle for the given icon URI. This may involve the decode I/O
    718  // thread, as we can only call SHGetFileInfo() from that thread
    719  //
    720  // Since this API is synchronous, this call will not return until the decode
    721  // I/O thread returns with the icon handle
    722  //
    723  // Once we have the handle, we create a Windows ICO buffer with it and
    724  // dump the buffer into the output end of the pipe. The input end will
    725  // be returned to the caller
    726  HICON hIcon = nullptr;
    727  rv = GetIconHandleFromURLBlocking(iconURI, &hIcon);
    728  NS_ENSURE_SUCCESS(rv, rv);
    729 
    730  ByteBuf iconBuffer;
    731  rv = MakeIconBuffer(hIcon, &iconBuffer);
    732  NS_ENSURE_SUCCESS(rv, rv);
    733 
    734  // Create the asynchronous pipe with a blocking read end
    735  nsCOMPtr<nsIAsyncInputStream> inputStream;
    736  nsCOMPtr<nsIAsyncOutputStream> outputStream;
    737  NS_NewPipe2(getter_AddRefs(inputStream), getter_AddRefs(outputStream),
    738              false /*nonBlockingInput*/, false /*nonBlockingOutput*/,
    739              iconBuffer.mLen /*segmentSize*/, 1 /*segmentCount*/);
    740 
    741  rv = WriteByteBufToOutputStream(iconBuffer, outputStream);
    742 
    743  if (NS_SUCCEEDED(rv)) {
    744    inputStream.forget(aStream);
    745  }
    746 
    747  return rv;
    748 }
    749 
    750 NS_IMETHODIMP
    751 nsIconChannel::AsyncOpen(nsIStreamListener* aListener) {
    752  nsCOMPtr<nsIStreamListener> listener = aListener;
    753  nsresult rv =
    754      nsContentSecurityManager::doContentSecurityCheck(this, listener);
    755  if (NS_FAILED(rv)) {
    756    mCallbacks = nullptr;
    757    return rv;
    758  }
    759 
    760  MOZ_ASSERT(
    761      mLoadInfo->GetSecurityMode() == 0 ||
    762          mLoadInfo->GetInitialSecurityCheckDone() ||
    763          (mLoadInfo->GetSecurityMode() ==
    764               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
    765           mLoadInfo->GetLoadingPrincipal() &&
    766           mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
    767      "security flags in loadInfo but doContentSecurityCheck() not called");
    768 
    769  mListener = listener;
    770 
    771  rv = StartAsyncOpen();
    772  if (NS_FAILED(rv)) {
    773    mListener = nullptr;
    774    mCallbacks = nullptr;
    775    return rv;
    776  }
    777 
    778  // Add ourself to the load group, if available
    779  if (mLoadGroup) {
    780    mLoadGroup->AddRequest(this, nullptr);
    781  }
    782 
    783  return NS_OK;
    784 }
    785 
    786 nsresult nsIconChannel::StartAsyncOpen() {
    787  // Double-check that we are actually an icon URL
    788  nsresult rv = NS_OK;
    789  nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
    790  NS_ENSURE_SUCCESS(rv, rv);
    791 
    792  // Create the asynchronous pipe with a non-blocking read end
    793  nsCOMPtr<nsIAsyncInputStream> inputStream;
    794  nsCOMPtr<nsIAsyncOutputStream> outputStream;
    795  NS_NewPipe2(getter_AddRefs(inputStream), getter_AddRefs(outputStream),
    796              true /*nonBlockingInput*/, false /*nonBlockingOutput*/,
    797              0 /*segmentSize*/, UINT32_MAX /*segmentCount*/);
    798 
    799  // If we are in content, we asynchronously request the ICO buffer from
    800  // the parent process because the APIs to load icons don't work with
    801  // Win32k Lockdown
    802  using ContentChild = mozilla::dom::ContentChild;
    803  if (auto* contentChild = ContentChild::GetSingleton()) {
    804    RefPtr<ContentChild::GetSystemIconPromise> iconPromise =
    805        contentChild->SendGetSystemIcon(mUrl);
    806    if (!iconPromise) {
    807      return NS_ERROR_UNEXPECTED;
    808    }
    809 
    810    iconPromise->Then(
    811        mozilla::GetCurrentSerialEventTarget(), __func__,
    812        [outputStream](std::tuple<nsresult, mozilla::Maybe<ByteBuf>>&& aArg) {
    813          nsresult rv = std::get<0>(aArg);
    814          mozilla::Maybe<ByteBuf> iconBuffer = std::move(std::get<1>(aArg));
    815 
    816          if (NS_SUCCEEDED(rv)) {
    817            MOZ_RELEASE_ASSERT(iconBuffer);
    818            rv = WriteByteBufToOutputStream(*iconBuffer, outputStream);
    819          }
    820 
    821          outputStream->CloseWithStatus(rv);
    822        },
    823        [outputStream](mozilla::ipc::ResponseRejectReason) {
    824          outputStream->CloseWithStatus(NS_ERROR_FAILURE);
    825        });
    826  } else {
    827    // Get the handle for the given icon URI. This may involve the decode I/O
    828    // thread, as we can only call SHGetFileInfo() from that thread
    829    //
    830    // Once we have the handle, we create a Windows ICO buffer with it and
    831    // dump the buffer into the output end of the pipe. The input end will be
    832    // pumped to our attached nsIStreamListener
    833    GetIconBufferFromURLAsync(iconURI)->Then(
    834        GetCurrentSerialEventTarget(), __func__,
    835        [outputStream](ByteBuf aIconBuffer) {
    836          nsresult rv =
    837              WriteByteBufToOutputStream(std::move(aIconBuffer), outputStream);
    838          outputStream->CloseWithStatus(rv);
    839        },
    840        [outputStream](nsresult rv) { outputStream->CloseWithStatus(rv); });
    841  }
    842 
    843  rv = mPump->Init(inputStream.get(), 0 /*segmentSize*/, 0 /*segmentCount*/,
    844                   false /*closeWhenDone*/, GetMainThreadSerialEventTarget());
    845  NS_ENSURE_SUCCESS(rv, rv);
    846 
    847  return mPump->AsyncRead(this);
    848 }
    849 
    850 NS_IMETHODIMP
    851 nsIconChannel::GetContentType(nsACString& aContentType) {
    852  aContentType.AssignLiteral(IMAGE_ICO);
    853  return NS_OK;
    854 }
    855 
    856 NS_IMETHODIMP
    857 nsIconChannel::SetContentType(const nsACString& aContentType) {
    858  // It doesn't make sense to set the content-type on this type
    859  // of channel...
    860  return NS_ERROR_FAILURE;
    861 }
    862 
    863 NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString& aContentCharset) {
    864  aContentCharset.Truncate();
    865  return NS_OK;
    866 }
    867 
    868 NS_IMETHODIMP
    869 nsIconChannel::SetContentCharset(const nsACString& aContentCharset) {
    870  // It doesn't make sense to set the content-charset on this type
    871  // of channel...
    872  return NS_ERROR_FAILURE;
    873 }
    874 
    875 NS_IMETHODIMP
    876 nsIconChannel::GetContentDisposition(uint32_t* aContentDisposition) {
    877  return NS_ERROR_NOT_AVAILABLE;
    878 }
    879 
    880 NS_IMETHODIMP
    881 nsIconChannel::SetContentDisposition(uint32_t aContentDisposition) {
    882  return NS_ERROR_NOT_AVAILABLE;
    883 }
    884 
    885 NS_IMETHODIMP
    886 nsIconChannel::GetContentDispositionFilename(
    887    nsAString& aContentDispositionFilename) {
    888  return NS_ERROR_NOT_AVAILABLE;
    889 }
    890 
    891 NS_IMETHODIMP
    892 nsIconChannel::SetContentDispositionFilename(
    893    const nsAString& aContentDispositionFilename) {
    894  return NS_ERROR_NOT_AVAILABLE;
    895 }
    896 
    897 NS_IMETHODIMP
    898 nsIconChannel::GetContentDispositionHeader(
    899    nsACString& aContentDispositionHeader) {
    900  return NS_ERROR_NOT_AVAILABLE;
    901 }
    902 
    903 NS_IMETHODIMP
    904 nsIconChannel::GetContentLength(int64_t* aContentLength) {
    905  *aContentLength = 0;
    906  return NS_ERROR_FAILURE;
    907 }
    908 
    909 NS_IMETHODIMP
    910 nsIconChannel::SetContentLength(int64_t aContentLength) {
    911  MOZ_ASSERT_UNREACHABLE("nsIconChannel::SetContentLength");
    912  return NS_ERROR_NOT_IMPLEMENTED;
    913 }
    914 
    915 NS_IMETHODIMP
    916 nsIconChannel::GetOwner(nsISupports** aOwner) {
    917  *aOwner = mOwner.get();
    918  NS_IF_ADDREF(*aOwner);
    919  return NS_OK;
    920 }
    921 
    922 NS_IMETHODIMP
    923 nsIconChannel::SetOwner(nsISupports* aOwner) {
    924  mOwner = aOwner;
    925  return NS_OK;
    926 }
    927 
    928 NS_IMETHODIMP
    929 nsIconChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
    930  NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
    931  return NS_OK;
    932 }
    933 
    934 NS_IMETHODIMP
    935 nsIconChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
    936  MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
    937  mLoadInfo = aLoadInfo;
    938  return NS_OK;
    939 }
    940 
    941 NS_IMETHODIMP
    942 nsIconChannel::GetNotificationCallbacks(
    943    nsIInterfaceRequestor** aNotificationCallbacks) {
    944  *aNotificationCallbacks = mCallbacks.get();
    945  NS_IF_ADDREF(*aNotificationCallbacks);
    946  return NS_OK;
    947 }
    948 
    949 NS_IMETHODIMP
    950 nsIconChannel::SetNotificationCallbacks(
    951    nsIInterfaceRequestor* aNotificationCallbacks) {
    952  mCallbacks = aNotificationCallbacks;
    953  return NS_OK;
    954 }
    955 
    956 NS_IMETHODIMP
    957 nsIconChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
    958  *aSecurityInfo = nullptr;
    959  return NS_OK;
    960 }
    961 
    962 // nsIRequestObserver methods
    963 NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest) {
    964  if (mListener) {
    965    return mListener->OnStartRequest(this);
    966  }
    967  return NS_OK;
    968 }
    969 
    970 NS_IMETHODIMP
    971 nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
    972  if (mListener) {
    973    mListener->OnStopRequest(this, aStatus);
    974    mListener = nullptr;
    975  }
    976 
    977  // Remove from load group
    978  if (mLoadGroup) {
    979    mLoadGroup->RemoveRequest(this, nullptr, aStatus);
    980  }
    981 
    982  // Drop notification callbacks to prevent cycles.
    983  mCallbacks = nullptr;
    984 
    985  return NS_OK;
    986 }
    987 
    988 // nsIStreamListener methods
    989 NS_IMETHODIMP
    990 nsIconChannel::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
    991                               uint64_t aOffset, uint32_t aCount) {
    992  if (mListener) {
    993    return mListener->OnDataAvailable(this, aStream, aOffset, aCount);
    994  }
    995  return NS_OK;
    996 }