diff --git a/browser/base/content/browser-siteProtections.js b/browser/base/content/browser-siteProtections.js index 2f9aea9645ae..962ebc026aca 100644 --- a/browser/base/content/browser-siteProtections.js +++ b/browser/base/content/browser-siteProtections.js @@ -1397,6 +1397,11 @@ var gProtectionsHandler = { shimId: "TiktokEmbed", displayName: "Tiktok", }, + { + sites: ["https://platform.twitter.com"], + shimId: "TwitterEmbed", + displayName: "X", + }, ], /** diff --git a/browser/extensions/webcompat/data/shims.js b/browser/extensions/webcompat/data/shims.js index cf4f3229bf6e..5c92ad281198 100644 --- a/browser/extensions/webcompat/data/shims.js +++ b/browser/extensions/webcompat/data/shims.js @@ -1025,6 +1025,33 @@ const AVAILABLE_SHIMS = [ ["*://steam.tv/*", "*://checkout.steampowered.com/*"], ], }, + { + id: "TwitterEmbed", + platform: "desktop", + name: "Twitter embed placeholder", + bug: "1901602", + runFirst: "twitter-embed.js", + // Blank stub file just so we run the script above when the matched script + // files get blocked. + file: "empty-script.js", + matches: ["https://platform.twitter.com/widgets.js"], + logos: ["x-logo.svg"], + needsShimHelpers: [ + "embedClicked", + "smartblockEmbedReplaced", + "smartblockGetFluentString", + ], + isSmartblockEmbedShim: true, + onlyIfBlockedByETP: true, + unblocksOnOptIn: [ + "*://platform.twitter.com/*", + "*://syndication.twitter.com/*", + "*://cdn.syndication.twimg.com/*", + "*://pbs.twimg.com/*", + "*://abs.twimg.com/*", + "*://abs-0.twimg.com/*", + ], + }, ]; if (typeof module !== "undefined") { diff --git a/browser/extensions/webcompat/manifest.json b/browser/extensions/webcompat/manifest.json index 4bdb5800862f..12942d38970c 100644 --- a/browser/extensions/webcompat/manifest.json +++ b/browser/extensions/webcompat/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Web Compatibility Interventions", "description": "Urgent post-release fixes for web compatibility.", - "version": "138.2.0", + "version": "138.3.0", "browser_specific_settings": { "gecko": { "id": "webcompat@mozilla.org", @@ -164,10 +164,12 @@ "shims/tiktok.svg", "shims/tracking-pixel.png", "shims/tsn-ca.js", + "shims/twitter-embed.js", "shims/vast2.xml", "shims/vast3.xml", "shims/vidible.js", "shims/vmad.xml", - "shims/webtrends.js" + "shims/webtrends.js", + "shims/x-logo.svg" ] } diff --git a/browser/extensions/webcompat/shims/twitter-embed.js b/browser/extensions/webcompat/shims/twitter-embed.js new file mode 100644 index 000000000000..ed47ceb80c21 --- /dev/null +++ b/browser/extensions/webcompat/shims/twitter-embed.js @@ -0,0 +1,237 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +/* globals browser */ + +if (!window.smartblockTwitterShimInitialized) { + // Guard against this script running multiple times + window.smartblockTwitterShimInitialized = true; + + const SHIM_ID = "TwitterEmbed"; + + const SHIM_EMBED_CLASSES = ["twitter-tweet", "twitter-timeline"]; + const SHIM_CLASS_SELECTORS = SHIM_EMBED_CLASSES.map( + className => `.${className}` + ).join(","); + + // Original URL of the embed script. + const ORIGINAL_URL = "https://platform.twitter.com/widgets.js"; + const LOGO_URL = "https://smartblock.firefox.etp/x-logo.svg"; + + // Timeout for observing new changes to the page + const OBSERVER_TIMEOUT_MS = 10000; + let observerTimeout; + let newEmbedObserver; + + let originalEmbedContainers = []; + let embedPlaceholders = []; + + function sendMessageToAddon(message) { + return browser.runtime.sendMessage({ message, shimId: SHIM_ID }); + } + + function addonMessageHandler(message) { + let { topic, shimId } = message; + // Only react to messages which are targeting this shim. + if (shimId != SHIM_ID) { + return; + } + + if (topic === "smartblock:unblock-embed") { + if (newEmbedObserver) { + newEmbedObserver.disconnect(); + newEmbedObserver = null; + } + + if (observerTimeout) { + clearTimeout(observerTimeout); + } + + // remove embed placeholders + embedPlaceholders.forEach((p, idx) => { + p.replaceWith(originalEmbedContainers[idx]); + }); + + // recreate scripts + let scriptElement = document.createElement("script"); + + // Set the script element's src with the website's principal instead of + // the content script principal to ensure the tracker script is not loaded + // via the content script's expanded principal. + scriptElement.wrappedJSObject.src = ORIGINAL_URL; + document.body.appendChild(scriptElement); + } + } + + /** + * Replaces embeds with a SmartBlock Embed placeholder. Optionally takes a list + * of embeds to replace, otherwise will search for all embeds on the page. + * + * @param {HTMLElement[]} embedContainers - Array of elements to replace with placeholders. + * If the array is empty, this function will search + * for and replace all embeds on the page. + */ + async function createShimPlaceholders(embedContainers = []) { + const [titleString, descriptionString, buttonString] = + await sendMessageToAddon("smartblockGetFluentString"); + + if (!embedContainers.length) { + // No containers were passed in, do own search for containers + embedContainers = document.querySelectorAll(SHIM_CLASS_SELECTORS); + } + + embedContainers.forEach(originalContainer => { + // this string has to be defined within this function to avoid linting errors + // see: https://github.com/mozilla/eslint-plugin-no-unsanitized/issues/259 + const SMARTBLOCK_PLACEHOLDER_HTML_STRING = ` + +