diff -r 8ad353f31184 -r 5f8d08aa2217 WebBrowser/GreaseMonkey/GreaseMonkeyScript.py --- a/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py Sun Feb 21 19:54:14 2016 +0100 +++ b/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py Mon Feb 22 19:57:58 2016 +0100 @@ -9,18 +9,25 @@ from __future__ import unicode_literals -from PyQt5.QtCore import QUrl, QRegExp +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QRegExp, \ + QByteArray, QCryptographicHash +from PyQt5.QtWebEngineWidgets import QWebEngineScript from .GreaseMonkeyUrlMatcher import GreaseMonkeyUrlMatcher +from .GreaseMonkeyJavaScript import bootstrap_js, values_js + +from ..Tools.DelayedFileWatcher import DelayedFileWatcher -class GreaseMonkeyScript(object): +class GreaseMonkeyScript(QObject): """ Class implementing the GreaseMonkey script. """ DocumentStart = 0 DocumentEnd = 1 + scriptChanged = pyqtSignal() + def __init__(self, manager, path): """ Constructor @@ -28,7 +35,10 @@ @param manager reference to the manager object (GreaseMonkeyManager) @param path path of the Javascript file (string) """ + super(GreaseMonkeyScript, self).__init__(manager) + self.__manager = manager + self.__fileWatcher = DelayedFileWatcher(parent=None) self.__name = "" self.__namespace = "GreaseMonkeyNS" @@ -39,6 +49,7 @@ self.__exclude = [] self.__downloadUrl = QUrl() + self.__updateUrl = QUrl() self.__startAt = GreaseMonkeyScript.DocumentEnd self.__script = "" @@ -46,10 +57,12 @@ self.__enabled = True self.__valid = False self.__metaData = "" + self.__noFrames = False - self.__parseScript(path) -## , m_fileWatcher(new DelayedFileWatcher(this)) -## connect(m_fileWatcher, SIGNAL(delayedFileChanged(QString)), this, SLOT(watchedFileChanged(QString))); + self.__parseScript() + + self.__fileWatcher.delayedFileChanged.connect( + self.__watchedFileChanged) def isValid(self): """ @@ -107,6 +120,14 @@ """ 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. @@ -115,13 +136,22 @@ """ 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 + return self.__enabled and self.__valid def setEnabled(self, enable): """ @@ -161,10 +191,14 @@ """ return self.__script -##QString GM_Script::metaData() const -##{ -## return m_metadata; -##} + def metaData(self): + """ + Public method to get the script meta information. + + @return script meta information + @rtype str + """ + return self.__metaData def fileName(self): """ @@ -181,7 +215,7 @@ @param urlString URL (string) @return flag indicating a match (boolean) """ - if not self.__enabled: + if not self.isEnabled(): return False for matcher in self.__exclude: @@ -194,6 +228,22 @@ return False + @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.__parseScript() + + self.__manager.removeScript(self, False) + self.__manager.addScript(self) + + self.scriptChanged.emit() + def __parseScript(self, path): """ Private method to parse the given script and populate the data @@ -201,6 +251,24 @@ @param path path of the Javascript file (string) """ + self.__name = "" + self.__namespace = "GreaseMonkeyNS" + self.__description = "" + self.__version = "" + + self.__include = [] + self.__exclude = [] + + self.__downloadUrl = QUrl() + self.__updateUrl = QUrl() + self.__startAt = GreaseMonkeyScript.DocumentEnd + + self.__script = "" + self.__enabled = True + self.__valid = False + self.__metaData = "" + self.__noFrames = False + try: f = open(path, "r", encoding="utf-8") fileData = f.read() @@ -209,6 +277,9 @@ # silently ignore because it shouldn't happen return + if self.__fileName not in self.__fileWatcher.files(): + self.__fileWatcher.addPath(self.__fileName) + rx = QRegExp("// ==UserScript==(.*)// ==/UserScript==") rx.indexIn(fileData) metaDataBlock = rx.cap(1).strip() @@ -219,6 +290,9 @@ requireList = [] for line in metaDataBlock.splitlines(): + if not line.strip(): + continue + if not line.startswith("// @"): continue @@ -247,9 +321,9 @@ elif key == "@version": self.__version = value - elif key == "@updateURL": - self.__downloadUrl = QUrl(value) - +## elif key == "@updateURL": +## self.__downloadUrl = QUrl(value) +## elif key in ["@include", "@match"]: self.__include.append(GreaseMonkeyUrlMatcher(value)) @@ -267,6 +341,9 @@ elif key == "@downloadURL" and self.__downloadUrl.isEmpty(): self.__downloadUrl = QUrl(value) + + elif key == "@updateURL" and self.__updateUrl.isEmpty(): + self.__updateUrl = QUrl(value) if not self.__include: self.__include.append(GreaseMonkeyUrlMatcher("*")) @@ -275,31 +352,32 @@ index = fileData.find(marker) + len(marker) self.__metaData = fileData[:index] script = fileData[index:].strip() - script = "{0}{1}".format( - self.__manager.requireScripts(requireList), - script) - self.__script = "(function(){{{0}}})();".format(script) - self.__valid = len(script) > 0 -## const QString nspace = QCryptographicHash::hash(fullName().toUtf8(), QCryptographicHash::Md4).toHex(); -## const QString gmValues = m_manager->valuesScript().arg(nspace); -## -## m_script = QSL("(function(){%1\n%2\n%3\n})();").arg(gmValues, m_manager->requireScripts(requireList), script); -## m_valid = true; + + nspace = bytes(QCryptographicHash.hash( + QByteArray(self.fullName().encode("utf-8")), + QCryptographicHash.Md4).toHex()).decode("ascii") + valuesScript = values_js.format(nspace) + self.__script = "(function(){{{0}\n{1}\n{2}\n}})();".format( + valuesScript, self.__manager.requireScripts(requireList), script + ) + self.__valid = True def webScript(self): """ - Public method to create a script object + Public method to create a script object. @return prepared script object @rtype QWebEngineScript """ -##QWebEngineScript GM_Script::webScript() const -##{ -## QWebEngineScript script; -## script.setName(fullName()); -## script.setInjectionPoint(startAt() == DocumentStart ? QWebEngineScript::DocumentCreation : QWebEngineScript::DocumentReady); -## script.setWorldId(QWebEngineScript::MainWorld); -## script.setRunsOnSubFrames(!m_noframes); -## script.setSourceCode(QSL("%1\n%2\n%3").arg(m_metadata, m_manager->bootstrapScript(), m_script)); -## return script; -##} + script = QWebEngineScript() + script.setName(self.fullName()) + if self.startAt() == GreaseMonkeyScript.DocumentStart: + script.setInjectionPoint(QWebEngineScript.DocumentCreation) + else: + script.setInjectionPoint(QWebEngineScript.DocumentReady) + script.setWorldId(QWebEngineScript.MainWorld) + script.setRunsOnSubFrames(not self.__noFrames) + script.setSourceCode("{0}\n{1}\n{2}".format( + self.__metaData, bootstrap_js, self.__script + )) + return script