diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/WebBrowser/AdBlock/AdBlockManager.py --- a/src/eric7/WebBrowser/AdBlock/AdBlockManager.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/WebBrowser/AdBlock/AdBlockManager.py Wed Jul 13 14:55:47 2022 +0200 @@ -10,9 +10,7 @@ import os import contextlib -from PyQt6.QtCore import ( - pyqtSignal, QObject, QUrl, QUrlQuery, QByteArray, QMutex -) +from PyQt6.QtCore import pyqtSignal, QObject, QUrl, QUrlQuery, QByteArray, QMutex from PyQt6.QtWebEngineCore import QWebEngineUrlRequestInfo from EricWidgets import EricMessageBox @@ -31,26 +29,27 @@ class AdBlockManager(QObject): """ Class implementing the AdBlock manager. - + @signal rulesChanged() emitted after some rule has changed @signal requiredSubscriptionLoaded(subscription) emitted to indicate loading of a required subscription is finished (AdBlockSubscription) @signal enabledChanged(enabled) emitted to indicate a change of the enabled state """ + rulesChanged = pyqtSignal() requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription) enabledChanged = pyqtSignal(bool) - + def __init__(self, parent=None): """ Constructor - + @param parent reference to the parent object @type QObject """ super().__init__(parent) - + self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False @@ -61,9 +60,8 @@ self.__subscriptions = [] self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) - self.__limitedEasyList = Preferences.getWebBrowser( - "AdBlockUseLimitedEasyList") - + self.__limitedEasyList = Preferences.getWebBrowser("AdBlockUseLimitedEasyList") + self.__defaultSubscriptionUrlString = ( "abp:subscribe?location=" "https://easylist-downloads.adblockplus.org/easylist.txt&" @@ -74,76 +72,77 @@ "hoshsadiq/adblock-nocoin-list/master/nocoin.txt&" "title=NoCoin", ) - self.__customSubscriptionUrlString = ( - bytes(self.__customSubscriptionUrl().toEncoded()).decode() - ) - + self.__customSubscriptionUrlString = bytes( + self.__customSubscriptionUrl().toEncoded() + ).decode() + self.__mutex = QMutex() self.__matcher = AdBlockMatcher(self) - + self.rulesChanged.connect(self.__saveTimer.changeOccurred) self.rulesChanged.connect(self.__rulesChanged) - + self.__interceptor = AdBlockUrlInterceptor(self) - + from WebBrowser.WebBrowserWindow import WebBrowserWindow - WebBrowserWindow.networkManager().installUrlInterceptor( - self.__interceptor) - + + WebBrowserWindow.networkManager().installUrlInterceptor(self.__interceptor) + def __rulesChanged(self): """ Private slot handling a change of the AdBlock rules. """ from WebBrowser.WebBrowserWindow import WebBrowserWindow + WebBrowserWindow.mainWindow().reloadUserStyleSheet() self.__updateMatcher() - + def close(self): """ Public method to close the open search engines manager. """ self.__adBlockDialog and self.__adBlockDialog.close() - (self.__adBlockExceptionsDialog and - self.__adBlockExceptionsDialog.close()) - + (self.__adBlockExceptionsDialog and self.__adBlockExceptionsDialog.close()) + self.__saveTimer.saveIfNeccessary() - + def isEnabled(self): """ Public method to check, if blocking ads is enabled. - + @return flag indicating the enabled state @rtype bool """ if not self.__loaded: self.load() - + return self.__enabled - + def setEnabled(self, enabled): """ Public slot to set the enabled state. - + @param enabled flag indicating the enabled state @type bool """ if self.isEnabled() == enabled: return - + from WebBrowser.WebBrowserWindow import WebBrowserWindow + self.__enabled = enabled for mainWindow in WebBrowserWindow.mainWindows(): mainWindow.adBlockIcon().setEnabled(enabled) if enabled: self.__loadSubscriptions() - + self.rulesChanged.emit() self.enabledChanged.emit(enabled) - + def block(self, info): """ Public method to check, if a request should be blocked. - + @param info request info object @type QWebEngineUrlRequestInfo @return flag indicating to block the request @@ -152,93 +151,95 @@ with EricMutexLocker(self.__mutex): if not self.isEnabled(): return False - + urlString = bytes(info.requestUrl().toEncoded()).decode().lower() urlDomain = info.requestUrl().host().lower() urlScheme = info.requestUrl().scheme().lower() - - if ( - not self.canRunOnScheme(urlScheme) or - not self.__canBeBlocked(info.firstPartyUrl()) + + if not self.canRunOnScheme(urlScheme) or not self.__canBeBlocked( + info.firstPartyUrl() ): return False - + res = False blockedRule = self.__matcher.match(info, urlDomain, urlString) - + if blockedRule: res = True if ( - info.resourceType() == - QWebEngineUrlRequestInfo.ResourceType - .ResourceTypeMainFrame + info.resourceType() + == QWebEngineUrlRequestInfo.ResourceType.ResourceTypeMainFrame ): url = QUrl("eric:adblock") query = QUrlQuery() query.addQueryItem("rule", blockedRule.filter()) query.addQueryItem( - "subscription", blockedRule.subscription().title()) + "subscription", blockedRule.subscription().title() + ) url.setQuery(query) info.redirect(url) else: info.block(True) - + return res - + def canRunOnScheme(self, scheme): """ Public method to check, if AdBlock can be performed on the scheme. - + @param scheme scheme to check @type str @return flag indicating, that AdBlock can be performed @rtype bool """ return scheme not in ["data", "eric", "qthelp", "qrc", "file", "abp"] - + def page(self): """ Public method to get a reference to the page block object. - + @return reference to the page block object @rtype AdBlockPage """ if self.__adBlockPage is None: from .AdBlockPage import AdBlockPage + self.__adBlockPage = AdBlockPage(self) return self.__adBlockPage - + def __customSubscriptionLocation(self): """ Private method to generate the path for custom subscriptions. - + @return URL for custom subscriptions @rtype QUrl """ - dataDir = os.path.join(Utilities.getConfigDir(), "web_browser", - "subscriptions") + dataDir = os.path.join(Utilities.getConfigDir(), "web_browser", "subscriptions") if not os.path.exists(dataDir): os.makedirs(dataDir) fileName = os.path.join(dataDir, "adblock_subscription_custom") return QUrl.fromLocalFile(fileName) - + def __customSubscriptionUrl(self): """ Private method to generate the URL for custom subscriptions. - + @return URL for custom subscriptions @rtype QUrl """ location = self.__customSubscriptionLocation() encodedUrl = bytes(location.toEncoded()).decode() - url = QUrl("abp:subscribe?location={0}&title={1}".format( - encodedUrl, self.tr("Custom Rules"))) + url = QUrl( + "abp:subscribe?location={0}&title={1}".format( + encodedUrl, self.tr("Custom Rules") + ) + ) return url - + def customRules(self): """ Public method to get a subscription for custom rules. - + @return subscription object for custom rules @rtype AdBlockSubscription """ @@ -246,28 +247,28 @@ for subscription in self.__subscriptions: if subscription.location() == location: return subscription - + url = self.__customSubscriptionUrl() customAdBlockSubscription = AdBlockSubscription(url, True, self) self.addSubscription(customAdBlockSubscription) return customAdBlockSubscription - + def subscriptions(self): """ Public method to get all subscriptions. - + @return list of subscriptions @rtype list of AdBlockSubscription """ if not self.__loaded: self.load() - + return self.__subscriptions[:] - + def subscription(self, location): """ Public method to get a subscription based on its location. - + @param location location of the subscription to search for @type str @return subscription or None @@ -277,20 +278,20 @@ for subscription in self.__subscriptions: if subscription.location().toString() == location: return subscription - + return None - + def updateAllSubscriptions(self): """ Public method to update all subscriptions. """ for subscription in self.__subscriptions: subscription.updateNow() - + def removeSubscription(self, subscription, emitSignal=True): """ Public method to remove an AdBlock subscription. - + @param subscription AdBlock subscription to be removed @type AdBlockSubscription @param emitSignal flag indicating to send a signal @@ -298,12 +299,19 @@ """ if subscription is None: return - - if subscription.url().toString().startswith( - (self.__defaultSubscriptionUrlString, - self.__customSubscriptionUrlString)): + + if ( + subscription.url() + .toString() + .startswith( + ( + self.__defaultSubscriptionUrlString, + self.__customSubscriptionUrlString, + ) + ) + ): return - + with contextlib.suppress(ValueError): self.__subscriptions.remove(subscription) rulesFileName = subscription.rulesFileName() @@ -313,11 +321,11 @@ self.removeSubscription(requiresSubscription, False) if emitSignal: self.rulesChanged.emit() - + def addSubscriptionFromUrl(self, url): """ Public method to ad an AdBlock subscription given the abp URL. - + @param url URL to subscribe an AdBlock subscription @type QUrl @return flag indicating success @@ -325,58 +333,60 @@ """ if url.path() != "subscribe": return False - + title = QUrl.fromPercentEncoding( - QByteArray(QUrlQuery(url).queryItemValue("title").encode())) + QByteArray(QUrlQuery(url).queryItemValue("title").encode()) + ) if not title: return False - + res = EricMessageBox.yesNo( None, self.tr("Subscribe?"), self.tr( - """<p>Subscribe to this AdBlock subscription?</p>""" - """<p>{0}</p>""").format(title)) + """<p>Subscribe to this AdBlock subscription?</p>""" """<p>{0}</p>""" + ).format(title), + ) if res: from .AdBlockSubscription import AdBlockSubscription from WebBrowser.WebBrowserWindow import WebBrowserWindow - + dlg = WebBrowserWindow.adBlockManager().showDialog() subscription = AdBlockSubscription( - url, False, - WebBrowserWindow.adBlockManager()) + url, False, WebBrowserWindow.adBlockManager() + ) WebBrowserWindow.adBlockManager().addSubscription(subscription) dlg.addSubscription(subscription, False) dlg.setFocus() dlg.raise_() - + return res - + def addSubscription(self, subscription): """ Public method to add an AdBlock subscription. - + @param subscription AdBlock subscription to be added @type AdBlockSubscription """ if subscription is None: return - + self.__subscriptions.insert(-1, subscription) - + subscription.rulesChanged.connect(self.rulesChanged) subscription.changed.connect(self.rulesChanged) subscription.enabledChanged.connect(self.rulesChanged) - + self.rulesChanged.emit() - + def save(self): """ Public method to save the AdBlock subscriptions. """ if not self.__loaded: return - + Preferences.setWebBrowser("AdBlockEnabled", self.__enabled) if self.__subscriptionsLoaded: subscriptions = [] @@ -394,27 +404,27 @@ for subscription in requiresSubscriptions: subscriptions.insert(-1, subscription) # custom should be last Preferences.setWebBrowser("AdBlockSubscriptions", subscriptions) - + def load(self): """ Public method to load the AdBlock subscriptions. """ if self.__loaded: return - + self.__loaded = True - + self.__enabled = Preferences.getWebBrowser("AdBlockEnabled") if self.__enabled: self.__loadSubscriptions() - + def __loadSubscriptions(self): """ Private method to load the set of subscriptions. """ if self.__subscriptionsLoaded: return - + subscriptions = Preferences.getWebBrowser("AdBlockSubscriptions") if subscriptions: for subscription in subscriptions: @@ -424,9 +434,9 @@ subscriptions.append(self.__customSubscriptionUrlString) else: subscriptions = ( - [self.__defaultSubscriptionUrlString] + - list(self.__additionalDefaultSubscriptionUrlStrings) + - [self.__customSubscriptionUrlString] + [self.__defaultSubscriptionUrlString] + + list(self.__additionalDefaultSubscriptionUrlStrings) + + [self.__customSubscriptionUrlString] ) for subscription in subscriptions: url = QUrl.fromEncoded(subscription.encode("utf-8")) @@ -434,48 +444,48 @@ url, subscription.startswith(self.__customSubscriptionUrlString), self, - subscription.startswith(self.__defaultSubscriptionUrlString)) + subscription.startswith(self.__defaultSubscriptionUrlString), + ) adBlockSubscription.rulesChanged.connect(self.rulesChanged) adBlockSubscription.changed.connect(self.rulesChanged) adBlockSubscription.enabledChanged.connect(self.rulesChanged) + adBlockSubscription.rulesEnabledChanged.connect(self.__updateMatcher) adBlockSubscription.rulesEnabledChanged.connect( - self.__updateMatcher) - adBlockSubscription.rulesEnabledChanged.connect( - self.__saveTimer.changeOccurred) + self.__saveTimer.changeOccurred + ) self.__subscriptions.append(adBlockSubscription) - + self.__subscriptionsLoaded = True - + self.__updateMatcher() - + def loadRequiredSubscription(self, location, title): """ Public method to load a subscription required by another one. - + @param location location of the required subscription @type str @param title title of the required subscription @type str """ # Step 1: check, if the subscription is in the list of subscriptions - urlString = "abp:subscribe?location={0}&title={1}".format( - location, title) + urlString = "abp:subscribe?location={0}&title={1}".format(location, title) for subscription in self.__subscriptions: if subscription.url().toString().startswith(urlString): # We found it! return - + # Step 2: if it is not, get it url = QUrl.fromEncoded(urlString.encode("utf-8")) adBlockSubscription = AdBlockSubscription(url, False, self) self.addSubscription(adBlockSubscription) self.requiredSubscriptionLoaded.emit(adBlockSubscription) - + def getRequiresSubscriptions(self, subscription): """ Public method to get a list of subscriptions, that require the given one. - + @param subscription subscription to check for @type AdBlockSubscription @return list of subscription requiring the given one @@ -486,109 +496,108 @@ for subscription in self.__subscriptions: if subscription.requiresLocation() == location: subscriptions.append(subscription) - + return subscriptions - + def showDialog(self): """ Public slot to show the AdBlock subscription management dialog. - + @return reference to the dialog @rtype AdBlockDialog """ if self.__adBlockDialog is None: from .AdBlockDialog import AdBlockDialog + self.__adBlockDialog = AdBlockDialog(self) - + self.__adBlockDialog.show() return self.__adBlockDialog - + def elementHidingRules(self, url): """ Public method to get the element hiding rules. - - + + @param url URL to get hiding rules for @type QUrl @return element hiding rules @rtype str """ if ( - not self.isEnabled() or - not self.canRunOnScheme(url.scheme()) or - not self.__canBeBlocked(url) + not self.isEnabled() + or not self.canRunOnScheme(url.scheme()) + or not self.__canBeBlocked(url) ): return "" - + return self.__matcher.elementHidingRules() - + def elementHidingRulesForDomain(self, url): """ Public method to get the element hiding rules for a domain. - + @param url URL to get hiding rules for @type QUrl @return element hiding rules @rtype str """ if ( - not self.isEnabled() or - not self.canRunOnScheme(url.scheme()) or - not self.__canBeBlocked(url) + not self.isEnabled() + or not self.canRunOnScheme(url.scheme()) + or not self.__canBeBlocked(url) ): return "" - + return self.__matcher.elementHidingRulesForDomain(url.host()) - + def exceptions(self): """ Public method to get a list of excepted hosts. - + @return list of excepted hosts @rtype list of str """ return self.__exceptedHosts - + def setExceptions(self, hosts): """ Public method to set the list of excepted hosts. - + @param hosts list of excepted hosts @type list of str """ self.__exceptedHosts = [host.lower() for host in hosts] Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts) - + def addException(self, host): """ Public method to add an exception. - + @param host to be excepted @type str """ host = host.lower() if host and host not in self.__exceptedHosts: self.__exceptedHosts.append(host) - Preferences.setWebBrowser( - "AdBlockExceptions", self.__exceptedHosts) - + Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts) + def removeException(self, host): """ Public method to remove an exception. - + @param host to be removed from the list of exceptions @type str """ host = host.lower() if host in self.__exceptedHosts: self.__exceptedHosts.remove(host) - Preferences.setWebBrowser( - "AdBlockExceptions", self.__exceptedHosts) - + Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts) + def isHostExcepted(self, host): """ Public slot to check, if a host is excepted. - + @param host host to check @type str @return flag indicating an exception @@ -596,77 +605,80 @@ """ host = host.lower() return host in self.__exceptedHosts - + def showExceptionsDialog(self): """ Public method to show the AdBlock Exceptions dialog. - + @return reference to the exceptions dialog @rtype AdBlockExceptionsDialog """ if self.__adBlockExceptionsDialog is None: from .AdBlockExceptionsDialog import AdBlockExceptionsDialog + self.__adBlockExceptionsDialog = AdBlockExceptionsDialog() - + self.__adBlockExceptionsDialog.load(self.__exceptedHosts) self.__adBlockExceptionsDialog.show() return self.__adBlockExceptionsDialog - + def useLimitedEasyList(self): """ Public method to test, if limited EasyList rules shall be used. - + @return flag indicating limited EasyList rules @rtype bool """ return self.__limitedEasyList - + def setUseLimitedEasyList(self, limited): """ Public method to set the limited EasyList flag. - + @param limited flag indicating to use limited EasyList @type bool """ self.__limitedEasyList = limited - + for subscription in self.__subscriptions: - if subscription.url().toString().startswith( - self.__defaultSubscriptionUrlString): + if ( + subscription.url() + .toString() + .startswith(self.__defaultSubscriptionUrlString) + ): subscription.updateNow() - + Preferences.setWebBrowser("AdBlockUseLimitedEasyList", limited) - + def getDefaultSubscriptionUrl(self): """ Public method to get the default subscription URL. - + @return default subscription URL @rtype str """ return self.__defaultSubscriptionUrlString - + def __updateMatcher(self): """ Private slot to update the adblock matcher. """ from WebBrowser.WebBrowserWindow import WebBrowserWindow - WebBrowserWindow.networkManager().removeUrlInterceptor( - self.__interceptor) - + + WebBrowserWindow.networkManager().removeUrlInterceptor(self.__interceptor) + if self.__enabled: self.__matcher.update() else: self.__matcher.clear() - - WebBrowserWindow.networkManager().installUrlInterceptor( - self.__interceptor) - + + WebBrowserWindow.networkManager().installUrlInterceptor(self.__interceptor) + def __canBeBlocked(self, url): """ Private method to check, if the given URL could be blocked (i.e. is not whitelisted). - + @param url URL to be checked @type QUrl @return flag indicating that the given URL can be blocked