commit 75101d5ffba3ee8c1fdbbf0e14de496755257eac parent c91fb7a5354d86dd84aed0cb4a41205e245b9290 Author: Tom Schuster <tschuster@mozilla.com> Date: Mon, 10 Nov 2025 09:50:39 +0000 Bug 1586083 - Send raster favicons as one or more VideoFrame blobs. r=mossop,sessionstore-reviewers,urlbar-reviewers,sthompson,Standard8 Differential Revision: https://phabricator.services.mozilla.com/D269948 Diffstat:
17 files changed, 296 insertions(+), 72 deletions(-)
diff --git a/browser/actors/LinkHandlerParent.sys.mjs b/browser/actors/LinkHandlerParent.sys.mjs @@ -2,6 +2,13 @@ * 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/. */ +import { + TYPE_SVG, + TYPE_ICO, + TRUSTED_FAVICON_SCHEMES, + blobAsDataURL, +} from "moz-src:///browser/modules/FaviconUtils.sys.mjs"; + const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { @@ -12,6 +19,63 @@ ChromeUtils.defineESModuleGetters(lazy, { let gTestListeners = new Set(); +async function drawImageOnCanvas(canvas, image) { + let data = await image.blob.bytes(); + let frame = new VideoFrame(data, { + timestamp: 0, + format: image.format, + codedWidth: image.displayWidth, + codedHeight: image.displayHeight, + }); + + canvas.width = frame.displayWidth; + canvas.height = frame.displayHeight; + let ctx = canvas.getContext("2d"); + ctx.drawImage(frame, 0, 0); +} + +// Re-construct the ICO file with different sized PNG images. +// See https://en.wikipedia.org/wiki/ICO_(file_format). +function createICO(images) { + const ICO_HEADER_SIZE = 6; + const ICO_DIR_ENTRY_SIZE = 16; + + const metadataSize = ICO_HEADER_SIZE + ICO_DIR_ENTRY_SIZE * images.length; + const size = + metadataSize + images.reduce((acc, image) => acc + image.byteLength, 0); + + let buffer = new ArrayBuffer(size); + let u8 = new Uint8Array(buffer); + let view = new DataView(buffer); + + view.setUint16(0, 0, true); // idReserved + view.setUint16(2, 1, true); // idType (1 = ICO) + view.setUint16(4, images.length, true); // idCount + + let dataOffset = metadataSize; // Append image data directly after the meta data. + for (let i = 0; i < images.length; i++) { + const off = ICO_HEADER_SIZE + ICO_DIR_ENTRY_SIZE * i; + + // We use a zero width and height because we always use compressed PNGs, + // which require this and have their own width/height information. + view.setUint8(off, 0); // bWidth + view.setUint8(off + 1, 0); // bHeight + view.setUint8(off + 2, 0); // bColorCount + view.setUint8(off + 3, 0); // bReserved + view.setUint16(off + 4, 1, true); // wPlanes + view.setUint16(off + 6, 32, true); // wBitCount + view.setUint32(off + 8, images[i].byteLength, true); // dwBytesInRes + view.setUint32(off + 12, dataOffset, true); // dwImageOffset + + // Copy the image's bytes into the ICO buffer. + u8.set(images[i], dataOffset); + + dataOffset += images[i].byteLength; + } + + return buffer; +} + export class LinkHandlerParent extends JSWindowActorParent { static addListenerForTests(listener) { gTestListeners.add(listener); @@ -96,7 +160,7 @@ export class LinkHandlerParent extends JSWindowActorParent { tab.removeAttribute("pendingicon"); } - setIconFromLink( + async setIconFromLink( gBrowser, browser, { @@ -104,6 +168,7 @@ export class LinkHandlerParent extends JSWindowActorParent { originalURL, expiration, iconURL, + images, canStoreIcon, beforePageShow, isRichIcon, @@ -114,6 +179,35 @@ export class LinkHandlerParent extends JSWindowActorParent { return; } + if (images) { + let canvas = tab.ownerDocument.createElement("canvas"); + + // We have multiple images, need to create an ICO file to collect them. + if (images.length > 1) { + // Convert all images to PNG bytes. + let blobs = []; + for (let image of images) { + await drawImageOnCanvas(canvas, image); + blobs.push(await new Promise(resolve => canvas.toBlob(resolve))); + } + let buffers = await Promise.all(blobs.map(blob => blob.bytes())); + + // Create an ICO "file" containing all the PNGs. + let ico = createICO(buffers); + + // Convert the ICO bytes to a data URL. + iconURL = await blobAsDataURL(new Blob([ico], { type: TYPE_ICO })); + } else { + await drawImageOnCanvas(canvas, images[0]); + iconURL = canvas.toDataURL(); + } + } + + // The browser might have gone away during `await` above. + if (!gBrowser.getBrowserForTab(tab)) { + return; + } + if (!isRichIcon) { this.clearPendingIcon(gBrowser, browser); } @@ -125,6 +219,19 @@ export class LinkHandlerParent extends JSWindowActorParent { console.error(ex); return; } + + // The content process should send decoded images for all schemes except for trusted schemes and SVGs, which should not be rasterized. + if ( + !images && + !TRUSTED_FAVICON_SCHEMES.includes(iconURI.scheme) && + !iconURL.startsWith(`data:${TYPE_SVG};base64,`) + ) { + console.error( + `Not allowed to set favicon "${iconURL}" with that scheme!` + ); + return; + } + if (!iconURI.schemeIs("data")) { try { Services.scriptSecurityManager.checkLoadURIWithPrincipal( diff --git a/browser/base/content/test/favicons/browser.toml b/browser/base/content/test/favicons/browser.toml @@ -106,6 +106,12 @@ support-files = [ "file_favicon.png", ] +["browser_multiple_ico.js"] +support-files = [ + "file_multiple_ico.html", + "file_multiple.ico", +] + ["browser_multiple_icons_in_short_timeframe.js"] ["browser_oversized.js"] diff --git a/browser/base/content/test/favicons/browser_favicon_empty_data.js b/browser/base/content/test/favicons/browser_favicon_empty_data.js @@ -8,10 +8,11 @@ const TEST_ROOT = const PAGE_URL = TEST_ROOT + "blank.html"; const ICON_URL = TEST_ROOT + "file_bug970276_favicon1.ico"; -const ICON_DATAURI_START = "data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAA"; +const ICON_DATAURI = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABx0lEQVQ4T6WSMWsbQRCFP4EEKqTCBAwJpFkV+QGbStUh0igolVAjUohgzpUrBQ5cyFiFYCEuU/hQ4cJVVCblFfcDsj8gzZUJXGMEVwouzGrvYqGEBPJgudnZeW/e7F6D/0SjjjxWq1W5Xq85yTLQmvF4TL/fJwiCo1pBndxut+VgMABruQUsoIHYx4KbND0Scpu7u/vy7GzGu93OkYTwDLjScG3hO5AoRZ7nFEVxLNBsNkv5ikDVXZSshR/ACI2ZKKy1RFnG+SPnDSEvl0sWiwUfdztCf3DNvnPohaq8iMtYUz9OoyzLstvt8qIouNWg7b6gIghE7Km/E1kCqTFK/XLw5PLyoPuVjwUvgW+dDsPhkE+bjcs5J1rjHPR6PfdsIiBLCG8eiYjgZx+/iiIekgRtrRNxDpIkYTQa8b4oHEkOxKoQBVXui7+XkyjCJIZQh3sHrVaLdrvNTVEczC6k16enPM9z1GSCUgplEmJtubi4ZzZ726jvQP62eRAcWBeBc2/bGHPw/hXqZBRFZWIMX/3e3bwG86DIsuy3ZEF9kKZpOZ/PCa11Y1Tdxfpms/m7QAURSoPAxR86HabTKXEcH9VV+OPBv+In4P+u1zGvpjQAAAAASUVORK5CYII="; const EMPTY_PAGE_URL = TEST_ROOT + "file_favicon_empty.html"; -const EMPTY_ICON_URL = "data:image/x-icon"; +const EMPTY_ICON_URL = "about:blank"; add_task(async function () { await BrowserTestUtils.withNewTab( @@ -21,10 +22,7 @@ add_task(async function () { .getTabForBrowser(browser) .querySelector(".tab-icon-image"); await addContentLinkForIconUrl(ICON_URL, browser); - Assert.ok( - browser.mIconURL.startsWith(ICON_DATAURI_START), - "Favicon is correctly set." - ); + Assert.equal(browser.mIconURL, ICON_DATAURI, "Favicon is correctly set."); // Give some time to ensure the icon is rendered. /* eslint-disable mozilla/no-arbitrary-setTimeout */ diff --git a/browser/base/content/test/favicons/browser_multiple_ico.js b/browser/base/content/test/favicons/browser_multiple_ico.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TEST_ROOT = + "http://mochi.test:8888/browser/browser/base/content/test/favicons/"; + +const PAGE_URL = TEST_ROOT + "file_multiple_ico.html"; +// An ICO file with three BMP images +const ICO_WITH_BMP_URL = TEST_ROOT + "file_multiple.ico"; +// An ICO file with three PNG images (with the same colors/sizes as the file above) +const ICO_WITH_PNG_DATA_URL = `data:image/x-icon;base64,AAABAAMAAAAAAAEAIABNAAAANgAAAAAAAAABACAATAAAAIMAAAAAAAAAAQAgAEwAAADPAAAAiVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFElEQVQIW2NkYPj/n4GBgYERxgAAKAUD/zojOmwAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAABAAAAAQIBgAAAKnxnn4AAAATSURBVAhbY2T4z/ifAQkwki4AADxfCAHpZAkkAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAE0lEQVQYV2P8x8DwnwEPYBwZCgAYdw/xpf5XXwAAAABJRU5ErkJggg==`; + +add_task(async function () { + await BrowserTestUtils.withNewTab( + { gBrowser, url: PAGE_URL }, + async browser => { + await waitForFavicon(browser, ICO_WITH_BMP_URL); + is( + browser.mIconURL, + ICO_WITH_PNG_DATA_URL, + "Got PNG ICO with correct image data" + ); + } + ); +}); diff --git a/browser/base/content/test/favicons/file_favicon_empty.html b/browser/base/content/test/favicons/file_favicon_empty.html @@ -3,7 +3,7 @@ <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> - <link rel="icon" href="data:image/x-icon" type="image/ico" id="i"> + <link rel="icon" href="about:blank" type="image/ico" id="i"> </head> <body> diff --git a/browser/base/content/test/favicons/file_multiple.ico b/browser/base/content/test/favicons/file_multiple.ico Binary files differ. diff --git a/browser/base/content/test/favicons/file_multiple_ico.html b/browser/base/content/test/favicons/file_multiple_ico.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> + +<head> + <meta http-equiv="content-type" content="text/html; charset=utf-8"> + <link rel="icon" href="file_multiple.ico" type="image/ico" id="i"> +</head> + +<body> +</body> + +</html> diff --git a/browser/base/content/test/favicons/head.js b/browser/base/content/test/favicons/head.js @@ -34,7 +34,6 @@ function waitForFaviconMessage(isTabIcon = undefined, expectedURL = undefined) { if (name == "SetIcon") { resolve({ iconURL: data.originalURL, - dataURL: data.iconURL, isRichIcon: data.isRichIcon, }); } else { diff --git a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js @@ -30,7 +30,7 @@ const FAVICON_URI = TEST_DIRECTORY + "file_favicon.png"; const TEST_FAVICON_CACHE_URI = TEST_DIRECTORY + "file_favicon_cache.png"; const ICON_DATA = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABH0lEQVRYw2P8////f4YBBEwMAwxGHcBCUMX/91DGOSj/BpT/DkpzQChGBSjfBErLQsVZhmoI/L8LpRdD6X1QietQGhYy7FB5aAgwmkLpBKi4BZTPMThDgBGjHIDF+f9mKD0fKvGBRKNdoF7sgPL1saaJwZgGDkJ9vpZMn8PAHqg5G9FyifBgD4H/W9HyOWrU/f+DIzHhkoeZxxgzZEIAVtJ9RxX+Q6DAxCmP3byhXxkxshAs5odqbcioAY3UC1CBLyTGOTqAmsfAOWRCwBvqxV0oIUB2OQAzDy3/D+a6wB7q8mCU2vD/nw94GziYIQOtDRn9oXz+IZMGBKGMbCjNh9Ii+v8HR4uIAUeLiEEbb9twELaIRlqrmHG0bzjiHQAA1LVfww8jwM4AAAAASUVORK5CYII="; + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABDElEQVRYR+2WsUoDQRRF31uEiG1qIVVaQRRs7PwQm9T5glRp8hcWFlr6BSljZ6tNGj9ALVIm+wK59zUjo+6CvFnYU+wZGNg9zA6zq2ZmEoj2AeUH2CcHL5C9wfJBH0M6guWCPoX0CM5QcICt6Xt6Ccsr7WEDmgF6Sd/CcgUpQxMKDPAltzl9B8sX/VduoGoByxmUvJISA57gegrLO90SndG8nw5hUmBAPYHtEZbN4eorZ1s4JT9/DlUPsI5hUmDA7pqDZ3pHt+UEqlawcjOS7gbklzylcwGZTdieppswPCBzEP225Nn5xgdRfMDPHyN/UMq3B7f/GEUHOGE/JE54gBP2U+qEB/wzfUB4wB7D3yOwzNKs4AAAAABJRU5ErkJggg=="; let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); diff --git a/browser/components/sessionstore/test/browser_tabicon_after_bg_tab_crash.js b/browser/components/sessionstore/test/browser_tabicon_after_bg_tab_crash.js @@ -1,7 +1,7 @@ "use strict"; const FAVICON = - "data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw=="; + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAARElEQVQYV2NkYLj3nwEnUGKEMxkYGBghilEFIQBTHKqYOIDFZHQ+DNz7j0MxdoDDGThNRgfoNsEATsXYARbFuAHtFAMAuvMbOrNomdAAAAAASUVORK5CYII="; const PAGE_URL = `data:text/html, <html> <head> diff --git a/browser/components/urlbar/tests/browser/browser_add_search_engine.js b/browser/components/urlbar/tests/browser/browser_add_search_engine.js @@ -29,7 +29,10 @@ add_task(async function context_none() { add_task(async function context_one() { info("Checks the context menu with a page that offers one engine."); let url = getRootDirectory(gTestPath) + "add_search_engine_one.html"; - await BrowserTestUtils.withNewTab(url, async () => { + await BrowserTestUtils.withNewTab(url, async browser => { + info("Waiting for favicon to load"); + await BrowserTestUtils.waitForCondition(() => !!browser.mIconURL); + await UrlbarTestUtils.withContextMenu(window, async popup => { info("The separator and the add engine item should be present."); let elt = popup.parentNode.getMenuItem("add-engine-separator"); @@ -170,7 +173,10 @@ add_task(async function context_two() { add_task(async function context_many() { info("Checks the context menu with a page that offers many engines."); let url = getRootDirectory(gTestPath) + "add_search_engine_many.html"; - await BrowserTestUtils.withNewTab(url, async () => { + await BrowserTestUtils.withNewTab(url, async browser => { + info("Waiting for favicon to load"); + await BrowserTestUtils.waitForCondition(() => !!browser.mIconURL); + await UrlbarTestUtils.withContextMenu(window, async popup => { info("The separator and the add engine menu should be present."); let separator = popup.parentNode.getMenuItem("add-engine-separator"); diff --git a/browser/modules/FaviconLoader.sys.mjs b/browser/modules/FaviconLoader.sys.mjs @@ -2,6 +2,16 @@ * 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/. */ +// Bug 1924775 - ESLint doesn't yet know about `ImageDecoder`. +/* globals ImageDecoder:false */ + +import { + TYPE_SVG, + TYPE_ICO, + TRUSTED_FAVICON_SCHEMES, + blobAsDataURL, +} from "moz-src:///browser/modules/FaviconUtils.sys.mjs"; + const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { @@ -38,42 +48,92 @@ const FAVICON_PARSING_TIMEOUT = 100; const FAVICON_RICH_ICON_MIN_WIDTH = 96; const PREFERRED_WIDTH = 16; -// URL schemes that we don't want to load and convert to data URLs. -const LOCAL_FAVICON_SCHEMES = ["chrome", "about", "resource", "data"]; - const MAX_FAVICON_EXPIRATION = 7 * 24 * 60 * 60 * 1000; const MAX_ICON_SIZE = 2048; -const TYPE_ICO = "image/x-icon"; -const TYPE_SVG = "image/svg+xml"; +async function decodeImage({ + url, + type, + data, + transfer, + desiredWidth, + desiredHeight, +}) { + let image; + try { + let decoder = new ImageDecoder({ + type, + data, + desiredWidth, + desiredHeight, + transfer: transfer ? [data] : undefined, + }); -function promiseBlobAsDataURL(blob) { - return new Promise((resolve, reject) => { - let reader = new FileReader(); - reader.addEventListener("load", () => resolve(reader.result)); - reader.addEventListener("error", reject); - reader.readAsDataURL(blob); - }); -} + let result = await decoder.decode({ completeFramesOnly: true }); + image = result.image; + } catch { + throw Components.Exception( + `Favicon at "${url}" could not be decoded.`, + Cr.NS_ERROR_FAILURE + ); + } -function promiseImage(stream, type) { - return new Promise((resolve, reject) => { - let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools); + if ( + image.displayWidth > MAX_ICON_SIZE || + image.displayHeight > MAX_ICON_SIZE + ) { + throw Components.Exception( + `Favicon at "${url}" is too large.`, + Cr.NS_ERROR_FAILURE + ); + } - imgTools.decodeImageAsync( - stream, - type, - (image, result) => { - if (!Components.isSuccessCode(result)) { - reject(); - return; - } + let imageBuffer = new ArrayBuffer(image.allocationSize()); + await image.copyTo(imageBuffer); + return { + blob: new Blob([imageBuffer]), + format: image.format, + displayWidth: image.displayWidth, + displayHeight: image.displayHeight, + }; +} - resolve(image); - }, - Services.tm.currentThread - ); +// Convert image data bytes to an array of blobs with associated format/size info. +async function convertImage(url, type, data) { + if (type == TYPE_ICO) { + try { + let decoder = new ImageDecoder({ + type, + data, + }); + await decoder.tracks.ready; + let sizes = decoder.tracks[0].getSizes(); + if (sizes.length > 1) { + return Promise.all( + sizes.map(({ width, height }) => + decodeImage({ + url, + type, + // Can't transfer the data buffer, because we decode multiple times. + transfer: false, + data, + // Decode the ICO image at the different sizes of contained images. + desiredWidth: width, + desiredHeight: height, + }) + ) + ); + } + } catch {} + } + + let image = await decodeImage({ + url, + type, + transfer: true, + data, }); + return [image]; } class FaviconLoad { @@ -279,8 +339,7 @@ class FaviconLoad { stream.readArrayBuffer(buffer.byteLength, buffer); let type = this.channel.contentType; - let blob = new Blob([buffer], { type }); - + let images, dataURL; if (type != "image/svg+xml") { let octets = new Uint8Array(buffer); let sniffer = Cc["@mozilla.org/image/loader;1"].createInstance( @@ -299,30 +358,14 @@ class FaviconLoad { ); } - blob = blob.slice(0, blob.size, type); - - let image; - try { - image = await promiseImage(this.dataBuffer.newInputStream(0), type); - } catch (e) { - throw Components.Exception( - `Favicon at "${this.icon.iconUri.spec}" could not be decoded.`, - Cr.NS_ERROR_FAILURE - ); - } - - if (image.width > MAX_ICON_SIZE || image.height > MAX_ICON_SIZE) { - throw Components.Exception( - `Favicon at "${this.icon.iconUri.spec}" is too large.`, - Cr.NS_ERROR_FAILURE - ); - } + images = await convertImage(this.icon.iconUri.spec, type, buffer); + } else { + dataURL = await blobAsDataURL(new Blob([buffer], { type })); } - let dataURL = await promiseBlobAsDataURL(blob); - this._deferred.resolve({ expiration, + images, dataURL, canStoreIcon, }); @@ -521,7 +564,7 @@ class IconLoader { this._loader.cancel(); } - if (LOCAL_FAVICON_SCHEMES.includes(iconInfo.iconUri.scheme)) { + if (TRUSTED_FAVICON_SCHEMES.includes(iconInfo.iconUri.scheme)) { // We need to do a manual security check because the channel won't do // it for us. try { @@ -554,13 +597,15 @@ class IconLoader { try { this._loader = new FaviconLoad(iconInfo); - let { dataURL, expiration, canStoreIcon } = await this._loader.load(); + let { dataURL, images, expiration, canStoreIcon } = + await this._loader.load(); this.actor.sendAsyncMessage("Link:SetIcon", { pageURL: iconInfo.pageUri.spec, originalURL: iconInfo.iconUri.spec, expiration, iconURL: dataURL, + images, canStoreIcon, beforePageShow: iconInfo.beforePageShow, isRichIcon: iconInfo.isRichIcon, diff --git a/browser/modules/FaviconUtils.sys.mjs b/browser/modules/FaviconUtils.sys.mjs @@ -0,0 +1,29 @@ +/* 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 const TYPE_ICO = "image/x-icon"; +export const TYPE_SVG = "image/svg+xml"; + +// URL schemes that we don't want to load and convert to data URLs. +export const TRUSTED_FAVICON_SCHEMES = Object.freeze([ + "chrome", + "about", + "resource", +]); + +/** + * Converts a Blob into a data: URL. + * + * @param {Blob} blob The Blob to be converted. + * @returns {Promise<string>} Returns a promise that resolves with the data: URL + * or rejects with an error. + */ +export function blobAsDataURL(blob) { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + reader.addEventListener("load", () => resolve(reader.result)); + reader.addEventListener("error", reject); + reader.readAsDataURL(blob); + }); +} diff --git a/browser/modules/moz.build b/browser/modules/moz.build @@ -149,6 +149,7 @@ EXTRA_JS_MODULES += [ MOZ_SRC_FILES += [ "CanvasPermissionPromptHelper.sys.mjs", "ContextId.sys.mjs", + "FaviconUtils.sys.mjs", "ObserverForwarder.sys.mjs", "PrivateBrowsingUI.sys.mjs", "UnexpectedScriptObserver.sys.mjs", diff --git a/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_tab_favicons.js b/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_tab_favicons.js @@ -16,10 +16,7 @@ const TAB_URL = // This is the same png data-url as the one used in test-tab-favicons.html. const EXPECTED_FAVICON = - "data:image/png;base64," + - "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAATklEQVRYhe3SIQ4AI" + - "BADwf7/04elBAtrVlSduGnSTDJ7cuT1PQJwwO+Hl7sAGAA07gjAAfgIBeAAoH" + - "FHAA7ARygABwCNOwJwAD5CATRgAYXh+kypw86nAAAAAElFTkSuQmCC"; + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAXElEQVRYR+3SMQoAIAxD0fb+h9ZBEM3SKfwlTkISeEN7Va16Xt/feV9oyDsAHKCdaeDIA2AB+BEGgAO0Mw0ceQAsAD/CAHCAdqaBIw+ABeBHGAAO0M40cOQBoIANOAOf8d5vwtsAAAAASUVORK5CYII="; add_task(async function () { const faviconTab = await addTab(TAB_URL, { background: true }); diff --git a/devtools/client/aboutdebugging/test/browser/test-tab-favicons.html b/devtools/client/aboutdebugging/test/browser/test-tab-favicons.html @@ -2,7 +2,7 @@ <html> <head> <title>Favicon tab</title> - <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAATklEQVRYhe3SIQ4AIBADwf7/04elBAtrVlSduGnSTDJ7cuT1PQJwwO+Hl7sAGAA07gjAAfgIBeAAoHFHAA7ARygABwCNOwJwAD5CATRgAYXh+kypw86nAAAAAElFTkSuQmCC"> + <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAXElEQVRYR+3SMQoAIAxD0fb+h9ZBEM3SKfwlTkISeEN7Va16Xt/feV9oyDsAHKCdaeDIA2AB+BEGgAO0Mw0ceQAsAD/CAHCAdqaBIw+ABeBHGAAO0M40cOQBoIANOAOf8d5vwtsAAAAASUVORK5CYII="> <head> <body>Some page with a favicon</body> </html> diff --git a/toolkit/components/reader/tests/browser/browser_readerMode.js b/toolkit/components/reader/tests/browser/browser_readerMode.js @@ -50,8 +50,7 @@ add_task(async function test_reader_button() { let url = TEST_PATH + "readerModeArticle.html"; // Set up favicon for testing. let favicon = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" + - "AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="; + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg=="; info("Adding visit so we can add favicon"); await PlacesTestUtils.addVisits(new URL(url)); info("Adding favicon");