File indexing completed on 2024-05-12 04:57:50
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2014-2017 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "adblockmatcher.h" 0019 #include "adblockmanager.h" 0020 #include "adblockrule.h" 0021 #include "adblocksubscription.h" 0022 0023 AdBlockMatcher::AdBlockMatcher(AdBlockManager* manager) 0024 : QObject(manager) 0025 , m_manager(manager) 0026 { 0027 } 0028 0029 AdBlockMatcher::~AdBlockMatcher() 0030 { 0031 clear(); 0032 } 0033 0034 const AdBlockRule* AdBlockMatcher::match(const QWebEngineUrlRequestInfo &request, const QString &urlDomain, const QString &urlString) const 0035 { 0036 // Exception rules 0037 if (m_networkExceptionTree.find(request, urlDomain, urlString)) 0038 return nullptr; 0039 0040 int count = m_networkExceptionRules.count(); 0041 for (int i = 0; i < count; ++i) { 0042 const AdBlockRule* rule = m_networkExceptionRules.at(i); 0043 if (rule->networkMatch(request, urlDomain, urlString)) 0044 return nullptr; 0045 } 0046 0047 // Block rules 0048 if (const AdBlockRule* rule = m_networkBlockTree.find(request, urlDomain, urlString)) 0049 return rule; 0050 0051 count = m_networkBlockRules.count(); 0052 for (int i = 0; i < count; ++i) { 0053 const AdBlockRule* rule = m_networkBlockRules.at(i); 0054 if (rule->networkMatch(request, urlDomain, urlString)) 0055 return rule; 0056 } 0057 0058 return nullptr; 0059 } 0060 0061 bool AdBlockMatcher::adBlockDisabledForUrl(const QUrl &url) const 0062 { 0063 int count = m_documentRules.count(); 0064 0065 for (int i = 0; i < count; ++i) 0066 if (m_documentRules.at(i)->urlMatch(url)) 0067 return true; 0068 0069 return false; 0070 } 0071 0072 bool AdBlockMatcher::elemHideDisabledForUrl(const QUrl &url) const 0073 { 0074 if (adBlockDisabledForUrl(url)) 0075 return true; 0076 0077 int count = m_elemhideRules.count(); 0078 0079 for (int i = 0; i < count; ++i) 0080 if (m_elemhideRules.at(i)->urlMatch(url)) 0081 return true; 0082 0083 return false; 0084 } 0085 0086 bool AdBlockMatcher::genericElemHideDisabledForUrl(const QUrl &url) const 0087 { 0088 if (elemHideDisabledForUrl(url)) 0089 return true; 0090 0091 int count = m_generichideRules.count(); 0092 0093 for (int i = 0; i < count; ++i) 0094 if (m_generichideRules.at(i)->urlMatch(url)) 0095 return true; 0096 0097 return false; 0098 } 0099 0100 QString AdBlockMatcher::elementHidingRules() const 0101 { 0102 return m_elementHidingRules; 0103 } 0104 0105 QString AdBlockMatcher::elementHidingRulesForDomain(const QString &domain) const 0106 { 0107 QString rules; 0108 int addedRulesCount = 0; 0109 int count = m_domainRestrictedCssRules.count(); 0110 0111 for (int i = 0; i < count; ++i) { 0112 const AdBlockRule* rule = m_domainRestrictedCssRules.at(i); 0113 if (!rule->matchDomain(domain)) 0114 continue; 0115 0116 if (Q_UNLIKELY(addedRulesCount == 1000)) { 0117 rules.append(rule->cssSelector()); 0118 rules.append(QL1S("{display:none !important;}\n")); 0119 addedRulesCount = 0; 0120 } 0121 else { 0122 rules.append(rule->cssSelector() + QLatin1Char(',')); 0123 addedRulesCount++; 0124 } 0125 } 0126 0127 if (addedRulesCount != 0) { 0128 rules = rules.left(rules.size() - 1); 0129 rules.append(QL1S("{display:none !important;}\n")); 0130 } 0131 0132 return rules; 0133 } 0134 0135 void AdBlockMatcher::update() 0136 { 0137 clear(); 0138 0139 QHash<QString, const AdBlockRule*> cssRulesHash; 0140 QVector<const AdBlockRule*> exceptionCssRules; 0141 0142 const auto subscriptions = m_manager->subscriptions(); 0143 for (AdBlockSubscription* subscription : subscriptions) { 0144 const auto rules = subscription->allRules(); 0145 for (const AdBlockRule* rule : rules) { 0146 // Don't add internally disabled rules to cache 0147 if (rule->isInternalDisabled()) 0148 continue; 0149 // Or unsupported ones 0150 if (rule->isUnsupportedRule()) 0151 continue; 0152 0153 if (rule->isCssRule()) { 0154 // We will add only enabled css rules to cache, because there is no enabled/disabled 0155 // check on match. They are directly embedded to pages. 0156 if (!rule->isEnabled()) 0157 continue; 0158 0159 if (rule->isException()) 0160 exceptionCssRules.append(rule); 0161 else 0162 cssRulesHash.insert(rule->cssSelector(), rule); 0163 } 0164 else if (rule->isDocument()) { 0165 m_documentRules.append(rule); 0166 } 0167 else if (rule->isElemhide()) { 0168 m_elemhideRules.append(rule); 0169 } 0170 else if (rule->isGenerichide()) { 0171 m_generichideRules.append(rule); 0172 } 0173 else if (rule->isException()) { 0174 if (!m_networkExceptionTree.add(rule)) 0175 m_networkExceptionRules.append(rule); 0176 } else { 0177 if (!m_networkBlockTree.add(rule)) 0178 m_networkBlockRules.append(rule); 0179 } 0180 } 0181 } 0182 0183 for (const AdBlockRule* rule : std::as_const(exceptionCssRules)) { 0184 const AdBlockRule* originalRule = cssRulesHash.value(rule->cssSelector()); 0185 0186 // If we don't have this selector, the exception does nothing 0187 if (!originalRule) 0188 continue; 0189 0190 AdBlockRule* copiedRule = originalRule->copy(); 0191 copiedRule->m_options |= AdBlockRule::DomainRestrictedOption; 0192 copiedRule->m_blockedDomains.append(rule->m_allowedDomains); 0193 0194 cssRulesHash[rule->cssSelector()] = copiedRule; 0195 m_createdRules.append(copiedRule); 0196 } 0197 0198 // Apparently, excessive amount of selectors for one CSS rule is not what WebKit likes. 0199 // (In my testings, 4931 is the number that makes it crash) 0200 // So let's split it by 1000 selectors... 0201 int hidingRulesCount = 0; 0202 0203 QHashIterator<QString, const AdBlockRule*> it(cssRulesHash); 0204 while (it.hasNext()) { 0205 it.next(); 0206 const AdBlockRule* rule = it.value(); 0207 0208 if (rule->isDomainRestricted()) { 0209 m_domainRestrictedCssRules.append(rule); 0210 } 0211 else if (Q_UNLIKELY(hidingRulesCount == 1000)) { 0212 m_elementHidingRules.append(rule->cssSelector()); 0213 m_elementHidingRules.append(QL1S("{display:none !important;} ")); 0214 hidingRulesCount = 0; 0215 } 0216 else { 0217 m_elementHidingRules.append(rule->cssSelector() + QLatin1Char(',')); 0218 hidingRulesCount++; 0219 } 0220 } 0221 0222 if (hidingRulesCount != 0) { 0223 m_elementHidingRules = m_elementHidingRules.left(m_elementHidingRules.size() - 1); 0224 m_elementHidingRules.append(QL1S("{display:none !important;} ")); 0225 } 0226 } 0227 0228 void AdBlockMatcher::clear() 0229 { 0230 m_networkExceptionTree.clear(); 0231 m_networkExceptionRules.clear(); 0232 m_networkBlockTree.clear(); 0233 m_networkBlockRules.clear(); 0234 m_domainRestrictedCssRules.clear(); 0235 m_elementHidingRules.clear(); 0236 m_documentRules.clear(); 0237 m_elemhideRules.clear(); 0238 m_generichideRules.clear(); 0239 qDeleteAll(m_createdRules); 0240 m_createdRules.clear(); 0241 }