Merged the QtWebEngine branch into the default development branch to finalize the port in here.

Sun, 03 Apr 2016 16:33:37 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 03 Apr 2016 16:33:37 +0200
changeset 4915
8081031061a2
parent 4911
652cb0520e9a (current diff)
parent 4914
d095b591e6fa (diff)
child 4917
682750cc7bd5

Merged the QtWebEngine branch into the default development branch to finalize the port in here.

install.py file | annotate | diff | comparison | revisions
--- a/.hgignore	Sun Apr 03 12:29:37 2016 +0200
+++ b/.hgignore	Sun Apr 03 16:33:37 2016 +0200
@@ -16,3 +16,4 @@
 glob:__pycache__
 glob:**.DS_Store
 glob:**.coverage
+glob:GPUCache
--- a/DebugClients/Python/DebugClientThreads.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/DebugClients/Python/DebugClientThreads.py	Sun Apr 03 16:33:37 2016 +0200
@@ -200,4 +200,4 @@
 
 #
 # eflag: FileType = Python2
-# eflag: noqa = M601, M702
+# eflag: noqa = M601, M702, E402
--- a/DebugClients/Python3/DebugClientThreads.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/DebugClients/Python3/DebugClientThreads.py	Sun Apr 03 16:33:37 2016 +0200
@@ -199,4 +199,4 @@
     debugClient.main()
 
 #
-# eflag: noqa = M702
+# eflag: noqa = M702, E402
Binary file Documentation/Help/source.qch has changed
--- a/E5Gui/E5ErrorMessage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/E5Gui/E5ErrorMessage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -44,7 +44,9 @@
             "QFont::",
             "QCocoaMenu::removeMenuItem",
             "QCocoaMenu::insertNative",
-            ",type id:"
+            ",type id:",
+            "Remote debugging server started successfully",
+            "Uncaught SecurityError:",
         ]
     
     def __filterMessage(self, message):
--- a/Helpviewer/HelpBrowserWV.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Helpviewer/HelpBrowserWV.py	Sun Apr 03 16:33:37 2016 +0200
@@ -10,7 +10,7 @@
 
 from __future__ import unicode_literals
 try:
-    str = unicode
+    str = unicode       # __IGNORE_EXCEPTION__
 except NameError:
     pass
 
@@ -1617,8 +1617,8 @@
         
         searchUrl = QUrl(self.page().mainFrame().baseUrl().resolved(
             QUrl(formElement.attribute("action"))))
-        if searchUrl.scheme() != "http":
-            return
+##        if searchUrl.scheme() != "http":
+##            return
         
         if qVersion() >= "5.0.0":
             from PyQt5.QtCore import QUrlQuery
--- a/Helpviewer/HelpWindow.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Helpviewer/HelpWindow.py	Sun Apr 03 16:33:37 2016 +0200
@@ -71,8 +71,6 @@
     
     helpwindows = []
 
-    maxMenuFilePathLen = 75
-    
     _fromEric = False
     useQtHelp = QTHELP_AVAILABLE
     
--- a/PluginManager/PluginManager.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/PluginManager/PluginManager.py	Sun Apr 03 16:33:37 2016 +0200
@@ -144,7 +144,11 @@
             self.__networkManager.sslErrors.connect(self.__sslErrors)
         self.__replies = []
         
-        self.__ui.onlineStateChanged.connect(self.__onlineStateChanged)
+        try:
+            self.__ui.onlineStateChanged.connect(self.__onlineStateChanged)
+        except AttributeError:
+            # it was not called from eric
+            pass
     
     def finalizeSetup(self):
         """
--- a/Preferences/ConfigurationDialog.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -79,9 +79,10 @@
     HelpBrowserMode = 1
     TrayStarterMode = 2
     HexEditorMode = 3
+    WebBrowserMode = 4
     
     def __init__(self, parent=None, fromEric=True, displayMode=DefaultMode,
-                 expandedEntries=[]):
+                 expandedEntries=[], webEngine=False):
         """
         Constructor
         
@@ -89,21 +90,25 @@
         @keyparam fromEric flag indicating a dialog generation from within the
             eric6 ide (boolean)
         @keyparam displayMode mode of the configuration dialog
-            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode)
+            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode,
+             WebBrowserMode)
         @exception RuntimeError raised to indicate an invalid dialog mode
         @keyparam expandedEntries list of entries to be shown expanded
             (list of strings)
+        @keyparam webEngine flag indicating QtWebEngine is used (bool)
         """
         assert displayMode in (
             ConfigurationWidget.DefaultMode,
             ConfigurationWidget.HelpBrowserMode,
             ConfigurationWidget.TrayStarterMode,
             ConfigurationWidget.HexEditorMode,
+            ConfigurationWidget.WebBrowserMode,
         )
         
         super(ConfigurationWidget, self).__init__(parent)
         self.fromEric = fromEric
         self.displayMode = displayMode
+        self.__webEngine = webEngine
         
         self.__setupUi()
         
@@ -315,25 +320,49 @@
                 [self.tr("Viewmanager"), "preferences-viewmanager.png",
                  "ViewmanagerPage", "0interfacePage", None],
             }
-            try:
-                from PyQt5 import QtWebKit      # __IGNORE_WARNING__
+            if webEngine:
                 self.configItems.update({
-                    "helpAppearancePage":
+                    "0webBrowserPage":
+                    [self.tr("Web Browser"), "ericWeb.png",
+                     None, None, None],
+                    "webBrowserAppearancePage":
                     [self.tr("Appearance"), "preferences-styles.png",
-                     "HelpAppearancePage", "0helpPage", None],
+                     "WebBrowserAppearancePage", "0webBrowserPage", None],
+                    "webBrowserPage":
+                    [self.tr("eric6 Web Browser"), "ericWeb.png",
+                     "WebBrowserPage", "0webBrowserPage", None],
                     "helpFlashCookieManagerPage":
                     [self.tr("Flash Cookie Manager"),
                      "flashCookie16.png",
-                     "HelpFlashCookieManagerPage", "0helpPage", None],
+                     "HelpFlashCookieManagerPage", "0webBrowserPage", None],
                     "helpVirusTotalPage":
                     [self.tr("VirusTotal Interface"), "virustotal.png",
-                     "HelpVirusTotalPage", "0helpPage", None],
-                    "helpWebBrowserPage":
-                    [self.tr("eric6 Web Browser"), "ericWeb.png",
-                     "HelpWebBrowserPage", "0helpPage", None],
+                     "HelpVirusTotalPage", "0webBrowserPage", None],
                 })
-            except ImportError:
-                pass
+            else:
+                try:
+                    from PyQt5 import QtWebKit      # __IGNORE_WARNING__
+                    self.configItems.update({
+                        "0helpBrowserPage":
+                        [self.tr("Web Browser"), "ericWeb.png",
+                         None, None, None],
+                        "helpAppearancePage":
+                        [self.tr("Appearance"), "preferences-styles.png",
+                         "HelpAppearancePage", "0helpBrowserPage", None],
+                        "helpWebBrowserPage":
+                        [self.tr("eric6 Web Browser"), "ericWeb.png",
+                         "HelpWebBrowserPage", "0helpBrowserPage", None],
+                        "helpFlashCookieManagerPage":
+                        [self.tr("Flash Cookie Manager"),
+                         "flashCookie16.png",
+                         "HelpFlashCookieManagerPage", "0helpBrowserPage",
+                         None],
+                        "helpVirusTotalPage":
+                        [self.tr("VirusTotal Interface"), "virustotal.png",
+                         "HelpVirusTotalPage", "0helpBrowserPage", None],
+                    })
+                except ImportError:
+                    pass
             
             self.configItems.update(
                 e5App().getObject("PluginManager").getPluginConfigData())
@@ -359,34 +388,73 @@
                 [self.tr("Security"), "preferences-security.png",
                  "SecurityPage", None, None],
                 
-                "0helpPage":
-                [self.tr("Help"), "preferences-help.png",
-                 None, None, None],
                 "helpDocumentationPage":
                 [self.tr("Help Documentation"),
                  "preferences-helpdocumentation.png",
-                 "HelpDocumentationPage", "0helpPage", None],
+                 "HelpDocumentationPage", None, None],
             }
             try:
                 from PyQt5 import QtWebKit      # __IGNORE_WARNING__
                 self.configItems.update({
                     "helpAppearancePage":
                     [self.tr("Appearance"), "preferences-styles.png",
-                     "HelpAppearancePage", "0helpPage", None],
+                     "HelpAppearancePage", None, None],
                     "helpFlashCookieManagerPage":
                     [self.tr("Flash Cookie Manager"),
                      "flashCookie16.png",
-                     "HelpFlashCookieManagerPage", "0helpPage", None],
+                     "HelpFlashCookieManagerPage", None, None],
                     "helpVirusTotalPage":
                     [self.tr("VirusTotal Interface"), "virustotal.png",
-                     "HelpVirusTotalPage", "0helpPage", None],
+                     "HelpVirusTotalPage", None, None],
                     "helpWebBrowserPage":
                     [self.tr("eric6 Web Browser"), "ericWeb.png",
-                     "HelpWebBrowserPage", "0helpPage", None],
+                     "HelpWebBrowserPage", None, None],
                 })
             except ImportError:
                 pass
         
+        elif displayMode == ConfigurationWidget.WebBrowserMode:
+            self.configItems = {
+                # key : [display string, pixmap name, dialog module name or
+                #        page creation function, parent key,
+                #        reference to configuration page (must always be last)]
+                # The dialog module must have the module function 'create' to
+                # create the configuration page. This must have the method
+                # 'save' to save the settings.
+                "interfacePage":
+                [self.tr("Interface"), "preferences-interface.png",
+                 "HelpInterfacePage", None, None],
+                "networkPage":
+                [self.tr("Network"), "preferences-network.png",
+                 "NetworkPage", None, None],
+                "printerPage":
+                [self.tr("Printer"), "preferences-printer.png",
+                 "PrinterPage", None, None],
+                "securityPage":
+                [self.tr("Security"), "preferences-security.png",
+                 "SecurityPage", None, None],
+                
+                "helpDocumentationPage":
+                [self.tr("Help Documentation"),
+                 "preferences-helpdocumentation.png",
+                 "HelpDocumentationPage", None, None],
+                
+                "webBrowserAppearancePage":
+                [self.tr("Appearance"), "preferences-styles.png",
+                 "WebBrowserAppearancePage", None, None],
+                "webBrowserPage":
+                [self.tr("eric6 Web Browser"), "ericWeb.png",
+                 "WebBrowserPage", None, None],
+                
+                "helpFlashCookieManagerPage":
+                [self.tr("Flash Cookie Manager"),
+                 "flashCookie16.png",
+                 "HelpFlashCookieManagerPage", None, None],
+                "helpVirusTotalPage":
+                [self.tr("VirusTotal Interface"), "virustotal.png",
+                 "HelpVirusTotalPage", None, None],
+            }
+        
         elif displayMode == ConfigurationWidget.TrayStarterMode:
             self.configItems = {
                 # key : [display string, pixmap name, dialog module name or
@@ -446,7 +514,8 @@
         
         if displayMode in [ConfigurationWidget.HelpBrowserMode,
                            ConfigurationWidget.TrayStarterMode,
-                           ConfigurationWidget.HexEditorMode]:
+                           ConfigurationWidget.HexEditorMode,
+                           ConfigurationWidget.WebBrowserMode]:
             self.configListSearch.hide()
         
         if displayMode not in [ConfigurationWidget.TrayStarterMode,
@@ -837,6 +906,13 @@
         pageName = item.data(0, Qt.UserRole)
         if pageName not in self.__expandedEntries:
             self.__expandedEntries.append(pageName)
+    
+    def isUsingWebEngine(self):
+        """
+        Public method to get an indication, if QtWebEngine is being used.
+        """
+        return self.__webEngine or \
+            self.displayMode == ConfigurationWidget.WebBrowserMode
 
 
 class ConfigurationDialog(QDialog):
@@ -854,10 +930,11 @@
     HelpBrowserMode = ConfigurationWidget.HelpBrowserMode
     TrayStarterMode = ConfigurationWidget.TrayStarterMode
     HexEditorMode = ConfigurationWidget.HexEditorMode
+    WebBrowserMode = ConfigurationWidget.WebBrowserMode
     
     def __init__(self, parent=None, name=None, modal=False,
                  fromEric=True, displayMode=ConfigurationWidget.DefaultMode,
-                 expandedEntries=[]):
+                 expandedEntries=[], webEngine=False):
         """
         Constructor
         
@@ -867,9 +944,11 @@
         @keyparam fromEric flag indicating a dialog generation from within the
             eric6 ide (boolean)
         @keyparam displayMode mode of the configuration dialog
-            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode)
+            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode,
+             WebBrowserMode)
         @keyparam expandedEntries list of entries to be shown expanded
             (list of strings)
+        @keyparam webEngine flag indicating QtWebEngine is used (bool)
         """
         super(ConfigurationDialog, self).__init__(parent)
         if name:
@@ -883,7 +962,8 @@
         
         self.cw = ConfigurationWidget(self, fromEric=fromEric,
                                       displayMode=displayMode,
-                                      expandedEntries=expandedEntries)
+                                      expandedEntries=expandedEntries,
+                                      webEngine=webEngine)
         size = self.cw.size()
         self.layout.addWidget(self.cw)
         self.resize(size)
@@ -951,15 +1031,17 @@
     """
     Main window class for the standalone dialog.
     """
-    def __init__(self, parent=None):
+    def __init__(self, parent=None, webEngine=False):
         """
         Constructor
         
         @param parent reference to the parent widget (QWidget)
+        @keyparam webEngine flag indicating QtWebEngine is used (bool)
         """
         super(ConfigurationWindow, self).__init__(parent)
         
-        self.cw = ConfigurationWidget(self, fromEric=False)
+        self.cw = ConfigurationWidget(self, fromEric=False,
+                                      webEngine=webEngine)
         size = self.cw.size()
         self.setCentralWidget(self.cw)
         self.resize(size)
--- a/Preferences/ConfigurationPages/HelpViewersPage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/HelpViewersPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -9,7 +9,21 @@
 
 from __future__ import unicode_literals
 
+from PyQt5.QtCore import qVersion
 from PyQt5.QtWidgets import QButtonGroup
+try:
+    from PyQt5 import QtWebKit      # __IGNORE_WARNING__
+    WEBKIT_AVAILABLE = True
+except ImportError:
+    WEBKIT_AVAILABLE = False
+if qVersion() < "5.6.0":
+    WEBENGINE_AVAILABLE = False
+else:
+    try:
+        from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+        WEBENGINE_AVAILABLE = True
+    except ImportError:
+        WEBENGINE_AVAILABLE = False
 
 from E5Gui.E5PathPicker import E5PathPickerModes
 
@@ -40,14 +54,14 @@
         self.helpViewerGroup.addButton(self.customViewerButton)
         
         # set initial values
-        hvId = Preferences.getHelp("HelpViewerType")
-        # check availability of QtWebKit
-        try:
-            from PyQt5 import QtWebKit      # __IGNORE_WARNING__
-        except ImportError:
-            # not available, reset help viewer to default
+        if WEBENGINE_AVAILABLE:
+            hvId = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvId = Preferences.getHelp("HelpViewerType")
+        if not WEBENGINE_AVAILABLE and not WEBKIT_AVAILABLE:
             if hvId == 1:
-                hvId = Preferences.Prefs.helpDefaults["HelpViewerType"]
+                hvId = Preferences.Prefs.webBrowserDefaultsDefaults[
+                    "HelpViewerType"]
             self.helpBrowserButton.setEnabled(False)
         
         if hvId == 1:
@@ -74,6 +88,7 @@
         elif self.customViewerButton.isChecked():
             hvId = 4
         Preferences.setHelp("HelpViewerType", hvId)
+        Preferences.setWebBrowser("HelpViewerType", hvId)
         Preferences.setHelp(
             "CustomViewer",
             self.customViewerPicker.text())
--- a/Preferences/ConfigurationPages/NetworkPage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/NetworkPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -25,14 +25,22 @@
     """
     Class implementing the Network configuration page.
     """
-    def __init__(self):
+    def __init__(self, configDialog):
         """
         Constructor
+        
+        @param configDialog reference to the configuration dialog
+            (ConfigurationDialog)
         """
         super(NetworkPage, self).__init__()
         self.setupUi(self)
         self.setObjectName("NetworkPage")
         
+        self.__configDlg = configDialog
+        self.__displayMode = None
+        self.__webEngine = False
+        self.__webKit = False
+        
         self.downloadDirPicker.setMode(E5PathPickerModes.DirectoryMode)
         
         self.ftpProxyTypeCombo.addItem(
@@ -60,17 +68,6 @@
         self.downloadDirPicker.setText(Preferences.getUI("DownloadPath"))
         self.requestFilenameCheckBox.setChecked(
             Preferences.getUI("RequestDownloadFilename"))
-        try:
-            policy = Preferences.getHelp("DownloadManagerRemovePolicy")
-            from Helpviewer.Download.DownloadManager import DownloadManager
-            if policy == DownloadManager.RemoveNever:
-                self.cleanupNeverButton.setChecked(True)
-            elif policy == DownloadManager.RemoveExit:
-                self.cleanupExitButton.setChecked(True)
-            else:
-                self.cleanupSuccessfulButton.setChecked(True)
-        except ImportError:
-            self.cleanupGroup.hide()
         
         # HTTP proxy
         self.httpProxyHostEdit.setText(
@@ -111,6 +108,48 @@
         self.exceptionsEdit.setText(
             ", ".join(Preferences.getUI("ProxyExceptions").split(",")))
     
+    def setMode(self, displayMode):
+        """
+        Public method to perform mode dependent setups.
+        
+        @param displayMode mode of the configuration dialog
+            (ConfigurationWidget.DefaultMode,
+             ConfigurationWidget.HelpBrowserMode,
+             ConfigurationWidget.WebBrowserMode)
+        """
+        from ..ConfigurationDialog import ConfigurationWidget
+        assert displayMode in (
+            ConfigurationWidget.DefaultMode,
+            ConfigurationWidget.HelpBrowserMode,
+            ConfigurationWidget.WebBrowserMode
+        )
+        
+        self.__displayMode = displayMode
+        if self.__displayMode == ConfigurationWidget.HelpBrowserMode or \
+                not self.__configDlg.isUsingWebEngine():
+            try:
+                policy = Preferences.getHelp("DownloadManagerRemovePolicy")
+                from Helpviewer.Download.DownloadManager import DownloadManager
+                if policy == DownloadManager.RemoveNever:
+                    self.cleanupNeverButton.setChecked(True)
+                elif policy == DownloadManager.RemoveExit:
+                    self.cleanupExitButton.setChecked(True)
+                else:
+                    self.cleanupSuccessfulButton.setChecked(True)
+                self.__webKit = True
+            except ImportError:
+                self.cleanupGroup.hide()
+        else:
+            policy = Preferences.getWebBrowser("DownloadManagerRemovePolicy")
+            from WebBrowser.Download.DownloadManager import DownloadManager
+            if policy == DownloadManager.RemoveNever:
+                self.cleanupNeverButton.setChecked(True)
+            elif policy == DownloadManager.RemoveExit:
+                self.cleanupExitButton.setChecked(True)
+            else:
+                self.cleanupSuccessfulButton.setChecked(True)
+            self.__webEngine = True
+    
     def save(self):
         """
         Public slot to save the Networj configuration.
@@ -121,7 +160,7 @@
         Preferences.setUI(
             "RequestDownloadFilename",
             self.requestFilenameCheckBox.isChecked())
-        try:
+        if self.__webKit:
             from Helpviewer.Download.DownloadManager import DownloadManager
             if self.cleanupNeverButton.isChecked():
                 policy = DownloadManager.RemoveNever
@@ -130,9 +169,15 @@
             else:
                 policy = DownloadManager.RemoveSuccessFullDownload
             Preferences.setHelp("DownloadManagerRemovePolicy", policy)
-        except ImportError:
-            # ignore it
-            pass
+        elif self.__webEngine:
+            from WebBrowser.Download.DownloadManager import DownloadManager
+            if self.cleanupNeverButton.isChecked():
+                policy = DownloadManager.RemoveNever
+            elif self.cleanupExitButton.isChecked():
+                policy = DownloadManager.RemoveExit
+            else:
+                policy = DownloadManager.RemoveSuccessFullDownload
+            Preferences.setWebBrowser("DownloadManagerRemovePolicy", policy)
         
         Preferences.setUI(
             "UseProxy",
@@ -222,5 +267,5 @@
     @param dlg reference to the configuration dialog
     @return reference to the instantiated page (ConfigurationPageBase)
     """
-    page = NetworkPage()
+    page = NetworkPage(dlg)
     return page
--- a/Preferences/ConfigurationPages/PrinterPage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/PrinterPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -52,6 +52,8 @@
             Preferences.getPrinter("TopMargin"))
         self.bottomMarginSpinBox.setValue(
             Preferences.getPrinter("BottomMargin"))
+        self.resolutionSpinBox.setValue(
+            Preferences.getPrinter("Resolution"))
         
     def save(self):
         """
@@ -84,6 +86,9 @@
         Preferences.setPrinter(
             "BottomMargin",
             self.bottomMarginSpinBox.value())
+        Preferences.setPrinter(
+            "Resolution",
+            self.resolutionSpinBox.value())
         
     @pyqtSlot()
     def on_printheaderFontButton_clicked(self):
--- a/Preferences/ConfigurationPages/PrinterPage.ui	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/PrinterPage.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -1,56 +1,86 @@
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>PrinterPage</class>
- <widget class="QWidget" name="PrinterPage" >
-  <property name="geometry" >
+ <widget class="QWidget" name="PrinterPage">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>446</width>
+    <width>448</width>
     <height>568</height>
    </rect>
   </property>
-  <layout class="QVBoxLayout" >
+  <layout class="QVBoxLayout" name="verticalLayout">
    <item>
-    <widget class="QLabel" name="headerLabel" >
-     <property name="text" >
-      <string>&lt;b>Configure printer settings&lt;/b></string>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure printer settings&lt;/b&gt;</string>
      </property>
     </widget>
    </item>
    <item>
-    <widget class="Line" name="line7" >
-     <property name="frameShape" >
+    <widget class="Line" name="line7">
+     <property name="frameShape">
       <enum>QFrame::HLine</enum>
      </property>
-     <property name="frameShadow" >
+     <property name="frameShadow">
       <enum>QFrame::Sunken</enum>
      </property>
-     <property name="orientation" >
+     <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
    <item>
-    <layout class="QGridLayout" >
-     <item row="1" column="1" colspan="2" >
-      <widget class="QFrame" name="frame_2" >
-       <property name="frameShape" >
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="TextLabel1">
+       <property name="text">
+        <string>Printername:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" colspan="3">
+      <widget class="QLineEdit" name="printerNameEdit"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="TextLabel2">
+       <property name="text">
+        <string>Colour Mode:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" colspan="3">
+      <widget class="QFrame" name="frame_2">
+       <property name="frameShape">
         <enum>QFrame::NoFrame</enum>
        </property>
-       <layout class="QVBoxLayout" >
-        <property name="margin" >
+       <layout class="QVBoxLayout">
+        <property name="leftMargin">
+         <number>0</number>
+        </property>
+        <property name="topMargin">
+         <number>0</number>
+        </property>
+        <property name="rightMargin">
+         <number>0</number>
+        </property>
+        <property name="bottomMargin">
          <number>0</number>
         </property>
         <item>
-         <widget class="QRadioButton" name="printerColorButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printerColorButton">
+          <property name="text">
            <string>Colour</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QRadioButton" name="printerGrayscaleButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printerGrayscaleButton">
+          <property name="text">
            <string>Gray Scale</string>
           </property>
          </widget>
@@ -58,28 +88,47 @@
        </layout>
       </widget>
      </item>
-     <item row="2" column="1" colspan="2" >
-      <widget class="QFrame" name="frame" >
-       <property name="frameShape" >
+     <item row="2" column="0">
+      <widget class="QLabel" name="TextLabel4">
+       <property name="text">
+        <string>Page Order:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1" colspan="3">
+      <widget class="QFrame" name="frame">
+       <property name="frameShape">
         <enum>QFrame::NoFrame</enum>
        </property>
-       <layout class="QVBoxLayout" >
-        <property name="spacing" >
+       <layout class="QVBoxLayout">
+        <property name="spacing">
          <number>6</number>
         </property>
-        <property name="margin" >
+        <property name="leftMargin">
+         <number>0</number>
+        </property>
+        <property name="topMargin">
+         <number>0</number>
+        </property>
+        <property name="rightMargin">
+         <number>0</number>
+        </property>
+        <property name="bottomMargin">
          <number>0</number>
         </property>
         <item>
-         <widget class="QRadioButton" name="printFirstPageFirstButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printFirstPageFirstButton">
+          <property name="text">
            <string>First Page First</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QRadioButton" name="printFirstPageLastButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printFirstPageLastButton">
+          <property name="text">
            <string>Last Page First</string>
           </property>
          </widget>
@@ -87,88 +136,32 @@
        </layout>
       </widget>
      </item>
-     <item row="0" column="1" colspan="2" >
-      <widget class="QLineEdit" name="printerNameEdit" />
-     </item>
-     <item row="0" column="0" >
-      <widget class="QLabel" name="TextLabel1" >
-       <property name="text" >
-        <string>Printername:</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="0" >
-      <widget class="QLabel" name="TextLabel3" >
-       <property name="text" >
+     <item row="3" column="0">
+      <widget class="QLabel" name="TextLabel3">
+       <property name="text">
         <string>Magnification:</string>
        </property>
       </widget>
      </item>
-     <item row="4" column="1" colspan="2" >
-      <widget class="QLineEdit" name="printheaderFontSample" >
-       <property name="focusPolicy" >
-        <enum>Qt::NoFocus</enum>
-       </property>
-       <property name="text" >
-        <string>Header Font</string>
-       </property>
-       <property name="alignment" >
-        <set>Qt::AlignHCenter</set>
-       </property>
-       <property name="readOnly" >
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="0" >
-      <widget class="QPushButton" name="printheaderFontButton" >
-       <property name="toolTip" >
-        <string>Press to select the font for the page headers</string>
-       </property>
-       <property name="text" >
-        <string>Header Font</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="1" >
-      <widget class="QSpinBox" name="printMagnificationSpinBox" >
-       <property name="minimum" >
+     <item row="3" column="1">
+      <widget class="QSpinBox" name="printMagnificationSpinBox">
+       <property name="minimum">
         <number>-10</number>
        </property>
-       <property name="maximum" >
+       <property name="maximum">
         <number>10</number>
        </property>
-       <property name="value" >
+       <property name="value">
         <number>-3</number>
        </property>
       </widget>
      </item>
-     <item row="1" column="0" >
-      <widget class="QLabel" name="TextLabel2" >
-       <property name="text" >
-        <string>Colour Mode:</string>
-       </property>
-       <property name="alignment" >
-        <set>Qt::AlignTop</set>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="0" >
-      <widget class="QLabel" name="TextLabel4" >
-       <property name="text" >
-        <string>Page Order:</string>
-       </property>
-       <property name="alignment" >
-        <set>Qt::AlignTop</set>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="2" >
+     <item row="3" column="2" colspan="2">
       <spacer>
-       <property name="orientation" >
+       <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
-       <property name="sizeHint" stdset="0" >
+       <property name="sizeHint" stdset="0">
         <size>
          <width>251</width>
          <height>20</height>
@@ -176,88 +169,153 @@
        </property>
       </spacer>
      </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Resolution:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1" colspan="2">
+      <widget class="QSpinBox" name="resolutionSpinBox">
+       <property name="toolTip">
+        <string>Select the printer resolution </string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+       <property name="suffix">
+        <string> DPI</string>
+       </property>
+       <property name="maximum">
+        <number>6000</number>
+       </property>
+       <property name="singleStep">
+        <number>50</number>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="3">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>208</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="5" column="0">
+      <widget class="QPushButton" name="printheaderFontButton">
+       <property name="toolTip">
+        <string>Press to select the font for the page headers</string>
+       </property>
+       <property name="text">
+        <string>Header Font</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1" colspan="3">
+      <widget class="QLineEdit" name="printheaderFontSample">
+       <property name="focusPolicy">
+        <enum>Qt::NoFocus</enum>
+       </property>
+       <property name="text">
+        <string>Header Font</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignHCenter</set>
+       </property>
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
     </layout>
    </item>
    <item>
-    <layout class="QHBoxLayout" >
+    <layout class="QHBoxLayout">
      <item>
-      <widget class="QGroupBox" name="groupBox" >
-       <property name="title" >
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
         <string>Margins</string>
        </property>
-       <layout class="QGridLayout" >
-        <item row="0" column="1" >
-         <widget class="QDoubleSpinBox" name="topMarginSpinBox" >
-          <property name="toolTip" >
+       <layout class="QGridLayout">
+        <item row="0" column="1">
+         <widget class="QDoubleSpinBox" name="topMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the top margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
         </item>
-        <item row="1" column="0" >
-         <widget class="QDoubleSpinBox" name="leftMarginSpinBox" >
-          <property name="toolTip" >
+        <item row="1" column="0">
+         <widget class="QDoubleSpinBox" name="leftMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the left margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
         </item>
-        <item row="1" column="2" >
-         <widget class="QDoubleSpinBox" name="rightMarginSpinBox" >
-          <property name="toolTip" >
+        <item row="1" column="2">
+         <widget class="QDoubleSpinBox" name="rightMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the right margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
         </item>
-        <item row="2" column="1" >
-         <widget class="QDoubleSpinBox" name="bottomMarginSpinBox" >
-          <property name="toolTip" >
+        <item row="2" column="1">
+         <widget class="QDoubleSpinBox" name="bottomMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the bottom margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
@@ -267,10 +325,10 @@
      </item>
      <item>
       <spacer>
-       <property name="orientation" >
+       <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
-       <property name="sizeHint" stdset="0" >
+       <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
@@ -282,10 +340,10 @@
    </item>
    <item>
     <spacer>
-     <property name="orientation" >
+     <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
-     <property name="sizeHint" stdset="0" >
+     <property name="sizeHint" stdset="0">
       <size>
        <width>428</width>
        <height>61</height>
@@ -302,6 +360,7 @@
   <tabstop>printFirstPageFirstButton</tabstop>
   <tabstop>printFirstPageLastButton</tabstop>
   <tabstop>printMagnificationSpinBox</tabstop>
+  <tabstop>resolutionSpinBox</tabstop>
   <tabstop>printheaderFontButton</tabstop>
   <tabstop>topMarginSpinBox</tabstop>
   <tabstop>leftMarginSpinBox</tabstop>
--- a/Preferences/ConfigurationPages/SecurityPage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/SecurityPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -11,10 +11,6 @@
 
 from PyQt5.QtCore import pyqtSlot
 from PyQt5.QtWidgets import QDialog
-try:
-    from PyQt5.QtWebKit import QWebSettings
-except ImportError:
-    QWebSettings = None
 
 from .ConfigurationPageBase import ConfigurationPageBase
 from .Ui_SecurityPage import Ui_SecurityPage
@@ -38,6 +34,7 @@
         self.setObjectName("SecurityPage")
         
         self.__configDlg = configDialog
+        self.__displayMode = None
         
         # set initial values
         self.savePasswordsCheckBox.setChecked(
@@ -46,15 +43,41 @@
             Preferences.getUser("UseMasterPassword"))
         self.masterPasswordButton.setEnabled(
             Preferences.getUser("UseMasterPassword"))
-        if QWebSettings and hasattr(QWebSettings, "DnsPrefetchEnabled"):
-            self.dnsPrefetchCheckBox.setChecked(
-                Preferences.getHelp("DnsPrefetchEnabled"))
-        else:
-            self.dnsPrefetchCheckBox.setEnabled(False)
         
         self.__newPassword = ""
         self.__oldUseMasterPassword = Preferences.getUser("UseMasterPassword")
     
+    def setMode(self, displayMode):
+        """
+        Public method to perform mode dependent setups.
+        
+        @param displayMode mode of the configuration dialog
+            (ConfigurationWidget.DefaultMode,
+             ConfigurationWidget.HelpBrowserMode,
+             ConfigurationWidget.WebBrowserMode)
+        """
+        from ..ConfigurationDialog import ConfigurationWidget
+        assert displayMode in (
+            ConfigurationWidget.DefaultMode,
+            ConfigurationWidget.HelpBrowserMode,
+            ConfigurationWidget.WebBrowserMode
+        )
+        
+        self.__displayMode = displayMode
+        if self.__displayMode == ConfigurationWidget.HelpBrowserMode:
+            try:
+                from PyQt5.QtWebKit import QWebSettings
+                if QWebSettings and \
+                        hasattr(QWebSettings, "DnsPrefetchEnabled"):
+                    self.dnsPrefetchCheckBox.setChecked(
+                        Preferences.getHelp("DnsPrefetchEnabled"))
+            except ImportError:
+                self.dnsPrefetchCheckBox.setEnabled(False)
+        else:
+            if self.__configDlg.isUsingWebEngine():
+                self.dnsPrefetchCheckBox.setEnabled(False)
+                self.dnsGroup.hide()
+    
     def save(self):
         """
         Public slot to save the Help Viewers configuration.
--- a/Preferences/ConfigurationPages/SecurityPage.ui	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/SecurityPage.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -91,7 +91,7 @@
     </widget>
    </item>
    <item>
-    <widget class="QGroupBox" name="groupBox_3">
+    <widget class="QGroupBox" name="dnsGroup">
      <property name="title">
       <string>DNS</string>
      </property>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserAppearancePage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2006 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Web Browser configuration page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtGui import QFont
+from PyQt5.QtWidgets import QFontDialog
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .ConfigurationPageBase import ConfigurationPageBase
+from .Ui_WebBrowserAppearancePage import Ui_WebBrowserAppearancePage
+
+import Preferences
+
+try:
+    MonospacedFontsOption = QFontDialog.MonospacedFonts
+except AttributeError:
+    MonospacedFontsOption = QFontDialog.FontDialogOptions(0x10)
+
+
+class WebBrowserAppearancePage(ConfigurationPageBase,
+                               Ui_WebBrowserAppearancePage):
+    """
+    Class implementing the Web Browser Appearance page.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(WebBrowserAppearancePage, self).__init__()
+        self.setupUi(self)
+        self.setObjectName("WebBrowserAppearancePage")
+        
+        self.styleSheetPicker.setMode(E5PathPickerModes.OpenFileMode)
+        self.styleSheetPicker.setFilters(self.tr(
+            "Cascading Style Sheets (*.css);;All files (*)"))
+        
+        self.__displayMode = None
+        
+        # set initial values
+        defaultFontSize = Preferences.getWebBrowser("DefaultFontSize")
+        fixedFontSize = Preferences.getWebBrowser("DefaultFixedFontSize")
+        self.defaultSizeSpinBox.setValue(defaultFontSize)
+        self.fixedSizeSpinBox.setValue(fixedFontSize)
+        self.minSizeSpinBox.setValue(
+            Preferences.getWebBrowser("MinimumFontSize"))
+        self.minLogicalSizeSpinBox.setValue(
+            Preferences.getWebBrowser("MinimumLogicalFontSize"))
+        
+        self.standardFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("StandardFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        self.fixedFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("FixedFontFamily"),
+                  fixedFontSize, QFont.Normal, False))
+        self.serifFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("SerifFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        self.sansSerifFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("SansSerifFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        self.cursiveFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("CursiveFontFamily"),
+                  defaultFontSize, QFont.Normal, True))
+        self.fantasyFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("FantasyFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        
+        self.initColour("SaveUrlColor", self.secureURLsColourButton,
+                        Preferences.getWebBrowser)
+        
+        self.autoLoadImagesCheckBox.setChecked(
+            Preferences.getWebBrowser("AutoLoadImages"))
+        
+        self.styleSheetPicker.setText(
+            Preferences.getWebBrowser("UserStyleSheet"))
+        
+        self.tabsCloseButtonCheckBox.setChecked(
+            Preferences.getUI("SingleCloseButton"))
+        self.warnOnMultipleCloseCheckBox.setChecked(
+            Preferences.getWebBrowser("WarnOnMultipleClose"))
+    
+    def setMode(self, displayMode):
+        """
+        Public method to perform mode dependent setups.
+        
+        @param displayMode mode of the configuration dialog
+            (ConfigurationWidget.DefaultMode,
+             ConfigurationWidget.HelpBrowserMode,
+             ConfigurationWidget.TrayStarterMode)
+        """
+        from ..ConfigurationDialog import ConfigurationWidget
+        assert displayMode in (
+            ConfigurationWidget.DefaultMode,
+            ConfigurationWidget.WebBrowserMode,
+        )
+        
+        self.__displayMode = displayMode
+        if self.__displayMode != ConfigurationWidget.WebBrowserMode:
+            self.tabsGroupBox.hide()
+    
+    def save(self):
+        """
+        Public slot to save the Help Viewers configuration.
+        """
+        Preferences.setWebBrowser(
+            "StandardFontFamily",
+            self.standardFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "FixedFontFamily",
+            self.fixedFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "SerifFontFamily",
+            self.serifFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "SansSerifFontFamily",
+            self.sansSerifFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "CursiveFontFamily",
+            self.cursiveFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "FantasyFontFamily",
+            self.fantasyFontCombo.currentFont().family())
+        
+        Preferences.setWebBrowser(
+            "DefaultFontSize",
+            self.defaultSizeSpinBox.value())
+        Preferences.setWebBrowser(
+            "DefaultFixedFontSize",
+            self.fixedSizeSpinBox.value())
+        Preferences.setWebBrowser(
+            "MinimumFontSize",
+            self.minSizeSpinBox.value())
+        Preferences.setWebBrowser(
+            "MinimumLogicalFontSize",
+            self.minLogicalSizeSpinBox.value())
+        
+        Preferences.setWebBrowser(
+            "AutoLoadImages",
+            self.autoLoadImagesCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "UserStyleSheet",
+            self.styleSheetPicker.text())
+        
+        self.saveColours(Preferences.setWebBrowser)
+        
+        from ..ConfigurationDialog import ConfigurationWidget
+        if self.__displayMode == ConfigurationWidget.WebBrowserMode:
+            Preferences.setUI(
+                "SingleCloseButton",
+                self.tabsCloseButtonCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "WarnOnMultipleClose",
+            self.warnOnMultipleCloseCheckBox.isChecked())
+    
+
+def create(dlg):
+    """
+    Module function to create the configuration page.
+    
+    @param dlg reference to the configuration dialog
+    @return reference to the instantiated page (ConfigurationPageBase)
+    """
+    page = WebBrowserAppearancePage()
+    return page
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserAppearancePage.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,410 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebBrowserAppearancePage</class>
+ <widget class="QWidget" name="WebBrowserAppearancePage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>499</width>
+    <height>782</height>
+   </rect>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_3">
+   <item>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure Web Browser appearance&lt;/b&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line17">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_4">
+     <property name="title">
+      <string>Fonts</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Standard Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QFontComboBox" name="standardFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the standard font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Fixed Width Font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QFontComboBox" name="fixedFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the fixed width font</string>
+        </property>
+        <property name="fontFilters">
+         <set>QFontComboBox::MonospacedFonts</set>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Serif Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QFontComboBox" name="serifFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the serif font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Sans Serif Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QFontComboBox" name="sansSerifFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the sans serif font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Cursive Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="QFontComboBox" name="cursiveFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the cursive font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Fantasy Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1">
+       <widget class="QFontComboBox" name="fantasyFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the fantasy font</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Font Sizes</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_8">
+        <property name="text">
+         <string>Default Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QSpinBox" name="defaultSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_9">
+        <property name="text">
+         <string>Fixed Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="fixedSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_10">
+        <property name="text">
+         <string>Minimum Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QSpinBox" name="minSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_11">
+        <property name="text">
+         <string>Minimum Logical Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QSpinBox" name="minLogicalSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="2">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>230</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Colours</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="textLabel1_3">
+        <property name="text">
+         <string>Background colour of secure URLs:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QPushButton" name="secureURLsColourButton">
+        <property name="minimumSize">
+         <size>
+          <width>100</width>
+          <height>0</height>
+         </size>
+        </property>
+        <property name="toolTip">
+         <string>Select the background colour for secure URLs.</string>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>141</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Images</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QCheckBox" name="autoLoadImagesCheckBox">
+        <property name="toolTip">
+         <string>Select to load images</string>
+        </property>
+        <property name="text">
+         <string>Load images</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Style Sheet</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>User Style Sheet:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="E5PathPicker" name="styleSheetPicker" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="focusPolicy">
+         <enum>Qt::StrongFocus</enum>
+        </property>
+        <property name="toolTip">
+         <string>Enter the file name of a user style sheet</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="tabsGroupBox">
+     <property name="title">
+      <string>Tabs</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QCheckBox" name="tabsCloseButtonCheckBox">
+        <property name="text">
+         <string>Show only one close button instead of one for each tab</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="warnOnMultipleCloseCheckBox">
+        <property name="toolTip">
+         <string>Select to issue a warning, if multiple tabs are about to be closed</string>
+        </property>
+        <property name="text">
+         <string>Warn, if multiple tabs are about to be closed</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>479</width>
+       <height>121</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>secureURLsColourButton</tabstop>
+  <tabstop>autoLoadImagesCheckBox</tabstop>
+  <tabstop>styleSheetPicker</tabstop>
+  <tabstop>tabsCloseButtonCheckBox</tabstop>
+  <tabstop>warnOnMultipleCloseCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,300 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the  Web Browser configuration page.
+"""
+
+from PyQt5.QtCore import pyqtSlot, QLocale
+
+from .ConfigurationPageBase import ConfigurationPageBase
+from .Ui_WebBrowserPage import Ui_WebBrowserPage
+
+import Preferences
+
+
+class WebBrowserPage(ConfigurationPageBase, Ui_WebBrowserPage):
+    """
+    Class documentation goes here.
+    """
+    def __init__(self, configDialog):
+        """
+        Constructor
+        
+        @param configDialog reference to the configuration dialog
+            (ConfigurationDialog)
+        """
+        super(WebBrowserPage, self).__init__()
+        self.setupUi(self)
+        self.setObjectName("WebBrowserPage")
+        
+        self.__configDlg = configDialog
+        mw = configDialog.parent().parent()
+        if hasattr(mw, "helpWindow") and mw.helpWindow is not None:
+            # IDE
+            self.__browserWindow = mw.helpWindow
+        elif hasattr(mw, "currentBrowser"):
+            # Web Browser
+            self.__browserWindow = mw
+        else:
+            self.__browserWindow = None
+        self.setCurrentPageButton.setEnabled(self.__browserWindow is not None)
+        
+        defaultSchemes = ["file://", "http://", "https://"]
+        self.defaultSchemeCombo.addItems(defaultSchemes)
+        
+        # set initial values
+        self.singleHelpWindowCheckBox.setChecked(
+            Preferences.getWebBrowser("SingleWebBrowserWindow"))
+        self.saveGeometryCheckBox.setChecked(
+            Preferences.getWebBrowser("SaveGeometry"))
+        self.webSuggestionsCheckBox.setChecked(
+            Preferences.getWebBrowser("WebSearchSuggestions"))
+        self.showTabPreviews.setChecked(
+            Preferences.getWebBrowser("ShowPreview"))
+        self.errorPageCheckBox.setChecked(
+            Preferences.getWebBrowser("ErrorPageEnabled"))
+        self.scrollingCheckBox.setChecked(
+            Preferences.getWebBrowser("ScrollAnimatorEnabled"))
+        self.fullscreenCheckBox.setChecked(
+            Preferences.getWebBrowser("FullScreenSupportEnabled"))
+        
+        self.javaScriptGroup.setChecked(
+            Preferences.getWebBrowser("JavaScriptEnabled"))
+        self.jsOpenWindowsCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanOpenWindows"))
+        # TODO: Qt 5.7?
+##        self.jsCloseWindowsCheckBox.setChecked(
+##            Preferences.getWebBrowser("JavaScriptCanCloseWindows"))
+        self.jsClipboardCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanAccessClipboard"))
+        self.pluginsCheckBox.setChecked(
+            Preferences.getWebBrowser("PluginsEnabled"))
+        self.doNotTrackCheckBox.setChecked(
+            Preferences.getWebBrowser("DoNotTrack"))
+        self.sendRefererCheckBox.setChecked(
+            Preferences.getWebBrowser("SendReferer"))
+        
+        self.diskCacheCheckBox.setChecked(
+            Preferences.getWebBrowser("DiskCacheEnabled"))
+        self.cacheSizeSpinBox.setValue(
+            Preferences.getWebBrowser("DiskCacheSize"))
+        
+        self.startupCombo.setCurrentIndex(
+            Preferences.getWebBrowser("StartupBehavior"))
+        self.homePageEdit.setText(
+            Preferences.getWebBrowser("HomePage"))
+        
+        self.defaultSchemeCombo.setCurrentIndex(
+            self.defaultSchemeCombo.findText(
+                Preferences.getWebBrowser("DefaultScheme")))
+        
+        historyLimit = Preferences.getWebBrowser("HistoryLimit")
+        idx = 0
+        if historyLimit == 1:
+            idx = 0
+        elif historyLimit == 7:
+            idx = 1
+        elif historyLimit == 14:
+            idx = 2
+        elif historyLimit == 30:
+            idx = 3
+        elif historyLimit == 365:
+            idx = 4
+        elif historyLimit == -1:
+            idx = 5
+        elif historyLimit == -2:
+            idx = 6
+        else:
+            idx = 5
+        self.expireHistory.setCurrentIndex(idx)
+        
+        for language in range(2, QLocale.LastLanguage + 1):
+            countries = [l.country() for l in QLocale.matchingLocales(
+                language, QLocale.AnyScript, QLocale.AnyCountry)]
+            if len(countries) > 0:
+                self.languageCombo.addItem(
+                    QLocale.languageToString(language), language)
+        self.languageCombo.model().sort(0)
+        self.languageCombo.insertSeparator(0)
+        self.languageCombo.insertItem(0, QLocale.languageToString(0), 0)
+        index = self.languageCombo.findData(
+            Preferences.getWebBrowser("SearchLanguage"))
+        if index > -1:
+            self.languageCombo.setCurrentIndex(index)
+        
+        self.spatialCheckBox.setChecked(
+            Preferences.getWebBrowser("SpatialNavigationEnabled"))
+        self.linksInFocusChainCheckBox.setChecked(
+            Preferences.getWebBrowser("LinksIncludedInFocusChain"))
+        self.xssAuditingCheckBox.setChecked(
+            Preferences.getWebBrowser("XSSAuditingEnabled"))
+        
+        self.webInspectorGroup.setChecked(
+            Preferences.getWebBrowser("WebInspectorEnabled"))
+        self.webInspectorPortSpinBox.setValue(
+            Preferences.getWebBrowser("WebInspectorPort"))
+        
+        # TODO: Qt 5.7?
+        # Hide entries not yet supported
+        self.jsCloseWindowsCheckBox.hide()
+    
+    def save(self):
+        """
+        Public slot to save the Help Viewers configuration.
+        """
+        Preferences.setWebBrowser(
+            "SingleWebBrowserWindow",
+            self.singleHelpWindowCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SaveGeometry",
+            self.saveGeometryCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "WebSearchSuggestions",
+            self.webSuggestionsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "ShowPreview",
+            self.showTabPreviews.isChecked())
+        Preferences.setWebBrowser(
+            "ErrorPageEnabled",
+            self.errorPageCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "ScrollAnimatorEnabled",
+            self.scrollingCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "FullScreenSupportEnabled",
+            self.fullscreenCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "JavaScriptEnabled",
+            self.javaScriptGroup.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanOpenWindows",
+            self.jsOpenWindowsCheckBox.isChecked())
+        # TODO: Qt 5.7?
+##        Preferences.setWebBrowser(
+##            "JavaScriptCanCloseWindows",
+##            self.jsCloseWindowsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanAccessClipboard",
+            self.jsClipboardCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "PluginsEnabled",
+            self.pluginsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "DoNotTrack",
+            self.doNotTrackCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SendReferer",
+            self.sendRefererCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "DiskCacheEnabled",
+            self.diskCacheCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "DiskCacheSize",
+            self.cacheSizeSpinBox.value())
+        
+        Preferences.setWebBrowser(
+            "StartupBehavior",
+            self.startupCombo.currentIndex())
+        Preferences.setWebBrowser(
+            "HomePage",
+            self.homePageEdit.text())
+        
+        Preferences.setWebBrowser(
+            "DefaultScheme",
+            self.defaultSchemeCombo.currentText())
+        
+        idx = self.expireHistory.currentIndex()
+        if idx == 0:
+            historyLimit = 1
+        elif idx == 1:
+            historyLimit = 7
+        elif idx == 2:
+            historyLimit = 14
+        elif idx == 3:
+            historyLimit = 30
+        elif idx == 4:
+            historyLimit = 365
+        elif idx == 5:
+            historyLimit = -1
+        elif idx == 6:
+            historyLimit = -2
+        Preferences.setWebBrowser("HistoryLimit", historyLimit)
+        
+        languageIndex = self.languageCombo.currentIndex()
+        if languageIndex > -1:
+            language = self.languageCombo.itemData(languageIndex)
+        else:
+            # fall back to system default
+            language = QLocale.system().language()
+        Preferences.setWebBrowser("SearchLanguage", language)
+        
+        Preferences.setWebBrowser(
+            "SpatialNavigationEnabled",
+            self.spatialCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "LinksIncludedInFocusChain",
+            self.linksInFocusChainCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "XSSAuditingEnabled",
+            self.xssAuditingCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "WebInspectorEnabled",
+            self.webInspectorGroup.isChecked())
+        Preferences.setWebBrowser(
+            "WebInspectorPort",
+            self.webInspectorPortSpinBox.value())
+    
+    @pyqtSlot()
+    def on_setCurrentPageButton_clicked(self):
+        """
+        Private slot to set the current page as the home page.
+        """
+        url = self.__browserWindow.currentBrowser().url()
+        self.homePageEdit.setText(bytes(url.toEncoded()).decode())
+    
+    @pyqtSlot()
+    def on_defaultHomeButton_clicked(self):
+        """
+        Private slot to set the default home page.
+        """
+        self.homePageEdit.setText(Preferences.Prefs.helpDefaults["HomePage"])
+    
+    @pyqtSlot(int)
+    def on_startupCombo_currentIndexChanged(self, index):
+        """
+        Private slot to enable elements depending on the selected startup
+        entry.
+        
+        @param index index of the selected entry (integer)
+        """
+        enable = index == 0
+        self.homePageLabel.setEnabled(enable)
+        self.homePageEdit.setEnabled(enable)
+        self.defaultHomeButton.setEnabled(enable)
+        self.setCurrentPageButton.setEnabled(enable)
+    
+    @pyqtSlot()
+    def on_refererWhitelistButton_clicked(self):
+        """
+        Private slot to edit the referer whitelist.
+        """
+        from WebBrowser.Network.SendRefererWhitelistDialog import \
+            SendRefererWhitelistDialog
+        SendRefererWhitelistDialog(self).exec_()
+
+
+def create(dlg):
+    """
+    Module function to create the configuration page.
+    
+    @param dlg reference to the configuration dialog
+    @return reference to the instantiated page (ConfigurationPageBase)
+    """
+    page = WebBrowserPage(dlg)
+    return page
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserPage.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,681 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebBrowserPage</class>
+ <widget class="QWidget" name="WebBrowserPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>616</width>
+    <height>1329</height>
+   </rect>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure Web Browser&lt;/b&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line17">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_4">
+     <property name="title">
+      <string>General</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_5">
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="singleHelpWindowCheckBox">
+        <property name="toolTip">
+         <string>Select to use a single web browser window only</string>
+        </property>
+        <property name="text">
+         <string>Use single web browser window</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="webSuggestionsCheckBox">
+        <property name="toolTip">
+         <string>Select to enable suggestions for web searches</string>
+        </property>
+        <property name="text">
+         <string>Show suggestions for web searches</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="saveGeometryCheckBox">
+        <property name="toolTip">
+         <string>Select to save the window size and position</string>
+        </property>
+        <property name="text">
+         <string>Save size and position upon exit</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QCheckBox" name="showTabPreviews">
+        <property name="toolTip">
+         <string>Select to show a page preview when the mouse hovers over the tab</string>
+        </property>
+        <property name="text">
+         <string>Show preview when hovering tab</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QCheckBox" name="errorPageCheckBox">
+        <property name="toolTip">
+         <string>Select to enable displaying the built-in Chromium error pages.</string>
+        </property>
+        <property name="text">
+         <string>Use built-in Chromium error page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QCheckBox" name="scrollingCheckBox">
+        <property name="toolTip">
+         <string>Select to activate animated scrolling</string>
+        </property>
+        <property name="text">
+         <string>Enable animated scrolling</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QCheckBox" name="fullscreenCheckBox">
+        <property name="toolTip">
+         <string>Select to enable fullscreen support</string>
+        </property>
+        <property name="text">
+         <string>Enable Fullscreen Support</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="startupGroupBox">
+     <property name="title">
+      <string>Startup</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>On startup:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1" colspan="3">
+       <widget class="QComboBox" name="startupCombo">
+        <property name="toolTip">
+         <string>Select the startup behavior</string>
+        </property>
+        <item>
+         <property name="text">
+          <string>Show Home Page</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Show Speed Dial</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Show Empty Page</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="homePageLabel">
+        <property name="text">
+         <string>Home Page:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" colspan="3">
+       <widget class="QLineEdit" name="homePageEdit">
+        <property name="toolTip">
+         <string>Enter the desired home page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QPushButton" name="setCurrentPageButton">
+        <property name="toolTip">
+         <string>Press to set the current page as the home page</string>
+        </property>
+        <property name="text">
+         <string>Set to current page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="2">
+       <widget class="QPushButton" name="defaultHomeButton">
+        <property name="toolTip">
+         <string>Press to set the default home page</string>
+        </property>
+        <property name="text">
+         <string>Set to default home page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="3">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>160</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Scheme</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Default Scheme:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="defaultSchemeCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the default scheme</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Default Scheme&lt;/b&gt;&lt;p&gt;Select the default scheme. This scheme is prepended to URLs, that don't contain one.&lt;/p&gt;</string>
+        </property>
+        <property name="editable">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="privacyGroup">
+     <property name="title">
+      <string>Privacy</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_4">
+      <item>
+       <widget class="QGroupBox" name="javaScriptGroup">
+        <property name="toolTip">
+         <string>Select to enable JavaScript</string>
+        </property>
+        <property name="title">
+         <string>Enable JavaScript</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>false</bool>
+        </property>
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="0">
+          <widget class="QCheckBox" name="jsOpenWindowsCheckBox">
+           <property name="toolTip">
+            <string>Select to allow JavaScript to open windows</string>
+           </property>
+           <property name="text">
+            <string>JavaScript can open windows</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QCheckBox" name="jsCloseWindowsCheckBox">
+           <property name="toolTip">
+            <string>Select to allow JavaScript to close windows</string>
+           </property>
+           <property name="text">
+            <string>JavaScript can close windows</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QCheckBox" name="jsClipboardCheckBox">
+           <property name="toolTip">
+            <string>Select to allow JavaScript to access the clipboard</string>
+           </property>
+           <property name="text">
+            <string>JavaScript can access clipboard</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="pluginsCheckBox">
+        <property name="toolTip">
+         <string>Select to enable plugins in web pages</string>
+        </property>
+        <property name="text">
+         <string>Enable Plug-ins</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="Line" name="line">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="doNotTrackCheckBox">
+        <property name="toolTip">
+         <string>Select to enabled the &quot;Do Not Track&quot; feature</string>
+        </property>
+        <property name="text">
+         <string>Tell web sites I do not want to be tracked</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_4">
+        <item>
+         <widget class="QCheckBox" name="sendRefererCheckBox">
+          <property name="toolTip">
+           <string>Select to send referer headers to the server</string>
+          </property>
+          <property name="text">
+           <string>Send Referer header to servers</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_6">
+          <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="QPushButton" name="refererWhitelistButton">
+          <property name="toolTip">
+           <string>Press to edit the list of whitelisted hosts</string>
+          </property>
+          <property name="text">
+           <string>Edit Referer Whitelist ...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Security</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QCheckBox" name="xssAuditingCheckBox">
+        <property name="toolTip">
+         <string>Select to enable XSS auditing</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Enable XSS Auditing&lt;/b&gt;
+&lt;p&gt;This selects whether load requests should be monitored for cross-site scripting attempts. Suspicious scripts will be blocked. These will be reported in the JavaScript console. Enabling this feature might have an impact on performance.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Enable XSS Auditing</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>History</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Remove history items:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="expireHistory">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the period for expiration of history entries</string>
+        </property>
+        <item>
+         <property name="text">
+          <string>After one day</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one week</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After two weeks</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one month</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one year</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Manually</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>On application exit</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="cacheGroup">
+     <property name="title">
+      <string>Browser Cache</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0" colspan="3">
+       <widget class="QCheckBox" name="diskCacheCheckBox">
+        <property name="text">
+         <string>Enable disk cache</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Cache size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="cacheSizeSpinBox">
+        <property name="toolTip">
+         <string>Enter the maximum size of the disk cache</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="suffix">
+         <string> MB</string>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <number>999</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>410</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_5">
+     <property name="title">
+      <string>Web Search</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <item>
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Language:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="languageCombo">
+        <property name="toolTip">
+         <string>Select the language to be used for web searches</string>
+        </property>
+        <property name="sizeAdjustPolicy">
+         <enum>QComboBox::AdjustToContents</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_3">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>450</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Navigation</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_6">
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="spatialCheckBox">
+        <property name="toolTip">
+         <string>Select to enable the spatial navigation feature</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Enable Spatial Navigation&lt;/b&gt;
+&lt;p&gt;This enables or disables the Spatial Navigation feature, which consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using Left, Right, Up and Down arrow keys. For example, if a user presses the Right key, heuristics determine whether there is an element he might be trying to reach towards the right and which element he probably wants.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Enable Spatial Navigation</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="linksInFocusChainCheckBox">
+        <property name="toolTip">
+         <string>Select to include links in focus chain</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Include Links in Focus Chain&lt;/b&gt;
+&lt;p&gt;This selects whether hyperlinks should be included in the keyboard focus chain.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Include Links in Focus Chain</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="webInspectorGroup">
+     <property name="toolTip">
+      <string>Select to enable the Web Inspector tool</string>
+     </property>
+     <property name="title">
+      <string>Enable Web Development (Web Inspector)</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_8">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Web Inspector Port:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QSpinBox" name="webInspectorPortSpinBox">
+        <property name="toolTip">
+         <string>Enter the port to be used by the web inspector</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1025</number>
+        </property>
+        <property name="maximum">
+         <number>65535</number>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer_4">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>372</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="0" colspan="3">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>&lt;font color=&quot;red&quot;&gt;&lt;b&gt;Note:&lt;/b&gt; Web Inspector settings are activated after a restart of the application.&lt;/font&gt;</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>479</width>
+       <height>121</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>singleHelpWindowCheckBox</tabstop>
+  <tabstop>webSuggestionsCheckBox</tabstop>
+  <tabstop>saveGeometryCheckBox</tabstop>
+  <tabstop>showTabPreviews</tabstop>
+  <tabstop>errorPageCheckBox</tabstop>
+  <tabstop>scrollingCheckBox</tabstop>
+  <tabstop>fullscreenCheckBox</tabstop>
+  <tabstop>startupCombo</tabstop>
+  <tabstop>homePageEdit</tabstop>
+  <tabstop>setCurrentPageButton</tabstop>
+  <tabstop>defaultHomeButton</tabstop>
+  <tabstop>defaultSchemeCombo</tabstop>
+  <tabstop>javaScriptGroup</tabstop>
+  <tabstop>jsOpenWindowsCheckBox</tabstop>
+  <tabstop>jsCloseWindowsCheckBox</tabstop>
+  <tabstop>jsClipboardCheckBox</tabstop>
+  <tabstop>pluginsCheckBox</tabstop>
+  <tabstop>doNotTrackCheckBox</tabstop>
+  <tabstop>sendRefererCheckBox</tabstop>
+  <tabstop>refererWhitelistButton</tabstop>
+  <tabstop>xssAuditingCheckBox</tabstop>
+  <tabstop>expireHistory</tabstop>
+  <tabstop>diskCacheCheckBox</tabstop>
+  <tabstop>cacheSizeSpinBox</tabstop>
+  <tabstop>languageCombo</tabstop>
+  <tabstop>spatialCheckBox</tabstop>
+  <tabstop>linksInFocusChainCheckBox</tabstop>
+  <tabstop>webInspectorGroup</tabstop>
+  <tabstop>webInspectorPortSpinBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- a/Preferences/Shortcuts.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/Shortcuts.py	Sun Apr 03 16:33:37 2016 +0200
@@ -37,7 +37,8 @@
             act.setAlternateShortcut(QKeySequence(accel), removeEmpty=True)
 
 
-def readShortcuts(prefClass=Prefs, helpViewer=None, pluginName=None):
+def readShortcuts(prefClass=Prefs, helpViewer=None, pluginName=None,
+                  helpViewerCategory=""):
     """
     Module function to read the keyboard shortcuts for the defined QActions.
     
@@ -45,6 +46,7 @@
     @keyparam helpViewer reference to the help window object
     @keyparam pluginName name of the plugin for which to load shortcuts
         (string)
+    @keyparam helpViewerCategory name of the help viewer category (string)
     """
     if helpViewer is None and pluginName is None:
         for act in e5App().getObject("Project").getActions():
@@ -92,8 +94,10 @@
                     __readShortcut(act, category, prefClass)
     
     if helpViewer is not None:
+        if not helpViewerCategory:
+            helpViewerCategory = "HelpViewer"
         for act in helpViewer.getActions():
-            __readShortcut(act, "HelpViewer", prefClass)
+            __readShortcut(act, helpViewerCategory, prefClass)
     
     if pluginName is not None:
         try:
--- a/Preferences/__init__.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/__init__.py	Sun Apr 03 16:33:37 2016 +0200
@@ -37,6 +37,10 @@
     from PyQt5.QtWebKit import QWebSettings
 except ImportError:
     QWebSettings = None
+try:
+    from PyQt5.QtWebEngineWidgets import QWebEngineSettings
+except ImportError:
+    QWebEngineSettings = None
 from PyQt5.Qsci import QsciScintilla, QsciLexerPython
 
 from E5Gui import E5FileDialog
@@ -51,9 +55,12 @@
     ResourcesBrowserFlag, TranslationsBrowserFlag, InterfacesBrowserFlag, \
     OthersBrowserFlag, AllBrowsersFlag
 
-from Helpviewer.FlashCookieManager.FlashCookieUtilities import \
-    flashDataPathForOS
-
+try:
+    from Helpviewer.FlashCookieManager.FlashCookieUtilities import \
+        flashDataPathForOS
+except ImportError:
+    from WebBrowser.FlashCookieManager.FlashCookieUtilities import \
+        flashDataPathForOS
 
 class Prefs(object):
     """
@@ -705,6 +712,7 @@
         "RightMargin": 1.0,
         "TopMargin": 1.0,
         "BottomMargin": 1.0,
+        "Resolution": 150,      # printer resolution in DPI
     }
     
     # defaults for the project settings
@@ -835,7 +843,7 @@
         "StartupBehavior": 1,      # show speed dial
         "HomePage": "eric:home",
         "HistoryLimit": 30,
-        "DefaultScheme": "file://",
+        "DefaultScheme": "https://",
         "OfflineStorageDatabaseQuota": 50,     # 50 MB
         "UserAgent": "",
         "ShowPreview": True,
@@ -998,6 +1006,148 @@
         cls.webSettingsIntitialized = True
     
     webSettingsIntitialized = False
+    
+    # defaults for the web browser settings
+    webBrowserDefaults = {
+        "SingleWebBrowserWindow": True,
+        "SaveGeometry": True,
+        "WebBrowserState": QByteArray(),
+        "StartupBehavior": 1,      # show speed dial
+        "HomePage": "eric:home",
+        "WarnOnMultipleClose": True,
+        "DefaultScheme": "https://",
+        "UserStyleSheet": "",
+        "ZoomValuesDB": "{}",       # empty JSON dictionary
+        "HistoryLimit": 30,
+        "WebSearchSuggestions": True,
+        "WebSearchEngine": "DuckDuckGo",
+        "WebSearchKeywords": [],    # array of two tuples (keyword,
+                                    # search engine name)
+        "SearchLanguage": QLocale().language(),
+        "RssFeeds": [],
+        "ShowPreview": True,
+        "WebInspectorPort": 42024,
+        "WebInspectorEnabled": False,
+        "DiskCacheEnabled": True,
+        "DiskCacheSize": 50,        # 50 MB
+        "SslExceptionsDB": "{}",    # empty JSON dictionary
+        "DoNotTrack": False,
+        "SendReferer": True,
+        "SendRefererWhitelist": ["qt-apps.org", "kde-apps.org"],
+        "AcceptCookies": 2,         # CookieJar.AcceptOnlyFromSitesNavigatedTo
+        "KeepCookiesUntil": 0,      # CookieJar.KeepUntilExpire
+        "FilterTrackingCookies": True,
+        "SaveUrlColor": QColor(184, 248, 169),
+        "UserAgent": "",
+        # Grease Monkey
+        "GreaseMonkeyDisabledScripts": [],
+        # Downloads
+        "DownloadManagerRemovePolicy": 0,      # never delete downloads
+        "DownloadManagerSize": QSize(400, 300),
+        "DownloadManagerPosition": QPoint(),
+        "DownloadManagerDownloads": [],
+        # Sync
+        "SyncEnabled": False,
+        "SyncBookmarks": True,
+        "SyncHistory": True,
+        "SyncPasswords": False,
+        "SyncUserAgents": True,
+        "SyncSpeedDial": True,
+        "SyncEncryptData": False,
+        "SyncEncryptionKey": "",
+        "SyncEncryptionKeyLength": 32,      # 16, 24 or 32
+        "SyncEncryptPasswordsOnly": False,
+        "SyncType": 0,
+        "SyncFtpServer": "",
+        "SyncFtpUser": "",
+        "SyncFtpPassword": "",
+        "SyncFtpPath": "",
+        "SyncFtpPort": 21,
+        "SyncFtpIdleTimeout": 30,
+        "SyncDirectoryPath": "",
+        # AdBlock
+        "AdBlockEnabled": False,
+        "AdBlockSubscriptions": [],
+        "AdBlockUpdatePeriod": 1,
+        "AdBlockExceptions": [],
+        "AdBlockUseLimitedEasyList": True,
+        # Flash Cookie Manager: identical to helpDefaults
+        # PIM:                  identical to helpDefaults
+        # VirusTotal:           identical to helpDefaults
+    }
+    if QWebEngineSettings:
+        webBrowserDefaults["HelpViewerType"] = 1      # eric browser
+    else:
+        webBrowserDefaults["HelpViewerType"] = 2      # Qt Assistant
+    
+    @classmethod
+    def initWebEngineSettingsDefaults(cls):
+        """
+        Class method to initialize the web engine settings related defaults.
+        """
+        if QWebEngineSettings is None:
+            return
+        
+        webEngineSettings = QWebEngineSettings.globalSettings()
+        cls.webBrowserDefaults.update({
+            "StandardFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.StandardFont),
+            "FixedFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.FixedFont),
+            "SerifFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.StandardFont),
+            "SansSerifFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.SansSerifFont),
+            "CursiveFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.CursiveFont),
+            "FantasyFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.FantasyFont),
+            "DefaultFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.DefaultFontSize),
+            "DefaultFixedFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.DefaultFixedFontSize),
+            "MinimumFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.MinimumFontSize),
+            "MinimumLogicalFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.MinimumLogicalFontSize),
+            
+            "AutoLoadImages": webEngineSettings.testAttribute(
+                QWebEngineSettings.AutoLoadImages),
+            "JavaScriptEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptEnabled),
+            "JavaScriptCanOpenWindows": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanOpenWindows),
+            # TODO: Qt 5.7?
+##            "JavaScriptCanCloseWindows": webEngineSettings.testAttribute(
+##                QWebEngineSettings.JavascriptCanCloseWindows),
+            "JavaScriptCanAccessClipboard": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanAccessClipboard),
+            "PluginsEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.PluginsEnabled),
+            "LocalStorageEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalStorageEnabled),
+            "DefaultTextEncoding": webEngineSettings.defaultTextEncoding(),
+            "SpatialNavigationEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.SpatialNavigationEnabled),
+            "LinksIncludedInFocusChain": webEngineSettings.testAttribute(
+                QWebEngineSettings.LinksIncludedInFocusChain),
+            "LocalContentCanAccessRemoteUrls": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalContentCanAccessRemoteUrls),
+            "LocalContentCanAccessFileUrls": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalContentCanAccessFileUrls),
+            "XSSAuditingEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.XSSAuditingEnabled),
+            "ScrollAnimatorEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.ScrollAnimatorEnabled),
+            "ErrorPageEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.ErrorPageEnabled),
+            "FullScreenSupportEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.FullScreenSupportEnabled),
+        })
+        
+        cls.webEngineSettingsIntitialized = True
+    
+    webEngineSettingsIntitialized = False
 
     # defaults for system settings
     sysDefaults = {
@@ -1128,10 +1278,12 @@
     geometryDefaults = {
         "HelpViewerGeometry": QByteArray(),
         "HelpInspectorGeometry": QByteArray(),
+        "WebBrowserGeometry": QByteArray(),
         "IconEditorGeometry": QByteArray(),
         "HexEditorGeometry": QByteArray(),
         "MainGeometry": QByteArray(),
         "MainMaximized": False,
+        "WebInspectorGeometry": QByteArray(),
     }
 
     # if true, revert layouts to factory defaults
@@ -2141,7 +2293,7 @@
     if key in ["ColorMode", "FirstPageFirst"]:
         return toBool(prefClass.settings.value(
             "Printer/" + key, prefClass.printerDefaults[key]))
-    elif key in ["Magnification", "Orientation", "PageSize"]:
+    elif key in ["Magnification", "Orientation", "PageSize", "Resolution"]:
         return int(prefClass.settings.value(
             "Printer/" + key, prefClass.printerDefaults[key]))
     elif key in ["LeftMargin", "RightMargin", "TopMargin", "BottomMargin"]:
@@ -2543,6 +2695,191 @@
             "Help/" + key, pwConvert(value, encode=True))
     else:
         prefClass.settings.setValue("Help/" + key, value)
+
+
+def getWebBrowser(key, prefClass=Prefs):
+    """
+    Module function to retrieve the various web browser settings.
+    
+    @param key the key of the value to get
+    @param prefClass preferences class used as the storage area
+    @return the requested help setting
+    """
+    # the following entries are identical to the ones of the QtWebKit based
+    # help viewer and are being redirected there
+    if key.startswith(("FlashCookie", "Pim", "VirusTotal")):
+        return getHelp(key, prefClass)
+    
+    # Web inspector stuff must come before initializing web engine settings
+    # because that starts the chromium web process
+    if key == "WebInspectorPort":
+        return int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    elif key == "WebInspectorEnabled":
+        return toBool(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    
+    if not prefClass.webEngineSettingsIntitialized:
+        prefClass.initWebEngineSettingsDefaults()
+    
+    if key in ["StandardFont", "FixedFont"]:
+        f = QFont()
+        f.fromString(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+        return f
+    elif key in ["SaveUrlColor"]:
+        col = prefClass.settings.value("WebBrowser/" + key)
+        if col is not None:
+            return QColor(col)
+        else:
+            return prefClass.webBrowserDefaults[key]
+    elif key in ["WebSearchKeywords"]:
+        # return a list of tuples of (keyword, engine name)
+        keywords = []
+        size = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(size):
+            prefClass.settings.setArrayIndex(index)
+            keyword = prefClass.settings.value("Keyword")
+            engineName = prefClass.settings.value("Engine")
+            keywords.append((keyword, engineName))
+        prefClass.settings.endArray()
+        return keywords
+    elif key in ["DownloadManagerDownloads"]:
+        # return a list of tuples of (URL, save location, done flag, page url)
+        downloads = []
+        length = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(length):
+            prefClass.settings.setArrayIndex(index)
+            url = prefClass.settings.value("URL")
+            location = prefClass.settings.value("Location")
+            done = toBool(prefClass.settings.value("Done"))
+            pageUrl = prefClass.settings.value("PageURL")
+            if pageUrl is None:
+                pageUrl = QUrl()
+            downloads.append((url, location, done, pageUrl))
+        prefClass.settings.endArray()
+        return downloads
+    elif key == "RssFeeds":
+        # return a list of tuples of (URL, title, icon)
+        feeds = []
+        length = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(length):
+            prefClass.settings.setArrayIndex(index)
+            url = prefClass.settings.value("URL")
+            title = prefClass.settings.value("Title")
+            icon = prefClass.settings.value("Icon")
+            feeds.append((url, title, icon))
+        prefClass.settings.endArray()
+        return feeds
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        return pwConvert(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]), encode=False)
+    elif key == "HelpViewerType":
+        # special treatment to adjust for missing QtWebEngine
+        value = int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+        if QWebEngineSettings is None:
+            value = prefClass.helpDefaults[key]
+        return value
+    elif key in ["StartupBehavior", "HistoryLimit",
+                 "DownloadManagerRemovePolicy","SyncType", "SyncFtpPort",
+                 "SyncFtpIdleTimeout", "SyncEncryptionKeyLength",
+                 "SearchLanguage", "WebInspectorPort",
+                 "DefaultFontSize", "DefaultFixedFontSize",
+                 "MinimumFontSize", "MinimumLogicalFontSize",
+                 "DiskCacheSize", "AcceptCookies", "KeepCookiesUntil",
+                 "AdBlockUpdatePeriod",
+                 ]:
+        return int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    elif key in ["SingleWebBrowserWindow", "SaveGeometry", "JavaScriptEnabled",
+                 "JavaScriptCanOpenWindows", "JavaScriptCanAccessClipboard",
+                 "AutoLoadImages", "LocalStorageEnabled",
+                 "SpatialNavigationEnabled", "LinksIncludedInFocusChain",
+                 "LocalContentCanAccessRemoteUrls",
+                 "LocalContentCanAccessFileUrls", "XSSAuditingEnabled",
+                 "ScrollAnimatorEnabled", "ErrorPageEnabled",
+                 "WarnOnMultipleClose", "WebSearchSuggestions",
+                 "SyncEnabled", "SyncBookmarks", "SyncHistory",
+                 "SyncPasswords", "SyncUserAgents", "SyncSpeedDial",
+                 "SyncEncryptData", "SyncEncryptPasswordsOnly",
+                 "ShowPreview", "WebInspectorEnabled", "DiskCacheEnabled",
+                 "DoNotTrack", "SendReferer", "FilterTrackingCookies",
+                 "AdBlockEnabled", "AdBlockUseLimitedEasyList",
+                 "PluginsEnabled", "FullScreenSupportEnabled",
+                 ]:
+        return toBool(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    elif key in ["GreaseMonkeyDisabledScripts", "SendRefererWhitelist",
+                 "AdBlockSubscriptions", "AdBlockExceptions",
+                 ]:
+        return toList(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+    else:
+        return prefClass.settings.value("WebBrowser/" + key,
+                                        prefClass.webBrowserDefaults[key])
+    
+
+def setWebBrowser(key, value, prefClass=Prefs):
+    """
+    Module function to store the various web browser settings.
+    
+    @param key the key of the setting to be set
+    @param value the value to be set
+    @param prefClass preferences class used as the storage area
+    """
+    # the following entries are identical to the ones of the QtWebKit based
+    # help viewer and are being redirected there
+    if key.startswith(("FlashCookie", "Pim", "VirusTotal")):
+        setHelp(key, value, prefClass)
+    
+    if key in ["StandardFont", "FixedFont"]:
+        prefClass.settings.setValue("WebBrowser/" + key, value.toString())
+    elif key == "SaveUrlColor":
+        prefClass.settings.setValue("WebBrowser/" + key, value.name())
+    elif key == "WebSearchKeywords":
+        # value is list of tuples of (keyword, engine name)
+        prefClass.settings.remove("WebBrowser/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("Keyword", v[0])
+            prefClass.settings.setValue("Engine", v[1])
+            index += 1
+        prefClass.settings.endArray()
+    elif key == "DownloadManagerDownloads":
+        # value is list of tuples of (URL, save location, done flag, page url)
+        prefClass.settings.remove("Help/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("URL", v[0])
+            prefClass.settings.setValue("Location", v[1])
+            prefClass.settings.setValue("Done", v[2])
+            prefClass.settings.setValue("PageURL", v[3])
+            index += 1
+        prefClass.settings.endArray()
+    elif key == "RssFeeds":
+        # value is list of tuples of (URL, title, icon)
+        prefClass.settings.remove("WebBrowser/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("URL", v[0])
+            prefClass.settings.setValue("Title", v[1])
+            prefClass.settings.setValue("Icon", v[2])
+            index += 1
+        prefClass.settings.endArray()
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        prefClass.settings.setValue(
+            "WebBrowser/" + key, pwConvert(value, encode=True))
+    else:
+        prefClass.settings.setValue("WebBrowser/" + key, value)
     
 
 def getSystem(key, prefClass=Prefs):
@@ -3198,6 +3535,16 @@
                 newPassword
             )
         )
+    for key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        prefClass.settings.setValue(
+            "WebBrowser/" + key,
+            pwRecode(
+                prefClass.settings.value("WebBrowser/" + key,
+                                         prefClass.webBrowserDefaults[key]),
+                oldPassword,
+                newPassword
+            )
+        )
 
 
 initPreferences()
--- a/UI/UserInterface.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/UI/UserInterface.py	Sun Apr 03 16:33:37 2016 +0200
@@ -9,7 +9,7 @@
 
 from __future__ import unicode_literals
 try:
-    str = unicode
+    str = unicode       # __IGNORE_EXCEPTION__
 except NameError:
     pass
 
@@ -32,6 +32,14 @@
     WEBKIT_AVAILABLE = True
 except ImportError:
     WEBKIT_AVAILABLE = False
+if qVersion() < "5.6.0":
+    WEBENGINE_AVAILABLE = False
+else:
+    try:
+        from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+        WEBENGINE_AVAILABLE = True
+    except ImportError:
+        WEBENGINE_AVAILABLE = False
 
 from .Info import Version, BugAddress, Program, FeatureAddress
 from . import Config
@@ -468,10 +476,16 @@
         self.__initExternalToolsActions()
         
         # create a dummy help window for shortcuts handling
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE:
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            self.dummyHelpViewer = \
+                WebBrowserWindow(None, '.', None, 'web_browser', True, True)
+        elif WEBKIT_AVAILABLE:
             from Helpviewer.HelpWindow import HelpWindow
             self.dummyHelpViewer = \
                 HelpWindow(None, '.', None, 'help viewer', True, True)
+        else:
+            self.dummyHelpViewer = None
         
         # register all relevant objects
         splash.showMessage(self.tr("Registering Objects..."))
@@ -486,7 +500,7 @@
         e5App().registerObject("TaskViewer", self.taskViewer)
         e5App().registerObject("TemplateViewer", self.templateViewer)
         e5App().registerObject("Shell", self.shell)
-        if WEBKIT_AVAILABLE:
+        if self.dummyHelpViewer is not None:
             e5App().registerObject("DummyHelpViewer", self.dummyHelpViewer)
         e5App().registerObject("PluginManager", self.pluginManager)
         e5App().registerObject("ToolbarManager", self.toolbarManager)
@@ -1596,7 +1610,7 @@
         self.whatsThisAct.triggered.connect(self.__whatsThis)
         self.actions.append(self.whatsThisAct)
 
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
             self.helpviewerAct = E5Action(
                 self.tr('Helpviewer'),
                 UI.PixmapCache.getIcon("help.png"),
@@ -1925,7 +1939,7 @@
         self.hexEditorAct.triggered.connect(self.__openHexEditor)
         self.actions.append(self.hexEditorAct)
 
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
             self.webBrowserAct = E5Action(
                 self.tr('eric6 Web Browser'),
                 UI.PixmapCache.getIcon("ericWeb.png"),
@@ -3002,12 +3016,16 @@
             .format(sip_version_str)
         versionText += """<tr><td><b>QScintilla</b></td><td>{0}</td></tr>"""\
             .format(QSCINTILLA_VERSION_STR)
-        try:
+        if WEBENGINE_AVAILABLE:
+            from WebBrowser.Tools import WebBrowserTools
+            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
+            versionText += \
+                """<tr><td><b>WebEngine</b></td><td>{0}</td></tr>"""\
+                .format(chromeVersion)
+        if WEBKIT_AVAILABLE:
             from PyQt5.QtWebKit import qWebKitVersion
             versionText += """<tr><td><b>WebKit</b></td><td>{0}</td></tr>"""\
                 .format(qWebKitVersion())
-        except ImportError:
-            pass
         versionText += """<tr><td><b>{0}</b></td><td>{1}</td></tr>"""\
             .format(Program, Version)
         versionText += self.tr("""</table>""")
@@ -4846,7 +4864,10 @@
         if home.endswith(".chm"):
             self.__chmViewer(home)
         else:
-            hvType = Preferences.getHelp("HelpViewerType")
+            if WEBENGINE_AVAILABLE:
+                hvType = Preferences.getWebBrowser("HelpViewerType")
+            else:
+                hvType = Preferences.getHelp("HelpViewerType")
             if hvType == 1:
                 self.launchHelpViewer(home)
             elif hvType == 2:
@@ -4905,7 +4926,10 @@
         if home.endswith(".chm"):
             self.__chmViewer(home)
         else:
-            hvType = Preferences.getHelp("HelpViewerType")
+            if WEBENGINE_AVAILABLE:
+                hvType = Preferences.getWebBrowser("HelpViewerType")
+            else:
+                hvType = Preferences.getHelp("HelpViewerType")
             if hvType == 1:
                 self.launchHelpViewer(home)
             elif hvType == 2:
@@ -4971,7 +4995,10 @@
             else:
                 home = "file://" + home
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5033,7 +5060,10 @@
         else:
             home = pyqt4DocDir
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5097,7 +5127,10 @@
         else:
             home = pyqt5DocDir
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5134,7 +5167,10 @@
             else:
                 home = "file://" + home
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5186,7 +5222,10 @@
         else:
             home = pysideDocDir
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5213,12 +5252,22 @@
             if not homeUrl.scheme():
                 home = QUrl.fromLocalFile(home).toString()
         
-        if WEBKIT_AVAILABLE:
-            if not (useSingle or Preferences.getHelp("SingleHelpWindow")) or \
-               self.helpWindow is None:
-                from Helpviewer.HelpWindow import HelpWindow
-                help = HelpWindow(home, '.', None, 'help viewer', True,
-                                  searchWord=searchWord)
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
+            single = useSingle
+            if WEBENGINE_AVAILABLE:
+                single = single or \
+                    Preferences.getWebBrowser("SingleWebBrowserWindow")
+            elif WEBKIT_AVAILABLE:
+                single = single or Preferences.getHelp("SingleHelpWindow")
+            if not single or self.helpWindow is None:
+                if WEBENGINE_AVAILABLE:
+                    from WebBrowser.WebBrowserWindow import WebBrowserWindow
+                    help = WebBrowserWindow(home, '.', None, 'web_browser',
+                                            True, searchWord=searchWord)
+                elif WEBKIT_AVAILABLE:
+                    from Helpviewer.HelpWindow import HelpWindow
+                    help = HelpWindow(home, '.', None, 'help viewer', True,
+                                      searchWord=searchWord)
 
                 if QApplication.desktop().width() > 400 and \
                    QApplication.desktop().height() > 500:
@@ -5226,9 +5275,13 @@
                 else:
                     help.showMaximized()
                 
-                if useSingle or Preferences.getHelp("SingleHelpWindow"):
+                if single:
                     self.helpWindow = help
-                    self.helpWindow.helpClosed.connect(self.__helpClosed)
+                    try:
+                        self.helpWindow.webBrowserClosed.connect(
+                            self.__helpClosed)
+                    except AttributeError:
+                        self.helpWindow.helpClosed.connect(self.__helpClosed)
                     self.preferencesChanged.connect(
                         self.helpWindow.preferencesChanged)
                     self.masterPasswordChanged.connect(
@@ -5246,7 +5299,11 @@
         """
         Private slot to handle the helpClosed signal of the help window.
         """
-        if Preferences.getHelp("SingleHelpWindow"):
+        if WEBENGINE_AVAILABLE:
+            single = Preferences.getWebBrowser("SingleWebBrowserWindow")
+        elif WEBKIT_AVAILABLE:
+            single = Preferences.getHelp("SingleHelpWindow")
+        if single:
             self.preferencesChanged.disconnect(
                 self.helpWindow.preferencesChanged)
             self.masterPasswordChanged.disconnect(
@@ -5284,7 +5341,7 @@
             (boolean)
         @return reference to the help window instance (HelpWindow)
         """
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
             if self.helpWindow is None:
                 self.launchHelpViewer("", useSingle=True)
             self.helpWindow.raise_()
@@ -5304,6 +5361,7 @@
         dlg = ConfigurationDialog(
             self, 'Configuration',
             expandedEntries=self.__expandedConfigurationEntries,
+            webEngine=WEBENGINE_AVAILABLE,
         )
         dlg.preferencesChanged.connect(self.__preferencesChanged)
         dlg.masterPasswordChanged.connect(self.__masterPasswordChanged)
--- a/Utilities/__init__.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Utilities/__init__.py	Sun Apr 03 16:33:37 2016 +0200
@@ -1682,6 +1682,14 @@
         qVersion(), linesep, PYQT_VERSION_STR, linesep)
     info += "  sip {0}{1}  QScintilla {2}{3}".format(
         sip_version_str, linesep, QSCINTILLA_VERSION_STR, linesep)
+    if qVersion() >= "5.6.0":
+        try:
+            from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+            from WebBrowser.Tools import WebBrowserTools
+            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
+            info += "  WebEngine {0}{1}".format(chromeVersion, linesep)
+        except ImportError:
+            pass
     try:
         from PyQt5.QtWebKit import qWebKitVersion
         info += "  WebKit {0}{1}".format(qWebKitVersion(), linesep)
--- a/ViewManager/ViewManager.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/ViewManager/ViewManager.py	Sun Apr 03 16:33:37 2016 +0200
@@ -11,7 +11,7 @@
 
 import os
 
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSignalMapper, QTimer, \
+from PyQt5.QtCore import pyqtSignal, QSignalMapper, QTimer, \
     QFileInfo, QRegExp, Qt, QCoreApplication
 from PyQt5.QtGui import QColor, QKeySequence, QPalette, QPixmap
 from PyQt5.QtWidgets import QLineEdit, QToolBar, QWidgetAction, QDialog, \
@@ -6574,7 +6574,6 @@
         if editor:
             self.editorRenamedEd.emit(editor)
         
-##    @pyqtSlot(str, int, int)
     def __cursorChanged(self, fn, line, pos):
         """
         Private slot to handle the cursorChanged signal.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,346 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QTimer, QCoreApplication
+from PyQt5.QtWidgets import QDialog, QMenu, QToolButton
+
+from E5Gui import E5MessageBox
+
+from .Ui_AdBlockDialog import Ui_AdBlockDialog
+
+import UI.PixmapCache
+import Preferences
+
+
+class AdBlockDialog(QDialog, Ui_AdBlockDialog):
+    """
+    Class implementing the AdBlock configuration dialog.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the AdBlock manager (AdBlockManager)
+        @param parent reference to the parent object (QWidget)
+        """
+        super(AdBlockDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__manager = manager
+        
+        self.iconLabel.setPixmap(UI.PixmapCache.getPixmap("adBlockPlus48.png"))
+        
+        self.updateSpinBox.setValue(
+            Preferences.getWebBrowser("AdBlockUpdatePeriod"))
+        
+        self.useLimitedEasyListCheckBox.setChecked(Preferences.getWebBrowser(
+            "AdBlockUseLimitedEasyList"))
+        
+        self.searchEdit.setInactiveText(self.tr("Search..."))
+        
+        self.adBlockGroup.setChecked(self.__manager.isEnabled())
+        self.__manager.requiredSubscriptionLoaded.connect(self.addSubscription)
+        
+        self.__currentTreeWidget = None
+        self.__currentSubscription = None
+        self.__loaded = False
+        
+        menu = QMenu(self)
+        menu.aboutToShow.connect(self.__aboutToShowActionMenu)
+        self.actionButton.setMenu(menu)
+        self.actionButton.setIcon(UI.PixmapCache.getIcon("adBlockAction.png"))
+        self.actionButton.setPopupMode(QToolButton.InstantPopup)
+        
+        self.__load()
+        
+        self.buttonBox.setFocus()
+    
+    def __loadSubscriptions(self):
+        """
+        Private slot to load the AdBlock subscription rules.
+        """
+        for index in range(self.subscriptionsTabWidget.count()):
+            tree = self.subscriptionsTabWidget.widget(index)
+            tree.refresh()
+    
+    def __load(self):
+        """
+        Private slot to populate the tab widget with subscriptions.
+        """
+        if self.__loaded or not self.adBlockGroup.isChecked():
+            return
+        
+        from .AdBlockTreeWidget import AdBlockTreeWidget
+        for subscription in self.__manager.subscriptions():
+            tree = AdBlockTreeWidget(subscription, self.subscriptionsTabWidget)
+            if subscription.isEnabled():
+                icon = UI.PixmapCache.getIcon("adBlockPlus.png")
+            else:
+                icon = UI.PixmapCache.getIcon("adBlockPlusDisabled.png")
+            self.subscriptionsTabWidget.addTab(
+                tree, icon, subscription.title())
+        
+        self.__loaded = True
+        QCoreApplication.processEvents()
+        
+        QTimer.singleShot(50, self.__loadSubscriptions)
+    
+    def addSubscription(self, subscription, refresh=True):
+        """
+        Public slot adding a subscription to the list.
+        
+        @param subscription reference to the subscription to be
+            added (AdBlockSubscription)
+        @param refresh flag indicating to refresh the tree (boolean)
+        """
+        from .AdBlockTreeWidget import AdBlockTreeWidget
+        tree = AdBlockTreeWidget(subscription, self.subscriptionsTabWidget)
+        index = self.subscriptionsTabWidget.insertTab(
+            self.subscriptionsTabWidget.count() - 1, tree,
+            subscription.title())
+        self.subscriptionsTabWidget.setCurrentIndex(index)
+        QCoreApplication.processEvents()
+        if refresh:
+            tree.refresh()
+        self.__setSubscriptionEnabled(subscription, True)
+    
+    def __aboutToShowActionMenu(self):
+        """
+        Private slot to show the actions menu.
+        """
+        subscriptionEditable = self.__currentSubscription and \
+            self.__currentSubscription.canEditRules()
+        subscriptionRemovable = self.__currentSubscription and \
+            self.__currentSubscription.canBeRemoved()
+        subscriptionEnabled = self.__currentSubscription and \
+            self.__currentSubscription.isEnabled()
+        
+        menu = self.actionButton.menu()
+        menu.clear()
+        
+        menu.addAction(self.tr("Add Rule"), self.__addCustomRule)\
+            .setEnabled(subscriptionEditable)
+        menu.addAction(self.tr("Remove Rule"), self.__removeCustomRule)\
+            .setEnabled(subscriptionEditable)
+        menu.addSeparator()
+        menu.addAction(
+            self.tr("Browse Subscriptions..."), self.__browseSubscriptions)
+        menu.addAction(
+            self.tr("Remove Subscription"), self.__removeSubscription)\
+            .setEnabled(subscriptionRemovable)
+        if self.__currentSubscription:
+            menu.addSeparator()
+            if subscriptionEnabled:
+                txt = self.tr("Disable Subscription")
+            else:
+                txt = self.tr("Enable Subscription")
+            menu.addAction(txt, self.__switchSubscriptionEnabled)
+        menu.addSeparator()
+        menu.addAction(
+            self.tr("Update Subscription"), self.__updateSubscription)\
+            .setEnabled(not subscriptionEditable)
+        menu.addAction(
+            self.tr("Update All Subscriptions"),
+            self.__updateAllSubscriptions)
+        menu.addSeparator()
+        menu.addAction(self.tr("Learn more about writing rules..."),
+                       self.__learnAboutWritingFilters)
+    
+    def addCustomRule(self, filter):
+        """
+        Public slot to add a custom AdBlock rule.
+        
+        @param filter filter to be added (string)
+        """
+        self.subscriptionsTabWidget.setCurrentIndex(
+            self.subscriptionsTabWidget.count() - 1)
+        self.__currentTreeWidget.addRule(filter)
+    
+    def __addCustomRule(self):
+        """
+        Private slot to add a custom AdBlock rule.
+        """
+        self.__currentTreeWidget.addRule()
+    
+    def __removeCustomRule(self):
+        """
+        Private slot to remove a custom AdBlock rule.
+        """
+        self.__currentTreeWidget.removeRule()
+    
+    def __updateSubscription(self):
+        """
+        Private slot to update the selected subscription.
+        """
+        self.__currentSubscription.updateNow()
+    
+    def __updateAllSubscriptions(self):
+        """
+        Private slot to update all subscriptions.
+        """
+        self.__manager.updateAllSubscriptions()
+    
+    def __browseSubscriptions(self):
+        """
+        Private slot to browse the list of available AdBlock subscriptions.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        mw = WebBrowserWindow.mainWindow()
+        mw.newTab("http://adblockplus.org/en/subscriptions")
+        mw.raise_()
+    
+    def __learnAboutWritingFilters(self):
+        """
+        Private slot to show the web page about how to write filters.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        mw = WebBrowserWindow.mainWindow()
+        mw.newTab("http://adblockplus.org/en/filters")
+        mw.raise_()
+    
+    def __removeSubscription(self):
+        """
+        Private slot to remove the selected subscription.
+        """
+        requiresTitles = []
+        requiresSubscriptions = \
+            self.__manager.getRequiresSubscriptions(self.__currentSubscription)
+        for subscription in requiresSubscriptions:
+            requiresTitles.append(subscription.title())
+        if requiresTitles:
+            message = self.tr(
+                "<p>Do you really want to remove subscription"
+                " <b>{0}</b> and all subscriptions requiring it?</p>"
+                "<ul><li>{1}</li></ul>").format(
+                self.__currentSubscription.title(),
+                "</li><li>".join(requiresTitles))
+        else:
+            message = self.tr(
+                "<p>Do you really want to remove subscription"
+                " <b>{0}</b>?</p>").format(self.__currentSubscription.title())
+        res = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Subscription"),
+            message)
+        
+        if res:
+            removeSubscription = self.__currentSubscription
+            removeTrees = [self.__currentTreeWidget]
+            for index in range(self.subscriptionsTabWidget.count()):
+                tree = self.subscriptionsTabWidget.widget(index)
+                if tree.subscription() in requiresSubscriptions:
+                    removeTrees.append(tree)
+            for tree in removeTrees:
+                self.subscriptionsTabWidget.removeTab(
+                    self.subscriptionsTabWidget.indexOf(tree))
+            self.__manager.removeSubscription(removeSubscription)
+    
+    def __switchSubscriptionEnabled(self):
+        """
+        Private slot to switch the enabled state of the selected subscription.
+        """
+        newState = not self.__currentSubscription.isEnabled()
+        self.__setSubscriptionEnabled(self.__currentSubscription, newState)
+    
+    def __setSubscriptionEnabled(self, subscription, enable):
+        """
+        Private slot to set the enabled state of a subscription.
+        
+        @param subscription subscription to set the state for
+            (AdBlockSubscription)
+        @param enable state to set to (boolean)
+        """
+        if enable:
+            # enable required one as well
+            sub = self.__manager.subscription(subscription.requiresLocation())
+            requiresSubscriptions = [] if sub is None else [sub]
+            icon = UI.PixmapCache.getIcon("adBlockPlus.png")
+        else:
+            # disable dependent ones as well
+            requiresSubscriptions = \
+                self.__manager.getRequiresSubscriptions(subscription)
+            icon = UI.PixmapCache.getIcon("adBlockPlusDisabled.png")
+        requiresSubscriptions.append(subscription)
+        for sub in requiresSubscriptions:
+            sub.setEnabled(enable)
+        
+        for index in range(self.subscriptionsTabWidget.count()):
+            tree = self.subscriptionsTabWidget.widget(index)
+            if tree.subscription() in requiresSubscriptions:
+                self.subscriptionsTabWidget.setTabIcon(
+                    self.subscriptionsTabWidget.indexOf(tree), icon)
+    
+    @pyqtSlot(int)
+    def on_updateSpinBox_valueChanged(self, value):
+        """
+        Private slot to handle changes of the update period.
+        
+        @param value update period (integer)
+        """
+        if value != Preferences.getWebBrowser("AdBlockUpdatePeriod"):
+            Preferences.setWebBrowser("AdBlockUpdatePeriod", value)
+            
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            manager = WebBrowserWindow.adBlockManager()
+            for subscription in manager.subscriptions():
+                subscription.checkForUpdate()
+    
+    @pyqtSlot(int)
+    def on_subscriptionsTabWidget_currentChanged(self, index):
+        """
+        Private slot handling the selection of another tab.
+        
+        @param index index of the new current tab (integer)
+        """
+        if index != -1:
+            self.__currentTreeWidget = \
+                self.subscriptionsTabWidget.widget(index)
+            self.__currentSubscription = \
+                self.__currentTreeWidget.subscription()
+            
+            isEasyList = \
+                self.__currentSubscription.url().toString().startswith(
+                    self.__manager.getDefaultSubscriptionUrl())
+            self.useLimitedEasyListCheckBox.setVisible(isEasyList)
+    
+    @pyqtSlot(str)
+    def on_searchEdit_textChanged(self, filter):
+        """
+        Private slot to set a new filter on the current widget.
+        
+        @param filter filter to be set (string)
+        """
+        if self.__currentTreeWidget and self.adBlockGroup.isChecked():
+            self.__currentTreeWidget.filterString(filter)
+    
+    @pyqtSlot(bool)
+    def on_adBlockGroup_toggled(self, state):
+        """
+        Private slot handling the enabling/disabling of AdBlock.
+        
+        @param state state of the toggle (boolean)
+        """
+        self.__manager.setEnabled(state)
+        
+        if state:
+            self.__load()
+    
+    @pyqtSlot(bool)
+    def on_useLimitedEasyListCheckBox_clicked(self, checked):
+        """
+        Private slot handling the selection of the limited EasyList.
+        
+        @param checked flag indicating the state of the check box
+        @type bool
+        """
+        self.__manager.setUseLimitedEasyList(
+            self.useLimitedEasyListCheckBox.isChecked())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AdBlockDialog</class>
+ <widget class="QDialog" name="AdBlockDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>AdBlock Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="adBlockGroup">
+     <property name="title">
+      <string>Enable AdBlock</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QGridLayout" name="gridLayout">
+        <property name="horizontalSpacing">
+         <number>20</number>
+        </property>
+        <item row="0" column="0" rowspan="2">
+         <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 row="0" column="1">
+         <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 row="1" column="1">
+         <widget class="E5ClearableLineEdit" name="searchEdit">
+          <property name="toolTip">
+           <string>Enter search term for subscriptions and rules</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QTabWidget" name="subscriptionsTabWidget">
+        <property name="documentMode">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QToolButton" name="actionButton">
+          <property name="text">
+           <string>Actions</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>
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>Default Update Period (days):</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QSpinBox" name="updateSpinBox">
+          <property name="toolTip">
+           <string>Enter the update period (1 to 14 days)</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="suffix">
+           <string/>
+          </property>
+          <property name="minimum">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <number>14</number>
+          </property>
+          <property name="value">
+           <number>7</number>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="useLimitedEasyListCheckBox">
+        <property name="toolTip">
+         <string/>
+        </property>
+        <property name="text">
+         <string>Use only essential part of EasyList (for performance reasons)</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </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>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>adBlockGroup</tabstop>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>subscriptionsTabWidget</tabstop>
+  <tabstop>actionButton</tabstop>
+  <tabstop>updateSpinBox</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AdBlockDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>252</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AdBlockDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>445</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/AdBlock/AdBlockExceptionsDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to configure the AdBlock exceptions.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_AdBlockExceptionsDialog import Ui_AdBlockExceptionsDialog
+
+import UI.PixmapCache
+
+
+class AdBlockExceptionsDialog(QDialog, Ui_AdBlockExceptionsDialog):
+    """
+    Class implementing a dialog to configure the AdBlock exceptions.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(AdBlockExceptionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("adBlockPlusGreen48.png"))
+        
+        self.hostEdit.setInactiveText(self.tr("Enter host to be added..."))
+        
+        self.buttonBox.setFocus()
+    
+    def load(self, hosts):
+        """
+        Public slot to load the list of excepted hosts.
+        
+        @param hosts list of excepted hosts
+        """
+        self.hostList.clear()
+        self.hostList.addItems(hosts)
+    
+    @pyqtSlot(str)
+    def on_hostEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the host edit.
+        
+        @param txt text of the edit (string)
+        """
+        self.addButton.setEnabled(bool(txt))
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to handle a click of the add button.
+        """
+        self.hostList.addItem(self.hostEdit.text())
+        self.hostEdit.clear()
+    
+    @pyqtSlot()
+    def on_hostList_itemSelectionChanged(self):
+        """
+        Private slot handling a change of the number of selected items.
+        """
+        self.deleteButton.setEnabled(len(self.hostList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def on_deleteButton_clicked(self):
+        """
+        Private slot handling a click of the delete button.
+        """
+        for itm in self.hostList.selectedItems():
+            row = self.hostList.row(itm)
+            removedItem = self.hostList.takeItem(row)
+            del removedItem
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        hosts = []
+        for row in range(self.hostList.count()):
+            hosts.append(self.hostList.item(row).text())
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.adBlockManager().setExceptions(hosts)
+        
+        super(AdBlockExceptionsDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockExceptionsDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AdBlockExceptionsDialog</class>
+ <widget class="QDialog" name="AdBlockExceptionsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>AdBlock Exceptions</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" rowspan="2">
+      <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 row="0" column="1">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>188</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="1" column="1">
+      <widget class="E5ClearableLineEdit" name="hostEdit">
+       <property name="toolTip">
+        <string>Enter a host to block AdBlock for</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QPushButton" name="addButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to add the host</string>
+       </property>
+       <property name="text">
+        <string>&amp;Add</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0" rowspan="2" colspan="2">
+      <widget class="QListWidget" name="hostList">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QPushButton" name="deleteButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to delete the selected hosts</string>
+       </property>
+       <property name="text">
+        <string>&amp;Delete</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="2">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>148</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>hostEdit</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>hostList</tabstop>
+  <tabstop>deleteButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AdBlockExceptionsDialog</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>AdBlockExceptionsDialog</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/AdBlock/AdBlockIcon.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock icon for the main window status bar.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QAction, QMenu
+
+from E5Gui.E5ClickableLabel import E5ClickableLabel
+
+import UI.PixmapCache
+
+
+class AdBlockIcon(E5ClickableLabel):
+    """
+    Class implementing the AdBlock icon for the main window status bar.
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (HelpWindow)
+        """
+        super(AdBlockIcon, self).__init__(parent)
+        
+        self.__mw = parent
+        self.__menuAction = None
+        self.__enabled = False
+        
+        self.setMaximumHeight(16)
+        self.setCursor(Qt.PointingHandCursor)
+        self.setToolTip(self.tr(
+            "AdBlock lets you block unwanted content on web pages."))
+        
+        self.clicked.connect(self.__showMenu)
+    
+    def setEnabled(self, enabled):
+        """
+        Public slot to set the enabled state.
+        
+        @param enabled enabled state (boolean)
+        """
+        self.__enabled = enabled
+        if enabled:
+            self.currentChanged()
+        else:
+            self.setPixmap(
+                UI.PixmapCache.getPixmap("adBlockPlusDisabled16.png"))
+    
+    def __createMenu(self, menu=None):
+        """
+        Private slot to create the context menu.
+        
+        @param menu parent menu (QMenu)
+        """
+        if menu is None:
+            menu = self.sender()
+            if menu is None:
+                return
+        
+        menu.clear()
+        
+        manager = self.__mw.adBlockManager()
+        
+        if manager.isEnabled():
+            menu.addAction(
+                UI.PixmapCache.getIcon("adBlockPlusDisabled.png"),
+                self.tr("Disable AdBlock"),
+                self.__enableAdBlock).setData(False)
+        else:
+            menu.addAction(
+                UI.PixmapCache.getIcon("adBlockPlus.png"),
+                self.tr("Enable AdBlock"),
+                self.__enableAdBlock).setData(True)
+        menu.addSeparator()
+        if manager.isEnabled() and self.__mw.currentBrowser().url().host():
+            if self.__isCurrentHostExcepted():
+                menu.addAction(
+                    UI.PixmapCache.getIcon("adBlockPlus.png"),
+                    self.tr("Remove AdBlock Exception"),
+                    self.__setException).setData(False)
+            else:
+                menu.addAction(
+                    UI.PixmapCache.getIcon("adBlockPlusGreen.png"),
+                    self.tr("Add AdBlock Exception"),
+                    self.__setException).setData(True)
+        menu.addAction(
+            UI.PixmapCache.getIcon("adBlockPlusGreen.png"),
+            self.tr("AdBlock Exceptions..."), manager.showExceptionsDialog)
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("adBlockPlus.png"),
+            self.tr("AdBlock Configuration..."), manager.showDialog)
+    
+    def menuAction(self):
+        """
+        Public method to get a reference to the menu action.
+        
+        @return reference to the menu action (QAction)
+        """
+        if not self.__menuAction:
+            self.__menuAction = QAction(self.tr("AdBlock"), self)
+            self.__menuAction.setMenu(QMenu())
+            self.__menuAction.menu().aboutToShow.connect(self.__createMenu)
+        
+        if self.__enabled:
+            self.__menuAction.setIcon(
+                UI.PixmapCache.getIcon("adBlockPlus.png"))
+        else:
+            self.__menuAction.setIcon(
+                UI.PixmapCache.getIcon("adBlockPlusDisabled.png"))
+        
+        return self.__menuAction
+    
+    def __showMenu(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos position the context menu should be shown (QPoint)
+        """
+        menu = QMenu()
+        self.__createMenu(menu)
+        menu.exec_(pos)
+    
+    def __enableAdBlock(self):
+        """
+        Private slot to enable or disable AdBlock.
+        """
+        act = self.sender()
+        if act is not None:
+            self.__mw.adBlockManager().setEnabled(act.data())
+    
+    def __isCurrentHostExcepted(self):
+        """
+        Private method to check, if the host of the current browser is
+        excepted.
+        
+        @return flag indicating an exception (boolean)
+        """
+        browser = self.__mw.currentBrowser()
+        if browser is None:
+            return False
+        
+        urlHost = browser.page().url().host()
+        
+        return urlHost and \
+            self.__mw.adBlockManager().isHostExcepted(urlHost)
+    
+    def currentChanged(self):
+        """
+        Public slot to handle a change of the current browser tab.
+        """
+        if self.__enabled:
+            if self.__isCurrentHostExcepted():
+                self.setPixmap(
+                    UI.PixmapCache.getPixmap("adBlockPlusGreen16.png"))
+            else:
+                self.setPixmap(UI.PixmapCache.getPixmap("adBlockPlus16.png"))
+    
+    def __setException(self):
+        """
+        Private slot to add or remove the current host from the list of
+        exceptions.
+        """
+        act = self.sender()
+        if act is not None:
+            urlHost = self.__mw.currentBrowser().url().host()
+            if act.data():
+                self.__mw.adBlockManager().addException(urlHost)
+            else:
+                self.__mw.adBlockManager().removeException(urlHost)
+            self.currentChanged()
+    
+    def sourceChanged(self, browser, url):
+        """
+        Public slot to handle URL changes.
+        
+        @param browser reference to the browser (HelpBrowser)
+        @param url new URL (QUrl)
+        """
+        if browser == self.__mw.currentBrowser():
+            self.currentChanged()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockManager.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,609 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QUrlQuery, QFile, \
+    QByteArray
+from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInfo
+
+from E5Gui import E5MessageBox
+
+from .AdBlockSubscription import AdBlockSubscription
+from .AdBlockUrlInterceptor import AdBlockUrlInterceptor
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+
+class AdBlockManager(QObject):
+    """
+    Class implementing the AdBlock manager.
+    
+    @signal rulesChanged() emitted after some rule has changed
+    """
+    rulesChanged = pyqtSignal()
+    requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(AdBlockManager, self).__init__(parent)
+        
+        self.__loaded = False
+        self.__subscriptionsLoaded = False
+        self.__enabled = False
+        self.__adBlockDialog = None
+        self.__adBlockExceptionsDialog = None
+        self.__adBlockNetwork = None
+        self.__adBlockPage = None
+        self.__subscriptions = []
+        self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions")
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.__limitedEasyList = Preferences.getWebBrowser(
+            "AdBlockUseLimitedEasyList")
+        
+        self.__defaultSubscriptionUrlString = \
+            "abp:subscribe?location=" \
+            "https://easylist-downloads.adblockplus.org/easylist.txt&"\
+            "title=EasyList"
+        self.__customSubscriptionUrlString = \
+            bytes(self.__customSubscriptionUrl().toEncoded()).decode()
+        
+        self.rulesChanged.connect(self.__saveTimer.changeOccurred)
+        self.rulesChanged.connect(self.__rulesChanged)
+        
+        self.__interceptor = AdBlockUrlInterceptor(self)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.networkManager().installUrlInterceptor(
+            self.__interceptor)
+    
+    def __rulesChanged(self):
+        """
+        Private slot handling a change of the AdBlock rules.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().reloadUserStyleSheet()
+    
+    def close(self):
+        """
+        Public method to close the open search engines manager.
+        """
+        self.__adBlockDialog and self.__adBlockDialog.close()
+        self.__adBlockExceptionsDialog and \
+            self.__adBlockExceptionsDialog.close()
+        
+        self.__saveTimer.saveIfNeccessary()
+    
+    def isEnabled(self):
+        """
+        Public method to check, if blocking ads is enabled.
+        
+        @return flag indicating the enabled state (boolean)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public slot to set the enabled state.
+        
+        @param enabled flag indicating the enabled state (boolean)
+        """
+        if self.isEnabled() == enabled:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        self.__enabled = enabled
+        for mainWindow in WebBrowserWindow.mainWindows():
+            mainWindow.adBlockIcon().setEnabled(enabled)
+        if enabled:
+            self.__loadSubscriptions()
+        self.rulesChanged.emit()
+    
+    def block(self, info):
+        """
+        Public method to check, if a request should be blocked.
+        
+        @param info request info aobject
+        @type QWebEngineUrlRequestInfo
+        @return flag indicating to block the request
+        @rtype bool
+        """
+        urlString = bytes(info.requestUrl().toEncoded()).decode().lower()
+        urlDomain = info.requestUrl().host().lower()
+        urlScheme = info.requestUrl().scheme().lower()
+        refererHost = info.firstPartyUrl().host().lower()
+        
+        if not self.isEnabled() or not self.__canRunOnScheme(urlScheme):
+            return False
+        
+        if self.isHostExcepted(urlDomain) or self.isHostExcepted(refererHost):
+            return False
+            
+        res = False
+        
+        for subscription in self.subscriptions():
+            if subscription.isEnabled():
+                if subscription.adBlockDisabledForUrl(info.requestUrl()):
+                    continue
+                
+                blockedRule = subscription.match(info, urlDomain, urlString)
+                if blockedRule:
+                    res = True
+                    if info.resourceType() == \
+                            QWebEngineUrlRequestInfo.ResourceTypeMainFrame:
+                        url = QUrl("eric:adblock")
+                        query = QUrlQuery()
+                        query.addQueryItem("rule", blockedRule.filter())
+                        query.addQueryItem(
+                            "subscription", blockedRule.subscription().title())
+                        url.setQuery(query)
+                        info.redirect(url)
+                        res = False
+                    else:
+                        info.block(True)
+                    break
+        
+        return res
+    
+    def __canRunOnScheme(self, scheme):
+        """
+        Private method to check, if AdBlock can be performed on the scheme.
+        
+        @param scheme scheme to check (string)
+        @return flag indicating, that AdBlock can be performed (boolean)
+        """
+        return scheme not in ["data", "eric", "qthelp", "qrc", "file", "abp"]
+    
+    def page(self):
+        """
+        Public method to get a reference to the page block object.
+        
+        @return reference to the page block object (AdBlockPage)
+        """
+        if self.__adBlockPage is None:
+            from .AdBlockPage import AdBlockPage
+            self.__adBlockPage = AdBlockPage(self)
+        return self.__adBlockPage
+    
+    def __customSubscriptionLocation(self):
+        """
+        Private method to generate the path for custom subscriptions.
+        
+        @return URL for custom subscriptions (QUrl)
+        """
+        dataDir = os.path.join(Utilities.getConfigDir(), "web_browser",
+                               "subscriptions")
+        if not os.path.exists(dataDir):
+            os.makedirs(dataDir)
+        fileName = os.path.join(dataDir, "adblock_subscription_custom")
+        return QUrl.fromLocalFile(fileName)
+    
+    def __customSubscriptionUrl(self):
+        """
+        Private method to generate the URL for custom subscriptions.
+        
+        @return URL for custom subscriptions (QUrl)
+        """
+        location = self.__customSubscriptionLocation()
+        encodedUrl = bytes(location.toEncoded()).decode()
+        url = QUrl("abp:subscribe?location={0}&title={1}".format(
+            encodedUrl, self.tr("Custom Rules")))
+        return url
+    
+    def customRules(self):
+        """
+        Public method to get a subscription for custom rules.
+        
+        @return subscription object for custom rules (AdBlockSubscription)
+        """
+        location = self.__customSubscriptionLocation()
+        for subscription in self.__subscriptions:
+            if subscription.location() == location:
+                return subscription
+        
+        url = self.__customSubscriptionUrl()
+        customAdBlockSubscription = AdBlockSubscription(url, True, self)
+        self.addSubscription(customAdBlockSubscription)
+        return customAdBlockSubscription
+    
+    def subscriptions(self):
+        """
+        Public method to get all subscriptions.
+        
+        @return list of subscriptions (list of AdBlockSubscription)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__subscriptions[:]
+    
+    def subscription(self, location):
+        """
+        Public method to get a subscription based on its location.
+        
+        @param location location of the subscription to search for (string)
+        @return subscription or None (AdBlockSubscription)
+        """
+        if location != "":
+            for subscription in self.__subscriptions:
+                if subscription.location().toString() == location:
+                    return subscription
+        
+        return None
+    
+    def updateAllSubscriptions(self):
+        """
+        Public method to update all subscriptions.
+        """
+        for subscription in self.__subscriptions:
+            subscription.updateNow()
+    
+    def removeSubscription(self, subscription, emitSignal=True):
+        """
+        Public method to remove an AdBlock subscription.
+        
+        @param subscription AdBlock subscription to be removed
+            (AdBlockSubscription)
+        @param emitSignal flag indicating to send a signal (boolean)
+        """
+        if subscription is None:
+            return
+        
+        if subscription.url().toString().startswith(
+            (self.__defaultSubscriptionUrlString,
+             self.__customSubscriptionUrlString)):
+            return
+        
+        try:
+            self.__subscriptions.remove(subscription)
+            rulesFileName = subscription.rulesFileName()
+            QFile.remove(rulesFileName)
+            requiresSubscriptions = self.getRequiresSubscriptions(subscription)
+            for requiresSubscription in requiresSubscriptions:
+                self.removeSubscription(requiresSubscription, False)
+            if emitSignal:
+                self.rulesChanged.emit()
+        except ValueError:
+            pass
+    
+    def addSubscriptionFromUrl(self, url):
+        """
+        Public method to ad an AdBlock subscription given the abp URL:
+        
+        @param url URL to subscribe an AdBlock subscription
+        @type QUrl
+        @return flag indicating success
+        @rtype bool
+        """
+        if url.path() != "subscribe":
+            return False
+        
+        title = QUrl.fromPercentEncoding(
+            QByteArray(QUrlQuery(url).queryItemValue("title").encode()))
+        if not title:
+            return False
+        
+        res = E5MessageBox.yesNo(
+            None,
+            self.tr("Subscribe?"),
+            self.tr(
+                """<p>Subscribe to this AdBlock subscription?</p>"""
+                """<p>{0}</p>""").format(title))
+        if res:
+            from .AdBlockSubscription import AdBlockSubscription
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            
+            dlg = WebBrowserWindow.adBlockManager().showDialog()
+            subscription = AdBlockSubscription(
+                url, False,
+                WebBrowserWindow.adBlockManager())
+            WebBrowserWindow.adBlockManager().addSubscription(subscription)
+            dlg.addSubscription(subscription, False)
+            dlg.setFocus()
+            dlg.raise_()
+    
+    def addSubscription(self, subscription):
+        """
+        Public method to add an AdBlock subscription.
+        
+        @param subscription AdBlock subscription to be added
+            (AdBlockSubscription)
+        """
+        if subscription is None:
+            return
+        
+        self.__subscriptions.insert(-1, subscription)
+        
+        subscription.rulesChanged.connect(self.rulesChanged)
+        subscription.changed.connect(self.rulesChanged)
+        subscription.enabledChanged.connect(self.rulesChanged)
+        
+        self.rulesChanged.emit()
+    
+    def save(self):
+        """
+        Public method to save the AdBlock subscriptions.
+        """
+        if not self.__loaded:
+            return
+        
+        Preferences.setWebBrowser("AdBlockEnabled", self.__enabled)
+        if self.__subscriptionsLoaded:
+            subscriptions = []
+            requiresSubscriptions = []
+            # intermediate store for subscription requiring others
+            for subscription in self.__subscriptions:
+                if subscription is None:
+                    continue
+                urlString = bytes(subscription.url().toEncoded()).decode()
+                if "requiresLocation" in urlString:
+                    requiresSubscriptions.append(urlString)
+                else:
+                    subscriptions.append(urlString)
+                subscription.saveRules()
+            for subscription in requiresSubscriptions:
+                subscriptions.insert(-1, subscription)  # custom should be last
+            Preferences.setWebBrowser("AdBlockSubscriptions", subscriptions)
+    
+    def load(self):
+        """
+        Public method to load the AdBlock subscriptions.
+        """
+        if self.__loaded:
+            return
+        
+        self.__loaded = True
+        
+        self.__enabled = Preferences.getWebBrowser("AdBlockEnabled")
+        if self.__enabled:
+            self.__loadSubscriptions()
+    
+    def __loadSubscriptions(self):
+        """
+        Private method to load the set of subscriptions.
+        """
+        if self.__subscriptionsLoaded:
+            return
+        
+        subscriptions = Preferences.getWebBrowser("AdBlockSubscriptions")
+        if subscriptions:
+            for subscription in subscriptions:
+                if subscription.startswith(
+                        self.__defaultSubscriptionUrlString):
+                    break
+            else:
+                subscriptions.insert(0, self.__defaultSubscriptionUrlString)
+            for subscription in subscriptions:
+                if subscription.startswith(self.__customSubscriptionUrlString):
+                    break
+            else:
+                subscriptions.append(self.__customSubscriptionUrlString)
+        else:
+            subscriptions = [self.__defaultSubscriptionUrlString,
+                             self.__customSubscriptionUrlString]
+        for subscription in subscriptions:
+            url = QUrl.fromEncoded(subscription.encode("utf-8"))
+            adBlockSubscription = AdBlockSubscription(
+                url,
+                subscription.startswith(self.__customSubscriptionUrlString),
+                self,
+                subscription.startswith(self.__defaultSubscriptionUrlString))
+            adBlockSubscription.rulesChanged.connect(self.rulesChanged)
+            adBlockSubscription.changed.connect(self.rulesChanged)
+            adBlockSubscription.enabledChanged.connect(self.rulesChanged)
+            self.__subscriptions.append(adBlockSubscription)
+        
+        self.__subscriptionsLoaded = True
+    
+    def loadRequiredSubscription(self, location, title):
+        """
+        Public method to load a subscription required by another one.
+        
+        @param location location of the required subscription (string)
+        @param title title of the required subscription (string)
+        """
+        # Step 1: check, if the subscription is in the list of subscriptions
+        urlString = "abp:subscribe?location={0}&title={1}".format(
+            location, title)
+        for subscription in self.__subscriptions:
+            if subscription.url().toString().startswith(urlString):
+                # We found it!
+                return
+        
+        # Step 2: if it is not, get it
+        url = QUrl.fromEncoded(urlString.encode("utf-8"))
+        adBlockSubscription = AdBlockSubscription(url, False, self)
+        self.addSubscription(adBlockSubscription)
+        self.requiredSubscriptionLoaded.emit(adBlockSubscription)
+    
+    def getRequiresSubscriptions(self, subscription):
+        """
+        Public method to get a list of subscriptions, that require the given
+        one.
+        
+        @param subscription subscription to check for (AdBlockSubscription)
+        @return list of subscription requiring the given one (list of
+            AdBlockSubscription)
+        """
+        subscriptions = []
+        location = subscription.location().toString()
+        for subscription in self.__subscriptions:
+            if subscription.requiresLocation() == location:
+                subscriptions.append(subscription)
+        
+        return subscriptions
+    
+    def showDialog(self):
+        """
+        Public slot to show the AdBlock subscription management dialog.
+        
+        @return reference to the dialog (AdBlockDialog)
+        """
+        if self.__adBlockDialog is None:
+            from .AdBlockDialog import AdBlockDialog
+            self.__adBlockDialog = AdBlockDialog(self)
+        
+        self.__adBlockDialog.show()
+        return self.__adBlockDialog
+    
+    def elementHidingRules(self):
+        """
+        Public method to get the element hiding rules.
+        
+        @return element hiding rules (string)
+        """
+        if not self.isEnabled():
+            return ""
+        
+        rules = ""
+        
+        for subscription in self.__subscriptions:
+            rules += subscription.elementHidingRules()
+        
+        if rules:
+            # remove last ",
+            rules = rules[:-1]
+        
+        return rules
+    
+    def elementHidingRulesForDomain(self, url):
+        """
+        Public method to get the element hiding rules for a domain.
+        
+        @param url URL to get hiding rules for (QUrl)
+        @return element hiding rules (string)
+        """
+        if not self.isEnabled():
+            return ""
+        
+        rules = ""
+        
+        for subscription in self.__subscriptions:
+            if subscription.elemHideDisabledForUrl(url):
+                continue
+            
+            rules += subscription.elementHidingRulesForDomain(url.host())
+        
+        if rules:
+            # remove last ","
+            rules = rules[:-1]
+        
+        rules += "{display:none !important;}\n"
+        
+        return rules
+    
+    def exceptions(self):
+        """
+        Public method to get a list of excepted hosts.
+        
+        @return list of excepted hosts (list of string)
+        """
+        return self.__exceptedHosts
+    
+    def setExceptions(self, hosts):
+        """
+        Public method to set the list of excepted hosts.
+        
+        @param hosts list of excepted hosts (list of string)
+        """
+        self.__exceptedHosts = [host.lower() for host in hosts]
+        Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts)
+    
+    def addException(self, host):
+        """
+        Public method to add an exception.
+        
+        @param host to be excepted (string)
+        """
+        host = host.lower()
+        if host and host not in self.__exceptedHosts:
+            self.__exceptedHosts.append(host)
+            Preferences.setWebBrowser(
+                "AdBlockExceptions", self.__exceptedHosts)
+    
+    def removeException(self, host):
+        """
+        Public method to remove an exception.
+        
+        @param host to be removed from the list of exceptions (string)
+        """
+        host = host.lower()
+        if host in self.__exceptedHosts:
+            self.__exceptedHosts.remove(host)
+            Preferences.setWebBrowser(
+                "AdBlockExceptions", self.__exceptedHosts)
+    
+    def isHostExcepted(self, host):
+        """
+        Public slot to check, if a host is excepted.
+        
+        @param host host to check (string)
+        @return flag indicating an exception (boolean)
+        """
+        host = host.lower()
+        return host in self.__exceptedHosts
+    
+    def showExceptionsDialog(self):
+        """
+        Public method to show the AdBlock Exceptions dialog.
+        
+        @return reference to the exceptions dialog (AdBlockExceptionsDialog)
+        """
+        if self.__adBlockExceptionsDialog is None:
+            from .AdBlockExceptionsDialog import AdBlockExceptionsDialog
+            self.__adBlockExceptionsDialog = AdBlockExceptionsDialog()
+        
+        self.__adBlockExceptionsDialog.load(self.__exceptedHosts)
+        self.__adBlockExceptionsDialog.show()
+        return self.__adBlockExceptionsDialog
+    
+    def useLimitedEasyList(self):
+        """
+        Public method to test, if limited EasyList rules shall be used.
+        
+        @return flag indicating limited EasyList rules
+        @rtype bool
+        """
+        return self.__limitedEasyList
+    
+    def setUseLimitedEasyList(self, limited):
+        """
+        Public method to set the limited EasyList flag.
+        
+        @param limited flag indicating to use limited EasyList
+        @type bool
+        """
+        self.__limitedEasyList = limited
+        
+        for subscription in self.__subscriptions:
+            if subscription.url().toString().startswith(
+                    self.__defaultSubscriptionUrlString):
+                subscription.updateNow()
+        
+        Preferences.setWebBrowser("AdBlockUseLimitedEasyList", limited)
+    
+    def getDefaultSubscriptionUrl(self):
+        """
+        Public method to get the default subscription URL.
+        
+        @return default subscription URL
+        @rtype str
+        """
+        return self.__defaultSubscriptionUrlString
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to apply AdBlock rules to a web page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+
+from ..Tools import Scripts
+
+
+class AdBlockPage(QObject):
+    """
+    Class to apply AdBlock rules to a web page.
+    """
+    def hideBlockedPageEntries(self, page):
+        """
+        Public method to apply AdBlock rules to a web page.
+        
+        @param page reference to the web page (HelpWebPage)
+        """
+        if page is None:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        manager = WebBrowserWindow.adBlockManager()
+        if not manager.isEnabled():
+            return
+        
+        # apply domain specific element hiding rules
+        elementHiding = manager.elementHidingRulesForDomain(page.url())
+        if elementHiding:
+            script = Scripts.setCss(elementHiding)
+            page.runJavaScript(script)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockRule.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,681 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock rule class.
+"""
+
+from __future__ import unicode_literals
+
+import re
+
+from PyQt5.QtCore import Qt, QRegExp
+from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInfo
+
+
+def toSecondLevelDomain(url):
+    """
+    Module function to get a second level domain from the given URL.
+    
+    @param url URL to extract domain from (QUrl)
+    @return name of second level domain (string)
+    """
+    topLevelDomain = url.topLevelDomain()
+    urlHost = url.host()
+    
+    if not topLevelDomain or not urlHost:
+        return ""
+    
+    domain = urlHost[:len(urlHost) - len(topLevelDomain)]
+    if domain.count(".") == 0:
+        return urlHost
+    
+    while domain.count(".") != 0:
+        domain = domain[domain.find(".") + 1:]
+    
+    return domain + topLevelDomain
+
+
+class AdBlockRule(object):
+    """
+    Class implementing the AdBlock rule.
+    """
+    def __init__(self, filter="", subscription=None):
+        """
+        Constructor
+        
+        @param filter filter string of the rule (string)
+        @param subscription reference to the subscription object
+            (AdBlockSubscription)
+        """
+        self.__subscription = subscription
+        
+        self.__regExp = QRegExp()
+        self.__options = []
+        self.__blockedDomains = []
+        self.__allowedDomains = []
+        
+        self.__enabled = True
+        self.__cssRule = False
+        self.__exception = False
+        self.__internalDisabled = False
+        self.__domainRestricted = False
+        self.__useRegExp = False
+        self.__useDomainMatch = False
+        self.__useEndsMatch = False
+        self.__thirdParty = False
+        self.__thirdPartyException = False
+        self.__object = False
+        self.__objectException = False
+        self.__subdocument = False
+        self.__subdocumentException = False
+        self.__xmlhttprequest = False
+        self.__xmlhttprequestException = False
+        self.__document = False
+        self.__elemhide = False
+        self.__caseSensitivity = Qt.CaseInsensitive
+        self.__image = False
+        self.__imageException = False
+        self.__script = False
+        self.__scriptException = False
+        self.__stylesheet = False
+        self.__stylesheetException = False
+        self.__objectSubrequest = False
+        self.__objectSubrequestException = False
+        self.__stringMatchRule = False
+        
+        self.setFilter(filter)
+    
+    def subscription(self):
+        """
+        Public method to get the subscription this rule belongs to.
+        
+        @return subscription of the rule (AdBlockSubscription)
+        """
+        return self.__subscription
+    
+    def filter(self):
+        """
+        Public method to get the rule filter string.
+        
+        @return rule filter string (string)
+        """
+        return self.__filter
+    
+    def setFilter(self, filter):
+        """
+        Public method to set the rule filter string.
+        
+        @param filter rule filter string (string)
+        """
+        self.__filter = filter
+        self.__parseFilter()
+    
+    def __parseFilter(self):
+        """
+        Private method to parse the filter pattern.
+        """
+        parsedLine = self.__filter
+        
+        # empty rule or just a comment
+        if not parsedLine.strip() or parsedLine.startswith(("!", "[Adblock")):
+            self.__enabled = False
+            return
+        
+        # CSS element hiding rule
+        if "##" in parsedLine or "#@#" in parsedLine:
+            self.__cssRule = True
+            pos = parsedLine.find("#")
+            
+            # domain restricted rule
+            if not parsedLine.startswith("##"):
+                domains = parsedLine[:pos]
+                self.__parseDomains(domains, ",")
+            
+            self.__exception = parsedLine[pos + 1] == "@"
+            
+            if self.__exception:
+                self.__cssSelector = parsedLine[pos + 3:]
+            else:
+                self.__cssSelector = parsedLine[pos + 2:]
+            # CSS rule cannot have more options -> stop parsing
+            return
+        
+        # Exception always starts with @@
+        if parsedLine.startswith("@@"):
+            self.__exception = True
+            parsedLine = parsedLine[2:]
+        
+        # Parse all options following '$' character
+        optionsIndex = parsedLine.find("$")
+        if optionsIndex >= 0:
+            options = parsedLine[optionsIndex + 1:].split(",")
+            
+            handledOptions = 0
+            for option in options:
+                if option.startswith("domain="):
+                    self.__parseDomains(option[7:], "|")
+                    handledOptions += 1
+                elif option == "match-case":
+                    self.__caseSensitivity = Qt.CaseSensitive
+                    handledOptions += 1
+                elif option.endswith("third-party"):
+                    self.__thirdParty = True
+                    self.__thirdPartyException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("object"):
+                    self.__object = True
+                    self.__objectException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("subdocument"):
+                    self.__subdocument = True
+                    self.__subdocumentException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("xmlhttprequest"):
+                    self.__xmlhttprequest = True
+                    self.__xmlhttprequestException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("image"):
+                    self.__image = True
+                    self.__imageException = option.startswith("~")
+                elif option.endswith("script"):
+                    self.__script = True
+                    self.__scriptException = option.startswith("~")
+                elif option.endswith("stylesheet"):
+                    self.__stylesheet = True
+                    self.__stylesheetException = option.startswith("~")
+                elif option.endswith("object-subrequest"):
+                    self.__objectSubrequest = True
+                    self.__objectSubrequestException = option.startswith("~")
+                elif option == "document" and self.__exception:
+                    self.__document = True
+                    handledOptions += 1
+                elif option == "elemhide" and self.__exception:
+                    self.__elemhide = True
+                    handledOptions += 1
+                elif option == "collapse":
+                    # Hiding placeholders of blocked elements
+                    handledOptions += 1
+            
+            # If we don't handle all options, it's safer to just disable
+            # this rule
+            if handledOptions != len(options):
+                self.__internalDisabled = True
+                return
+            
+            parsedLine = parsedLine[:optionsIndex]
+        
+        # Rule is classic regexp
+        if parsedLine.startswith("/") and parsedLine.endswith("/"):
+            parsedLine = parsedLine[1:-1]
+            self.__useRegExp = True
+            self.__regExp = QRegExp(parsedLine, self.__caseSensitivity,
+                                    QRegExp.RegExp)
+            return
+        
+        # Remove starting / ending wildcards
+        if parsedLine.startswith("*"):
+            parsedLine = parsedLine[1:]
+        if parsedLine.endswith("*"):
+            parsedLine = parsedLine[:-1]
+        
+        # Fast string matching for domain can be used
+        if parsedLine.startswith("||") and \
+           parsedLine.endswith("^") and \
+           QRegExp("[/:?=&\\*]").indexIn(parsedLine) == -1:
+            parsedLine = parsedLine[2:-1]
+            self.__useDomainMatch = True
+            self.__matchString = parsedLine
+            return
+        
+        # If rule contains '|' only at the end, string matching can be used
+        if parsedLine.endswith("|") and \
+           QRegExp("[\\^\\*]").indexIn(parsedLine) == -1 and \
+           parsedLine.count("|") == 1:
+            parsedLine = parsedLine[:-1]
+            self.__useEndsMatch = True
+            self.__matchString = parsedLine
+            return
+        
+        # If there is still a wildcard (*) or separator (^) or (|),
+        # the rule must be modified to comply with QRegExp.
+        if "*" in parsedLine or "^" in parsedLine or "|" in parsedLine:
+            pattern = self.__convertPatternToRegExp(parsedLine)
+            self.__useRegExp = True
+            self.__regExp = QRegExp(pattern, self.__caseSensitivity,
+                                    QRegExp.RegExp)
+            return
+        
+        # no regexp required
+        self.__useRegExp = False
+        self.__matchString = parsedLine
+        self.__stringMatchRule = True
+    
+    def __parseDomains(self, domains, separator):
+        """
+        Private method to parse a string with a domain list.
+        
+        @param domains list of domains (string)
+        @param separator separator character used by the list (string)
+        """
+        domainsList = domains.split(separator)
+        
+        for domain in domainsList:
+            if not domain:
+                continue
+            if domain.startswith("~"):
+                self.__blockedDomains.append(domain[1:])
+            else:
+                self.__allowedDomains.append(domain)
+        
+        self.__domainRestricted = \
+            bool(self.__blockedDomains) or bool(self.__allowedDomains)
+    
+    def networkMatch(self, request, domain, encodedUrl):
+        """
+        Public method to check the rule for a match.
+        
+        @param request reference to the network request
+        @type QWebEngineUrlRequestInfo
+        @param domain domain name
+        @type str
+        @param encodedUrl string encoded URL to be checked
+        @type str
+        @return flag indicating a match
+        @rtype bool
+        """
+        if self.__cssRule or not self.__enabled or self.__internalDisabled:
+            return False
+        
+        matched = self.__stringMatch(domain, encodedUrl)
+        
+        if matched:
+            # check domain restrictions
+            if self.__domainRestricted and \
+                    not self.matchDomain(request.firstPartyUrl().host()):
+                return False
+            
+            # check third-party restrictions
+            if self.__thirdParty and not self.matchThirdParty(request):
+                return False
+            
+            # check object restrictions
+            if self.__object and not self.matchObject(request):
+                return False
+            
+            # check subdocument restrictions
+            if self.__subdocument and not self.matchSubdocument(request):
+                return False
+            
+            # check xmlhttprequest restriction
+            if self.__xmlhttprequest and not self.matchXmlHttpRequest(request):
+                return False
+            
+            # check image restriction
+            if self.__image and not self.matchImage(request):
+                return False
+            
+            # check script restriction
+            if self.__script and not self.matchScript(request):
+                return False
+            
+            # check stylesheet restriction
+            if self.__stylesheet and not self.matchStyleSheet(request):
+                return False
+            
+            # check object-subrequest restriction
+            if self.__objectSubrequest and \
+                    not self.matchObjectSubrequest(request):
+                return False
+        
+        return matched
+    
+    def urlMatch(self, url):
+        """
+        Public method to check an URL against the rule.
+        
+        @param url URL to check (QUrl)
+        @return flag indicating a match (boolean)
+        """
+        if not self.__document and not self.__elemhide:
+            return False
+        
+        encodedUrl = bytes(url.toEncoded()).decode()
+        domain = url.host()
+        return self.__stringMatch(domain, encodedUrl)
+    
+    def __stringMatch(self, domain, encodedUrl):
+        """
+        Private method to match a domain string.
+        
+        @param domain domain to match
+        @type str
+        @param encodedUrl URL in encoded form
+        @type str
+        @return flag indicating a match
+        @rtype bool
+        """
+        if self.__cssRule or not self.__enabled or self.__internalDisabled:
+            return False
+        
+        matched = False
+        
+        if self.__useRegExp:
+            matched = self.__regExp.indexIn(encodedUrl) != -1
+        elif self.__useDomainMatch:
+            matched = domain.endswith(self.__matchString)
+        elif self.__useEndsMatch:
+            if self.__caseSensitivity == Qt.CaseInsensitive:
+                matched = encodedUrl.lower().endswith(
+                    self.__matchString.lower())
+            else:
+                matched = encodedUrl.endswith(self.__matchString)
+        else:
+            if self.__caseSensitivity == Qt.CaseInsensitive:
+                matched = self.__matchString.lower() in encodedUrl.lower()
+            else:
+                matched = self.__matchString in encodedUrl
+        
+        return matched
+    
+    def matchDomain(self, domain):
+        """
+        Public method to match a domain.
+        
+        @param domain domain name to check (string)
+        @return flag indicating a match (boolean)
+        """
+        if not self.__enabled:
+            return False
+        
+        if not self.__domainRestricted:
+            return True
+        
+        if len(self.__blockedDomains) == 0:
+            for dom in self.__allowedDomains:
+                if domain.endswith(dom):
+                    return True
+        elif len(self.__allowedDomains) == 0:
+            for dom in self.__blockedDomains:
+                if domain.endswith(dom):
+                    return False
+            return True
+        else:
+            for dom in self.__blockedDomains:
+                if domain.endswith(dom):
+                    return False
+            for dom in self.__allowedDomains:
+                if domain.endswith(dom):
+                    return True
+        
+        return False
+    
+    def matchThirdParty(self, req):
+        """
+        Public slot to match a third-party rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        # Third-party matching should be performed on second-level domains
+        firstPartyHost = toSecondLevelDomain(req.firstPartyUrl())
+        host = toSecondLevelDomain(req.requestUrl())
+        
+        match = firstPartyHost != host
+        
+        if self.__thirdPartyException:
+            return not match
+        else:
+            return match
+    
+    def matchObject(self, req):
+        """
+        Public slot to match an object rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeObject)
+        
+        if self.__objectException:
+            return not match
+        else:
+            return match
+    
+    def matchSubdocument(self, req):
+        """
+        Public slot to match a sub-document rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() ==
+            QWebEngineUrlRequestInfo.ResourceTypeSubFrame)
+        
+        if self.__subdocumentException:
+            return not match
+        else:
+            return match
+    
+    def matchXmlHttpRequest(self, req):
+        """
+        Public slot to match a XmlHttpRequest rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeXhr)
+        
+        if self.__xmlhttprequestException:
+            return not match
+        else:
+            return match
+    
+    def matchImage(self, req):
+        """
+        Public slot to match an Image rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeImage)
+        
+        if self.__imageException:
+            return not match
+        else:
+            return match
+    
+    def matchScript(self, req):
+        """
+        Public slot to match a Script rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeScript)
+        
+        if self.__scriptException:
+            return not match
+        else:
+            return match
+    
+    def matchStyleSheet(self, req):
+        """
+        Public slot to match a StyleSheet rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() ==
+            QWebEngineUrlRequestInfo.ResourceTypeStylesheet)
+        
+        if self.__stylesheetException:
+            return not match
+        else:
+            return match
+    
+    def matchObjectSubrequest(self, req):
+        """
+        Public slot to match an Object Subrequest rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() ==
+            QWebEngineUrlRequestInfo.ResourceTypeSubResource)
+        
+        if self.__objectSubrequestException:
+            return not match
+        else:
+            return match
+    
+    def isException(self):
+        """
+        Public method to check, if the rule defines an exception.
+        
+        @return flag indicating an exception (boolean)
+        """
+        return self.__exception
+    
+    def setException(self, exception):
+        """
+        Public method to set the rule's exception flag.
+        
+        @param exception flag indicating an exception rule (boolean)
+        """
+        self.__exception = exception
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the rule is enabled.
+        
+        @return flag indicating enabled state (boolean)
+        """
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public method to set the rule's enabled state.
+        
+        @param enabled flag indicating the new enabled state (boolean)
+        """
+        self.__enabled = enabled
+        if not enabled:
+            self.__filter = "!" + self.__filter
+        else:
+            self.__filter = self.__filter[1:]
+    
+    def isCSSRule(self):
+        """
+        Public method to check, if the rule is a CSS rule.
+        
+        @return flag indicating a CSS rule (boolean)
+        """
+        return self.__cssRule
+    
+    def cssSelector(self):
+        """
+        Public method to get the CSS selector of the rule.
+        
+        @return CSS selector (string)
+        """
+        return self.__cssSelector
+    
+    def isDocument(self):
+        """
+        Public method to check, if this is a document rule.
+        
+        @return flag indicating a document rule (boolean)
+        """
+        return self.__document
+    
+    def isElementHiding(self):
+        """
+        Public method to check, if this is an element hiding rule.
+        
+        @return flag indicating an element hiding rule (boolean)
+        """
+        return self.__elemhide
+    
+    def isDomainRestricted(self):
+        """
+        Public method to check, if this rule is restricted by domain.
+        
+        @return flag indicating a domain restriction (boolean)
+        """
+        return self.__domainRestricted
+    
+    def isComment(self):
+        """
+        Public method to check, if this is a comment.
+        
+        @return flag indicating a comment (boolean)
+        """
+        return self.__filter.startswith("!")
+    
+    def isHeader(self):
+        """
+        Public method to check, if this is a header.
+        
+        @return flag indicating a header (boolean)
+        """
+        return self.__filter.startswith("[Adblock")
+    
+    def isSlow(self):
+        """
+        Public method to check, if this is a slow rule.
+        
+        @return flag indicating a slow rule (boolean)
+        """
+        return self.__useRegExp
+    
+    def isInternalDisabled(self):
+        """
+        Public method to check, if this rule was disabled internally.
+        
+        @return flag indicating an internally disabled rule (boolean)
+        """
+        return self.__internalDisabled
+    
+    def __convertPatternToRegExp(self, wildcardPattern):
+        """
+        Private method to convert a wildcard pattern to a regular expression.
+        
+        @param wildcardPattern string containing the wildcard pattern (string)
+        @return string containing a regular expression (string)
+        """
+        pattern = wildcardPattern
+        
+        # remove multiple wildcards
+        pattern = re.sub(r"\*+", "*", pattern)
+        # remove anchors following separator placeholder
+        pattern = re.sub(r"\^\|$", "^", pattern)
+        # remove leading wildcards
+        pattern = re.sub(r"^(\*)", "", pattern)
+        # remove trailing wildcards
+        pattern = re.sub(r"(\*)$", "", pattern)
+        # escape special symbols
+        pattern = re.sub(r"(\W)", r"\\\1", pattern)
+        # process extended anchor at expression start
+        pattern = re.sub(
+            r"^\\\|\\\|",
+            r"^[\w\-]+:\/+(?!\/)(?:[^\/]+\.)?", pattern)
+        # process separator placeholders
+        pattern = re.sub(r"\\\^", r"(?:[^\w\d\-.%]|$)", 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)
+        
+        return pattern
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockSubscription.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,716 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock subscription class.
+"""
+
+from __future__ import unicode_literals
+
+import os
+import re
+import hashlib
+import base64
+
+from PyQt5.QtCore import pyqtSignal, Qt, QObject, QByteArray, QDateTime, \
+    QUrl, QUrlQuery, QCryptographicHash, QFile, QIODevice, QTextStream, \
+    QDate, QTime
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
+
+from E5Gui import E5MessageBox
+
+import Utilities
+import Preferences
+
+
+class AdBlockSubscription(QObject):
+    """
+    Class implementing the AdBlock subscription.
+    
+    @signal changed() emitted after the subscription has changed
+    @signal rulesChanged() emitted after the subscription's rules have changed
+    @signal enabledChanged(bool) emitted after the enabled state was changed
+    """
+    changed = pyqtSignal()
+    rulesChanged = pyqtSignal()
+    enabledChanged = pyqtSignal(bool)
+    
+    def __init__(self, url, custom, parent=None, default=False):
+        """
+        Constructor
+        
+        @param url AdBlock URL for the subscription (QUrl)
+        @param custom flag indicating a custom subscription (boolean)
+        @param parent reference to the parent object (QObject)
+        @param default flag indicating a default subscription (boolean)
+        """
+        super(AdBlockSubscription, self).__init__(parent)
+        
+        self.__custom = custom
+        self.__url = url.toEncoded()
+        self.__enabled = False
+        self.__downloading = None
+        self.__defaultSubscription = default
+        
+        self.__title = ""
+        self.__location = QByteArray()
+        self.__lastUpdate = QDateTime()
+        self.__requiresLocation = ""
+        self.__requiresTitle = ""
+        
+        self.__updatePeriod = 0     # update period in hours, 0 = use default
+        self.__remoteModified = QDateTime()
+        
+        self.__rules = []   # list containing all AdBlock rules
+        
+        self.__networkExceptionRules = []
+        self.__networkBlockRules = []
+        self.__domainRestrictedCssRules = []
+        self.__elementHidingRules = ""
+        self.__documentRules = []
+        self.__elemhideRules = []
+        
+        self.__checksumRe = re.compile(
+            r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""",
+            re.IGNORECASE | re.MULTILINE)
+        self.__expiresRe = re.compile(
+            r"""(?:expires:|expires after)\s*(\d+)\s*(hour|h)?""",
+            re.IGNORECASE)
+        self.__remoteModifiedRe = re.compile(
+            r"""!\s*(?:Last modified|Updated):\s*(\d{1,2})\s*"""
+            r"""(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*"""
+            r"""(\d{2,4})\s*((\d{1,2}):(\d{2}))?""",
+            re.IGNORECASE)
+        
+        self.__monthNameToNumber = {
+            "Jan": 1,
+            "Feb": 2,
+            "Mar": 3,
+            "Apr": 4,
+            "May": 5,
+            "Jun": 6,
+            "Jul": 7,
+            "Aug": 8,
+            "Sep": 9,
+            "Oct": 10,
+            "Nov": 11,
+            "Dec": 12
+        }
+        
+        self.__parseUrl(url)
+    
+    def __parseUrl(self, url):
+        """
+        Private method to parse the AdBlock URL for the subscription.
+        
+        @param url AdBlock URL for the subscription (QUrl)
+        """
+        if url.scheme() != "abp":
+            return
+        
+        if url.path() != "subscribe":
+            return
+        
+        urlQuery = QUrlQuery(url)
+        self.__title = QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue("title").encode()))
+        self.__enabled = urlQuery.queryItemValue("enabled") != "false"
+        self.__location = QByteArray(QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue("location").encode()))
+            .encode("utf-8"))
+        
+        # Check for required subscription
+        self.__requiresLocation = QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue(
+                "requiresLocation").encode()))
+        self.__requiresTitle = QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue("requiresTitle").encode()))
+        if self.__requiresLocation and self.__requiresTitle:
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            WebBrowserWindow.adBlockManager().loadRequiredSubscription(
+                self.__requiresLocation, self.__requiresTitle)
+        
+        lastUpdateString = urlQuery.queryItemValue("lastUpdate")
+        self.__lastUpdate = QDateTime.fromString(lastUpdateString,
+                                                 Qt.ISODate)
+        
+        self.__loadRules()
+    
+    def url(self):
+        """
+        Public method to generate the URL for this subscription.
+        
+        @return AdBlock URL for the subscription (QUrl)
+        """
+        url = QUrl()
+        url.setScheme("abp")
+        url.setPath("subscribe")
+        
+        queryItems = []
+        queryItems.append(("location", bytes(self.__location).decode()))
+        queryItems.append(("title", self.__title))
+        if self.__requiresLocation and self.__requiresTitle:
+            queryItems.append(("requiresLocation", self.__requiresLocation))
+            queryItems.append(("requiresTitle", self.__requiresTitle))
+        if not self.__enabled:
+            queryItems.append(("enabled", "false"))
+        if self.__lastUpdate.isValid():
+            queryItems.append(("lastUpdate",
+                               self.__lastUpdate.toString(Qt.ISODate)))
+        
+        query = QUrlQuery()
+        query.setQueryItems(queryItems)
+        url.setQuery(query)
+        return url
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the subscription is enabled.
+        
+        @return flag indicating the enabled status (boolean)
+        """
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public method to set the enabled status.
+        
+        @param enabled flag indicating the enabled status (boolean)
+        """
+        if self.__enabled == enabled:
+            return
+        
+        self.__enabled = enabled
+        self.enabledChanged.emit(enabled)
+    
+    def title(self):
+        """
+        Public method to get the subscription title.
+        
+        @return subscription title (string)
+        """
+        return self.__title
+    
+    def setTitle(self, title):
+        """
+        Public method to set the subscription title.
+        
+        @param title subscription title (string)
+        """
+        if self.__title == title:
+            return
+        
+        self.__title = title
+        self.changed.emit()
+    
+    def location(self):
+        """
+        Public method to get the subscription location.
+        
+        @return URL of the subscription location (QUrl)
+        """
+        return QUrl.fromEncoded(self.__location)
+    
+    def setLocation(self, url):
+        """
+        Public method to set the subscription location.
+        
+        @param url URL of the subscription location (QUrl)
+        """
+        if url == self.location():
+            return
+        
+        self.__location = url.toEncoded()
+        self.__lastUpdate = QDateTime()
+        self.changed.emit()
+    
+    def requiresLocation(self):
+        """
+        Public method to get the location of a required subscription.
+        
+        @return location of a required subscription (string)
+        """
+        return self.__requiresLocation
+    
+    def lastUpdate(self):
+        """
+        Public method to get the date and time of the last update.
+        
+        @return date and time of the last update (QDateTime)
+        """
+        return self.__lastUpdate
+    
+    def rulesFileName(self):
+        """
+        Public method to get the name of the rules file.
+        
+        @return name of the rules file (string)
+        """
+        if self.location().scheme() == "file":
+            return self.location().toLocalFile()
+        
+        if self.__location.isEmpty():
+            return ""
+        
+        sha1 = bytes(QCryptographicHash.hash(
+            self.__location, QCryptographicHash.Sha1).toHex()).decode()
+        dataDir = os.path.join(
+            Utilities.getConfigDir(), "web_browser", "subscriptions")
+        if not os.path.exists(dataDir):
+            os.makedirs(dataDir)
+        fileName = os.path.join(
+            dataDir, "adblock_subscription_{0}".format(sha1))
+        return fileName
+    
+    def __loadRules(self):
+        """
+        Private method to load the rules of the subscription.
+        """
+        fileName = self.rulesFileName()
+        f = QFile(fileName)
+        if f.exists():
+            if not f.open(QIODevice.ReadOnly):
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Load subscription rules"),
+                    self.tr(
+                        """Unable to open AdBlock file '{0}' for reading.""")
+                    .format(fileName))
+            else:
+                textStream = QTextStream(f)
+                header = textStream.readLine(1024)
+                if not header.startswith("[Adblock"):
+                    E5MessageBox.warning(
+                        None,
+                        self.tr("Load subscription rules"),
+                        self.tr("""AdBlock file '{0}' does not start"""
+                                """ with [Adblock.""")
+                        .format(fileName))
+                    f.close()
+                    f.remove()
+                    self.__lastUpdate = QDateTime()
+                else:
+                    from .AdBlockRule import AdBlockRule
+                    
+                    self.__updatePeriod = 0
+                    self.__remoteModified = QDateTime()
+                    self.__rules = []
+                    self.__rules.append(AdBlockRule(header, self))
+                    while not textStream.atEnd():
+                        line = textStream.readLine()
+                        self.__rules.append(AdBlockRule(line, self))
+                        expires = self.__expiresRe.search(line)
+                        if expires:
+                            period, kind = expires.groups()
+                            if kind:
+                                # hours
+                                self.__updatePeriod = int(period)
+                            else:
+                                # days
+                                self.__updatePeriod = int(period) * 24
+                        remoteModified = self.__remoteModifiedRe.search(line)
+                        if remoteModified:
+                            day, month, year, time, hour, minute = \
+                                remoteModified.groups()
+                            self.__remoteModified.setDate(
+                                QDate(int(year),
+                                      self.__monthNameToNumber[month],
+                                      int(day))
+                            )
+                            if time:
+                                self.__remoteModified.setTime(
+                                    QTime(int(hour), int(minute)))
+                    self.__populateCache()
+                    self.changed.emit()
+        elif not fileName.endswith("_custom"):
+            self.__lastUpdate = QDateTime()
+        
+        self.checkForUpdate()
+    
+    def checkForUpdate(self):
+        """
+        Public method to check for an update.
+        """
+        if self.__updatePeriod:
+            updatePeriod = self.__updatePeriod
+        else:
+            updatePeriod = \
+                Preferences.getWebBrowser("AdBlockUpdatePeriod") * 24
+        if not self.__lastUpdate.isValid() or \
+           (self.__remoteModified.isValid() and
+            self.__remoteModified.addSecs(updatePeriod * 3600) <
+                QDateTime.currentDateTime()) or \
+           self.__lastUpdate.addSecs(updatePeriod * 3600) < \
+                QDateTime.currentDateTime():
+            self.updateNow()
+    
+    def updateNow(self):
+        """
+        Public method to update the subscription immediately.
+        """
+        if self.__downloading is not None:
+            return
+        
+        if not self.location().isValid():
+            return
+        
+        if self.location().scheme() == "file":
+            self.__lastUpdate = QDateTime.currentDateTime()
+            self.__loadRules()
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        self.__downloading = WebBrowserWindow.networkManager().get(
+            QNetworkRequest(self.location()))
+        self.__downloading.finished.connect(self.__rulesDownloaded)
+    
+    def __rulesDownloaded(self):
+        """
+        Private slot to deal with the downloaded rules.
+        """
+        reply = self.sender()
+        
+        response = reply.readAll()
+        reply.close()
+        self.__downloading = None
+        
+        if reply.error() != QNetworkReply.NoError:
+            if not self.__defaultSubscription:
+                # don't show error if we try to load the default
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Downloading subscription rules"),
+                    self.tr(
+                        """<p>Subscription rules could not be"""
+                        """ downloaded.</p><p>Error: {0}</p>""")
+                    .format(reply.errorString()))
+            else:
+                # reset after first download attempt
+                self.__defaultSubscription = False
+            return
+        
+        if response.isEmpty():
+            E5MessageBox.warning(
+                None,
+                self.tr("Downloading subscription rules"),
+                self.tr("""Got empty subscription rules."""))
+            return
+        
+        fileName = self.rulesFileName()
+        QFile.remove(fileName)
+        f = QFile(fileName)
+        if not f.open(QIODevice.ReadWrite):
+            E5MessageBox.warning(
+                None,
+                self.tr("Downloading subscription rules"),
+                self.tr(
+                    """Unable to open AdBlock file '{0}' for writing.""")
+                .file(fileName))
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if WebBrowserWindow.adBlockManager().useLimitedEasyList() and \
+            self.url().toString().startswith(
+                WebBrowserWindow.adBlockManager().getDefaultSubscriptionUrl()):
+            limited = True
+            # ignore Third-party advertisers rules for performance
+            # whitelist rules at the end will be used
+            index = response.indexOf(
+                "!---------------------------"
+                "Third-party advertisers"
+                "---------------------------!")
+            part1 = response.left(index)
+            index = response.indexOf(
+                "!-----------------------"
+                "Whitelists to fix broken sites"
+                "------------------------!")
+            part2 = response.mid(index)
+            f.write(part1)
+            f.write(part2)
+        else:
+            limited = False
+            f.write(response)
+        f.close()
+        self.__lastUpdate = QDateTime.currentDateTime()
+        if limited or self.__validateCheckSum(fileName):
+            self.__loadRules()
+        else:
+            QFile.remove(fileName)
+        self.__downloading = None
+        reply.deleteLater()
+    
+    def __validateCheckSum(self, fileName):
+        """
+        Private method to check the subscription file's checksum.
+        
+        @param fileName name of the file containing the subscription (string)
+        @return flag indicating a valid file (boolean). A file is considered
+            valid, if the checksum is OK, the file does not contain a
+            checksum (i.e. cannot be checked) or we are using the limited
+            EasyList (because we fiddled with the original).
+        """
+        try:
+            f = open(fileName, "r", encoding="utf-8")
+            data = f.read()
+            f.close()
+        except (IOError, OSError):
+            return False
+        
+        match = re.search(self.__checksumRe, data)
+        if match:
+            expectedChecksum = match.group(1)
+        else:
+            # consider it as valid
+            return True
+        
+        # normalize the data
+        data = re.sub(r"\r", "", data)              # normalize eol
+        data = re.sub(r"\n+", "\n", data)           # remove empty lines
+        data = re.sub(self.__checksumRe, "", data)  # remove checksum line
+        
+        # calculate checksum
+        md5 = hashlib.md5()
+        md5.update(data.encode("utf-8"))
+        calculatedChecksum = base64.b64encode(md5.digest()).decode()\
+            .rstrip("=")
+        if calculatedChecksum == expectedChecksum:
+            return True
+        else:
+            res = E5MessageBox.yesNo(
+                None,
+                self.tr("Downloading subscription rules"),
+                self.tr(
+                    """<p>AdBlock subscription <b>{0}</b> has a wrong"""
+                    """ checksum.<br/>"""
+                    """Found: {1}<br/>"""
+                    """Calculated: {2}<br/>"""
+                    """Use it anyway?</p>""")
+                .format(self.__title, expectedChecksum,
+                        calculatedChecksum))
+            return res
+    
+    def saveRules(self):
+        """
+        Public method to save the subscription rules.
+        """
+        fileName = self.rulesFileName()
+        if not fileName:
+            return
+        
+        f = QFile(fileName)
+        if not f.open(QIODevice.ReadWrite | QIODevice.Truncate):
+            E5MessageBox.warning(
+                None,
+                self.tr("Saving subscription rules"),
+                self.tr(
+                    """Unable to open AdBlock file '{0}' for writing.""")
+                .format(fileName))
+            return
+        
+        textStream = QTextStream(f)
+        if not self.__rules or not self.__rules[0].isHeader():
+            textStream << "[Adblock Plus 1.1.1]\n"
+        for rule in self.__rules:
+            textStream << rule.filter() << "\n"
+    
+    def match(self, req, urlDomain, urlString):
+        """
+        Public method to check the subscription for a matching rule.
+        
+        @param req reference to the network request (QWebEngineUrlRequestInfo)
+        @param urlDomain domain of the URL (string)
+        @param urlString URL (string)
+        @return reference to the rule object or None (AdBlockRule)
+        """
+        for rule in self.__networkExceptionRules:
+            if rule.networkMatch(req, urlDomain, urlString):
+                return None
+        
+        for rule in self.__networkBlockRules:
+            if rule.networkMatch(req, urlDomain, urlString):
+                return rule
+        
+        return None
+    
+    def adBlockDisabledForUrl(self, url):
+        """
+        Public method to check, if AdBlock is disabled for the given URL.
+        
+        @param url URL to check (QUrl)
+        @return flag indicating disabled state (boolean)
+        """
+        for rule in self.__documentRules:
+            if rule.urlMatch(url):
+                return True
+        
+        return False
+    
+    def elemHideDisabledForUrl(self, url):
+        """
+        Public method to check, if element hiding is disabled for the given
+        URL.
+        
+        @param url URL to check (QUrl)
+        @return flag indicating disabled state (boolean)
+        """
+        if self.adBlockDisabledForUrl(url):
+            return True
+        
+        for rule in self.__elemhideRules:
+            if rule.urlMatch(url):
+                return True
+        
+        return False
+    
+    def elementHidingRules(self):
+        """
+        Public method to get the element hiding rules.
+        
+        @return element hiding rules (string)
+        """
+        return self.__elementHidingRules
+    
+    def elementHidingRulesForDomain(self, domain):
+        """
+        Public method to get the element hiding rules for the given domain.
+        
+        @param domain domain name (string)
+        @return element hiding rules (string)
+        """
+        rules = ""
+        
+        for rule in self.__domainRestrictedCssRules:
+            if rule.matchDomain(domain):
+                rules += rule.cssSelector() + ","
+        
+        return rules
+    
+    def rule(self, offset):
+        """
+        Public method to get a specific rule.
+        
+        @param offset offset of the rule (integer)
+        @return requested rule (AdBlockRule)
+        """
+        if offset >= len(self.__rules):
+            return None
+        
+        return self.__rules[offset]
+    
+    def allRules(self):
+        """
+        Public method to get the list of rules.
+        
+        @return list of rules (list of AdBlockRule)
+        """
+        return self.__rules[:]
+    
+    def addRule(self, rule):
+        """
+        Public method to add a rule.
+        
+        @param rule reference to the rule to add (AdBlockRule)
+        @return offset of the rule (integer)
+        """
+        self.__rules.append(rule)
+        self.__populateCache()
+        self.rulesChanged.emit()
+        
+        return len(self.__rules) - 1
+    
+    def removeRule(self, offset):
+        """
+        Public method to remove a rule given the offset.
+        
+        @param offset offset of the rule to remove (integer)
+        """
+        if offset < 0 or offset > len(self.__rules):
+            return
+        
+        del self.__rules[offset]
+        self.__populateCache()
+        self.rulesChanged.emit()
+    
+    def replaceRule(self, rule, offset):
+        """
+        Public method to replace a rule given the offset.
+        
+        @param rule reference to the rule to set (AdBlockRule)
+        @param offset offset of the rule to remove (integer)
+        @return requested rule (AdBlockRule)
+        """
+        if offset >= len(self.__rules):
+            return None
+        
+        self.__rules[offset] = rule
+        self.__populateCache()
+        self.rulesChanged.emit()
+        
+        return self.__rules[offset]
+    
+    def __populateCache(self):
+        """
+        Private method to populate the various rule caches.
+        """
+        self.__networkExceptionRules = []
+        self.__networkBlockRules = []
+        self.__domainRestrictedCssRules = []
+        self.__elementHidingRules = ""
+        self.__documentRules = []
+        self.__elemhideRules = []
+        
+        for rule in self.__rules:
+            if not rule.isEnabled():
+                continue
+            
+            if rule.isCSSRule():
+                if rule.isDomainRestricted():
+                    self.__domainRestrictedCssRules.append(rule)
+                else:
+                    self.__elementHidingRules += rule.cssSelector() + ","
+            elif rule.isDocument():
+                self.__documentRules.append(rule)
+            elif rule.isElementHiding():
+                self.__elemhideRules.append(rule)
+            elif rule.isException():
+                self.__networkExceptionRules.append(rule)
+            else:
+                self.__networkBlockRules.append(rule)
+    
+    def canEditRules(self):
+        """
+        Public method to check, if rules can be edited.
+        
+        @return flag indicating rules may be edited (boolean)
+        """
+        return self.__custom
+    
+    def canBeRemoved(self):
+        """
+        Public method to check, if the subscription can be removed.
+        
+        @return flag indicating removal is allowed (boolean)
+        """
+        return not self.__custom and not self.__defaultSubscription
+    
+    def setRuleEnabled(self, offset, enabled):
+        """
+        Public method to enable a specific rule.
+        
+        @param offset offset of the rule (integer)
+        @param enabled new enabled state (boolean)
+        @return reference to the changed rule (AdBlockRule)
+        """
+        if offset >= len(self.__rules):
+            return None
+        
+        rule = self.__rules[offset]
+        rule.setEnabled(enabled)
+        if rule.isCSSRule():
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            self.__populateCache()
+            WebBrowserWindow.mainWindow().reloadUserStyleSheet()
+        
+        return rule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockTreeWidget.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,262 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a tree widget for the AdBlock configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QFont, QColor
+from PyQt5.QtWidgets import QAbstractItemView, QTreeWidgetItem, QInputDialog, \
+    QLineEdit, QMenu, QApplication
+
+from E5Gui.E5TreeWidget import E5TreeWidget
+
+
+class AdBlockTreeWidget(E5TreeWidget):
+    """
+    Class implementing a tree widget for the AdBlock configuration dialog.
+    """
+    def __init__(self, subscription, parent=None):
+        """
+        Constructor
+        
+        @param subscription reference to the subscription (AdBlockSubscription)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(AdBlockTreeWidget, self).__init__(parent)
+        
+        self.__subscription = subscription
+        self.__topItem = None
+        self.__ruleToBeSelected = ""
+        self.__itemChangingBlock = False
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.setDefaultItemShowMode(E5TreeWidget.ItemsExpanded)
+        self.setHeaderHidden(True)
+        self.setAlternatingRowColors(True)
+        
+        self.customContextMenuRequested.connect(self.__contextMenuRequested)
+        self.itemChanged.connect(self.__itemChanged)
+        self.__subscription.changed.connect(self.__subscriptionChanged)
+        self.__subscription.rulesChanged.connect(self.__subscriptionChanged)
+    
+    def subscription(self):
+        """
+        Public method to get a reference to the subscription.
+        
+        @return reference to the subscription (AdBlockSubscription)
+        """
+        return self.__subscription
+    
+    def showRule(self, rule):
+        """
+        Public method to highlight the given rule.
+        
+        @param rule AdBlock rule to be shown (AdBlockRule)
+        """
+        if rule:
+            self.__ruleToBeSelected = rule.filter()
+        if not self.__topItem:
+            return
+        if self.__ruleToBeSelected:
+            items = self.findItems(self.__ruleToBeSelected, Qt.MatchRecursive)
+            if items:
+                item = items[0]
+                self.setCurrentItem(item)
+                self.scrollToItem(item, QAbstractItemView.PositionAtCenter)
+            
+            self.__ruleToBeSelected = ""
+    
+    def refresh(self):
+        """
+        Public method to refresh the tree.
+        """
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        self.__itemChangingBlock = True
+        self.clear()
+        
+        boldFont = QFont()
+        boldFont.setBold(True)
+        
+        self.__topItem = QTreeWidgetItem(self)
+        self.__topItem.setText(0, self.__subscription.title())
+        self.__topItem.setFont(0, boldFont)
+        self.addTopLevelItem(self.__topItem)
+        
+        allRules = self.__subscription.allRules()
+        
+        index = 0
+        for rule in allRules:
+            item = QTreeWidgetItem(self.__topItem)
+            item.setText(0, rule.filter())
+            item.setData(0, Qt.UserRole, index)
+            if self.__subscription.canEditRules():
+                item.setFlags(item.flags() | Qt.ItemIsEditable)
+            self.__adjustItemFeatures(item, rule)
+            index += 1
+        
+        self.expandAll()
+        self.showRule(None)
+        self.__itemChangingBlock = False
+        QApplication.restoreOverrideCursor()
+        QApplication.processEvents()
+    
+    def addRule(self, filter=""):
+        """
+        Public slot to add a new rule.
+        
+        @param filter filter to be added (string)
+        """
+        if not self.__subscription.canEditRules():
+            return
+        
+        if not filter:
+            filter, ok = QInputDialog.getText(
+                self,
+                self.tr("Add Custom Rule"),
+                self.tr("Write your rule here:"),
+                QLineEdit.Normal)
+            if not ok or filter == "":
+                return
+        
+        from .AdBlockRule import AdBlockRule
+        rule = AdBlockRule(filter, self.__subscription)
+        self.__subscription.addRule(rule)
+    
+    def removeRule(self):
+        """
+        Public slot to remove the current rule.
+        """
+        item = self.currentItem()
+        if item is None or \
+           not self.__subscription.canEditRules() or \
+           item == self.__topItem:
+            return
+        
+        offset = item.data(0, Qt.UserRole)
+        self.__subscription.removeRule(offset)
+        self.deleteItem(item)
+    
+    def __contextMenuRequested(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos position for the menu (QPoint)
+        """
+        if not self.__subscription.canEditRules():
+            return
+        
+        item = self.itemAt(pos)
+        if item is None:
+            return
+        
+        menu = QMenu()
+        menu.addAction(self.tr("Add Rule"), self.addRule)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("Remove Rule"), self.removeRule)
+        if item.parent() is None:
+            act.setDisabled(True)
+        
+        menu.exec_(self.viewport().mapToGlobal(pos))
+    
+    def __itemChanged(self, itm):
+        """
+        Private slot to handle the change of an item.
+        
+        @param itm changed item (QTreeWidgetItem)
+        """
+        if itm is None or self.__itemChangingBlock:
+            return
+        
+        self.__itemChangingBlock = True
+        
+        offset = itm.data(0, Qt.UserRole)
+        oldRule = self.__subscription.rule(offset)
+        
+        if itm.checkState(0) == Qt.Unchecked and oldRule.isEnabled():
+            # Disable rule
+            rule = self.__subscription.setRuleEnabled(offset, False)
+            self.__adjustItemFeatures(itm, rule)
+        elif itm.checkState(0) == Qt.Checked and not oldRule.isEnabled():
+            # Enable rule
+            rule = self.__subscription.setRuleEnabled(offset, True)
+            self.__adjustItemFeatures(itm, rule)
+        elif self.__subscription.canEditRules():
+            from .AdBlockRule import AdBlockRule
+            # Custom rule has been changed
+            rule = self.__subscription.replaceRule(
+                AdBlockRule(itm.text(0), self.__subscription), offset)
+            self.__adjustItemFeatures(itm, rule)
+        
+        self.__itemChangingBlock = False
+    
+    def __copyFilter(self):
+        """
+        Private slot to copy the current filter to the clipboard.
+        """
+        item = self.currentItem()
+        if item is not None:
+            QApplication.clipboard().setText(item.text(0))
+    
+    def __subscriptionChanged(self):
+        """
+        Private slot handling a subscription change.
+        """
+        self.refresh()
+        
+        self.__itemChangingBlock = True
+        self.__topItem.setText(
+            0, self.tr("{0} (recently updated)").format(
+                self.__subscription.title()))
+        self.__itemChangingBlock = False
+    
+    def __adjustItemFeatures(self, itm, rule):
+        """
+        Private method to adjust an item.
+        
+        @param itm item to be adjusted (QTreeWidgetItem)
+        @param rule rule for the adjustment (AdBlockRule)
+        """
+        if not rule.isEnabled():
+            font = QFont()
+            font.setItalic(True)
+            itm.setForeground(0, QColor(Qt.gray))
+            
+            if not rule.isComment() and not rule.isHeader():
+                itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable)
+                itm.setCheckState(0, Qt.Unchecked)
+                itm.setFont(0, font)
+            
+            return
+        
+        itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable)
+        itm.setCheckState(0, Qt.Checked)
+        
+        if rule.isCSSRule():
+            itm.setForeground(0, QColor(Qt.darkBlue))
+            itm.setFont(0, QFont())
+        elif rule.isException():
+            itm.setForeground(0, QColor(Qt.darkGreen))
+            itm.setFont(0, QFont())
+        else:
+            itm.setForeground(0, QColor())
+            itm.setFont(0, QFont())
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method handling key presses.
+        
+        @param evt key press event (QKeyEvent)
+        """
+        if evt.key() == Qt.Key_C and \
+           evt.modifiers() & Qt.ControlModifier:
+            self.__copyFilter()
+        elif evt.key() == Qt.Key_Delete:
+            self.removeRule()
+        else:
+            super(AdBlockTreeWidget, self).keyPressEvent(evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockUrlInterceptor.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an URL interceptor base class.
+"""
+
+from __future__ import unicode_literals
+
+from ..Network.UrlInterceptor import UrlInterceptor
+
+class AdBlockUrlInterceptor(UrlInterceptor):
+    """
+    Class implementing an URL interceptor for AdBlock.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the AdBlock manager
+        @type AdBlockManager
+        @param parent referemce to the parent object
+        @type QObject
+        """
+        super(AdBlockUrlInterceptor, self).__init__(parent)
+        
+        self.__manager = manager
+    
+    def interceptRequest(self, info):
+        """
+        Public method to intercept a request.
+        
+        @param info request info object
+        @type QWebEngineUrlRequestInfo
+        """
+        if self.__manager.block(info):
+            info.block(True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/__init__.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the advertisements blocker functionality.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/AddBookmarkDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to add a bookmark or a bookmark folder.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QModelIndex, QSortFilterProxyModel
+from PyQt5.QtWidgets import QDialog, QTreeView
+
+from .Ui_AddBookmarkDialog import Ui_AddBookmarkDialog
+
+
+class AddBookmarkProxyModel(QSortFilterProxyModel):
+    """
+    Class implementing a proxy model used by the AddBookmarkDialog dialog.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(AddBookmarkProxyModel, self).__init__(parent)
+    
+    def columnCount(self, parent):
+        """
+        Public method to return the number of columns.
+        
+        @param parent index of the parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        return min(1, QSortFilterProxyModel.columnCount(self, parent))
+    
+    def filterAcceptsRow(self, sourceRow, sourceParent):
+        """
+        Public method to determine, if the row is acceptable.
+        
+        @param sourceRow row number in the source model (integer)
+        @param sourceParent index of the source item (QModelIndex)
+        @return flag indicating acceptance (boolean)
+        """
+        idx = self.sourceModel().index(sourceRow, 0, sourceParent)
+        return self.sourceModel().hasChildren(idx)
+    
+    def filterAcceptsColumn(self, sourceColumn, sourceParent):
+        """
+        Public method to determine, if the column is acceptable.
+        
+        @param sourceColumn column number in the source model (integer)
+        @param sourceParent index of the source item (QModelIndex)
+        @return flag indicating acceptance (boolean)
+        """
+        return sourceColumn == 0
+    
+    def hasChildren(self, parent=QModelIndex()):
+        """
+        Public method to check, if a parent node has some children.
+        
+        @param parent index of the parent node (QModelIndex)
+        @return flag indicating the presence of children (boolean)
+        """
+        sindex = self.mapToSource(parent)
+        return self.sourceModel().hasChildren(sindex)
+
+
+class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog):
+    """
+    Class implementing a dialog to add a bookmark or a bookmark folder.
+    """
+    def __init__(self, parent=None, bookmarksManager=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        @param bookmarksManager reference to the bookmarks manager
+            object (BookmarksManager)
+        """
+        super(AddBookmarkDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__bookmarksManager = bookmarksManager
+        self.__addedNode = None
+        self.__addFolder = False
+        
+        if self.__bookmarksManager is None:
+            import WebBrowser.WebBrowserWindow
+            self.__bookmarksManager = \
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.bookmarksManager()
+        
+        self.__proxyModel = AddBookmarkProxyModel(self)
+        model = self.__bookmarksManager.bookmarksModel()
+        self.__proxyModel.setSourceModel(model)
+        
+        self.__treeView = QTreeView(self)
+        self.__treeView.setModel(self.__proxyModel)
+        self.__treeView.expandAll()
+        self.__treeView.header().setStretchLastSection(True)
+        self.__treeView.header().hide()
+        self.__treeView.setItemsExpandable(False)
+        self.__treeView.setRootIsDecorated(False)
+        self.__treeView.setIndentation(10)
+        self.__treeView.show()
+        
+        self.locationCombo.setModel(self.__proxyModel)
+        self.locationCombo.setView(self.__treeView)
+        
+        self.addressEdit.setInactiveText(self.tr("Url"))
+        self.nameEdit.setInactiveText(self.tr("Title"))
+        
+        self.resize(self.sizeHint())
+    
+    def setUrl(self, url):
+        """
+        Public slot to set the URL of the new bookmark.
+        
+        @param url URL of the bookmark (string)
+        """
+        self.addressEdit.setText(url)
+        self.resize(self.sizeHint())
+    
+    def url(self):
+        """
+        Public method to get the URL of the bookmark.
+        
+        @return URL of the bookmark (string)
+        """
+        return self.addressEdit.text()
+    
+    def setTitle(self, title):
+        """
+        Public method to set the title of the new bookmark.
+        
+        @param title title of the bookmark (string)
+        """
+        self.nameEdit.setText(title)
+    
+    def title(self):
+        """
+        Public method to get the title of the bookmark.
+        
+        @return title of the bookmark (string)
+        """
+        return self.nameEdit.text()
+    
+    def setDescription(self, description):
+        """
+        Public method to set the description of the new bookmark.
+        
+        @param description description of the bookamrk (string)
+        """
+        self.descriptionEdit.setPlainText(description)
+    
+    def description(self):
+        """
+        Public method to get the description of the bookmark.
+        
+        @return description of the bookamrk (string)
+        """
+        return self.descriptionEdit.toPlainText()
+    
+    def setCurrentIndex(self, idx):
+        """
+        Public method to set the current index.
+        
+        @param idx current index to be set (QModelIndex)
+        """
+        proxyIndex = self.__proxyModel.mapFromSource(idx)
+        self.__treeView.setCurrentIndex(proxyIndex)
+        self.locationCombo.setCurrentIndex(proxyIndex.row())
+    
+    def currentIndex(self):
+        """
+        Public method to get the current index.
+        
+        @return current index (QModelIndex)
+        """
+        idx = self.locationCombo.view().currentIndex()
+        idx = self.__proxyModel.mapToSource(idx)
+        return idx
+    
+    def setFolder(self, folder):
+        """
+        Public method to set the dialog to "Add Folder" mode.
+        
+        @param folder flag indicating "Add Folder" mode (boolean)
+        """
+        self.__addFolder = folder
+        
+        if folder:
+            self.setWindowTitle(self.tr("Add Folder"))
+            self.addressEdit.setVisible(False)
+        else:
+            self.setWindowTitle(self.tr("Add Bookmark"))
+            self.addressEdit.setVisible(True)
+        
+        self.resize(self.sizeHint())
+    
+    def isFolder(self):
+        """
+        Public method to test, if the dialog is in "Add Folder" mode.
+        
+        @return flag indicating "Add Folder" mode (boolean)
+        """
+        return self.__addFolder
+    
+    def addedNode(self):
+        """
+        Public method to get a reference to the added bookmark node.
+        
+        @return reference to the added bookmark node (BookmarkNode)
+        """
+        return self.__addedNode
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        if (not self.__addFolder and not self.addressEdit.text()) or \
+           not self.nameEdit.text():
+            super(AddBookmarkDialog, self).accept()
+            return
+        
+        from .BookmarkNode import BookmarkNode
+        
+        idx = self.currentIndex()
+        if not idx.isValid():
+            idx = self.__bookmarksManager.bookmarksModel().index(0, 0)
+        parent = self.__bookmarksManager.bookmarksModel().node(idx)
+        
+        if self.__addFolder:
+            type_ = BookmarkNode.Folder
+        else:
+            type_ = BookmarkNode.Bookmark
+        bookmark = BookmarkNode(type_)
+        bookmark.title = self.nameEdit.text()
+        if not self.__addFolder:
+            bookmark.url = self.addressEdit.text()
+        bookmark.desc = self.descriptionEdit.toPlainText()
+        
+        self.__bookmarksManager.addBookmark(parent, bookmark)
+        self.__addedNode = bookmark
+        
+        super(AddBookmarkDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/AddBookmarkDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddBookmarkDialog</class>
+ <widget class="QDialog" name="AddBookmarkDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>230</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>500</width>
+    <height>250</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Add Bookmark</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="E5LineEdit" name="nameEdit">
+     <property name="toolTip">
+      <string>Enter the name</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Address:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="E5LineEdit" name="addressEdit">
+     <property name="toolTip">
+      <string>Enter the address</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Description:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QPlainTextEdit" name="descriptionEdit">
+     <property name="toolTip">
+      <string>Enter a description</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Folder:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QComboBox" name="locationCombo"/>
+   </item>
+   <item row="4" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5LineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>addressEdit</tabstop>
+  <tabstop>descriptionEdit</tabstop>
+  <tabstop>locationCombo</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AddBookmarkDialog</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>AddBookmarkDialog</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/Bookmarks/BookmarkNode.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmark node.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QDateTime
+
+
+class BookmarkNode(object):
+    """
+    Class implementing the bookmark node type.
+    """
+    # possible bookmark node types
+    Root = 0
+    Folder = 1
+    Bookmark = 2
+    Separator = 3
+    
+    # possible timestamp types
+    TsAdded = 0
+    TsModified = 1
+    TsVisited = 2
+    
+    def __init__(self, type_=Root, parent=None):
+        """
+        Constructor
+        
+        @param type_ type of the bookmark node (BookmarkNode.Type)
+        @param parent reference to the parent node (BookmarkNode)
+        """
+        self.url = ""
+        self.title = ""
+        self.desc = ""
+        self.expanded = False
+        self.added = QDateTime()
+        self.modified = QDateTime()
+        self.visited = QDateTime()
+        
+        self._children = []
+        self._parent = parent
+        self._type = type_
+        
+        if parent is not None:
+            parent.add(self)
+    
+    def type(self):
+        """
+        Public method to get the bookmark's type.
+        
+        @return bookmark type (BookmarkNode.Type)
+        """
+        return self._type
+    
+    def setType(self, type_):
+        """
+        Public method to set the bookmark's type.
+        
+        @param type_ type of the bookmark node (BookmarkNode.Type)
+        """
+        self._type = type_
+    
+    def children(self):
+        """
+        Public method to get the list of child nodes.
+        
+        @return list of all child nodes (list of BookmarkNode)
+        """
+        return self._children[:]
+    
+    def parent(self):
+        """
+        Public method to get a reference to the parent node.
+        
+        @return reference to the parent node (BookmarkNode)
+        """
+        return self._parent
+    
+    def add(self, child, offset=-1):
+        """
+        Public method to add/insert a child node.
+        
+        @param child reference to the node to add (BookmarkNode)
+        @param offset position where to insert child (integer, -1 = append)
+        """
+        if child._type == BookmarkNode.Root:
+            return
+        
+        if child._parent is not None:
+            child._parent.remove(child)
+        
+        child._parent = self
+        if offset == -1:
+            self._children.append(child)
+        else:
+            self._children.insert(offset, child)
+    
+    def remove(self, child):
+        """
+        Public method to remove a child node.
+        
+        @param child reference to the child node (BookmarkNode)
+        """
+        child._parent = None
+        if child in self._children:
+            self._children.remove(child)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarkPropertiesDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show and edit bookmark properties.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_BookmarkPropertiesDialog import Ui_BookmarkPropertiesDialog
+
+
+class BookmarkPropertiesDialog(QDialog, Ui_BookmarkPropertiesDialog):
+    """
+    Class implementing a dialog to show and edit bookmark properties.
+    """
+    def __init__(self, node, parent=None):
+        """
+        Constructor
+        
+        @param node reference to the bookmark (BookmarkNode)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(BookmarkPropertiesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        from .BookmarkNode import BookmarkNode
+        self.__node = node
+        if self.__node.type() == BookmarkNode.Folder:
+            self.addressLabel.hide()
+            self.addressEdit.hide()
+        
+        self.nameEdit.setText(self.__node.title)
+        self.descriptionEdit.setPlainText(self.__node.desc)
+        self.addressEdit.setText(self.__node.url)
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        from .BookmarkNode import BookmarkNode
+        
+        if (self.__node.type() == BookmarkNode.Bookmark and
+            not self.addressEdit.text()) or \
+           not self.nameEdit.text():
+            super(BookmarkPropertiesDialog, self).accept()
+            return
+        
+        import WebBrowser.WebBrowserWindow
+        bookmarksManager = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .bookmarksManager()
+        title = self.nameEdit.text()
+        if title != self.__node.title:
+            bookmarksManager.setTitle(self.__node, title)
+        if self.__node.type() == BookmarkNode.Bookmark:
+            url = self.addressEdit.text()
+            if url != self.__node.url:
+                bookmarksManager.setUrl(self.__node, url)
+        description = self.descriptionEdit.toPlainText()
+        if description != self.__node.desc:
+            self.__node.desc = description
+            bookmarksManager.setNodeChanged(self.__node)
+        
+        super(BookmarkPropertiesDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarkPropertiesDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarkPropertiesDialog</class>
+ <widget class="QDialog" name="BookmarkPropertiesDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>221</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>500</width>
+    <height>250</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Bookmark Properties</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="E5LineEdit" name="nameEdit">
+     <property name="toolTip">
+      <string>Enter the name</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="addressLabel">
+     <property name="text">
+      <string>Address:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="E5LineEdit" name="addressEdit">
+     <property name="toolTip">
+      <string>Enter the address</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Description:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QPlainTextEdit" name="descriptionEdit">
+     <property name="toolTip">
+      <string>Enter a description</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5LineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>addressEdit</tabstop>
+  <tabstop>descriptionEdit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>BookmarkPropertiesDialog</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>BookmarkPropertiesDialog</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/Bookmarks/BookmarksDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,266 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QModelIndex
+from PyQt5.QtGui import QFontMetrics, QCursor
+from PyQt5.QtWidgets import QDialog, QMenu, QApplication
+
+from E5Gui.E5TreeSortFilterProxyModel import E5TreeSortFilterProxyModel
+
+from .Ui_BookmarksDialog import Ui_BookmarksDialog
+
+
+class BookmarksDialog(QDialog, Ui_BookmarksDialog):
+    """
+    Class implementing a dialog to manage bookmarks.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, parent=None, manager=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget
+        @param manager reference to the bookmarks manager object
+            (BookmarksManager)
+        """
+        super(BookmarksDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__bookmarksManager = manager
+        if self.__bookmarksManager is None:
+            import WebBrowser.WebBrowserWindow
+            self.__bookmarksManager = WebBrowser.WebBrowserWindow\
+                .WebBrowserWindow.bookmarksManager()
+        
+        self.__bookmarksModel = self.__bookmarksManager.bookmarksModel()
+        self.__proxyModel = E5TreeSortFilterProxyModel(self)
+        self.__proxyModel.setFilterKeyColumn(-1)
+        self.__proxyModel.setSourceModel(self.__bookmarksModel)
+        
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        
+        self.bookmarksTree.setModel(self.__proxyModel)
+        self.bookmarksTree.setExpanded(self.__proxyModel.index(0, 0), True)
+        fm = QFontMetrics(self.font())
+        header = fm.width("m") * 40
+        self.bookmarksTree.header().resizeSection(0, header)
+        self.bookmarksTree.header().setStretchLastSection(True)
+        self.bookmarksTree.setContextMenuPolicy(Qt.CustomContextMenu)
+        
+        self.bookmarksTree.activated.connect(self.__activated)
+        self.bookmarksTree.customContextMenuRequested.connect(
+            self.__customContextMenuRequested)
+        
+        self.removeButton.clicked.connect(
+            self.bookmarksTree.removeSelected)
+        self.addFolderButton.clicked.connect(self.__newFolder)
+        
+        self.__expandNodes(self.__bookmarksManager.bookmarks())
+    
+    def closeEvent(self, evt):
+        """
+        Protected method to handle the closing of the dialog.
+        
+        @param evt reference to the event object (QCloseEvent) (ignored)
+        """
+        self.__shutdown()
+    
+    def reject(self):
+        """
+        Public method called when the dialog is rejected.
+        """
+        self.__shutdown()
+        super(BookmarksDialog, self).reject()
+    
+    def __shutdown(self):
+        """
+        Private method to perform shutdown actions for the dialog.
+        """
+        if self.__saveExpandedNodes(self.bookmarksTree.rootIndex()):
+            self.__bookmarksManager.changeExpanded()
+    
+    def __saveExpandedNodes(self, parent):
+        """
+        Private method to save the child nodes of an expanded node.
+        
+        @param parent index of the parent node (QModelIndex)
+        @return flag indicating a change (boolean)
+        """
+        changed = False
+        for row in range(self.__proxyModel.rowCount(parent)):
+            child = self.__proxyModel.index(row, 0, parent)
+            sourceIndex = self.__proxyModel.mapToSource(child)
+            childNode = self.__bookmarksModel.node(sourceIndex)
+            wasExpanded = childNode.expanded
+            if self.bookmarksTree.isExpanded(child):
+                childNode.expanded = True
+                changed |= self.__saveExpandedNodes(child)
+            else:
+                childNode.expanded = False
+            changed |= (wasExpanded != childNode.expanded)
+        
+        return changed
+    
+    def __expandNodes(self, node):
+        """
+        Private method to expand all child nodes of a node.
+        
+        @param node reference to the bookmark node to expand (BookmarkNode)
+        """
+        for childNode in node.children():
+            if childNode.expanded:
+                idx = self.__bookmarksModel.nodeIndex(childNode)
+                idx = self.__proxyModel.mapFromSource(idx)
+                self.bookmarksTree.setExpanded(idx, True)
+                self.__expandNodes(childNode)
+    
+    def __customContextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request for the bookmarks tree.
+        
+        @param pos position the context menu was requested (QPoint)
+        """
+        from .BookmarkNode import BookmarkNode
+        
+        menu = QMenu()
+        idx = self.bookmarksTree.indexAt(pos)
+        idx = idx.sibling(idx.row(), 0)
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        node = self.__bookmarksModel.node(sourceIndex)
+        if idx.isValid() and node.type() != BookmarkNode.Folder:
+            menu.addAction(
+                self.tr("&Open"), self.__openBookmarkInCurrentTab)
+            menu.addAction(
+                self.tr("Open in New &Tab"), self.__openBookmarkInNewTab)
+            menu.addSeparator()
+        act = menu.addAction(self.tr("Edit &Name"), self.__editName)
+        act.setEnabled(idx.flags() & Qt.ItemIsEditable)
+        if idx.isValid() and node.type() != BookmarkNode.Folder:
+            menu.addAction(self.tr("Edit &Address"), self.__editAddress)
+        menu.addSeparator()
+        act = menu.addAction(
+            self.tr("&Delete"), self.bookmarksTree.removeSelected)
+        act.setEnabled(idx.flags() & Qt.ItemIsDragEnabled)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("&Properties..."), self.__edit)
+        act.setEnabled(idx.flags() & Qt.ItemIsEditable)
+        menu.exec_(QCursor.pos())
+    
+    def __activated(self, idx):
+        """
+        Private slot to handle the activation of an entry.
+        
+        @param idx reference to the entry index (QModelIndex)
+        """
+        self.__openBookmark(
+            QApplication.keyboardModifiers() & Qt.ControlModifier)
+        
+    def __openBookmarkInCurrentTab(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        self.__openBookmark(False)
+    
+    def __openBookmarkInNewTab(self):
+        """
+        Private slot to open a bookmark in a new browser tab.
+        """
+        self.__openBookmark(True)
+    
+    def __openBookmark(self, newTab):
+        """
+        Private method to open a bookmark.
+        
+        @param newTab flag indicating to open the bookmark in a new tab
+            (boolean)
+        """
+        from .BookmarkNode import BookmarkNode
+        from .BookmarksModel import BookmarksModel
+        
+        idx = self.bookmarksTree.currentIndex()
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        node = self.__bookmarksModel.node(sourceIndex)
+        if not idx.parent().isValid() or \
+           node is None or \
+           node.type() == BookmarkNode.Folder:
+            return
+        if newTab:
+            self.newUrl.emit(
+                idx.sibling(idx.row(), 1).data(BookmarksModel.UrlRole),
+                idx.sibling(idx.row(), 0).data(Qt.DisplayRole))
+        else:
+            self.openUrl.emit(
+                idx.sibling(idx.row(), 1).data(BookmarksModel.UrlRole),
+                idx.sibling(idx.row(), 0).data(Qt.DisplayRole))
+    
+    def __editName(self):
+        """
+        Private slot to edit the name part of a bookmark.
+        """
+        idx = self.bookmarksTree.currentIndex()
+        idx = idx.sibling(idx.row(), 0)
+        self.bookmarksTree.edit(idx)
+    
+    def __editAddress(self):
+        """
+        Private slot to edit the address part of a bookmark.
+        """
+        idx = self.bookmarksTree.currentIndex()
+        idx = idx.sibling(idx.row(), 1)
+        self.bookmarksTree.edit(idx)
+    
+    def __edit(self):
+        """
+        Private slot to edit a bookmarks properties.
+        """
+        from .BookmarkPropertiesDialog import BookmarkPropertiesDialog
+        
+        idx = self.bookmarksTree.currentIndex()
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        node = self.__bookmarksModel.node(sourceIndex)
+        dlg = BookmarkPropertiesDialog(node)
+        dlg.exec_()
+    
+    def __newFolder(self):
+        """
+        Private slot to add a new bookmarks folder.
+        """
+        from .BookmarkNode import BookmarkNode
+        
+        currentIndex = self.bookmarksTree.currentIndex()
+        idx = QModelIndex(currentIndex)
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        sourceNode = self.__bookmarksModel.node(sourceIndex)
+        row = -1    # append new folder as the last item per default
+        
+        if sourceNode is not None and \
+           sourceNode.type() != BookmarkNode.Folder:
+            # If the selected item is not a folder, add a new folder to the
+            # parent folder, but directly below the selected item.
+            idx = idx.parent()
+            row = currentIndex.row() + 1
+        
+        if not idx.isValid():
+            # Select bookmarks menu as default.
+            idx = self.__proxyModel.index(1, 0)
+        
+        idx = self.__proxyModel.mapToSource(idx)
+        parent = self.__bookmarksModel.node(idx)
+        node = BookmarkNode(BookmarkNode.Folder)
+        node.title = self.tr("New Folder")
+        self.__bookmarksManager.addBookmark(parent, node, row)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarksDialog</class>
+ <widget class="QDialog" name="BookmarksDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>750</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Manage Bookmarks</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <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>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="E5ClearableLineEdit" name="searchEdit">
+         <property name="toolTip">
+          <string>Enter search term for bookmarks</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="E5TreeView" name="bookmarksTree">
+     <property name="dragDropMode">
+      <enum>QAbstractItemView::InternalMove</enum>
+     </property>
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::ExtendedSelection</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="uniformRowHeights">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to delete the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Delete</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="addFolderButton">
+       <property name="toolTip">
+        <string>Press to add a new bookmarks folder</string>
+       </property>
+       <property name="text">
+        <string>Add &amp;Folder</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="spacer">
+       <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="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TreeView</class>
+   <extends>QTreeView</extends>
+   <header>E5Gui/E5TreeView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>bookmarksTree</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>addFolderButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>BookmarksDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>252</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>BookmarksDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>445</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/Bookmarks/BookmarksImportDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog for importing bookmarks from other sources.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, Qt, QSize
+from PyQt5.QtWidgets import QDialog, QListWidgetItem
+
+from E5Gui import E5MessageBox
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_BookmarksImportDialog import Ui_BookmarksImportDialog
+
+from . import BookmarksImporters
+
+import Globals
+
+
+class BookmarksImportDialog(QDialog, Ui_BookmarksImportDialog):
+    """
+    Class implementing a dialog for importing bookmarks from other sources.
+    """
+    SourcesListIdRole = Qt.UserRole
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(BookmarksImportDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.filePicker.setMode(E5PathPickerModes.OpenFileMode)
+        
+        self.sourcesList.setIconSize(QSize(48, 48))
+        for icon, displayText, idText in BookmarksImporters.getImporters():
+            itm = QListWidgetItem(icon, displayText, self.sourcesList)
+            itm.setData(self.SourcesListIdRole, idText)
+        
+        self.__currentPage = 0
+        self.__selectedSource = ""
+        self.__topLevelBookmarkNode = None
+        self.__sourceFile = ""
+        self.__sourceDir = ""
+        
+        self.pagesWidget.setCurrentIndex(self.__currentPage)
+        self.__enableNextButton()
+    
+    def __enableNextButton(self):
+        """
+        Private slot to set the enabled state of the next button.
+        """
+        if self.__currentPage == 0:
+            self.nextButton.setEnabled(
+                len(self.sourcesList.selectedItems()) == 1)
+        elif self.__currentPage == 1:
+            self.nextButton.setEnabled(self.filePicker.text() != "")
+    
+    @pyqtSlot()
+    def on_sourcesList_itemSelectionChanged(self):
+        """
+        Private slot to handle changes of the selection of the import source.
+        """
+        self.__enableNextButton()
+    
+    @pyqtSlot(str)
+    def on_filePicker_textChanged(self, txt):
+        """
+        Private slot handling changes of the file to import bookmarks form.
+        
+        @param txt text of the line edit (string)
+        """
+        self.__enableNextButton()
+    
+    @pyqtSlot()
+    def on_nextButton_clicked(self):
+        """
+        Private slot to switch to the next page.
+        """
+        if self.sourcesList.currentItem() is None:
+            return
+        
+        if self.__currentPage == 0:
+            self.__selectedSource = self.sourcesList.currentItem().data(
+                self.SourcesListIdRole)
+            (pixmap, sourceName, self.__sourceFile, info, prompt,
+             self.__sourceDir) = BookmarksImporters.getImporterInfo(
+                self.__selectedSource)
+            
+            self.iconLabel.setPixmap(pixmap)
+            self.importingFromLabel.setText(
+                self.tr("<b>Importing from {0}</b>").format(sourceName))
+            self.fileLabel1.setText(info)
+            self.fileLabel2.setText(prompt)
+            self.standardDirLabel.setText(
+                "<i>{0}</i>".format(self.__sourceDir))
+            
+            self.nextButton.setText(self.tr("Finish"))
+            
+            self.__currentPage += 1
+            self.pagesWidget.setCurrentIndex(self.__currentPage)
+            self.__enableNextButton()
+            
+            if self.__selectedSource == "ie":
+                self.filePicker.setMode(E5PathPickerModes.DirectoryMode)
+            else:
+                self.filePicker.setMode(E5PathPickerModes.OpenFileMode)
+                if Globals.isMacPlatform():
+                    filter = "*{0}".format(
+                        os.path.splitext(self.__sourceFile)[1])
+                else:
+                    filter = self.__sourceFile
+                self.filePicker.setFilters(filter)
+            self.filePicker.setDefaultDirectory(self.__sourceDir)
+        
+        elif self.__currentPage == 1:
+            if self.filePicker.text() == "":
+                return
+            
+            importer = BookmarksImporters.getImporter(self.__selectedSource)
+            importer.setPath(self.filePicker.text())
+            if importer.open():
+                self.__topLevelBookmarkNode = importer.importedBookmarks()
+            if importer.error():
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Error importing bookmarks"),
+                    importer.errorString())
+                return
+            
+            self.accept()
+    
+    @pyqtSlot()
+    def on_cancelButton_clicked(self):
+        """
+        Private slot documentation goes here.
+        """
+        self.reject()
+    
+    def getImportedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return top level bookmark (BookmarkNode)
+        """
+        return self.__topLevelBookmarkNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImportDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarksImportDialog</class>
+ <widget class="QDialog" name="BookmarksImportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>354</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>550</width>
+    <height>350</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Import Bookmarks</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_3">
+   <item>
+    <widget class="QStackedWidget" name="pagesWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="sourcePage">
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Choose source from which you want to import bookmarks:</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QListWidget" name="sourcesList">
+         <property name="toolTip">
+          <string>Choose the source to import from</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="filePage">
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <widget class="QLabel" name="iconLabel">
+           <property name="minimumSize">
+            <size>
+             <width>48</width>
+             <height>48</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>48</width>
+             <height>48</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="importingFromLabel"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>44</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QLabel" name="fileLabel1">
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="standardDirLabel">
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="fileLabel2"/>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>44</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="E5PathPicker" name="filePicker" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="focusPolicy">
+          <enum>Qt::StrongFocus</enum>
+         </property>
+         <property name="toolTip">
+          <string>Enter the name of the bookmarks file or directory</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>44</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <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="QPushButton" name="nextButton">
+       <property name="text">
+        <string>Next &gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>sourcesList</tabstop>
+  <tabstop>nextButton</tabstop>
+  <tabstop>cancelButton</tabstop>
+  <tabstop>filePicker</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/BookmarksImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a base class for the bookmarks importers.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+
+
+class BookmarksImporter(QObject):
+    """
+    Class implementing the base class for the bookmarks importers.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(BookmarksImporter, self).__init__(parent)
+        
+        self._path = ""
+        self._file = ""
+        self._error = False
+        self._errorString = ""
+        self._id = id
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        @exception NotImplementedError raised to indicate this method must
+            be implemented by a subclass
+        """
+        raise NotImplementedError
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        It must return a flag indicating success (boolean).
+        
+        @exception NotImplementedError raised to indicate this method must
+            be implemented by a subclass
+        """
+        raise NotImplementedError
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        It must return the imported bookmarks (BookmarkNode).
+        
+        @exception NotImplementedError raised to indicate this method must
+            be implemented by a subclass
+        """
+        raise NotImplementedError
+    
+    def error(self):
+        """
+        Public method to check for an error.
+        
+        @return flag indicating an error (boolean)
+        """
+        return self._error
+    
+    def errorString(self):
+        """
+        Public method to get the error description.
+        
+        @return error description (string)
+        """
+        return self._errorString
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/ChromeImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Chrome bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+import json
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "chrome":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%USERPROFILE%\\AppData\\Local\\Google\\Chrome\\"
+                "User Data\\Default")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser(
+                "~/Library/Application Support/Google/Chrome/Default")
+        else:
+            standardDir = os.path.expanduser("~/.config/google-chrome/Default")
+        return (
+            UI.PixmapCache.getPixmap("chrome.png"),
+            "Google Chrome",
+            "Bookmarks",
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Google Chrome stores its bookmarks in the"""
+                """ <b>Bookmarks</b> text file. This file is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    elif id == "chromium":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%USERPROFILE%\\AppData\\Local\\Google\\Chrome\\"
+                "User Data\\Default")
+        else:
+            standardDir = os.path.expanduser("~/.config/chromium/Default")
+        return (
+            UI.PixmapCache.getPixmap("chromium.png"),
+            "Chromium",
+            "Bookmarks",
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Chromium stores its bookmarks in the <b>Bookmarks</b>"""
+                """ text file. This file is usually located in"""),
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class ChromeImporter(BookmarksImporter):
+    """
+    Class implementing the Chrome bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(ChromeImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr(
+                "File '{0}' does not exist.").format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        try:
+            f = open(self.__fileName, "r", encoding="utf-8")
+            contents = json.load(f)
+            f.close()
+        except IOError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "File '{0}' cannot be read.\nReason: {1}")\
+                .format(self.__fileName, str(err))
+            return None
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        if contents["version"] == 1:
+            self.__processRoots(contents["roots"], importRootNode)
+        
+        if self._id == "chrome":
+            importRootNode.title = self.tr("Google Chrome Import")
+        elif self._id == "chromium":
+            importRootNode.title = self.tr("Chromium Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
+    
+    def __processRoots(self, data, rootNode):
+        """
+        Private method to process the bookmark roots.
+        
+        @param data dictionary with the bookmarks data (dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        for node in data.values():
+            if node["type"] == "folder":
+                self.__generateFolderNode(node, rootNode)
+            elif node["type"] == "url":
+                self.__generateUrlNode(node, rootNode)
+    
+    def __generateFolderNode(self, data, rootNode):
+        """
+        Private method to process a bookmarks folder.
+        
+        @param data dictionary with the bookmarks data (dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        folder = BookmarkNode(BookmarkNode.Folder, rootNode)
+        folder.title = data["name"].replace("&", "&&")
+        for node in data["children"]:
+            if node["type"] == "folder":
+                self.__generateFolderNode(node, folder)
+            elif node["type"] == "url":
+                self.__generateUrlNode(node, folder)
+    
+    def __generateUrlNode(self, data, rootNode):
+        """
+        Private method to process a bookmarks node.
+        
+        @param data dictionary with the bookmarks data (dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        bookmark = BookmarkNode(BookmarkNode.Bookmark, rootNode)
+        bookmark.url = data["url"]
+        bookmark.title = data["name"].replace("&", "&&")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/FirefoxImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Firefox bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+import sqlite3
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt, QUrl
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "firefox":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%APPDATA%\\Mozilla\\Firefox\\Profiles")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser(
+                "~/Library/Application Support/Firefox/Profiles")
+        else:
+            standardDir = os.path.expanduser("~/.mozilla/firefox")
+        return (
+            UI.PixmapCache.getPixmap("chrome.png"),
+            "Mozilla Firefox",
+            "places.sqlite",
+            QCoreApplication.translate(
+                "FirefoxImporter",
+                """Mozilla Firefox stores its bookmarks in the"""
+                """ <b>places.sqlite</b> SQLite database. This file is"""
+                """ usually located in"""),
+            QCoreApplication.translate(
+                "FirefoxImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class FirefoxImporter(BookmarksImporter):
+    """
+    Class implementing the Chrome bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(FirefoxImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+        self.__db = None
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        
+        try:
+            self.__db = sqlite3.connect(self.__fileName)
+        except sqlite3.DatabaseError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Unable to open database.\nReason: {0}").format(str(err))
+            return False
+        
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Root)
+        
+        # step 1: build the hierarchy of bookmark folders
+        folders = {}
+        
+        try:
+            cursor = self.__db.cursor()
+            cursor.execute(
+                "SELECT id, parent, title FROM moz_bookmarks "
+                "WHERE type = 2 and title !=''")
+            for row in cursor:
+                id_ = row[0]
+                parent = row[1]
+                title = row[2]
+                if parent in folders:
+                    folder = BookmarkNode(BookmarkNode.Folder, folders[parent])
+                else:
+                    folder = BookmarkNode(BookmarkNode.Folder, importRootNode)
+                folder.title = title.replace("&", "&&")
+                folders[id_] = folder
+        except sqlite3.DatabaseError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Unable to open database.\nReason: {0}").format(str(err))
+            return None
+        
+        try:
+            cursor = self.__db.cursor()
+            cursor.execute(
+                "SELECT parent, title, fk, position FROM moz_bookmarks"
+                " WHERE type = 1 and title != '' ORDER BY position")
+            for row in cursor:
+                parent = row[0]
+                title = row[1]
+                placesId = row[2]
+                
+                cursor2 = self.__db.cursor()
+                cursor2.execute(
+                    "SELECT url FROM moz_places WHERE id = {0}"
+                    .format(placesId))
+                row2 = cursor2.fetchone()
+                if row2:
+                    url = QUrl(row2[0])
+                    if not title or url.isEmpty() or \
+                            url.scheme() in ["place", "about"]:
+                        continue
+                    
+                    if parent in folders:
+                        bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                folders[parent])
+                    else:
+                        bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                importRootNode)
+                    bookmark.url = url.toString()
+                    bookmark.title = title.replace("&", "&&")
+        except sqlite3.DatabaseError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Unable to open database.\nReason: {0}").format(str(err))
+            return None
+        
+        importRootNode.setType(BookmarkNode.Folder)
+        if self._id == "firefox":
+            importRootNode.title = self.tr("Mozilla Firefox Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/HtmlImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for HTML bookmark files.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given HTML source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "html":
+        return (
+            UI.PixmapCache.getPixmap("html.png"),
+            "HTML Netscape Bookmarks",
+            QCoreApplication.translate(
+                "HtmlImporter",
+                "HTML Netscape Bookmarks") + " (*.htm *.html)",
+            QCoreApplication.translate(
+                "HtmlImporter",
+                """You can import bookmarks from any browser that supports"""
+                """ HTML exporting. This file has usually the extension"""
+                """ .htm or .html."""),
+            QCoreApplication.translate(
+                "HtmlImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            "",
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class HtmlImporter(BookmarksImporter):
+    """
+    Class implementing the HTML bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HtmlImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+        self.__inFile = None
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        from ..NsHtmlReader import NsHtmlReader
+        
+        reader = NsHtmlReader()
+        importRootNode = reader.read(self.__fileName)
+        
+        importRootNode.setType(BookmarkNode.Folder)
+        if self._id == "html":
+            importRootNode.title = self.tr("HTML Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/IExplorerImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Internet Explorer bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "ie":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%USERPROFILE%\\Favorites")
+        else:
+            standardDir = ""
+        return (
+            UI.PixmapCache.getPixmap("internet_explorer.png"),
+            "Internet Explorer",
+            "",
+            QCoreApplication.translate(
+                "IExplorerImporter",
+                """Internet Explorer stores its bookmarks in the"""
+                """ <b>Favorites</b> folder This folder is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "IExplorerImporter",
+                """Please choose the folder to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class IExplorerImporter(BookmarksImporter):
+    """
+    Class implementing the Chrome bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(IExplorerImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("Folder '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        if not os.path.isdir(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("'{0}' is not a folder.")\
+                .format(self.__fileName)
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        
+        folders = {}
+        
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        folders[self.__fileName] = importRootNode
+        
+        for dir, subdirs, files in os.walk(self.__fileName):
+            for subdir in subdirs:
+                path = os.path.join(dir, subdir)
+                if dir in folders:
+                    folder = BookmarkNode(BookmarkNode.Folder, folders[dir])
+                else:
+                    folder = BookmarkNode(BookmarkNode.Folder, importRootNode)
+                folder.title = subdir.replace("&", "&&")
+                folders[path] = folder
+            
+            for file in files:
+                name, ext = os.path.splitext(file)
+                if ext.lower() == ".url":
+                    path = os.path.join(dir, file)
+                    try:
+                        f = open(path, "r")
+                        contents = f.read()
+                        f.close()
+                    except IOError:
+                        continue
+                    url = ""
+                    for line in contents.splitlines():
+                        if line.startswith("URL="):
+                            url = line.replace("URL=", "")
+                            break
+                    if url:
+                        if dir in folders:
+                            bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                    folders[dir])
+                        else:
+                            bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                    importRootNode)
+                        bookmark.url = url
+                        bookmark.title = name.replace("&", "&&")
+        
+        if self._id == "ie":
+            importRootNode.title = self.tr("Internet Explorer Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/OperaImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Opera bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "opera":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars("%APPDATA%\\Opera\\Opera")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser(
+                "~/Library/Opera")
+        else:
+            standardDir = os.path.expanduser("~/.opera")
+        return (
+            UI.PixmapCache.getPixmap("opera.png"),
+            "Opera",
+            "bookmarks.adr",
+            QCoreApplication.translate(
+                "OperaImporter",
+                """Opera stores its bookmarks in the <b>bookmarks.adr</b> """
+                """text file. This file is usually located in"""),
+            QCoreApplication.translate(
+                "OperaImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class OperaImporter(BookmarksImporter):
+    """
+    Class implementing the Opera bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(OperaImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        try:
+            f = open(self.__fileName, "r", encoding="utf-8")
+            contents = f.read()
+            f.close()
+        except IOError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "File '{0}' cannot be read.\nReason: {1}")\
+                .format(self.__fileName, str(err))
+            return None
+        
+        folderStack = []
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        folderStack.append(importRootNode)
+        
+        for line in contents.splitlines():
+            line = line.strip()
+            if line == "#FOLDER":
+                node = BookmarkNode(BookmarkNode.Folder, folderStack[-1])
+                folderStack.append(node)
+            elif line == "#URL":
+                node = BookmarkNode(BookmarkNode.Bookmark, folderStack[-1])
+            elif line == "-":
+                folderStack.pop()
+            elif line.startswith("NAME="):
+                node.title = line.replace("NAME=", "").replace("&", "&&")
+            elif line.startswith("URL="):
+                node.url = line.replace("URL=", "")
+        
+        if self._id == "opera":
+            importRootNode.title = self.tr("Opera Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/SafariImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Apple Safari bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+from Utilities import binplistlib
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "safari":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%APPDATA%\\Apple Computer\\Safari")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser("~/Library/Safari")
+        else:
+            standardDir = ""
+        return (
+            UI.PixmapCache.getPixmap("safari.png"),
+            "Apple Safari",
+            "Bookmarks.plist",
+            QCoreApplication.translate(
+                "SafariImporter",
+                """Apple Safari stores its bookmarks in the"""
+                """ <b>Bookmarks.plist</b> file. This file is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "SafariImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class SafariImporter(BookmarksImporter):
+    """
+    Class implementing the Apple Safari bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(SafariImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        try:
+            bookmarksDict = binplistlib.readPlist(self.__fileName)
+        except binplistlib.InvalidPlistException as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Bookmarks file cannot be read.\nReason: {0}".format(str(err)))
+            return None
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        if bookmarksDict["WebBookmarkFileVersion"] == 1 and \
+           bookmarksDict["WebBookmarkType"] == "WebBookmarkTypeList":
+            self.__processChildren(bookmarksDict["Children"], importRootNode)
+        
+        if self._id == "safari":
+            importRootNode.title = self.tr("Apple Safari Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
+    
+    def __processChildren(self, children, rootNode):
+        """
+        Private method to process the list of children.
+        
+        @param children list of child nodes to be processed (list of dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        for child in children:
+            if child["WebBookmarkType"] == "WebBookmarkTypeList":
+                folder = BookmarkNode(BookmarkNode.Folder, rootNode)
+                folder.title = child["Title"].replace("&", "&&")
+                if "Children" in child:
+                    self.__processChildren(child["Children"], folder)
+            elif child["WebBookmarkType"] == "WebBookmarkTypeLeaf":
+                url = child["URLString"]
+                if url.startswith(("place:", "about:")):
+                    continue
+                
+                bookmark = BookmarkNode(BookmarkNode.Bookmark, rootNode)
+                bookmark.url = url
+                bookmark.title = child["URIDictionary"]["title"]\
+                    .replace("&", "&&")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/XbelImporter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for XBEL files.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QXmlStreamReader, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given XBEL source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "e5browser":
+        from ..BookmarksManager import BookmarksManager
+        bookmarksFile = BookmarksManager.getFileName()
+        return (
+            UI.PixmapCache.getPixmap("ericWeb48.png"),
+            "eric6 Web Browser",
+            os.path.basename(bookmarksFile),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """eric6 Web Browser stores its bookmarks in the"""
+                """ <b>{0}</b> XML file. This file is usually located in"""
+            ).format(os.path.basename(bookmarksFile)),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            os.path.dirname(bookmarksFile),
+        )
+    elif id == "konqueror":
+        if os.path.exists(os.path.expanduser("~/.kde4")):
+            standardDir = os.path.expanduser("~/.kde4/share/apps/konqueror")
+        elif os.path.exists(os.path.expanduser("~/.kde")):
+            standardDir = os.path.expanduser("~/.kde/share/apps/konqueror")
+        else:
+            standardDir = ""
+        return (
+            UI.PixmapCache.getPixmap("konqueror.png"),
+            "Konqueror",
+            "bookmarks.xml",
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Konqueror stores its bookmarks in the"""
+                """ <b>bookmarks.xml</b> XML file. This file is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    elif id == "xbel":
+        return (
+            UI.PixmapCache.getPixmap("xbel.png"),
+            "XBEL Bookmarks",
+            QCoreApplication.translate(
+                "XbelImporter", "XBEL Bookmarks") + " (*.xbel *.xml)",
+            QCoreApplication.translate(
+                "XbelImporter",
+                """You can import bookmarks from any browser that supports"""
+                """ XBEL exporting. This file has usually the extension"""
+                """ .xbel or .xml."""),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            "",
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class XbelImporter(BookmarksImporter):
+    """
+    Class implementing the XBEL bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(XbelImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..XbelReader import XbelReader
+        
+        reader = XbelReader()
+        importRootNode = reader.read(self.__fileName)
+        
+        if reader.error() != QXmlStreamReader.NoError:
+            self._error = True
+            self._errorString = self.tr(
+                """Error when importing bookmarks on line {0},"""
+                """ column {1}:\n{2}""")\
+                .format(reader.lineNumber(),
+                        reader.columnNumber(),
+                        reader.errorString())
+            return None
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode.setType(BookmarkNode.Folder)
+        if self._id == "e5browser":
+            importRootNode.title = self.tr("eric6 Web Browser Import")
+        elif self._id == "konqueror":
+            importRootNode.title = self.tr("Konqueror Import")
+        elif self._id == "xbel":
+            importRootNode.title = self.tr("XBEL Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/__init__.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing bookmarks importers for various sources.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QCoreApplication
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporters():
+    """
+    Module function to get a list of supported importers.
+    
+    @return list of tuples with an icon (QIcon), readable name (string) and
+        internal name (string)
+    """
+    importers = []
+    importers.append(
+        (UI.PixmapCache.getIcon("ericWeb48.png"), "eric6 Web Browser",
+         "e5browser"))
+    importers.append(
+        (UI.PixmapCache.getIcon("firefox.png"), "Mozilla Firefox", "firefox"))
+    importers.append(
+        (UI.PixmapCache.getIcon("chrome.png"), "Google Chrome", "chrome"))
+    if Globals.isLinuxPlatform():
+        importers.append(
+            (UI.PixmapCache.getIcon("chromium.png"), "Chromium", "chromium"))
+        importers.append(
+            (UI.PixmapCache.getIcon("konqueror.png"), "Konqueror",
+             "konqueror"))
+    importers.append(
+        (UI.PixmapCache.getIcon("opera.png"), "Opera", "opera"))
+    importers.append(
+        (UI.PixmapCache.getIcon("safari.png"), "Apple Safari", "safari"))
+    if Globals.isWindowsPlatform():
+        importers.append(
+            (UI.PixmapCache.getIcon("internet_explorer.png"),
+             "Internet Explorer", "ie"))
+    importers.append(
+        (UI.PixmapCache.getIcon("xbel.png"),
+         QCoreApplication.translate("BookmarksImporters", "XBEL File"),
+         "xbel"))
+    importers.append(
+        (UI.PixmapCache.getIcon("html.png"),
+         QCoreApplication.translate("BookmarksImporters", "HTML File"),
+         "html"))
+    return importers
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id source id to get info for (string)
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks
+        file (string)
+    @exception ValueError raised to indicate an unsupported importer