| Left: | ||
| Right: |
| 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 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 90 * | 90 * |
| 91 * @returns {function} The generated injector. | 91 * @returns {function} The generated injector. |
| 92 */ | 92 */ |
| 93 function makeInjector(injectable, ...dependencies) | 93 function makeInjector(injectable, ...dependencies) |
| 94 { | 94 { |
| 95 return (...args) => injectCode(stringifyFunctionCall(injectable, ...args), | 95 return (...args) => injectCode(stringifyFunctionCall(injectable, ...args), |
| 96 dependencies); | 96 dependencies); |
| 97 } | 97 } |
| 98 | 98 |
| 99 /** | 99 /** |
| 100 * Hides an HTML element by settings its <code>style</code> attribute to | |
| 101 * <code>display: none !important</code>. | |
| 102 * | |
| 103 * @param {HTMLElement} element The HTML element to hide. | |
| 104 */ | |
| 105 function hideElement(element) | |
| 106 { | |
| 107 element.style.setProperty("display", "none", "important"); | |
| 108 | |
| 109 // Listen for changes to the style property and if our values are unset | |
| 110 // then reset them. | |
| 111 new MutationObserver(() => | |
|
kzar
2018/07/19 13:02:09
I wonder if we should just remove the element, ins
Manish Jethani
2018/07/19 13:30:09
I think it's probably best to mess as little as re
kzar
2018/07/19 13:38:11
Acknowledged.
| |
| 112 { | |
| 113 if (element.style.getPropertyValue("display") != "none" || | |
| 114 element.style.getPropertyPriority("display") != "important") | |
| 115 { | |
| 116 element.style.setProperty("display", "none", "important"); | |
| 117 } | |
| 118 }) | |
| 119 .observe(element, {attributes: true, attributeFilter: ["style"]}); | |
| 120 } | |
| 121 | |
| 122 /** | |
| 100 * Logs its arguments to the console. This may be used for testing and | 123 * Logs its arguments to the console. This may be used for testing and |
| 101 * debugging. | 124 * debugging. |
| 102 * | 125 * |
| 103 * @param {...*} [args] The arguments to log. | 126 * @param {...*} [args] The arguments to log. |
| 104 */ | 127 */ |
| 105 function log(...args) | 128 function log(...args) |
| 106 { | 129 { |
| 107 console.log(...args); | 130 console.log(...args); |
| 108 } | 131 } |
| 109 | 132 |
| 110 exports.log = log; | 133 exports.log = log; |
| 111 | 134 |
| 112 /** | 135 /** |
| 113 * Similar to {@link log}, but does the logging in the context of the document | 136 * Similar to {@link log}, but does the logging in the context of the document |
| 114 * rather than the content script. This may be used for testing and debugging, | 137 * rather than the content script. This may be used for testing and debugging, |
| 115 * especially to verify that the injection of snippets into the document is | 138 * especially to verify that the injection of snippets into the document is |
| 116 * working without any errors. | 139 * working without any errors. |
| 117 * | 140 * |
| 118 * @param {...*} [args] The arguments to log. | 141 * @param {...*} [args] The arguments to log. |
| 119 */ | 142 */ |
| 120 function trace(...args) | 143 function trace(...args) |
| 121 { | 144 { |
| 122 // We could simply use console.log here, but the goal is to demonstrate the | 145 // We could simply use console.log here, but the goal is to demonstrate the |
| 123 // usage of snippet dependencies. | 146 // usage of snippet dependencies. |
| 124 log(...args); | 147 log(...args); |
| 125 } | 148 } |
| 126 | 149 |
| 127 exports.trace = makeInjector(trace, log); | 150 exports.trace = makeInjector(trace, log); |
| 151 | |
| 152 /** | |
| 153 * Hides any HTML element or one of its ancestors matching a CSS selector if | |
| 154 * the text content of the element's shadow contains a given string. | |
| 155 * | |
| 156 * @param {string} search The string to look for in every HTML element's | |
| 157 * shadow. | |
| 158 * @param {string} selector The CSS selector that an HTML element must match | |
| 159 * for it to be hidden. | |
| 160 */ | |
| 161 function hideIfShadowContains(search, selector = "*") | |
| 162 { | |
| 163 let originalAttachShadow = Element.prototype.attachShadow; | |
| 164 | |
| 165 // If there's no Element.attachShadow API present then we don't care, it must | |
| 166 // be Firefox or an older version of Chrome. | |
| 167 if (!originalAttachShadow) | |
| 168 return; | |
| 169 | |
| 170 let applyOriginalAttachShadow = | |
| 171 originalAttachShadow.apply.bind(originalAttachShadow); | |
|
kzar
2018/07/19 13:02:09
This looks like an attempt at hardening the code t
Manish Jethani
2018/07/19 13:30:09
Yeah, I'm not really sure why this is needed. Even
kzar
2018/07/19 13:38:11
This reply kind of implies that you didn't write t
Manish Jethani
2018/07/19 14:20:54
No, it does not apply anything like that. I did wr
kzar
2018/07/19 15:02:52
Gotya, if the idea/code came from our inject.prelo
Manish Jethani
2018/07/19 15:28:34
OK, but why can't you just do this:
originalFun
kzar
2018/07/19 15:33:33
In case the website messes with `Function.prototyp
Manish Jethani
2018/07/19 15:39:57
I see, in that case I'd like to keep it. Reason: o
kzar
2018/07/19 15:52:40
Well, in that case, what about all the other APIs
Manish Jethani
2018/07/19 16:04:58
Fair enough, let's keep it simple for now and let'
| |
| 172 | |
| 173 // Mutation observers mapped to their corresponding shadow roots and their | |
| 174 // hosts. | |
| 175 let shadows = new WeakMap(); | |
| 176 | |
| 177 function observe(mutations, observer) | |
| 178 { | |
| 179 let {host, root} = shadows.get(observer) || {}; | |
| 180 | |
| 181 // Since it's a weak map, it's possible that either the element or its | |
| 182 // shadow has been removed. | |
| 183 if (!host || !root) | |
|
kzar
2018/07/19 13:02:09
I thought WeakMap just avoided memory leaks if the
Manish Jethani
2018/07/19 13:30:09
This is just a hypothesis, but I think that if eit
| |
| 184 return; | |
| 185 | |
| 186 // If the shadow contains the given text, check if the host or one of its | |
| 187 // ancestors matches the selector; if a matching element is found, hide | |
| 188 // it. | |
| 189 if (root.textContent.includes(search)) | |
| 190 { | |
| 191 let element = host; | |
| 192 | |
| 193 do | |
| 194 { | |
| 195 if (element.matches(selector)) | |
| 196 { | |
| 197 hideElement(element); | |
| 198 break; | |
| 199 } | |
| 200 } | |
| 201 while (element = element.parentElement); | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 Object.defineProperty(Element.prototype, "attachShadow", { | |
| 206 value(...args) | |
| 207 { | |
| 208 // Create the shadow root first. It doesn't matter if it's a closed | |
| 209 // shadow root, we keep the reference in a weak map. | |
| 210 let root = applyOriginalAttachShadow(this, args); | |
| 211 | |
| 212 // Listen for relevant DOM mutations in the shadow. | |
| 213 let observer = new MutationObserver(observe); | |
| 214 observer.observe(root, { | |
| 215 childList: true, | |
| 216 characterData: true, | |
| 217 subtree: true | |
| 218 }); | |
| 219 | |
| 220 // Keep references to the shadow root and its host in a weak map. If | |
| 221 // either the shadow is detached or the host itself is removed from the | |
| 222 // DOM, the mutation observer too will be freed eventually and the entry | |
| 223 // will be removed. | |
| 224 shadows.set(observer, {host: this, root}); | |
| 225 | |
| 226 return root; | |
| 227 } | |
| 228 }); | |
| 229 } | |
| 230 | |
| 231 exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains, | |
| 232 hideElement); | |
| OLD | NEW |