Merge branch 'imransobir-master'

This commit is contained in:
Florian Bruhin 2017-02-11 20:40:59 +01:00
commit 760f016285
11 changed files with 370 additions and 0 deletions

View File

@ -31,6 +31,7 @@ Added
- Support for the HTML5 fullscreen API (e.g. youtube videos) with QtWebEngine - Support for the HTML5 fullscreen API (e.g. youtube videos) with QtWebEngine
- Support for the `general -> print-element-backgrounds` option with QtWebEngine on Qt >= 5.8 - Support for the `general -> print-element-backgrounds` option with QtWebEngine on Qt >= 5.8
- Support for `:download --mhtml` with QtWebEngine - Support for `:download --mhtml` with QtWebEngine
- New `qute:history` URL and `:history` command to show the browsing history.
Changed Changed
~~~~~~~ ~~~~~~~

View File

@ -169,6 +169,7 @@ Contributors, sorted by the number of commits in descending order:
* Artur Shaik * Artur Shaik
* Nathan Isom * Nathan Isom
* Thorsten Wißmann * Thorsten Wißmann
* Imran Sobir
* Austin Anderson * Austin Anderson
* Fritz Reichwald * Fritz Reichwald
* Jimmy * Jimmy

View File

@ -44,6 +44,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<fullscreen,fullscreen>>|Toggle fullscreen mode. |<<fullscreen,fullscreen>>|Toggle fullscreen mode.
|<<help,help>>|Show help about a command or setting. |<<help,help>>|Show help about a command or setting.
|<<hint,hint>>|Start hinting. |<<hint,hint>>|Start hinting.
|<<history,history>>|Show browsing history
|<<history-clear,history-clear>>|Clear all browsing history. |<<history-clear,history-clear>>|Clear all browsing history.
|<<home,home>>|Open main startpage in current tab. |<<home,home>>|Open main startpage in current tab.
|<<insert-text,insert-text>>|Insert text at cursor position. |<<insert-text,insert-text>>|Insert text at cursor position.
@ -418,6 +419,17 @@ Start hinting.
==== note ==== note
* This command does not split arguments after the last argument and handles quotes literally. * This command does not split arguments after the last argument and handles quotes literally.
[[history]]
=== history
Syntax: +:history [*--tab*] [*--bg*] [*--window*]+
Show browsing history
==== optional arguments
* +*-t*+, +*--tab*+: Open in a new tab.
* +*-b*+, +*--bg*+: Open in a background tab.
* +*-w*+, +*--window*+: Open in a new window.
[[history-clear]] [[history-clear]]
=== history-clear === history-clear
Syntax: +:history-clear [*--force*]+ Syntax: +:history-clear [*--force*]+

View File

@ -19,6 +19,7 @@ parse-type==0.3.4
py==1.4.32 py==1.4.32
pytest==3.0.6 pytest==3.0.6
pytest-bdd==2.18.1 pytest-bdd==2.18.1
pytest-benchmark==3.0.0
pytest-catchlog==1.2.2 pytest-catchlog==1.2.2
pytest-cov==2.4.0 pytest-cov==2.4.0
pytest-faulthandler==1.3.1 pytest-faulthandler==1.3.1

View File

@ -6,6 +6,7 @@ httpbin
hypothesis hypothesis
pytest pytest
pytest-bdd pytest-bdd
pytest-benchmark
pytest-catchlog pytest-catchlog
pytest-cov pytest-cov
pytest-faulthandler pytest-faulthandler

View File

@ -1408,6 +1408,18 @@ class CommandDispatcher:
tab.dump_async(callback, plain=plain) tab.dump_async(callback, plain=plain)
@cmdutils.register(instance='command-dispatcher', scope='window')
def history(self, tab=True, bg=False, window=False):
"""Show browsing history.
Args:
tab: Open in a new tab.
bg: Open in a background tab.
window: Open in a new window.
"""
url = QUrl('qute://history/')
self._open(url, tab, bg, window)
@cmdutils.register(instance='command-dispatcher', name='help', @cmdutils.register(instance='command-dispatcher', name='help',
scope='window') scope='window')
@cmdutils.argument('topic', completion=usertypes.Completion.helptopic) @cmdutils.argument('topic', completion=usertypes.Completion.helptopic)

View File

@ -24,8 +24,13 @@ Module attributes:
_HANDLERS: The handlers registered via decorators. _HANDLERS: The handlers registered via decorators.
""" """
import sys
import time
import datetime
import urllib.parse import urllib.parse
from PyQt5.QtCore import QUrlQuery
import qutebrowser import qutebrowser
from qutebrowser.utils import (version, utils, jinja, log, message, docutils, from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
objreg) objreg)
@ -158,6 +163,87 @@ def qute_bookmarks(_url):
return 'text/html', html return 'text/html', html
@add_handler('history') # noqa
def qute_history(url):
"""Handler for qute:history. Display history."""
# Get current date from query parameter, if not given choose today.
curr_date = datetime.date.today()
try:
query_date = QUrlQuery(url).queryItemValue("date")
if query_date:
curr_date = datetime.datetime.strptime(query_date, "%Y-%m-%d")
curr_date = curr_date.date()
except ValueError:
log.misc.debug("Invalid date passed to qute:history: " + query_date)
one_day = datetime.timedelta(days=1)
next_date = curr_date + one_day
prev_date = curr_date - one_day
def history_iter(reverse):
"""Iterate through the history and get the items we're interested in."""
curr_timestamp = time.mktime(curr_date.timetuple())
history = objreg.get('web-history').history_dict.values()
if reverse:
history = reversed(history)
for item in history:
# If we can't apply the reverse performance trick below,
# at least continue as early as possible with old items.
# This gets us down from 550ms to 123ms with 500k old items on my
# machine.
if item.atime < curr_timestamp and not reverse:
continue
# Convert timestamp
try:
item_atime = datetime.datetime.fromtimestamp(item.atime)
except (ValueError, OSError, OverflowError):
log.misc.debug("Invalid timestamp {}.".format(item.atime))
continue
if reverse and item_atime.date() < curr_date:
# If we could reverse the history in-place, and this entry is
# older than today, only older entries will follow, so we can
# abort here.
return
# Skip items not on curr_date
# Skip redirects
# Skip qute:// links
is_internal = item.url.scheme() == 'qute'
is_not_today = item_atime.date() != curr_date
if item.redirect or is_internal or is_not_today:
continue
# Use item's url as title if there's no title.
item_url = item.url.toDisplayString()
item_title = item.title if item.title else item_url
display_atime = item_atime.strftime("%X")
yield (item_url, item_title, display_atime)
if sys.hexversion >= 0x03050000:
# On Python >= 3.5 we can reverse the ordereddict in-place and thus
# apply an additional performance improvement in history_iter.
# On my machine, this gets us down from 550ms to 72us with 500k old
# items.
history = list(history_iter(reverse=True))
else:
# On Python 3.4, we can't do that, so we'd need to copy the entire
# history to a list. There, filter first and then reverse it here.
history = reversed(list(history_iter(reverse=False)))
html = jinja.render('history.html',
title='History',
history=history,
curr_date=curr_date,
next_date=next_date,
prev_date=prev_date,
today=datetime.date.today())
return 'text/html', html
@add_handler('pyeval') @add_handler('pyeval')
def qute_pyeval(_url): def qute_pyeval(_url):
"""Handler for qute:pyeval.""" """Handler for qute:pyeval."""

View File

@ -0,0 +1,92 @@
{% extends "base.html" %}
{% block style %}
body {
background: #fefefe;
font-family: sans-serif;
margin: 0 auto;
max-width: 1440px;
padding-left: 20px;
padding-right: 20px;
}
h1 {
color: #444;
font-weight: normal;
}
table {
border-collapse: collapse;
width: 100%;
}
td {
max-width: 50%;
padding: 2px 5px;
text-align: left;
}
td.title {
word-break: break-all;
}
td.time {
color: #555;
text-align: right;
white-space: nowrap;
}
a {
text-decoration: none;
color: #2562dc
}
a:hover {
text-decoration: underline;
}
tr:nth-child(odd) {
background-color: #f8f8f8;
}
.date {
color: #888;
font-size: 14pt;
padding-left: 25px;
}
.pagination-link {
display: inline-block;
margin-bottom: 10px;
margin-top: 10px;
padding-right: 10px;
}
.pagination-link > a {
color: #333;
font-weight: bold;
}
{% endblock %}
{% block content %}
<h1>Browsing history <span class="date">{{curr_date.strftime("%a, %d %B %Y")}}</span></h1>
<table>
<tbody>
{% for url, title, time in history %}
<tr>
<td class="title"><a href="{{url}}">{{title}}</a></td>
<td class="time">{{time}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<span class="pagination-link"><a href="qute://history/?date={{prev_date.strftime("%Y-%m-%d")}}" rel="prev">Previous</a></span>
{% if today >= next_date %}
<span class="pagination-link"><a href="qute://history/?date={{next_date.strftime("%Y-%m-%d")}}" rel="next">Next</a></span>
{% endif %}
{% endblock %}

View File

@ -74,6 +74,13 @@ Feature: Page history
http://localhost:(port)/data/hints/html/simple.html Simple link http://localhost:(port)/data/hints/html/simple.html Simple link
http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello.txt
Scenario: Listing history
When I open data/numbers/3.txt
And I open data/numbers/4.txt
And I open qute:history
Then the page should contain the plaintext "3.txt"
Then the page should contain the plaintext "4.txt"
## Bugs ## Bugs
@qtwebengine_skip @qtwebkit_ng_skip @qtwebengine_skip @qtwebkit_ng_skip

View File

@ -290,6 +290,24 @@ Feature: Various utility commands.
- about:blank - about:blank
- qute://help/index.html (active) - qute://help/index.html (active)
# :history
Scenario: :history without arguments
When I run :tab-only
And I run :history
And I wait until qute://history/ is loaded
Then the following tabs should be open:
- qute://history/ (active)
Scenario: :history with -t
When I open about:blank
And I run :tab-only
And I run :history -t
And I wait until qute://history/ is loaded
Then the following tabs should be open:
- about:blank
- qute://history/ (active)
# :home # :home
Scenario: :home with single page Scenario: :home with single page

View File

@ -0,0 +1,139 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2017 Imran Sobir
#
# 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/>.
import datetime
import collections
from PyQt5.QtCore import QUrl
import pytest
from qutebrowser.browser import history, qutescheme
from qutebrowser.utils import objreg
Dates = collections.namedtuple('Dates', ['yesterday', 'today', 'tomorrow'])
class TestHistoryHandler:
"""Test the qute://history endpoint."""
@pytest.fixture
def dates(self):
one_day = datetime.timedelta(days=1)
today = datetime.datetime.today()
tomorrow = today + one_day
yesterday = today - one_day
return Dates(yesterday, today, tomorrow)
@pytest.fixture
def entries(self, dates):
today = history.Entry(atime=str(dates.today.timestamp()),
url=QUrl('www.today.com'), title='today')
tomorrow = history.Entry(atime=str(dates.tomorrow.timestamp()),
url=QUrl('www.tomorrow.com'), title='tomorrow')
yesterday = history.Entry(atime=str(dates.yesterday.timestamp()),
url=QUrl('www.yesterday.com'), title='yesterday')
return Dates(yesterday, today, tomorrow)
@pytest.fixture
def fake_web_history(self, fake_save_manager, tmpdir):
"""Create a fake web-history and register it into objreg."""
web_history = history.WebHistory(tmpdir.dirname, 'fake-history')
objreg.register('web-history', web_history)
yield web_history
objreg.delete('web-history')
@pytest.fixture(autouse=True)
def fake_history(self, fake_web_history, entries):
"""Create fake history for three different days."""
fake_web_history._add_entry(entries.yesterday)
fake_web_history._add_entry(entries.today)
fake_web_history._add_entry(entries.tomorrow)
fake_web_history.save()
def test_history_without_query(self):
"""Ensure qute://history shows today's history without any query."""
_mimetype, data = qutescheme.qute_history(QUrl("qute://history"))
key = "<span class=\"date\">{}</span>".format(
datetime.date.today().strftime("%a, %d %B %Y"))
assert key in data
def test_history_with_bad_query(self):
"""Ensure qute://history shows today's history with bad query."""
url = QUrl("qute://history?date=204-blaah")
_mimetype, data = qutescheme.qute_history(url)
key = "<span class=\"date\">{}</span>".format(
datetime.date.today().strftime("%a, %d %B %Y"))
assert key in data
def test_history_today(self):
"""Ensure qute://history shows history for today."""
url = QUrl("qute://history")
_mimetype, data = qutescheme.qute_history(url)
assert "today" in data
assert "tomorrow" not in data
assert "yesterday" not in data
def test_history_yesterday(self, dates):
"""Ensure qute://history shows history for yesterday."""
url = QUrl("qute://history?date=" +
dates.yesterday.strftime("%Y-%m-%d"))
_mimetype, data = qutescheme.qute_history(url)
assert "today" not in data
assert "tomorrow" not in data
assert "yesterday" in data
def test_history_tomorrow(self, dates):
"""Ensure qute://history shows history for tomorrow."""
url = QUrl("qute://history?date=" +
dates.tomorrow.strftime("%Y-%m-%d"))
_mimetype, data = qutescheme.qute_history(url)
assert "today" not in data
assert "tomorrow" in data
assert "yesterday" not in data
def test_no_next_link_to_future(self, dates):
"""Ensure there's no next link pointing to the future."""
url = QUrl("qute://history")
_mimetype, data = qutescheme.qute_history(url)
assert "Next" not in data
url = QUrl("qute://history?date=" +
dates.tomorrow.strftime("%Y-%m-%d"))
_mimetype, data = qutescheme.qute_history(url)
assert "Next" not in data
def test_qute_history_benchmark(self, dates, entries, fake_web_history,
benchmark):
for i in range(100000):
entry = history.Entry(
atime=str(dates.yesterday.timestamp()),
url=QUrl('www.yesterday.com/{}'.format(i)),
title='yesterday')
fake_web_history._add_entry(entry)
fake_web_history._add_entry(entries.today)
fake_web_history._add_entry(entries.tomorrow)
url = QUrl("qute://history")
_mimetype, data = benchmark(qutescheme.qute_history, url)
assert "today" in data
assert "tomorrow" not in data
assert "yesterday" not in data