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