| LEFT | RIGHT | 
|---|
| 1 #include "ElemHideBase.h" | 1 #include "ElemHideBase.h" | 
| 2 #include "ElemHideFilter.h" |  | 
| 3 #include "ElemHideException.h" |  | 
| 4 #include "CSSPropertyFilter.h" | 2 #include "CSSPropertyFilter.h" | 
| 5 #include "InvalidFilter.h" |  | 
| 6 #include "StringScanner.h" | 3 #include "StringScanner.h" | 
| 7 | 4 | 
| 8 ElemHideBase::ElemHideBase(const std::u16string& text, | 5 namespace | 
| 9     const std::u16string& domains, const std::u16string& selector) |  | 
| 10     : ActiveFilter(text, false), selector(selector) |  | 
| 11 { | 6 { | 
| 12   if (!domains.empty()) | 7   void NormalizeWhitespace(DependentString& text, String::size_type& domainsEnd, | 
| 13     ParseDomains(domains, u','); | 8       String::size_type& selectorStart) | 
|  | 9   { | 
|  | 10     // For element hiding filters we only want to remove spaces preceding the | 
|  | 11     // selector part. The positions we've determined already have to be adjusted | 
|  | 12     // accordingly. | 
|  | 13 | 
|  | 14     String::size_type delta = 0; | 
|  | 15     String::size_type len = text.length(); | 
|  | 16 | 
|  | 17     // The first character is guaranteed to be a non-space, the string has been | 
|  | 18     // trimmed earlier. | 
|  | 19     for (String::size_type pos = 1; pos < len; pos++) | 
|  | 20     { | 
|  | 21       if (pos == domainsEnd) | 
|  | 22         domainsEnd -= delta; | 
|  | 23 | 
|  | 24       // Only spaces before selectorStart position should be removed. | 
|  | 25       if (pos < selectorStart && text[pos] == ' ') | 
|  | 26         delta++; | 
|  | 27       else | 
|  | 28         text[pos - delta] = text[pos]; | 
|  | 29     } | 
|  | 30     selectorStart -= delta; | 
|  | 31 | 
|  | 32     text.reset(text, 0, len - delta); | 
|  | 33   } | 
| 14 } | 34 } | 
| 15 | 35 | 
| 16 Filter::Type ElemHideBase::Parse(const std::u16string& text, size_t* domainsEnd, | 36 ElemHideBase::ElemHideBase(Type type, const String& text, const ElemHideBaseData
     & data) | 
| 17     size_t* selectorStart, size_t* prefixEnd, size_t* regexpStart, | 37     : ActiveFilter(type, text, false), mData(data) | 
| 18     size_t* regexpEnd, size_t* suffixStart) | 38 { | 
|  | 39   if (mData.HasDomains()) | 
|  | 40     ParseDomains(mData.GetDomainsSource(mText), u','); | 
|  | 41 } | 
|  | 42 | 
|  | 43 Filter::Type ElemHideBase::Parse(DependentString& text, ElemHideData& data) | 
| 19 { | 44 { | 
| 20   StringScanner scanner(text); | 45   StringScanner scanner(text); | 
| 21 | 46 | 
| 22   // Domains part | 47   // Domains part | 
| 23   loop: | 48   bool seenSpaces = false; | 
| 24   while (!scanner.done()) | 49   while (!scanner.done()) | 
| 25   { | 50   { | 
| 26     char16_t next = scanner.next(); | 51     String::value_type next = scanner.next(); | 
| 27     if (next == u'#') | 52     if (next == u'#') | 
| 28     { | 53     { | 
| 29       *domainsEnd = scanner.position(); | 54       data.mDomainsEnd = scanner.position(); | 
| 30       break; | 55       break; | 
| 31     } | 56     } | 
| 32 | 57 | 
| 33     switch (next) | 58     switch (next) | 
| 34     { | 59     { | 
| 35       case u'/': | 60       case u'/': | 
| 36       case u'*': | 61       case u'*': | 
| 37       case u'|': | 62       case u'|': | 
| 38       case u'@': | 63       case u'@': | 
| 39       case u'"': | 64       case u'"': | 
| 40       case u'!': | 65       case u'!': | 
| 41         return Type::UNKNOWN; | 66         return Type::UNKNOWN; | 
|  | 67       case u' ': | 
|  | 68         seenSpaces = true; | 
|  | 69         break; | 
| 42     } | 70     } | 
| 43   } | 71   } | 
| 44 | 72 | 
| 45   bool exception = false; | 73   seenSpaces |= scanner.skip(u' '); | 
| 46   char16_t next = scanner.next(); | 74   bool exception = scanner.skipOne(u'@'); | 
| 47   if (next == u'@') | 75   if (exception) | 
| 48   { | 76     seenSpaces |= scanner.skip(u' '); | 
| 49     exception = true; |  | 
| 50     next = scanner.next(); |  | 
| 51   } |  | 
| 52 | 77 | 
|  | 78   String::value_type next = scanner.next(); | 
| 53   if (next != u'#') | 79   if (next != u'#') | 
| 54     return Type::UNKNOWN; | 80     return Type::UNKNOWN; | 
| 55 | 81 | 
| 56   // Selector part | 82   // Selector part | 
| 57 | 83 | 
| 58   // Selector shouldn't be empty | 84   // Selector shouldn't be empty | 
|  | 85   seenSpaces |= scanner.skip(u' '); | 
| 59   if (scanner.done()) | 86   if (scanner.done()) | 
| 60     return Type::UNKNOWN; | 87     return Type::UNKNOWN; | 
| 61 | 88 | 
| 62   *selectorStart = scanner.position() + 1; | 89   data.mSelectorStart = scanner.position() + 1; | 
| 63   while (!scanner.done()) | 90   while (!scanner.done()) | 
| 64   { | 91   { | 
| 65     switch (scanner.next()) | 92     switch (scanner.next()) | 
| 66     { | 93     { | 
| 67       case u'{': | 94       case u'{': | 
| 68       case u'}': | 95       case u'}': | 
| 69         return Type::UNKNOWN; | 96         return Type::UNKNOWN; | 
| 70     } | 97     } | 
| 71   } | 98   } | 
| 72 | 99 | 
|  | 100   // We are done validating, now we can normalize whitespace and the domain part | 
|  | 101   if (seenSpaces) | 
|  | 102     NormalizeWhitespace(text, data.mDomainsEnd, data.mSelectorStart); | 
|  | 103   DependentString(text, 0, data.mDomainsEnd).toLower(); | 
|  | 104 | 
| 73   if (exception) | 105   if (exception) | 
| 74     return Type::ELEMHIDEEXCEPTION; | 106     return Type::ELEMHIDEEXCEPTION; | 
| 75 | 107 | 
| 76   do | 108   do | 
| 77   { | 109   { | 
| 78     // Is this a CSS property rule maybe? | 110     // Is this a CSS property rule maybe? | 
| 79     if (!prefixEnd) | 111     DependentString searchString(u"[-abp-properties="_str); | 
| 80     { | 112     data.mPrefixEnd = text.find(searchString, data.mSelectorStart); | 
| 81       // Caller doesn't care | 113     if (data.mPrefixEnd == text.npos || | 
| 82       break; | 114         data.mPrefixEnd + searchString.length() + 1 >= text.length()) | 
| 83     } |  | 
| 84 |  | 
| 85     const std::u16string searchString(u"[-abp-properties="); |  | 
| 86     *prefixEnd = text.find(searchString, *selectorStart); |  | 
| 87     if (*prefixEnd == text.npos || |  | 
| 88         *prefixEnd + searchString.length() + 1 >= text.length()) |  | 
| 89     { | 115     { | 
| 90       break; | 116       break; | 
| 91     } | 117     } | 
| 92 | 118 | 
| 93     *regexpStart = *prefixEnd + searchString.length() + 1; | 119     data.mRegexpStart = data.mPrefixEnd + searchString.length() + 1; | 
| 94     char16_t quotation = text[*regexpStart - 1]; | 120     char16_t quotation = text[data.mRegexpStart - 1]; | 
| 95     if (quotation != u'\'' && quotation != u'"') | 121     if (quotation != u'\'' && quotation != u'"') | 
| 96       break; | 122       break; | 
| 97 | 123 | 
| 98     *regexpEnd = text.find(quotation, *regexpStart); | 124     data.mRegexpEnd = text.find(quotation, data.mRegexpStart); | 
| 99     if (*regexpEnd == text.npos || *regexpEnd + 1 >= text.length() || | 125     if (data.mRegexpEnd == text.npos || data.mRegexpEnd + 1 >= text.length() || | 
| 100       text[*regexpEnd + 1] != u']') | 126       text[data.mRegexpEnd + 1] != u']') | 
| 101     { | 127     { | 
| 102       break; | 128       break; | 
| 103     } | 129     } | 
| 104 | 130 | 
| 105     *suffixStart = *regexpEnd + 2; | 131     data.mSuffixStart = data.mRegexpEnd + 2; | 
| 106     return Type::CSSPROPERTY; | 132     return Type::CSSPROPERTY; | 
| 107   } while (false); | 133   } while (false); | 
| 108 | 134 | 
| 109   return Type::ELEMHIDE; | 135   return Type::ELEMHIDE; | 
| 110 } | 136 } | 
| 111 | 137 | 
| 112 Filter* ElemHideBase::Create(const std::u16string& text) | 138 OwnedString ElemHideBase::GetSelectorDomain() const | 
| 113 { | 139 { | 
| 114   size_t domainsEnd; | 140   /* TODO this is inefficient */ | 
| 115   size_t selectorStart; | 141   OwnedString result; | 
| 116   size_t prefixEnd; | 142   if (mDomains) | 
| 117   size_t regexpStart; |  | 
| 118   size_t regexpEnd; |  | 
| 119   size_t suffixStart; |  | 
| 120   Type type = Parse(text, &domainsEnd, &selectorStart, &prefixEnd, ®expStart, |  | 
| 121       ®expEnd, &suffixStart); |  | 
| 122   if (type == Type::UNKNOWN) |  | 
| 123     return nullptr; |  | 
| 124 |  | 
| 125   try |  | 
| 126   { | 143   { | 
| 127     std::u16string domains(text.substr(0, domainsEnd)); | 144     for (auto it = mDomains->begin(); it != mDomains->end(); ++it) | 
| 128     std::u16string selector(text.substr(selectorStart)); |  | 
| 129     if (type == Type::ELEMHIDEEXCEPTION) |  | 
| 130       return new ElemHideException(text, domains, selector); |  | 
| 131     else if (type == Type::CSSPROPERTY) |  | 
| 132     { | 145     { | 
| 133       std::u16string prefix(text.substr(selectorStart, prefixEnd - selectorStart
     )); | 146       if (it->second && !it->first.empty()) | 
| 134       std::u16string regexpSource(text.substr(regexpStart, regexpEnd - regexpSta
     rt)); | 147       { | 
| 135       std::u16string suffix(text.substr(suffixStart)); | 148         if (!result.empty()) | 
| 136       return new CSSPropertyFilter(text, domains, selector, regexpSource, prefix
     , | 149           result.append(u','); | 
| 137           suffix); | 150         result.append(it->first); | 
| 138     } | 151       } | 
| 139     else |  | 
| 140       return new ElemHideFilter(text, domains, selector); |  | 
| 141   } |  | 
| 142   catch (const std::u16string& e) |  | 
| 143   { |  | 
| 144     return new InvalidFilter(text, e); |  | 
| 145   } |  | 
| 146 } |  | 
| 147 |  | 
| 148 const std::u16string ElemHideBase::GetSelectorDomain() const |  | 
| 149 { |  | 
| 150   std::u16string result; |  | 
| 151   for (auto it = domains.begin(); it != domains.end(); ++it) |  | 
| 152   { |  | 
| 153     if (it->second && !it->first.empty()) |  | 
| 154     { |  | 
| 155       if (!result.empty()) |  | 
| 156         result.append(u","); |  | 
| 157       result.append(it->first); |  | 
| 158     } | 152     } | 
| 159   } | 153   } | 
| 160   return result; | 154   return result; | 
| 161 } | 155 } | 
| LEFT | RIGHT | 
|---|