From bf37d16896d4953778b764cf6176860b5cd310d1 Mon Sep 17 00:00:00 2001 From: Brian Jackson Date: Tue, 25 Nov 2014 10:34:06 -0600 Subject: [PATCH 1/3] View/edit browser config in a special browser page Add the ability to view/edit the browser's config settings via a special browser page. It's very simplistic for now, but a good starting point. Future possibilities: * Matching config types to html input types * colors = html colorpicker * options with valid_values = select/multi-select * plain text fallbacks where appropriate * multi-line text edits for long options (i.e. host-block-lists) * Javascript option verification * switch from submitting changes onblur to onchange if an option passes basic verification, etc. --- qutebrowser/config/config.py | 7 ++++++ qutebrowser/html/settings.html | 37 +++++++++++++++++++++++++++++++ qutebrowser/network/qutescheme.py | 17 +++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 qutebrowser/html/settings.html diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 4a27a8073..ad9c81fdd 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -520,6 +520,13 @@ class ConfigManager(QObject): if self._initialized: self._after_set(sectname, optname) + @pyqtSlot(str, str, str) + def set_javascript(self, sectname, optname, value): + try: + self.set('conf', sectname, optname, value) + except configtypes.ValidationError as e: + message.error(e) + @cmdutils.register(instance='config') def save(self): """Save the config file.""" diff --git a/qutebrowser/html/settings.html b/qutebrowser/html/settings.html new file mode 100644 index 000000000..d8994e111 --- /dev/null +++ b/qutebrowser/html/settings.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block script %} +var cset = function(section, option, el) { + value = el.value; + window.qutesettings.set_javascript(section, option, value); +} +{% endblock %} + +{% block style %} +table { border: 1px solid grey; border-collapse: collapse; } +pre { margin: 2px; } +th, td { border: 1px solid grey; padding: 0px 5px; } +th { background: lightgrey; } +th pre { color: grey; text-align: left; } +{% endblock %} + +{% block content %} +

{{ title }}

+ +{% for section in config.DATA %} + + {% for d, e in config.DATA.get(section).items() %} + + + + + {% endfor %} +{% endfor %} +

{{ section }}

{{ config.SECTION_DESC.get(section)|wordwrap(width=120) }}
{{ d }} (Current: {{ e.value()|truncate(100) }}) + + +
+ +{% endblock %} \ No newline at end of file diff --git a/qutebrowser/network/qutescheme.py b/qutebrowser/network/qutescheme.py index a9695be8e..542281ff2 100644 --- a/qutebrowser/network/qutescheme.py +++ b/qutebrowser/network/qutescheme.py @@ -31,7 +31,8 @@ from PyQt5.QtNetwork import QNetworkReply import qutebrowser from qutebrowser.network import schemehandler, networkreply -from qutebrowser.utils import version, utils, jinja, log, message, docutils +from qutebrowser.utils import (version, utils, jinja, log, message, docutils, + objreg) pyeval_output = ":pyeval was never called" @@ -146,6 +147,19 @@ def qute_help(win_id, request): return utils.read_file(path).encode('UTF-8', errors='xmlcharrefreplace') +def qute_settings(win_id, request): + """Handler for qute:settings. View/change qute configuration""" + from qutebrowser.config import configdata + + cfg = objreg.get('config') + frame = objreg.get('webview', scope='tab').page().mainFrame() + frame.addToJavaScriptWindowObject("qutesettings", cfg) + + html = jinja.env.get_template('settings.html').render( + title='settings', config=configdata, cfg=cfg) + return html.encode('UTF-8', errors='xmlcharrefreplace') + + HANDLERS = { 'pyeval': qute_pyeval, 'version': qute_version, @@ -153,4 +167,5 @@ HANDLERS = { 'log': qute_log, 'gpl': qute_gpl, 'help': qute_help, + 'settings': qute_settings, } From 4fd4376c6a2822729798994705cec90ca5d949bb Mon Sep 17 00:00:00 2001 From: Brian Jackson Date: Tue, 25 Nov 2014 11:19:00 -0600 Subject: [PATCH 2/3] Pass around win_id so we can use it in the error message handler The message class needs a win_id to know where to send error messages. Just pass it through the jinja->js->qtwebbridge as it's just a simple int. --- qutebrowser/config/config.py | 6 +++--- qutebrowser/html/settings.html | 3 ++- qutebrowser/network/qutescheme.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index ad9c81fdd..bb8b2b2ea 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -520,12 +520,12 @@ class ConfigManager(QObject): if self._initialized: self._after_set(sectname, optname) - @pyqtSlot(str, str, str) - def set_javascript(self, sectname, optname, value): + @pyqtSlot(int, str, str, str) + def set_javascript(self, win_id, sectname, optname, value): try: self.set('conf', sectname, optname, value) except configtypes.ValidationError as e: - message.error(e) + message.error(win_id, e) @cmdutils.register(instance='config') def save(self): diff --git a/qutebrowser/html/settings.html b/qutebrowser/html/settings.html index d8994e111..38449ad95 100644 --- a/qutebrowser/html/settings.html +++ b/qutebrowser/html/settings.html @@ -1,9 +1,10 @@ {% extends "base.html" %} {% block script %} +var win_id = {{ win_id }}; var cset = function(section, option, el) { value = el.value; - window.qutesettings.set_javascript(section, option, value); + window.qutesettings.set_javascript(win_id, section, option, value); } {% endblock %} diff --git a/qutebrowser/network/qutescheme.py b/qutebrowser/network/qutescheme.py index 542281ff2..e29f4631d 100644 --- a/qutebrowser/network/qutescheme.py +++ b/qutebrowser/network/qutescheme.py @@ -156,7 +156,7 @@ def qute_settings(win_id, request): frame.addToJavaScriptWindowObject("qutesettings", cfg) html = jinja.env.get_template('settings.html').render( - title='settings', config=configdata, cfg=cfg) + win_id=win_id, title='settings', config=configdata, cfg=cfg) return html.encode('UTF-8', errors='xmlcharrefreplace') From 9cfb4b34316e20c0fd6d65fd576073facd036db7 Mon Sep 17 00:00:00 2001 From: Brian Jackson Date: Tue, 25 Nov 2014 14:29:20 -0600 Subject: [PATCH 3/3] Fix problem with qutesettings scope in pages Fix to make sure the js bridge code is only enabled when qute: pages are shown. Previously it would only be available to the first page (and before that it was available to all pages). --- qutebrowser/app.py | 3 +++ qutebrowser/config/config.py | 7 ------- qutebrowser/html/settings.html | 5 +++-- qutebrowser/network/qutescheme.py | 24 +++++++++++++++++++----- qutebrowser/widgets/webview.py | 18 ++++++++++++++---- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 22ec7ab38..6d4ceacbc 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -154,6 +154,9 @@ class Application(QApplication): config.init(self._args) log.init.debug("Initializing crashlog...") self._handle_segfault() + log.init.debug("Initializing js-bridge...") + js_bridge = qutescheme.JSBridge(self) + objreg.register('js-bridge', js_bridge) log.init.debug("Initializing websettings...") websettings.init() log.init.debug("Initializing adblock...") diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index bb8b2b2ea..4a27a8073 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -520,13 +520,6 @@ class ConfigManager(QObject): if self._initialized: self._after_set(sectname, optname) - @pyqtSlot(int, str, str, str) - def set_javascript(self, win_id, sectname, optname, value): - try: - self.set('conf', sectname, optname, value) - except configtypes.ValidationError as e: - message.error(win_id, e) - @cmdutils.register(instance='config') def save(self): """Save the config file.""" diff --git a/qutebrowser/html/settings.html b/qutebrowser/html/settings.html index 38449ad95..2bf6c6fc2 100644 --- a/qutebrowser/html/settings.html +++ b/qutebrowser/html/settings.html @@ -4,7 +4,7 @@ var win_id = {{ win_id }}; var cset = function(section, option, el) { value = el.value; - window.qutesettings.set_javascript(win_id, section, option, value); + window.qute.set(win_id, section, option, value); } {% endblock %} @@ -17,6 +17,7 @@ th pre { color: grey; text-align: left; } {% endblock %} {% block content %} +

{{ title }}

{% for section in config.DATA %} @@ -35,4 +36,4 @@ th pre { color: grey; text-align: left; } {% endfor %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/qutebrowser/network/qutescheme.py b/qutebrowser/network/qutescheme.py index e29f4631d..a0c0a08ea 100644 --- a/qutebrowser/network/qutescheme.py +++ b/qutebrowser/network/qutescheme.py @@ -27,12 +27,14 @@ Module attributes: pyeval_output: The output of the last :pyeval command. """ +from PyQt5.QtCore import pyqtSlot, QObject from PyQt5.QtNetwork import QNetworkReply import qutebrowser from qutebrowser.network import schemehandler, networkreply from qutebrowser.utils import (version, utils, jinja, log, message, docutils, objreg) +from qutebrowser.config import configtypes pyeval_output = ":pyeval was never called" @@ -79,6 +81,22 @@ class QuteSchemeHandler(schemehandler.SchemeHandler): request, data, 'text/html', self.parent()) +class JSBridge(QObject): + + """Javascript-bridge for special qute:... pages.""" + + def __init__(self, parent=None): + super().__init__(parent) + + @pyqtSlot(int, str, str, str) + def set(self, win_id, sectname, optname, value): + """Slot to set a setting from qute:settings.""" + try: + objreg.get('config').set('conf', sectname, optname, value) + except configtypes.ValidationError as e: + message.error(win_id, e) + + def qute_pyeval(_win_id, _request): """Handler for qute:pyeval. Return HTML content as bytes.""" html = jinja.env.get_template('pre.html').render( @@ -151,12 +169,8 @@ def qute_settings(win_id, request): """Handler for qute:settings. View/change qute configuration""" from qutebrowser.config import configdata - cfg = objreg.get('config') - frame = objreg.get('webview', scope='tab').page().mainFrame() - frame.addToJavaScriptWindowObject("qutesettings", cfg) - html = jinja.env.get_template('settings.html').render( - win_id=win_id, title='settings', config=configdata, cfg=cfg) + win_id=win_id, title='settings', config=configdata) return html.encode('UTF-8', errors='xmlcharrefreplace') diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 9530ae1a6..abc52c257 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -289,9 +289,6 @@ class WebView(QWebView): Args: url: The URL to load as QUrl - - Return: - Return status of self.load """ qtutils.ensure_valid(url) urlstr = url.toDisplayString() @@ -299,7 +296,20 @@ class WebView(QWebView): self.titleChanged.emit(urlstr) self.cur_url = url self.url_text_changed.emit(url.toDisplayString()) - return self.load(url) + self.load(url) + if url.scheme() == 'qute': + frame = self.page().mainFrame() + frame.javaScriptWindowObjectCleared.connect(self.add_js_bridge) + + def add_js_bridge(self): + """Add the javascript bridge for qute:... pages.""" + frame = self.sender() + assert frame.url().scheme() == 'qute' + bridge = objreg.get('js-bridge') + frame.addToJavaScriptWindowObject('qute', bridge) + # We need to make sure the bridge doesn't get added on non-qute:... + # pages. + frame.javaScriptWindowObjectCleared.disconnect(self.add_js_bridge) def zoom_perc(self, perc, fuzzyval=True): """Zoom to a given zoom percentage.