Skip to content
Open
Changes from all 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
119 changes: 83 additions & 36 deletions injected/src/features/element-hiding.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,52 @@
import ContentFeature from '../content-feature';
import { isBeingFramed, injectGlobalStyles } from '../utils';

/**
* @typedef {Object} ElementHidingValue
* @property {string} property
* @property {string} value
*/

/**
* @typedef {Object} ElementHidingRuleWithSelector
* @property {string} selector
* @property {'hide-empty' | 'hide' | 'closest-empty' | 'override' | 'modify-style' | 'modify-attr'} type
* @property {ElementHidingValue[]} [values]
*/

/**
* @typedef {Object} ElementHidingRuleWithoutSelector
* @property {'disable-default'} type
*/

/**
* @typedef {ElementHidingRuleWithSelector | ElementHidingRuleWithoutSelector} ElementHidingRule
*/

/**
* @typedef {Object} ElementHidingDomain
* @property {string | string[]} domain
* @property {ElementHidingRule[]} rules
*/

/**
* @typedef {Object} StyleTagException
* @property {string} domain
* @property {string} reason
*/

/**
* @typedef {Object} ElementHidingConfiguration
* @property {boolean} [useStrictHideStyleTag]
* @property {ElementHidingRule[]} rules
* @property {ElementHidingDomain[]} domains
* @property {number[]} [hideTimeouts]
* @property {number[]} [unhideTimeouts]
* @property {string} [mediaAndFormSelectors]
* @property {string[]} [adLabelStrings]
* @property {StyleTagException[]} [styleTagExceptions]
*/

let adLabelStrings = [];
const parser = new DOMParser();
let hiddenElements = new WeakMap();
Expand All @@ -18,7 +64,7 @@ let featureInstance;
/**
* Hide DOM element if rule conditions met
* @param {HTMLElement} element
* @param {Object} rule
* @param {ElementHidingRule} rule
* @param {HTMLElement} [previousElement]
*/
function collapseDomNode(element, rule, previousElement) {
Expand Down Expand Up @@ -54,10 +100,14 @@ function collapseDomNode(element, rule, previousElement) {
}
break;
case 'modify-attr':
modifyAttribute(element, rule.values);
if (rule.values) {
modifyAttribute(element, rule.values);
}
break;
case 'modify-style':
modifyStyle(element, rule.values);
if (rule.values) {
modifyStyle(element, rule.values);
}
break;
default:
break;
Expand All @@ -67,7 +117,7 @@ function collapseDomNode(element, rule, previousElement) {
/**
* Unhide previously hidden DOM element if content loaded into it
* @param {HTMLElement} element
* @param {Object} rule
* @param {ElementHidingRule} rule
*/
function expandNonEmptyDomNode(element, rule) {
if (!element) {
Expand Down Expand Up @@ -185,9 +235,7 @@ function isDomNodeEmpty(node) {
/**
* Modify specified attribute(s) on element
* @param {HTMLElement} element
* @param {Object[]} values
* @param {string} values[].property
* @param {string} values[].value
* @param {ElementHidingValue[]} values
*/
function modifyAttribute(element, values) {
values.forEach((item) => {
Expand All @@ -199,9 +247,7 @@ function modifyAttribute(element, values) {
/**
* Modify specified style(s) on element
* @param {HTMLElement} element
* @param {Object[]} values
* @param {string} values[].property
* @param {string} values[].value
* @param {ElementHidingValue[]} values
*/
function modifyStyle(element, values) {
values.forEach((item) => {
Expand All @@ -212,9 +258,7 @@ function modifyStyle(element, values) {

/**
* Separate strict hide rules to inject as style tag if enabled
* @param {Object[]} rules
* @param {string} rules[].selector
* @param {string} rules[].type
* @param {ElementHidingRule[]} rules
*/
function extractTimeoutRules(rules) {
if (!shouldInjectStyleTag) {
Expand All @@ -238,9 +282,7 @@ function extractTimeoutRules(rules) {

/**
* Create styletag for strict hide rules and append it to the document
* @param {Object[]} rules
* @param {string} rules[].selector
* @param {string} rules[].type
* @param {ElementHidingRule[]} rules
*/
function injectStyleTag(rules) {
// if style tag already injected on SPA url change, don't inject again
Expand All @@ -252,10 +294,12 @@ function injectStyleTag(rules) {
let selector = '';

rules.forEach((rule, i) => {
if (i !== rules.length - 1) {
selector = selector.concat(rule.selector, ',');
} else {
selector = selector.concat(rule.selector);
if ('selector' in rule) {
if (i !== rules.length - 1) {
selector = selector.concat(rule.selector, ',');
} else {
selector = selector.concat(rule.selector);
}
}
});
const styleTagProperties = 'display:none!important;min-height:0!important;height:0!important;';
Expand All @@ -267,20 +311,20 @@ function injectStyleTag(rules) {

/**
* Apply list of active element hiding rules to page
* @param {Object[]} rules
* @param {string} rules[].selector
* @param {string} rules[].type
* @param {ElementHidingRule[]} rules
*/
function hideAdNodes(rules) {
const document = globalThis.document;

rules.forEach((rule) => {
const selector = forgivingSelector(rule.selector);
const matchingElementArray = [...document.querySelectorAll(selector)];
matchingElementArray.forEach((element) => {
// @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f
collapseDomNode(element, rule);
});
if ('selector' in rule) {
const selector = forgivingSelector(rule.selector);
const matchingElementArray = [...document.querySelectorAll(selector)];
matchingElementArray.forEach((element) => {
// @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f
collapseDomNode(element, rule);
});
}
});
}

Expand Down Expand Up @@ -317,14 +361,19 @@ export default class ElementHiding extends ContentFeature {
}

let activeRules;
const globalRules = this.getFeatureSetting('rules');
adLabelStrings = this.getFeatureSetting('adLabelStrings');
shouldInjectStyleTag = this.getFeatureSetting('useStrictHideStyleTag');
/** @type {ElementHidingRule[]} */
const globalRules = this.getFeatureSetting('rules') || [];
/** @type {string[]} */
adLabelStrings = this.getFeatureSetting('adLabelStrings') || [];
/** @type {boolean} */
shouldInjectStyleTag = this.getFeatureSetting('useStrictHideStyleTag') || false;
/** @type {number[]} */
hideTimeouts = this.getFeatureSetting('hideTimeouts') || hideTimeouts;
/** @type {number[]} */
unhideTimeouts = this.getFeatureSetting('unhideTimeouts') || unhideTimeouts;
/** @type {string} */
mediaAndFormSelectors = this.getFeatureSetting('mediaAndFormSelectors') || mediaAndFormSelectors;

// determine whether strict hide rules should be injected as a style tag
if (shouldInjectStyleTag) {
shouldInjectStyleTag = this.matchConditionalFeatureSetting('styleTagExceptions').length === 0;
}
Expand Down Expand Up @@ -377,9 +426,7 @@ export default class ElementHiding extends ContentFeature {

/**
* Apply relevant hiding rules to page at set intervals
* @param {Object[]} rules
* @param {string} rules[].selector
* @param {string} rules[].type
* @param {ElementHidingRule[]} rules
*/
applyRules(rules) {
const timeoutRules = extractTimeoutRules(rules);
Expand Down
Loading