| 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 |