tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e1f13ae91a088f522f8e2ea39bd75f98637d29d6
parent ebda7f435a3d643b216dd9efffea15438f7233dc
Author: Tom Schuster <tschuster@mozilla.com>
Date:   Mon, 13 Oct 2025 16:11:18 +0000

Bug 1980376 - Use a remote <browser> for displaying media in the Page Info window. r=florian,desktop-theme-reviewers,emilio

Differential Revision: https://phabricator.services.mozilla.com/D259426

Diffstat:
Mbrowser/actors/ContextMenuParent.sys.mjs | 4++++
Abrowser/actors/PageInfoPreviewChild.sys.mjs | 37+++++++++++++++++++++++++++++++++++++
Mbrowser/actors/moz.build | 5+++--
Mbrowser/base/content/pageinfo/pageInfo.css | 10+++++-----
Mbrowser/base/content/pageinfo/pageInfo.js | 255++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mbrowser/base/content/pageinfo/pageInfo.xhtml | 2+-
Mbrowser/base/content/test/pageinfo/browser.toml | 16++++++++--------
Dbrowser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js | 89-------------------------------------------------------------------------------
Mbrowser/base/content/test/pageinfo/browser_pageinfo_image_info.js | 14+++++++++++---
Mbrowser/base/content/test/pageinfo/browser_pageinfo_images.js | 30++++++++++++++++++++----------
Abrowser/base/content/test/pageinfo/browser_pageinfo_previewBrowser.js | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/components/DesktopActorRegistry.sys.mjs | 6++++++
Mtoolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js | 19++++++++-----------
13 files changed, 321 insertions(+), 235 deletions(-)

diff --git a/browser/actors/ContextMenuParent.sys.mjs b/browser/actors/ContextMenuParent.sys.mjs @@ -20,6 +20,10 @@ 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 @@ -0,0 +1,37 @@ +/* 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("PageInfoChild.sys.mjs"): +with Files("PageInfo*.sys.mjs"): BUG_COMPONENT = ("Firefox", "Page Info Window") -with Files("PageStyleChild.sys.mjs"): +with Files("PageStyle*.sys.mjs"): BUG_COMPONENT = ("Firefox", "Menus") with Files("PluginChild.sys.mjs"): @@ -64,6 +64,7 @@ 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, -#thepreviewimage, +#mediaBrowser, #mainDeck > vbox { min-width: 0; min-height: 0; @@ -30,10 +30,10 @@ display: none; } -#thepreviewimage { - margin: 1em auto; - flex: none; - display: block; +#theimagecontainer, +#mediaBrowser { + width: 100%; + height: 100%; } table { diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js @@ -446,6 +446,30 @@ 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. @@ -468,6 +492,8 @@ 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") || @@ -944,7 +970,6 @@ 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); @@ -952,7 +977,7 @@ function makePreview(row) { // get cache info var cacheKey = url.replace(/#.*$/, ""); - openCacheEntry(cacheKey, function (cacheEntry) { + openCacheEntry(cacheKey, async function (cacheEntry) { // find out the file size if (cacheEntry) { let imageSize = cacheEntry.dataSize; @@ -1000,19 +1025,25 @@ function makePreview(row) { element.removeAttribute("data-l10n-id"); } - var imageContainer = document.getElementById("theimagecontainer"); - var oldImage = document.getElementById("thepreviewimage"); - - var isProtocolAllowed = checkProtocol(gImageView.data[row]); + let forceMediaDocument = null; + let message = { + width: undefined, + height: undefined, + }; - var newImage = new Image(); - newImage.id = "thepreviewimage"; - var physWidth = 0, - physHeight = 0; - var width = 0, - height = 0; + let isAllowed = checkProtocol(gImageView.data[row]); + if (isAllowed) { + try { + Services.scriptSecurityManager.checkLoadURIWithPrincipal( + gDocInfo.principal, + Services.io.newURI(url), + 0 + ); + } catch { + isAllowed = false; + } + } - let triggeringPrinStr = E10SUtils.serializePrincipal(gDocInfo.principal); if ( (item.HTMLLinkElement || item.HTMLInputElement || @@ -1020,117 +1051,129 @@ function makePreview(row) { item.SVGImageElement || (item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && - isProtocolAllowed + isAllowed ) { - 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; + 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; + } + } + + document.getElementById("theimagecontainer").collapsed = false; + document.getElementById("brokenimagecontainer").collapsed = true; + } else if (item.HTMLVideoElement && isAllowed) { + forceMediaDocument = "video"; - if (item.SVGImageElement) { - newImage.width = item.SVGImageElementWidth; - newImage.height = item.SVGImageElementHeight; + 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), } + ); + } else if (item.HTMLAudioElement && isAllowed) { + forceMediaDocument = "video"; // Audio also uses a VideoDocument. - 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) } + 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 ); + 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; } - // We need to wait for the image to finish loading before using width & height - newImage.addEventListener("load", loadOrErrorListener); - newImage.addEventListener("error", loadOrErrorListener); + let tree = document.getElementById("imagetree"); + let activeRow = getSelectedRows(tree)[0]; - 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; + // Make sure we only update the dimensions if the + // image is still selected. + if (url !== gImageView.data[activeRow][COL_IMAGE_ADDRESS]) { + return; } - if (url && !isAudio) { + 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 { document.l10n.setAttributes( document.getElementById("imagedimensiontext"), "media-dimensions", - { dimx: formatNumber(width), dimy: formatNumber(height) } + { + dimx: formatNumber(data.width), + dimy: formatNumber(data.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 @@ -247,7 +247,7 @@ </hbox> <vbox id="imagecontainerbox" flex="1"> <hbox id="theimagecontainer"> - <image id="thepreviewimage"/> + <browser id="mediaBrowser" /> </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,13 +1,5 @@ [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"] @@ -19,6 +11,14 @@ 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 @@ -1,89 +0,0 @@ -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,10 +33,18 @@ add_task(async function () { "mediaTab", imageInfo ); - await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); + await BrowserTestUtils.waitForEvent(pageInfo, "page-info-mediapreview-load"); - let pageInfoImg = pageInfo.document.getElementById("thepreviewimage"); - await BrowserTestUtils.waitForEvent(pageInfoImg, "load"); + 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, + }; + }); 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,8 +2,7 @@ const TEST_PATH = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", - // eslint-disable-next-line @microsoft/sdl/no-insecure-url - "http://example.com" + "https://example.com" ); add_task(async function test_all_images_mentioned() { @@ -59,7 +58,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, [], async () => { + let imageInfo = await SpecialPowers.spawn(browser, [], () => { let testImg = content.document.querySelector("img"); return { src: testImg.src, @@ -74,20 +73,28 @@ add_task(async function test_view_image_info() { await BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); - let promisePageInfoLoaded = BrowserTestUtils.domWindowOpened().then(win => - BrowserTestUtils.waitForEvent(win, "page-info-init") + let promiseMediaLoad = BrowserTestUtils.domWindowOpened().then(win => + BrowserTestUtils.waitForEvent(win, "page-info-mediapreview-load") ); contextMenu.activateItem(viewImageInfo); - let pageInfo = (await promisePageInfoLoaded).target.ownerGlobal; - let pageInfoImg = pageInfo.document.getElementById("thepreviewimage"); + 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, + }; + }); Assert.equal( - pageInfoImg.src, + previewImageInfo.src, imageInfo.src, - "selected image is the correct" + "selected image is correct" ); + await BrowserTestUtils.closeWindow(pageInfo); } ); @@ -101,7 +108,10 @@ add_task(async function test_image_size() { gBrowser.selectedBrowser.currentURI.spec, "mediaTab" ); - await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); + await BrowserTestUtils.waitForEvent( + pageInfo, + "page-info-mediapreview-load" + ); 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 @@ -0,0 +1,69 @@ +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,6 +526,12 @@ let JSWINDOWACTORS = { allFrames: true, }, + PageInfoPreview: { + child: { + esModuleURI: "resource:///actors/PageInfoPreviewChild.sys.mjs", + }, + }, + PageStyle: { parent: { esModuleURI: "resource:///actors/PageStyleParent.sys.mjs", diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js @@ -384,13 +384,10 @@ add_task(async function testPageInfoMediaSaveAs() { imageTree.focus(); // Wait until the preview is loaded. - 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"); - } + await BrowserTestUtils.waitForEvent( + pageInfo, + "page-info-mediapreview-load" + ); let url = pageInfo.gImageView.data[i][0]; // COL_IMAGE_ADDRESS info(`Start to save the media item with URL: ${url}`); @@ -442,10 +439,10 @@ add_task(async function testPageInfoMediaMultipleSelectedSaveAs() { // Make sure the preview image is loaded in order to avoid interfering // following tests. - let preview = pageInfo.document.getElementById("thepreviewimage"); - await BrowserTestUtils.waitForCondition(() => { - return preview.complete; - }); + await BrowserTestUtils.waitForEvent( + pageInfo, + "page-info-mediapreview-load" + ); let imageTree = pageInfo.document.getElementById("imagetree"); let imageRowsNum = imageTree.view.rowCount;