Skip to content

Commit b0596b6

Browse files
icodebyamandaeuw-arasolofotsara1
andauthored
Add TypeScript types to element-hiding feature (#2016)
* Add TypeScript types to element-hiding feature - Add JSDoc type definitions matching element-hiding.ts schema - Cast getFeatureSetting return values to proper types with fallbacks - Update function parameter types to use new JSDoc types - Add type guards for union types (ElementHidingRule) - Preserve all original comments from codebase - Fix TypeScript compilation errors with proper null checks This integrates the refactored schema types from privacy-configuration into the content-scope-scripts element-hiding feature for better type safety and maintainability. * Restore missing comment * Update element-hiding types and remove unnecessary selector guards - Update TypeScript definitions to match new privacy configuration schema - Split ElementHidingRuleWithSelector into ElementHidingRuleHide and ElementHidingRuleModify - Make values required for modify rules, remove from hide rules - Remove unnecessary 'selector' in rule guards from injectStyleTag() and hideAdNodes() - Add type assertions to clarify that these functions only receive rules with selectors - Improve code consistency and efficiency * resolve fallback inconsistency over typedef on line 367 * Clarify mediaAndFormSelectors fallback logic - Replace confusing self-referential fallback with explicit conditional - Add comment explaining fallback to default value when setting is missing - Make intent clear: fallback to hardcoded default, not preserve existing value - Addresses feedback about unclear fallback pattern * Remove null checks for modify rule values to surface configuration errors - Remove if (rule.values) checks from modify-attr and modify-style cases - Values property is required according to ElementHidingRuleModify typedef - Null checks were masking configuration errors by silently ignoring malformed rules - Now fails fast when values is missing, helping developers catch config issues early - Fixes JSDoc type mismatch between required values property and null checks * Fix mediaAndFormSelectors fallback to handle all falsy values --------- Co-authored-by: euw-arasolofotsara1 <arasolofotsara@duckduckgo.com>
1 parent 28c4640 commit b0596b6

File tree

1 file changed

+76
-27
lines changed

1 file changed

+76
-27
lines changed

injected/src/features/element-hiding.js

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,58 @@
11
import ContentFeature from '../content-feature';
22
import { isBeingFramed, injectGlobalStyles } from '../utils';
33

4+
/**
5+
* @typedef {Object} ElementHidingValue
6+
* @property {string} property
7+
* @property {string} value
8+
*/
9+
10+
/**
11+
* @typedef {Object} ElementHidingRuleHide
12+
* @property {string} selector
13+
* @property {'hide-empty' | 'hide' | 'closest-empty' | 'override'} type
14+
*/
15+
16+
/**
17+
* @typedef {Object} ElementHidingRuleModify
18+
* @property {string} selector
19+
* @property {'modify-style' | 'modify-attr'} type
20+
* @property {ElementHidingValue[]} values
21+
*/
22+
23+
/**
24+
* @typedef {Object} ElementHidingRuleWithoutSelector
25+
* @property {'disable-default'} type
26+
*/
27+
28+
/**
29+
* @typedef {ElementHidingRuleHide | ElementHidingRuleModify | ElementHidingRuleWithoutSelector} ElementHidingRule
30+
*/
31+
32+
/**
33+
* @typedef {Object} ElementHidingDomain
34+
* @property {string | string[]} domain
35+
* @property {ElementHidingRule[]} rules
36+
*/
37+
38+
/**
39+
* @typedef {Object} StyleTagException
40+
* @property {string} domain
41+
* @property {string} reason
42+
*/
43+
44+
/**
45+
* @typedef {Object} ElementHidingConfiguration
46+
* @property {boolean} [useStrictHideStyleTag]
47+
* @property {ElementHidingRule[]} rules
48+
* @property {ElementHidingDomain[]} domains
49+
* @property {number[]} [hideTimeouts]
50+
* @property {number[]} [unhideTimeouts]
51+
* @property {string} [mediaAndFormSelectors]
52+
* @property {string[]} [adLabelStrings]
53+
* @property {StyleTagException[]} [styleTagExceptions]
54+
*/
55+
456
let adLabelStrings = [];
557
const parser = new DOMParser();
658
let hiddenElements = new WeakMap();
@@ -18,7 +70,7 @@ let featureInstance;
1870
/**
1971
* Hide DOM element if rule conditions met
2072
* @param {HTMLElement} element
21-
* @param {Object} rule
73+
* @param {ElementHidingRule} rule
2274
* @param {HTMLElement} [previousElement]
2375
*/
2476
function collapseDomNode(element, rule, previousElement) {
@@ -67,7 +119,7 @@ function collapseDomNode(element, rule, previousElement) {
67119
/**
68120
* Unhide previously hidden DOM element if content loaded into it
69121
* @param {HTMLElement} element
70-
* @param {Object} rule
122+
* @param {ElementHidingRule} rule
71123
*/
72124
function expandNonEmptyDomNode(element, rule) {
73125
if (!element) {
@@ -185,9 +237,7 @@ function isDomNodeEmpty(node) {
185237
/**
186238
* Modify specified attribute(s) on element
187239
* @param {HTMLElement} element
188-
* @param {Object[]} values
189-
* @param {string} values[].property
190-
* @param {string} values[].value
240+
* @param {ElementHidingValue[]} values
191241
*/
192242
function modifyAttribute(element, values) {
193243
values.forEach((item) => {
@@ -199,9 +249,7 @@ function modifyAttribute(element, values) {
199249
/**
200250
* Modify specified style(s) on element
201251
* @param {HTMLElement} element
202-
* @param {Object[]} values
203-
* @param {string} values[].property
204-
* @param {string} values[].value
252+
* @param {ElementHidingValue[]} values
205253
*/
206254
function modifyStyle(element, values) {
207255
values.forEach((item) => {
@@ -212,9 +260,7 @@ function modifyStyle(element, values) {
212260

213261
/**
214262
* Separate strict hide rules to inject as style tag if enabled
215-
* @param {Object[]} rules
216-
* @param {string} rules[].selector
217-
* @param {string} rules[].type
263+
* @param {ElementHidingRule[]} rules
218264
*/
219265
function extractTimeoutRules(rules) {
220266
if (!shouldInjectStyleTag) {
@@ -238,9 +284,7 @@ function extractTimeoutRules(rules) {
238284

239285
/**
240286
* Create styletag for strict hide rules and append it to the document
241-
* @param {Object[]} rules
242-
* @param {string} rules[].selector
243-
* @param {string} rules[].type
287+
* @param {ElementHidingRule[]} rules
244288
*/
245289
function injectStyleTag(rules) {
246290
// if style tag already injected on SPA url change, don't inject again
@@ -253,9 +297,9 @@ function injectStyleTag(rules) {
253297

254298
rules.forEach((rule, i) => {
255299
if (i !== rules.length - 1) {
256-
selector = selector.concat(rule.selector, ',');
300+
selector = selector.concat(/** @type {ElementHidingRuleHide | ElementHidingRuleModify} */ (rule).selector, ',');
257301
} else {
258-
selector = selector.concat(rule.selector);
302+
selector = selector.concat(/** @type {ElementHidingRuleHide | ElementHidingRuleModify} */ (rule).selector);
259303
}
260304
});
261305
const styleTagProperties = 'display:none!important;min-height:0!important;height:0!important;';
@@ -267,15 +311,13 @@ function injectStyleTag(rules) {
267311

268312
/**
269313
* Apply list of active element hiding rules to page
270-
* @param {Object[]} rules
271-
* @param {string} rules[].selector
272-
* @param {string} rules[].type
314+
* @param {ElementHidingRule[]} rules
273315
*/
274316
function hideAdNodes(rules) {
275317
const document = globalThis.document;
276318

277319
rules.forEach((rule) => {
278-
const selector = forgivingSelector(rule.selector);
320+
const selector = forgivingSelector(/** @type {ElementHidingRuleHide | ElementHidingRuleModify} */ (rule).selector);
279321
const matchingElementArray = [...document.querySelectorAll(selector)];
280322
matchingElementArray.forEach((element) => {
281323
// @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f
@@ -317,14 +359,23 @@ export default class ElementHiding extends ContentFeature {
317359
}
318360

319361
let activeRules;
362+
/** @type {ElementHidingRule[]} */
320363
const globalRules = this.getFeatureSetting('rules');
321-
adLabelStrings = this.getFeatureSetting('adLabelStrings');
322-
shouldInjectStyleTag = this.getFeatureSetting('useStrictHideStyleTag');
364+
/** @type {string[]} */
365+
adLabelStrings = this.getFeatureSetting('adLabelStrings') || [];
366+
/** @type {boolean} */
367+
shouldInjectStyleTag = this.getFeatureSetting('useStrictHideStyleTag') || false;
368+
/** @type {number[]} */
323369
hideTimeouts = this.getFeatureSetting('hideTimeouts') || hideTimeouts;
370+
/** @type {number[]} */
324371
unhideTimeouts = this.getFeatureSetting('unhideTimeouts') || unhideTimeouts;
325-
mediaAndFormSelectors = this.getFeatureSetting('mediaAndFormSelectors') || mediaAndFormSelectors;
372+
/** @type {string} */
373+
mediaAndFormSelectors = this.getFeatureSetting('mediaAndFormSelectors');
374+
// Fall back to default value if setting is missing, null, empty, or other falsy values
375+
if (!mediaAndFormSelectors) {
376+
mediaAndFormSelectors = 'video,canvas,embed,object,audio,map,form,input,textarea,select,option,button';
377+
}
326378

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

378429
/**
379430
* Apply relevant hiding rules to page at set intervals
380-
* @param {Object[]} rules
381-
* @param {string} rules[].selector
382-
* @param {string} rules[].type
431+
* @param {ElementHidingRule[]} rules
383432
*/
384433
applyRules(rules) {
385434
const timeoutRules = extractTimeoutRules(rules);

0 commit comments

Comments
 (0)