WebBrowser/GreaseMonkey/GreaseMonkeyScript.py

branch
QtWebEngine
changeset 4809
4daf93888029
parent 4766
5f8d08aa2217
child 4886
b56735d36159
equal deleted inserted replaced
4808:328e613165fe 4809:4daf93888029
11 11
12 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QRegExp, \ 12 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QRegExp, \
13 QByteArray, QCryptographicHash 13 QByteArray, QCryptographicHash
14 from PyQt5.QtWebEngineWidgets import QWebEngineScript 14 from PyQt5.QtWebEngineWidgets import QWebEngineScript
15 15
16 from .GreaseMonkeyUrlMatcher import GreaseMonkeyUrlMatcher
17 from .GreaseMonkeyJavaScript import bootstrap_js, values_js 16 from .GreaseMonkeyJavaScript import bootstrap_js, values_js
18 17
19 from ..Tools.DelayedFileWatcher import DelayedFileWatcher 18 from ..Tools.DelayedFileWatcher import DelayedFileWatcher
20 19
21 20
23 """ 22 """
24 Class implementing the GreaseMonkey script. 23 Class implementing the GreaseMonkey script.
25 """ 24 """
26 DocumentStart = 0 25 DocumentStart = 0
27 DocumentEnd = 1 26 DocumentEnd = 1
27 DocumentIdle = 2
28 28
29 scriptChanged = pyqtSignal() 29 scriptChanged = pyqtSignal()
30 30
31 def __init__(self, manager, path): 31 def __init__(self, manager, path):
32 """ 32 """
54 54
55 self.__script = "" 55 self.__script = ""
56 self.__fileName = path 56 self.__fileName = path
57 self.__enabled = True 57 self.__enabled = True
58 self.__valid = False 58 self.__valid = False
59 self.__metaData = ""
60 self.__noFrames = False 59 self.__noFrames = False
61 60
62 self.__parseScript() 61 self.__parseScript()
63 62
64 self.__fileWatcher.delayedFileChanged.connect( 63 self.__fileWatcher.delayedFileChanged.connect(
165 """ 164 """
166 Public method to get the list of included URLs. 165 Public method to get the list of included URLs.
167 166
168 @return list of included URLs (list of strings) 167 @return list of included URLs (list of strings)
169 """ 168 """
170 list = [] 169 return self.__include[:]
171 for matcher in self.__include:
172 list.append(matcher.pattern())
173 return list
174 170
175 def exclude(self): 171 def exclude(self):
176 """ 172 """
177 Public method to get the list of excluded URLs. 173 Public method to get the list of excluded URLs.
178 174
179 @return list of excluded URLs (list of strings) 175 @return list of excluded URLs (list of strings)
180 """ 176 """
181 list = [] 177 return self.__exclude[:]
182 for matcher in self.__exclude:
183 list.append(matcher.pattern())
184 return list
185 178
186 def script(self): 179 def script(self):
187 """ 180 """
188 Public method to get the Javascript source. 181 Public method to get the Javascript source.
189 182
190 @return Javascript source (string) 183 @return Javascript source (string)
191 """ 184 """
192 return self.__script 185 return self.__script
193 186
194 def metaData(self):
195 """
196 Public method to get the script meta information.
197
198 @return script meta information
199 @rtype str
200 """
201 return self.__metaData
202
203 def fileName(self): 187 def fileName(self):
204 """ 188 """
205 Public method to get the path of the Javascript file. 189 Public method to get the path of the Javascript file.
206 190
207 @return path path of the Javascript file (string) 191 @return path path of the Javascript file (string)
208 """ 192 """
209 return self.__fileName 193 return self.__fileName
210
211 def match(self, urlString):
212 """
213 Public method to check, if the script matches the given URL.
214
215 @param urlString URL (string)
216 @return flag indicating a match (boolean)
217 """
218 if not self.isEnabled():
219 return False
220
221 for matcher in self.__exclude:
222 if matcher.match(urlString):
223 return False
224
225 for matcher in self.__include:
226 if matcher.match(urlString):
227 return True
228
229 return False
230 194
231 @pyqtSlot(str) 195 @pyqtSlot(str)
232 def __watchedFileChanged(self, fileName): 196 def __watchedFileChanged(self, fileName):
233 """ 197 """
234 Private slot handling changes of the script file. 198 Private slot handling changes of the script file.
264 self.__startAt = GreaseMonkeyScript.DocumentEnd 228 self.__startAt = GreaseMonkeyScript.DocumentEnd
265 229
266 self.__script = "" 230 self.__script = ""
267 self.__enabled = True 231 self.__enabled = True
268 self.__valid = False 232 self.__valid = False
269 self.__metaData = ""
270 self.__noFrames = False 233 self.__noFrames = False
271 234
272 try: 235 try:
273 f = open(path, "r", encoding="utf-8") 236 f = open(path, "r", encoding="utf-8")
274 fileData = f.read() 237 fileData = f.read()
323 286
324 ## elif key == "@updateURL": 287 ## elif key == "@updateURL":
325 ## self.__downloadUrl = QUrl(value) 288 ## self.__downloadUrl = QUrl(value)
326 ## 289 ##
327 elif key in ["@include", "@match"]: 290 elif key in ["@include", "@match"]:
328 self.__include.append(GreaseMonkeyUrlMatcher(value)) 291 self.__include.append(value)
329 292
330 elif key in ["@exclude", "@exclude_match"]: 293 elif key in ["@exclude", "@exclude_match"]:
331 self.__exclude.append(GreaseMonkeyUrlMatcher(value)) 294 self.__exclude.append(value)
332 295
333 elif key == "@require": 296 elif key == "@require":
334 requireList.append(value) 297 requireList.append(value)
335 298
336 elif key == "@run-at": 299 elif key == "@run-at":
337 if value == "document-end": 300 if value == "document-end":
338 self.__startAt = GreaseMonkeyScript.DocumentEnd 301 self.__startAt = GreaseMonkeyScript.DocumentEnd
339 elif value == "document-start": 302 elif value == "document-start":
340 self.__startAt = GreaseMonkeyScript.DocumentStart 303 self.__startAt = GreaseMonkeyScript.DocumentStart
304 elif value == "document-idle":
305 self.__startAt = GreaseMonkeyScript.DocumentIdle
341 306
342 elif key == "@downloadURL" and self.__downloadUrl.isEmpty(): 307 elif key == "@downloadURL" and self.__downloadUrl.isEmpty():
343 self.__downloadUrl = QUrl(value) 308 self.__downloadUrl = QUrl(value)
344 309
345 elif key == "@updateURL" and self.__updateUrl.isEmpty(): 310 elif key == "@updateURL" and self.__updateUrl.isEmpty():
346 self.__updateUrl = QUrl(value) 311 self.__updateUrl = QUrl(value)
347 312
348 if not self.__include: 313 if not self.__include:
349 self.__include.append(GreaseMonkeyUrlMatcher("*")) 314 self.__include.append("*")
350
351 marker = "// ==/UserScript=="
352 index = fileData.find(marker) + len(marker)
353 self.__metaData = fileData[:index]
354 script = fileData[index:].strip()
355 315
356 nspace = bytes(QCryptographicHash.hash( 316 nspace = bytes(QCryptographicHash.hash(
357 QByteArray(self.fullName().encode("utf-8")), 317 QByteArray(self.fullName().encode("utf-8")),
358 QCryptographicHash.Md4).toHex()).decode("ascii") 318 QCryptographicHash.Md4).toHex()).decode("ascii")
359 valuesScript = values_js.format(nspace) 319 valuesScript = values_js.format(nspace)
360 self.__script = "(function(){{{0}\n{1}\n{2}\n}})();".format( 320 runCheck = """
361 valuesScript, self.__manager.requireScripts(requireList), script 321 for (var value of {0}) {{
322 var re = new RegExp(value);
323 if (re.test(window.location.href)) {{
324 return;
325 }}
326 }}
327 __eric_includes = false;
328 for (var value of {1}) {{
329 var re = new RegExp(value);
330 if (re.test(window.location.href)) {{
331 __eric_includes = true;
332 break;
333 }}
334 }}
335 if (!__eric_includes) {{
336 return;
337 }}
338 delete __eric_includes;""".format(
339 self.__toJavaScriptList(self.__exclude[:]),
340 self.__toJavaScriptList(self.__include[:])
341 )
342 self.__script = "(function(){{{0}\n{1}\n{2}\n{3}\n}})();".format(
343 runCheck, valuesScript,
344 self.__manager.requireScripts(requireList), fileData
362 ) 345 )
363 self.__valid = True 346 self.__valid = True
364 347
365 def webScript(self): 348 def webScript(self):
366 """ 349 """
367 Public method to create a script object. 350 Public method to create a script object.
368 351
369 @return prepared script object 352 @return prepared script object
370 @rtype QWebEngineScript 353 @rtype QWebEngineScript
371 """ 354 """
355 if self.startAt() == GreaseMonkeyScript.DocumentStart:
356 injectionPoint = QWebEngineScript.DocumentCreation
357 elif self.startAt() == GreaseMonkeyScript.DocumentEnd:
358 injectionPoint = QWebEngineScript.DocumentReady
359 elif self.startAt() == GreaseMonkeyScript.DocumentIdle:
360 injectionPoint = QWebEngineScript.Deferred
361 else:
362 raise ValueError("Wrong script start point.")
363
372 script = QWebEngineScript() 364 script = QWebEngineScript()
373 script.setName(self.fullName()) 365 script.setName(self.fullName())
374 if self.startAt() == GreaseMonkeyScript.DocumentStart: 366 script.setInjectionPoint(injectionPoint)
375 script.setInjectionPoint(QWebEngineScript.DocumentCreation)
376 else:
377 script.setInjectionPoint(QWebEngineScript.DocumentReady)
378 script.setWorldId(QWebEngineScript.MainWorld) 367 script.setWorldId(QWebEngineScript.MainWorld)
379 script.setRunsOnSubFrames(not self.__noFrames) 368 script.setRunsOnSubFrames(not self.__noFrames)
380 script.setSourceCode("{0}\n{1}\n{2}".format( 369 script.setSourceCode("{0}\n{1}".format(
381 self.__metaData, bootstrap_js, self.__script 370 bootstrap_js, self.__script
382 )) 371 ))
383 return script 372 return script
373
374 def __toJavaScriptList(self, patterns):
375 """
376 Private method to convert a list of str to a string containing a valid
377 JavaScript list definition.
378
379 @param patterns list of match patterns
380 @type list of str
381 @return JavaScript script containing the list
382 @rtype str
383 """
384 patternList = []
385 for pattern in patterns:
386 if pattern.startswith("/") and pattern.endswith("/") and \
387 len(pattern) > 1:
388 pattern = pattern[1:-1]
389 else:
390 pattern = pattern.replace(".", "\\.").replace("*", ".*")
391 pattern = "'{0}'".format(pattern)
392 patternList.append(pattern)
393
394 script = "[{0}]".format(",".join(patternList))
395 return script

eric ide

mercurial