| ... | ... | @@ -320,7 +320,11 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request, | 
| 320 | 320 |    return NS_OK;
 | 
| 321 | 321 |  }
 | 
| 322 | 322 |  
 | 
| 323 |  | -static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) {
 | 
|  | 323 | +static bool IsContentPDF(
 | 
|  | 324 | +    nsIChannel* aChannel, const nsACString& aContentType,
 | 
|  | 325 | +    nsAutoCString* aOutExt =
 | 
|  | 326 | +        nullptr  // best-guess file extension, useful for non-PDFs
 | 
|  | 327 | +) {
 | 
| 324 | 328 |    bool isPDF = aContentType.LowerCaseEqualsASCII(APPLICATION_PDF);
 | 
| 325 | 329 |    if (!isPDF && (aContentType.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM) ||
 | 
| 326 | 330 |                   aContentType.IsEmpty())) {
 | 
| ... | ... | @@ -328,14 +332,25 @@ static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) { | 
| 328 | 332 |      aChannel->GetContentDispositionFilename(flname);
 | 
| 329 | 333 |      isPDF = StringEndsWith(flname, u".pdf"_ns);
 | 
| 330 | 334 |      if (!isPDF) {
 | 
|  | 335 | +      nsAutoCString ext;
 | 
| 331 | 336 |        nsCOMPtr<nsIURI> uri;
 | 
| 332 | 337 |        aChannel->GetURI(getter_AddRefs(uri));
 | 
| 333 | 338 |        nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
 | 
| 334 | 339 |        if (url) {
 | 
| 335 |  | -        nsAutoCString ext;
 | 
| 336 | 340 |          url->GetFileExtension(ext);
 | 
| 337 | 341 |          isPDF = ext.EqualsLiteral("pdf");
 | 
| 338 | 342 |        }
 | 
|  | 343 | +      if (aOutExt) {
 | 
|  | 344 | +        // Fill the extension out param if required
 | 
|  | 345 | +        if (!(isPDF || flname.IsEmpty())) {
 | 
|  | 346 | +          // For non PDFs, fallback to filename from content disposition
 | 
|  | 347 | +          int32_t extStart = flname.RFindChar(u'.');
 | 
|  | 348 | +          if (extStart != kNotFound) {
 | 
|  | 349 | +            CopyUTF16toUTF8(Substring(flname, extStart + 1), ext);
 | 
|  | 350 | +          }
 | 
|  | 351 | +        }
 | 
|  | 352 | +        *aOutExt = ext;
 | 
|  | 353 | +      }
 | 
| 339 | 354 |      }
 | 
| 340 | 355 |    }
 | 
| 341 | 356 |  
 | 
| ... | ... | @@ -343,7 +358,7 @@ static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) { | 
| 343 | 358 |  }
 | 
| 344 | 359 |  
 | 
| 345 | 360 |  static mozilla::Result<bool, nsresult> ShouldHandleExternally(
 | 
| 346 |  | -    const nsACString& aMimeType) {
 | 
|  | 361 | +    const nsACString& aMimeType, const nsACString& aExtension) {
 | 
| 347 | 362 |    // For a PDF, check if the preference is set that forces attachments to be
 | 
| 348 | 363 |    // opened inline. If so, treat it as a non-attachment by clearing
 | 
| 349 | 364 |    // 'forceExternalHandling' again. This allows it open a PDF directly
 | 
| ... | ... | @@ -356,7 +371,7 @@ static mozilla::Result<bool, nsresult> ShouldHandleExternally( | 
| 356 | 371 |      return mozilla::Err(NS_ERROR_FAILURE);
 | 
| 357 | 372 |    }
 | 
| 358 | 373 |  
 | 
| 359 |  | -  mimeSvc->GetFromTypeAndExtension(aMimeType, EmptyCString(),
 | 
|  | 374 | +  mimeSvc->GetFromTypeAndExtension(aMimeType, aExtension,
 | 
| 360 | 375 |                                     getter_AddRefs(mimeInfo));
 | 
| 361 | 376 |  
 | 
| 362 | 377 |    if (mimeInfo) {
 | 
| ... | ... | @@ -430,31 +445,43 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) { | 
| 430 | 445 |      forceExternalHandling = false;
 | 
| 431 | 446 |    }
 | 
| 432 | 447 |  
 | 
|  | 448 | +  nsAutoCString ext;
 | 
|  | 449 | +  bool isPDF =
 | 
|  | 450 | +      forceExternalHandling && IsContentPDF(aChannel, mContentType, &ext);
 | 
|  | 451 | +
 | 
| 433 | 452 |    bool maybeForceInternalHandling =
 | 
| 434 |  | -      forceExternalHandling &&
 | 
| 435 |  | -      (mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline() ||
 | 
| 436 |  | -       mozilla::StaticPrefs::browser_download_ignore_content_disposition());
 | 
|  | 453 | +      (isPDF &&
 | 
|  | 454 | +        mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline()) ||
 | 
|  | 455 | +       (
 | 
|  | 456 | +        forceExternalHandling &&
 | 
|  | 457 | +        mozilla::StaticPrefs::browser_download_ignore_content_disposition() &&
 | 
|  | 458 | +        // we want to exclude html and svg files, which could execute
 | 
|  | 459 | +        // scripts (tor-browser#43211)
 | 
|  | 460 | +        kNotFound == mContentType.LowerCaseFindASCII("html") &&
 | 
|  | 461 | +        kNotFound == ext.LowerCaseFindASCII("htm") &&
 | 
|  | 462 | +        kNotFound == mContentType.LowerCaseFindASCII("/svg+") &&
 | 
|  | 463 | +        !ext.EqualsIgnoreCase("svg"));
 | 
| 437 | 464 |  
 | 
| 438 | 465 |    // Check if this is a PDF which should be opened internally. We also handle
 | 
| 439 | 466 |    // octet-streams that look like they might be PDFs based on their extension.
 | 
|  | 467 | +  // Additionally, we try to avoid downloading also non-PDF attachments
 | 
|  | 468 | +  // when the general Content-Disposition override preference is set to true.
 | 
| 440 | 469 |    if (maybeForceInternalHandling) {
 | 
| 441 |  | -    // For a PDF, check if the preference is set that forces attachments to be
 | 
| 442 |  | -    // opened inline. If so, treat it as a non-attachment by clearing
 | 
|  | 470 | +    // Preferences are set to open attachments inline by clearing
 | 
| 443 | 471 |      // 'forceExternalHandling' again. This allows it open a PDF directly
 | 
| 444 | 472 |      // instead of downloading it first. It may still end up being handled by
 | 
| 445 | 473 |      // a helper app depending anyway on the later checks.
 | 
| 446 |  | -    nsCString mimeType = IsContentPDF(aChannel, mContentType)
 | 
| 447 |  | -                             ? nsLiteralCString(APPLICATION_PDF)
 | 
| 448 |  | -                             : mContentType;
 | 
| 449 |  | -    auto result = ShouldHandleExternally(mimeType);
 | 
|  | 474 | +    // This may apply to other file types if an internal handler exists.
 | 
|  | 475 | +    auto result = ShouldHandleExternally(
 | 
|  | 476 | +        isPDF ? nsLiteralCString(APPLICATION_PDF) : mContentType, ext);
 | 
| 450 | 477 |      if (result.isErr()) {
 | 
| 451 | 478 |        return result.unwrapErr();
 | 
| 452 | 479 |      }
 | 
| 453 | 480 |      forceExternalHandling = result.unwrap();
 | 
| 454 | 481 |  
 | 
| 455 |  | -    // If we're not opening the PDF externally we block it if it's sandboxed.
 | 
|  | 482 | +    // If we're not opening the file externally and it's sandboxed we block it.
 | 
| 456 | 483 |      if (IsSandboxed(aChannel) && !forceExternalHandling) {
 | 
| 457 |  | -      LOG(("Blocked sandboxed PDF"));
 | 
|  | 484 | +      LOG(("Blocked sandboxed file"));
 | 
| 458 | 485 |        nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
 | 
| 459 | 486 |        if (httpChannel) {
 | 
| 460 | 487 |          nsContentSecurityUtils::LogMessageToConsole(
 |