commit 4382e1c7c28f07e7098e1b7c597a40f4b59aaf3f parent e324032a0affd3d91c5b261dad9b4e33e5edef0f Author: Atila Butkovits <abutkovits@mozilla.com> Date: Mon, 13 Oct 2025 20:05:22 +0300 Revert "Bug 1980376: apply code formatting via Lando" for causing failures at browser_pageinfo_image_info.js. This reverts commit f45dc38d9ab419a29246f85865518b77da9adcfe. Revert "Bug 1980376 - Restrict pageInfo.xhtml CSP. r=florian" This reverts commit bd21e15d9b165ac3931f4ddbf5e2485d84a82d61. Revert "Bug 1980376 - Use a remote <browser> for displaying media in the Page Info window. r=florian,desktop-theme-reviewers,emilio" This reverts commit e1f13ae91a088f522f8e2ea39bd75f98637d29d6. Diffstat:
14 files changed, 258 insertions(+), 324 deletions(-)
diff --git a/browser/actors/ContextMenuParent.sys.mjs b/browser/actors/ContextMenuParent.sys.mjs @@ -20,10 +20,6 @@ XPCOMUtils.defineLazyServiceGetters(lazy, { export class ContextMenuParent extends JSWindowActorParent { receiveMessage(message) { let browser = this.manager.rootFrameLoader.ownerElement; - if (browser.hasAttribute("disablecontextmenu")) { - return; - } - let win = browser.ownerGlobal; // It's possible that the <xul:browser> associated with this // ContextMenu message doesn't belong to a window that actually diff --git a/browser/actors/PageInfoPreviewChild.sys.mjs b/browser/actors/PageInfoPreviewChild.sys.mjs @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -export class PageInfoPreviewChild extends JSWindowActorChild { - async receiveMessage(message) { - if (message.name === "PageInfoPreview:resize") { - return this.resize(this.contentWindow.document, message.data); - } - - return undefined; - } - - resize(document, data) { - let img = document.querySelector("img"); - if (!img) { - return undefined; - } - - const physWidth = img.width || 0; - const physHeight = img.height || 0; - - if (data.width !== undefined) { - img.width = data.width; - } - if (data.height !== undefined) { - img.height = data.height; - } - - return { - physWidth, - physHeight, - width: img.width, - height: img.height, - }; - } -} diff --git a/browser/actors/moz.build b/browser/actors/moz.build @@ -16,10 +16,10 @@ with Files("AboutReaderParent.sys.mjs"): with Files("LightweightThemeChild.sys.mjs"): BUG_COMPONENT = ("WebExtensions", "Themes") -with Files("PageInfo*.sys.mjs"): +with Files("PageInfoChild.sys.mjs"): BUG_COMPONENT = ("Firefox", "Page Info Window") -with Files("PageStyle*.sys.mjs"): +with Files("PageStyleChild.sys.mjs"): BUG_COMPONENT = ("Firefox", "Menus") with Files("PluginChild.sys.mjs"): @@ -64,7 +64,6 @@ FINAL_TARGET_FILES.actors += [ "LinkHandlerChild.sys.mjs", "LinkHandlerParent.sys.mjs", "PageInfoChild.sys.mjs", - "PageInfoPreviewChild.sys.mjs", "PageStyleChild.sys.mjs", "PageStyleParent.sys.mjs", "PluginChild.sys.mjs", diff --git a/browser/base/content/pageinfo/pageInfo.css b/browser/base/content/pageinfo/pageInfo.css @@ -14,7 +14,7 @@ #mediaPreviewBox, #imagecontainerbox, -#mediaBrowser, +#thepreviewimage, #mainDeck > vbox { min-width: 0; min-height: 0; @@ -30,10 +30,10 @@ display: none; } -#theimagecontainer, -#mediaBrowser { - width: 100%; - height: 100%; +#thepreviewimage { + margin: 1em auto; + flex: none; + display: block; } table { diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js @@ -446,30 +446,6 @@ async function loadPageInfo(browsingContext, imageElement, browser) { } } -// Setup the <browser> used for media previews -function createPreviewBrowserElement(browser, docInfo) { - const previewBrowser = document.createXULElement("browser"); - previewBrowser.setAttribute("id", "mediaBrowser"); - previewBrowser.setAttribute("type", "content"); - previewBrowser.setAttribute("remote", "true"); - previewBrowser.setAttribute("remoteType", browser.remoteType); - previewBrowser.setAttribute("maychangeremoteness", "true"); - previewBrowser.setAttribute("disableglobalhistory", "true"); - previewBrowser.setAttribute("nodefaultsrc", "true"); - previewBrowser.setAttribute("disablecontextmenu", "true"); - previewBrowser.setAttribute( - "initialBrowsingContextGroupId", - browser.browsingContext.group.id - ); - - let { userContextId } = docInfo.principal.originAttributes; - if (userContextId) { - previewBrowser.setAttribute("usercontextid", userContextId); - } - - document.getElementById("mediaBrowser").replaceWith(previewBrowser); -} - /** * onNonMediaPageInfoLoad is responsible for populating the page info * UI other than the media tab. This includes general, permissions, and security. @@ -492,8 +468,6 @@ async function onNonMediaPageInfoLoad(browser, pageInfoData, imageInfo) { .getElementById("main-window") .setAttribute("relatedUrl", docInfo.location); - createPreviewBrowserElement(browser, docInfo); - await makeGeneralTab(pageInfoData.metaViewRows, docInfo); if ( uri.spec.startsWith("about:neterror") || @@ -970,6 +944,7 @@ function makePreview(row) { var item = gImageView.data[row][COL_IMAGE_NODE]; var url = gImageView.data[row][COL_IMAGE_ADDRESS]; var isBG = gImageView.data[row][COL_IMAGE_BG]; + var isAudio = false; setItemValue("imageurltext", url); setItemValue("imagetext", item.imageText); @@ -977,7 +952,7 @@ function makePreview(row) { // get cache info var cacheKey = url.replace(/#.*$/, ""); - openCacheEntry(cacheKey, async function (cacheEntry) { + openCacheEntry(cacheKey, function (cacheEntry) { // find out the file size if (cacheEntry) { let imageSize = cacheEntry.dataSize; @@ -1025,25 +1000,19 @@ function makePreview(row) { element.removeAttribute("data-l10n-id"); } - let forceMediaDocument = null; - let message = { - width: undefined, - height: undefined, - }; + var imageContainer = document.getElementById("theimagecontainer"); + var oldImage = document.getElementById("thepreviewimage"); - let isAllowed = checkProtocol(gImageView.data[row]); - if (isAllowed) { - try { - Services.scriptSecurityManager.checkLoadURIWithPrincipal( - gDocInfo.principal, - Services.io.newURI(url), - 0 - ); - } catch { - isAllowed = false; - } - } + var isProtocolAllowed = checkProtocol(gImageView.data[row]); + + var newImage = new Image(); + newImage.id = "thepreviewimage"; + var physWidth = 0, + physHeight = 0; + var width = 0, + height = 0; + let triggeringPrinStr = E10SUtils.serializePrincipal(gDocInfo.principal); if ( (item.HTMLLinkElement || item.HTMLInputElement || @@ -1051,129 +1020,117 @@ function makePreview(row) { item.SVGImageElement || (item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && - isAllowed + isProtocolAllowed ) { - forceMediaDocument = "image"; - - if (item.SVGImageElement) { - message.width = item.SVGImageElementWidth; - message.height = item.SVGImageElementHeight; - } else if (!isBG) { - if ("width" in item && item.width) { - message.width = item.width; - } - if ("height" in item && item.height) { - message.height = item.height; + function loadOrErrorListener() { + newImage.removeEventListener("load", loadOrErrorListener); + newImage.removeEventListener("error", loadOrErrorListener); + physWidth = newImage.width || 0; + physHeight = newImage.height || 0; + + // "width" and "height" attributes must be set to newImage, + // even if there is no "width" or "height attribute in item; + // otherwise, the preview image cannot be displayed correctly. + // Since the image might have been loaded out-of-process, we expect + // the item to tell us its width / height dimensions. Failing that + // the item should tell us the natural dimensions of the image. Finally + // failing that, we'll assume that the image was never loaded in the + // other process (this can be true for favicons, for example), and so + // we'll assume that we can use the natural dimensions of the newImage + // we just created. If the natural dimensions of newImage are not known + // then the image is probably broken. + if (!isBG) { + newImage.width = + ("width" in item && item.width) || newImage.naturalWidth; + newImage.height = + ("height" in item && item.height) || newImage.naturalHeight; + } else { + // the Width and Height of an HTML tag should not be used for its background image + // (for example, "table" can have "width" or "height" attributes) + newImage.width = item.naturalWidth || newImage.naturalWidth; + newImage.height = item.naturalHeight || newImage.naturalHeight; } - } - - document.getElementById("theimagecontainer").collapsed = false; - document.getElementById("brokenimagecontainer").collapsed = true; - } else if (item.HTMLVideoElement && isAllowed) { - forceMediaDocument = "video"; - document.getElementById("theimagecontainer").collapsed = false; - document.getElementById("brokenimagecontainer").collapsed = true; - - document.l10n.setAttributes( - document.getElementById("imagedimensiontext"), - "media-dimensions", - { - dimx: formatNumber(item.videoWidth), - dimy: formatNumber(item.videoHeight), + if (item.SVGImageElement) { + newImage.width = item.SVGImageElementWidth; + newImage.height = item.SVGImageElementHeight; } - ); - } else if (item.HTMLAudioElement && isAllowed) { - forceMediaDocument = "video"; // Audio also uses a VideoDocument. - document.getElementById("theimagecontainer").collapsed = false; - document.getElementById("brokenimagecontainer").collapsed = true; - } else { - // fallback image for protocols not allowed (e.g., javascript:) - // or elements not [yet] handled (e.g., object, embed). - document.getElementById("brokenimagecontainer").collapsed = false; - document.getElementById("theimagecontainer").collapsed = true; - return; - } - - const mediaBrowser = document.getElementById("mediaBrowser"); - - const options = { - triggeringPrincipal: gDocInfo.principal, - forceMediaDocument, - }; - mediaBrowser.loadURI(Services.io.newURI(url), options); - - await new Promise(resolve => { - let webProgressListener = { - onStateChange(webProgress, request, aStateFlags) { - if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) { - mediaBrowser.webProgress?.removeProgressListener( - webProgressListener + width = newImage.width; + height = newImage.height; + + document.getElementById("theimagecontainer").collapsed = false; + document.getElementById("brokenimagecontainer").collapsed = true; + + if (url) { + if (width != physWidth || height != physHeight) { + document.l10n.setAttributes( + document.getElementById("imagedimensiontext"), + "media-dimensions-scaled", + { + dimx: formatNumber(physWidth), + dimy: formatNumber(physHeight), + scaledx: formatNumber(width), + scaledy: formatNumber(height), + } + ); + } else { + document.l10n.setAttributes( + document.getElementById("imagedimensiontext"), + "media-dimensions", + { dimx: formatNumber(width), dimy: formatNumber(height) } ); - resolve(); } - }, - - QueryInterface: ChromeUtils.generateQI([ - "nsIWebProgressListener2", - "nsIWebProgressListener", - "nsISupportsWeakReference", - ]), - }; - mediaBrowser.addProgressListener( - webProgressListener, - Ci.nsIWebProgress.NOTIFY_STATE_WINDOW - ); - }); - - try { - const actor = - mediaBrowser.browsingContext.currentWindowGlobal.getActor( - "PageInfoPreview" - ); - - let data = await actor.sendQuery("PageInfoPreview:resize", message); - if (!data) { - return; + } } - let tree = document.getElementById("imagetree"); - let activeRow = getSelectedRows(tree)[0]; + // We need to wait for the image to finish loading before using width & height + newImage.addEventListener("load", loadOrErrorListener); + newImage.addEventListener("error", loadOrErrorListener); - // Make sure we only update the dimensions if the - // image is still selected. - if (url !== gImageView.data[activeRow][COL_IMAGE_ADDRESS]) { - return; + newImage.setAttribute("triggeringprincipal", triggeringPrinStr); + newImage.setAttribute("src", url); + } else { + // Handle the case where newImage is not used for width & height + if (item.HTMLVideoElement && isProtocolAllowed) { + newImage = document.createElement("video"); + newImage.id = "thepreviewimage"; + newImage.setAttribute("triggeringprincipal", triggeringPrinStr); + newImage.src = url; + newImage.controls = true; + width = physWidth = item.videoWidth; + height = physHeight = item.videoHeight; + + document.getElementById("theimagecontainer").collapsed = false; + document.getElementById("brokenimagecontainer").collapsed = true; + } else if (item.HTMLAudioElement && isProtocolAllowed) { + newImage = new Audio(); + newImage.id = "thepreviewimage"; + newImage.setAttribute("triggeringprincipal", triggeringPrinStr); + newImage.src = url; + newImage.controls = true; + isAudio = true; + + document.getElementById("theimagecontainer").collapsed = false; + document.getElementById("brokenimagecontainer").collapsed = true; + } else { + // fallback image for protocols not allowed (e.g., javascript:) + // or elements not [yet] handled (e.g., object, embed). + document.getElementById("brokenimagecontainer").collapsed = false; + document.getElementById("theimagecontainer").collapsed = true; } - if (data.width != data.physWidth || data.height != data.physHeight) { - document.l10n.setAttributes( - document.getElementById("imagedimensiontext"), - "media-dimensions-scaled", - { - dimx: formatNumber(data.physWidth), - dimy: formatNumber(data.physHeight), - scaledx: formatNumber(data.width), - scaledy: formatNumber(data.height), - } - ); - } else { + if (url && !isAudio) { document.l10n.setAttributes( document.getElementById("imagedimensiontext"), "media-dimensions", - { - dimx: formatNumber(data.width), - dimy: formatNumber(data.height), - } + { dimx: formatNumber(width), dimy: formatNumber(height) } ); } - } catch (e) { - console.error(e); - } finally { - // Event for tests. - window.dispatchEvent(new Event("page-info-mediapreview-load")); } + + imageContainer.removeChild(oldImage); + imageContainer.appendChild(newImage); }); } diff --git a/browser/base/content/pageinfo/pageInfo.xhtml b/browser/base/content/pageinfo/pageInfo.xhtml @@ -2,7 +2,7 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -<?csp default-src chrome:; style-src chrome: 'unsafe-inline'; ?> +<?csp default-src chrome:; img-src data: *; media-src data: *; style-src chrome: 'unsafe-inline'; ?> <window id="main-window" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" @@ -247,7 +247,7 @@ </hbox> <vbox id="imagecontainerbox" flex="1"> <hbox id="theimagecontainer"> - <browser id="mediaBrowser" /> + <image id="thepreviewimage"/> </hbox> <hbox id="brokenimagecontainer" pack="center" collapsed="true"> <image id="brokenimage" src="resource://gre-resources/broken-image.png"/> diff --git a/browser/base/content/test/pageinfo/browser.toml b/browser/base/content/test/pageinfo/browser.toml @@ -1,5 +1,13 @@ [DEFAULT] +["browser_pageinfo_firstPartyIsolation.js"] +support-files = [ + "image.html", + "../general/audio.ogg", + "../general/moz.png", + "../general/video.webm", +] + ["browser_pageinfo_iframe_media.js"] support-files = ["iframes.html"] @@ -11,14 +19,6 @@ support-files = ["all_images.html"] ["browser_pageinfo_permissions.js"] -["browser_pageinfo_previewBrowser.js"] -support-files = [ - "image.html", - "../general/audio.ogg", - "../general/moz.png", - "../general/video.webm", -] - ["browser_pageinfo_rtl.js"] ["browser_pageinfo_security.js"] diff --git a/browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js b/browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js @@ -0,0 +1,89 @@ +const Cm = Components.manager; + +async function testFirstPartyDomain(pageInfo) { + const EXPECTED_DOMAIN = "example.com"; + await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); + info("pageInfo initialized"); + let tree = pageInfo.document.getElementById("imagetree"); + Assert.ok(!!tree, "should have imagetree element"); + + // i=0: <img> + // i=1: <video> + // i=2: <audio> + for (let i = 0; i < 3; i++) { + info("imagetree select " + i); + tree.view.selection.select(i); + tree.ensureRowIsVisible(i); + tree.focus(); + + let preview = pageInfo.document.getElementById("thepreviewimage"); + info("preview.src=" + preview.src); + + // For <img>, we will query imgIRequest.imagePrincipal later, so we wait + // for load event. For <audio> and <video>, so far we only can get + // the triggeringprincipal attribute on the node, so we simply wait for + // loadstart. + if (i == 0) { + await BrowserTestUtils.waitForEvent(preview, "load"); + } else { + await BrowserTestUtils.waitForEvent(preview, "loadstart"); + } + + info("preview load " + i); + + // Originally thepreviewimage is loaded with SystemPrincipal, therefore + // it won't have origin attributes, now we've changed to loadingPrincipal + // to the content in bug 1376971, it should have firstPartyDomain set. + if (i == 0) { + let req = preview.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); + Assert.equal( + req.imagePrincipal.originAttributes.firstPartyDomain, + EXPECTED_DOMAIN, + "imagePrincipal should have firstPartyDomain set to " + EXPECTED_DOMAIN + ); + } + + // Check the node has the attribute 'triggeringprincipal'. + let loadingPrincipalStr = preview.getAttribute("triggeringprincipal"); + let loadingPrincipal = E10SUtils.deserializePrincipal(loadingPrincipalStr); + Assert.equal( + loadingPrincipal.originAttributes.firstPartyDomain, + EXPECTED_DOMAIN, + "loadingPrincipal should have firstPartyDomain set to " + EXPECTED_DOMAIN + ); + } +} + +async function test() { + waitForExplicitFinish(); + + Services.prefs.setBoolPref("privacy.firstparty.isolate", true); + registerCleanupFunction(function () { + Services.prefs.clearUserPref("privacy.firstparty.isolate"); + }); + + let url = + "https://example.com/browser/browser/base/content/test/pageinfo/image.html"; + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); + let loadPromise = BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + false, + url + ); + BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url); + await loadPromise; + + // Pass a dummy imageElement, if there isn't an imageElement, pageInfo.js + // will do a preview, however this sometimes will cause intermittent failures, + // see bug 1403365. + let pageInfo = BrowserCommands.pageInfo(url, "mediaTab", {}); + info("waitForEvent pageInfo"); + await BrowserTestUtils.waitForEvent(pageInfo, "load"); + + info("calling testFirstPartyDomain"); + await testFirstPartyDomain(pageInfo); + + pageInfo.close(); + gBrowser.removeCurrentTab(); + finish(); +} diff --git a/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js b/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js @@ -33,18 +33,10 @@ add_task(async function () { "mediaTab", imageInfo ); - await BrowserTestUtils.waitForEvent(pageInfo, "page-info-mediapreview-load"); + await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); - let mediaBrowser = pageInfo.document.getElementById("mediaBrowser"); - let pageInfoImg = await SpecialPowers.spawn(mediaBrowser, [], () => { - let previewImg = content.document.querySelector("img"); - - return { - src: previewImg.src, - width: previewImg.width, - height: previewImg.height, - }; - }); + let pageInfoImg = pageInfo.document.getElementById("thepreviewimage"); + await BrowserTestUtils.waitForEvent(pageInfoImg, "load"); Assert.equal( pageInfoImg.src, imageInfo.src, diff --git a/browser/base/content/test/pageinfo/browser_pageinfo_images.js b/browser/base/content/test/pageinfo/browser_pageinfo_images.js @@ -2,7 +2,8 @@ const TEST_PATH = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", - "https://example.com" + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" ); add_task(async function test_all_images_mentioned() { @@ -58,7 +59,7 @@ add_task(async function test_view_image_info() { let contextMenu = document.getElementById("contentAreaContextMenu"); let viewImageInfo = document.getElementById("context-viewimageinfo"); - let imageInfo = await SpecialPowers.spawn(browser, [], () => { + let imageInfo = await SpecialPowers.spawn(browser, [], async () => { let testImg = content.document.querySelector("img"); return { src: testImg.src, @@ -73,28 +74,20 @@ add_task(async function test_view_image_info() { await BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); - let promiseMediaLoad = BrowserTestUtils.domWindowOpened().then(win => - BrowserTestUtils.waitForEvent(win, "page-info-mediapreview-load") + let promisePageInfoLoaded = BrowserTestUtils.domWindowOpened().then(win => + BrowserTestUtils.waitForEvent(win, "page-info-init") ); contextMenu.activateItem(viewImageInfo); - let pageInfo = (await promiseMediaLoad).target.ownerGlobal; - let mediaBrowser = pageInfo.document.getElementById("mediaBrowser"); - - let previewImageInfo = await SpecialPowers.spawn(mediaBrowser, [], () => { - let img = content.document.querySelector("img"); - return { - src: img.src, - }; - }); + let pageInfo = (await promisePageInfoLoaded).target.ownerGlobal; + let pageInfoImg = pageInfo.document.getElementById("thepreviewimage"); Assert.equal( - previewImageInfo.src, + pageInfoImg.src, imageInfo.src, - "selected image is correct" + "selected image is the correct" ); - await BrowserTestUtils.closeWindow(pageInfo); } ); @@ -108,10 +101,7 @@ add_task(async function test_image_size() { gBrowser.selectedBrowser.currentURI.spec, "mediaTab" ); - await BrowserTestUtils.waitForEvent( - pageInfo, - "page-info-mediapreview-load" - ); + await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); let imageSize = pageInfo.document.getElementById("imagesizetext"); diff --git a/browser/base/content/test/pageinfo/browser_pageinfo_previewBrowser.js b/browser/base/content/test/pageinfo/browser_pageinfo_previewBrowser.js @@ -1,69 +0,0 @@ -const Cm = Components.manager; - -async function testPreviewBrowser(pageInfo) { - await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); - info("pageInfo initialized"); - let tree = pageInfo.document.getElementById("imagetree"); - Assert.ok(!!tree, "should have imagetree element"); - - // i=0: <img> - // i=1: <video> - // i=2: <audio> - for (let i = 0; i < 3; i++) { - info("imagetree select " + i); - tree.view.selection.select(i); - tree.ensureRowIsVisible(i); - tree.focus(); - - info("Waiting for the page-info-mediapreview-load event"); - await BrowserTestUtils.waitForEvent( - pageInfo, - "page-info-mediapreview-load" - ); - - info("preview load " + i); - - let mediaBrowser = pageInfo.document.getElementById("mediaBrowser"); - - Assert.equal( - mediaBrowser.browsingContext.currentRemoteType, - "webIsolated=https://example.com", - "Preview mediaBrowser is running in a webIsolated process" - ); - - Assert.equal( - gBrowser.selectedBrowser.browsingContext.group, - mediaBrowser.browsingContext.group, - "Preview browser is in the same group as the original page" - ); - } -} - -async function test() { - waitForExplicitFinish(); - - let url = - "https://example.com/browser/browser/base/content/test/pageinfo/image.html"; - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - let loadPromise = BrowserTestUtils.browserLoaded( - gBrowser.selectedBrowser, - false, - url - ); - BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url); - await loadPromise; - - // Pass a dummy imageElement, if there isn't an imageElement, pageInfo.js - // will do a preview, however this sometimes will cause intermittent failures, - // see bug 1403365. - let pageInfo = BrowserCommands.pageInfo(url, "mediaTab", {}); - info("waitForEvent pageInfo"); - await BrowserTestUtils.waitForEvent(pageInfo, "load"); - - info("calling testPreviewBrowser"); - await testPreviewBrowser(pageInfo); - - pageInfo.close(); - gBrowser.removeCurrentTab(); - finish(); -} diff --git a/browser/components/DesktopActorRegistry.sys.mjs b/browser/components/DesktopActorRegistry.sys.mjs @@ -526,12 +526,6 @@ let JSWINDOWACTORS = { allFrames: true, }, - PageInfoPreview: { - child: { - esModuleURI: "resource:///actors/PageInfoPreviewChild.sys.mjs", - }, - }, - PageStyle: { parent: { esModuleURI: "resource:///actors/PageStyleParent.sys.mjs", diff --git a/dom/security/nsContentSecurityUtils.cpp b/dom/security/nsContentSecurityUtils.cpp @@ -1343,6 +1343,7 @@ static nsLiteralCString sImgSrcDataBlobAllowList[] = { "chrome://browser/content/aboutDialog.xhtml"_ns, "chrome://browser/content/aboutlogins/aboutLogins.html"_ns, "chrome://browser/content/genai/chat.html"_ns, + "chrome://browser/content/pageinfo/pageInfo.xhtml"_ns, "chrome://browser/content/places/bookmarksSidebar.xhtml"_ns, "chrome://browser/content/places/places.xhtml"_ns, "chrome://browser/content/preferences/dialogs/permissions.xhtml"_ns, @@ -1403,7 +1404,8 @@ static nsLiteralCString sImgSrcAddonsAllowList[] = { // img-src * // UNSAFE! Allows loading everything. static nsLiteralCString sImgSrcWildcardAllowList[] = { - "about:reader"_ns, "chrome://browser/content/syncedtabs/sidebar.xhtml"_ns, + "about:reader"_ns, "chrome://browser/content/pageinfo/pageInfo.xhtml"_ns, + "chrome://browser/content/syncedtabs/sidebar.xhtml"_ns, // STOP! Do not add anything to this list. }; // img-src https://example.org @@ -1413,10 +1415,14 @@ static nsLiteralCString sImgSrcHttpsHostAllowList[] = { "chrome://browser/content/aboutlogins/aboutLogins.html"_ns, "chrome://browser/content/spotlight.html"_ns, }; +// media-src data: blob: +static nsLiteralCString sMediaSrcDataBlobAllowList[] = { + "chrome://browser/content/pageinfo/pageInfo.xhtml"_ns, +}; // media-src * // UNSAFE! Allows loading everything. static nsLiteralCString sMediaSrcWildcardAllowList[] = { - "about:reader"_ns, + "about:reader"_ns, "chrome://browser/content/pageinfo/pageInfo.xhtml"_ns, // STOP! Do not add anything to this list. }; // media-src https://example.org @@ -1637,6 +1643,20 @@ class MediaSrcVisitor : public AllowBuiltinSrcVisitor { MOZ_ASSERT(aDirective == CSPDirective::MEDIA_SRC_DIRECTIVE); } + bool visitSchemeSrc(const nsCSPSchemeSrc& src) override { + nsAutoString scheme; + src.getScheme(scheme); + + // data: and blob: can be used to decode arbitrary media. + if (scheme == u"data"_ns || scheme == u"blob") { + if (CheckAllowList(sMediaSrcDataBlobAllowList)) { + return true; + } + } + + return AllowBuiltinSrcVisitor::visitSchemeSrc(src); + } + bool visitHostSrc(const nsCSPHostSrc& src) override { return VisitHostSrcWithWildcardAndHttpsHostAllowLists( src, sMediaSrcWildcardAllowList, sMediaSrcHttpsHostAllowList); diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js @@ -384,10 +384,13 @@ add_task(async function testPageInfoMediaSaveAs() { imageTree.focus(); // Wait until the preview is loaded. - await BrowserTestUtils.waitForEvent( - pageInfo, - "page-info-mediapreview-load" - ); + let preview = pageInfo.document.getElementById("thepreviewimage"); + let mediaType = pageInfo.gImageView.data[i][1]; // COL_IMAGE_TYPE + if (mediaType == "Image") { + await BrowserTestUtils.waitForEvent(preview, "load"); + } else if (mediaType == "Video") { + await BrowserTestUtils.waitForEvent(preview, "canplaythrough"); + } let url = pageInfo.gImageView.data[i][0]; // COL_IMAGE_ADDRESS info(`Start to save the media item with URL: ${url}`); @@ -439,10 +442,10 @@ add_task(async function testPageInfoMediaMultipleSelectedSaveAs() { // Make sure the preview image is loaded in order to avoid interfering // following tests. - await BrowserTestUtils.waitForEvent( - pageInfo, - "page-info-mediapreview-load" - ); + let preview = pageInfo.document.getElementById("thepreviewimage"); + await BrowserTestUtils.waitForCondition(() => { + return preview.complete; + }); let imageTree = pageInfo.document.getElementById("imagetree"); let imageRowsNum = imageTree.view.rowCount;