diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py --- a/src/eric7/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py Wed Jul 13 14:55:47 2022 +0200 @@ -10,7 +10,12 @@ import re from PyQt6.QtCore import ( - pyqtSignal, pyqtSlot, QObject, QUrl, QByteArray, QCryptographicHash + pyqtSignal, + pyqtSlot, + QObject, + QUrl, + QByteArray, + QCryptographicHash, ) from PyQt6.QtGui import QIcon, QPixmap, QImage from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply @@ -27,227 +32,227 @@ class GreaseMonkeyScript(QObject): """ Class implementing the GreaseMonkey script. - + @signal scriptChanged() emitted to indicate a script change @signal updatingChanged(bool) emitted to indicate a change of the updating state """ + DocumentStart = 0 DocumentEnd = 1 DocumentIdle = 2 - + scriptChanged = pyqtSignal() updatingChanged = pyqtSignal(bool) - + def __init__(self, manager, path): """ Constructor - + @param manager reference to the manager object (GreaseMonkeyManager) @param path path of the Javascript file (string) """ super().__init__(manager) - + self.__manager = manager self.__fileWatcher = DelayedFileWatcher(parent=None) - + self.__name = "" self.__namespace = "GreaseMonkeyNS" self.__description = "" self.__version = "" - + self.__include = [] self.__exclude = [] self.__require = [] - + self.__icon = QIcon() self.__iconUrl = QUrl() self.__downloadUrl = QUrl() self.__updateUrl = QUrl() self.__startAt = GreaseMonkeyScript.DocumentEnd - + self.__script = "" self.__fileName = path self.__enabled = True self.__valid = False self.__noFrames = False - + self.__updating = False - + self.__downloaders = [] self.__iconReplies = [] - + self.__parseScript() - - self.__fileWatcher.delayedFileChanged.connect( - self.__watchedFileChanged) - + + self.__fileWatcher.delayedFileChanged.connect(self.__watchedFileChanged) + def isValid(self): """ Public method to check the validity of the script. - + @return flag indicating a valid script (boolean) """ return self.__valid - + def name(self): """ Public method to get the name of the script. - + @return name of the script (string) """ return self.__name - + def nameSpace(self): """ Public method to get the name space of the script. - + @return name space of the script (string) """ return self.__namespace - + def fullName(self): """ Public method to get the full name of the script. - + @return full name of the script (string) """ return "{0}/{1}".format(self.__namespace, self.__name) - + def description(self): """ Public method to get the description of the script. - + @return description of the script (string) """ return self.__description - + def version(self): """ Public method to get the version of the script. - + @return version of the script (string) """ return self.__version - + def icon(self): """ Public method to get the icon of the script. - + @return script icon @rtype QIcon """ return self.__icon - + def iconUrl(self): """ Public method to get the icon URL of the script. - + @return icon URL of the script (QUrl) """ return QUrl(self.__iconUrl) - + def downloadUrl(self): """ Public method to get the download URL of the script. - + @return download URL of the script (QUrl) """ return QUrl(self.__downloadUrl) - + def updateUrl(self): """ Public method to get the update URL of the script. - + @return update URL of the script (QUrl) """ return QUrl(self.__updateUrl) - + def startAt(self): """ Public method to get the start point of the script. - + @return start point of the script (DocumentStart or DocumentEnd) """ return self.__startAt - + def noFrames(self): """ Public method to get the noFrames flag. - + @return flag indicating to not run on sub frames @rtype bool """ return self.__noFrames - + def isEnabled(self): """ Public method to check, if the script is enabled. - + @return flag indicating an enabled state (boolean) """ return self.__enabled and self.__valid - + def setEnabled(self, enable): """ Public method to enable a script. - + @param enable flag indicating the new enabled state (boolean) """ self.__enabled = enable - + def include(self): """ Public method to get the list of included URLs. - + @return list of included URLs (list of strings) """ return self.__include[:] - + def exclude(self): """ Public method to get the list of excluded URLs. - + @return list of excluded URLs (list of strings) """ return self.__exclude[:] - + def require(self): """ Public method to get the list of required scripts. - + @return list of required scripts (list of strings) """ return self.__require[:] - + def fileName(self): """ Public method to get the path of the Javascript file. - + @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): """ Private slot handling changes of the script file. - + @param fileName path of the script file @type str """ if self.__fileName == fileName: self.__reloadScript() - + def __parseScript(self): """ Private method to parse the given script and populate the data @@ -257,83 +262,80 @@ self.__namespace = "GreaseMonkeyNS" self.__description = "" self.__version = "" - + self.__include = [] self.__exclude = [] self.__require = [] - + self.__icon = QIcon() self.__iconUrl = QUrl() self.__downloadUrl = QUrl() self.__updateUrl = QUrl() self.__startAt = GreaseMonkeyScript.DocumentEnd - + self.__script = "" self.__enabled = True self.__valid = False self.__noFrames = False - + try: with open(self.__fileName, "r", encoding="utf-8") as f: fileData = f.read() except OSError: # silently ignore because it shouldn't happen return - + if self.__fileName not in self.__fileWatcher.files(): self.__fileWatcher.addPath(self.__fileName) - - rx = re.compile( - r"""// ==UserScript==(.*)// ==/UserScript==""", - re.DOTALL - ) + + rx = re.compile(r"""// ==UserScript==(.*)// ==/UserScript==""", re.DOTALL) match = rx.search(fileData) if match is None: # invalid script file return - + metaDataBlock = match.group(1).strip() if metaDataBlock == "": # invalid script file return - + for line in metaDataBlock.splitlines(): if not line.strip(): continue - + if not line.startswith("// @"): continue - + line = line[3:].replace("\t", " ") index = line.find(" ") - + key = line[:index].strip() - value = line[index + 1:].strip() if index > 0 else "" - + value = line[index + 1 :].strip() if index > 0 else "" + if not key: continue - + if key == "@name": self.__name = value - + elif key == "@namespace": self.__namespace = value - + elif key == "@description": self.__description = value - + elif key == "@version": self.__version = value - + elif key in ["@include", "@match"]: self.__include.append(value) - + elif key in ["@exclude", "@exclude_match"]: self.__exclude.append(value) - + elif key == "@require": self.__require.append(value) - + elif key == "@run-at": if value == "document-end": self.__startAt = GreaseMonkeyScript.DocumentEnd @@ -341,80 +343,79 @@ self.__startAt = GreaseMonkeyScript.DocumentStart elif value == "document-idle": self.__startAt = GreaseMonkeyScript.DocumentIdle - + elif key == "@downloadURL" and self.__downloadUrl.isEmpty(): self.__downloadUrl = QUrl(value) - + elif key == "@updateURL" and self.__updateUrl.isEmpty(): self.__updateUrl = QUrl(value) - + elif key == "@icon": self.__iconUrl = QUrl(value) - + elif key == "@noframes": self.__noFrames = True - + self.__iconUrl = self.__downloadUrl.resolved(self.__iconUrl) - + if not self.__include: self.__include.append("*") - - nspace = bytes(QCryptographicHash.hash( - QByteArray(self.fullName().encode("utf-8")), - QCryptographicHash.Algorithm.Md4).toHex()).decode("ascii") + + nspace = bytes( + QCryptographicHash.hash( + QByteArray(self.fullName().encode("utf-8")), + QCryptographicHash.Algorithm.Md4, + ).toHex() + ).decode("ascii") valuesScript = values_js.format(nspace) self.__script = "(function(){{{0}\n{1}\n{2}\n}})();".format( - valuesScript, self.__manager.requireScripts(self.__require), - fileData + valuesScript, self.__manager.requireScripts(self.__require), fileData ) self.__valid = True - + self.__downloadIcon() self.__downloadRequires() - + def webScript(self): """ Public method to create a script object. - + @return prepared script object @rtype QWebEngineScript """ script = QWebEngineScript() - script.setSourceCode("{0}\n{1}".format( - bootstrap_js, self.__script - )) + script.setSourceCode("{0}\n{1}".format(bootstrap_js, self.__script)) script.setName(self.fullName()) script.setWorldId(WebBrowserPage.SafeJsWorld) script.setRunsOnSubFrames(not self.__noFrames) 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) + GreaseMonkeyDownloader.DownloadMainScript, + ) downloader.updateScript(self.__fileName) - downloader.finished.connect( - lambda: self.__downloaderFinished(downloader)) - downloader.error.connect( - lambda: self.__downloaderError(downloader)) + downloader.finished.connect(lambda: self.__downloaderFinished(downloader)) + downloader.error.connect(lambda: self.__downloaderError(downloader)) self.__downloaders.append(downloader) - + self.__downloadRequires() - + def __downloaderFinished(self, downloader): """ Private slot to handle a finished download. - + @param downloader reference to the downloader object @type GreaseMonkeyDownloader """ @@ -422,11 +423,11 @@ self.__downloaders.remove(downloader) self.__updating = False self.updatingChanged.emit(self.__updating) - + def __downloaderError(self, downloader): """ Private slot to handle a downloader error. - + @param downloader reference to the downloader object @type GreaseMonkeyDownloader """ @@ -434,18 +435,18 @@ 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. @@ -455,35 +456,38 @@ downloader = GreaseMonkeyDownloader( QUrl(urlStr), self.__manager, - GreaseMonkeyDownloader.DownloadRequireScript) + GreaseMonkeyDownloader.DownloadRequireScript, + ) downloader.finished.connect( - lambda: self.__requireDownloaded(downloader)) + lambda: self.__requireDownloaded(downloader) + ) downloader.error.connect( - lambda: self.__requireDownloadError(downloader)) + lambda: self.__requireDownloadError(downloader) + ) self.__downloaders.append(downloader) - + def __requireDownloaded(self, downloader): """ Private slot to handle a finished download of a required script. - + @param downloader reference to the downloader object @type GreaseMonkeyDownloader """ if downloader in self.__downloaders: self.__downloaders.remove(downloader) - + self.__reloadScript() - + def __requireDownloadError(self, downloader): """ Private slot to handle a downloader error. - + @param downloader reference to the downloader object @type GreaseMonkeyDownloader """ if downloader in self.__downloaders: self.__downloaders.remove(downloader) - + def __downloadIcon(self): """ Private slot to download the script icon. @@ -493,17 +497,17 @@ reply = WebBrowserWindow.networkManager().get(request) reply.finished.connect(lambda: self.__iconDownloaded(reply)) self.__iconReplies.append(reply) - + def __iconDownloaded(self, reply): """ Private slot to handle a finished download of a script icon. - + @param reply reference to the network reply @type QNetworkReply """ if reply in self.__iconReplies: self.__iconReplies.remove(reply) - + reply.deleteLater() if reply.error() == QNetworkReply.NetworkError.NoError: self.__icon = QPixmap.fromImage(QImage.fromData(reply.readAll()))