@@ -137,17 +137,27 @@ function isDomNodeEmpty (node) {
137
137
} )
138
138
139
139
const visibleText = parsedNode . innerText . trim ( ) . toLocaleLowerCase ( ) . replace ( / : $ / , '' )
140
- const mediaContent = parsedNode . querySelector ( 'video,canvas,picture' )
140
+ const mediaContent = parsedNode . querySelector ( 'video,canvas,embed,object,audio,map' )
141
+ const formContent = parsedNode . querySelector ( 'form,input,textarea,select,option,button' )
141
142
const frameElements = [ ...parsedNode . querySelectorAll ( 'iframe' ) ]
143
+ // query original node instead of parsedNode for img elements since heuristic relies
144
+ // on size of image elements
145
+ const imageElements = [ ...node . querySelectorAll ( 'img,svg' ) ]
142
146
// about:blank iframes don't count as content, return true if:
143
147
// - node doesn't contain any iframes
144
148
// - node contains iframes, all of which are hidden or have src='about:blank'
145
149
const noFramesWithContent = frameElements . every ( ( frame ) => {
146
150
return ( frame . hidden || frame . src === 'about:blank' )
147
151
} )
152
+ // ad containers often contain tracking pixels and other small images (eg adchoices logo).
153
+ // these should be treated as empty and hidden, but real images should not.
154
+ const visibleImages = imageElements . some ( ( image ) => {
155
+ return ( image . getBoundingClientRect ( ) . width > 20 || image . getBoundingClientRect ( ) . height > 20 )
156
+ } )
148
157
149
158
if ( ( visibleText === '' || adLabelStrings . includes ( visibleText ) ) &&
150
- noFramesWithContent && mediaContent === null ) {
159
+ mediaContent === null && formContent === null &&
160
+ noFramesWithContent && ! visibleImages ) {
151
161
return true
152
162
}
153
163
return false
0 commit comments