WebBrowser/AdBlock/AdBlockManager.py

branch
QtWebEngine
changeset 4847
a1a8eac81b54
parent 4631
5c1a96925da4
child 4858
19dff9c9cf26
equal deleted inserted replaced
4846:960e5e18894b 4847:a1a8eac81b54
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the AdBlock manager.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QFile
15
16 from .AdBlockSubscription import AdBlockSubscription
17
18 from Utilities.AutoSaver import AutoSaver
19 import Utilities
20 import Preferences
21
22
23 class AdBlockManager(QObject):
24 """
25 Class implementing the AdBlock manager.
26
27 @signal rulesChanged() emitted after some rule has changed
28 """
29 rulesChanged = pyqtSignal()
30 requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription)
31
32 def __init__(self, parent=None):
33 """
34 Constructor
35
36 @param parent reference to the parent object (QObject)
37 """
38 super(AdBlockManager, self).__init__(parent)
39
40 self.__loaded = False
41 self.__subscriptionsLoaded = False
42 self.__enabled = False
43 self.__adBlockDialog = None
44 self.__adBlockExceptionsDialog = None
45 self.__adBlockNetwork = None
46 self.__adBlockPage = None
47 self.__subscriptions = []
48 self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions")
49 self.__saveTimer = AutoSaver(self, self.save)
50
51 self.__defaultSubscriptionUrlString = \
52 "abp:subscribe?location=" \
53 "https://easylist-downloads.adblockplus.org/easylist.txt&"\
54 "title=EasyList"
55 self.__customSubscriptionUrlString = \
56 bytes(self.__customSubscriptionUrl().toEncoded()).decode()
57
58 self.rulesChanged.connect(self.__saveTimer.changeOccurred)
59
60 def close(self):
61 """
62 Public method to close the open search engines manager.
63 """
64 self.__adBlockDialog and self.__adBlockDialog.close()
65 self.__adBlockExceptionsDialog and \
66 self.__adBlockExceptionsDialog.close()
67
68 self.__saveTimer.saveIfNeccessary()
69
70 def isEnabled(self):
71 """
72 Public method to check, if blocking ads is enabled.
73
74 @return flag indicating the enabled state (boolean)
75 """
76 if not self.__loaded:
77 self.load()
78
79 return self.__enabled
80
81 def setEnabled(self, enabled):
82 """
83 Public slot to set the enabled state.
84
85 @param enabled flag indicating the enabled state (boolean)
86 """
87 if self.isEnabled() == enabled:
88 return
89
90 from WebBrowser.WebBrowserWindow import WebBrowserWindow
91 self.__enabled = enabled
92 for mainWindow in WebBrowserWindow.mainWindows():
93 mainWindow.adBlockIcon().setEnabled(enabled)
94 if enabled:
95 self.__loadSubscriptions()
96 self.rulesChanged.emit()
97 ##
98 ## def network(self):
99 ## """
100 ## Public method to get a reference to the network block object.
101 ##
102 ## @return reference to the network block object (AdBlockNetwork)
103 ## """
104 ## if self.__adBlockNetwork is None:
105 ## from .AdBlockNetwork import AdBlockNetwork
106 ## self.__adBlockNetwork = AdBlockNetwork(self)
107 ## return self.__adBlockNetwork
108
109 def page(self):
110 """
111 Public method to get a reference to the page block object.
112
113 @return reference to the page block object (AdBlockPage)
114 """
115 if self.__adBlockPage is None:
116 from .AdBlockPage import AdBlockPage
117 self.__adBlockPage = AdBlockPage(self)
118 return self.__adBlockPage
119
120 def __customSubscriptionLocation(self):
121 """
122 Private method to generate the path for custom subscriptions.
123
124 @return URL for custom subscriptions (QUrl)
125 """
126 dataDir = os.path.join(Utilities.getConfigDir(), "web_browser",
127 "subscriptions")
128 if not os.path.exists(dataDir):
129 os.makedirs(dataDir)
130 fileName = os.path.join(dataDir, "adblock_subscription_custom")
131 return QUrl.fromLocalFile(fileName)
132
133 def __customSubscriptionUrl(self):
134 """
135 Private method to generate the URL for custom subscriptions.
136
137 @return URL for custom subscriptions (QUrl)
138 """
139 location = self.__customSubscriptionLocation()
140 encodedUrl = bytes(location.toEncoded()).decode()
141 url = QUrl("abp:subscribe?location={0}&title={1}".format(
142 encodedUrl, self.tr("Custom Rules")))
143 return url
144
145 def customRules(self):
146 """
147 Public method to get a subscription for custom rules.
148
149 @return subscription object for custom rules (AdBlockSubscription)
150 """
151 location = self.__customSubscriptionLocation()
152 for subscription in self.__subscriptions:
153 if subscription.location() == location:
154 return subscription
155
156 url = self.__customSubscriptionUrl()
157 customAdBlockSubscription = AdBlockSubscription(url, True, self)
158 self.addSubscription(customAdBlockSubscription)
159 return customAdBlockSubscription
160
161 def subscriptions(self):
162 """
163 Public method to get all subscriptions.
164
165 @return list of subscriptions (list of AdBlockSubscription)
166 """
167 if not self.__loaded:
168 self.load()
169
170 return self.__subscriptions[:]
171
172 def subscription(self, location):
173 """
174 Public method to get a subscription based on its location.
175
176 @param location location of the subscription to search for (string)
177 @return subscription or None (AdBlockSubscription)
178 """
179 if location != "":
180 for subscription in self.__subscriptions:
181 if subscription.location().toString() == location:
182 return subscription
183
184 return None
185
186 def updateAllSubscriptions(self):
187 """
188 Public method to update all subscriptions.
189 """
190 for subscription in self.__subscriptions:
191 subscription.updateNow()
192
193 def removeSubscription(self, subscription, emitSignal=True):
194 """
195 Public method to remove an AdBlock subscription.
196
197 @param subscription AdBlock subscription to be removed
198 (AdBlockSubscription)
199 @param emitSignal flag indicating to send a signal (boolean)
200 """
201 if subscription is None:
202 return
203
204 if subscription.url().toString().startswith(
205 (self.__defaultSubscriptionUrlString,
206 self.__customSubscriptionUrlString)):
207 return
208
209 try:
210 self.__subscriptions.remove(subscription)
211 rulesFileName = subscription.rulesFileName()
212 QFile.remove(rulesFileName)
213 requiresSubscriptions = self.getRequiresSubscriptions(subscription)
214 for requiresSubscription in requiresSubscriptions:
215 self.removeSubscription(requiresSubscription, False)
216 if emitSignal:
217 self.rulesChanged.emit()
218 except ValueError:
219 pass
220
221 def addSubscription(self, subscription):
222 """
223 Public method to add an AdBlock subscription.
224
225 @param subscription AdBlock subscription to be added
226 (AdBlockSubscription)
227 """
228 if subscription is None:
229 return
230
231 self.__subscriptions.insert(-1, subscription)
232
233 subscription.rulesChanged.connect(self.rulesChanged)
234 subscription.changed.connect(self.rulesChanged)
235 subscription.enabledChanged.connect(self.rulesChanged)
236
237 self.rulesChanged.emit()
238
239 def save(self):
240 """
241 Public method to save the AdBlock subscriptions.
242 """
243 if not self.__loaded:
244 return
245
246 Preferences.setWebBrowser("AdBlockEnabled", self.__enabled)
247 if self.__subscriptionsLoaded:
248 subscriptions = []
249 requiresSubscriptions = []
250 # intermediate store for subscription requiring others
251 for subscription in self.__subscriptions:
252 if subscription is None:
253 continue
254 urlString = bytes(subscription.url().toEncoded()).decode()
255 if "requiresLocation" in urlString:
256 requiresSubscriptions.append(urlString)
257 else:
258 subscriptions.append(urlString)
259 subscription.saveRules()
260 for subscription in requiresSubscriptions:
261 subscriptions.insert(-1, subscription) # custom should be last
262 Preferences.setWebBrowser("AdBlockSubscriptions", subscriptions)
263
264 def load(self):
265 """
266 Public method to load the AdBlock subscriptions.
267 """
268 if self.__loaded:
269 return
270
271 self.__loaded = True
272
273 self.__enabled = Preferences.getWebBrowser("AdBlockEnabled")
274 if self.__enabled:
275 self.__loadSubscriptions()
276
277 def __loadSubscriptions(self):
278 """
279 Private method to load the set of subscriptions.
280 """
281 if self.__subscriptionsLoaded:
282 return
283
284 subscriptions = Preferences.getWebBrowser("AdBlockSubscriptions")
285 if subscriptions:
286 for subscription in subscriptions:
287 if subscription.startswith(
288 self.__defaultSubscriptionUrlString):
289 break
290 else:
291 subscriptions.insert(0, self.__defaultSubscriptionUrlString)
292 for subscription in subscriptions:
293 if subscription.startswith(self.__customSubscriptionUrlString):
294 break
295 else:
296 subscriptions.append(self.__customSubscriptionUrlString)
297 else:
298 subscriptions = [self.__defaultSubscriptionUrlString,
299 self.__customSubscriptionUrlString]
300 for subscription in subscriptions:
301 url = QUrl.fromEncoded(subscription.encode("utf-8"))
302 adBlockSubscription = AdBlockSubscription(
303 url,
304 subscription.startswith(self.__customSubscriptionUrlString),
305 self,
306 subscription.startswith(self.__defaultSubscriptionUrlString))
307 adBlockSubscription.rulesChanged.connect(self.rulesChanged)
308 adBlockSubscription.changed.connect(self.rulesChanged)
309 adBlockSubscription.enabledChanged.connect(self.rulesChanged)
310 self.__subscriptions.append(adBlockSubscription)
311
312 self.__subscriptionsLoaded = True
313
314 def loadRequiredSubscription(self, location, title):
315 """
316 Public method to load a subscription required by another one.
317
318 @param location location of the required subscription (string)
319 @param title title of the required subscription (string)
320 """
321 # Step 1: check, if the subscription is in the list of subscriptions
322 urlString = "abp:subscribe?location={0}&title={1}".format(
323 location, title)
324 for subscription in self.__subscriptions:
325 if subscription.url().toString().startswith(urlString):
326 # We found it!
327 return
328
329 # Step 2: if it is not, get it
330 url = QUrl.fromEncoded(urlString.encode("utf-8"))
331 adBlockSubscription = AdBlockSubscription(url, False, self)
332 self.addSubscription(adBlockSubscription)
333 self.requiredSubscriptionLoaded.emit(adBlockSubscription)
334
335 def getRequiresSubscriptions(self, subscription):
336 """
337 Public method to get a list of subscriptions, that require the given
338 one.
339
340 @param subscription subscription to check for (AdBlockSubscription)
341 @return list of subscription requiring the given one (list of
342 AdBlockSubscription)
343 """
344 subscriptions = []
345 location = subscription.location().toString()
346 for subscription in self.__subscriptions:
347 if subscription.requiresLocation() == location:
348 subscriptions.append(subscription)
349
350 return subscriptions
351
352 def showDialog(self):
353 """
354 Public slot to show the AdBlock subscription management dialog.
355
356 @return reference to the dialog (AdBlockDialog)
357 """
358 if self.__adBlockDialog is None:
359 from .AdBlockDialog import AdBlockDialog
360 self.__adBlockDialog = AdBlockDialog()
361
362 self.__adBlockDialog.show()
363 return self.__adBlockDialog
364
365 def showRule(self):
366 """
367 Public slot to show an AdBlock rule.
368 """
369 act = self.sender()
370 if act is not None:
371 rule = act.data()
372 if rule:
373 self.showDialog().showRule(rule)
374
375 def elementHidingRules(self):
376 """
377 Public method to get the element hiding rules.
378
379 @return element hiding rules (string)
380 """
381 if not self.__enabled:
382 return ""
383
384 rules = ""
385
386 for subscription in self.__subscriptions:
387 rules += subscription.elementHidingRules()
388
389 if rules:
390 # remove last ",
391 rules = rules[:-1]
392
393 return rules
394
395 def elementHidingRulesForDomain(self, url):
396 """
397 Public method to get the element hiding rules for a domain.
398
399 @param url URL to get hiding rules for (QUrl)
400 @return element hiding rules (string)
401 """
402 if not self.__enabled:
403 return ""
404
405 rules = ""
406
407 for subscription in self.__subscriptions:
408 if subscription.elemHideDisabledForUrl(url):
409 continue
410
411 rules += subscription.elementHidingRulesForDomain(url.host())
412
413 if rules:
414 # remove last ","
415 rules = rules[:-1]
416
417 rules += "{display:none !important;}\n"
418
419 return rules
420
421 def exceptions(self):
422 """
423 Public method to get a list of excepted hosts.
424
425 @return list of excepted hosts (list of string)
426 """
427 return self.__exceptedHosts
428
429 def setExceptions(self, hosts):
430 """
431 Public method to set the list of excepted hosts.
432
433 @param hosts list of excepted hosts (list of string)
434 """
435 self.__exceptedHosts = hosts[:]
436 Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts)
437
438 def addException(self, host):
439 """
440 Public method to add an exception.
441
442 @param host to be excepted (string)
443 """
444 if host and host not in self.__exceptedHosts:
445 self.__exceptedHosts.append(host)
446 Preferences.setWebBrowser(
447 "AdBlockExceptions", self.__exceptedHosts)
448
449 def removeException(self, host):
450 """
451 Public method to remove an exception.
452
453 @param host to be removed from the list of exceptions (string)
454 """
455 if host in self.__exceptedHosts:
456 self.__exceptedHosts.remove(host)
457 Preferences.setWebBrowser(
458 "AdBlockExceptions", self.__exceptedHosts)
459
460 def isHostExcepted(self, host):
461 """
462 Public slot to check, if a host is excepted.
463
464 @param host host to check (string)
465 @return flag indicating an exception (boolean)
466 """
467 return host in self.__exceptedHosts
468
469 def showExceptionsDialog(self):
470 """
471 Public method to show the AdBlock Exceptions dialog.
472
473 @return reference to the exceptions dialog (AdBlockExceptionsDialog)
474 """
475 if self.__adBlockExceptionsDialog is None:
476 from .AdBlockExceptionsDialog import AdBlockExceptionsDialog
477 self.__adBlockExceptionsDialog = AdBlockExceptionsDialog()
478
479 self.__adBlockExceptionsDialog.load(self.__exceptedHosts)
480 self.__adBlockExceptionsDialog.show()
481 return self.__adBlockExceptionsDialog

eric ide

mercurial