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."""
from qutebrowser.network.schemehandler import (SchemeHandler,
SpecialNetworkReply)
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 = """
@ -84,6 +86,30 @@ def _get_html(title, snippet):
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:
"""Handlers for about:... pages."""

View File

@ -17,20 +17,30 @@
"""Our own QNetworkAccessManager."""
import logging
from PyQt5.QtNetwork import QNetworkAccessManager
from qutebrowser.network.about import AboutSchemeHandler
class NetworkManager(QNetworkAccessManager):
"""Our own QNetworkAccessManager.
Attributes:
_requests: Pending requests.
_scheme_handlers: A dictionary (scheme -> handler) of supported custom
schemes.
"""
def __init__(self, parent=None):
self._requests = {}
super().__init__(parent)
self._requests = {}
self._scheme_handlers = {
'about': AboutSchemeHandler,
}
def abort_requests(self):
"""Abort all running requests."""
@ -41,7 +51,7 @@ class NetworkManager(QNetworkAccessManager):
"""Return a new QNetworkReply object.
Extend QNetworkAccessManager::createRequest to save requests in
self._requests.
self._requests and handle custom schemes.
Args:
op: Operation op
@ -52,6 +62,13 @@ class NetworkManager(QNetworkAccessManager):
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)
self._requests[id(reply)] = reply
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.config as config
import qutebrowser.network.about as about
from qutebrowser.widgets.tabbar import TabWidget
from qutebrowser.network.networkmanager import NetworkManager
from qutebrowser.utils.signals import SignalCache, dbg_signal
@ -719,16 +718,7 @@ class BrowserTab(QWebView):
logging.debug('New title: {}'.format(urlutils.urlstring(u)))
self.titleChanged.emit(urlutils.urlstring(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):
"""Increase/Decrease the zoom level.