 Issue 29595633:
  Issue 5870 - Implement the new ElemHideEmulation filter type  (Closed) 
  Base URL: https://hg.adblockplus.org/adblockpluscore/
    
  
    Issue 29595633:
  Issue 5870 - Implement the new ElemHideEmulation filter type  (Closed) 
  Base URL: https://hg.adblockplus.org/adblockpluscore/| Index: compiled/filter/ElemHideBase.cpp | 
| =================================================================== | 
| --- a/compiled/filter/ElemHideBase.cpp | 
| +++ b/compiled/filter/ElemHideBase.cpp | 
| @@ -14,16 +14,17 @@ | 
| * You should have received a copy of the GNU General Public License | 
| * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 
| */ | 
| #include <cstring> | 
| #include "ElemHideBase.h" | 
| #include "../StringScanner.h" | 
| +#include "../Utils.h" | 
| ABP_NS_USING | 
| namespace | 
| { | 
| void NormalizeWhitespace(DependentString& text, String::size_type& domainsEnd, | 
| String::size_type& selectorStart) | 
| { | 
| @@ -46,29 +47,43 @@ | 
| delta++; | 
| else | 
| text[pos - delta] = text[pos]; | 
| } | 
| selectorStart -= delta; | 
| text.reset(text, 0, len - delta); | 
| } | 
| + | 
| + static constexpr String::value_type ELEM_HIDE_DELIMITER[] = u"##"; | 
| + static constexpr String::size_type ELEM_HIDE_DELIMITER_LEN = str_length_of(ELEM_HIDE_DELIMITER); | 
| + | 
| + static constexpr String::value_type ELEM_HIDE_EMULATION_DELIMITER[] = u"#?#"; | 
| + static constexpr String::size_type ELEM_HIDE_EMULATION_DELIMITER_LEN = str_length_of(ELEM_HIDE_EMULATION_DELIMITER); | 
| + | 
| + static constexpr String::value_type OLD_PROPS_SELECTOR[] = u"[-abp-properties="; | 
| + static constexpr String::size_type OLD_PROPS_SELECTOR_LEN = str_length_of(OLD_PROPS_SELECTOR); | 
| + | 
| + static constexpr String::value_type PROPS_SELECTOR[] = u":-abp-properties("; | 
| + static constexpr String::size_type PROPS_SELECTOR_LEN = str_length_of(PROPS_SELECTOR); | 
| } | 
| ElemHideBase::ElemHideBase(Type type, const String& text, const ElemHideData& data) | 
| : ActiveFilter(type, text, false), mData(data) | 
| { | 
| if (mData.HasDomains()) | 
| ParseDomains(mData.GetDomainsSource(mText), u','); | 
| } | 
| -Filter::Type ElemHideBase::Parse(DependentString& text, ElemHideData& data) | 
| +Filter::Type ElemHideBase::Parse(DependentString& text, ElemHideData& data, bool& needConversion) | 
| { | 
| StringScanner scanner(text); | 
| + needConversion = false; | 
| 
sergei
2018/02/14 15:59:35
Perhaps, it should be the very first instruction o
 
hub
2018/02/14 17:05:20
Done.
 | 
| + | 
| // Domains part | 
| bool seenSpaces = false; | 
| while (!scanner.done()) | 
| { | 
| String::value_type next = scanner.next(); | 
| if (next == u'#') | 
| { | 
| data.mDomainsEnd = scanner.position(); | 
| @@ -86,19 +101,22 @@ | 
| return Type::UNKNOWN; | 
| case u' ': | 
| seenSpaces = true; | 
| break; | 
| } | 
| } | 
| seenSpaces |= scanner.skip(u' '); | 
| + bool emulation = false; | 
| bool exception = scanner.skipOne(u'@'); | 
| if (exception) | 
| seenSpaces |= scanner.skip(u' '); | 
| + else | 
| + emulation = scanner.skipOne(u'?'); | 
| String::value_type next = scanner.next(); | 
| if (next != u'#') | 
| return Type::UNKNOWN; | 
| // Selector part | 
| // Selector shouldn't be empty | 
| @@ -108,30 +126,156 @@ | 
| data.mSelectorStart = scanner.position() + 1; | 
| // We are done validating, now we can normalize whitespace and the domain part | 
| if (seenSpaces) | 
| NormalizeWhitespace(text, data.mDomainsEnd, data.mSelectorStart); | 
| DependentString(text, 0, data.mDomainsEnd).toLower(); | 
| + // We still need to check the old syntax. It will be converted when | 
| + // we instantiate the filter. | 
| + if (!emulation && | 
| + text.find(OLD_PROPS_SELECTOR, data.mSelectorStart, OLD_PROPS_SELECTOR_LEN) != text.npos) | 
| + { | 
| + needConversion = true; | 
| + emulation = !exception; | 
| + } | 
| + | 
| if (exception) | 
| return Type::ELEMHIDEEXCEPTION; | 
| - if (text.find(u"[-abp-properties="_str, data.mSelectorStart) != text.npos) | 
| + if (emulation) | 
| return Type::ELEMHIDEEMULATION; | 
| return Type::ELEMHIDE; | 
| } | 
| namespace | 
| { | 
| + struct Range | 
| + { | 
| + String::size_type start; | 
| + String::size_type end; | 
| + String::size_type len() const | 
| + { | 
| + return end - start; | 
| + } | 
| + String::size_type byte_len() const | 
| + { | 
| + return len() * sizeof(String::value_type); | 
| + } | 
| + }; | 
| +} | 
| + | 
| +// Convert filter from the old syntax to the new. | 
| +DependentString ElemHideBase::ConvertFilter(String& text, String::size_type& at) | 
| +{ | 
| + Range prefix = {at, text.find(OLD_PROPS_SELECTOR, at, OLD_PROPS_SELECTOR_LEN)}; | 
| + if (prefix.end == text.npos) | 
| + return DependentString(text); | 
| + | 
| + auto length = text.length(); | 
| + Range suffix = {at, length}; | 
| + Range properties = { prefix.end + OLD_PROPS_SELECTOR_LEN, 0 }; | 
| + String::value_type quote = 0; | 
| + for (auto index = properties.start; | 
| + index < length && (suffix.start == at); index++) | 
| + { | 
| + auto c = text[index]; | 
| + switch (c) | 
| + { | 
| + case u'"': | 
| + case u'\'': | 
| + if (quote == 0) | 
| + { | 
| + // syntax error: we already have a quoted section. | 
| + if (properties.end) | 
| + return DependentString(); | 
| + | 
| + quote = c; | 
| + properties.start = index + 1; | 
| + } | 
| + else if (quote == c) | 
| + { | 
| + // end of quoted. | 
| + quote = 0; | 
| + properties.end = index; | 
| + } | 
| + break; | 
| + case u']': | 
| 
sergei
2018/02/14 15:59:35
What about filter containing `[-abp-properties=wha
 
hub
2018/02/14 17:05:20
This would definitely be invalid. I'll make sure t
 
hub
2018/02/22 18:56:30
It is definitely handled in that last patch.
 | 
| + if (quote == 0) | 
| + { | 
| + if (properties.end == 0) | 
| + return DependentString(); | 
| + suffix.start = index + 1; | 
| + } | 
| + break; | 
| + default: | 
| + break; | 
| + } | 
| + } | 
| + | 
| + if (suffix.start == at) | 
| + return DependentString(); | 
| + | 
| + String::size_type delimiter = text.find(ELEM_HIDE_DELIMITER, 0, | 
| + ELEM_HIDE_DELIMITER_LEN); | 
| + // +1 for the replacement of "##" by "#?#" | 
| + if (delimiter != text.npos) | 
| + at++; | 
| + auto new_len = at + prefix.len() + PROPS_SELECTOR_LEN + properties.len() + 1 /* ) */ + suffix.len(); | 
| + | 
| + assert2(new_len + 1 == length || (delimiter == text.npos && new_len + 2 == length), u"Inconsistent length in filter conversion."_str); | 
| + | 
| + DependentString converted(text, 0, new_len); | 
| + | 
| + if (suffix.len()) | 
| + { | 
| + new_len -= suffix.len(); | 
| + std::memmove(converted.data() + new_len, | 
| + text.data() + suffix.start, | 
| + suffix.byte_len()); | 
| + } | 
| + new_len--; | 
| + // here we need to move the properties before inserting the ')' | 
| + auto parens = new_len; | 
| + if (properties.len()) | 
| + { | 
| + new_len -= properties.len(); | 
| + std::memmove(converted.data() + new_len, | 
| + text.data() + properties.start, properties.byte_len()); | 
| + } | 
| + converted[parens] = u')'; | 
| + | 
| + new_len -= PROPS_SELECTOR_LEN; | 
| + std::memcpy(converted.data() + new_len, | 
| + PROPS_SELECTOR, | 
| + PROPS_SELECTOR_LEN * sizeof(String::value_type)); | 
| + if (prefix.len()) | 
| + { | 
| + new_len -= prefix.len(); | 
| + std::memmove(converted.data() + new_len, | 
| + text.data() + prefix.start, prefix.byte_len()); | 
| + } | 
| + | 
| + if (delimiter != String::npos) | 
| + { | 
| + std::memcpy(converted.data() + delimiter, ELEM_HIDE_EMULATION_DELIMITER, | 
| + ELEM_HIDE_EMULATION_DELIMITER_LEN * sizeof(String::value_type)); | 
| + } | 
| + | 
| + return converted; | 
| +} | 
| + | 
| +namespace | 
| +{ | 
| static constexpr String::value_type OPENING_CURLY_REPLACEMENT[] = u"\\7B "; | 
| static constexpr String::value_type CLOSING_CURLY_REPLACEMENT[] = u"\\7D "; | 
| - static constexpr String::size_type CURLY_REPLACEMENT_SIZE = sizeof(OPENING_CURLY_REPLACEMENT) / sizeof(OPENING_CURLY_REPLACEMENT[0]) - 1; | 
| + static constexpr String::size_type CURLY_REPLACEMENT_SIZE = str_length_of(OPENING_CURLY_REPLACEMENT); | 
| OwnedString EscapeCurlies(String::size_type replacementCount, | 
| const DependentString& str) | 
| { | 
| OwnedString result(str.length() + replacementCount * (CURLY_REPLACEMENT_SIZE - 1)); | 
| String::value_type* current = result.data(); | 
| for (String::size_type i = 0; i < str.length(); i++) |