| 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 | 
| 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 #include <cstring> | 18 #include <cstring> | 
| 19 | 19 | 
| 20 #include "ElemHideBase.h" | 20 #include "ElemHideBase.h" | 
| 21 #include "../StringScanner.h" | 21 #include "../StringScanner.h" | 
|  | 22 #include "../Utils.h" | 
|  | 23 | 
|  | 24 ABP_NS_USING | 
| 22 | 25 | 
| 23 namespace | 26 namespace | 
| 24 { | 27 { | 
| 25   void NormalizeWhitespace(DependentString& text, String::size_type& domainsEnd, | 28   void NormalizeWhitespace(DependentString& text, String::size_type& domainsEnd, | 
| 26       String::size_type& selectorStart) | 29       String::size_type& selectorStart) | 
| 27   { | 30   { | 
| 28     // For element hiding filters we only want to remove spaces preceding the | 31     // For element hiding filters we only want to remove spaces preceding the | 
| 29     // selector part. The positions we've determined already have to be adjusted | 32     // selector part. The positions we've determined already have to be adjusted | 
| 30     // accordingly. | 33     // accordingly. | 
| 31 | 34 | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 43       if (pos < selectorStart && text[pos] == ' ') | 46       if (pos < selectorStart && text[pos] == ' ') | 
| 44         delta++; | 47         delta++; | 
| 45       else | 48       else | 
| 46         text[pos - delta] = text[pos]; | 49         text[pos - delta] = text[pos]; | 
| 47     } | 50     } | 
| 48     selectorStart -= delta; | 51     selectorStart -= delta; | 
| 49 | 52 | 
| 50     text.reset(text, 0, len - delta); | 53     text.reset(text, 0, len - delta); | 
| 51   } | 54   } | 
| 52 | 55 | 
| 53   // Convert filter from the old syntax to the new. | 56   static constexpr String::value_type ELEM_HIDE_DELIMITER[] = u"##"; | 
| 54   OwnedString ConvertFilter(const String& text, String::size_type at) | 57   static constexpr String::size_type ELEM_HIDE_DELIMITER_LEN = str_length_of(ELE
     M_HIDE_DELIMITER); | 
| 55   { | 58 | 
| 56     static const auto propsSelector = u"[-abp-properties="_str; | 59   static constexpr String::value_type ELEM_HIDE_EMULATION_DELIMITER[] = u"#?#"; | 
| 57     static const auto newPropsSelector = u":-abp-properties("_str; | 60   static constexpr String::size_type ELEM_HIDE_EMULATION_DELIMITER_LEN = str_len
     gth_of(ELEM_HIDE_EMULATION_DELIMITER); | 
| 58     auto selectorPos = text.find(propsSelector, at); | 61 | 
| 59     if (selectorPos != text.npos) | 62   static constexpr String::value_type OLD_PROPS_SELECTOR[] = u"[-abp-properties=
     "; | 
| 60     { | 63   static constexpr String::size_type OLD_PROPS_SELECTOR_LEN = str_length_of(OLD_
     PROPS_SELECTOR); | 
| 61       auto length = text.length(); | 64 | 
| 62       auto properties = selectorPos + propsSelector.length(); | 65   static constexpr String::value_type PROPS_SELECTOR[] = u":-abp-properties("; | 
| 63       String::value_type quote = 0; | 66   static constexpr String::size_type PROPS_SELECTOR_LEN = str_length_of(PROPS_SE
     LECTOR); | 
| 64       bool escape = false; | 67 } | 
| 65       String::size_type removed = 0; // how many chars we remove | 68 | 
| 66       String::size_type end = properties; | 69 ElemHideBase::ElemHideBase(Type type, const String& text, const ElemHideData& da
     ta, const ParsedDomains& parsedDomains) | 
| 67       String::size_type quote_start = 0; | 70     : ActiveFilter(type, text, false), mData(data) | 
| 68       String::size_type quote_end = 0; |  | 
| 69       for (auto index = properties; |  | 
| 70            index < length && end == properties; index++) |  | 
| 71       { |  | 
| 72         if (escape) |  | 
| 73         { |  | 
| 74           escape = false; |  | 
| 75           continue; |  | 
| 76         } |  | 
| 77 |  | 
| 78         auto c = text[index]; |  | 
| 79         switch (c) |  | 
| 80         { |  | 
| 81         case '\\': |  | 
| 82           escape = true; |  | 
| 83           break; |  | 
| 84         case '"': |  | 
| 85         case '\'': |  | 
| 86           if (quote == 0) |  | 
| 87           { |  | 
| 88             quote = c; |  | 
| 89             quote_start = index + 1; |  | 
| 90           } |  | 
| 91           else if (quote == c) |  | 
| 92           { |  | 
| 93             // end of quoted. |  | 
| 94             quote = 0; |  | 
| 95             removed += 2; |  | 
| 96             quote_end = index; |  | 
| 97           } |  | 
| 98           break; |  | 
| 99         case ']': |  | 
| 100           if (quote == 0) |  | 
| 101             end = index + 1; // end of properties (after ]) |  | 
| 102           break; |  | 
| 103         default: |  | 
| 104           break; |  | 
| 105         } |  | 
| 106       } |  | 
| 107 |  | 
| 108       if (quote != 0) |  | 
| 109         quote_end = end - 1; |  | 
| 110       else if (quote_end <= quote_start) |  | 
| 111       { |  | 
| 112         // we likely didn't find a quoted content so we just take it as is. |  | 
| 113         quote_start = properties; |  | 
| 114         quote_end = end - 1; |  | 
| 115       } |  | 
| 116 |  | 
| 117       OwnedString converted(length - removed); |  | 
| 118       String::size_type offset = 0; |  | 
| 119       std::memcpy(converted.data(), text.data(), |  | 
| 120                   selectorPos * sizeof(String::value_type)); |  | 
| 121       offset += selectorPos; |  | 
| 122 |  | 
| 123       std::memcpy(converted.data() + offset, newPropsSelector.data(), |  | 
| 124                   newPropsSelector.length() * sizeof(String::value_type)); |  | 
| 125       offset += newPropsSelector.length(); |  | 
| 126 |  | 
| 127       std::memcpy(converted.data() + offset, text.data() + quote_start, |  | 
| 128                   (quote_end - quote_start) * sizeof(String::value_type)); |  | 
| 129       offset += quote_end - quote_start; |  | 
| 130 |  | 
| 131       std::memcpy(converted.data() + offset, u")", sizeof(String::value_type)); |  | 
| 132       offset++; |  | 
| 133 |  | 
| 134       std::memcpy(converted.data() + offset, text.data() + end, |  | 
| 135                   (length - end) * sizeof(String::value_type)); |  | 
| 136       offset += (length - end) * sizeof(String::value_type); |  | 
| 137 |  | 
| 138       return converted; |  | 
| 139     } |  | 
| 140 |  | 
| 141     return OwnedString(text); |  | 
| 142   } |  | 
| 143 } |  | 
| 144 |  | 
| 145 ElemHideBase::ElemHideBase(Type type, const String& text, |  | 
| 146     const ElemHideData& data, const ParsedDomains& parsedDomains) |  | 
| 147     : ActiveFilter(type, ConvertFilter(text, data.mSelectorStart), false), |  | 
| 148       mData(data) |  | 
| 149 { | 71 { | 
| 150   if (mData.HasDomains()) | 72   if (mData.HasDomains()) | 
| 151     FillDomains(mData.GetDomainsSource(mText), parsedDomains); | 73     FillDomains(mData.GetDomainsSource(mText), parsedDomains); | 
| 152 } | 74 } | 
| 153 | 75 | 
| 154 Filter::Type ElemHideBase::Parse(DependentString& text, DependentString& error, | 76 Filter::Type ElemHideBase::Parse(DependentString& text, DependentString& error, | 
| 155     ElemHideData& data, ParsedDomains& parsedDomains) | 77                                  ElemHideData& data, bool& needConversion, | 
| 156 { | 78                                  ParsedDomains& parsedDomains) | 
|  | 79 { | 
|  | 80   needConversion = false; | 
|  | 81 | 
| 157   StringScanner scanner(text); | 82   StringScanner scanner(text); | 
| 158 | 83 | 
| 159   // Domains part | 84   // Domains part | 
| 160   bool seenSpaces = false; | 85   bool seenSpaces = false; | 
| 161   while (!scanner.done()) | 86   while (!scanner.done()) | 
| 162   { | 87   { | 
| 163     String::value_type next = scanner.next(); | 88     String::value_type next = scanner.next(); | 
| 164     if (next == u'#') | 89     if (next == u'#') | 
| 165     { | 90     { | 
| 166       data.mDomainsEnd = scanner.position(); | 91       data.mDomainsEnd = scanner.position(); | 
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 208     NormalizeWhitespace(text, data.mDomainsEnd, data.mSelectorStart); | 133     NormalizeWhitespace(text, data.mDomainsEnd, data.mSelectorStart); | 
| 209   DependentString(text, 0, data.mDomainsEnd).toLower(); | 134   DependentString(text, 0, data.mDomainsEnd).toLower(); | 
| 210 | 135 | 
| 211   parsedDomains = | 136   parsedDomains = | 
| 212     ParseDomainsInternal(data.GetDomainsSource(text), u',', false); | 137     ParseDomainsInternal(data.GetDomainsSource(text), u',', false); | 
| 213   if (parsedDomains.hasEmpty) | 138   if (parsedDomains.hasEmpty) | 
| 214   { | 139   { | 
| 215     error = u"filter_invalid_domain"_str; | 140     error = u"filter_invalid_domain"_str; | 
| 216     return Type::INVALID; | 141     return Type::INVALID; | 
| 217   } | 142   } | 
|  | 143   // We still need to check the old syntax. It will be converted when | 
|  | 144   // we instantiate the filter. | 
|  | 145   if (!emulation && | 
|  | 146       text.find(OLD_PROPS_SELECTOR, data.mSelectorStart, OLD_PROPS_SELECTOR_LEN)
      != text.npos) | 
|  | 147   { | 
|  | 148     needConversion = true; | 
|  | 149     emulation = !exception; | 
|  | 150   } | 
| 218 | 151 | 
| 219   if (exception) | 152   if (exception) | 
| 220     return Type::ELEMHIDEEXCEPTION; | 153     return Type::ELEMHIDEEXCEPTION; | 
| 221 | 154 | 
| 222   if (emulation) | 155   if (emulation) | 
| 223     return Type::ELEMHIDEEMULATION; | 156     return Type::ELEMHIDEEMULATION; | 
| 224 | 157 | 
| 225   return Type::ELEMHIDE; | 158   return Type::ELEMHIDE; | 
| 226 } | 159 } | 
| 227 | 160 | 
| 228 namespace | 161 namespace | 
| 229 { | 162 { | 
|  | 163   struct Range | 
|  | 164   { | 
|  | 165     String::size_type start; | 
|  | 166     String::size_type end; | 
|  | 167     String::size_type len() const | 
|  | 168     { | 
|  | 169         return end - start; | 
|  | 170     } | 
|  | 171     String::size_type byte_len() const | 
|  | 172     { | 
|  | 173       return len() * sizeof(String::value_type); | 
|  | 174     } | 
|  | 175   }; | 
|  | 176 } | 
|  | 177 | 
|  | 178 // Convert filter from the old syntax to the new. | 
|  | 179 DependentString ElemHideBase::ConvertFilter(String& text, String::size_type& at) | 
|  | 180 { | 
|  | 181   Range prefix = {at, text.find(OLD_PROPS_SELECTOR, at, OLD_PROPS_SELECTOR_LEN)}
     ; | 
|  | 182   if (prefix.end == text.npos) | 
|  | 183     return DependentString(text); | 
|  | 184 | 
|  | 185   auto length = text.length(); | 
|  | 186   Range suffix = {at, length}; | 
|  | 187   Range properties = { prefix.end + OLD_PROPS_SELECTOR_LEN, 0 }; | 
|  | 188   String::value_type quote = 0; | 
|  | 189   for (auto index = properties.start; | 
|  | 190        index < length && (suffix.start == at); index++) | 
|  | 191   { | 
|  | 192     auto c = text[index]; | 
|  | 193     switch (c) | 
|  | 194     { | 
|  | 195     case u'"': | 
|  | 196     case u'\'': | 
|  | 197       if (quote == 0) | 
|  | 198       { | 
|  | 199         // syntax error: we already have a quoted section. | 
|  | 200         if (properties.end) | 
|  | 201           return DependentString(); | 
|  | 202 | 
|  | 203         if (properties.start != index) | 
|  | 204           return DependentString(); | 
|  | 205 | 
|  | 206         quote = c; | 
|  | 207         properties.start = index + 1; | 
|  | 208       } | 
|  | 209       else if (quote == c) | 
|  | 210       { | 
|  | 211         // end of quoted. | 
|  | 212         quote = 0; | 
|  | 213         properties.end = index; | 
|  | 214       } | 
|  | 215       break; | 
|  | 216     case u']': | 
|  | 217       if (quote == 0) | 
|  | 218       { | 
|  | 219         if (properties.end == 0) | 
|  | 220           return DependentString(); | 
|  | 221         if (properties.end + 1 != index) | 
|  | 222           return DependentString(); | 
|  | 223         suffix.start = index + 1; | 
|  | 224       } | 
|  | 225       break; | 
|  | 226     default: | 
|  | 227       break; | 
|  | 228     } | 
|  | 229   } | 
|  | 230 | 
|  | 231   if (suffix.start == at) | 
|  | 232     return DependentString(); | 
|  | 233 | 
|  | 234   String::size_type delimiter = text.find(ELEM_HIDE_DELIMITER, 0, | 
|  | 235                                           ELEM_HIDE_DELIMITER_LEN); | 
|  | 236   // +1 for the replacement of "##" by "#?#" | 
|  | 237   if (delimiter != text.npos) | 
|  | 238     at++; | 
|  | 239   auto new_len = at + prefix.len() + PROPS_SELECTOR_LEN + properties.len() + 1 /
     * ) */ + suffix.len(); | 
|  | 240 | 
|  | 241   assert2(length == new_len + (delimiter == text.npos ? 2 : 1), u"Inconsistent l
     ength in filter conversion."_str); | 
|  | 242 | 
|  | 243   DependentString converted(text, 0, new_len); | 
|  | 244 | 
|  | 245   if (suffix.len()) | 
|  | 246   { | 
|  | 247     new_len -= suffix.len(); | 
|  | 248     std::memmove(converted.data() + new_len, | 
|  | 249                  text.data() + suffix.start, | 
|  | 250                  suffix.byte_len()); | 
|  | 251   } | 
|  | 252   new_len--; | 
|  | 253   // here we need to move the properties before inserting the ')' | 
|  | 254   auto parens = new_len; | 
|  | 255   if (properties.len()) | 
|  | 256   { | 
|  | 257     new_len -= properties.len(); | 
|  | 258     std::memmove(converted.data() + new_len, | 
|  | 259                  text.data() + properties.start, properties.byte_len()); | 
|  | 260   } | 
|  | 261   converted[parens] = u')'; | 
|  | 262 | 
|  | 263   new_len -= PROPS_SELECTOR_LEN; | 
|  | 264   std::memcpy(converted.data() + new_len, | 
|  | 265               PROPS_SELECTOR, | 
|  | 266               PROPS_SELECTOR_LEN * sizeof(String::value_type)); | 
|  | 267   if (prefix.len()) | 
|  | 268   { | 
|  | 269     new_len -= prefix.len(); | 
|  | 270     std::memmove(converted.data() + new_len, | 
|  | 271                  text.data() + prefix.start, prefix.byte_len()); | 
|  | 272   } | 
|  | 273 | 
|  | 274   if (delimiter != String::npos) | 
|  | 275   { | 
|  | 276     std::memcpy(converted.data() + delimiter, ELEM_HIDE_EMULATION_DELIMITER, | 
|  | 277                 ELEM_HIDE_EMULATION_DELIMITER_LEN * sizeof(String::value_type)); | 
|  | 278   } | 
|  | 279 | 
|  | 280   return converted; | 
|  | 281 } | 
|  | 282 | 
|  | 283 namespace | 
|  | 284 { | 
| 230   static constexpr String::value_type OPENING_CURLY_REPLACEMENT[] = u"\\7B "; | 285   static constexpr String::value_type OPENING_CURLY_REPLACEMENT[] = u"\\7B "; | 
| 231   static constexpr String::value_type CLOSING_CURLY_REPLACEMENT[] = u"\\7D "; | 286   static constexpr String::value_type CLOSING_CURLY_REPLACEMENT[] = u"\\7D "; | 
| 232   static constexpr String::size_type CURLY_REPLACEMENT_SIZE = sizeof(OPENING_CUR
     LY_REPLACEMENT) / sizeof(OPENING_CURLY_REPLACEMENT[0]) - 1; | 287   static constexpr String::size_type CURLY_REPLACEMENT_SIZE = str_length_of(OPEN
     ING_CURLY_REPLACEMENT); | 
| 233 | 288 | 
| 234   OwnedString EscapeCurlies(String::size_type replacementCount, | 289   OwnedString EscapeCurlies(String::size_type replacementCount, | 
| 235                             const DependentString& str) | 290                             const DependentString& str) | 
| 236   { | 291   { | 
| 237     OwnedString result(str.length() + replacementCount * (CURLY_REPLACEMENT_SIZE
      - 1)); | 292     OwnedString result(str.length() + replacementCount * (CURLY_REPLACEMENT_SIZE
      - 1)); | 
| 238 | 293 | 
| 239     String::value_type* current = result.data(); | 294     String::value_type* current = result.data(); | 
| 240     for (String::size_type i = 0; i < str.length(); i++) | 295     for (String::size_type i = 0; i < str.length(); i++) | 
| 241     { | 296     { | 
| 242       switch(str[i]) | 297       switch(str[i]) | 
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 286       if (item.second && !item.first.empty()) | 341       if (item.second && !item.first.empty()) | 
| 287       { | 342       { | 
| 288         if (!result.empty()) | 343         if (!result.empty()) | 
| 289           result.append(u','); | 344           result.append(u','); | 
| 290         result.append(item.first); | 345         result.append(item.first); | 
| 291       } | 346       } | 
| 292     } | 347     } | 
| 293   } | 348   } | 
| 294   return result; | 349   return result; | 
| 295 } | 350 } | 
| LEFT | RIGHT | 
|---|