Continued porting the web browser. QtWebEngine

Sun, 21 Feb 2016 19:54:14 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 21 Feb 2016 19:54:14 +0100
branch
QtWebEngine
changeset 4763
8ad353f31184
parent 4762
ea40955a0937
child 4766
5f8d08aa2217

Continued porting the web browser.

- started adding the GreaseMonkey stuff

WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.ui file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.ui file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListDelegate.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListWidget.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.ui file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyDownloader.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyJavaScript.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyManager.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyScript.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyUrlInterceptor.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyUrlMatcher.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/DelayedFileWatcher.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/WebBrowserTools.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing a dialog for adding GreaseMonkey scripts..
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, QDir, QFile
+from PyQt5.QtWidgets import QDialog
+
+from E5Gui import E5MessageBox
+
+from .Ui_GreaseMonkeyAddScriptDialog import Ui_GreaseMonkeyAddScriptDialog
+
+import UI.PixmapCache
+
+
+class GreaseMonkeyAddScriptDialog(QDialog, Ui_GreaseMonkeyAddScriptDialog):
+    """
+    Class implementing a dialog for adding GreaseMonkey scripts..
+    """
+    def __init__(self, manager, script, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the GreaseMonkey manager
+            (GreaseMonkeyManager)
+        @param script GreaseMonkey script to be added (GreaseMonkeyScript)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyAddScriptDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("greaseMonkey48.png"))
+        
+        self.__manager = manager
+        self.__script = script
+        
+        runsAt = ""
+        doesNotRunAt = ""
+        
+        include = script.include()
+        exclude = script.exclude()
+        
+        if include:
+            runsAt = self.tr("<p>runs at:<br/><i>{0}</i></p>").format(
+                "<br/>".join(include))
+        
+        if exclude:
+            doesNotRunAt = self.tr(
+                "<p>does not run at:<br/><i>{0}</i></p>").format(
+                "<br/>".join(exclude))
+        
+        scriptInfoTxt = "<p><b>{0}</b> {1}<br/>{2}</p>{3}{4}".format(
+            script.name(), script.version(), script.description(), runsAt,
+            doesNotRunAt)
+        self.scriptInfo.setHtml(scriptInfoTxt)
+        
+        self.accepted.connect(self.__accepted)
+    
+    @pyqtSlot()
+    def on_showScriptSourceButton_clicked(self):
+        """
+        Private slot to show an editor window with the source code.
+        """
+        from WebBrowser.Tools import WebBrowserTools
+        
+        tmpFileName = WebBrowserTools.ensureUniqueFilename(
+            os.path.join(QDir.tempPath(), "tmp-userscript.js"))
+        if QFile.copy(self.__script.fileName(), tmpFileName):
+            from QScintilla.MiniEditor import MiniEditor
+            editor = MiniEditor(tmpFileName, "JavaScript", self)
+            editor.show()
+    
+    def __accepted(self):
+        """
+        Private slot handling the accepted signal.
+        """
+        if self.__manager.addScript(self.__script):
+            msg = self.tr(
+                "<p><b>{0}</b> installed successfully.</p>").format(
+                self.__script.name())
+            success = True
+        else:
+            msg = self.tr("<p>Cannot install script.</p>")
+            success = False
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if success and WebBrowserWindow.notificationsEnabled():
+            WebBrowserWindow.showNotification(
+                UI.PixmapCache.getPixmap("greaseMonkey48.png"),
+                self.tr("GreaseMonkey Script Installation"),
+                msg)
+        else:
+            E5MessageBox.information(
+                self,
+                self.tr("GreaseMonkey Script Installation"),
+                msg)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.ui	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GreaseMonkeyAddScriptDialog</class>
+ <widget class="QDialog" name="GreaseMonkeyAddScriptDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GreaseMonkey Script Installation</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>&lt;h2&gt;GreaseMonkey Script Installation&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>You are about to install this userscript into GreaseMonkey:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTextBrowser" name="scriptInfo"/>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_5">
+     <property name="text">
+      <string>&lt;b&gt;You should only install scripts from sources you trust!&lt;/b&gt;</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Are you sure you want to install it?</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="showScriptSourceButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Press to open an editor with the script's source</string>
+       </property>
+       <property name="text">
+        <string>Show source code of script</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GreaseMonkeyAddScriptDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>401</x>
+     <y>389</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GreaseMonkeyAddScriptDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>439</x>
+     <y>389</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the GreaseMonkey scripts configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QUrl
+from PyQt5.QtGui import QDesktopServices
+from PyQt5.QtWidgets import QDialog, QListWidgetItem
+
+from E5Gui import E5MessageBox
+
+from .Ui_GreaseMonkeyConfigurationDialog import \
+    Ui_GreaseMonkeyConfigurationDialog
+
+import UI.PixmapCache
+
+
+class GreaseMonkeyConfigurationDialog(
+        QDialog, Ui_GreaseMonkeyConfigurationDialog):
+    """
+    Class implementing the GreaseMonkey scripts configuration dialog.
+    """
+    ScriptVersionRole = Qt.UserRole
+    ScriptDescriptionRole = Qt.UserRole + 1
+    ScriptRole = Qt.UserRole + 2
+    
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the manager object (GreaseMonkeyManager)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyConfigurationDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("greaseMonkey48.png"))
+        
+        self.__manager = manager
+        
+        self.__loadScripts()
+        
+        self.scriptsList.removeItemRequested.connect(self.__removeItem)
+        self.scriptsList.itemChanged.connect(self.__itemChanged)
+    
+    @pyqtSlot()
+    def on_openDirectoryButton_clicked(self):
+        """
+        Private slot to open the GreaseMonkey scripts directory.
+        """
+        QDesktopServices.openUrl(
+            QUrl.fromLocalFile(self.__manager.scriptsDirectory()))
+    
+    @pyqtSlot(str)
+    def on_downloadLabel_linkActivated(self, link):
+        """
+        Private slot to open the greasyfork.org web site.
+        
+        @param link URL (string)
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if not link or "userscript.org" in link:
+            # userscript.org is down, default to Greasy Fork.
+            link = "https://greasyfork.org/"
+        WebBrowserWindow.mainWindow().newTab(QUrl(link))
+        self.close()
+    
+    @pyqtSlot(QListWidgetItem)
+    def on_scriptsList_itemDoubleClicked(self, item):
+        """
+        Private slot to show information about the selected script.
+        
+        @param item reference to the double clicked item (QListWidgetItem)
+        """
+        script = self.__getScript(item)
+        if script is not None:
+            from .GreaseMonkeyConfigurationScriptInfoDialog import \
+                GreaseMonkeyConfigurationScriptInfoDialog
+            infoDlg = GreaseMonkeyConfigurationScriptInfoDialog(script, self)
+            infoDlg.exec_()
+    
+    def __loadScripts(self):
+        """
+        Private method to load all the available scripts.
+        """
+        for script in self.__manager.allScripts():
+            itm = QListWidgetItem(
+                UI.PixmapCache.getIcon("greaseMonkeyScript.png"),
+                script.name(), self.scriptsList)
+            itm.setData(
+                GreaseMonkeyConfigurationDialog.ScriptVersionRole,
+                script.version())
+            itm.setData(
+                GreaseMonkeyConfigurationDialog.ScriptDescriptionRole,
+                script.description())
+            itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable)
+            if script.isEnabled():
+                itm.setCheckState(Qt.Checked)
+            else:
+                itm.setCheckState(Qt.Unchecked)
+            itm.setData(GreaseMonkeyConfigurationDialog.ScriptRole, script)
+            self.scriptsList.addItem(itm)
+        
+        self.scriptsList.sortItems()
+        
+        itemMoved = True
+        while itemMoved:
+            itemMoved = False
+            for row in range(self.scriptsList.count()):
+                topItem = self.scriptsList.item(row)
+                bottomItem = self.scriptsList.item(row + 1)
+                if topItem is None or bottomItem is None:
+                    continue
+                
+                if topItem.checkState() == Qt.Unchecked and \
+                   bottomItem.checkState == Qt.Checked:
+                    itm = self.scriptsList.takeItem(row + 1)
+                    self.scriptsList.insertItem(row, itm)
+                    itemMoved = True
+    
+    def __getScript(self, itm):
+        """
+        Private method to get the script for the given item.
+        
+        @param itm item to get script for (QListWidgetItem)
+        @return reference to the script object (GreaseMonkeyScript)
+        """
+        if itm is None:
+            return None
+        
+        script = itm.data(GreaseMonkeyConfigurationDialog.ScriptRole)
+        return script
+    
+    def __removeItem(self, itm):
+        """
+        Private slot to remove a script item.
+        
+        @param itm item to be removed (QListWidgetItem)
+        """
+        script = self.__getScript(itm)
+        if script is None:
+            return
+        
+        removeIt = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Script"),
+            self.tr(
+                """<p>Are you sure you want to remove <b>{0}</b>?</p>""")
+            .format(script.name()))
+        if removeIt and self.__manager.removeScript(script):
+            self.scriptsList.takeItem(self.scriptsList.row(itm))
+            del itm
+    
+    def __itemChanged(self, itm):
+        """
+        Private slot to handle changes of a script item.
+        
+        @param itm changed item (QListWidgetItem)
+        """
+        script = self.__getScript(itm)
+        if script is None:
+            return
+        
+        if itm.checkState() == Qt.Checked:
+            self.__manager.enableScript(script)
+        else:
+            self.__manager.disableScript(script)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.ui	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GreaseMonkeyConfigurationDialog</class>
+ <widget class="QDialog" name="GreaseMonkeyConfigurationDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GreaseMonkey Scripts Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>&lt;h2&gt;GreaseMonkey Scripts&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Double clicking script will show additional information.</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="GreaseMonkeyConfigurationListWidget" name="scriptsList">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="verticalScrollMode">
+      <enum>QAbstractItemView::ScrollPerPixel</enum>
+     </property>
+     <property name="uniformItemSizes">
+      <bool>true</bool>
+     </property>
+     <property name="selectionRectVisible">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="downloadLabel">
+     <property name="text">
+      <string>&lt;p&gt;Get more scripts from &lt;a href=&quot;https://greasyfork.org/&quot;&gt;greasyfork.org&lt;/a&gt; or via &lt;a href=&quot;http://wiki.greasespot.net/User_Script_Hosting&quot;&gt;Greasespot Wiki.&lt;/a&gt;&lt;/p&gt;</string>
+     </property>
+     <property name="textFormat">
+      <enum>Qt::RichText</enum>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+     <property name="textInteractionFlags">
+      <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QPushButton" name="openDirectoryButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Press to open the scripts directory</string>
+       </property>
+       <property name="text">
+        <string>Open Scripts Directory</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>GreaseMonkeyConfigurationListWidget</class>
+   <extends>QListWidget</extends>
+   <header>.GreaseMonkeyConfigurationListWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>scriptsList</tabstop>
+  <tabstop>openDirectoryButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GreaseMonkeyConfigurationDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GreaseMonkeyConfigurationDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListDelegate.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a delegate for the special list widget for GreaseMonkey
+scripts.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QSize, QRect, qVersion
+from PyQt5.QtGui import QFontMetrics, QPalette, QFont
+from PyQt5.QtWidgets import QStyle, QStyledItemDelegate, QApplication
+if qVersion() >= "5.0.0":
+    from PyQt5.QtWidgets import QStyleOptionViewItem
+else:
+    from PyQt5.QtWidgets import QStyleOptionViewItemV4 as QStyleOptionViewItem
+
+import UI.PixmapCache
+import Globals
+
+
+class GreaseMonkeyConfigurationListDelegate(QStyledItemDelegate):
+    """
+    Class implementing a delegate for the special list widget for GreaseMonkey
+    scripts.
+    """
+    IconSize = 32
+    RemoveIconSize = 16
+    CheckBoxSize = 18
+    MinPadding = 5
+    ItemWidth = 200
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(GreaseMonkeyConfigurationListDelegate, self).__init__(parent)
+        
+        self.__removePixmap = \
+            UI.PixmapCache.getIcon("greaseMonkeyTrash.png").pixmap(
+                GreaseMonkeyConfigurationListDelegate.RemoveIconSize)
+        self.__rowHeight = 0
+        self.__padding = 0
+    
+    def padding(self):
+        """
+        Public method to get the padding used.
+        
+        @return padding used (integer)
+        """
+        return self.__padding
+    
+    def paint(self, painter, option, index):
+        """
+        Public method to paint the specified list item.
+        
+        @param painter painter object to paint to (QPainter)
+        @param option style option used for painting (QStyleOptionViewItem)
+        @param index model index of the item (QModelIndex)
+        """
+        opt = QStyleOptionViewItem(option)
+        self.initStyleOption(opt, index)
+        
+        widget = opt.widget
+        style = widget.style() if widget is not None else QApplication.style()
+        height = opt.rect.height()
+        center = height // 2 + opt.rect.top()
+        
+        # Prepare title font
+        titleFont = QFont(opt.font)
+        titleFont.setBold(True)
+        titleFont.setPointSize(titleFont.pointSize() + 1)
+        
+        titleMetrics = QFontMetrics(titleFont)
+        if Globals.isWindowsPlatform():
+            colorRole = QPalette.Text
+        else:
+            colorRole = QPalette.HighlightedText \
+                if opt.state & QStyle.State_Selected else QPalette.Text
+        
+        leftPos = self.__padding
+        rightPos = opt.rect.right() - self.__padding - \
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize
+        
+        # Draw background
+        style.drawPrimitive(QStyle.PE_PanelItemViewItem, opt, painter, widget)
+        
+        # Draw checkbox
+        checkBoxYPos = center - \
+            GreaseMonkeyConfigurationListDelegate.CheckBoxSize // 2
+        opt2 = QStyleOptionViewItem(opt)
+        if opt2.checkState == Qt.Checked:
+            opt2.state |= QStyle.State_On
+        else:
+            opt2.state |= QStyle.State_Off
+        styleCheckBoxRect = style.subElementRect(
+            QStyle.SE_ViewItemCheckIndicator, opt2, widget)
+        opt2.rect = QRect(
+            leftPos, checkBoxYPos,
+            styleCheckBoxRect.width(), styleCheckBoxRect.height())
+        style.drawPrimitive(QStyle.PE_IndicatorViewItemCheck, opt2, painter,
+                            widget)
+        leftPos = opt2.rect.right() + self.__padding
+        
+        # Draw icon
+        iconYPos = center - GreaseMonkeyConfigurationListDelegate.IconSize // 2
+        iconRect = QRect(leftPos, iconYPos,
+                         GreaseMonkeyConfigurationListDelegate.IconSize,
+                         GreaseMonkeyConfigurationListDelegate.IconSize)
+        pixmap = index.data(Qt.DecorationRole).pixmap(
+            GreaseMonkeyConfigurationListDelegate.IconSize)
+        painter.drawPixmap(iconRect, pixmap)
+        leftPos = iconRect.right() + self.__padding
+        
+        # Draw script name
+        name = index.data(Qt.DisplayRole)
+        leftTitleEdge = leftPos + 2
+        rightTitleEdge = rightPos - self.__padding
+        leftPosForVersion = titleMetrics.width(name) + self.__padding
+        nameRect = QRect(leftTitleEdge, opt.rect.top() + self.__padding,
+                         rightTitleEdge - leftTitleEdge, titleMetrics.height())
+        painter.setFont(titleFont)
+        style.drawItemText(painter, nameRect, Qt.AlignLeft, opt.palette, True,
+                           name, colorRole)
+        
+        # Draw version
+        version = index.data(Qt.UserRole)
+        versionRect = QRect(
+            nameRect.x() + leftPosForVersion, nameRect.y(),
+            rightTitleEdge - leftTitleEdge, titleMetrics.height())
+        versionFont = titleFont
+        painter.setFont(versionFont)
+        style.drawItemText(painter, versionRect, Qt.AlignLeft, opt.palette,
+                           True, version, colorRole)
+        
+        # Draw description
+        infoYPos = nameRect.bottom() + opt.fontMetrics.leading()
+        infoRect = QRect(
+            nameRect.x(), infoYPos,
+            nameRect.width(), opt.fontMetrics.height())
+        info = opt.fontMetrics.elidedText(
+            index.data(Qt.UserRole + 1), Qt.ElideRight, infoRect.width())
+        painter.setFont(opt.font)
+        style.drawItemText(painter, infoRect, Qt.AlignLeft | Qt.TextSingleLine,
+                           opt.palette, True, info, colorRole)
+        
+        # Draw remove button
+        removeIconYPos = center - \
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize // 2
+        removeIconRect = QRect(
+            rightPos, removeIconYPos,
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize,
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize)
+        painter.drawPixmap(removeIconRect, self.__removePixmap)
+    
+    def sizeHint(self, option, index):
+        """
+        Public method to get a size hint for the specified list item.
+        
+        @param option style option used for painting (QStyleOptionViewItem)
+        @param index model index of the item (QModelIndex)
+        @return size hint (QSize)
+        """
+        if not self.__rowHeight:
+            opt = QStyleOptionViewItem(option)
+            self.initStyleOption(opt, index)
+            
+            widget = opt.widget
+            style = widget.style() if widget is not None \
+                else QApplication.style()
+            padding = style.pixelMetric(QStyle.PM_FocusFrameHMargin) + 1
+            
+            titleFont = opt.font
+            titleFont.setBold(True)
+            titleFont.setPointSize(titleFont.pointSize() + 1)
+            
+            self.__padding = padding \
+                if padding > GreaseMonkeyConfigurationListDelegate.MinPadding \
+                else GreaseMonkeyConfigurationListDelegate.MinPadding
+            
+            titleMetrics = QFontMetrics(titleFont)
+            
+            self.__rowHeight = 2 * self.__padding + \
+                opt.fontMetrics.leading() + \
+                opt.fontMetrics.height() + \
+                titleMetrics.height()
+        
+        return QSize(GreaseMonkeyConfigurationListDelegate.ItemWidth,
+                     self.__rowHeight)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListWidget.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a special list widget for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QRect
+from PyQt5.QtWidgets import QListWidget, QListWidgetItem
+
+from .GreaseMonkeyConfigurationListDelegate import \
+    GreaseMonkeyConfigurationListDelegate
+
+
+class GreaseMonkeyConfigurationListWidget(QListWidget):
+    """
+    Class implementing a special list widget for GreaseMonkey scripts.
+    """
+    removeItemRequested = pyqtSignal(QListWidgetItem)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyConfigurationListWidget, self).__init__(parent)
+        
+        self.__delegate = GreaseMonkeyConfigurationListDelegate(self)
+        self.setItemDelegate(self.__delegate)
+    
+    def __containsRemoveIcon(self, pos):
+        """
+        Private method to check, if the given position is inside the remove
+        icon.
+        
+        @param pos position to check for (QPoint)
+        @return flag indicating success (boolean)
+        """
+        itm = self.itemAt(pos)
+        if itm is None:
+            return False
+        
+        rect = self.visualItemRect(itm)
+        iconSize = GreaseMonkeyConfigurationListDelegate.RemoveIconSize
+        removeIconXPos = rect.right() - self.__delegate.padding() - iconSize
+        center = rect.height() // 2 + rect.top()
+        removeIconYPos = center - iconSize // 2
+        
+        removeIconRect = QRect(removeIconXPos, removeIconYPos,
+                               iconSize, iconSize)
+        return removeIconRect.contains(pos)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method handling presses of mouse buttons.
+        
+        @param evt mouse press event (QMouseEvent)
+        """
+        if self.__containsRemoveIcon(evt.pos()):
+            self.removeItemRequested.emit(self.itemAt(evt.pos()))
+            return
+        
+        super(GreaseMonkeyConfigurationListWidget, self).mousePressEvent(evt)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method handling mouse double click events.
+        
+        @param evt mouse press event (QMouseEvent)
+        """
+        if self.__containsRemoveIcon(evt.pos()):
+            self.removeItemRequested.emit(self.itemAt(evt.pos()))
+            return
+        
+        super(GreaseMonkeyConfigurationListWidget, self).mouseDoubleClickEvent(
+            evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show GreaseMonkey script information.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_GreaseMonkeyConfigurationScriptInfoDialog import \
+    Ui_GreaseMonkeyConfigurationScriptInfoDialog
+
+from ..GreaseMonkeyScript import GreaseMonkeyScript
+
+import UI.PixmapCache
+
+
+class GreaseMonkeyConfigurationScriptInfoDialog(
+        QDialog, Ui_GreaseMonkeyConfigurationScriptInfoDialog):
+    """
+    Class implementing a dialog to show GreaseMonkey script information.
+    """
+    def __init__(self, script, parent=None):
+        """
+        Constructor
+        
+        @param script reference to the script (GreaseMonkeyScript)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyConfigurationScriptInfoDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("greaseMonkey48.png"))
+        
+        self.__scriptFileName = script.fileName()
+        
+        self.setWindowTitle(
+            self.tr("Script Details of {0}").format(script.name()))
+        
+        self.nameLabel.setText(script.fullName())
+        self.versionLabel.setText(script.version())
+        self.urlLabel.setText(script.downloadUrl().toString())
+        if script.startAt() == GreaseMonkeyScript.DocumentStart:
+            self.startAtLabel.setText("document-start")
+        else:
+            self.startAtLabel.setText("document-end")
+        self.descriptionBrowser.setHtml(script.description())
+        self.runsAtBrowser.setHtml("<br/>".join(script.include()))
+        self.doesNotRunAtBrowser.setHtml("<br/>".join(script.exclude()))
+    
+    @pyqtSlot()
+    def on_showScriptSourceButton_clicked(self):
+        """
+        Private slot to show an editor window with the script source code.
+        """
+        from QScintilla.MiniEditor import MiniEditor
+        editor = MiniEditor(self.__scriptFileName, "JavaScript", self)
+        editor.show()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.ui	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GreaseMonkeyConfigurationScriptInfoDialog</class>
+ <widget class="QDialog" name="GreaseMonkeyConfigurationScriptInfoDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_8">
+       <property name="text">
+        <string>&lt;h2&gt;GreaseMonkey Script Details&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Name:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="nameLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Version:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLabel" name="versionLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>URL:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="urlLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Start at:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QLabel" name="startAtLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Description:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QTextBrowser" name="descriptionBrowser"/>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="label_6">
+       <property name="text">
+        <string>Runs at:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QTextBrowser" name="runsAtBrowser"/>
+     </item>
+     <item row="6" column="0">
+      <widget class="QLabel" name="label_7">
+       <property name="text">
+        <string>Does not run at:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="1">
+      <widget class="QTextBrowser" name="doesNotRunAtBrowser"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="showScriptSourceButton">
+       <property name="toolTip">
+        <string>Press to open an editor with the script's source</string>
+       </property>
+       <property name="text">
+        <string>Show source code of script</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Close</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>descriptionBrowser</tabstop>
+  <tabstop>runsAtBrowser</tabstop>
+  <tabstop>doesNotRunAtBrowser</tabstop>
+  <tabstop>showScriptSourceButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GreaseMonkeyConfigurationScriptInfoDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>402</x>
+     <y>484</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GreaseMonkeyConfigurationScriptInfoDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>470</x>
+     <y>490</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/__init__.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the GreaseMonkey configuration dialogs.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyDownloader.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the downloader for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QSettings, QRegExp, QUrl
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtNetwork import QNetworkReply
+
+from E5Gui import E5MessageBox
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from WebBrowser.Network.FollowRedirectReply import FollowRedirectReply
+
+
+class GreaseMonkeyDownloader(QObject):
+    """
+    Class implementing the downloader for GreaseMonkey scripts.
+    """
+    finished = pyqtSignal()
+    
+    def __init__(self, request, manager):
+        """
+        Constructor
+        
+        @param request reference to the request object (QNetworkRequest)
+        @param manager reference to the GreaseMonkey manager
+            (GreaseMonkeyManager)
+        """
+        super(GreaseMonkeyDownloader, self).__init__()
+        
+        self.__manager = manager
+        
+        self.__reply = FollowRedirectReply(
+            request.url(), WebBrowserWindow.networkAccessManager())
+        self.__reply.finished.connect(self.__scriptDownloaded)
+        
+        self.__fileName = ""
+        self.__requireUrls = []
+    
+    def __scriptDownloaded(self):
+        """
+        Private slot to handle the finished download of a script.
+        """
+        if self.sender() != self.__reply:
+            self.finished.emit()
+            return
+        
+        response = bytes(self.__reply.readAll()).decode()
+        
+        if self.__reply.error() == QNetworkReply.NoError and \
+           "// ==UserScript==" in response:
+            from WebBrowser.Tools import WebBrowserTools
+            filePath = os.path.join(
+                self.__manager.scriptsDirectory(),
+                WebBrowserTools.getFileNameFromUrl(self.__reply.url()))
+            self.__fileName = WebBrowserTools.ensureUniqueFilename(filePath)
+            
+            try:
+                f = open(self.__fileName, "w", encoding="utf-8")
+            except (IOError, OSError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("GreaseMonkey Download"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be opened"""
+                        """ for writing.<br/>Reason: {1}</p>""").format(
+                        self.__fileName, str(err)))
+                self.finished.emit()
+                return
+            f.write(response)
+            f.close()
+            
+            settings = QSettings(
+                os.path.join(self.__manager.requireScriptsDirectory(),
+                             "requires.ini"),
+                QSettings.IniFormat)
+            settings.beginGroup("Files")
+            
+            rx = QRegExp("@require(.*)\\n")
+            rx.setMinimal(True)
+            rx.indexIn(response)
+            
+            for i in range(1, rx.captureCount() + 1):
+                url = rx.cap(i).strip()
+                if url and not settings.contains(url):
+                    self.__requireUrls.append(QUrl(url))
+        
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__downloadRequires()
+    
+    def __requireDownloaded(self):
+        """
+        Private slot to handle the finished download of a required script.
+        """
+        if self.sender() != self.__reply:
+            self.finished.emit()
+            return
+        
+        response = bytes(self.__reply.readAll()).decode()
+        
+        if self.__reply.error() == QNetworkReply.NoError and response:
+            from WebBrowser.Tools import WebBrowserTools
+            filePath = os.path.join(self.__manager.requireScriptsDirectory(),
+                                    "require.js")
+            fileName = WebBrowserTools.ensureUniqueFilename(filePath, "{0}")
+            
+            try:
+                f = open(fileName, "w", encoding="utf-8")
+            except (IOError, OSError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("GreaseMonkey Download"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be opened"""
+                        """ for writing.<br/>Reason: {1}</p>""").format(
+                        fileName, str(err)))
+                self.finished.emit()
+                return
+            f.write(response)
+            f.close()
+            
+            settings = QSettings(
+                os.path.join(self.__manager.requireScriptsDirectory(),
+                             "requires.ini"),
+                QSettings.IniFormat)
+            settings.beginGroup("Files")
+            settings.setValue(self.__reply.originalUrl().toString(), fileName)
+        
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__downloadRequires()
+    
+    def __downloadRequires(self):
+        """
+        Private slot to initiate the download of required scripts.
+        """
+        if self.__requireUrls:
+            self.__reply = FollowRedirectReply(
+                self.__requireUrls.pop(0),
+                WebBrowserWindow.networkAccessManager())
+            self.__reply.finished.connect(self.__requireDownloaded)
+        else:
+            from .GreaseMonkeyScript import GreaseMonkeyScript
+            deleteScript = True
+            script = GreaseMonkeyScript(self.__manager, self.__fileName)
+            
+            if script.isValid():
+                if not self.__manager.containsScript(script.fullName()):
+                    from .GreaseMonkeyAddScriptDialog import \
+                        GreaseMonkeyAddScriptDialog
+                    dlg = GreaseMonkeyAddScriptDialog(self.__manager, script)
+                    deleteScript = dlg.exec_() != QDialog.Accepted
+                else:
+                    E5MessageBox.information(
+                        None,
+                        self.tr("GreaseMonkey Download"),
+                        self.tr(
+                            """<p><b>{0}</b> is already installed.</p>""")
+                        .format(script.name()))
+            
+            if deleteScript:
+                try:
+                    os.remove(self.__fileName)
+                except (IOError, OSError):
+                    # ignore
+                    pass
+            
+            self.finished.emit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyJavaScript.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module containing some JavaScript resources.
+"""
+
+from __future__ import unicode_literals
+
+bootstrap_js = """
+if(typeof GM_xmlhttpRequest === "undefined") {
+    GM_xmlhttpRequest = function(/* object */ details) {
+        details.method = details.method.toUpperCase() || "GET";
+
+        if(!details.url) {
+            throw("GM_xmlhttpRequest requires an URL.");
+        }
+
+        // build XMLHttpRequest object
+        var oXhr = new XMLHttpRequest;
+        // run it
+        if(oXhr) {
+            if("onreadystatechange" in details)
+                oXhr.onreadystatechange = function() {
+                    details.onreadystatechange(oXhr)
+                };
+            if("onload" in details)
+                oXhr.onload = function() { details.onload(oXhr) };
+            if("onerror" in details)
+                oXhr.onerror = function() { details.onerror(oXhr) };
+
+            oXhr.open(details.method, details.url, true);
+
+            if("headers" in details)
+                for(var header in details.headers)
+                    oXhr.setRequestHeader(header, details.headers[header]);
+
+            if("data" in details)
+                oXhr.send(details.data);
+            else
+                oXhr.send();
+        } else
+            throw ("This Browser is not supported, please upgrade.")
+    }
+}
+
+if(typeof GM_addStyle === "undefined") {
+    function GM_addStyle(/* String */ styles) {
+        var head = document.getElementsByTagName("head")[0];
+        if (head === undefined) {
+            document.onreadystatechange = function() {
+                if (document.readyState == "interactive") {
+                  var oStyle = document.createElement("style");
+                  oStyle.setAttribute("type", "text\/css");
+                  oStyle.appendChild(document.createTextNode(styles));
+                  document.getElementsByTagName("head")[0].appendChild(oStyle);
+                }
+            }
+        }
+        else {
+            var oStyle = document.createElement("style");
+            oStyle.setAttribute("type", "text\/css");
+            oStyle.appendChild(document.createTextNode(styles));
+            head.appendChild(oStyle);
+        }
+    }
+}
+
+if(typeof GM_log === "undefined") {
+    function GM_log(log) {
+        if(console)
+            console.log(log);
+    }
+}
+
+if(typeof GM_openInTab === "undefined") {
+    function GM_openInTab(url) {
+        window.open(url)
+    }
+}
+
+// Define unsafe window
+var unsafeWindow = window;
+window.wrappedJSObject = unsafeWindow;
+
+// GM_registerMenuCommand not supported
+if(typeof GM_registerMenuCommand === "undefined") {
+    function GM_registerMenuCommand(caption, commandFunc, accessKey) { }
+}
+
+// GM Resource not supported
+if(typeof GM_getResourceText === "undefined") {
+    function GM_getResourceText(resourceName) {
+        throw ("eric6 Web Browser: GM Resource is not supported!");
+    }
+}
+
+if(typeof GM_getResourceURL === "undefined") {
+    function GM_getResourceURL(resourceName) {
+        throw ("eric6 Web Browser: GM Resource is not supported!");
+    }
+}
+
+// GM Settings not supported
+if(typeof GM_getValue === "undefined") {
+    function GM_getValue(name, defaultValue) {
+        return defaultValue;
+    }
+}
+
+if(typeof GM_setValue === "undefined") {
+    function GM_setValue(name, value) { }
+}
+
+if(typeof GM_deleteValue === "undefined") {
+    function GM_deleteValue(name) { }
+}
+
+if(typeof GM_listValues === "undefined") {
+    function GM_listValues() {
+        return new Array("");
+    }
+}
+"""
+
+
+# {0} - unique script id
+values_js = """
+function GM_deleteValue(aKey) {{
+    localStorage.removeItem("{0}" + aKey);
+}}
+
+function GM_getValue(aKey, aDefault) 
+    var val = localStorage.getItem("{0}" + aKey)
+    if (null === val && 'undefined' != typeof aDefault) return aDefault;
+    return val;
+}}
+
+function GM_listValues() {{
+    var values = [];
+    for (var i = 0; i < localStorage.length; i++) {{
+        var k = localStorage.key(i);
+        if (k.indexOf("{0}") === 0) {{
+            values.push(k.replace("{0}", ""));
+        }}
+    }}
+    return values;
+}}
+
+function GM_setValue(aKey, aVal) {{
+    localStorage.setItem("{0}" + aKey, aVal);
+}}
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyManager.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,429 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the manager for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QTimer, QFile, QDir, QSettings, \
+    QUrl, QByteArray
+from PyQt5.QtNetwork import QNetworkAccessManager
+
+import Utilities
+import Preferences
+
+
+class GreaseMonkeyManager(QObject):
+    """
+    Class implementing the manager for GreaseMonkey scripts.
+    """
+    scriptsChanged = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(GreaseMonkeyManager, self).__init__(parent)
+        
+        self.__disabledScripts = []
+        self.__endScripts = []
+        self.__startScripts = []
+        self.__downloaders = []
+        
+        QTimer.singleShot(0, self.__load)
+##    , m_interceptor(new GM_UrlInterceptor(this))
+##{
+##    mApp->networkManager()->installUrlInterceptor(m_interceptor);
+##
+##    QTimer::singleShot(0, this, SLOT(load()));
+##}
+##
+##GM_Manager::~GM_Manager()
+##{
+##    mApp->networkManager()->removeUrlInterceptor(m_interceptor);
+##}
+    
+    def showConfigurationDialog(self, parent=None):
+        """
+        Public method to show the configuration dialog.
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        from .GreaseMonkeyConfiguration.GreaseMonkeyConfigurationDialog \
+            import GreaseMonkeyConfigurationDialog
+        self.__configDiaolg = GreaseMonkeyConfigurationDialog(self, parent)
+        self.__configDiaolg.show()
+    
+    def downloadScript(self, request):
+        """
+        Public method to download a GreaseMonkey script.
+        
+        @param request reference to the request (QNetworkRequest)
+        """
+        from .GreaseMonkeyDownloader import GreaseMonkeyDownloader
+        downloader = GreaseMonkeyDownloader(request, self)
+        downloader.finished.connect(self.__downloaderFinished)
+        self.__downloaders.append(downloader)
+    
+    def __downloaderFinished(self):
+        """
+        Private slot to handle the completion of a script download.
+        """
+        downloader = self.sender()
+        if downloader is None or downloader not in self.__downloaders:
+            return
+        
+        self.__downloaders.remove(downloader)
+    
+    def scriptsDirectory(self):
+        """
+        Public method to get the path of the scripts directory.
+        
+        @return path of the scripts directory (string)
+        """
+        return os.path.join(
+            Utilities.getConfigDir(), "web_browser", "greasemonkey")
+    
+    def requireScriptsDirectory(self):
+        """
+        Public method to get the path of the scripts directory.
+        
+        @return path of the scripts directory (string)
+        """
+        return os.path.join(self.scriptsDirectory(), "requires")
+    
+    def requireScripts(self, urlList):
+        """
+        Public method to get the sources of all required scripts.
+        
+        @param urlList list of URLs (list of string)
+        @return sources of all required scripts (string)
+        """
+        requiresDir = QDir(self.requireScriptsDirectory())
+        if not requiresDir.exists() or len(urlList) == 0:
+            return ""
+        
+        script = ""
+        
+        settings = QSettings(
+            os.path.join(self.requireScriptsDirectory(), "requires.ini"),
+            QSettings.IniFormat)
+        settings.beginGroup("Files")
+        for url in urlList:
+            if settings.contains(url):
+                fileName = settings.value(url)
+                try:
+                    f = open(fileName, "r", encoding="utf-8")
+                    source = f.read()
+                    f.close()
+                except (IOError, OSError):
+                    source = ""
+                script += source.strip() + "\n"
+        
+        return script
+    
+    def saveConfiguration(self):
+        """
+        Public method to save the configuration.
+        """
+        Preferences.setWebBrowser("GreaseMonkeyDisabledScripts",
+                                  self.__disabledScripts)
+    
+    def allScripts(self):
+        """
+        Public method to get a list of all scripts.
+        
+        @return list of all scripts (list of GreaseMonkeyScript)
+        """
+        return self.__startScripts[:] + self.__endScripts[:]
+    
+    def containsScript(self, fullName):
+        """
+        Public method to check, if the given script exists.
+        
+        @param fullName full name of the script (string)
+        @return flag indicating the existence (boolean)
+        """
+        for script in self.__startScripts:
+            if script.fullName() == fullName:
+                return True
+        for script in self.__endScripts:
+            if script.fullName() == fullName:
+                return True
+        return False
+    
+    def enableScript(self, script):
+        """
+        Public method to enable the given script.
+        
+        @param script script to be enabled (GreaseMonkeyScript)
+        """
+        script.setEnabled(True)
+        fullName = script.fullName()
+        if fullName in self.__disabledScripts:
+            self.__disabledScripts.remove(fullName)
+##void GM_Manager::enableScript(GM_Script* script)
+##{
+##    script->setEnabled(true);
+##    m_disabledScripts.removeOne(script->fullName());
+##
+##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
+##    collection->insert(script->webScript());
+##}
+    
+    def disableScript(self, script):
+        """
+        Public method to disable the given script.
+        
+        @param script script to be disabled (GreaseMonkeyScript)
+        """
+        script.setEnabled(False)
+        fullName = script.fullName()
+        if fullName not in self.__disabledScripts:
+            self.__disabledScripts.append(fullName)
+##void GM_Manager::disableScript(GM_Script* script)
+##{
+##    script->setEnabled(false);
+##    m_disabledScripts.append(script->fullName());
+##
+##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
+##    collection->remove(collection->findScript(script->fullName()));
+    
+    def addScript(self, script):
+        """
+        Public method to add a script.
+        
+        @param script script to be added (GreaseMonkeyScript)
+        @return flag indicating success (boolean)
+        """
+        if not script:
+            return False
+        
+        from .GreaseMonkeyScript import GreaseMonkeyScript
+        if script.startAt() == GreaseMonkeyScript.DocumentStart:
+            self.__startScripts.append(script)
+        else:
+            self.__endScripts.append(script)
+        
+        self.scriptsChanged.emit()
+        return True
+##bool GM_Manager::addScript(GM_Script* script)
+##{
+##    if (!script || !script->isValid()) {
+##        return false;
+##    }
+##
+##    m_scripts.append(script);
+##    connect(script, &GM_Script::scriptChanged, this, &GM_Manager::scriptChanged);
+##
+##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
+##    collection->insert(script->webScript());
+##
+##    emit scriptsChanged();
+##    return true;
+##}
+    
+    def removeScript(self, script):
+        """
+        Public method to remove a script.
+        
+        @param script script to be removed (GreaseMonkeyScript)
+        @return flag indicating success (boolean)
+        """
+        if not script:
+            return False
+        
+        from .GreaseMonkeyScript import GreaseMonkeyScript
+        if script.startAt() == GreaseMonkeyScript.DocumentStart:
+            try:
+                self.__startScripts.remove(script)
+            except ValueError:
+                pass
+        else:
+            try:
+                self.__endScripts.remove(script)
+            except ValueError:
+                pass
+        
+        fullName = script.fullName()
+        if fullName in self.__disabledScripts:
+            self.__disabledScripts.remove(fullName)
+        QFile.remove(script.fileName())
+        
+        self.scriptsChanged.emit()
+        return True
+##bool GM_Manager::removeScript(GM_Script* script, bool removeFile)
+##{
+##    if (!script) {
+##        return false;
+##    }
+##
+##    m_scripts.removeOne(script);
+##
+##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
+##    collection->remove(collection->findScript(script->fullName()));
+##
+##    m_disabledScripts.removeOne(script->fullName());
+##
+##    if (removeFile) {
+##        QFile::remove(script->fileName());
+##        delete script;
+##    }
+##
+##    emit scriptsChanged();
+##    return true;
+##}
+    
+    def canRunOnScheme(self, scheme):
+        """
+        Public method to check, if scripts can be run on a scheme.
+        
+        @param scheme scheme to check (string)
+        @return flag indicating, that scripts can be run (boolean)
+        """
+        return scheme in ["http", "https", "data", "ftp"]
+    
+##    def pageLoadStarted(self):
+##        """
+##        Public slot to handle the start of loading a page.
+##        """
+##        frame = self.sender()
+##        if not frame:
+##            return
+##        
+##        urlScheme = frame.url().scheme()
+##        urlString = bytes(frame.url().toEncoded()).decode()
+##        
+##        if not self.canRunOnScheme(urlScheme):
+##            return
+##        
+##        from .GreaseMonkeyJavaScript import bootstrap_js
+##        for script in self.__startScripts:
+##            if script.match(urlString):
+##                frame.evaluateJavaScript(bootstrap_js + script.script())
+##        
+##        for script in self.__endScripts:
+##            if script.match(urlString):
+##                javascript = 'window.addEventListener("DOMContentLoaded",' \
+##                    'function(e) {{ {0} }}, false);'.format(
+##                        bootstrap_js + script.script())
+##                frame.evaluateJavaScript(javascript)
+    
+    def __load(self):
+        """
+        Private slot to load the available scripts into the manager.
+        """
+        scriptsDir = QDir(self.scriptsDirectory())
+        if not scriptsDir.exists():
+            scriptsDir.mkpath(self.scriptsDirectory())
+        
+        if not scriptsDir.exists("requires"):
+            scriptsDir.mkdir("requires")
+        
+        self.__disabledScripts = \
+            Preferences.getHelp("GreaseMonkeyDisabledScripts")
+        
+        from .GreaseMonkeyScript import GreaseMonkeyScript
+        for fileName in scriptsDir.entryList(["*.js"], QDir.Files):
+            absolutePath = scriptsDir.absoluteFilePath(fileName)
+            script = GreaseMonkeyScript(self, absolutePath)
+            
+            if script.fullName() in self.__disabledScripts:
+                script.setEnabled(False)
+            
+            if script.startAt() == GreaseMonkeyScript.DocumentStart:
+                self.__startScripts.append(script)
+            else:
+                self.__endScripts.append(script)
+##void GM_Manager::load()
+##{
+##    QDir gmDir(m_settingsPath + QL1S("/greasemonkey"));
+##    if (!gmDir.exists()) {
+##        gmDir.mkdir(m_settingsPath + QL1S("/greasemonkey"));
+##    }
+##
+##    if (!gmDir.exists("requires")) {
+##        gmDir.mkdir("requires");
+##    }
+##
+##    m_bootstrapScript = QzTools::readAllFileContents(":gm/data/bootstrap.min.js");
+##    m_valuesScript = QzTools::readAllFileContents(":gm/data/values.min.js");
+##
+##    QSettings settings(m_settingsPath + QL1S("/extensions.ini"), QSettings::IniFormat);
+##    settings.beginGroup("GreaseMonkey");
+##    m_disabledScripts = settings.value("disabledScripts", QStringList()).toStringList();
+##
+##    foreach (const QString &fileName, gmDir.entryList(QStringList("*.js"), QDir::Files)) {
+##        const QString absolutePath = gmDir.absoluteFilePath(fileName);
+##        GM_Script* script = new GM_Script(this, absolutePath);
+##
+##        if (!script->isValid()) {
+##            delete script;
+##            continue;
+##        }
+##
+##        m_scripts.append(script);
+##
+##        if (m_disabledScripts.contains(script->fullName())) {
+##            script->setEnabled(false);
+##        }
+##        else {
+##            mApp->webProfile()->scripts()->insert(script->webScript());
+##        }
+##    }
+##}
+    
+    def __scriptChanged(self):
+        """
+        Private slot handling a changed script.
+        """
+##void GM_Manager::scriptChanged()
+##{
+##    GM_Script *script = qobject_cast<GM_Script*>(sender());
+##    if (!script)
+##        return;
+##
+##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
+##    collection->remove(collection->findScript(script->fullName()));
+##    collection->insert(script->webScript());
+##}
+    
+##    def connectPage(self, page):
+##        """
+##        Public method to allow the GreaseMonkey manager to connect to the page.
+##        
+##        @param page reference to the web page (HelpWebPage)
+##        """
+##        page.mainFrame().javaScriptWindowObjectCleared.connect(
+##            self.pageLoadStarted)
+##    
+##    def createRequest(self, op, request, outgoingData=None):
+##        """
+##        Public method to create a request.
+##        
+##        @param op the operation to be performed
+##            (QNetworkAccessManager.Operation)
+##        @param request reference to the request object (QNetworkRequest)
+##        @param outgoingData reference to an IODevice containing data to be sent
+##            (QIODevice)
+##        @return reference to the created reply object (QNetworkReply)
+##        """
+##        if op == QNetworkAccessManager.GetOperation and \
+##           request.rawHeader(b"X-Eric6-UserLoadAction") == QByteArray(b"1"):
+##            urlString = request.url().toString(
+##                QUrl.RemoveFragment | QUrl.RemoveQuery)
+##            if urlString.endswith(".user.js"):
+##                self.downloadScript(request)
+##                from Helpviewer.Network.EmptyNetworkReply import \
+##                    EmptyNetworkReply
+##                return EmptyNetworkReply(self)
+##        
+##        return None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,305 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the GreaseMonkey script.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QUrl, QRegExp
+
+from .GreaseMonkeyUrlMatcher import GreaseMonkeyUrlMatcher
+
+
+class GreaseMonkeyScript(object):
+    """
+    Class implementing the GreaseMonkey script.
+    """
+    DocumentStart = 0
+    DocumentEnd = 1
+    
+    def __init__(self, manager, path):
+        """
+        Constructor
+        
+        @param manager reference to the manager object (GreaseMonkeyManager)
+        @param path path of the Javascript file (string)
+        """
+        self.__manager = manager
+        
+        self.__name = ""
+        self.__namespace = "GreaseMonkeyNS"
+        self.__description = ""
+        self.__version = ""
+        
+        self.__include = []
+        self.__exclude = []
+        
+        self.__downloadUrl = QUrl()
+        self.__startAt = GreaseMonkeyScript.DocumentEnd
+        
+        self.__script = ""
+        self.__fileName = path
+        self.__enabled = True
+        self.__valid = False
+        self.__metaData = ""
+        
+        self.__parseScript(path)
+##    , m_fileWatcher(new DelayedFileWatcher(this))
+##    connect(m_fileWatcher, SIGNAL(delayedFileChanged(QString)), this, SLOT(watchedFileChanged(QString)));
+    
+    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 downloadUrl(self):
+        """
+        Public method to get the download URL of the script.
+        
+        @return download URL of the script (QUrl)
+        """
+        return QUrl(self.__downloadUrl)
+    
+    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 isEnabled(self):
+        """
+        Public method to check, if the script is enabled.
+        
+        @return flag indicating an enabled state (boolean)
+        """
+        return self.__enabled
+    
+    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)
+        """
+        list = []
+        for matcher in self.__include:
+            list.append(matcher.pattern())
+        return list
+    
+    def exclude(self):
+        """
+        Public method to get the list of excluded URLs.
+        
+        @return list of excluded URLs (list of strings)
+        """
+        list = []
+        for matcher in self.__exclude:
+            list.append(matcher.pattern())
+        return list
+    
+    def script(self):
+        """
+        Public method to get the Javascript source.
+        
+        @return Javascript source (string)
+        """
+        return self.__script
+    
+##QString GM_Script::metaData() const
+##{
+##    return m_metadata;
+##}
+    
+    def fileName(self):
+        """
+        Public method to get the path of the Javascript file.
+        
+        @return path path of the Javascript file (string)
+        """
+        return self.__fileName
+    
+    def match(self, urlString):
+        """
+        Public method to check, if the script matches the given URL.
+        
+        @param urlString URL (string)
+        @return flag indicating a match (boolean)
+        """
+        if not self.__enabled:
+            return False
+        
+        for matcher in self.__exclude:
+            if matcher.match(urlString):
+                return False
+        
+        for matcher in self.__include:
+            if matcher.match(urlString):
+                return True
+        
+        return False
+    
+    def __parseScript(self, path):
+        """
+        Private method to parse the given script and populate the data
+        structure.
+        
+        @param path path of the Javascript file (string)
+        """
+        try:
+            f = open(path, "r", encoding="utf-8")
+            fileData = f.read()
+            f.close()
+        except (IOError, OSError):
+            # silently ignore because it shouldn't happen
+            return
+        
+        rx = QRegExp("// ==UserScript==(.*)// ==/UserScript==")
+        rx.indexIn(fileData)
+        metaDataBlock = rx.cap(1).strip()
+        
+        if metaDataBlock == "":
+            # invalid script file
+            return
+        
+        requireList = []
+        for line in metaDataBlock.splitlines():
+            if not line.startswith("// @"):
+                continue
+            
+            line = line[3:].replace("\t", " ")
+            index = line.find(" ")
+            if index < 0:
+                continue
+            
+            key = line[:index].strip()
+            value = line[index + 1:].strip()
+            
+            # Ignored values: @resource, @unwrap
+            
+            if not key or not value:
+                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 == "@updateURL":
+                self.__downloadUrl = QUrl(value)
+            
+            elif key in ["@include", "@match"]:
+                self.__include.append(GreaseMonkeyUrlMatcher(value))
+            
+            elif key in ["@exclude", "@exclude_match"]:
+                self.__exclude.append(GreaseMonkeyUrlMatcher(value))
+            
+            elif key == "@require":
+                requireList.append(value)
+            
+            elif key == "@run-at":
+                if value == "document-end":
+                    self.__startAt = GreaseMonkeyScript.DocumentEnd
+                elif value == "document-start":
+                    self.__startAt = GreaseMonkeyScript.DocumentStart
+            
+            elif key == "@downloadURL" and self.__downloadUrl.isEmpty():
+                self.__downloadUrl = QUrl(value)
+        
+        if not self.__include:
+            self.__include.append(GreaseMonkeyUrlMatcher("*"))
+        
+        marker = "// ==/UserScript=="
+        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;
+    
+    def webScript(self):
+        """
+        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;
+##}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyUrlInterceptor.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+from __future__ import unicode_literals
+
+##class GM_UrlInterceptor : public UrlInterceptor
+##{
+##public:
+##    explicit GM_UrlInterceptor(GM_Manager* manager);
+##
+##    void interceptRequest(QWebEngineUrlRequestInfo &info);
+##
+##private:
+##    GM_Manager *m_manager;
+##
+##};
+
+
+##GM_UrlInterceptor::GM_UrlInterceptor(GM_Manager *manager)
+##    : UrlInterceptor(manager)
+##    , m_manager(manager)
+##{
+##}
+##
+##void GM_UrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info)
+##{
+##    if (info.requestUrl().toString().endsWith(QLatin1String(".user.js"))) {
+##        m_manager->downloadScript(info.requestUrl());
+##        info.block(true);
+##    }
+##}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyUrlMatcher.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the GreaseMonkey URL matcher.
+"""
+
+from __future__ import unicode_literals
+
+import re
+
+from PyQt5.QtCore import Qt, QRegExp
+
+
+def wildcardMatch(string, pattern):
+    """
+    Module function implementing a special wildcard matcher.
+    
+    @param string string to match (string)
+    @param pattern pattern to be used (string)
+    @return flag indicating a successful match (boolean)
+    """
+    stringSize = len(string)
+    
+    startsWithWildcard = pattern.startswith("*")
+    endsWithWildcard = pattern.endswith("*")
+    
+    parts = pattern.split("*")
+    pos = 0
+    
+    if startsWithWildcard:
+        pos = string.find(parts[1])
+        if pos == -1:
+            return False
+    
+    for part in parts:
+        pos = string.find(part, pos)
+        if pos == -1:
+            return False
+    
+    if not endsWithWildcard and stringSize - pos != len(parts[-1]):
+        return False
+    
+    return True
+
+
+class GreaseMonkeyUrlMatcher(object):
+    """
+    Class implementing the GreaseMonkey URL matcher.
+    """
+    def __init__(self, pattern):
+        """
+        Constructor
+        
+        @param pattern pattern to be used for the matching (string)
+        """
+        self.__pattern = pattern
+        self.__matchString = ""
+        self.__regExp = QRegExp()
+        self.__useRegExp = False
+        
+        self.__parsePattern(self.__pattern)
+    
+    def pattern(self):
+        """
+        Public method to get the match pattern.
+        
+        @return match pattern (string)
+        """
+        return self.__pattern
+    
+    def match(self, urlString):
+        """
+        Public method to match the given URL.
+        
+        @param urlString URL to match (string)
+        @return flag indicating a successful match (boolean)
+        """
+        if self.__useRegExp:
+            return self.__regExp.indexIn(urlString) != -1
+        else:
+            return wildcardMatch(urlString, self.__matchString)
+    
+    def __parsePattern(self, pattern):
+        """
+        Private method to parse the match pattern.
+        
+        @param pattern match pattern to be used (string)
+        """
+        if pattern.startswith("/") and pattern.endswith("/"):
+            pattern = pattern[1:-1]
+            
+            self.__regExp = QRegExp(pattern, Qt.CaseInsensitive)
+            self.__useRegExp = True
+        elif ".tld" in pattern:
+            # escape special symbols
+            pattern = re.sub(r"(\W)", r"\\\1", pattern)
+            # remove multiple wildcards
+            pattern = re.sub(r"\*+", "*", pattern)
+            # process anchor at expression start
+            pattern = re.sub(r"^\\\|", "^", pattern)
+            # process anchor at expression end
+            pattern = re.sub(r"\\\|$", "$", pattern)
+            # replace wildcards by .*
+            pattern = re.sub(r"\\\*", ".*", pattern)
+            # replace domain pattern
+            pattern = re.sub(r"\.tld", r"\.[a-z.]{2,6}")
+            
+            self.__useRegExp = True
+            self.__regExp = QRegExp(pattern, Qt.CaseInsensitive)
+        else:
+            self.__matchString = pattern
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/__init__.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the GreaseMonkey support.
+"""
+
+#
+# The code in this package was inspired and ported in parts from QupZilla
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/DelayedFileWatcher.py	Sun Feb 21 19:54:14 2016 +0100
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+from __future__ import unicode_literals
+
+##class DelayedFileWatcher : public QFileSystemWatcher
+##{
+##    Q_OBJECT
+##
+##public:
+##    explicit DelayedFileWatcher(QObject* parent = 0);
+##    explicit DelayedFileWatcher(const QStringList &paths, QObject* parent = 0);
+##
+##signals:
+##    void delayedDirectoryChanged(const QString &path);
+##    void delayedFileChanged(const QString &path);
+##
+##private slots:
+##    void slotDirectoryChanged(const QString &path);
+##    void slotFileChanged(const QString &path);
+##
+##    void dequeueDirectory();
+##    void dequeueFile();
+##
+##private:
+##    void init();
+##
+##    QQueue<QString> m_dirQueue;
+##    QQueue<QString> m_fileQueue;
+##};
+##
+##
+##DelayedFileWatcher::DelayedFileWatcher(QObject* parent)
+##    : QFileSystemWatcher(parent)
+##{
+##    init();
+##}
+##
+##DelayedFileWatcher::DelayedFileWatcher(const QStringList &paths, QObject* parent)
+##    : QFileSystemWatcher(paths, parent)
+##{
+##    init();
+##}
+##
+##void DelayedFileWatcher::init()
+##{
+##    connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(slotDirectoryChanged(QString)));
+##    connect(this, SIGNAL(fileChanged(QString)), this, SLOT(slotFileChanged(QString)));
+##}
+##
+##void DelayedFileWatcher::slotDirectoryChanged(const QString &path)
+##{
+##    m_dirQueue.enqueue(path);
+##    QTimer::singleShot(500, this, SLOT(dequeueDirectory()));
+##}
+##
+##void DelayedFileWatcher::slotFileChanged(const QString &path)
+##{
+##    m_fileQueue.enqueue(path);
+##    QTimer::singleShot(500, this, SLOT(dequeueFile()));
+##}
+##
+##void DelayedFileWatcher::dequeueDirectory()
+##{
+##    emit delayedDirectoryChanged(m_dirQueue.dequeue());
+##}
+##
+##void DelayedFileWatcher::dequeueFile()
+##{
+##    emit delayedFileChanged(m_fileQueue.dequeue());
+##}
--- a/WebBrowser/Tools/WebBrowserTools.py	Sun Feb 21 18:54:48 2016 +0100
+++ b/WebBrowser/Tools/WebBrowserTools.py	Sun Feb 21 19:54:14 2016 +0100
@@ -13,7 +13,9 @@
 except NameError:
     pass
 
-from PyQt5.QtCore import QFile, QByteArray
+import os
+
+from PyQt5.QtCore import QFile, QByteArray, QUrl
 
 
 def readAllFileContents(filename):
@@ -60,3 +62,73 @@
             return True
     
     return False
+
+
+def ensureUniqueFilename(name, appendFormat="({0})"):
+    """
+    Module function to generate an unique file name based on a pattern.
+    
+    @param name desired file name (string)
+    @param appendFormat format pattern to be used to make the unique name
+        (string)
+    @return unique file name
+    """
+    if not os.path.exists(name):
+        return name
+    
+    tmpFileName = name
+    i = 1
+    while os.path.exists(tmpFileName):
+        tmpFileName = name
+        index = tmpFileName.rfind(".")
+        
+        appendString = appendFormat.format(i)
+        if index == -1:
+            tmpFileName += appendString
+        else:
+            tmpFileName = tmpFileName[:index] + appendString + \
+                tmpFileName[index:]
+        i += 1
+    
+    return tmpFileName
+
+
+def getFileNameFromUrl(url):
+    """
+    Module function to generate a file name based on the given URL.
+    
+    @param url URL (QUrl)
+    @return file name (string)
+    """
+    fileName = url.toString(QUrl.RemoveFragment | QUrl.RemoveQuery |
+                            QUrl.RemoveScheme | QUrl.RemovePort)
+    if fileName.find("/") != -1:
+        pos = fileName.rfind("/")
+        fileName = fileName[pos:]
+        fileName = fileName.replace("/", "")
+    
+    fileName = filterCharsFromFilename(fileName)
+    
+    if not fileName:
+        fileName = filterCharsFromFilename(url.host().replace(".", "_"))
+    
+    return fileName
+
+
+def filterCharsFromFilename(name):
+    """
+    Module function to filter illegal characters.
+    
+    @param name name to be sanitized (string)
+    @return sanitized name (string)
+    """
+    return name\
+        .replace("/", "_")\
+        .replace("\\", "")\
+        .replace(":", "")\
+        .replace("*", "")\
+        .replace("?", "")\
+        .replace('"', "")\
+        .replace("<", "")\
+        .replace(">", "")\
+        .replace("|", "")
--- a/eric6.e4p	Sun Feb 21 18:54:48 2016 +0100
+++ b/eric6.e4p	Sun Feb 21 19:54:14 2016 +0100
@@ -1304,6 +1304,19 @@
     <Source>WebBrowser/FlashCookieManager/FlashCookieReader.py</Source>
     <Source>WebBrowser/FlashCookieManager/FlashCookieUtilities.py</Source>
     <Source>WebBrowser/FlashCookieManager/__init__.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListDelegate.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListWidget.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/__init__.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyDownloader.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyJavaScript.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyManager.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyScript.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyUrlInterceptor.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyUrlMatcher.py</Source>
+    <Source>WebBrowser/GreaseMonkey/__init__.py</Source>
     <Source>WebBrowser/History/HistoryCompleter.py</Source>
     <Source>WebBrowser/History/HistoryDialog.py</Source>
     <Source>WebBrowser/History/HistoryFilterModel.py</Source>
@@ -1340,6 +1353,7 @@
     <Source>WebBrowser/PersonalInformationManager/PersonalInformationManager.py</Source>
     <Source>WebBrowser/PersonalInformationManager/__init__.py</Source>
     <Source>WebBrowser/SearchWidget.py</Source>
+    <Source>WebBrowser/Tools/DelayedFileWatcher.py</Source>
     <Source>WebBrowser/Tools/Scripts.py</Source>
     <Source>WebBrowser/Tools/WebBrowserTools.py</Source>
     <Source>WebBrowser/Tools/WebHitTestResult.py</Source>
@@ -1777,6 +1791,9 @@
     <Form>WebBrowser/Feeds/FeedsDialog.ui</Form>
     <Form>WebBrowser/Feeds/FeedsManager.ui</Form>
     <Form>WebBrowser/FlashCookieManager/FlashCookieManagerDialog.ui</Form>
+    <Form>WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.ui</Form>
+    <Form>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.ui</Form>
+    <Form>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.ui</Form>
     <Form>WebBrowser/History/HistoryDialog.ui</Form>
     <Form>WebBrowser/OpenSearch/OpenSearchDialog.ui</Form>
     <Form>WebBrowser/OpenSearch/OpenSearchEditDialog.ui</Form>

eric ide

mercurial