CVE-2025-6430
Overview
Background
- `Content-Disposition: attachment`
- An HTTP response directive telling the browser to download (not render) the resource. Honoring it for subresources prevents active content (HTML/SVG) from executing in the site’s origin.
- `<object>` / `<embed>`
- Elements that load a subresource and may render some MIME types internally as a document (TYPE_DOCUMENT) or fall back (TYPE_FALLBACK).
Root Cause Analysis
Previously, object/embed loads were given the DONT_RETARGET flag, and DispatchContent only consulted the Content-Disposition header when DONT_RETARGET was not set: if (!(mFlags & DONT_RETARGET)) { ... if (disposition == DISPOSITION_ATTACHMENT) forceExternalHandling = true; }. Because object/embed loads always had DONT_RETARGET, the attachment check was skipped entirely, so a subresource served with Content-Disposition: attachment and loaded via <object>/<embed> was displayed inline as a document regardless. For HTML or SVG payloads that means active, scriptable content was rendered in the embedding context instead of being treated as a download — a cross-site-scripting vector, since the server’s explicit “download, do not render” instruction was ignored.
The patch decouples disposition handling from retargeting. It unconditionally reads the disposition (forceExternalHandling = NS_SUCCEEDED(rv) && disposition == DISPOSITION_ATTACHMENT), so attachment is now respected for object/embed loads too. It then adds a narrow carve-out: only when the load is an object/embed (mFlags & IS_OBJECT_EMBED) and the type is an image (imgLoader::SupportImageWithMimeType) or a PDF (IsContentPDF) is forceExternalHandling cleared, preserving the intended inline behavior for those safe, non-scripting types. nsDocShell now sets the new IS_OBJECT_EMBED flag for object/embed loads (GetDocShellFlags and OpenRedirectedChannel, the latter keyed on ExtContentPolicy::TYPE_OBJECT). The rewritten browser_object_attachment.js codifies the new matrix: SVG and HTML attachments now fail to load inline (and are downloaded when retargeting is allowed), while PDFs/images still display inline. The advisory (CWE-79, XSS) and the diff agree: the root cause was unconditionally ignoring Content-Disposition: attachment for object/embed subresources, allowing scriptable documents to render.
DONT_RETARGET, and the code only honored Content-Disposition: attachment when DONT_RETARGET was absent, so attachment was silently ignored for every object/embed subresource — letting an attachment-marked HTML/SVG render and execute script; the fix honors attachment for object/embed and only exempts non-scripting image/PDF types.Attack Path
- Host payload
Attacker serves an HTML or SVG resource with
Content-Disposition: attachmentfrom a domain, expecting the directive to prevent rendering. (web-reachable) - Embed it
A victim site (or attacker-influenced content) loads that resource via
<object>or<embed>. - Disposition ignored
Because the object/embed load has
DONT_RETARGET,DispatchContentskips the attachment check and displays the content inline as TYPE_DOCUMENT. - Script executes The HTML/SVG renders as an active document, running attacker script in the embedding context rather than being downloaded.
- Primitive Cross-site scripting: script execution in a context the attachment directive was meant to prevent (scope-changed, CVSS 6.1, confidentiality/integrity low).
Impact Assessment
<object>/<embed> (UI:R, scope change S:C). The gained capability is XSS — execution of attacker-controlled HTML/SVG in a rendering context despite the server marking it as a download attachment — undermining a defense sites rely on when serving user-uploaded or untrusted files with attachment disposition. It is not memory corruption; impact is script execution bounded by the embedding context. PDFs and images remain intentionally inline and are not affected.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdocshell/base/nsDocShell.cpp |
modified | |
add_taskdom/base/test/browser_object_attachment.js |
modified | |
ifdom/base/test/browser_object_attachment.js |
modified | |
onDownloadAddeddom/base/test/browser_object_attachment.js |
modified |
Files Changed
docshell/base/nsDocShell.cppdom/base/test/browser.tomldom/base/test/browser_object_attachment.jsdom/base/test/file_html_attachment.htmldom/base/test/file_html_attachment.html^headers^dom/base/test/file_html_object_attachment.htmldom/base/test/file_svg_attachment.svgdom/base/test/file_svg_attachment.svg^headers^dom/base/test/file_svg_object_attachment.htmluriloader/base/nsIURILoader.idluriloader/base/nsURILoader.cpp
Audit Directions
- Other `DONT_RETARGET` consumersReview every site that couples disposition handling to
DONT_RETARGET; the same conflation could hide attachment semantics elsewhere. - Inline-type allowlistAudit
imgLoader::SupportImageWithMimeType/IsContentPDFcoverage so no scriptable type (e.g. SVG served as an image MIME) slips into the internal-handling carve-out. - Object/embed type dispatchCheck
nsObjectLoadingContenttype resolution for other paths where attachment-marked documents could still be treated as TYPE_DOCUMENT. - Flag propagation on redirectsVerify
IS_OBJECT_EMBEDis set on all object/embed load entry points, not onlyTYPE_OBJECTredirected channels.
Patch
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 945e2080c5a..8875bfe59d8 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10938,11 +10938,14 @@ static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
openFlags |= nsIURILoader::DONT_RETARGET;
}
- // Unless the pref is set, object/embed loads always specify DONT_RETARGET.
- // See bug 1868001 for details.
- if (!aIsDocumentLoad &&
- !StaticPrefs::dom_navigation_object_embed_allow_retargeting()) {
- openFlags |= nsIURILoader::DONT_RETARGET;
+ if (!aIsDocumentLoad) {
+ openFlags |= nsIURILoader::IS_OBJECT_EMBED;
+
+ // Unless the pref is set, object/embed loads always specify DONT_RETARGET.
+ // See bug 1868001 for details.
+ if (!StaticPrefs::dom_navigation_object_embed_allow_retargeting()) {
+ openFlags |= nsIURILoader::DONT_RETARGET;
+ }
}
return openFlags;
@@ -11033,8 +11036,14 @@ nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
// ClientInfo, so we just need to allocate a corresponding ClientSource.
CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget());
+ uint32_t documentOpenInfoFlags = nsIURILoader::DONT_RETARGET;
+ if (loadInfo->GetExternalContentPolicyType() ==
+ ExtContentPolicy::TYPE_OBJECT) {
+ documentOpenInfoFlags |= nsIURILoader::IS_OBJECT_EMBED;
+ }
+
RefPtr<nsDocumentOpenInfo> loader =
- new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
+ new nsDocumentOpenInfo(this, documentOpenInfoFlags, nullptr);
channel->SetLoadGroup(mLoadGroup);
MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
diff --git a/uriloader/base/nsIURILoader.idl b/uriloader/base/nsIURILoader.idl
index 527092c5a64..2164cc8e0b4 100644
--- a/uriloader/base/nsIURILoader.idl
+++ b/uriloader/base/nsIURILoader.idl
@@ -53,6 +53,12 @@ interface nsIURILoader : nsISupports
* be indicated.
*/
const unsigned long DONT_RETARGET = 1 << 1;
+ /**
+ * If this flag is set, the navigation is for an object or embed element,
+ * which may handle some content types internally, even if an attachment
+ * content disposition is specified.
+ */
+ const unsigned long IS_OBJECT_EMBED = 1 << 2;
/* @} */
/**
diff --git a/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp
index 043c0cc25b0..c4c93baae07 100644
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -49,6 +49,7 @@
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_general.h"
#include "nsContentUtils.h"
+#include "imgLoader.h"
mozilla::LazyLogModule nsURILoader::mLog("URILoader");
@@ -409,26 +410,26 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) {
// could happen because the Content-Disposition header is set so, or, in the
// future, because the user has specified external handling for the MIME
// type.
- //
- // If we're not going to be able to retarget to an external handler, ignore
- // content-disposition, and unconditionally try to display the content.
- // This is used for object/embed tags, which expect to display subresources
- // marked with an attachment disposition.
- bool forceExternalHandling = false;
- if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
- uint32_t disposition;
- rv = aChannel->GetContentDisposition(&disposition);
-
- if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT) {
- forceExternalHandling = true;
- }
- }
+ uint32_t disposition;
+ rv = aChannel->GetContentDisposition(&disposition);
+
+ bool forceExternalHandling =
+ NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT;
LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
LOG((" IsSandboxed: %s", IsSandboxed(aChannel) ? "yes" : "no"));
LOG((" IsContentPDF: %s",
IsContentPDF(aChannel, mContentType) ? "yes" : "no"));
+ // Ignore the Content-Disposition header if we're loading a PDF or Image
+ // subresource within an object/embed element.
+ if (forceExternalHandling && (mFlags & nsIURILoader::IS_OBJECT_EMBED) &&
+ (imgLoader::SupportImageWithMimeType(mContentType) ||
+ IsContentPDF(aChannel, mContentType))) {
+ LOG(("Handling pdf/image MIME internally for object/embed element"));
+ forceExternalHandling = false;
+ }
+
bool maybeForceInternalHandling =
forceExternalHandling &&
mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline();
diff --git a/dom/base/test/browser.toml b/dom/base/test/browser.toml
index 89d4183bbd2..799fd8b1a81 100644
--- a/dom/base/test/browser.toml
+++ b/dom/base/test/browser.toml
@@ -123,6 +123,12 @@ support-files = [
"file_pdf_object_attachment.html",
"file_pdf_attachment.pdf",
"file_pdf_attachment.pdf^headers^",
+ "file_svg_object_attachment.html",
+ "file_svg_attachment.svg",
+ "file_svg_attachment.svg^headers^",
+ "file_html_object_attachment.html",
+ "file_html_attachment.html",
+ "file_html_attachment.html^headers^",
]
["browser_outline_refocus.js"]
diff --git a/dom/base/test/browser_object_attachment.js b/dom/base/test/browser_object_attachment.js
index b4432862f0a..851fffae565 100644
--- a/dom/base/test/browser_object_attachment.js
+++ b/dom/base/test/browser_object_attachment.js
@@ -7,29 +7,75 @@ const httpsTestRoot = getRootDirectory(gTestPath).replace(
"https://example.com"
);
-add_task(async function test_pdf_object_attachment() {
- await SpecialPowers.pushPrefEnv({
- set: [["dom.navigation.object_embed.allow_retargeting", false]],
- });
+async function loadAndCheck(file, displayInline, downloadFile = null) {
+ // Get the downloads list and add a view to listen for a download to be added.
+ // We do this even if we aren't going to download anything, so we notice if a
+ // download is started.
+ let download;
+ let downloadList = await Downloads.getList(Downloads.ALL);
+ let downloadView = {
+ async onDownloadAdded(aDownload) {
+ info("download added");
+ ok(downloadFile, "Should be expecting a download");
+ download = aDownload;
+
+ // Clean up the download from the list
+ downloadList.remove(aDownload);
+ await aDownload.finalize(true);
+ },
+ };
+ await downloadList.addView(downloadView);
+ // Open the new URL and perform the load.
await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_pdf_object_attachment.html`,
+ `${httpsTestRoot}/${file}`,
async browser => {
is(
browser.browsingContext.children.length,
- 1,
- "Should have a child frame"
+ displayInline ? 1 : 0,
+ `Should ${displayInline ? "not " : ""}have a child frame`
+ );
+
+ await SpecialPowers.spawn(
+ browser,
+ [displayInline],
+ async displayInline => {
+ let obj = content.document.querySelector("object");
+ is(
+ obj.displayedType,
+ displayInline
+ ? Ci.nsIObjectLoadingContent.TYPE_DOCUMENT
+ : Ci.nsIObjectLoadingContent.TYPE_FALLBACK,
+ `should be displaying TYPE_${displayInline ? "DOCUMENT" : "FALLBACK"}`
+ );
+ }
);
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_DOCUMENT,
- "should be displaying TYPE_DOCUMENT"
- );
- });
}
);
+
+ // Clean up our download observer.
+ await downloadList.removeView(downloadView);
+ if (downloadFile) {
+ is(
+ download.source.url,
+ `${httpsTestRoot}/${downloadFile}`,
+ "Download has the correct source"
+ );
+ } else {
+ is(download, undefined, "Should not have seen a download");
+ }
+}
+
+add_task(async function test_pdf_object_attachment() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.navigation.object_embed.allow_retargeting", false],
+ ["browser.download.open_pdf_attachments_inline", false],
+ ],
+ });
+
+ // PDF attachment should display inline.
+ await loadAndCheck("file_pdf_object_attachment.html", true);
});
add_task(async function test_img_object_attachment() {
@@ -37,132 +83,71 @@ add_task(async function test_img_object_attachment() {
set: [["dom.navigation.object_embed.allow_retargeting", false]],
});
- await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_img_object_attachment.html`,
- async browser => {
- is(
- browser.browsingContext.children.length,
- 1,
- "Should have a child frame"
- );
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_DOCUMENT,
- "should be displaying TYPE_DOCUMENT"
- );
- });
- }
- );
+ // Image attachment should display inline.
+ await loadAndCheck("file_img_object_attachment.html", true);
});
-async function waitForDownload() {
- // Get the downloads list and add a view to listen for a download to be added.
- let downloadList = await Downloads.getList(Downloads.ALL);
+add_task(async function test_svg_object_attachment() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.navigation.object_embed.allow_retargeting", false]],
+ });
- // Wait for a single download
- let downloadView;
- let finishedAllDownloads = new Promise(resolve => {
- downloadView = {
- onDownloadAdded(aDownload) {
- info("download added");
- resolve(aDownload);
- },
- };
+ // SVG attachment should fail to load.
+ await loadAndCheck("file_svg_object_attachment.html", false);
+});
+
+add_task(async function test_html_object_attachment() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.navigation.object_embed.allow_retargeting", false]],
});
- await downloadList.addView(downloadView);
- let download = await finishedAllDownloads;
- await downloadList.removeView(downloadView);
- // Clean up the download from the list.
- await downloadList.remove(download);
- await download.finalize(true);
+ // HTML attachment should fail to load.
+ await loadAndCheck("file_html_object_attachment.html", false);
+});
- // Return the download
- return download;
-}
+add_task(async function test_pdf_object_attachment_allow_retargeting() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.navigation.object_embed.allow_retargeting", true],
+ ["browser.download.open_pdf_attachments_inline", false],
+ ],
+ });
+
+ // Even if `allow_retargeting` is enabled, we always display PDFs inline.
+ await loadAndCheck("file_pdf_object_attachment.html", true);
+});
-add_task(async function test_pdf_object_attachment_download() {
+add_task(async function test_img_object_attachment_allow_retargeting() {
await SpecialPowers.pushPrefEnv({
set: [["dom.navigation.object_embed.allow_retargeting", true]],
});
- // Set the behaviour to save pdfs to disk and not handle internally, so we
- // don't end up with extra tabs after the test.
- var gMimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
- var gHandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
- Ci.nsIHandlerService
- );
- const mimeInfo = gMimeSvc.getFromTypeAndExtension("application/pdf", "pdf");
- let previousAction = mimeInfo.preferredAction;
- mimeInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk;
- gHandlerSvc.store(mimeInfo);
- registerCleanupFunction(() => {
- mimeInfo.preferredAction = previousAction;
- gHandlerSvc.store(mimeInfo);
- });
+ // Even if `allow_retargeting` is enabled, we always display images inline.
+ await loadAndCheck("file_img_object_attachment.html", true);
+});
- // Start listening for the download before opening the new tab.
- let downloadPromise = waitForDownload();
- await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_pdf_object_attachment.html`,
- async browser => {
- let download = await downloadPromise;
- is(
- download.source.url,
- `${httpsTestRoot}/file_pdf_attachment.pdf`,
- "download should be the pdf"
- );
+add_task(async function test_svg_object_attachment_allow_retargeting() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.navigation.object_embed.allow_retargeting", true]],
+ });
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_FALLBACK,
- "should be displaying TYPE_FALLBACK"
- );
- });
- }
+ // SVG attachments are downloaded if allow_retargeting is set.
+ await loadAndCheck(
+ "file_svg_object_attachment.html",
+ false,
+ "file_svg_attachment.svg"
);
});
-add_task(async function test_img_object_attachment_download() {
- // NOTE: This is testing our current behaviour here as of bug 1868001 (which
- // is to download an image with `Content-Disposition: attachment` embedded
- // within an object or embed element).
- //
- // Other browsers ignore the `Content-Disposition: attachment` header when
- // loading images within object or embed element as-of december 2023, as
- // we did prior to the changes in bug 1595491.
- //
- // If this turns out to be a web-compat issue, we may want to introduce
- // special handling to ignore content-disposition when loading images within
- // an object or embed element.
+add_task(async function test_html_object_attachment_allow_retargeting() {
await SpecialPowers.pushPrefEnv({
set: [["dom.navigation.object_embed.allow_retargeting", true]],
});
- // Start listening for the download before opening the new tab.
- let downloadPromise = waitForDownload();
- await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_img_object_attachment.html`,
- async browser => {
- let download = await downloadPromise;
- is(
- download.source.url,
- `${httpsTestRoot}/file_img_attachment.jpg`,
- "download should be the jpg"
- );
-
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_FALLBACK,
- "should be displaying TYPE_FALLBACK"
- );
- });
- }
+ // HTML attachments are downloaded if allow_retargeting is set.
+ await loadAndCheck(
+ "file_html_object_attachment.html",
+ false,
+ "file_html_attachment.html"
);
... (file diff truncated)
Regression Test / PoC
diff --git a/dom/base/test/browser.toml b/dom/base/test/browser.toml
index 89d4183bbd2..799fd8b1a81 100644
--- a/dom/base/test/browser.toml
+++ b/dom/base/test/browser.toml
@@ -123,6 +123,12 @@ support-files = [
"file_pdf_object_attachment.html",
"file_pdf_attachment.pdf",
"file_pdf_attachment.pdf^headers^",
+ "file_svg_object_attachment.html",
+ "file_svg_attachment.svg",
+ "file_svg_attachment.svg^headers^",
+ "file_html_object_attachment.html",
+ "file_html_attachment.html",
+ "file_html_attachment.html^headers^",
]
["browser_outline_refocus.js"]
diff --git a/dom/base/test/browser_object_attachment.js b/dom/base/test/browser_object_attachment.js
index b4432862f0a..851fffae565 100644
--- a/dom/base/test/browser_object_attachment.js
+++ b/dom/base/test/browser_object_attachment.js
@@ -7,29 +7,75 @@ const httpsTestRoot = getRootDirectory(gTestPath).replace(
"https://example.com"
);
-add_task(async function test_pdf_object_attachment() {
- await SpecialPowers.pushPrefEnv({
- set: [["dom.navigation.object_embed.allow_retargeting", false]],
- });
+async function loadAndCheck(file, displayInline, downloadFile = null) {
+ // Get the downloads list and add a view to listen for a download to be added.
+ // We do this even if we aren't going to download anything, so we notice if a
+ // download is started.
+ let download;
+ let downloadList = await Downloads.getList(Downloads.ALL);
+ let downloadView = {
+ async onDownloadAdded(aDownload) {
+ info("download added");
+ ok(downloadFile, "Should be expecting a download");
+ download = aDownload;
+
+ // Clean up the download from the list
+ downloadList.remove(aDownload);
+ await aDownload.finalize(true);
+ },
+ };
+ await downloadList.addView(downloadView);
+ // Open the new URL and perform the load.
await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_pdf_object_attachment.html`,
+ `${httpsTestRoot}/${file}`,
async browser => {
is(
browser.browsingContext.children.length,
- 1,
- "Should have a child frame"
+ displayInline ? 1 : 0,
+ `Should ${displayInline ? "not " : ""}have a child frame`
+ );
+
+ await SpecialPowers.spawn(
+ browser,
+ [displayInline],
+ async displayInline => {
+ let obj = content.document.querySelector("object");
+ is(
+ obj.displayedType,
+ displayInline
+ ? Ci.nsIObjectLoadingContent.TYPE_DOCUMENT
+ : Ci.nsIObjectLoadingContent.TYPE_FALLBACK,
+ `should be displaying TYPE_${displayInline ? "DOCUMENT" : "FALLBACK"}`
+ );
+ }
);
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_DOCUMENT,
- "should be displaying TYPE_DOCUMENT"
- );
- });
}
);
+
+ // Clean up our download observer.
+ await downloadList.removeView(downloadView);
+ if (downloadFile) {
+ is(
+ download.source.url,
+ `${httpsTestRoot}/${downloadFile}`,
+ "Download has the correct source"
+ );
+ } else {
+ is(download, undefined, "Should not have seen a download");
+ }
+}
+
+add_task(async function test_pdf_object_attachment() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.navigation.object_embed.allow_retargeting", false],
+ ["browser.download.open_pdf_attachments_inline", false],
+ ],
+ });
+
+ // PDF attachment should display inline.
+ await loadAndCheck("file_pdf_object_attachment.html", true);
});
add_task(async function test_img_object_attachment() {
@@ -37,132 +83,71 @@ add_task(async function test_img_object_attachment() {
set: [["dom.navigation.object_embed.allow_retargeting", false]],
});
- await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_img_object_attachment.html`,
- async browser => {
- is(
- browser.browsingContext.children.length,
- 1,
- "Should have a child frame"
- );
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_DOCUMENT,
- "should be displaying TYPE_DOCUMENT"
- );
- });
- }
- );
+ // Image attachment should display inline.
+ await loadAndCheck("file_img_object_attachment.html", true);
});
-async function waitForDownload() {
- // Get the downloads list and add a view to listen for a download to be added.
- let downloadList = await Downloads.getList(Downloads.ALL);
+add_task(async function test_svg_object_attachment() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.navigation.object_embed.allow_retargeting", false]],
+ });
- // Wait for a single download
- let downloadView;
- let finishedAllDownloads = new Promise(resolve => {
- downloadView = {
- onDownloadAdded(aDownload) {
- info("download added");
- resolve(aDownload);
- },
- };
+ // SVG attachment should fail to load.
+ await loadAndCheck("file_svg_object_attachment.html", false);
+});
+
+add_task(async function test_html_object_attachment() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.navigation.object_embed.allow_retargeting", false]],
});
- await downloadList.addView(downloadView);
- let download = await finishedAllDownloads;
- await downloadList.removeView(downloadView);
- // Clean up the download from the list.
- await downloadList.remove(download);
- await download.finalize(true);
+ // HTML attachment should fail to load.
+ await loadAndCheck("file_html_object_attachment.html", false);
+});
- // Return the download
- return download;
-}
+add_task(async function test_pdf_object_attachment_allow_retargeting() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.navigation.object_embed.allow_retargeting", true],
+ ["browser.download.open_pdf_attachments_inline", false],
+ ],
+ });
+
+ // Even if `allow_retargeting` is enabled, we always display PDFs inline.
+ await loadAndCheck("file_pdf_object_attachment.html", true);
+});
-add_task(async function test_pdf_object_attachment_download() {
+add_task(async function test_img_object_attachment_allow_retargeting() {
await SpecialPowers.pushPrefEnv({
set: [["dom.navigation.object_embed.allow_retargeting", true]],
});
- // Set the behaviour to save pdfs to disk and not handle internally, so we
- // don't end up with extra tabs after the test.
- var gMimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
- var gHandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
- Ci.nsIHandlerService
- );
- const mimeInfo = gMimeSvc.getFromTypeAndExtension("application/pdf", "pdf");
- let previousAction = mimeInfo.preferredAction;
- mimeInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk;
- gHandlerSvc.store(mimeInfo);
- registerCleanupFunction(() => {
- mimeInfo.preferredAction = previousAction;
- gHandlerSvc.store(mimeInfo);
- });
+ // Even if `allow_retargeting` is enabled, we always display images inline.
+ await loadAndCheck("file_img_object_attachment.html", true);
+});
- // Start listening for the download before opening the new tab.
- let downloadPromise = waitForDownload();
- await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_pdf_object_attachment.html`,
- async browser => {
- let download = await downloadPromise;
- is(
- download.source.url,
- `${httpsTestRoot}/file_pdf_attachment.pdf`,
- "download should be the pdf"
- );
+add_task(async function test_svg_object_attachment_allow_retargeting() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.navigation.object_embed.allow_retargeting", true]],
+ });
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_FALLBACK,
- "should be displaying TYPE_FALLBACK"
- );
- });
- }
+ // SVG attachments are downloaded if allow_retargeting is set.
+ await loadAndCheck(
+ "file_svg_object_attachment.html",
+ false,
+ "file_svg_attachment.svg"
);
});
-add_task(async function test_img_object_attachment_download() {
- // NOTE: This is testing our current behaviour here as of bug 1868001 (which
- // is to download an image with `Content-Disposition: attachment` embedded
- // within an object or embed element).
- //
- // Other browsers ignore the `Content-Disposition: attachment` header when
- // loading images within object or embed element as-of december 2023, as
- // we did prior to the changes in bug 1595491.
- //
- // If this turns out to be a web-compat issue, we may want to introduce
- // special handling to ignore content-disposition when loading images within
- // an object or embed element.
+add_task(async function test_html_object_attachment_allow_retargeting() {
await SpecialPowers.pushPrefEnv({
set: [["dom.navigation.object_embed.allow_retargeting", true]],
});
- // Start listening for the download before opening the new tab.
- let downloadPromise = waitForDownload();
- await BrowserTestUtils.withNewTab(
- `${httpsTestRoot}/file_img_object_attachment.html`,
- async browser => {
- let download = await downloadPromise;
- is(
- download.source.url,
- `${httpsTestRoot}/file_img_attachment.jpg`,
- "download should be the jpg"
- );
-
- await SpecialPowers.spawn(browser, [], async () => {
- let obj = content.document.querySelector("object");
- is(
- obj.displayedType,
- Ci.nsIObjectLoadingContent.TYPE_FALLBACK,
- "should be displaying TYPE_FALLBACK"
- );
- });
- }
+ // HTML attachments are downloaded if allow_retargeting is set.
+ await loadAndCheck(
+ "file_html_object_attachment.html",
+ false,
+ "file_html_attachment.html"
);
});
diff --git a/dom/base/test/file_html_attachment.html b/dom/base/test/file_html_attachment.html
new file mode 100644
index 00000000000..f23eccfecf4
--- /dev/null
+++ b/dom/base/test/file_html_attachment.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+ <body>
... (truncated)