| LEFT | RIGHT |
| 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 17 matching lines...) Expand all Loading... |
| 28 let selectorGroupSize = null; | 28 let selectorGroupSize = null; |
| 29 | 29 |
| 30 exports.setUp = function(callback) | 30 exports.setUp = function(callback) |
| 31 { | 31 { |
| 32 let sandboxedRequire = createSandbox({ | 32 let sandboxedRequire = createSandbox({ |
| 33 extraExports: { | 33 extraExports: { |
| 34 elemHide: ["filterTextByDomain", "selectorGroupSize"] | 34 elemHide: ["filterTextByDomain", "selectorGroupSize"] |
| 35 } | 35 } |
| 36 }); | 36 }); |
| 37 ( | 37 ( |
| 38 {ElemHide, createStyleSheet, rulesFromStyleSheet, filterTextByDomain, | 38 {ElemHide, createStyleSheet, rulesFromStyleSheet, |
| 39 selectorGroupSize} = sandboxedRequire("../lib/elemHide"), | 39 filterTextByDomain, selectorGroupSize} = sandboxedRequire("../lib/elemHide"
), |
| 40 {ElemHideExceptions} = sandboxedRequire("../lib/elemHideExceptions"), | 40 {ElemHideExceptions} = sandboxedRequire("../lib/elemHideExceptions"), |
| 41 {Filter} = sandboxedRequire("../lib/filterClasses") | 41 {Filter} = sandboxedRequire("../lib/filterClasses") |
| 42 ); | 42 ); |
| 43 | 43 |
| 44 callback(); | 44 callback(); |
| 45 }; | 45 }; |
| 46 | 46 |
| 47 function normalizeSelectors(selectors) | 47 function normalizeSelectors(selectors) |
| 48 { | 48 { |
| 49 // generateStyleSheetForDomain is currently allowed to return duplicate | 49 // generateStyleSheetForDomain is currently allowed to return duplicate |
| 50 // selectors for performance reasons, so we need to remove duplicates here. | 50 // selectors for performance reasons, so we need to remove duplicates here. |
| 51 return selectors.slice().sort().filter((selector, index, sortedSelectors) => | 51 return selectors.slice().sort().filter((selector, index, sortedSelectors) => |
| 52 { | 52 { |
| 53 return index == 0 || selector != sortedSelectors[index - 1]; | 53 return index == 0 || selector != sortedSelectors[index - 1]; |
| 54 }); | 54 }); |
| 55 } | 55 } |
| 56 | 56 |
| 57 function testResult(test, domain, expectedSelectors, specificOnly) | 57 function testResult(test, domain, expectedSelectors, |
| 58 {specificOnly = false, expectedExceptions = []} = {}) |
| 58 { | 59 { |
| 59 let normalizedExpectedSelectors = normalizeSelectors(expectedSelectors); | 60 let normalizedExpectedSelectors = normalizeSelectors(expectedSelectors); |
| 60 | 61 |
| 61 let {code, selectors} = | 62 let {code, selectors, exceptions} = |
| 62 ElemHide.generateStyleSheetForDomain(domain, specificOnly, true); | 63 ElemHide.generateStyleSheetForDomain(domain, specificOnly, true, true); |
| 63 | 64 |
| 64 test.deepEqual(normalizeSelectors(selectors), normalizedExpectedSelectors); | 65 test.deepEqual(normalizeSelectors(selectors), normalizedExpectedSelectors); |
| 66 |
| 67 // Test for consistency in exception free case. |
| 68 test.deepEqual(ElemHide.generateStyleSheetForDomain( |
| 69 domain, specificOnly, true, false), { |
| 70 code, |
| 71 selectors, |
| 72 exceptions: null |
| 73 }); |
| 74 |
| 75 test.deepEqual(exceptions.map(({text}) => text), expectedExceptions); |
| 65 | 76 |
| 66 // Make sure each expected selector is in the actual CSS code. | 77 // Make sure each expected selector is in the actual CSS code. |
| 67 for (let selector of normalizedExpectedSelectors) | 78 for (let selector of normalizedExpectedSelectors) |
| 68 { | 79 { |
| 69 test.ok(code.includes(selector + ", ") || | 80 test.ok(code.includes(selector + ", ") || |
| 70 code.includes(selector + " {display: none !important;}\n")); | 81 code.includes(selector + " {display: none !important;}\n")); |
| 71 } | 82 } |
| 72 } | 83 } |
| 73 | 84 |
| 74 exports.testGenerateStyleSheetForDomain = function(test) | 85 exports.testGenerateStyleSheetForDomain = function(test) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 91 testResult(test, "", []); | 102 testResult(test, "", []); |
| 92 | 103 |
| 93 addFilter("foo.example.com##turnip"); | 104 addFilter("foo.example.com##turnip"); |
| 94 testResult(test, "foo.example.com", ["turnip"]); | 105 testResult(test, "foo.example.com", ["turnip"]); |
| 95 testResult(test, "example.com", ["foo"]); | 106 testResult(test, "example.com", ["foo"]); |
| 96 testResult(test, "com", []); | 107 testResult(test, "com", []); |
| 97 testResult(test, "", []); | 108 testResult(test, "", []); |
| 98 | 109 |
| 99 addException("example.com#@#foo"); | 110 addException("example.com#@#foo"); |
| 100 testResult(test, "foo.example.com", ["turnip"]); | 111 testResult(test, "foo.example.com", ["turnip"]); |
| 101 testResult(test, "example.com", []); | 112 testResult(test, "example.com", [], { |
| 113 expectedExceptions: ["example.com#@#foo"] |
| 114 }); |
| 102 testResult(test, "com", []); | 115 testResult(test, "com", []); |
| 103 testResult(test, "", []); | 116 testResult(test, "", []); |
| 104 | 117 |
| 105 addFilter("com##bar"); | 118 addFilter("com##bar"); |
| 106 testResult(test, "foo.example.com", ["turnip", "bar"]); | 119 testResult(test, "foo.example.com", ["turnip", "bar"]); |
| 107 testResult(test, "example.com", ["bar"]); | 120 testResult(test, "example.com", ["bar"], { |
| 121 expectedExceptions: ["example.com#@#foo"] |
| 122 }); |
| 108 testResult(test, "com", ["bar"]); | 123 testResult(test, "com", ["bar"]); |
| 109 testResult(test, "", []); | 124 testResult(test, "", []); |
| 110 | 125 |
| 111 addException("example.com#@#bar"); | 126 addException("example.com#@#bar"); |
| 112 testResult(test, "foo.example.com", ["turnip"]); | 127 testResult(test, "foo.example.com", ["turnip"], { |
| 113 testResult(test, "example.com", []); | 128 expectedExceptions: ["example.com#@#bar"] |
| 129 }); |
| 130 testResult(test, "example.com", [], { |
| 131 expectedExceptions: ["example.com#@#foo", "example.com#@#bar"] |
| 132 }); |
| 114 testResult(test, "com", ["bar"]); | 133 testResult(test, "com", ["bar"]); |
| 115 testResult(test, "", []); | 134 testResult(test, "", []); |
| 116 | 135 |
| 117 removeException("example.com#@#foo"); | 136 removeException("example.com#@#foo"); |
| 118 testResult(test, "foo.example.com", ["turnip"]); | 137 testResult(test, "foo.example.com", ["turnip"], { |
| 119 testResult(test, "example.com", ["foo"]); | 138 expectedExceptions: ["example.com#@#bar"] |
| 139 }); |
| 140 testResult(test, "example.com", ["foo"], { |
| 141 expectedExceptions: ["example.com#@#bar"] |
| 142 }); |
| 120 testResult(test, "com", ["bar"]); | 143 testResult(test, "com", ["bar"]); |
| 121 testResult(test, "", []); | 144 testResult(test, "", []); |
| 122 | 145 |
| 123 removeException("example.com#@#bar"); | 146 removeException("example.com#@#bar"); |
| 124 testResult(test, "foo.example.com", ["turnip", "bar"]); | 147 testResult(test, "foo.example.com", ["turnip", "bar"]); |
| 125 testResult(test, "example.com", ["foo", "bar"]); | 148 testResult(test, "example.com", ["foo", "bar"]); |
| 126 testResult(test, "com", ["bar"]); | 149 testResult(test, "com", ["bar"]); |
| 127 testResult(test, "", []); | 150 testResult(test, "", []); |
| 128 | 151 |
| 129 addFilter("##generic"); | 152 addFilter("##generic"); |
| 130 testResult(test, "foo.example.com", ["turnip", "bar", "generic"]); | 153 testResult(test, "foo.example.com", ["turnip", "bar", "generic"]); |
| 131 testResult(test, "example.com", ["foo", "bar", "generic"]); | 154 testResult(test, "example.com", ["foo", "bar", "generic"]); |
| 132 testResult(test, "com", ["bar", "generic"]); | 155 testResult(test, "com", ["bar", "generic"]); |
| 133 testResult(test, "", ["generic"]); | 156 testResult(test, "", ["generic"]); |
| 134 testResult(test, "foo.example.com", ["turnip", "bar"], true); | 157 testResult(test, "foo.example.com", ["turnip", "bar"], {specificOnly: true}); |
| 135 testResult(test, "example.com", ["foo", "bar"], true); | 158 testResult(test, "example.com", ["foo", "bar"], {specificOnly: true}); |
| 136 testResult(test, "com", ["bar"], true); | 159 testResult(test, "com", ["bar"], {specificOnly: true}); |
| 137 testResult(test, "", [], true); | 160 testResult(test, "", [], {specificOnly: true}); |
| 138 removeFilter("##generic"); | 161 removeFilter("##generic"); |
| 139 | 162 |
| 140 addFilter("~adblockplus.org##example"); | 163 addFilter("~adblockplus.org##example"); |
| 141 testResult(test, "adblockplus.org", []); | 164 testResult(test, "adblockplus.org", []); |
| 142 testResult(test, "", ["example"]); | 165 testResult(test, "", ["example"]); |
| 143 testResult(test, "foo.example.com", ["turnip", "bar", "example"]); | 166 testResult(test, "foo.example.com", ["turnip", "bar", "example"]); |
| 144 testResult(test, "foo.example.com", ["turnip", "bar"], true); | 167 testResult(test, "foo.example.com", ["turnip", "bar"], {specificOnly: true}); |
| 145 removeFilter("~adblockplus.org##example"); | 168 removeFilter("~adblockplus.org##example"); |
| 146 | 169 |
| 147 removeFilter("~foo.example.com,example.com##foo"); | 170 removeFilter("~foo.example.com,example.com##foo"); |
| 148 testResult(test, "foo.example.com", ["turnip", "bar"]); | 171 testResult(test, "foo.example.com", ["turnip", "bar"]); |
| 149 testResult(test, "example.com", ["bar"]); | 172 testResult(test, "example.com", ["bar"]); |
| 150 testResult(test, "com", ["bar"]); | 173 testResult(test, "com", ["bar"]); |
| 151 testResult(test, "", []); | 174 testResult(test, "", []); |
| 152 | 175 |
| 153 removeFilter("com##bar"); | 176 removeFilter("com##bar"); |
| 154 testResult(test, "foo.example.com", ["turnip"]); | 177 testResult(test, "foo.example.com", ["turnip"]); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 testResult(test, "com", ["foo"]); | 214 testResult(test, "com", ["foo"]); |
| 192 testResult(test, "", ["foo"]); | 215 testResult(test, "", ["foo"]); |
| 193 removeFilter("~example.com##foo"); | 216 removeFilter("~example.com##foo"); |
| 194 | 217 |
| 195 removeFilter("~foo.example.com,example.com##foo"); | 218 removeFilter("~foo.example.com,example.com##foo"); |
| 196 | 219 |
| 197 // Test criteria | 220 // Test criteria |
| 198 addFilter("##hello"); | 221 addFilter("##hello"); |
| 199 addFilter("~example.com##world"); | 222 addFilter("~example.com##world"); |
| 200 addFilter("foo.com##specific"); | 223 addFilter("foo.com##specific"); |
| 201 testResult(test, "foo.com", ["specific"], true); | 224 testResult(test, "foo.com", ["specific"], {specificOnly: true}); |
| 202 testResult(test, "foo.com", ["hello", "specific", "world"], false); | |
| 203 testResult(test, "foo.com", ["hello", "specific", "world"]); | 225 testResult(test, "foo.com", ["hello", "specific", "world"]); |
| 204 testResult(test, "foo.com.", ["hello", "specific", "world"]); | 226 testResult(test, "example.com", [], {specificOnly: true}); |
| 205 testResult(test, "example.com", [], true); | |
| 206 removeFilter("foo.com##specific"); | 227 removeFilter("foo.com##specific"); |
| 207 removeFilter("~example.com##world"); | 228 removeFilter("~example.com##world"); |
| 208 removeFilter("##hello"); | 229 removeFilter("##hello"); |
| 209 testResult(test, "foo.com", []); | 230 testResult(test, "foo.com", []); |
| 210 | 231 |
| 211 addFilter("##hello"); | 232 addFilter("##hello"); |
| 212 testResult(test, "foo.com", [], true); | 233 testResult(test, "foo.com", [], {specificOnly: true}); |
| 213 testResult(test, "foo.com", ["hello"], false); | 234 testResult(test, "foo.com", ["hello"]); |
| 214 testResult(test, "foo.com", ["hello"]); | 235 testResult(test, "bar.com", [], {specificOnly: true}); |
| 215 testResult(test, "bar.com", [], true); | |
| 216 testResult(test, "bar.com", ["hello"], false); | |
| 217 testResult(test, "bar.com", ["hello"]); | 236 testResult(test, "bar.com", ["hello"]); |
| 218 addException("foo.com#@#hello"); | 237 addException("foo.com#@#hello"); |
| 219 testResult(test, "foo.com", [], true); | 238 testResult(test, "foo.com", [], {specificOnly: true}); |
| 220 testResult(test, "foo.com", [], false); | 239 testResult(test, "foo.com", [], {expectedExceptions: ["foo.com#@#hello"]}); |
| 221 testResult(test, "foo.com", []); | 240 testResult(test, "bar.com", [], {specificOnly: true}); |
| 222 testResult(test, "bar.com", [], true); | |
| 223 testResult(test, "bar.com", ["hello"], false); | |
| 224 testResult(test, "bar.com", ["hello"]); | 241 testResult(test, "bar.com", ["hello"]); |
| 225 removeException("foo.com#@#hello"); | 242 removeException("foo.com#@#hello"); |
| 226 testResult(test, "foo.com", [], true); | 243 testResult(test, "foo.com", [], {specificOnly: true}); |
| 227 // Note: We don't take care to track conditional selectors which became | 244 // Note: We don't take care to track conditional selectors which became |
| 228 // unconditional when a filter was removed. This was too expensive. | 245 // unconditional when a filter was removed. This was too expensive. |
| 229 testResult(test, "foo.com", ["hello"], false); | 246 testResult(test, "foo.com", ["hello"]); |
| 230 testResult(test, "foo.com", ["hello"]); | 247 testResult(test, "bar.com", [], {specificOnly: true}); |
| 231 testResult(test, "bar.com", [], true); | |
| 232 testResult(test, "bar.com", ["hello"], false); | |
| 233 testResult(test, "bar.com", ["hello"]); | 248 testResult(test, "bar.com", ["hello"]); |
| 234 removeFilter("##hello"); | 249 removeFilter("##hello"); |
| 235 testResult(test, "foo.com", []); | 250 testResult(test, "foo.com", []); |
| 236 testResult(test, "bar.com", []); | 251 testResult(test, "bar.com", []); |
| 237 | 252 |
| 238 addFilter("##hello"); | 253 addFilter("##hello"); |
| 239 addFilter("foo.com##hello"); | 254 addFilter("foo.com##hello"); |
| 240 testResult(test, "foo.com", ["hello"]); | 255 testResult(test, "foo.com", ["hello"]); |
| 241 removeFilter("foo.com##hello"); | 256 removeFilter("foo.com##hello"); |
| 242 testResult(test, "foo.com", ["hello"]); | 257 testResult(test, "foo.com", ["hello"]); |
| 243 removeFilter("##hello"); | 258 removeFilter("##hello"); |
| 244 testResult(test, "foo.com", []); | 259 testResult(test, "foo.com", []); |
| 245 | 260 |
| 246 addFilter("##hello"); | 261 addFilter("##hello"); |
| 247 addFilter("foo.com##hello"); | 262 addFilter("foo.com##hello"); |
| 248 testResult(test, "foo.com", ["hello"]); | 263 testResult(test, "foo.com", ["hello"]); |
| 249 removeFilter("##hello"); | 264 removeFilter("##hello"); |
| 250 testResult(test, "foo.com", ["hello"]); | 265 testResult(test, "foo.com", ["hello"]); |
| 251 removeFilter("foo.com##hello"); | 266 removeFilter("foo.com##hello"); |
| 252 testResult(test, "foo.com", []); | 267 testResult(test, "foo.com", []); |
| 253 | 268 |
| 254 test.done(); | 269 test.done(); |
| 255 }; | 270 }; |
| 256 | 271 |
| 257 exports.testZeroFilterKey = function(test) | 272 exports.testZeroFilterKey = function(test) |
| 258 { | 273 { |
| 259 ElemHide.add(Filter.fromText("##test")); | 274 ElemHide.add(Filter.fromText("##test")); |
| 260 ElemHideExceptions.add(Filter.fromText("foo.com#@#test")); | 275 ElemHideExceptions.add(Filter.fromText("foo.com#@#test")); |
| 261 testResult(test, "foo.com", []); | 276 testResult(test, "foo.com", [], {expectedExceptions: ["foo.com#@#test"]}); |
| 262 testResult(test, "bar.com", ["test"]); | 277 testResult(test, "bar.com", ["test"]); |
| 263 test.done(); | 278 test.done(); |
| 264 }; | 279 }; |
| 265 | 280 |
| 266 exports.testFiltersByDomain = function(test) | 281 exports.testFiltersByDomain = function(test) |
| 267 { | 282 { |
| 268 test.equal(filterTextByDomain.size, 0); | 283 test.equal(filterTextByDomain.size, 0); |
| 269 | 284 |
| 270 ElemHide.add(Filter.fromText("##test")); | 285 ElemHide.add(Filter.fromText("##test")); |
| 271 test.equal(filterTextByDomain.size, 0); | 286 test.equal(filterTextByDomain.size, 0); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 290 test.equal( | 305 test.equal( |
| 291 createStyleSheet([ | 306 createStyleSheet([ |
| 292 "html", "#foo", ".bar", "#foo .bar", "#foo > .bar", | 307 "html", "#foo", ".bar", "#foo .bar", "#foo > .bar", |
| 293 "#foo[data-bar='bar']" | 308 "#foo[data-bar='bar']" |
| 294 ]), | 309 ]), |
| 295 "html, #foo, .bar, #foo .bar, #foo > .bar, #foo[data-bar='bar'] " + | 310 "html, #foo, .bar, #foo .bar, #foo > .bar, #foo[data-bar='bar'] " + |
| 296 "{display: none !important;}\n", | 311 "{display: none !important;}\n", |
| 297 "Style sheet creation should work" | 312 "Style sheet creation should work" |
| 298 ); | 313 ); |
| 299 | 314 |
| 300 let selectors = new Array(50000).map((element, index) => ".s" + index); | 315 let selectors = new Array(50000).fill().map((element, index) => ".s" + index); |
| 301 | 316 |
| 302 test.equal((createStyleSheet(selectors).match(/\n/g) || []).length, | 317 test.equal((createStyleSheet(selectors).match(/\n/g) || []).length, |
| 303 Math.ceil(50000 / selectorGroupSize), | 318 Math.ceil(50000 / selectorGroupSize), |
| 304 "Style sheet should be split up into rules with at most " + | 319 "Style sheet should be split up into rules with at most " + |
| 305 selectorGroupSize + " selectors each"); | 320 selectorGroupSize + " selectors each"); |
| 306 | 321 |
| 322 test.equal( |
| 323 createStyleSheet([ |
| 324 "html", "#foo", ".bar", "#foo .bar", "#foo > .bar", |
| 325 "#foo[data-bar='{foo: 1}']" |
| 326 ]), |
| 327 "html, #foo, .bar, #foo .bar, #foo > .bar, " + |
| 328 "#foo[data-bar='\\7B foo: 1\\7D '] {display: none !important;}\n", |
| 329 "Braces should be escaped" |
| 330 ); |
| 331 |
| 307 test.done(); | 332 test.done(); |
| 308 }; | 333 }; |
| 309 | 334 |
| 310 exports.testRulesFromStyleSheet = function(test) | 335 exports.testRulesFromStyleSheet = function(test) |
| 311 { | 336 { |
| 312 // Note: The rulesFromStyleSheet function assumes that each rule will be | 337 // Note: The rulesFromStyleSheet function assumes that each rule will be |
| 313 // terminated with a newline character, including the last rule. If this is | 338 // terminated with a newline character, including the last rule. If this is |
| 314 // not the case, the function goes into an infinite loop. It should only be | 339 // not the case, the function goes into an infinite loop. It should only be |
| 315 // used with the return value of the createStyleSheet function. | 340 // used with the return value of the createStyleSheet function. |
| 316 | 341 |
| 317 test.deepEqual([...rulesFromStyleSheet("")], []); | 342 test.deepEqual([...rulesFromStyleSheet("")], []); |
| 318 test.deepEqual([...rulesFromStyleSheet("#foo {}\n")], ["#foo {}"]); | 343 test.deepEqual([...rulesFromStyleSheet("#foo {}\n")], ["#foo {}"]); |
| 319 test.deepEqual([...rulesFromStyleSheet("#foo {}\n#bar {}\n")], | 344 test.deepEqual([...rulesFromStyleSheet("#foo {}\n#bar {}\n")], |
| 320 ["#foo {}", "#bar {}"]); | 345 ["#foo {}", "#bar {}"]); |
| 321 | 346 |
| 322 test.done(); | 347 test.done(); |
| 323 }; | 348 }; |
| LEFT | RIGHT |