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 }