| Index: compiled/filter/ElemHideBase.cpp |
| =================================================================== |
| --- a/compiled/filter/ElemHideBase.cpp |
| +++ b/compiled/filter/ElemHideBase.cpp |
| @@ -15,16 +15,19 @@ |
| * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| #include <cstring> |
| #include "ElemHideBase.h" |
| #include "../StringScanner.h" |
| +// the length of a static string array |
| +#define LENGTH_OF(x) ((sizeof(x) / sizeof(x[0])) - 1) |
| + |
| namespace |
| { |
| void NormalizeWhitespace(DependentString& text, String::size_type& domainsEnd, |
| String::size_type& selectorStart) |
| { |
| // For element hiding filters we only want to remove spaces preceding the |
| // selector part. The positions we've determined already have to be adjusted |
| // accordingly. |
| @@ -84,52 +87,193 @@ |
| 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 |
| seenSpaces |= scanner.skip(u' '); |
| if (scanner.done()) |
| return Type::UNKNOWN; |
| data.mSelectorStart = scanner.position() + 1; |
| + data.mNeedConversion = false; |
| // 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(u"[-abp-properties="_str, data.mSelectorStart) != text.npos) |
| + { |
| + data.mNeedConversion = 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 |
| { |
| + static constexpr String::value_type ELEM_HIDE_DELIMITER[] = u"##"; |
| + static constexpr String::size_type ELEM_HIDE_DELIMITER_LEN = 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 = LENGTH_OF(ELEM_HIDE_EMULATION_DELIMITER); |
| + |
| + static constexpr String::value_type PROPS_SELECTOR[] = u"[-abp-properties="; |
| + static constexpr String::size_type PROPS_SELECTOR_LEN = LENGTH_OF(PROPS_SELECTOR); |
| + |
| + static constexpr String::value_type NEW_PROPS_SELECTOR[] = u":-abp-properties("; |
| + static constexpr String::size_type NEW_PROPS_SELECTOR_LEN = LENGTH_OF(NEW_PROPS_SELECTOR); |
| +} |
| + |
| +// Convert filter from the old syntax to the new. |
| +OwnedString ElemHideBase::ConvertFilter(const String& text, String::size_type& at) |
| +{ |
| + auto selectorPos = text.find(PROPS_SELECTOR, at, PROPS_SELECTOR_LEN); |
| + if (selectorPos != text.npos) |
| + { |
| + auto length = text.length(); |
| + auto properties = selectorPos + PROPS_SELECTOR_LEN; |
| + String::value_type quote = 0; |
| + bool escape = false; |
| + String::size_type removed = 0; // how many chars we remove |
| + String::size_type end = properties; |
| + String::size_type quote_start = 0; |
| + String::size_type quote_end = 0; |
| + for (auto index = properties; |
| + index < length && end == properties; index++) |
| + { |
| + if (escape) |
| + { |
| + escape = false; |
| + continue; |
| + } |
| + |
| + auto c = text[index]; |
| + switch (c) |
| + { |
| + case '\\': |
| + escape = true; |
| + break; |
| + case '"': |
| + case '\'': |
| + if (quote == 0) |
| + { |
| + quote = c; |
| + quote_start = index + 1; |
| + } |
| + else if (quote == c) |
| + { |
| + // end of quoted. |
| + quote = 0; |
| + removed += 2; |
| + quote_end = index; |
| + } |
| + break; |
| + case ']': |
| + if (quote == 0) |
| + end = index + 1; // end of properties (after ]) |
| + break; |
| + default: |
| + break; |
| + } |
| + } |
| + |
| + if (quote != 0) |
| + quote_end = end - 1; |
| + else if (quote_end <= quote_start) |
| + { |
| + // we likely didn't find a quoted content so we just take it as is. |
| + quote_start = properties; |
| + quote_end = end - 1; |
| + } |
| + |
| + // +1 for the replacement of "##" by "#?#" |
| + String::size_type offset = 0; |
| + |
| + String::size_type delimiter = text.find(ELEM_HIDE_DELIMITER, 0, |
| + ELEM_HIDE_DELIMITER_LEN); |
| + OwnedString converted(length + ((delimiter != text.npos) ? 1 : 0) - removed); |
| + if (delimiter != text.npos) |
| + { |
| + if (delimiter >= selectorPos) |
| + return OwnedString(text); |
| + |
| + at++; |
| + std::memcpy(converted.data(), text.data(), |
| + delimiter * sizeof(String::value_type)); |
| + offset += delimiter; |
| + std::memcpy(converted.data() + offset, ELEM_HIDE_EMULATION_DELIMITER, |
| + ELEM_HIDE_EMULATION_DELIMITER_LEN * sizeof(String::value_type)); |
| + offset += ELEM_HIDE_EMULATION_DELIMITER_LEN; |
| + delimiter += ELEM_HIDE_DELIMITER_LEN; |
| + // we have already parsed to past the delimiter. |
| + selectorPos -= delimiter; |
| + } |
| + else |
| + delimiter = 0; |
| + |
| + |
| + std::memcpy(converted.data() + offset, text.data() + delimiter, |
| + selectorPos * sizeof(String::value_type)); |
| + offset += selectorPos; |
| + |
| + std::memcpy(converted.data() + offset, NEW_PROPS_SELECTOR, |
| + NEW_PROPS_SELECTOR_LEN * sizeof(String::value_type)); |
| + offset += NEW_PROPS_SELECTOR_LEN; |
| + |
| + std::memcpy(converted.data() + offset, text.data() + quote_start, |
| + (quote_end - quote_start) * sizeof(String::value_type)); |
| + offset += quote_end - quote_start; |
| + |
| + std::memcpy(converted.data() + offset, u")", sizeof(String::value_type)); |
| + offset++; |
| + |
| + std::memcpy(converted.data() + offset, text.data() + end, |
| + (length - end) * sizeof(String::value_type)); |
| + offset += (length - end) * sizeof(String::value_type); |
| + |
| + return converted; |
| + } |
| + |
| + return OwnedString(text); |
| +} |
| + |
| +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 = 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++) |