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