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