--- a/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py Tue Apr 25 18:40:46 2017 +0200 +++ b/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py Tue Apr 25 19:20:18 2017 +0200 @@ -10,10 +10,11 @@ from __future__ import unicode_literals from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QRegExp, \ - QByteArray, QCryptographicHash + QByteArray, QCryptographicHash, qVersion from PyQt5.QtWebEngineWidgets import QWebEngineScript from .GreaseMonkeyJavaScript import bootstrap_js, values_js +from .GreaseMonkeyDownloader import GreaseMonkeyDownloader from ..Tools.DelayedFileWatcher import DelayedFileWatcher from ..WebBrowserPage import WebBrowserPage @@ -28,6 +29,7 @@ DocumentIdle = 2 scriptChanged = pyqtSignal() + updatingChanged = pyqtSignal(bool) def __init__(self, manager, path): """ @@ -48,6 +50,7 @@ self.__include = [] self.__exclude = [] + self.__require = [] self.__downloadUrl = QUrl() self.__updateUrl = QUrl() @@ -59,6 +62,10 @@ self.__valid = False self.__noFrames = False + self.__updating = False + + self.__downloaders = [] + self.__parseScript() self.__fileWatcher.delayedFileChanged.connect( @@ -177,22 +184,31 @@ """ return self.__exclude[:] - def script(self): + def require(self): """ - Public method to get the Javascript source. + Public method to get the list of required scripts. - @return Javascript source (string) + @return list of required scripts (list of strings) """ - return self.__script + return self.__require[:] def fileName(self): """ Public method to get the path of the Javascript file. - @return path path of the Javascript file (string) + @return path of the Javascript file (string) """ return self.__fileName + def isUpdating(self): + """ + Public method to get the updating flag. + + @return updating flag + @rtype bool + """ + return self.__updating + @pyqtSlot(str) def __watchedFileChanged(self, fileName): """ @@ -202,12 +218,7 @@ @type str """ if self.__fileName == fileName: - self.__parseScript() - - self.__manager.removeScript(self, False) - self.__manager.addScript(self) - - self.scriptChanged.emit() + self.__reloadScript() def __parseScript(self): """ @@ -221,6 +232,7 @@ self.__include = [] self.__exclude = [] + self.__require = [] self.__downloadUrl = QUrl() self.__updateUrl = QUrl() @@ -250,7 +262,6 @@ # invalid script file return - requireList = [] for line in metaDataBlock.splitlines(): if not line.strip(): continue @@ -290,7 +301,7 @@ self.__exclude.append(value) elif key == "@require": - requireList.append(value) + self.__require.append(value) elif key == "@run-at": if value == "document-end": @@ -313,33 +324,38 @@ QByteArray(self.fullName().encode("utf-8")), QCryptographicHash.Md4).toHex()).decode("ascii") valuesScript = values_js.format(nspace) - runCheck = """ - for (var value of {0}) {{ - var re = new RegExp(value); - if (re.test(window.location.href)) {{ + if qVersion() < "5.8.0": + runCheck = """ + for (var value of {0}) {{ + var re = new RegExp(value); + if (re.test(window.location.href)) {{ + return; + }} + }} + __eric_includes = false; + for (var value of {1}) {{ + var re = new RegExp(value); + if (re.test(window.location.href)) {{ + __eric_includes = true; + break; + }} + }} + if (!__eric_includes) {{ return; }} - }} - __eric_includes = false; - for (var value of {1}) {{ - var re = new RegExp(value); - if (re.test(window.location.href)) {{ - __eric_includes = true; - break; - }} - }} - if (!__eric_includes) {{ - return; - }} - delete __eric_includes;""".format( - self.__toJavaScriptList(self.__exclude[:]), - self.__toJavaScriptList(self.__include[:]) - ) - runCheck = "" - self.__script = "(function(){{{0}\n{1}\n{2}\n{3}\n}})();".format( - runCheck, valuesScript, - self.__manager.requireScripts(requireList), fileData - ) + delete __eric_includes;""".format( + self.__toJavaScriptList(self.__exclude[:]), + self.__toJavaScriptList(self.__include[:]) + ) + self.__script = "(function(){{{0}\n{1}\n{2}\n{3}\n}})();".format( + runCheck, valuesScript, + self.__manager.requireScripts(self.__require), fileData + ) + else: + self.__script = "(function(){{{0}\n{1}\n{2}\n}})();".format( + valuesScript, self.__manager.requireScripts(self.__require), + fileData + ) self.__valid = True def webScript(self): @@ -350,21 +366,23 @@ @rtype QWebEngineScript @exception ValueError raised to indicate an unsupported start point """ - if self.startAt() == GreaseMonkeyScript.DocumentStart: - injectionPoint = QWebEngineScript.DocumentCreation - elif self.startAt() == GreaseMonkeyScript.DocumentEnd: - injectionPoint = QWebEngineScript.DocumentReady - elif self.startAt() == GreaseMonkeyScript.DocumentIdle: - injectionPoint = QWebEngineScript.Deferred - else: - raise ValueError("Wrong script start point.") + if qVersion() < "5.8.0": + if self.startAt() == GreaseMonkeyScript.DocumentStart: + injectionPoint = QWebEngineScript.DocumentCreation + elif self.startAt() == GreaseMonkeyScript.DocumentEnd: + injectionPoint = QWebEngineScript.DocumentReady + elif self.startAt() == GreaseMonkeyScript.DocumentIdle: + injectionPoint = QWebEngineScript.Deferred + else: + raise ValueError("Wrong script start point.") script = QWebEngineScript() script.setSourceCode("{0}\n{1}".format( bootstrap_js, self.__script )) script.setName(self.fullName()) - script.setInjectionPoint(injectionPoint) + if qVersion() < "5.8.0": + script.setInjectionPoint(injectionPoint) script.setWorldId(WebBrowserPage.SafeJsWorld) script.setRunsOnSubFrames(not self.__noFrames) return script @@ -379,15 +397,102 @@ @return JavaScript script containing the list @rtype str """ - patternList = [] - for pattern in patterns: - if pattern.startswith("/") and pattern.endswith("/") and \ - len(pattern) > 1: - pattern = pattern[1:-1] - else: - pattern = pattern.replace(".", "\\.").replace("*", ".*") - pattern = "'{0}'".format(pattern) - patternList.append(pattern) + if qVersion() >= "5.8.0": + script = "" + else: + patternList = [] + for pattern in patterns: + if pattern.startswith("/") and pattern.endswith("/") and \ + len(pattern) > 1: + pattern = pattern[1:-1] + else: + pattern = pattern.replace(".", "\\.").replace("*", ".*") + pattern = "'{0}'".format(pattern) + patternList.append(pattern) + + script = "[{0}]".format(",".join(patternList)) + return script + + def updateScript(self): + """ + Public method to updated the script. + """ + if not self.__downloadUrl.isValid() or self.__updating: + return + + self.__updating = True + self.updatingChanged.emit(self.__updating) + + downloader = GreaseMonkeyDownloader( + self.__downloadUrl, + self.__manager, + GreaseMonkeyDownloader.DownloadMainScript) + downloader.updateScript(self.__fileName) + downloader.finished.connect(self.__downloaderFinished) + downloader.error.connect(self.__downloaderError) + self.__downloaders.append(downloader) - script = "[{0}]".format(",".join(patternList)) - return script + self.__downloadRequires() + + def __downloaderFinished(self): + """ + Private slot to handle a finished download. + """ + downloader = self.sender() + if downloader in self.__downloaders: + self.__downloaders.remove(downloader) + self.__updating = False + self.updatingChanged.emit(self.__updating) + + def __downloaderError(self): + """ + Private slot to handle a downloader error. + """ + downloader = self.sender() + if downloader in self.__downloaders: + self.__downloaders.remove(downloader) + self.__updating = False + self.updatingChanged.emit(self.__updating) + + def __reloadScript(self): + """ + Private method to reload the script. + """ + self.__parseScript() + + self.__manager.removeScript(self, False) + self.__manager.addScript(self) + + self.scriptChanged.emit() + + def __downloadRequires(self): + """ + Private method to download the required scripts. + """ + for urlStr in self.__require: + if not self.__manager.requireScripts([urlStr]): + downloader = GreaseMonkeyDownloader( + QUrl(urlStr), + self.__manager, + GreaseMonkeyDownloader.DownloadRequireScript) + downloader.finished.connect(self.__requireDownloaded) + downloader.error.connect(self.__requireDownloadError) + self.__downloaders.append(downloader) + + def __requireDownloaded(self): + """ + Private slot to handle a finished download of a required script. + """ + downloader = self.sender() + if downloader in self.__downloaders: + self.__downloaders.remove(downloader) + + self.__reloadScript() + + def __requireDownloadError(self): + """ + Private slot to handle a downloader error. + """ + downloader = self.sender() + if downloader in self.__downloaders: + self.__downloaders.remove(downloader)