|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the Flash blocker. |
|
8 """ |
|
9 |
|
10 |
|
11 from PyQt4.QtCore import pyqtSlot, QUrl, Qt, QByteArray, QTimer |
|
12 from PyQt4.QtGui import QWidget, QMenu, QCursor, QDialog, QLabel, QFormLayout |
|
13 from PyQt4.QtWebKit import QWebHitTestResult, QWebElement, QWebView, QWebElementCollection |
|
14 |
|
15 from .Ui_ClickToFlash import Ui_ClickToFlash |
|
16 |
|
17 import UI.PixmapCache |
|
18 |
|
19 import Helpviewer.HelpWindow |
|
20 |
|
21 |
|
22 class ClickToFlash(QWidget, Ui_ClickToFlash): |
|
23 """ |
|
24 Class implementing the Flash blocker. |
|
25 """ |
|
26 _acceptedUrl = QUrl() |
|
27 _acceptedArgNames = [] |
|
28 _acceptedArgValues = [] |
|
29 |
|
30 def __init__(self, plugin, mimeType, url, argumentNames, argumentValues, parent=None): |
|
31 """ |
|
32 Constructor |
|
33 |
|
34 @param plugin reference to the plug-in (ClickToFlashPlugin) |
|
35 @param mimeType MIME type for the plug-in (string) |
|
36 @param url requested URL (QUrl) |
|
37 @param argumentNames list of argument names (list of strings) |
|
38 @param argumentValues list of argument values (list of strings) |
|
39 @param parent reference to the parent widget (QWidget) |
|
40 """ |
|
41 super().__init__(parent) |
|
42 |
|
43 # Check AdBlock first |
|
44 manager = Helpviewer.HelpWindow.HelpWindow.adblockManager() |
|
45 if manager.isEnabled(): |
|
46 urlString = bytes(url.toEncoded()).decode() |
|
47 for subscription in manager.subscriptions(): |
|
48 if not subscription.allow(urlString) and subscription.block(urlString): |
|
49 QTimer.singleShot(200, self.__hideAdBlocked) |
|
50 return |
|
51 |
|
52 self.setupUi(self) |
|
53 |
|
54 self.__swapping = False |
|
55 self.__element = QWebElement() |
|
56 |
|
57 self.__plugin = plugin |
|
58 self.__url = QUrl(url) |
|
59 self.__argumentNames = argumentNames[:] |
|
60 self.__argumentValues = argumentValues[:] |
|
61 self.__mimeType = mimeType |
|
62 |
|
63 self.setContextMenuPolicy(Qt.CustomContextMenu) |
|
64 self.customContextMenuRequested.connect(self.__showContextMenu) |
|
65 self.setToolTip(self.__url.toString()) |
|
66 |
|
67 iconName = plugin.getIconName(mimeType) |
|
68 if iconName: |
|
69 self.loadFlashButton.setIcon(UI.PixmapCache.getIcon(iconName)) |
|
70 else: |
|
71 self.loadFlashButton.setText(self.trUtf8("Load")) |
|
72 |
|
73 @pyqtSlot() |
|
74 def on_loadFlashButton_clicked(self): |
|
75 """ |
|
76 Private slot handling the flash activation. |
|
77 """ |
|
78 self.__load() |
|
79 |
|
80 def __showContextMenu(self): |
|
81 """ |
|
82 Private slot to show the context menu. |
|
83 """ |
|
84 menu = QMenu() |
|
85 act = menu.addAction(self.trUtf8("Object blocked by ClickToFlash")) |
|
86 font = act.font() |
|
87 font.setBold(True) |
|
88 act.setFont(font) |
|
89 menu.addAction(self.trUtf8("Show information about object"), self.__showInfo) |
|
90 menu.addSeparator() |
|
91 menu.addAction(self.trUtf8("Load"), self.__load) |
|
92 menu.addAction(self.trUtf8("Delete object"), self.__hideAdBlocked) |
|
93 menu.addSeparator() |
|
94 host = self.__url.host() |
|
95 add = menu.addAction(self.trUtf8("Add '{0}' to Whitelist".format(host)), |
|
96 self.__addToWhitelist) |
|
97 remove = menu.addAction(self.trUtf8("Remove '{0}' from Whitelist".format(host)), |
|
98 self.__removeFromWhitelist) |
|
99 onWhitelist = self.__plugin.onWhitelist(host) |
|
100 add.setEnabled(not onWhitelist) |
|
101 remove.setEnabled(onWhitelist) |
|
102 menu.addSeparator() |
|
103 menu.addAction(self.trUtf8("Configure Whitelist"), self.__configure) |
|
104 menu.actions()[0].setEnabled(False) |
|
105 |
|
106 menu.exec_(QCursor.pos()) |
|
107 |
|
108 def swapping(self): |
|
109 """ |
|
110 Public method to check, if the plug-in is swapping. |
|
111 |
|
112 @return flag indicating the swapping status (boolean) |
|
113 """ |
|
114 return self.__swapping |
|
115 |
|
116 def __configure(self): |
|
117 """ |
|
118 Private slot to configure the whitelist. |
|
119 """ |
|
120 self.__plugin.configure() |
|
121 |
|
122 def __addToWhitelist(self): |
|
123 """ |
|
124 Private slot to add the host to the whitelist. |
|
125 """ |
|
126 self.__plugin.addToWhitelist(self.__url.host()) |
|
127 |
|
128 def __removeFromWhitelist(self): |
|
129 """ |
|
130 Private slot to remove the host from the whitelist. |
|
131 """ |
|
132 self.__plugin.removeFromWhitelist(self.__url.host()) |
|
133 |
|
134 def __load(self, all=False): |
|
135 """ |
|
136 Private slot to load the flash content. |
|
137 |
|
138 @param all flag indicating to load all flash players. (boolean) |
|
139 """ |
|
140 self.__findElement() |
|
141 if not self.__element.isNull(): |
|
142 substitute = self.__element.clone() |
|
143 substitute.setAttribute("type", self.__mimeType) |
|
144 self.__element.replace(substitute) |
|
145 |
|
146 ClickToFlash._acceptedUrl = self.__url; |
|
147 ClickToFlash._acceptedArgNames = self.__argumentNames; |
|
148 ClickToFlash._acceptedArgValues = self.__argumentValues; |
|
149 |
|
150 def __findElement(self): |
|
151 """ |
|
152 Private method to find the element belonging to this ClickToFlash instance. |
|
153 """ |
|
154 parent = self.parentWidget() |
|
155 view = None |
|
156 while parent is not None: |
|
157 if isinstance(parent, QWebView): |
|
158 view = parent |
|
159 break |
|
160 parent = parent.parentWidget() |
|
161 if view is None: |
|
162 return |
|
163 |
|
164 objectPos = view.mapFromGlobal(self.loadFlashButton.mapToGlobal( |
|
165 self.loadFlashButton.pos())) |
|
166 objectFrame = view.page().frameAt(objectPos) |
|
167 hitResult = QWebHitTestResult() |
|
168 hitElement = QWebElement() |
|
169 |
|
170 if objectFrame is not None: |
|
171 hitResult = objectFrame.hitTestContent(objectPos) |
|
172 hitElement = hitResult.element() |
|
173 |
|
174 if not hitElement.isNull() and \ |
|
175 hitElement.tagName().lower() in ["embed", "object"]: |
|
176 self.__element = hitElement |
|
177 return |
|
178 |
|
179 # hit test failed, trying to find element by src |
|
180 # attribute in elements of all frames on page (although less accurate |
|
181 frames = [] |
|
182 frames.append(view.page().mainFrame()) |
|
183 while frames: |
|
184 frame = frames.pop(0) |
|
185 if not frame: |
|
186 continue |
|
187 docElement = frame.documentElement() |
|
188 elements = QWebElementCollection() |
|
189 elements.append(docElement.findAll("embed")) |
|
190 elements.append(docElement.findAll("object")) |
|
191 |
|
192 for element in elements: |
|
193 if not self.__checkElement(element) and \ |
|
194 not self.__checkUrlOnElement(element, view): |
|
195 continue |
|
196 self.__element = element |
|
197 return |
|
198 frames.extend(frame.childFrames()) |
|
199 |
|
200 def __checkUrlOnElement(self, element, view): |
|
201 """ |
|
202 Private slot to check the URL of an element. |
|
203 |
|
204 @param element reference to the element to check (QWebElement) |
|
205 @param view reference to the view object (QWebView) |
|
206 @return flag indicating a positive result (boolean) |
|
207 """ |
|
208 checkString = element.attribute("src") |
|
209 if checkString == "": |
|
210 checkString = element.attribute("data") |
|
211 if checkString == "": |
|
212 checkString = element.attribute("value") |
|
213 |
|
214 checkString = view.url().resolved(QUrl(checkString)).toString(QUrl.RemoveQuery) |
|
215 return self.__url.toEncoded().contains(QByteArray(checkString.encode("utf-8"))) |
|
216 |
|
217 def __checkElement(self, element): |
|
218 """ |
|
219 Private slot to check an element against the saved arguments. |
|
220 |
|
221 @param element reference to the element to check (QWebElement) |
|
222 @return flag indicating a positive result (boolean) |
|
223 """ |
|
224 if self.__argumentNames == element.attributeNames(): |
|
225 for name in self.__argumentNames: |
|
226 if element.attribute(name) not in self.__argumentValues: |
|
227 return False |
|
228 |
|
229 return True |
|
230 |
|
231 return False |
|
232 |
|
233 def __hideAdBlocked(self): |
|
234 """ |
|
235 Private slot to hide the object. |
|
236 """ |
|
237 self.__findElement() |
|
238 if not self.__element.isNull(): |
|
239 self.__element.setStyleProperty("visibility", "hidden") |
|
240 else: |
|
241 self.hide() |
|
242 |
|
243 def __showInfo(self): |
|
244 """ |
|
245 Private slot to show information about the blocked object. |
|
246 """ |
|
247 dlg = QDialog() |
|
248 dlg.setWindowTitle(self.trUtf8("Flash Object")) |
|
249 dlg.setSizeGripEnabled(True) |
|
250 layout = QFormLayout(dlg) |
|
251 layout.addRow(QLabel(self.trUtf8("<b>Attribute Name</b>")), |
|
252 QLabel(self.trUtf8("<b>Value</b>"))) |
|
253 |
|
254 index = 0 |
|
255 for name in self.__argumentNames: |
|
256 nameLabel = QLabel(self.__elide(name, length=30)) |
|
257 value = self.__argumentValues[index] |
|
258 valueLabel = QLabel(self.__elide(value, length=60)) |
|
259 valueLabel.setTextInteractionFlags( |
|
260 Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse) |
|
261 layout.addRow(nameLabel, valueLabel) |
|
262 |
|
263 index += 1 |
|
264 |
|
265 if index == 0: |
|
266 layout.addRow(QLabel(self.trUtf8("No information available."))) |
|
267 |
|
268 dlg.setMaximumHeight(500) |
|
269 dlg.setMaximumWidth(500) |
|
270 dlg.exec_() |
|
271 |
|
272 def __elide(self, txt, mode=Qt.ElideMiddle, length=40): |
|
273 """ |
|
274 Private method to elide some text. |
|
275 |
|
276 @param txt text to be elided (string) |
|
277 @keyparam mode elide mode (Qt.TextElideMode) |
|
278 @keyparam length amount of characters to be used (integer) |
|
279 @return the elided text (string) |
|
280 """ |
|
281 if mode == Qt.ElideNone or len(txt) < length: |
|
282 return txt |
|
283 elif mode == Qt.ElideLeft: |
|
284 return "...{0}".format(txt[-length:]) |
|
285 elif mode == Qt.ElideMiddle: |
|
286 return "{0}...{1}".format(txt[:length // 2], txt[-(length // 2):]) |
|
287 elif mode == Qt.ElideRight: |
|
288 return "{0}...".format(txt[:length]) |
|
289 else: |
|
290 # just in case |
|
291 return txt |
|
292 |
|
293 @classmethod |
|
294 def isAlreadyAccepted(cls, url, argumentNames, argumentValues): |
|
295 """ |
|
296 Class method to check, if the given parameter combination is being accepted. |
|
297 |
|
298 @param url URL to be checked for (QUrl) |
|
299 @param argumentNames argument names to be checked for (list of strings) |
|
300 @param argumentValues argument values to be checked for (list of strings) |
|
301 """ |
|
302 return url == cls._acceptedUrl and \ |
|
303 argumentNames == cls._acceptedArgNames and \ |
|
304 argumentValues == cls._acceptedArgValues |