Bug 1901602 - Add Twitter SmartBlock Embeds shim. r=emz,webcompat-reviewers,twisniewski
Differential Revision: https://phabricator.services.mozilla.com/D239815
This commit is contained in:
@@ -1397,6 +1397,11 @@ var gProtectionsHandler = {
|
|||||||
shimId: "TiktokEmbed",
|
shimId: "TiktokEmbed",
|
||||||
displayName: "Tiktok",
|
displayName: "Tiktok",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
sites: ["https://platform.twitter.com"],
|
||||||
|
shimId: "TwitterEmbed",
|
||||||
|
displayName: "X",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1025,6 +1025,33 @@ const AVAILABLE_SHIMS = [
|
|||||||
["*://steam.tv/*", "*://checkout.steampowered.com/*"],
|
["*://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") {
|
if (typeof module !== "undefined") {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Web Compatibility Interventions",
|
"name": "Web Compatibility Interventions",
|
||||||
"description": "Urgent post-release fixes for web compatibility.",
|
"description": "Urgent post-release fixes for web compatibility.",
|
||||||
"version": "138.2.0",
|
"version": "138.3.0",
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "webcompat@mozilla.org",
|
"id": "webcompat@mozilla.org",
|
||||||
@@ -164,10 +164,12 @@
|
|||||||
"shims/tiktok.svg",
|
"shims/tiktok.svg",
|
||||||
"shims/tracking-pixel.png",
|
"shims/tracking-pixel.png",
|
||||||
"shims/tsn-ca.js",
|
"shims/tsn-ca.js",
|
||||||
|
"shims/twitter-embed.js",
|
||||||
"shims/vast2.xml",
|
"shims/vast2.xml",
|
||||||
"shims/vast3.xml",
|
"shims/vast3.xml",
|
||||||
"shims/vidible.js",
|
"shims/vidible.js",
|
||||||
"shims/vmad.xml",
|
"shims/vmad.xml",
|
||||||
"shims/webtrends.js"
|
"shims/webtrends.js",
|
||||||
|
"shims/x-logo.svg"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
237
browser/extensions/webcompat/shims/twitter-embed.js
Normal file
237
browser/extensions/webcompat/shims/twitter-embed.js
Normal file
@@ -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 = `
|
||||||
|
<style>
|
||||||
|
#smartblock-placeholder-wrapper {
|
||||||
|
min-height: 225px;
|
||||||
|
width: 400px;
|
||||||
|
padding: 32px 24px;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
background-color: light-dark(rgb(255, 255, 255), rgb(28, 27, 34));
|
||||||
|
color: light-dark(rgb(43, 42, 51), rgb(251, 251, 254));
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px dashed #0250bb;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.2;
|
||||||
|
font-family: system-ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
#smartblock-placeholder-button {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 600;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
/* Colours match light/dark theme from
|
||||||
|
https://searchfox.org/mozilla-central/source/browser/themes/addons/light/manifest.json
|
||||||
|
https://searchfox.org/mozilla-central/source/browser/themes/addons/dark/manifest.json */
|
||||||
|
background-color: light-dark(rgb(0, 97, 224), rgb(0, 221, 255));
|
||||||
|
color: light-dark(rgb(251, 251, 254), rgb(43, 42, 51));
|
||||||
|
}
|
||||||
|
|
||||||
|
#smartblock-placeholder-button:hover {
|
||||||
|
/* Colours match light/dark theme from
|
||||||
|
https://searchfox.org/mozilla-central/source/browser/themes/addons/light/manifest.json
|
||||||
|
https://searchfox.org/mozilla-central/source/browser/themes/addons/dark/manifest.json */
|
||||||
|
background-color: light-dark(rgb(2, 80, 187), rgb(128, 235, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
#smartblock-placeholder-button:hover:active {
|
||||||
|
/* Colours match light/dark theme from
|
||||||
|
https://searchfox.org/mozilla-central/source/browser/themes/addons/light/manifest.json
|
||||||
|
https://searchfox.org/mozilla-central/source/browser/themes/addons/dark/manifest.json */
|
||||||
|
background-color: light-dark(rgb(5, 62, 148), rgb(170, 242, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
#smartblock-placeholder-title {
|
||||||
|
margin-block: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#smartblock-placeholder-desc {
|
||||||
|
margin-block: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="smartblock-placeholder-wrapper">
|
||||||
|
<img id="smartblock-placeholder-image" width="24" height="24" />
|
||||||
|
<p id="smartblock-placeholder-title"></p>
|
||||||
|
<p id="smartblock-placeholder-desc"></p>
|
||||||
|
<button id="smartblock-placeholder-button"></button>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
// Create the placeholder inside a shadow dom
|
||||||
|
const placeholderDiv = document.createElement("div");
|
||||||
|
|
||||||
|
const shadowRoot = placeholderDiv.attachShadow({ mode: "closed" });
|
||||||
|
|
||||||
|
shadowRoot.innerHTML = SMARTBLOCK_PLACEHOLDER_HTML_STRING;
|
||||||
|
shadowRoot.getElementById("smartblock-placeholder-image").src = LOGO_URL;
|
||||||
|
shadowRoot.getElementById("smartblock-placeholder-title").textContent =
|
||||||
|
titleString;
|
||||||
|
shadowRoot.getElementById("smartblock-placeholder-desc").textContent =
|
||||||
|
descriptionString;
|
||||||
|
shadowRoot.getElementById("smartblock-placeholder-button").textContent =
|
||||||
|
buttonString;
|
||||||
|
|
||||||
|
// Wait for user to opt-in.
|
||||||
|
shadowRoot
|
||||||
|
.getElementById("smartblock-placeholder-button")
|
||||||
|
.addEventListener("click", ({ isTrusted }) => {
|
||||||
|
if (!isTrusted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Send a message to the addon to allow loading tracking resources
|
||||||
|
// needed by the embed.
|
||||||
|
sendMessageToAddon("embedClicked");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save the original embed element and the newly created placeholder
|
||||||
|
embedPlaceholders.push(placeholderDiv);
|
||||||
|
originalEmbedContainers.push(originalContainer);
|
||||||
|
|
||||||
|
// Replace the embed with the placeholder
|
||||||
|
originalContainer.replaceWith(placeholderDiv);
|
||||||
|
|
||||||
|
sendMessageToAddon("smartblockEmbedReplaced");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for messages from the background script.
|
||||||
|
browser.runtime.onMessage.addListener(request => {
|
||||||
|
addonMessageHandler(request);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Monitor for new embeds being added after page load so we can replace them
|
||||||
|
// with placeholders.
|
||||||
|
newEmbedObserver = new MutationObserver(mutations => {
|
||||||
|
for (let { addedNodes, target, type } of mutations) {
|
||||||
|
const nodes = type === "attributes" ? [target] : addedNodes;
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (
|
||||||
|
SHIM_EMBED_CLASSES.some(className =>
|
||||||
|
node.classList?.contains(className)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// If node is an embed, replace with placeholder
|
||||||
|
createShimPlaceholders([node]);
|
||||||
|
} else {
|
||||||
|
// If node is not an embed, check if any children are
|
||||||
|
// and replace if needed
|
||||||
|
let maybeEmbedNodeList =
|
||||||
|
node.querySelectorAll?.(SHIM_CLASS_SELECTORS);
|
||||||
|
if (maybeEmbedNodeList) {
|
||||||
|
createShimPlaceholders(maybeEmbedNodeList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newEmbedObserver.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disconnect the mutation observer after a fixed (long) timeout to conserve resources.
|
||||||
|
observerTimeout = setTimeout(
|
||||||
|
() => newEmbedObserver.disconnect(),
|
||||||
|
OBSERVER_TIMEOUT_MS
|
||||||
|
);
|
||||||
|
|
||||||
|
createShimPlaceholders();
|
||||||
|
}
|
||||||
5
browser/extensions/webcompat/shims/x-logo.svg
Normal file
5
browser/extensions/webcompat/shims/x-logo.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- copyright is dedicated to the Public Domain.
|
||||||
|
https://commons.wikimedia.org/wiki/File:X_logo_2023_original.svg -->
|
||||||
|
<svg width="300" height="300.251" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M178.57 127.15 290.27 0h-26.46l-97.03 110.38L89.34 0H0l117.13 166.93L0 300.25h26.46l102.4-116.59 81.8 116.59h89.34M36.01 19.54H76.66l187.13 262.13h-40.66"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 381 B |
Reference in New Issue
Block a user