6027:d056a536670e | 6028:859f6894eed9 |
---|---|
30 Class implementing the AdBlock subscription. | 30 Class implementing the AdBlock subscription. |
31 | 31 |
32 @signal changed() emitted after the subscription has changed | 32 @signal changed() emitted after the subscription has changed |
33 @signal rulesChanged() emitted after the subscription's rules have changed | 33 @signal rulesChanged() emitted after the subscription's rules have changed |
34 @signal enabledChanged(bool) emitted after the enabled state was changed | 34 @signal enabledChanged(bool) emitted after the enabled state was changed |
35 @signal rulesEnabledChanged() emitted after a rule enabled state was | |
36 changed | |
35 """ | 37 """ |
36 changed = pyqtSignal() | 38 changed = pyqtSignal() |
37 rulesChanged = pyqtSignal() | 39 rulesChanged = pyqtSignal() |
38 enabledChanged = pyqtSignal(bool) | 40 enabledChanged = pyqtSignal(bool) |
41 rulesEnabledChanged = pyqtSignal() | |
39 | 42 |
40 def __init__(self, url, custom, parent=None, default=False): | 43 def __init__(self, url, custom, parent=None, default=False): |
41 """ | 44 """ |
42 Constructor | 45 Constructor |
43 | 46 |
62 | 65 |
63 self.__updatePeriod = 0 # update period in hours, 0 = use default | 66 self.__updatePeriod = 0 # update period in hours, 0 = use default |
64 self.__remoteModified = QDateTime() | 67 self.__remoteModified = QDateTime() |
65 | 68 |
66 self.__rules = [] # list containing all AdBlock rules | 69 self.__rules = [] # list containing all AdBlock rules |
67 | |
68 self.__networkExceptionRules = [] | |
69 self.__networkBlockRules = [] | |
70 self.__domainRestrictedCssRules = [] | |
71 self.__elementHidingRules = "" | |
72 self.__documentRules = [] | |
73 self.__elemhideRules = [] | |
74 | 70 |
75 self.__checksumRe = re.compile( | 71 self.__checksumRe = re.compile( |
76 r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""", | 72 r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""", |
77 re.IGNORECASE | re.MULTILINE) | 73 re.IGNORECASE | re.MULTILINE) |
78 self.__expiresRe = re.compile( | 74 self.__expiresRe = re.compile( |
103 | 99 |
104 def __parseUrl(self, url): | 100 def __parseUrl(self, url): |
105 """ | 101 """ |
106 Private method to parse the AdBlock URL for the subscription. | 102 Private method to parse the AdBlock URL for the subscription. |
107 | 103 |
108 @param url AdBlock URL for the subscription (QUrl) | 104 @param url AdBlock URL for the subscription |
105 @type QUrl | |
109 """ | 106 """ |
110 if url.scheme() != "abp": | 107 if url.scheme() != "abp": |
111 return | 108 return |
112 | 109 |
113 if url.path() != "subscribe": | 110 if url.path() != "subscribe": |
140 | 137 |
141 def url(self): | 138 def url(self): |
142 """ | 139 """ |
143 Public method to generate the URL for this subscription. | 140 Public method to generate the URL for this subscription. |
144 | 141 |
145 @return AdBlock URL for the subscription (QUrl) | 142 @return AdBlock URL for the subscription |
143 @rtype QUrl | |
146 """ | 144 """ |
147 url = QUrl() | 145 url = QUrl() |
148 url.setScheme("abp") | 146 url.setScheme("abp") |
149 url.setPath("subscribe") | 147 url.setPath("subscribe") |
150 | 148 |
167 | 165 |
168 def isEnabled(self): | 166 def isEnabled(self): |
169 """ | 167 """ |
170 Public method to check, if the subscription is enabled. | 168 Public method to check, if the subscription is enabled. |
171 | 169 |
172 @return flag indicating the enabled status (boolean) | 170 @return flag indicating the enabled status |
171 @rtype bool | |
173 """ | 172 """ |
174 return self.__enabled | 173 return self.__enabled |
175 | 174 |
176 def setEnabled(self, enabled): | 175 def setEnabled(self, enabled): |
177 """ | 176 """ |
178 Public method to set the enabled status. | 177 Public method to set the enabled status. |
179 | 178 |
180 @param enabled flag indicating the enabled status (boolean) | 179 @param enabled flag indicating the enabled status |
180 @type bool | |
181 """ | 181 """ |
182 if self.__enabled == enabled: | 182 if self.__enabled == enabled: |
183 return | 183 return |
184 | 184 |
185 self.__enabled = enabled | 185 self.__enabled = enabled |
187 | 187 |
188 def title(self): | 188 def title(self): |
189 """ | 189 """ |
190 Public method to get the subscription title. | 190 Public method to get the subscription title. |
191 | 191 |
192 @return subscription title (string) | 192 @return subscription title |
193 @rtype string | |
193 """ | 194 """ |
194 return self.__title | 195 return self.__title |
195 | 196 |
196 def setTitle(self, title): | 197 def setTitle(self, title): |
197 """ | 198 """ |
198 Public method to set the subscription title. | 199 Public method to set the subscription title. |
199 | 200 |
200 @param title subscription title (string) | 201 @param title subscription title |
202 @type str | |
201 """ | 203 """ |
202 if self.__title == title: | 204 if self.__title == title: |
203 return | 205 return |
204 | 206 |
205 self.__title = title | 207 self.__title = title |
207 | 209 |
208 def location(self): | 210 def location(self): |
209 """ | 211 """ |
210 Public method to get the subscription location. | 212 Public method to get the subscription location. |
211 | 213 |
212 @return URL of the subscription location (QUrl) | 214 @return URL of the subscription location |
215 @rtype QUrl | |
213 """ | 216 """ |
214 return QUrl.fromEncoded(self.__location) | 217 return QUrl.fromEncoded(self.__location) |
215 | 218 |
216 def setLocation(self, url): | 219 def setLocation(self, url): |
217 """ | 220 """ |
218 Public method to set the subscription location. | 221 Public method to set the subscription location. |
219 | 222 |
220 @param url URL of the subscription location (QUrl) | 223 @param url URL of the subscription location |
224 @type QUrl | |
221 """ | 225 """ |
222 if url == self.location(): | 226 if url == self.location(): |
223 return | 227 return |
224 | 228 |
225 self.__location = url.toEncoded() | 229 self.__location = url.toEncoded() |
228 | 232 |
229 def requiresLocation(self): | 233 def requiresLocation(self): |
230 """ | 234 """ |
231 Public method to get the location of a required subscription. | 235 Public method to get the location of a required subscription. |
232 | 236 |
233 @return location of a required subscription (string) | 237 @return location of a required subscription |
238 @rtype str | |
234 """ | 239 """ |
235 return self.__requiresLocation | 240 return self.__requiresLocation |
236 | 241 |
237 def lastUpdate(self): | 242 def lastUpdate(self): |
238 """ | 243 """ |
239 Public method to get the date and time of the last update. | 244 Public method to get the date and time of the last update. |
240 | 245 |
241 @return date and time of the last update (QDateTime) | 246 @return date and time of the last update |
247 @rtype QDateTime | |
242 """ | 248 """ |
243 return self.__lastUpdate | 249 return self.__lastUpdate |
244 | 250 |
245 def rulesFileName(self): | 251 def rulesFileName(self): |
246 """ | 252 """ |
247 Public method to get the name of the rules file. | 253 Public method to get the name of the rules file. |
248 | 254 |
249 @return name of the rules file (string) | 255 @return name of the rules file |
256 @rtype str | |
250 """ | 257 """ |
251 if self.location().scheme() == "file": | 258 if self.location().scheme() == "file": |
252 return self.location().toLocalFile() | 259 return self.location().toLocalFile() |
253 | 260 |
254 if self.__location.isEmpty(): | 261 if self.__location.isEmpty(): |
320 int(day)) | 327 int(day)) |
321 ) | 328 ) |
322 if time: | 329 if time: |
323 self.__remoteModified.setTime( | 330 self.__remoteModified.setTime( |
324 QTime(int(hour), int(minute))) | 331 QTime(int(hour), int(minute))) |
325 self.__populateCache() | |
326 self.changed.emit() | 332 self.changed.emit() |
327 elif not fileName.endswith("_custom"): | 333 elif not fileName.endswith("_custom"): |
328 self.__lastUpdate = QDateTime() | 334 self.__lastUpdate = QDateTime() |
329 | 335 |
330 self.checkForUpdate() | 336 self.checkForUpdate() |
443 | 449 |
444 def __validateCheckSum(self, fileName): | 450 def __validateCheckSum(self, fileName): |
445 """ | 451 """ |
446 Private method to check the subscription file's checksum. | 452 Private method to check the subscription file's checksum. |
447 | 453 |
448 @param fileName name of the file containing the subscription (string) | 454 @param fileName name of the file containing the subscription |
449 @return flag indicating a valid file (boolean). A file is considered | 455 @type str |
456 @return flag indicating a valid file. A file is considered | |
450 valid, if the checksum is OK, the file does not contain a | 457 valid, if the checksum is OK, the file does not contain a |
451 checksum (i.e. cannot be checked) or we are using the limited | 458 checksum (i.e. cannot be checked) or we are using the limited |
452 EasyList (because we fiddled with the original). | 459 EasyList (because we fiddled with the original). |
460 @rtype bool | |
453 """ | 461 """ |
454 try: | 462 try: |
455 f = open(fileName, "r", encoding="utf-8") | 463 f = open(fileName, "r", encoding="utf-8") |
456 data = f.read() | 464 data = f.read() |
457 f.close() | 465 f.close() |
513 if not self.__rules or not self.__rules[0].isHeader(): | 521 if not self.__rules or not self.__rules[0].isHeader(): |
514 textStream << "[Adblock Plus 1.1.1]\n" | 522 textStream << "[Adblock Plus 1.1.1]\n" |
515 for rule in self.__rules: | 523 for rule in self.__rules: |
516 textStream << rule.filter() << "\n" | 524 textStream << rule.filter() << "\n" |
517 | 525 |
518 def match(self, req, urlDomain, urlString): | |
519 """ | |
520 Public method to check the subscription for a matching rule. | |
521 | |
522 @param req reference to the network request (QWebEngineUrlRequestInfo) | |
523 @param urlDomain domain of the URL (string) | |
524 @param urlString URL (string) | |
525 @return reference to the rule object or None (AdBlockRule) | |
526 """ | |
527 for rule in self.__networkExceptionRules: | |
528 if rule.networkMatch(req, urlDomain, urlString): | |
529 return None | |
530 | |
531 for rule in self.__networkBlockRules: | |
532 if rule.networkMatch(req, urlDomain, urlString): | |
533 return rule | |
534 | |
535 return None | |
536 | |
537 def adBlockDisabledForUrl(self, url): | |
538 """ | |
539 Public method to check, if AdBlock is disabled for the given URL. | |
540 | |
541 @param url URL to check (QUrl) | |
542 @return flag indicating disabled state (boolean) | |
543 """ | |
544 for rule in self.__documentRules: | |
545 if rule.urlMatch(url): | |
546 return True | |
547 | |
548 return False | |
549 | |
550 def elemHideDisabledForUrl(self, url): | |
551 """ | |
552 Public method to check, if element hiding is disabled for the given | |
553 URL. | |
554 | |
555 @param url URL to check (QUrl) | |
556 @return flag indicating disabled state (boolean) | |
557 """ | |
558 if self.adBlockDisabledForUrl(url): | |
559 return True | |
560 | |
561 for rule in self.__elemhideRules: | |
562 if rule.urlMatch(url): | |
563 return True | |
564 | |
565 return False | |
566 | |
567 def elementHidingRules(self): | |
568 """ | |
569 Public method to get the element hiding rules. | |
570 | |
571 @return element hiding rules (string) | |
572 """ | |
573 return self.__elementHidingRules | |
574 | |
575 def elementHidingRulesForDomain(self, domain): | |
576 """ | |
577 Public method to get the element hiding rules for the given domain. | |
578 | |
579 @param domain domain name (string) | |
580 @return element hiding rules (string) | |
581 """ | |
582 rules = "" | |
583 | |
584 for rule in self.__domainRestrictedCssRules: | |
585 if rule.matchDomain(domain): | |
586 rules += rule.cssSelector() + "," | |
587 | |
588 return rules | |
589 | |
590 def rule(self, offset): | 526 def rule(self, offset): |
591 """ | 527 """ |
592 Public method to get a specific rule. | 528 Public method to get a specific rule. |
593 | 529 |
594 @param offset offset of the rule (integer) | 530 @param offset offset of the rule |
595 @return requested rule (AdBlockRule) | 531 @type int |
532 @return requested rule | |
533 @rtype AdBlockRule | |
596 """ | 534 """ |
597 if offset >= len(self.__rules): | 535 if offset >= len(self.__rules): |
598 return None | 536 return None |
599 | 537 |
600 return self.__rules[offset] | 538 return self.__rules[offset] |
601 | 539 |
602 def allRules(self): | 540 def allRules(self): |
603 """ | 541 """ |
604 Public method to get the list of rules. | 542 Public method to get the list of rules. |
605 | 543 |
606 @return list of rules (list of AdBlockRule) | 544 @return list of rules |
545 @rtype list of AdBlockRule | |
607 """ | 546 """ |
608 return self.__rules[:] | 547 return self.__rules[:] |
609 | 548 |
610 def addRule(self, rule): | 549 def addRule(self, rule): |
611 """ | 550 """ |
612 Public method to add a rule. | 551 Public method to add a rule. |
613 | 552 |
614 @param rule reference to the rule to add (AdBlockRule) | 553 @param rule reference to the rule to add |
615 @return offset of the rule (integer) | 554 @type AdBlockRule |
555 @return offset of the rule | |
556 @rtype int | |
616 """ | 557 """ |
617 self.__rules.append(rule) | 558 self.__rules.append(rule) |
618 self.__populateCache() | |
619 self.rulesChanged.emit() | 559 self.rulesChanged.emit() |
620 | 560 |
621 return len(self.__rules) - 1 | 561 return len(self.__rules) - 1 |
622 | 562 |
623 def removeRule(self, offset): | 563 def removeRule(self, offset): |
624 """ | 564 """ |
625 Public method to remove a rule given the offset. | 565 Public method to remove a rule given the offset. |
626 | 566 |
627 @param offset offset of the rule to remove (integer) | 567 @param offset offset of the rule to remove |
568 @type int | |
628 """ | 569 """ |
629 if offset < 0 or offset > len(self.__rules): | 570 if offset < 0 or offset > len(self.__rules): |
630 return | 571 return |
631 | 572 |
632 del self.__rules[offset] | 573 del self.__rules[offset] |
633 self.__populateCache() | |
634 self.rulesChanged.emit() | 574 self.rulesChanged.emit() |
635 | 575 |
636 def replaceRule(self, rule, offset): | 576 def replaceRule(self, rule, offset): |
637 """ | 577 """ |
638 Public method to replace a rule given the offset. | 578 Public method to replace a rule given the offset. |
639 | 579 |
640 @param rule reference to the rule to set (AdBlockRule) | 580 @param rule reference to the rule to set |
641 @param offset offset of the rule to remove (integer) | 581 @type AdBlockRule |
642 @return requested rule (AdBlockRule) | 582 @param offset offset of the rule to remove |
583 @type int | |
584 @return requested rule | |
585 @rtype AdBlockRule | |
643 """ | 586 """ |
644 if offset >= len(self.__rules): | 587 if offset >= len(self.__rules): |
645 return None | 588 return None |
646 | 589 |
647 self.__rules[offset] = rule | 590 self.__rules[offset] = rule |
648 self.__populateCache() | |
649 self.rulesChanged.emit() | 591 self.rulesChanged.emit() |
650 | 592 |
651 return self.__rules[offset] | 593 return self.__rules[offset] |
652 | 594 |
653 def __populateCache(self): | |
654 """ | |
655 Private method to populate the various rule caches. | |
656 """ | |
657 self.__networkExceptionRules = [] | |
658 self.__networkBlockRules = [] | |
659 self.__domainRestrictedCssRules = [] | |
660 self.__elementHidingRules = "" | |
661 self.__documentRules = [] | |
662 self.__elemhideRules = [] | |
663 | |
664 for rule in self.__rules: | |
665 if not rule.isEnabled(): | |
666 continue | |
667 | |
668 if rule.isCSSRule(): | |
669 if rule.isDomainRestricted(): | |
670 self.__domainRestrictedCssRules.append(rule) | |
671 else: | |
672 self.__elementHidingRules += rule.cssSelector() + "," | |
673 elif rule.isDocument(): | |
674 self.__documentRules.append(rule) | |
675 elif rule.isElementHiding(): | |
676 self.__elemhideRules.append(rule) | |
677 elif rule.isException(): | |
678 self.__networkExceptionRules.append(rule) | |
679 else: | |
680 self.__networkBlockRules.append(rule) | |
681 | |
682 def canEditRules(self): | 595 def canEditRules(self): |
683 """ | 596 """ |
684 Public method to check, if rules can be edited. | 597 Public method to check, if rules can be edited. |
685 | 598 |
686 @return flag indicating rules may be edited (boolean) | 599 @return flag indicating rules may be edited |
600 @rtype bool | |
687 """ | 601 """ |
688 return self.__custom | 602 return self.__custom |
689 | 603 |
690 def canBeRemoved(self): | 604 def canBeRemoved(self): |
691 """ | 605 """ |
692 Public method to check, if the subscription can be removed. | 606 Public method to check, if the subscription can be removed. |
693 | 607 |
694 @return flag indicating removal is allowed (boolean) | 608 @return flag indicating removal is allowed |
609 @rtype bool | |
695 """ | 610 """ |
696 return not self.__custom and not self.__defaultSubscription | 611 return not self.__custom and not self.__defaultSubscription |
697 | 612 |
698 def setRuleEnabled(self, offset, enabled): | 613 def setRuleEnabled(self, offset, enabled): |
699 """ | 614 """ |
700 Public method to enable a specific rule. | 615 Public method to enable a specific rule. |
701 | 616 |
702 @param offset offset of the rule (integer) | 617 @param offset offset of the rule |
703 @param enabled new enabled state (boolean) | 618 @type int |
704 @return reference to the changed rule (AdBlockRule) | 619 @param enabled new enabled state |
620 @type bool | |
621 @return reference to the changed rule | |
622 @rtype AdBlockRule | |
705 """ | 623 """ |
706 if offset >= len(self.__rules): | 624 if offset >= len(self.__rules): |
707 return None | 625 return None |
708 | 626 |
709 rule = self.__rules[offset] | 627 rule = self.__rules[offset] |
710 rule.setEnabled(enabled) | 628 rule.setEnabled(enabled) |
629 self.rulesEnabledChanged.emit() | |
630 | |
711 if rule.isCSSRule(): | 631 if rule.isCSSRule(): |
712 from WebBrowser.WebBrowserWindow import WebBrowserWindow | 632 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
713 self.__populateCache() | |
714 WebBrowserWindow.mainWindow().reloadUserStyleSheet() | 633 WebBrowserWindow.mainWindow().reloadUserStyleSheet() |
715 | 634 |
716 return rule | 635 return rule |