First attempt at proper AboutSchemeHandler

This commit is contained in:
Florian Bruhin 2014-02-20 23:08:27 +01:00
parent ec1951c56c
commit 38ef8fe8c7
4 changed files with 165 additions and 14 deletions

View File

@ -17,8 +17,10 @@
"""Handler functions for different about:... pages.""" """Handler functions for different about:... pages."""
from qutebrowser.network.schemehandler import (SchemeHandler,
SpecialNetworkReply)
from qutebrowser.utils.version import version from qutebrowser.utils.version import version
from qutebrowser.utils.url import is_about_url from qutebrowser.utils.url import is_about_url, urlstring
_HTML_TEMPLATE = """ _HTML_TEMPLATE = """
@ -84,6 +86,30 @@ def _get_html(title, snippet):
return _HTML_TEMPLATE.format(title=title, body=snippet).encode('UTF-8') return _HTML_TEMPLATE.format(title=title, body=snippet).encode('UTF-8')
class AboutSchemeHandler(SchemeHandler):
"""Scheme handler for about: URLs."""
def createRequest(self, op, request, outgoingData=None):
"""Create a new request.
Args:
op: Operation op
req: const QNetworkRequest & req
outgoing_data: QIODevice * outgoingData
Return:
A QNetworkReply.
"""
# FIXME handle unknown pages
# FIXME adjust URLutils based on handlers
data = handle(urlstring(request.url()))
return SpecialNetworkReply(request, data, "text/html", self.parent())
class AboutHandlers: class AboutHandlers:
"""Handlers for about:... pages.""" """Handlers for about:... pages."""

View File

@ -17,20 +17,30 @@
"""Our own QNetworkAccessManager.""" """Our own QNetworkAccessManager."""
import logging
from PyQt5.QtNetwork import QNetworkAccessManager from PyQt5.QtNetwork import QNetworkAccessManager
from qutebrowser.network.about import AboutSchemeHandler
class NetworkManager(QNetworkAccessManager): class NetworkManager(QNetworkAccessManager):
"""Our own QNetworkAccessManager. """Our own QNetworkAccessManager.
Attributes: Attributes:
_requests: Pending requests. _requests: Pending requests.
_scheme_handlers: A dictionary (scheme -> handler) of supported custom
schemes.
""" """
def __init__(self, parent=None): def __init__(self, parent=None):
self._requests = {}
super().__init__(parent) super().__init__(parent)
self._requests = {}
self._scheme_handlers = {
'about': AboutSchemeHandler,
}
def abort_requests(self): def abort_requests(self):
"""Abort all running requests.""" """Abort all running requests."""
@ -41,7 +51,7 @@ class NetworkManager(QNetworkAccessManager):
"""Return a new QNetworkReply object. """Return a new QNetworkReply object.
Extend QNetworkAccessManager::createRequest to save requests in Extend QNetworkAccessManager::createRequest to save requests in
self._requests. self._requests and handle custom schemes.
Args: Args:
op: Operation op op: Operation op
@ -52,6 +62,13 @@ class NetworkManager(QNetworkAccessManager):
A QNetworkReply. A QNetworkReply.
""" """
scheme = req.url().scheme()
logging.debug("new req, scheme {}, handlers {}".format(scheme,
self._scheme_handlers))
if scheme in self._scheme_handlers:
reply = self._scheme_handlers[scheme].createRequest(
op, req, outgoing_data)
return reply
reply = super().createRequest(op, req, outgoing_data) reply = super().createRequest(op, req, outgoing_data)
self._requests[id(reply)] = reply self._requests[id(reply)] = reply
reply.destroyed.connect(lambda obj: self._requests.pop(id(obj))) reply.destroyed.connect(lambda obj: self._requests.pop(id(obj)))

View File

@ -0,0 +1,118 @@
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# Based on the Eric5 helpviewer,
# Copyright (c) 2009 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Base class for custom scheme handlers."""
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from PyQt5.QtCore import pyqtSlot, QObject, QIODevice, QByteArray
class SchemeHandler(QObject):
"""Abstract base class for custom scheme handlers."""
def createRequest(self, op, request, outgoingData=None):
"""Create a new request.
Args:
op: Operation op
req: const QNetworkRequest & req
outgoing_data: QIODevice * outgoingData
Return:
A QNetworkReply.
Raise:
NotImplementedError because this needs to be overwritten by
subclasses.
"""
raise NotImplementedError
class SpecialNetworkReply(QNetworkReply):
"""QNetworkReply subclass for special data."""
def __init__(self, request, fileData, mimeType, parent=None):
"""Constructor.
Args:
request: reference to the request object (QNetworkRequest)
fileData: reference to the data buffer (QByteArray)
mimeType: for the reply (string)
parent: reference to the parent object (QObject)
Emit:
metaDataChanged and readyRead after initializing.
"""
super().__init__(parent)
self._data = fileData
self.setRequest(request)
self.setOpenMode(QIODevice.ReadOnly)
self.setHeader(QNetworkRequest.ContentTypeHeader, mimeType)
self.setHeader(QNetworkRequest.ContentLengthHeader,
QByteArray.number(fileData.length()))
self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "OK")
self.metaDataChanged.emit()
self.readyRead.emit()
@pyqtSlot()
def abort(self):
"""Abort the operation."""
pass
def bytesAvailable(self):
"""Determine the bytes available for being read.
Return:
bytes available (int)
Emit:
finished: if the data length is 0
"""
if self._data.length() == 0:
self.finished.emit()
return self._data.length() + super().bytesAvailable()
def readData(self, maxlen):
"""Retrieve data from the reply object.
Args:
maxlen maximum number of bytes to read (int)
Return:
bytestring containing the data
Emit:
finished: if all data was read
"""
len_ = min(maxlen, self._data.length())
buf = bytes(self._data[:len_])
self._data.remove(0, len_)
if self._data.length() == 0:
self.finished.emit()
return buf

View File

@ -36,7 +36,6 @@ from PyQt5.QtWebKitWidgets import QWebView, QWebPage
import qutebrowser.utils.url as urlutils import qutebrowser.utils.url as urlutils
import qutebrowser.utils.config as config import qutebrowser.utils.config as config
import qutebrowser.network.about as about
from qutebrowser.widgets.tabbar import TabWidget from qutebrowser.widgets.tabbar import TabWidget
from qutebrowser.network.networkmanager import NetworkManager from qutebrowser.network.networkmanager import NetworkManager
from qutebrowser.utils.signals import SignalCache, dbg_signal from qutebrowser.utils.signals import SignalCache, dbg_signal
@ -719,15 +718,6 @@ class BrowserTab(QWebView):
logging.debug('New title: {}'.format(urlutils.urlstring(u))) logging.debug('New title: {}'.format(urlutils.urlstring(u)))
self.titleChanged.emit(urlutils.urlstring(u)) self.titleChanged.emit(urlutils.urlstring(u))
self.urlChanged.emit(urlutils.qurl(u)) self.urlChanged.emit(urlutils.qurl(u))
if urlutils.is_about_url(u):
try:
content = about.handle(urlutils.urlstring(u))
except AttributeError:
return self.load(u)
else:
self.setUrl(u)
self.setContent(content, 'text/html')
else:
return self.load(u) return self.load(u)
def zoom(self, offset): def zoom(self, offset):