diff -r f99d60d6b59b -r 2602857055c5 eric6/Helpviewer/WebPlugins/ClickToFlash/ClickToFlash.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/Helpviewer/WebPlugins/ClickToFlash/ClickToFlash.py Sun Apr 14 15:09:21 2019 +0200 @@ -0,0 +1,321 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Flash blocker. +""" + + +from __future__ import unicode_literals + +from PyQt5.QtCore import pyqtSlot, QUrl, Qt, QByteArray, QTimer +from PyQt5.QtGui import QCursor +from PyQt5.QtWidgets import QWidget, QMenu, QDialog, QLabel, QFormLayout +from PyQt5.QtNetwork import QNetworkRequest +from PyQt5.QtWebKit import QWebElement, QWebElementCollection +from PyQt5.QtWebKitWidgets import QWebHitTestResult, QWebView + +from .Ui_ClickToFlash import Ui_ClickToFlash + +import UI.PixmapCache + + +class ClickToFlash(QWidget, Ui_ClickToFlash): + """ + Class implementing the Flash blocker. + """ + _acceptedUrl = QUrl() + _acceptedArgNames = [] + _acceptedArgValues = [] + + def __init__(self, plugin, mimeType, url, argumentNames, argumentValues, + parent=None): + """ + Constructor + + @param plugin reference to the plug-in (ClickToFlashPlugin) + @param mimeType MIME type for the plug-in (string) + @param url requested URL (QUrl) + @param argumentNames list of argument names (list of strings) + @param argumentValues list of argument values (list of strings) + @param parent reference to the parent widget (QWidget) + """ + super(ClickToFlash, self).__init__(parent) + + # Check AdBlock first + import Helpviewer.HelpWindow + manager = Helpviewer.HelpWindow.HelpWindow.adBlockManager() + if manager.isEnabled(): + urlString = bytes(url.toEncoded()).decode() + urlDomain = url.host() + for subscription in manager.subscriptions(): + blockedRule = subscription.match( + QNetworkRequest(url), urlDomain, urlString) + if blockedRule: + QTimer.singleShot(200, self.__hideAdBlocked) + return + + self.setupUi(self) + + self.__swapping = False + self.__element = QWebElement() + + self.__plugin = plugin + self.__url = QUrl(url) + self.__argumentNames = argumentNames[:] + self.__argumentValues = argumentValues[:] + self.__mimeType = mimeType + + self.setContextMenuPolicy(Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.__showContextMenu) + self.setToolTip(self.__url.toString()) + + iconName = plugin.getIconName(mimeType) + if iconName: + self.loadFlashButton.setIcon(UI.PixmapCache.getIcon(iconName)) + else: + self.loadFlashButton.setText(self.tr("Load")) + + @pyqtSlot() + def on_loadFlashButton_clicked(self): + """ + Private slot handling the flash activation. + """ + self.__load() + + def __showContextMenu(self): + """ + Private slot to show the context menu. + """ + menu = QMenu() + act = menu.addAction(self.tr("Object blocked by ClickToFlash")) + font = act.font() + font.setBold(True) + act.setFont(font) + menu.addAction( + self.tr("Show information about object"), self.__showInfo) + menu.addSeparator() + menu.addAction(self.tr("Load"), self.__load) + menu.addAction(self.tr("Delete object"), self.__hideAdBlocked) + menu.addSeparator() + host = self.__url.host() + add = menu.addAction( + self.tr("Add '{0}' to Whitelist").format(host), + self.__addToWhitelist) + remove = menu.addAction( + self.tr("Remove '{0}' from Whitelist").format(host), + self.__removeFromWhitelist) + onWhitelist = self.__plugin.onWhitelist(host) + add.setEnabled(not onWhitelist) + remove.setEnabled(onWhitelist) + menu.addSeparator() + menu.addAction(self.tr("Configure Whitelist"), self.__configure) + menu.actions()[0].setEnabled(False) + + menu.exec_(QCursor.pos()) + + def swapping(self): + """ + Public method to check, if the plug-in is swapping. + + @return flag indicating the swapping status (boolean) + """ + return self.__swapping + + def __configure(self): + """ + Private slot to configure the whitelist. + """ + self.__plugin.configure() + + def __addToWhitelist(self): + """ + Private slot to add the host to the whitelist. + """ + self.__plugin.addToWhitelist(self.__url.host()) + + def __removeFromWhitelist(self): + """ + Private slot to remove the host from the whitelist. + """ + self.__plugin.removeFromWhitelist(self.__url.host()) + + def __load(self, allPlayers=False): + """ + Private slot to load the flash content. + + @param allPlayers flag indicating to load all flash players (boolean) + """ + self.__findElement() + if not self.__element.isNull(): + substitute = self.__element.clone() + substitute.setAttribute("type", self.__mimeType) + self.__element.replace(substitute) + + ClickToFlash._acceptedUrl = self.__url + ClickToFlash._acceptedArgNames = self.__argumentNames + ClickToFlash._acceptedArgValues = self.__argumentValues + + def __findElement(self): + """ + Private method to find the element belonging to this ClickToFlash + instance. + """ + parent = self.parentWidget() + view = None + while parent is not None: + if isinstance(parent, QWebView): + view = parent + break + parent = parent.parentWidget() + if view is None: + return + + objectPos = view.mapFromGlobal(self.loadFlashButton.mapToGlobal( + self.loadFlashButton.pos())) + objectFrame = view.page().frameAt(objectPos) + hitResult = QWebHitTestResult() + hitElement = QWebElement() + + if objectFrame is not None: + hitResult = objectFrame.hitTestContent(objectPos) + hitElement = hitResult.element() + + if not hitElement.isNull() and \ + hitElement.tagName().lower() in ["embed", "object"]: + self.__element = hitElement + return + + # hit test failed, trying to find element by src + # attribute in elements of all frames on page (although less accurate + frames = [] + frames.append(view.page().mainFrame()) + while frames: + frame = frames.pop(0) + if not frame: + continue + docElement = frame.documentElement() + elements = QWebElementCollection() + elements.append(docElement.findAll("embed")) + elements.append(docElement.findAll("object")) + + for element in elements: + if not self.__checkElement(element) and \ + not self.__checkUrlOnElement(element, view): + continue + self.__element = element + return + frames.extend(frame.childFrames()) + + def __checkUrlOnElement(self, element, view): + """ + Private slot to check the URL of an element. + + @param element reference to the element to check (QWebElement) + @param view reference to the view object (QWebView) + @return flag indicating a positive result (boolean) + """ + checkString = element.attribute("src") + if checkString == "": + checkString = element.attribute("data") + if checkString == "": + checkString = element.attribute("value") + + checkString = view.url().resolved(QUrl(checkString)).toString( + QUrl.RemoveQuery) + return self.__url.toEncoded().contains( + QByteArray(checkString.encode("utf-8"))) + + def __checkElement(self, element): + """ + Private slot to check an element against the saved arguments. + + @param element reference to the element to check (QWebElement) + @return flag indicating a positive result (boolean) + """ + if self.__argumentNames == element.attributeNames(): + for name in self.__argumentNames: + if element.attribute(name) not in self.__argumentValues: + return False + + return True + + return False + + def __hideAdBlocked(self): + """ + Private slot to hide the object. + """ + self.__findElement() + if not self.__element.isNull(): + self.__element.setStyleProperty("display", "none") + else: + self.hide() + + def __showInfo(self): + """ + Private slot to show information about the blocked object. + """ + dlg = QDialog() + dlg.setWindowTitle(self.tr("Flash Object")) + dlg.setSizeGripEnabled(True) + layout = QFormLayout(dlg) + layout.addRow(QLabel(self.tr("<b>Attribute Name</b>")), + QLabel(self.tr("<b>Value</b>"))) + + index = 0 + for name in self.__argumentNames: + nameLabel = QLabel(self.__elide(name, length=30)) + value = self.__argumentValues[index] + valueLabel = QLabel(self.__elide(value, length=60)) + valueLabel.setTextInteractionFlags( + Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse) + layout.addRow(nameLabel, valueLabel) + + index += 1 + + if index == 0: + layout.addRow(QLabel(self.tr("No information available."))) + + dlg.setMaximumHeight(500) + dlg.setMaximumWidth(500) + dlg.exec_() + + def __elide(self, txt, mode=Qt.ElideMiddle, length=40): + """ + Private method to elide some text. + + @param txt text to be elided (string) + @keyparam mode elide mode (Qt.TextElideMode) + @keyparam length amount of characters to be used (integer) + @return the elided text (string) + """ + if mode == Qt.ElideNone or len(txt) < length: + return txt + elif mode == Qt.ElideLeft: + return "...{0}".format(txt[-length:]) + elif mode == Qt.ElideMiddle: + return "{0}...{1}".format(txt[:length // 2], txt[-(length // 2):]) + elif mode == Qt.ElideRight: + return "{0}...".format(txt[:length]) + else: + # just in case + return txt + + @classmethod + def isAlreadyAccepted(cls, url, argumentNames, argumentValues): + """ + Class method to check, if the given parameter combination is being + accepted. + + @param url URL to be checked for (QUrl) + @param argumentNames argument names to be checked for (list of strings) + @param argumentValues argument values to be checked for (list of + strings) + @return flag indicating that this was already accepted (boolean) + """ + return url == cls._acceptedUrl and \ + argumentNames == cls._acceptedArgNames and \ + argumentValues == cls._acceptedArgValues