|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the manager for GreaseMonkey scripts. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import os |
|
13 |
|
14 from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QTimer, QFile, \ |
|
15 QFileInfo, QDir, QSettings, QMetaObject, QUrl, Q_ARG, QCoreApplication |
|
16 from PyQt5.QtWidgets import QDialog |
|
17 |
|
18 from E5Gui import E5MessageBox |
|
19 |
|
20 import Utilities |
|
21 import Preferences |
|
22 |
|
23 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
24 from WebBrowser.JavaScript.ExternalJsObject import ExternalJsObject |
|
25 |
|
26 from .GreaseMonkeyJsObject import GreaseMonkeyJsObject |
|
27 |
|
28 |
|
29 class GreaseMonkeyManager(QObject): |
|
30 """ |
|
31 Class implementing the manager for GreaseMonkey scripts. |
|
32 |
|
33 @signal scriptsChanged() emitted to indicate a change of scripts |
|
34 """ |
|
35 scriptsChanged = pyqtSignal() |
|
36 |
|
37 def __init__(self, parent=None): |
|
38 """ |
|
39 Constructor |
|
40 |
|
41 @param parent reference to the parent object (QObject) |
|
42 """ |
|
43 super(GreaseMonkeyManager, self).__init__(parent) |
|
44 |
|
45 self.__disabledScripts = [] |
|
46 self.__scripts = [] |
|
47 self.__downloaders = [] |
|
48 |
|
49 self.__jsObject = GreaseMonkeyJsObject(self) |
|
50 |
|
51 QTimer.singleShot(0, self.__load) |
|
52 |
|
53 def showConfigurationDialog(self, parent=None): |
|
54 """ |
|
55 Public method to show the configuration dialog. |
|
56 |
|
57 @param parent reference to the parent widget (QWidget) |
|
58 """ |
|
59 from .GreaseMonkeyConfiguration.GreaseMonkeyConfigurationDialog \ |
|
60 import GreaseMonkeyConfigurationDialog |
|
61 self.__configDiaolg = GreaseMonkeyConfigurationDialog(self, parent) |
|
62 self.__configDiaolg.show() |
|
63 |
|
64 def downloadScript(self, url): |
|
65 """ |
|
66 Public method to download a GreaseMonkey script. |
|
67 |
|
68 @param url URL to download script from |
|
69 @type QUrl |
|
70 """ |
|
71 QMetaObject.invokeMethod( |
|
72 self, "doDownloadScript", Qt.QueuedConnection, |
|
73 Q_ARG(QUrl, url)) |
|
74 |
|
75 @pyqtSlot(QUrl) |
|
76 def doDownloadScript(self, url): |
|
77 """ |
|
78 Public slot to download a GreaseMonkey script. |
|
79 |
|
80 Note: The download needed to be separated in the invoking part |
|
81 (s.a.) and the one doing the real download because the invoking |
|
82 part runs in a different thread (i.e. the web engine thread). |
|
83 |
|
84 @param url URL to download script from |
|
85 @type QUrl |
|
86 """ |
|
87 from .GreaseMonkeyDownloader import GreaseMonkeyDownloader |
|
88 downloader = GreaseMonkeyDownloader( |
|
89 url, self, GreaseMonkeyDownloader.DownloadMainScript) |
|
90 downloader.finished.connect( |
|
91 lambda f: self.__downloaderFinished(f, downloader)) |
|
92 self.__downloaders.append(downloader) |
|
93 |
|
94 def __downloaderFinished(self, fileName, downloader): |
|
95 """ |
|
96 Private slot to handle the completion of a script download. |
|
97 |
|
98 @param fileName name of the downloaded script |
|
99 @type str |
|
100 @param downloader reference to the downloader object |
|
101 @type GreaseMonkeyDownloader |
|
102 """ |
|
103 if downloader in self.__downloaders: |
|
104 self.__downloaders.remove(downloader) |
|
105 |
|
106 deleteScript = True |
|
107 from .GreaseMonkeyScript import GreaseMonkeyScript |
|
108 script = GreaseMonkeyScript(self, fileName) |
|
109 if script.isValid(): |
|
110 if not self.containsScript(script.fullName()): |
|
111 from .GreaseMonkeyAddScriptDialog import \ |
|
112 GreaseMonkeyAddScriptDialog |
|
113 dlg = GreaseMonkeyAddScriptDialog(self, script) |
|
114 deleteScript = dlg.exec_() != QDialog.Accepted |
|
115 else: |
|
116 E5MessageBox.information( |
|
117 None, |
|
118 QCoreApplication.translate( |
|
119 "GreaseMonkeyManager", |
|
120 "Install GreaseMonkey Script"), |
|
121 QCoreApplication.translate( |
|
122 "GreaseMonkeyManager", |
|
123 """'{0}' is already installed.""").format( |
|
124 script.fullName()) |
|
125 ) |
|
126 |
|
127 if deleteScript: |
|
128 try: |
|
129 os.remove(fileName) |
|
130 except (IOError, OSError): |
|
131 # ignore |
|
132 pass |
|
133 |
|
134 def scriptsDirectory(self): |
|
135 """ |
|
136 Public method to get the path of the scripts directory. |
|
137 |
|
138 @return path of the scripts directory (string) |
|
139 """ |
|
140 return os.path.join( |
|
141 Utilities.getConfigDir(), "web_browser", "greasemonkey") |
|
142 |
|
143 def requireScriptsDirectory(self): |
|
144 """ |
|
145 Public method to get the path of the scripts directory. |
|
146 |
|
147 @return path of the scripts directory (string) |
|
148 """ |
|
149 return os.path.join(self.scriptsDirectory(), "requires") |
|
150 |
|
151 def requireScripts(self, urlList): |
|
152 """ |
|
153 Public method to get the sources of all required scripts. |
|
154 |
|
155 @param urlList list of URLs (list of string) |
|
156 @return sources of all required scripts (string) |
|
157 """ |
|
158 requiresDir = QDir(self.requireScriptsDirectory()) |
|
159 if not requiresDir.exists() or len(urlList) == 0: |
|
160 return "" |
|
161 |
|
162 script = "" |
|
163 |
|
164 settings = QSettings( |
|
165 os.path.join(self.requireScriptsDirectory(), "requires.ini"), |
|
166 QSettings.IniFormat) |
|
167 settings.beginGroup("Files") |
|
168 for url in urlList: |
|
169 if settings.contains(url): |
|
170 fileName = settings.value(url) |
|
171 if not QFileInfo(fileName).isAbsolute(): |
|
172 fileName = os.path.join(self.requireScriptsDirectory(), |
|
173 fileName) |
|
174 try: |
|
175 f = open(fileName, "r", encoding="utf-8") |
|
176 source = f.read().strip() |
|
177 f.close() |
|
178 except (IOError, OSError): |
|
179 source = "" |
|
180 if source: |
|
181 script += source + "\n" |
|
182 |
|
183 return script |
|
184 |
|
185 def saveConfiguration(self): |
|
186 """ |
|
187 Public method to save the configuration. |
|
188 """ |
|
189 Preferences.setWebBrowser("GreaseMonkeyDisabledScripts", |
|
190 self.__disabledScripts) |
|
191 |
|
192 def allScripts(self): |
|
193 """ |
|
194 Public method to get a list of all scripts. |
|
195 |
|
196 @return list of all scripts (list of GreaseMonkeyScript) |
|
197 """ |
|
198 return self.__scripts[:] |
|
199 |
|
200 def containsScript(self, fullName): |
|
201 """ |
|
202 Public method to check, if the given script exists. |
|
203 |
|
204 @param fullName full name of the script (string) |
|
205 @return flag indicating the existence (boolean) |
|
206 """ |
|
207 for script in self.__scripts: |
|
208 if script.fullName() == fullName: |
|
209 return True |
|
210 |
|
211 return False |
|
212 |
|
213 def enableScript(self, script): |
|
214 """ |
|
215 Public method to enable the given script. |
|
216 |
|
217 @param script script to be enabled (GreaseMonkeyScript) |
|
218 """ |
|
219 script.setEnabled(True) |
|
220 fullName = script.fullName() |
|
221 if fullName in self.__disabledScripts: |
|
222 self.__disabledScripts.remove(fullName) |
|
223 |
|
224 collection = WebBrowserWindow.webProfile().scripts() |
|
225 collection.insert(script.webScript()) |
|
226 |
|
227 def disableScript(self, script): |
|
228 """ |
|
229 Public method to disable the given script. |
|
230 |
|
231 @param script script to be disabled (GreaseMonkeyScript) |
|
232 """ |
|
233 script.setEnabled(False) |
|
234 fullName = script.fullName() |
|
235 if fullName not in self.__disabledScripts: |
|
236 self.__disabledScripts.append(fullName) |
|
237 |
|
238 collection = WebBrowserWindow.webProfile().scripts() |
|
239 collection.remove(collection.findScript(fullName)) |
|
240 |
|
241 def addScript(self, script): |
|
242 """ |
|
243 Public method to add a script. |
|
244 |
|
245 @param script script to be added (GreaseMonkeyScript) |
|
246 @return flag indicating success (boolean) |
|
247 """ |
|
248 if not script or not script.isValid(): |
|
249 return False |
|
250 |
|
251 self.__scripts.append(script) |
|
252 script.scriptChanged.connect(lambda: self.__scriptChanged(script)) |
|
253 |
|
254 collection = WebBrowserWindow.webProfile().scripts() |
|
255 collection.insert(script.webScript()) |
|
256 |
|
257 self.scriptsChanged.emit() |
|
258 return True |
|
259 |
|
260 def removeScript(self, script, removeFile=True): |
|
261 """ |
|
262 Public method to remove a script. |
|
263 |
|
264 @param script script to be removed (GreaseMonkeyScript) |
|
265 @param removeFile flag indicating to remove the script file as well |
|
266 (bool) |
|
267 @return flag indicating success (boolean) |
|
268 """ |
|
269 if not script: |
|
270 return False |
|
271 |
|
272 try: |
|
273 self.__scripts.remove(script) |
|
274 except ValueError: |
|
275 pass |
|
276 |
|
277 fullName = script.fullName() |
|
278 collection = WebBrowserWindow.webProfile().scripts() |
|
279 collection.remove(collection.findScript(fullName)) |
|
280 |
|
281 if fullName in self.__disabledScripts: |
|
282 self.__disabledScripts.remove(fullName) |
|
283 |
|
284 if removeFile: |
|
285 QFile.remove(script.fileName()) |
|
286 del script |
|
287 |
|
288 self.scriptsChanged.emit() |
|
289 return True |
|
290 |
|
291 def canRunOnScheme(self, scheme): |
|
292 """ |
|
293 Public method to check, if scripts can be run on a scheme. |
|
294 |
|
295 @param scheme scheme to check (string) |
|
296 @return flag indicating, that scripts can be run (boolean) |
|
297 """ |
|
298 return scheme in ["http", "https", "data", "ftp"] |
|
299 |
|
300 def __load(self): |
|
301 """ |
|
302 Private slot to load the available scripts into the manager. |
|
303 """ |
|
304 scriptsDir = QDir(self.scriptsDirectory()) |
|
305 if not scriptsDir.exists(): |
|
306 scriptsDir.mkpath(self.scriptsDirectory()) |
|
307 |
|
308 if not scriptsDir.exists("requires"): |
|
309 scriptsDir.mkdir("requires") |
|
310 |
|
311 self.__disabledScripts = \ |
|
312 Preferences.getWebBrowser("GreaseMonkeyDisabledScripts") |
|
313 |
|
314 from .GreaseMonkeyScript import GreaseMonkeyScript |
|
315 for fileName in scriptsDir.entryList(["*.js"], QDir.Files): |
|
316 absolutePath = scriptsDir.absoluteFilePath(fileName) |
|
317 script = GreaseMonkeyScript(self, absolutePath) |
|
318 |
|
319 if not script.isValid(): |
|
320 del script |
|
321 continue |
|
322 |
|
323 self.__scripts.append(script) |
|
324 |
|
325 if script.fullName() in self.__disabledScripts: |
|
326 script.setEnabled(False) |
|
327 else: |
|
328 collection = WebBrowserWindow.webProfile().scripts() |
|
329 collection.insert(script.webScript()) |
|
330 |
|
331 self.__jsObject.setSettingsFile(os.path.join( |
|
332 Utilities.getConfigDir(), "web_browser", |
|
333 "greasemonkey_values.ini")) |
|
334 ExternalJsObject.registerExtraObject("GreaseMonkey", self.__jsObject) |
|
335 |
|
336 def __scriptChanged(self, script): |
|
337 """ |
|
338 Private slot handling a changed script. |
|
339 |
|
340 @param script reference to the changed script |
|
341 @type GreaseMonkeyScript |
|
342 """ |
|
343 fullName = script.fullName() |
|
344 collection = WebBrowserWindow.webProfile().scripts() |
|
345 collection.remove(collection.findScript(fullName)) |
|
346 collection.insert(script.webScript()) |