diff --git a/.pylintrc b/.pylintrc index 8498bea12..79fb9f860 100644 --- a/.pylintrc +++ b/.pylintrc @@ -25,7 +25,8 @@ disable=no-self-use, unnecessary-lambda, blacklisted-name, too-many-lines, - logging-format-interpolation + logging-format-interpolation, + interface-not-implemented [BASIC] module-rgx=(__)?[a-z][a-z0-9_]*(__)?$ diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 2eca4c9dc..a21c54800 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -40,7 +40,7 @@ import qutebrowser import qutebrowser.resources # pylint: disable=unused-import from qutebrowser.commands import cmdutils, runners from qutebrowser.config import style, config, websettings -from qutebrowser.browser import quickmarks, cookies, cache, adblock +from qutebrowser.browser import quickmarks, cookies, cache, adblock, history from qutebrowser.browser.network import qutescheme, proxy from qutebrowser.mainwindow import mainwindow from qutebrowser.misc import crashdialog, readline, ipc, earlyinit, savemanager @@ -197,6 +197,8 @@ class Application(QApplication): log.init.debug("Initializing cache...") diskcache = cache.DiskCache(self) objreg.register('cache', diskcache) + log.init.debug("Initializing web history...") + history.init() log.init.debug("Initializing main window...") win_id = mainwindow.MainWindow.spawn( False if self._args.nowindow else True) @@ -540,10 +542,10 @@ class Application(QApplication): pages = [] try: - history = objreg.get('command-history')[-5:] + cmd_history = objreg.get('command-history')[-5:] except Exception: log.destroy.exception("Error while getting history: {}") - history = [] + cmd_history = [] try: objects = self.get_all_objects() @@ -562,7 +564,7 @@ class Application(QApplication): log.destroy.exception("Error while preventing shutdown") QApplication.closeAllWindows() self._crashdlg = crashdialog.ExceptionCrashDialog( - self._args.debug, pages, history, exc, objects) + self._args.debug, pages, cmd_history, exc, objects) ret = self._crashdlg.exec_() if ret == QDialog.Accepted: # restore self.restart(shutdown=False, pages=pages) @@ -668,9 +670,9 @@ class Application(QApplication): def report(self): """Report a bug in qutebrowser.""" pages = self._recover_pages() - history = objreg.get('command-history')[-5:] + cmd_history = objreg.get('command-history')[-5:] objects = self.get_all_objects() - self._crashdlg = crashdialog.ReportDialog(pages, history, objects) + self._crashdlg = crashdialog.ReportDialog(pages, cmd_history, objects) self._crashdlg.show() def interrupt(self, signum, _frame): diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py new file mode 100644 index 000000000..91fddff83 --- /dev/null +++ b/qutebrowser/browser/history.py @@ -0,0 +1,114 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + +"""Simple history which gets written to disk.""" + +import time +import functools + +from PyQt5.QtCore import pyqtSignal, QStandardPaths +from PyQt5.QtWebKit import QWebHistoryInterface + +from qutebrowser.utils import utils, objreg, standarddir +from qutebrowser.config import config +from qutebrowser.config.parsers import line as lineparser + + +class HistoryEntry: + + """A single entry in the web history. + + Attributes: + atime: The time the page was accessed. + url: The URL which was accessed as string + """ + + def __init__(self, atime, url): + self.atime = atime + self.url = url + + def __repr__(self): + return utils.get_repr(self, constructor=True, atime=self.atime, + url=self.url) + + def __str__(self): + return '{} {}'.format(int(self.atime), self.url) + + @classmethod + def from_str(cls, s): + """Get a history based on a 'TIME URL' string.""" + splitted = s.split(' ') + if len(splitted) != 2: + raise OSError("Invalid history entry '{}'".format(s)) + return cls(*splitted) + + +class WebHistory(QWebHistoryInterface): + + """A QWebHistoryInterface which supports being written to disk.""" + + changed = pyqtSignal() + + def __init__(self, parent=None): + super().__init__(parent) + datadir = standarddir.get(QStandardPaths.DataLocation) + self._linecp = lineparser.LineConfigParser(datadir, 'history', + parent=self) + self._history = [HistoryEntry.from_str(e) for e in self._linecp.data] + objreg.get('save-manager').add_saveable('history', self.save, + self.changed) + + def __repr__(self): + return utils.get_repr(self, length=len(self._history)) + + def save(self): + """Save the history to disk.""" + self._linecp.data = (str(e) for e in self._history) + self._linecp.save() + + def addHistoryEntry(self, url_string): + """Called by WebKit when an URL should be added to the history. + + Args: + url_string: An url as string to add to the history. + """ + if not config.get('general', 'private-browsing'): + entry = HistoryEntry(time.time(), url_string) + self._history.append(entry) + self.historyContains.cache_clear() + self.changed.emit() + + @functools.lru_cache() + def historyContains(self, url_string): + """Called by WebKit to determine if an URL is contained in the history. + + Args: + url_string: The URL (as string) to check for. + + Return: + True if the url is in the history, False otherwise. + """ + return url_string in (entry.url for entry in self._history) + + +def init(): + """Initialize the web history.""" + history = WebHistory() + objreg.register('web-history', history) + QWebHistoryInterface.setDefaultInterface(history)