8 """ |
8 """ |
9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
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, qVersion |
14 from PyQt5.QtWebEngineWidgets import QWebEngineScript |
14 from PyQt5.QtWebEngineWidgets import QWebEngineScript |
15 |
15 |
16 from .GreaseMonkeyJavaScript import bootstrap_js, values_js |
16 from .GreaseMonkeyJavaScript import bootstrap_js, values_js |
|
17 from .GreaseMonkeyDownloader import GreaseMonkeyDownloader |
17 |
18 |
18 from ..Tools.DelayedFileWatcher import DelayedFileWatcher |
19 from ..Tools.DelayedFileWatcher import DelayedFileWatcher |
19 from ..WebBrowserPage import WebBrowserPage |
20 from ..WebBrowserPage import WebBrowserPage |
20 |
21 |
21 |
22 |
175 |
182 |
176 @return list of excluded URLs (list of strings) |
183 @return list of excluded URLs (list of strings) |
177 """ |
184 """ |
178 return self.__exclude[:] |
185 return self.__exclude[:] |
179 |
186 |
180 def script(self): |
187 def require(self): |
181 """ |
188 """ |
182 Public method to get the Javascript source. |
189 Public method to get the list of required scripts. |
183 |
190 |
184 @return Javascript source (string) |
191 @return list of required scripts (list of strings) |
185 """ |
192 """ |
186 return self.__script |
193 return self.__require[:] |
187 |
194 |
188 def fileName(self): |
195 def fileName(self): |
189 """ |
196 """ |
190 Public method to get the path of the Javascript file. |
197 Public method to get the path of the Javascript file. |
191 |
198 |
192 @return path path of the Javascript file (string) |
199 @return path of the Javascript file (string) |
193 """ |
200 """ |
194 return self.__fileName |
201 return self.__fileName |
|
202 |
|
203 def isUpdating(self): |
|
204 """ |
|
205 Public method to get the updating flag. |
|
206 |
|
207 @return updating flag |
|
208 @rtype bool |
|
209 """ |
|
210 return self.__updating |
195 |
211 |
196 @pyqtSlot(str) |
212 @pyqtSlot(str) |
197 def __watchedFileChanged(self, fileName): |
213 def __watchedFileChanged(self, fileName): |
198 """ |
214 """ |
199 Private slot handling changes of the script file. |
215 Private slot handling changes of the script file. |
200 |
216 |
201 @param fileName path of the script file |
217 @param fileName path of the script file |
202 @type str |
218 @type str |
203 """ |
219 """ |
204 if self.__fileName == fileName: |
220 if self.__fileName == fileName: |
205 self.__parseScript() |
221 self.__reloadScript() |
206 |
|
207 self.__manager.removeScript(self, False) |
|
208 self.__manager.addScript(self) |
|
209 |
|
210 self.scriptChanged.emit() |
|
211 |
222 |
212 def __parseScript(self): |
223 def __parseScript(self): |
213 """ |
224 """ |
214 Private method to parse the given script and populate the data |
225 Private method to parse the given script and populate the data |
215 structure. |
226 structure. |
311 |
322 |
312 nspace = bytes(QCryptographicHash.hash( |
323 nspace = bytes(QCryptographicHash.hash( |
313 QByteArray(self.fullName().encode("utf-8")), |
324 QByteArray(self.fullName().encode("utf-8")), |
314 QCryptographicHash.Md4).toHex()).decode("ascii") |
325 QCryptographicHash.Md4).toHex()).decode("ascii") |
315 valuesScript = values_js.format(nspace) |
326 valuesScript = values_js.format(nspace) |
316 runCheck = """ |
327 if qVersion() < "5.8.0": |
317 for (var value of {0}) {{ |
328 runCheck = """ |
318 var re = new RegExp(value); |
329 for (var value of {0}) {{ |
319 if (re.test(window.location.href)) {{ |
330 var re = new RegExp(value); |
|
331 if (re.test(window.location.href)) {{ |
|
332 return; |
|
333 }} |
|
334 }} |
|
335 __eric_includes = false; |
|
336 for (var value of {1}) {{ |
|
337 var re = new RegExp(value); |
|
338 if (re.test(window.location.href)) {{ |
|
339 __eric_includes = true; |
|
340 break; |
|
341 }} |
|
342 }} |
|
343 if (!__eric_includes) {{ |
320 return; |
344 return; |
321 }} |
345 }} |
322 }} |
346 delete __eric_includes;""".format( |
323 __eric_includes = false; |
347 self.__toJavaScriptList(self.__exclude[:]), |
324 for (var value of {1}) {{ |
348 self.__toJavaScriptList(self.__include[:]) |
325 var re = new RegExp(value); |
349 ) |
326 if (re.test(window.location.href)) {{ |
350 self.__script = "(function(){{{0}\n{1}\n{2}\n{3}\n}})();".format( |
327 __eric_includes = true; |
351 runCheck, valuesScript, |
328 break; |
352 self.__manager.requireScripts(self.__require), fileData |
329 }} |
353 ) |
330 }} |
354 else: |
331 if (!__eric_includes) {{ |
355 self.__script = "(function(){{{0}\n{1}\n{2}\n}})();".format( |
332 return; |
356 valuesScript, self.__manager.requireScripts(self.__require), |
333 }} |
357 fileData |
334 delete __eric_includes;""".format( |
358 ) |
335 self.__toJavaScriptList(self.__exclude[:]), |
|
336 self.__toJavaScriptList(self.__include[:]) |
|
337 ) |
|
338 runCheck = "" |
|
339 self.__script = "(function(){{{0}\n{1}\n{2}\n{3}\n}})();".format( |
|
340 runCheck, valuesScript, |
|
341 self.__manager.requireScripts(requireList), fileData |
|
342 ) |
|
343 self.__valid = True |
359 self.__valid = True |
344 |
360 |
345 def webScript(self): |
361 def webScript(self): |
346 """ |
362 """ |
347 Public method to create a script object. |
363 Public method to create a script object. |
348 |
364 |
349 @return prepared script object |
365 @return prepared script object |
350 @rtype QWebEngineScript |
366 @rtype QWebEngineScript |
351 @exception ValueError raised to indicate an unsupported start point |
367 @exception ValueError raised to indicate an unsupported start point |
352 """ |
368 """ |
353 if self.startAt() == GreaseMonkeyScript.DocumentStart: |
369 if qVersion() < "5.8.0": |
354 injectionPoint = QWebEngineScript.DocumentCreation |
370 if self.startAt() == GreaseMonkeyScript.DocumentStart: |
355 elif self.startAt() == GreaseMonkeyScript.DocumentEnd: |
371 injectionPoint = QWebEngineScript.DocumentCreation |
356 injectionPoint = QWebEngineScript.DocumentReady |
372 elif self.startAt() == GreaseMonkeyScript.DocumentEnd: |
357 elif self.startAt() == GreaseMonkeyScript.DocumentIdle: |
373 injectionPoint = QWebEngineScript.DocumentReady |
358 injectionPoint = QWebEngineScript.Deferred |
374 elif self.startAt() == GreaseMonkeyScript.DocumentIdle: |
359 else: |
375 injectionPoint = QWebEngineScript.Deferred |
360 raise ValueError("Wrong script start point.") |
376 else: |
|
377 raise ValueError("Wrong script start point.") |
361 |
378 |
362 script = QWebEngineScript() |
379 script = QWebEngineScript() |
363 script.setSourceCode("{0}\n{1}".format( |
380 script.setSourceCode("{0}\n{1}".format( |
364 bootstrap_js, self.__script |
381 bootstrap_js, self.__script |
365 )) |
382 )) |
366 script.setName(self.fullName()) |
383 script.setName(self.fullName()) |
367 script.setInjectionPoint(injectionPoint) |
384 if qVersion() < "5.8.0": |
|
385 script.setInjectionPoint(injectionPoint) |
368 script.setWorldId(WebBrowserPage.SafeJsWorld) |
386 script.setWorldId(WebBrowserPage.SafeJsWorld) |
369 script.setRunsOnSubFrames(not self.__noFrames) |
387 script.setRunsOnSubFrames(not self.__noFrames) |
370 return script |
388 return script |
371 |
389 |
372 def __toJavaScriptList(self, patterns): |
390 def __toJavaScriptList(self, patterns): |
377 @param patterns list of match patterns |
395 @param patterns list of match patterns |
378 @type list of str |
396 @type list of str |
379 @return JavaScript script containing the list |
397 @return JavaScript script containing the list |
380 @rtype str |
398 @rtype str |
381 """ |
399 """ |
382 patternList = [] |
400 if qVersion() >= "5.8.0": |
383 for pattern in patterns: |
401 script = "" |
384 if pattern.startswith("/") and pattern.endswith("/") and \ |
402 else: |
385 len(pattern) > 1: |
403 patternList = [] |
386 pattern = pattern[1:-1] |
404 for pattern in patterns: |
387 else: |
405 if pattern.startswith("/") and pattern.endswith("/") and \ |
388 pattern = pattern.replace(".", "\\.").replace("*", ".*") |
406 len(pattern) > 1: |
389 pattern = "'{0}'".format(pattern) |
407 pattern = pattern[1:-1] |
390 patternList.append(pattern) |
408 else: |
391 |
409 pattern = pattern.replace(".", "\\.").replace("*", ".*") |
392 script = "[{0}]".format(",".join(patternList)) |
410 pattern = "'{0}'".format(pattern) |
|
411 patternList.append(pattern) |
|
412 |
|
413 script = "[{0}]".format(",".join(patternList)) |
393 return script |
414 return script |
|
415 |
|
416 def updateScript(self): |
|
417 """ |
|
418 Public method to updated the script. |
|
419 """ |
|
420 if not self.__downloadUrl.isValid() or self.__updating: |
|
421 return |
|
422 |
|
423 self.__updating = True |
|
424 self.updatingChanged.emit(self.__updating) |
|
425 |
|
426 downloader = GreaseMonkeyDownloader( |
|
427 self.__downloadUrl, |
|
428 self.__manager, |
|
429 GreaseMonkeyDownloader.DownloadMainScript) |
|
430 downloader.updateScript(self.__fileName) |
|
431 downloader.finished.connect(self.__downloaderFinished) |
|
432 downloader.error.connect(self.__downloaderError) |
|
433 self.__downloaders.append(downloader) |
|
434 |
|
435 self.__downloadRequires() |
|
436 |
|
437 def __downloaderFinished(self): |
|
438 """ |
|
439 Private slot to handle a finished download. |
|
440 """ |
|
441 downloader = self.sender() |
|
442 if downloader in self.__downloaders: |
|
443 self.__downloaders.remove(downloader) |
|
444 self.__updating = False |
|
445 self.updatingChanged.emit(self.__updating) |
|
446 |
|
447 def __downloaderError(self): |
|
448 """ |
|
449 Private slot to handle a downloader error. |
|
450 """ |
|
451 downloader = self.sender() |
|
452 if downloader in self.__downloaders: |
|
453 self.__downloaders.remove(downloader) |
|
454 self.__updating = False |
|
455 self.updatingChanged.emit(self.__updating) |
|
456 |
|
457 def __reloadScript(self): |
|
458 """ |
|
459 Private method to reload the script. |
|
460 """ |
|
461 self.__parseScript() |
|
462 |
|
463 self.__manager.removeScript(self, False) |
|
464 self.__manager.addScript(self) |
|
465 |
|
466 self.scriptChanged.emit() |
|
467 |
|
468 def __downloadRequires(self): |
|
469 """ |
|
470 Private method to download the required scripts. |
|
471 """ |
|
472 for urlStr in self.__require: |
|
473 if not self.__manager.requireScripts([urlStr]): |
|
474 downloader = GreaseMonkeyDownloader( |
|
475 QUrl(urlStr), |
|
476 self.__manager, |
|
477 GreaseMonkeyDownloader.DownloadRequireScript) |
|
478 downloader.finished.connect(self.__requireDownloaded) |
|
479 downloader.error.connect(self.__requireDownloadError) |
|
480 self.__downloaders.append(downloader) |
|
481 |
|
482 def __requireDownloaded(self): |
|
483 """ |
|
484 Private slot to handle a finished download of a required script. |
|
485 """ |
|
486 downloader = self.sender() |
|
487 if downloader in self.__downloaders: |
|
488 self.__downloaders.remove(downloader) |
|
489 |
|
490 self.__reloadScript() |
|
491 |
|
492 def __requireDownloadError(self): |
|
493 """ |
|
494 Private slot to handle a downloader error. |
|
495 """ |
|
496 downloader = self.sender() |
|
497 if downloader in self.__downloaders: |
|
498 self.__downloaders.remove(downloader) |