| 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-2017 eyeo GmbH | 3  * Copyright (C) 2006-2017 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 "use strict"; | 18 "use strict"; | 
| 19 | 19 | 
| 20 let Filter = require("filterClasses").Filter; | 20 let Filter = require("filterClasses").Filter; | 
| 21 let ContentBlockerList = require("../lib/abp2blocklist.js").ContentBlockerList; | 21 let ContentBlockerList = require("../lib/abp2blocklist.js").ContentBlockerList; | 
| 22 | 22 | 
| 23 function testRules(test, filters, expected, transformFunction, | 23 function runTest(test, assertions) | 
| 24                    {merge = false} = {}) |  | 
| 25 { | 24 { | 
| 26   let blockerList = new ContentBlockerList(); | 25   // All the assertions are run in parallel but we wait for all of them to | 
|  | 26   // finish before moving on to the next test. | 
|  | 27   test.expect(assertions.length); | 
|  | 28   Promise.all(assertions).then(() => test.done()); | 
|  | 29 } | 
|  | 30 | 
|  | 31 function testRules(test, filters, expected, transformFunction, options) | 
|  | 32 { | 
|  | 33   let blockerList = new ContentBlockerList(options); | 
| 27   for (let filter of filters) | 34   for (let filter of filters) | 
| 28     blockerList.addFilter(Filter.fromText(filter)); | 35     blockerList.addFilter(Filter.fromText(filter)); | 
| 29 | 36 | 
| 30   let rules = blockerList.generateRules({merge}); | 37   return blockerList.generateRules().then(rules => | 
| 31   if (transformFunction) | 38   { | 
| 32     rules = transformFunction(rules); | 39     if (transformFunction) | 
| 33 | 40       rules = transformFunction(rules); | 
| 34   test.deepEqual(rules, expected); | 41 | 
|  | 42     test.deepEqual(rules, expected); | 
|  | 43   }); | 
| 35 } | 44 } | 
| 36 | 45 | 
| 37 exports.generateRules = { | 46 exports.generateRules = { | 
| 38   testElementHiding: function(test) | 47   testElementHiding: function(test) | 
| 39   { | 48   { | 
| 40     testRules(test, ["##.whatever"], [ | 49     runTest(test, [ | 
| 41       {trigger: {"url-filter": "^https?://", | 50       testRules(test, ["##.whatever"], [ | 
| 42                  "url-filter-is-case-sensitive": true}, | 51         {trigger: {"url-filter": "^https?://", | 
| 43        action: {type: "css-display-none", selector: ".whatever"}} | 52                    "url-filter-is-case-sensitive": true}, | 
| 44     ]); | 53          action: {type: "css-display-none", selector: ".whatever"}} | 
| 45     testRules(test, ["test.com##.whatever"], [ | 54       ]), | 
| 46       {trigger: {"url-filter": "^https?://([^/:]*\\.)?test\\.com[/:]", | 55       testRules(test, ["test.com##.whatever"], [ | 
| 47                  "url-filter-is-case-sensitive": true}, | 56         {trigger: {"url-filter": "^https?://([^/:]*\\.)?test\\.com[/:]", | 
| 48        action: {type: "css-display-none", selector: ".whatever"}} | 57                    "url-filter-is-case-sensitive": true}, | 
| 49     ]); | 58          action: {type: "css-display-none", selector: ".whatever"}} | 
| 50 | 59       ]) | 
| 51     test.done(); | 60     ]); | 
| 52   }, | 61   }, | 
| 53 | 62 | 
| 54   testElementHidingExceptions: function(test) | 63   testElementHidingExceptions: function(test) | 
| 55   { | 64   { | 
| 56     testRules(test, ["#@#whatever"], []); | 65     runTest(test, [ | 
| 57     testRules(test, ["test.com#@#whatever"], []); | 66       testRules(test, [ | 
| 58     testRules(test, ["~test.com#@#whatever"], []); | 67         "##.whatever", | 
| 59 | 68         "test.com,anothertest.com###something", | 
| 60     // We currently completely ignore any element hiding filters that have the | 69         "@@||special.test.com^$elemhide", | 
| 61     // same selector as an element hiding exception. In these examples #whatever | 70         "@@||test.com^$generichide", | 
| 62     // should be hidden for all domains not ending in test.com instead of | 71         "@@||anothertest.com^$elemhide", | 
| 63     // nowhere! | 72         "@@^something^$elemhide", | 
| 64     testRules(test, ["test.com#@#whatever", "##whatever"], []); | 73         "@@^anything^$generichide" | 
| 65     testRules(test, ["~test.com##whatever"], []); | 74       ], [ | 
| 66 | 75         ["^https?://", ["*test.com", "*special.test.com", "*anothertest.com"]], | 
| 67     test.done(); | 76         ["^https?://([^/:]*\\.)?test\\.com[/:]", ["*special.test.com"]] | 
|  | 77       ], rules => rules.map(rule => [rule.trigger["url-filter"], | 
|  | 78                                      rule.trigger["unless-domain"]])), | 
|  | 79 | 
|  | 80       testRules(test, ["#@#whatever"], []), | 
|  | 81       testRules(test, ["test.com#@#whatever"], []), | 
|  | 82       testRules(test, ["~test.com#@#whatever"], []), | 
|  | 83 | 
|  | 84       // We currently completely ignore any element hiding filters that have the | 
|  | 85       // same selector as an element hiding exception. In these examples | 
|  | 86       // #whatever should be hidden for all domains not ending in test.com | 
|  | 87       // instead of nowhere! | 
|  | 88       testRules(test, ["test.com#@#whatever", "##whatever"], []), | 
|  | 89       testRules(test, ["~test.com##whatever"], []) | 
|  | 90     ]); | 
| 68   }, | 91   }, | 
| 69 | 92 | 
| 70   testRequestFilters: function(test) | 93   testRequestFilters: function(test) | 
| 71   { | 94   { | 
| 72     testRules(test, ["/foo", "||test.com", "http://example.com/foo"], [ | 95     runTest(test, [ | 
| 73       {trigger: {"url-filter": "^https?://.*/foo", | 96       testRules(test, [ | 
| 74                  "resource-type": ["image", "style-sheet", "script", "font", | 97         "/foo", "||test.com^", "http://example.com/foo", "^foo^" | 
| 75                                    "media", "raw", "document"]}, | 98       ], [ | 
| 76        action: {type: "block"}}, | 99         { | 
| 77       {trigger: {"url-filter": "^https?://([^/]+\\.)?test\\.com", | 100           trigger: { | 
| 78                  "url-filter-is-case-sensitive": true, | 101             "url-filter": "^[^:]+:(//)?.*/foo", | 
| 79                  "resource-type": ["image", "style-sheet", "script", "font", | 102             "resource-type": ["image", "style-sheet", "script", "font", | 
| 80                                    "media", "raw", "document"]}, | 103                               "media", "raw"] | 
| 81        action: {type: "block"}}, | 104           }, | 
| 82       {trigger: {"url-filter": "http://example\\.com/foo", | 105           action: {type: "block"} | 
| 83                  "resource-type": ["image", "style-sheet", "script", "font", | 106         }, | 
| 84                                    "media", "raw", "document"]}, | 107         { | 
| 85        action: {type: "block"}} | 108           trigger: { | 
| 86     ]); | 109             "url-filter": | 
| 87 | 110               "^[^:]+:(//)?([^/]+\\.)?test\\.com([^-_.%a-z0-9].*)?$", | 
| 88     testRules(test, ["||example.com"], [ | 111             "url-filter-is-case-sensitive": true, | 
| 89       {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com", | 112             "resource-type": ["image", "style-sheet", "script", "font", | 
| 90                  "url-filter-is-case-sensitive": true, | 113                               "media", "raw", "document"], | 
| 91                  "resource-type": ["image", "style-sheet", "script", "font", | 114             "unless-top-url": [ | 
| 92                                    "media", "raw", "document"]}, | 115               "^[^:]+:(//)?([^/]+\\.)?test\\.com([^-_.%a-z0-9].*)?$" | 
| 93 | 116             ], | 
| 94        action: {type: "block"}} | 117             "top-url-filter-is-case-sensitive": true | 
| 95     ]); | 118           }, | 
| 96 | 119           action: {type: "block"} | 
| 97     // Rules which would match no resource-types shouldn't be generated. | 120         }, | 
| 98     testRules(test, ["foo$document", "||foo.com$document"], []); | 121         { | 
| 99 | 122           trigger: { | 
| 100     test.done(); | 123             "url-filter": "^http://example\\.com/foo", | 
|  | 124             "resource-type": ["image", "style-sheet", "script", "font", | 
|  | 125                               "media", "raw", "document"], | 
|  | 126             "unless-top-url": ["^http://example\\.com/foo"] | 
|  | 127           }, | 
|  | 128           action: {type: "block"} | 
|  | 129         }, | 
|  | 130         { | 
|  | 131           trigger: { | 
|  | 132             "url-filter": "^[^:]+:(//)?.*http://example\\.com/foo", | 
|  | 133             "resource-type": ["image", "style-sheet", "script", "font", | 
|  | 134                               "media", "raw", "document"], | 
|  | 135             "unless-top-url": ["^[^:]+:(//)?.*http://example\\.com/foo"] | 
|  | 136           }, | 
|  | 137           action: {type: "block"} | 
|  | 138         }, | 
|  | 139         { | 
|  | 140           trigger: { | 
|  | 141             "url-filter": | 
|  | 142               "^[^:]+:(//)?(.*[^-_.%A-Za-z0-9])?foo([^-_.%A-Za-z0-9].*)?$", | 
|  | 143             "resource-type": ["image", "style-sheet", "script", "font", | 
|  | 144                               "media", "raw"] | 
|  | 145           }, | 
|  | 146           action: {type: "block"} | 
|  | 147         } | 
|  | 148       ]), | 
|  | 149 | 
|  | 150       testRules(test, ["||example.com"], [ | 
|  | 151         {trigger: {"url-filter": "^[^:]+:(//)?([^/]+\\.)?example\\.com", | 
|  | 152                    "url-filter-is-case-sensitive": true, | 
|  | 153                    "resource-type": ["image", "style-sheet", "script", "font", | 
|  | 154                                      "media", "raw", "document"], | 
|  | 155                    "unless-top-url": ["^[^:]+:(//)?([^/]+\\.)?example\\.com"], | 
|  | 156                    "top-url-filter-is-case-sensitive": true}, | 
|  | 157 | 
|  | 158          action: {type: "block"}} | 
|  | 159       ]), | 
|  | 160 | 
|  | 161       // Rules which would match no resource-types shouldn't be generated. | 
|  | 162       testRules(test, ["foo$document", "||foo.com$document"], []) | 
|  | 163     ]); | 
| 101   }, | 164   }, | 
| 102 | 165 | 
| 103   testRequestFilterExceptions: function(test) | 166   testRequestFilterExceptions: function(test) | 
| 104   { | 167   { | 
| 105     testRules(test, ["@@example.com"], [ | 168     runTest(test, [ | 
| 106       {trigger: {"url-filter": "^https?://.*example\\.com", | 169       testRules(test, ["@@example.com"], [ | 
| 107                  "resource-type": ["image", "style-sheet", "script", "font", | 170         {trigger: {"url-filter": "^[^:]+:(//)?.*example\\.com", | 
| 108                                    "media", "raw", "document"]}, | 171                    "resource-type": ["image", "style-sheet", "script", "font", | 
| 109        action: {type: "ignore-previous-rules"}} | 172                                      "media", "raw", "document"]}, | 
| 110     ]); | 173          action: {type: "ignore-previous-rules"}} | 
| 111 | 174       ]), | 
| 112     testRules(test, ["@@||example.com"], [ | 175 | 
| 113       {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com", | 176       testRules(test, ["@@||example.com"], [ | 
| 114                  "url-filter-is-case-sensitive": true, | 177         {trigger: {"url-filter": "^[^:]+:(//)?([^/]+\\.)?example\\.com", | 
| 115                  "resource-type": ["image", "style-sheet", "script", "font", | 178                    "url-filter-is-case-sensitive": true, | 
| 116                                    "media", "raw", "document"]}, | 179                    "resource-type": ["image", "style-sheet", "script", "font", | 
| 117        action: {type: "ignore-previous-rules"}} | 180                                      "media", "raw", "document"]}, | 
| 118     ]); | 181          action: {type: "ignore-previous-rules"}} | 
| 119 | 182       ]) | 
| 120     test.done(); | 183     ]); | 
| 121   }, | 184   }, | 
| 122 | 185 | 
| 123   testElementIDattributeFormat: function(test) | 186   testElementIDattributeFormat: function(test) | 
| 124   { | 187   { | 
| 125     testRules(test, | 188     runTest(test, [ | 
| 126               ["###example", "test.com###EXAMPLE"], | 189       testRules(test, | 
| 127               ["[id=example]", "[id=EXAMPLE]"], | 190                 ["###example", "test.com###EXAMPLE"], | 
| 128               rules => rules.map(rule => rule.action.selector)); | 191                 ["[id=example]", "[id=EXAMPLE]"], | 
| 129 | 192                 rules => rules.map(rule => rule.action.selector)) | 
| 130     test.done(); | 193     ]); | 
| 131   }, | 194   }, | 
| 132 | 195 | 
| 133   testDomainWhitelisting: function(test) | 196   testDomainWhitelisting: function(test) | 
| 134   { | 197   { | 
| 135     testRules(test, ["@@||example.com^$document"], [ | 198     runTest(test, [ | 
| 136       {trigger: {"url-filter": ".*", | 199       testRules(test, ["@@||example.com^$document"], [ | 
| 137                  "if-domain": ["example.com", "www.example.com"]}, | 200         { | 
| 138        action: {type: "ignore-previous-rules"}} | 201           trigger: { | 
| 139     ]); | 202             "url-filter": ".*", | 
| 140     testRules(test, ["@@||example.com^$document,image"], [ | 203             "if-domain": ["*example.com"] | 
| 141       {trigger: {"url-filter": ".*", | 204           }, | 
| 142                  "if-domain": ["example.com", "www.example.com"]}, | 205           action: {type: "ignore-previous-rules"} | 
| 143        action: {type: "ignore-previous-rules"}}, | 206         } | 
| 144       {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com", | 207       ]), | 
| 145                  "url-filter-is-case-sensitive": true, | 208       testRules(test, ["@@||example.com^$document,image"], [ | 
| 146                  "resource-type": ["image"]}, | 209         { | 
| 147        action: {type: "ignore-previous-rules"}} | 210           trigger: { | 
| 148     ]); | 211             "url-filter": ".*", | 
| 149     testRules(test, ["@@||example.com/path^$font,document"], [ | 212             "if-domain": ["*example.com"] | 
| 150       {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com/path", | 213           }, | 
| 151                  "resource-type": ["font"]}, | 214           action: {type: "ignore-previous-rules"} | 
| 152        action: {type: "ignore-previous-rules"}} | 215         }, | 
| 153     ]); | 216         { | 
| 154 | 217           trigger: { | 
| 155     test.done(); | 218             "url-filter": | 
|  | 219               "^https?://([^/]+\\.)?example\\.com([^-_.%a-z0-9].*)?$", | 
|  | 220             "url-filter-is-case-sensitive": true, | 
|  | 221             "resource-type": ["image"] | 
|  | 222           }, | 
|  | 223           action: {type: "ignore-previous-rules"} | 
|  | 224         } | 
|  | 225       ]), | 
|  | 226       testRules(test, ["@@||example.com/path^$font,document"], [ | 
|  | 227         { | 
|  | 228           trigger: { | 
|  | 229             "url-filter": | 
|  | 230               "^https?://([^/]+\\.)?example\\.com/path([^-_.%A-Za-z0-9].*)?$", | 
|  | 231             "resource-type": ["font"] | 
|  | 232           }, | 
|  | 233           action: {type: "ignore-previous-rules"} | 
|  | 234         } | 
|  | 235       ]) | 
|  | 236     ]); | 
|  | 237   }, | 
|  | 238 | 
|  | 239   testGenericblockExceptions: function(test) | 
|  | 240   { | 
|  | 241     runTest(test, [ | 
|  | 242       testRules(test, ["^ad.jpg|", "@@||example.com^$genericblock"], | 
|  | 243                 [[undefined, ["*example.com"]]], | 
|  | 244                 rules => rules.map(rule => [rule.trigger["if-domain"], | 
|  | 245                                             rule.trigger["unless-domain"]])), | 
|  | 246       testRules(test, ["^ad.jpg|$domain=test.com", | 
|  | 247                        "@@||example.com^$genericblock"], | 
|  | 248                 [[["*test.com"], undefined]], | 
|  | 249                 rules => rules.map(rule => [rule.trigger["if-domain"], | 
|  | 250                                             rule.trigger["unless-domain"]])), | 
|  | 251       testRules(test, ["^ad.jpg|$domain=~test.com", | 
|  | 252                        "@@||example.com^$genericblock"], | 
|  | 253                 [[undefined, ["*test.com", "*example.com"]]], | 
|  | 254                 rules => rules.map(rule => [rule.trigger["if-domain"], | 
|  | 255                                             rule.trigger["unless-domain"]])) | 
|  | 256     ]); | 
| 156   }, | 257   }, | 
| 157 | 258 | 
| 158   testRuleOrdering: function(test) | 259   testRuleOrdering: function(test) | 
| 159   { | 260   { | 
| 160     testRules( | 261     runTest(test, [ | 
| 161       test, | 262       testRules( | 
| 162       ["/ads.jpg", "@@example.com", "test.com#@#foo", "##bar"], | 263         test, | 
| 163       ["css-display-none", "block", "ignore-previous-rules"], | 264         ["/ads.jpg", "@@example.com", "test.com#@#foo", "##bar"], | 
| 164       rules => rules.map(rule => rule.action.type) | 265         ["css-display-none", "block", "ignore-previous-rules"], | 
| 165     ); | 266         rules => rules.map(rule => rule.action.type) | 
| 166     testRules( | 267       ), | 
| 167       test, | 268       testRules( | 
| 168       ["@@example.com", "##bar", "/ads.jpg", "test.com#@#foo"], | 269         test, | 
| 169       ["css-display-none", "block", "ignore-previous-rules"], | 270         ["@@example.com", "##bar", "/ads.jpg", "test.com#@#foo"], | 
| 170       rules => rules.map(rule => rule.action.type) | 271         ["css-display-none", "block", "ignore-previous-rules"], | 
| 171     ); | 272         rules => rules.map(rule => rule.action.type) | 
| 172 | 273       ) | 
| 173     test.done(); | 274     ]); | 
| 174   }, | 275   }, | 
| 175 | 276 | 
| 176   testRequestTypeMapping: function(test) | 277   testRequestTypeMapping: function(test) | 
| 177   { | 278   { | 
| 178     testRules( | 279     runTest(test, [ | 
| 179       test, | 280       testRules( | 
| 180       ["1", "2$image", "3$stylesheet", "4$script", "5$font", "6$media", | 281         test, | 
| 181        "7$popup", "8$object", "9$object_subrequest", "10$xmlhttprequest", | 282         ["1", "2$image", "3$stylesheet", "4$script", "5$font", "6$media", | 
| 182        "11$ping", "12$subdocument", "13$other", "14$IMAGE", | 283          "7$popup", "8$object", "9$object_subrequest", "10$xmlhttprequest", | 
| 183        "15$script,PING,Popup", "16$~image"], | 284          "11$websocket", "12$webrtc", | 
| 184       [["image", "style-sheet", "script", "font", "media", "raw", "document" ], | 285          "13$ping", "14$subdocument", "15$other", "16$IMAGE", | 
| 185        ["image"], | 286          "17$script,PING,Popup", "18$~image"], | 
| 186        ["style-sheet"], | 287         [["image", "style-sheet", "script", "font", "media", "raw"], | 
| 187        ["script"], | 288          ["image"], | 
| 188        ["font"], | 289          ["style-sheet"], | 
| 189        ["media"], | 290          ["script"], | 
| 190        ["popup"], | 291          ["font"], | 
| 191        ["media"], | 292          ["media"], | 
| 192        ["raw"], | 293          ["popup"], | 
| 193        ["raw"], | 294          ["media"], | 
| 194        ["raw"], | 295          ["raw"], | 
| 195        ["document"], | 296          ["raw"], | 
| 196        ["raw"], | 297          ["raw"], // WebSocket | 
| 197        ["image"], | 298          ["raw"], // WebRTC: STUN | 
| 198        ["script", "popup", "raw" ], | 299          ["raw"], // WebRTC: TURN | 
| 199        ["style-sheet", "script", "font", "media", "raw", "document"]], | 300          ["raw"], | 
| 200       rules => rules.map(rule => rule.trigger["resource-type"]) | 301          ["raw"], | 
| 201     ); | 302          ["image"], | 
| 202 | 303          ["script", "popup", "raw" ], | 
| 203     test.done(); | 304          ["style-sheet", "script", "font", "media", "raw"]], | 
|  | 305         rules => rules.map(rule => rule.trigger["resource-type"]) | 
|  | 306       ) | 
|  | 307     ]); | 
| 204   }, | 308   }, | 
| 205 | 309 | 
| 206   testUnsupportedfilters: function(test) | 310   testUnsupportedfilters: function(test) | 
| 207   { | 311   { | 
| 208     // These types of filters are currently completely unsupported. | 312     runTest(test, [ | 
| 209     testRules(test, ["foo$sitekey=bar", "@@foo$genericblock", | 313       // These types of filters are currently completely unsupported. | 
| 210                      "@@bar$generichide"], []); | 314       testRules(test, ["foo$sitekey=bar"], []) | 
| 211 | 315     ]); | 
| 212     test.done(); |  | 
| 213   }, | 316   }, | 
| 214 | 317 | 
| 215   testFilterOptions: function(test) | 318   testFilterOptions: function(test) | 
| 216   { | 319   { | 
| 217     testRules(test, ["1$domain=foo.com"], ["foo.com", "www.foo.com"], | 320     runTest(test, [ | 
| 218               rules => rules[0]["trigger"]["if-domain"]); | 321       testRules(test, ["1$domain=foo.com"], ["*foo.com"], | 
| 219     testRules(test, ["2$domain=third-party"], ["third-party"], | 322                 rules => rules[0]["trigger"]["if-domain"]), | 
| 220               rules => rules[0]["trigger"]["if-domain"]); | 323       testRules(test, ["2$third-party"], ["third-party"], | 
| 221     testRules(test, ["foo$match_case"], true, | 324                 rules => rules[0]["trigger"]["load-type"]), | 
| 222               rules => rules[0]["trigger"]["url-filter-is-case-sensitive"]); | 325       testRules(test, ["foo$match_case"], true, | 
| 223 | 326                 rules => rules[0]["trigger"]["url-filter-is-case-sensitive"]), | 
| 224     test.done(); | 327 | 
|  | 328       // Test subdomain exceptions. | 
|  | 329       testRules(test, ["1$domain=foo.com|~bar.foo.com"], | 
|  | 330                 ["foo.com", "www.foo.com"], | 
|  | 331                 rules => rules[0]["trigger"]["if-domain"]), | 
|  | 332       testRules(test, ["1$domain=foo.com|~www.foo.com"], | 
|  | 333                 ["foo.com"], | 
|  | 334                 rules => rules[0]["trigger"]["if-domain"]) | 
|  | 335     ]); | 
| 225   }, | 336   }, | 
| 226 | 337 | 
| 227   testUnicode: function(test) | 338   testUnicode: function(test) | 
| 228   { | 339   { | 
| 229     testRules(test, ["$domain=🐈.cat"], ["xn--zn8h.cat", "www.xn--zn8h.cat"], | 340     runTest(test, [ | 
| 230               rules => rules[0]["trigger"]["if-domain"]); | 341       testRules(test, ["$domain=🐈.cat"], ["*xn--zn8h.cat"], | 
| 231     testRules(test, ["🐈$domain=🐈.cat"], []); | 342                 rules => rules[0]["trigger"]["if-domain"]), | 
| 232     testRules(test, ["###🐈"], []); | 343       testRules(test, ["||🐈"], "^[^:]+:(//)?([^/]+\\.)?xn--zn8h", | 
| 233 | 344                 rules => rules[0]["trigger"]["url-filter"]), | 
| 234     test.done(); | 345       testRules(test, ["🐈$domain=🐈.cat"], "^[^:]+:(//)?.*%F0%9F%90%88", | 
|  | 346                 rules => rules[0]["trigger"]["url-filter"]), | 
|  | 347       testRules(test, ["🐈%F0%9F%90%88$domain=🐈.cat"], | 
|  | 348                 "^[^:]+:(//)?.*%F0%9F%90%88%F0%9F%90%88", | 
|  | 349                 rules => rules[0]["trigger"]["url-filter"]), | 
|  | 350       testRules(test, ["###🐈"], "[id=🐈]", | 
|  | 351                 rules => rules[0]["action"]["selector"]) | 
|  | 352     ]); | 
|  | 353   }, | 
|  | 354 | 
|  | 355   testWebSocket: function(test) | 
|  | 356   { | 
|  | 357     runTest(test, [ | 
|  | 358       testRules(test, ["foo$websocket"], [ | 
|  | 359         {trigger: {"url-filter": "^wss?://.*foo", "resource-type": ["raw"]}, | 
|  | 360          action: {type: "block"}} | 
|  | 361       ]) | 
|  | 362     ]); | 
|  | 363   }, | 
|  | 364 | 
|  | 365   testWebRTC: function(test) | 
|  | 366   { | 
|  | 367     runTest(test, [ | 
|  | 368       testRules(test, ["foo$webrtc"], [ | 
|  | 369         {trigger: {"url-filter": "^stuns?:.*foo", "resource-type": ["raw"]}, | 
|  | 370          action: {type: "block"}}, | 
|  | 371         {trigger: {"url-filter": "^turns?:.*foo", "resource-type": ["raw"]}, | 
|  | 372          action: {type: "block"}} | 
|  | 373       ]) | 
|  | 374     ]); | 
| 235   }, | 375   }, | 
| 236 | 376 | 
| 237   testMerging: function(test) | 377   testMerging: function(test) | 
| 238   { | 378   { | 
| 239     // Single character substitutions, deletions, and insertions. | 379     runTest(test, [ | 
| 240     testRules(test, ["/ads", "/adv"], ["^https?://.*/ad[sv]"], | 380       // Single character substitutions, deletions, and insertions. | 
| 241               rules => rules.map(rule => rule.trigger["url-filter"]), | 381       testRules(test, ["/ads", "/adv"], ["^[^:]+:(//)?.*/ad[sv]"], | 
| 242               {merge: true}); | 382                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 243     testRules(test, ["/ad", "/ads"], ["^https?://.*/ads?"], | 383                 {merge: "all"}), | 
| 244               rules => rules.map(rule => rule.trigger["url-filter"]), | 384       testRules(test, ["/ads", "/advs"], ["^[^:]+:(//)?.*/adv?s"], | 
| 245               {merge: true}); | 385                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 246     testRules(test, ["/ads", "/ad"], ["^https?://.*/ads?"], | 386                 {merge: "all"}), | 
| 247               rules => rules.map(rule => rule.trigger["url-filter"]), | 387       testRules(test, ["/advs", "/ads"], ["^[^:]+:(//)?.*/adv?s"], | 
| 248               {merge: true}); | 388                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 249     testRules(test, ["/ads", "/adv", "/ad"], ["^https?://.*/ad[sv]?"], | 389                 {merge: "all"}), | 
| 250               rules => rules.map(rule => rule.trigger["url-filter"]), | 390       testRules(test, ["/adts", "/advs", "/ads"], ["^[^:]+:(//)?.*/ad[tv]?s"], | 
| 251               {merge: true}); | 391                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 252     testRules(test, ["/ad", "/ads", "/adv"], ["^https?://.*/ad[sv]?"], | 392                 {merge: "all"}), | 
| 253               rules => rules.map(rule => rule.trigger["url-filter"]), | 393       testRules(test, ["/ads", "/adts", "/advs"], ["^[^:]+:(//)?.*/ad[tv]?s"], | 
| 254               {merge: true}); | 394                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 255     testRules(test, ["/ads", "/ad", "/adv"], ["^https?://.*/ad[sv]?"], | 395                 {merge: "all"}), | 
| 256               rules => rules.map(rule => rule.trigger["url-filter"]), | 396       testRules(test, ["/adts", "/ads", "/advs"], ["^[^:]+:(//)?.*/ad[tv]?s"], | 
| 257               {merge: true}); | 397                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 258     testRules(test, ["/a", "/ad", "/ads", "/adv"], | 398                 {merge: "all"}), | 
| 259               ["^https?://.*/a", "^https?://.*/ad[sv]?"], | 399       testRules(test, ["/ax", "/adx", "/adsx", "/advx"], | 
| 260               rules => rules.map(rule => rule.trigger["url-filter"]), | 400                 ["^[^:]+:(//)?.*/ax", "^[^:]+:(//)?.*/ad[sv]?x"], | 
| 261               {merge: true}); | 401                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 262     testRules(test, ["/ad", "/a", "/ads", "/adv"], | 402                 {merge: "all"}), | 
| 263               ["^https?://.*/a", "^https?://.*/ad[sv]?"], | 403       testRules(test, ["/adx", "/ax", "/adsx", "/advx"], | 
| 264               rules => rules.map(rule => rule.trigger["url-filter"]), | 404                 ["^[^:]+:(//)?.*/ax", "^[^:]+:(//)?.*/ad[sv]?x"], | 
| 265               {merge: true}); | 405                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 266     testRules(test, ["/ads", "/adv", "/ad", "/a"], | 406                 {merge: "all"}), | 
| 267               ["^https?://.*/ad[sv]?", "^https?://.*/a"], | 407       testRules(test, ["/adsx", "/advx", "/adx", "/ax"], | 
| 268               rules => rules.map(rule => rule.trigger["url-filter"]), | 408                 ["^[^:]+:(//)?.*/ad[sv]?x", "^[^:]+:(//)?.*/ax"], | 
| 269               {merge: true}); | 409                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 270     testRules(test, ["/ads", "/adv", "/a", "/ad"], | 410                 {merge: "all"}), | 
| 271               ["^https?://.*/ad[sv]?", "^https?://.*/a"], | 411       testRules(test, ["/adsx", "/advx", "/ax", "/adx"], | 
| 272               rules => rules.map(rule => rule.trigger["url-filter"]), | 412                 ["^[^:]+:(//)?.*/ad[sv]?x", "^[^:]+:(//)?.*/ax"], | 
| 273               {merge: true}); | 413                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 274     testRules(test, ["/ad", "/a", "/ads", "/adv", "/adx"], | 414                 {merge: "all"}), | 
| 275               ["^https?://.*/a", "^https?://.*/ad[svx]?"], | 415       testRules(test, ["/ad-", "/a-", "/ads-", "/adv-", "/adx-"], | 
| 276               rules => rules.map(rule => rule.trigger["url-filter"]), | 416                 ["^[^:]+:(//)?.*/a-", "^[^:]+:(//)?.*/ad[svx]?-"], | 
| 277               {merge: true}); | 417                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 278     testRules(test, ["/ads", "/a", "/ad", "/adv", "/adx"], | 418                 {merge: "all"}), | 
| 279               ["^https?://.*/ad[svx]?", "^https?://.*/a"], | 419       testRules(test, ["/ads-", "/a-", "/ad-", "/adv-", "/adx-"], | 
| 280               rules => rules.map(rule => rule.trigger["url-filter"]), | 420                 ["^[^:]+:(//)?.*/ad[svx]?-", "^[^:]+:(//)?.*/a-"], | 
| 281               {merge: true}); | 421                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 282 | 422                 {merge: "all"}), | 
| 283     // Multiple character deletions and insertions. | 423 | 
| 284     testRules(test, ["/ad", "/adxsi"], | 424       // Multiple character deletions and insertions. | 
| 285               ["^https?://.*/ad(xsi)?"], | 425       testRules(test, ["/ads", "/adxis"], | 
| 286               rules => rules.map(rule => rule.trigger["url-filter"]), | 426                 ["^[^:]+:(//)?.*/ad(xi)?s"], | 
| 287               {merge: true}); | 427                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 288     testRules(test, ["/adxsi", "/xsi"], | 428                 {merge: "all"}), | 
| 289               ["^https?://.*/(ad)?xsi"], | 429       testRules(test, ["/adxsi", "/xsi"], | 
| 290               rules => rules.map(rule => rule.trigger["url-filter"]), | 430                 ["^[^:]+:(//)?.*/(ad)?xsi"], | 
| 291               {merge: true}); | 431                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 292     testRules(test, ["/adxsi", "/ai"], | 432                 {merge: "all"}), | 
| 293               ["^https?://.*/a(dxs)?i"], | 433       testRules(test, ["/adxsi", "/ai"], | 
| 294               rules => rules.map(rule => rule.trigger["url-filter"]), | 434                 ["^[^:]+:(//)?.*/a(dxs)?i"], | 
| 295               {merge: true}); | 435                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 296 | 436                 {merge: "all"}), | 
| 297     // Both single and multiple character edits combined. | 437 | 
| 298     testRules(test, ["/ad", "/adxsi", "/xsi", "/axsi", "/bxsi"], | 438       // Both single and multiple character edits combined. | 
| 299               ["^https?://.*/ad(xsi)?", "^https?://.*/[ab]?xsi"], | 439       testRules(test, ["/adq", "/adxsiq", "/xsiq", "/axsiq", "/bxsiq"], | 
| 300               rules => rules.map(rule => rule.trigger["url-filter"]), | 440                 ["^[^:]+:(//)?.*/ad(xsi)?q", "^[^:]+:(//)?.*/[ab]?xsiq"], | 
| 301               {merge: true}); | 441                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 302 | 442                 {merge: "all"}), | 
| 303     testRules(test, ["/ads", "/a", "/ad", "/adv", "/adx", "/adxs"], | 443 | 
| 304               ["^https?://.*/ad[svx]?", "^https?://.*/a(dxs)?"], | 444       testRules(test, ["/adsq", "/aq", "/adq", "/advq", "/adxq", "/adxsq"], | 
| 305               rules => rules.map(rule => rule.trigger["url-filter"]), | 445                 ["^[^:]+:(//)?.*/ad[svx]?q", "^[^:]+:(//)?.*/a(dxs)?q"], | 
| 306               {merge: true}); | 446                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 307     testRules(test, ["/adxs", "/a", "/ad", "/ads", "/adv", "/adx"], | 447                 {merge: "all"}), | 
| 308               ["^https?://.*/a(dxs)?", "^https?://.*/ad[svx]?"], | 448       testRules(test, ["/adxsq", "/aq", "/adq", "/adsq", "/advq", "/adxq"], | 
| 309               rules => rules.map(rule => rule.trigger["url-filter"]), | 449                 ["^[^:]+:(//)?.*/a(dxs)?q", "^[^:]+:(//)?.*/ad[svx]?q"], | 
| 310               {merge: true}); | 450                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 311     testRules(test, ["/adxs", "/a", "/ads", "/ad", "/adv", "/adx"], | 451                 {merge: "all"}), | 
| 312               ["^https?://.*/a(dxs)?", "^https?://.*/ad[svx]?"], | 452       testRules(test, ["/adxsq", "/aq", "/adsq", "/adq", "/advq", "/adxq"], | 
| 313               rules => rules.map(rule => rule.trigger["url-filter"]), | 453                 ["^[^:]+:(//)?.*/a(dxs)?q", "^[^:]+:(//)?.*/ad[svx]?q"], | 
| 314               {merge: true}); | 454                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 315     testRules(test, ["/adxs", "/a", "/ads", "/adv", "/ad", "/adx"], | 455                 {merge: "all"}), | 
| 316               ["^https?://.*/a(dxs)?", "^https?://.*/ad[svx]?"], | 456       testRules(test, ["/adxsq", "/aq", "/adsq", "/advq", "/adq", "/adxq"], | 
| 317               rules => rules.map(rule => rule.trigger["url-filter"]), | 457                 ["^[^:]+:(//)?.*/a(dxs)?q", "^[^:]+:(//)?.*/ad[svx]?q"], | 
| 318               {merge: true}); | 458                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 319     testRules(test, ["/adsxi", "/adxs", "/a", "/ads", "/adv", "/ad", "/adx"], | 459                 {merge: "all"}), | 
| 320               ["^https?://.*/a(dsxi)?", "^https?://.*/adxs", | 460       testRules(test, ["/adsxiq", "/adxsq", "/aq", "/adsq", "/advq", "/adq", | 
| 321                "^https?://.*/ad[svx]?"], | 461                        "/adxq"], | 
| 322               rules => rules.map(rule => rule.trigger["url-filter"]), | 462                 ["^[^:]+:(//)?.*/a(dsxi)?q", "^[^:]+:(//)?.*/adxsq", | 
| 323               {merge: true}); | 463                  "^[^:]+:(//)?.*/ad[svx]?q"], | 
| 324     testRules(test, ["/adxsi", "/adsxi", "/adxs", "/a", "/ads", "/adv", "/ad", | 464                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 325                      "/adx"], | 465                 {merge: "all"}), | 
| 326               ["^https?://.*/adxsi?", "^https?://.*/a(dsxi)?", | 466       testRules(test, ["/adxsiq", "/adsxiq", "/adxsq", "/aq", "/adsq", "/advq", | 
| 327                "^https?://.*/ad[svx]?"], | 467                        "/adq", "/adxq"], | 
| 328               rules => rules.map(rule => rule.trigger["url-filter"]), | 468                 ["^[^:]+:(//)?.*/adxsi?q", "^[^:]+:(//)?.*/a(dsxi)?q", | 
| 329               {merge: true}); | 469                  "^[^:]+:(//)?.*/ad[svx]?q"], | 
| 330 | 470                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 331     // Given the 6 rules "ads", "bds", "adv", "bdv", "adx", and "bdx", we want | 471                 {merge: "all"}), | 
| 332     // the 2 rules "ad[svx]" and "bd[svx]", not the 3 rules "[ab]ds", "[ab]dv", | 472 | 
| 333     // and "[ab]dx" | 473       // Given the 6 rules "adsi", "bdsi", "advi", "bdvi", "adxi", and "bdxi", | 
| 334     testRules(test, ["/ads", "/bds", "/adv", "/bdv", "/adx", "/bdx"], | 474       // we want the 2 rules "ad[svx]i" and "bd[svx]i", not the 3 rules | 
| 335               ["^https?://.*/ad[svx]", "^https?://.*/bd[svx]"], | 475       // "[ab]dsi", "[ab]dvi", and "[ab]dxi" | 
| 336               rules => rules.map(rule => rule.trigger["url-filter"]), | 476       testRules(test, ["/adsi", "/bdsi", "/advi", "/bdvi", "/adxi", "/bdxi"], | 
| 337               {merge: true}); | 477                 ["^[^:]+:(//)?.*/ad[svx]i", "^[^:]+:(//)?.*/bd[svx]i"], | 
| 338     testRules(test, ["/ads", "/bds", "/adv", "/bdv", "/bdx"], | 478                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 339               ["^https?://.*/ad[sv]", "^https?://.*/bd[svx]"], | 479                 {merge: "all"}), | 
| 340               rules => rules.map(rule => rule.trigger["url-filter"]), | 480       testRules(test, ["/adsi", "/bdsi", "/advi", "/bdvi", "/bdxi"], | 
| 341               {merge: true}); | 481                 ["^[^:]+:(//)?.*/ad[sv]i", "^[^:]+:(//)?.*/bd[svx]i"], | 
| 342 | 482                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 343     // Make sure there's no merge where there are special characters in the | 483                 {merge: "all"}), | 
| 344     // delta. | 484 | 
| 345     testRules(test, ["/ads?", "/ads"], | 485       // Make sure there's no merge where there are special characters in the | 
| 346               ["^https?://.*/ads\\?", "^https?://.*/ads"], | 486       // delta. | 
| 347               rules => rules.map(rule => rule.trigger["url-filter"]), | 487       testRules(test, ["/ads?q", "/adsq"], | 
| 348               {merge: true}); | 488                 ["^[^:]+:(//)?.*/ads\\?q", "^[^:]+:(//)?.*/adsq"], | 
| 349     testRules(test, ["/ads?", "/ads-"], | 489                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 350               ["^https?://.*/ads\\?", "^https?://.*/ads-"], | 490                 {merge: "all"}), | 
| 351               rules => rules.map(rule => rule.trigger["url-filter"]), | 491       testRules(test, ["/ads?", "/ads-"], | 
| 352               {merge: true}); | 492                 ["^[^:]+:(//)?.*/ads\\?", "^[^:]+:(//)?.*/ads-"], | 
| 353     testRules(test, ["/ads?-", "/ads-"], | 493                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 354               ["^https?://.*/ads\\?-", "^https?://.*/ads-"], | 494                 {merge: "all"}), | 
| 355               rules => rules.map(rule => rule.trigger["url-filter"]), | 495       testRules(test, ["/ads?-", "/ads-"], | 
| 356               {merge: true}); | 496                 ["^[^:]+:(//)?.*/ads\\?-", "^[^:]+:(//)?.*/ads-"], | 
| 357 | 497                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
| 358     test.done(); | 498                 {merge: "all"}), | 
|  | 499 | 
|  | 500       // Redundant rules should be discarded. | 
|  | 501       testRules(test, ["/ad", "/ads", "/advertisement"], | 
|  | 502                 ["^[^:]+:(//)?.*/ad"], | 
|  | 503                 rules => rules.map(rule => rule.trigger["url-filter"]), | 
|  | 504                 {merge: "all"}) | 
|  | 505     ]); | 
| 359   } | 506   } | 
| 360 }; | 507 }; | 
| LEFT | RIGHT | 
|---|