OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 /* eslint-env webextensions */ | 18 /* eslint-env webextensions */ |
19 /* eslint no-console: "off" */ | 19 /* eslint no-console: "off" */ |
20 | 20 |
21 "use strict"; | 21 "use strict"; |
22 | 22 |
23 /** | 23 /** |
| 24 * Escapes regular expression special characters in a string. The returned |
| 25 * string may be passed to the <code>RegExp</code> constructor to match the |
| 26 * original string. |
| 27 * |
| 28 * @param {string} string The string in which to escape special characters. |
| 29 * |
| 30 * @returns {string} A new string with the special characters escaped. |
| 31 */ |
| 32 function regexEscape(string) |
| 33 { |
| 34 return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); |
| 35 } |
| 36 |
| 37 /** |
| 38 * Converts a given pattern to a regular expression. |
| 39 * |
| 40 * @param {string} pattern The pattern to convert. If the pattern begins and |
| 41 * ends with a slash (<code>/</code>), the text in between is treated as a |
| 42 * regular expression; otherwise the pattern is treated as raw text. |
| 43 * |
| 44 * @returns {RegExp} A <code>RegExp</code> object based on the given pattern. |
| 45 */ |
| 46 function toRegExp(pattern) |
| 47 { |
| 48 if (pattern.length >= 2 && pattern[0] == "/" && |
| 49 pattern[pattern.length - 1] == "/") |
| 50 { |
| 51 return new RegExp(pattern.substring(1, pattern.length - 1)); |
| 52 } |
| 53 |
| 54 return new RegExp(regexEscape(pattern)); |
| 55 } |
| 56 |
| 57 /** |
24 * Injects JavaScript code into the document using a temporary | 58 * Injects JavaScript code into the document using a temporary |
25 * <code>script</code> element. | 59 * <code>script</code> element. |
26 * | 60 * |
27 * @param {string} code The code to inject. | 61 * @param {string} code The code to inject. |
28 * @param {Array.<function|string>} [dependencies] A list of dependencies | 62 * @param {Array.<function|string>} [dependencies] A list of dependencies |
29 * to inject along with the code. A dependency may be either a function or a | 63 * to inject along with the code. A dependency may be either a function or a |
30 * string containing some executable code. | 64 * string containing some executable code. |
31 */ | 65 */ |
32 function injectCode(code, dependencies = []) | 66 function injectCode(code, dependencies = []) |
33 { | 67 { |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 window.uabDetect = true; | 213 window.uabDetect = true; |
180 } | 214 } |
181 | 215 |
182 exports["uabinject-defuser"] = makeInjector(uabinjectDefuser); | 216 exports["uabinject-defuser"] = makeInjector(uabinjectDefuser); |
183 | 217 |
184 /** | 218 /** |
185 * Hides any HTML element or one of its ancestors matching a CSS selector if | 219 * Hides any HTML element or one of its ancestors matching a CSS selector if |
186 * the text content of the element's shadow contains a given string. | 220 * the text content of the element's shadow contains a given string. |
187 * | 221 * |
188 * @param {string} search The string to look for in every HTML element's | 222 * @param {string} search The string to look for in every HTML element's |
189 * shadow. | 223 * shadow. If the string begins and ends with a slash (<code>/</code>), the |
| 224 * text in between is treated as a regular expression. |
190 * @param {string} selector The CSS selector that an HTML element must match | 225 * @param {string} selector The CSS selector that an HTML element must match |
191 * for it to be hidden. | 226 * for it to be hidden. |
192 */ | 227 */ |
193 function hideIfShadowContains(search, selector = "*") | 228 function hideIfShadowContains(search, selector = "*") |
194 { | 229 { |
195 let originalAttachShadow = Element.prototype.attachShadow; | 230 let originalAttachShadow = Element.prototype.attachShadow; |
196 | 231 |
197 // If there's no Element.attachShadow API present then we don't care, it must | 232 // If there's no Element.attachShadow API present then we don't care, it must |
198 // be Firefox or an older version of Chrome. | 233 // be Firefox or an older version of Chrome. |
199 if (!originalAttachShadow) | 234 if (!originalAttachShadow) |
200 return; | 235 return; |
201 | 236 |
| 237 let re = toRegExp(search); |
| 238 |
202 // Mutation observers mapped to their corresponding shadow roots and their | 239 // Mutation observers mapped to their corresponding shadow roots and their |
203 // hosts. | 240 // hosts. |
204 let shadows = new WeakMap(); | 241 let shadows = new WeakMap(); |
205 | 242 |
206 function observeShadow(mutations, observer) | 243 function observeShadow(mutations, observer) |
207 { | 244 { |
208 let {host, root} = shadows.get(observer) || {}; | 245 let {host, root} = shadows.get(observer) || {}; |
209 | 246 |
210 // Since it's a weak map, it's possible that either the element or its | 247 // Since it's a weak map, it's possible that either the element or its |
211 // shadow has been removed. | 248 // shadow has been removed. |
212 if (!host || !root) | 249 if (!host || !root) |
213 return; | 250 return; |
214 | 251 |
215 // If the shadow contains the given text, check if the host or one of its | 252 // If the shadow contains the given text, check if the host or one of its |
216 // ancestors matches the selector; if a matching element is found, hide | 253 // ancestors matches the selector; if a matching element is found, hide |
217 // it. | 254 // it. |
218 if (root.textContent.includes(search)) | 255 if (re.test(root.textContent)) |
219 { | 256 { |
220 let closest = host.closest(selector); | 257 let closest = host.closest(selector); |
221 if (closest) | 258 if (closest) |
222 hideElement(closest); | 259 hideElement(closest); |
223 } | 260 } |
224 } | 261 } |
225 | 262 |
226 Object.defineProperty(Element.prototype, "attachShadow", { | 263 Object.defineProperty(Element.prototype, "attachShadow", { |
227 value(...args) | 264 value(...args) |
228 { | 265 { |
(...skipping 14 matching lines...) Expand all Loading... |
243 // DOM, the mutation observer too will be freed eventually and the entry | 280 // DOM, the mutation observer too will be freed eventually and the entry |
244 // will be removed. | 281 // will be removed. |
245 shadows.set(observer, {host: this, root}); | 282 shadows.set(observer, {host: this, root}); |
246 | 283 |
247 return root; | 284 return root; |
248 } | 285 } |
249 }); | 286 }); |
250 } | 287 } |
251 | 288 |
252 exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains, | 289 exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains, |
| 290 toRegExp, regexEscape, |
253 hideElement); | 291 hideElement); |
254 | 292 |
255 /** | 293 /** |
256 * Hides any HTML element or one of its ancestors matching a CSS selector if | 294 * Hides any HTML element or one of its ancestors matching a CSS selector if |
257 * the text content of the element contains a given string. | 295 * the text content of the element contains a given string. |
258 * | 296 * |
259 * @param {string} search The string to look for in HTML elements. | 297 * @param {string} search The string to look for in HTML elements. If the |
| 298 * string begins and ends with a slash (<code>/</code>), the text in between |
| 299 * is treated as a regular expression. |
260 * @param {string} selector The CSS selector that an HTML element must match | 300 * @param {string} selector The CSS selector that an HTML element must match |
261 * for it to be hidden. | 301 * for it to be hidden. |
262 * @param {string?} [searchSelector] The CSS selector that an HTML element | 302 * @param {string?} [searchSelector] The CSS selector that an HTML element |
263 * containing the given string must match. Defaults to the value of the | 303 * containing the given string must match. Defaults to the value of the |
264 * <code>selector</code> argument. | 304 * <code>selector</code> argument. |
265 */ | 305 */ |
266 function hideIfContains(search, selector = "*", searchSelector = null) | 306 function hideIfContains(search, selector = "*", searchSelector = null) |
267 { | 307 { |
268 if (searchSelector == null) | 308 if (searchSelector == null) |
269 searchSelector = selector; | 309 searchSelector = selector; |
270 | 310 |
| 311 let re = toRegExp(search); |
| 312 |
271 new MutationObserver(() => | 313 new MutationObserver(() => |
272 { | 314 { |
273 for (let element of document.querySelectorAll(searchSelector)) | 315 for (let element of document.querySelectorAll(searchSelector)) |
274 { | 316 { |
275 if (element.textContent.includes(search)) | 317 if (re.test(element.textContent)) |
276 { | 318 { |
277 let closest = element.closest(selector); | 319 let closest = element.closest(selector); |
278 if (closest) | 320 if (closest) |
279 hideElement(closest); | 321 hideElement(closest); |
280 } | 322 } |
281 } | 323 } |
282 }) | 324 }) |
283 .observe(document, {childList: true, characterData: true, subtree: true}); | 325 .observe(document, {childList: true, characterData: true, subtree: true}); |
284 } | 326 } |
285 | 327 |
(...skipping 22 matching lines...) Expand all Loading... |
308 // We don't have the location of the element in its former parent, | 350 // We don't have the location of the element in its former parent, |
309 // but it's usually OK to just add it at the end. | 351 // but it's usually OK to just add it at the end. |
310 mutation.target.appendChild(node); | 352 mutation.target.appendChild(node); |
311 } | 353 } |
312 } | 354 } |
313 } | 355 } |
314 }); | 356 }); |
315 } | 357 } |
316 | 358 |
317 exports.readd = readd; | 359 exports.readd = readd; |
OLD | NEW |