Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use MutationObserver to listen for DOM changes. #736

Merged
merged 4 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 73 additions & 30 deletions src/content_script.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ const PASSIVE_SHARE_PATTERN_DETECTION_SELECTORS = [
"[href*='facebook.com/sharer']", // Legacy Share dialog
];

// Attributes distilled from selectors above. Update when necessary.
const OBSERVER_ATTRIBUTES = [
"action", "aria-label", "class",
"data-action", "data-bfa-network", "data-destination", "data-login-with-facebook",
"data-oauthserver", "data-partner", "data-tag", "data-test-id", "data-tracking",
"href", "id", "title"];
Comment on lines +78 to +82
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: This is so much cleaner to watch these attrs, rather the whole, dang DOM.


async function getLocalStorageSettingFromBackground(setting) {
// Send request to background determine if to show Relay email field prompt
Expand Down Expand Up @@ -129,7 +135,7 @@ function buildSettingsObject() {
}

async function updateSettings() {

let localStorage = await browser.storage.local.get();

if(!localStorage.settings) {
Expand Down Expand Up @@ -227,9 +233,9 @@ function createBadgeFragment (socialAction) {
dontShowAgainCheckboxForm.className = "fbc-badge-prompt-dontShowAgain-checkbox";
const dontShowAgainCheckboxInput = document.createElement("input");
dontShowAgainCheckboxInput.type = "checkbox";
dontShowAgainCheckboxInput.id = "hideRelayEmailBadges";
dontShowAgainCheckboxInput.id = "hideRelayEmailBadges";
dontShowAgainCheckboxInput.classList.add("fbc-badge-prompt-dontShowAgain-checkbox-input", "settings-checkbox");
const dontShowAgainCheckboxLabel = document.createElement("label");
const dontShowAgainCheckboxLabel = document.createElement("label");
dontShowAgainCheckboxLabel.htmlFor = "hideRelayEmailBadges";
const dontShowAgainCheckboxText = document.createTextNode( browser.i18n.getMessage("inPageUI-tooltip-prompt-checkbox") );

Expand Down Expand Up @@ -313,8 +319,8 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
if (!e.isTrusted) {
// The click was not user generated so ignore
return false;
}
}

if (allowClickSwitch) {
// Button disabled. Either will trigger new HTTP request or page will refresh.
setTimeout(()=>{
Expand All @@ -333,8 +339,8 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
if (!e.isTrusted) {
// The click was not user generated so ignore
return false;
}
}

e.preventDefault();
openLoginPrompt(e.target.parentElement, target, htmlBadgeDiv);
});
Expand All @@ -345,7 +351,7 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
// The click was not user generated so ignore
e.preventDefault();
return false;
}
}

allowClickSwitch = true;
browser.runtime.sendMessage({
Expand All @@ -360,7 +366,7 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
if (!e.isTrusted) {
// The click was not user generated so ignore
return false;
}
}
e.preventDefault();
document.body.classList.remove("js-fbc-prompt-active");
document.querySelector(".fbc-has-badge.js-fbc-prompt-active").classList.remove("js-fbc-prompt-active");
Expand All @@ -371,7 +377,7 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
if (!e.isTrusted) {
// The click was not user generated so ignore
return false;
}
}
e.preventDefault();
e.target.parentElement.classList.toggle("active");
positionPrompt( htmlBadgeDiv );
Expand All @@ -384,8 +390,8 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
if (!e.isTrusted) {
// The click was not user generated so ignore
return false;
}
}


window.open("https://relay.firefox.com/?utm_source=firefox&utm_medium=addon&utm_campaign=Facebook%20Container&utm_content=Try%20Firefox%20Relay");
});
Expand All @@ -395,8 +401,8 @@ function addFacebookBadge (target, badgeClassUId, socialAction) {
if (!e.isTrusted) {
// The click was not user generated so ignore
return false;
}
}

closePrompt();
const activeBadge = document.querySelector("." + badgeClassUId);
activeBadge.style.display = "none";
Expand Down Expand Up @@ -660,10 +666,10 @@ function parentIsBadged(target) {
// List of badge-able in-page elements
const facebookDetectedElementsArr = [];

function patternDetection(selectionArray, socialActionIntent){
function patternDetection(selectionArray, socialActionIntent, target) {
let querySelector = selectionArray.join(",");

for (let item of document.querySelectorAll(querySelector)) {
for (let item of target.querySelectorAll(querySelector)) {
// overlay the FBC icon badge on the item
if (!item.classList.contains("fbc-has-badge") && !isPinterest(item) && !parentIsBadged(item)) {
const itemUIDClassName = "fbc-UID_" + (facebookDetectedElementsArr.length + 1);
Expand All @@ -677,16 +683,16 @@ function patternDetection(selectionArray, socialActionIntent){
}
}

async function detectFacebookOnPage () {
async function detectFacebookOnPage(target) {
if (!checkForTrackers) {
return;
}

patternDetection(PASSIVE_SHARE_PATTERN_DETECTION_SELECTORS, "share-passive");
patternDetection(SHARE_PATTERN_DETECTION_SELECTORS, "share");
patternDetection(LOGIN_PATTERN_DETECTION_SELECTORS, "login");
patternDetection(PASSIVE_SHARE_PATTERN_DETECTION_SELECTORS, "share-passive", target);
patternDetection(SHARE_PATTERN_DETECTION_SELECTORS, "share", target);
patternDetection(LOGIN_PATTERN_DETECTION_SELECTORS, "login", target);
const relayAddonEnabled = await getRelayAddonEnabledFromBackground();

// Check if any FB trackers were blocked, scoped to only the active tab
const trackersDetectedOnCurrentPage = await checkIfTrackersAreDetectedOnCurrentPage();

Expand Down Expand Up @@ -731,12 +737,18 @@ function screenUpdate () {
}
}

let escapeListerenInitialized = false;

function escapeKeyListener () {
document.body.addEventListener("keydown", function(e) {
if(e.key === "Escape" && document.body.classList.contains("js-fbc-prompt-active")) {
closePrompt();
}
});
if (!escapeListerenInitialized) {
escapeListerenInitialized = true;

document.body.addEventListener("keydown", function(e) {
if(e.key === "Escape" && document.body.classList.contains("js-fbc-prompt-active")) {
closePrompt();
}
});
}
}

window.addEventListener("click", function(e){
Expand All @@ -747,7 +759,7 @@ window.addEventListener("click", function(e){
closePrompt();
}
} else {
contentScriptInit(true);
// contentScriptInit(true);
}
Comment on lines 761 to 763
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this entire else statement, if not in use?

});

Expand Down Expand Up @@ -782,7 +794,7 @@ browser.runtime.onMessage.addListener(message => {
// let callCount = 0;
let contentScriptDelay = 999;

async function contentScriptInit(resetSwitch, msg) {
async function contentScriptInit(resetSwitch, msg, target = document) {
// Second arg is for debugging to see which contentScriptInit fires
// Call count tracks number of times contentScriptInit has been called
// callCount = callCount + 1;
Expand All @@ -794,7 +806,7 @@ async function contentScriptInit(resetSwitch, msg) {

// Resource call is not in FBC/FB Domain and is a FB resource
if (checkForTrackers && msg !== "other-domain") {
await detectFacebookOnPage();
await detectFacebookOnPage(target);
screenUpdate();
}
}
Expand Down Expand Up @@ -833,15 +845,46 @@ async function CheckIfURLShouldBeBlocked() {
if (siteList.includes(site)) {
checkForTrackers = false;
} else {
// Initialize the content script the first time
await contentScriptInit(false);

// Reinitialize the content script for mutated nodes
const observer = new MutationObserver((mutations) => {
new Set(mutations.flatMap(mutation => {
switch (mutation.type) {
case "attributes":
return mutation.target;
case "childList":
return Array.from(mutation.addedNodes);
default:
return [];
}
})).forEach(target => contentScriptInit(false, null, target));
});

// Check for mutations in the entire document
observer.observe(document, {
childList: true,
attributes: true,
attributeFilter: OBSERVER_ATTRIBUTES,
subtree: true
});
}

}

// Cross-browser implementation of element.addEventListener()
function addPassiveWindowOnloadListener() {
window.addEventListener("load", function() {
CheckIfURLShouldBeBlocked();
// XXX: Work around slow test startup.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: (non-blocking) Can we change this from XXX to TODO/FIXME?

// In the real world it works fine without setTimeout.
CheckIfURLShouldBeBlocked().catch(() => {
setTimeout(() => {
CheckIfURLShouldBeBlocked().catch(() =>
setTimeout(CheckIfURLShouldBeBlocked, 1000));
}, 1000);
});
escapeKeyListener();
}, false);
}

Expand Down
1 change: 1 addition & 0 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"content_scripts": [
{
"matches": ["<all_urls>"],
"run_at": "document_start",
"js": ["content_script.js"],
"css": ["content_script.css"]
}
Expand Down