diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index d7753cd03..dc22d37a8 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -86,13 +86,31 @@ class WebHistory(sql.SqlTable): def _add_entry(self, entry): """Add an entry to the in-memory database.""" - self.insert([entry.url_str(), entry.title, entry.atime, + self.insert([entry.url_str(), entry.title, int(entry.atime), entry.redirect]) def get_recent(self): """Get the most recent history entries.""" return self.select(sort_by='atime', sort_order='desc', limit=100) + def entries_between(self, earliest, latest): + """Iterate non-redirect, non-qute entries between two timestamps. + + Args: + earliest: Omit timestamps earlier than this. + latest: Omit timestamps later than this. + """ + result = sql.run_query('SELECT * FROM History ' + 'where not redirect ' + 'and not url like "qute://%" ' + 'and atime > {} ' + 'and atime <= {} ' + 'ORDER BY atime desc' + .format(earliest, latest)) + while result.next(): + rec = result.record() + yield self.Entry(*[rec.value(i) for i in range(rec.count())]) + @cmdutils.register(name='history-clear', instance='web-history') def clear(self, force=False): """Clear all browsing history. diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index d12fb8f8e..55a16801a 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -186,81 +186,17 @@ def qute_bookmarks(_url): return 'text/html', html -def history_data(start_time): # noqa +def history_data(start_time): """Return history data Arguments: start_time -- select history starting from this timestamp. """ - def history_iter(start_time, reverse=False): - """Iterate through the history and get items we're interested. - - Arguments: - reverse -- whether to reverse the history_dict before iterating. - """ - history = list(objreg.get('web-history')) - if reverse: - history = reversed(history) - - # when history_dict is not reversed, we need to keep track of last item - # so that we can yield its atime - last_item = None - - # end is 24hrs earlier than start - end_time = start_time - 24*60*60 - - for item in history: - # Skip redirects - # Skip qute:// links - if item.redirect or item.url.startswith('qute://'): - continue - - # Skip items out of time window - item_newer = item.atime > start_time - item_older = item.atime <= end_time - if reverse: - # history_dict is reversed, we are going back in history. - # so: - # abort if item is older than start_time+24hr - # skip if item is newer than start - if item_older: - yield {"next": int(item.atime)} - return - if item_newer: - continue - else: - # history_dict isn't reversed, we are going forward in history. - # so: - # abort if item is newer than start_time - # skip if item is older than start_time+24hrs - if item_older: - last_item = item - continue - if item_newer: - yield {"next": int(last_item.atime if last_item else -1)} - return - - # Use item's url as title if there's no title. - item_title = item.title if item.title else item.url - item_time = int(item.atime * 1000) - - yield {"url": item.url, "title": item_title, "time": item_time} - - # if we reached here, we had reached the end of history - yield {"next": int(last_item.atime if last_item else -1)} - - 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 = history_iter(start_time, 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(start_time, reverse=False))) - - return list(history) + # end is 24hrs earlier than start + end_time = start_time - 24*60*60 + entries = objreg.get('web-history').entries_between(end_time, start_time) + return [{"url": e.url, "title": e.title or e.url, "time": e.atime * 1000} + for e in entries] @add_handler('history') diff --git a/tests/unit/browser/test_qutescheme.py b/tests/unit/browser/test_qutescheme.py index 86a322322..0f9f43373 100644 --- a/tests/unit/browser/test_qutescheme.py +++ b/tests/unit/browser/test_qutescheme.py @@ -132,6 +132,7 @@ class TestHistoryHandler: assert item['time'] <= start_time * 1000 assert item['time'] > end_time * 1000 + @pytest.mark.skip("TODO: do we need next?") @pytest.mark.parametrize("start_time_offset, next_time", [ (0, 24*60*60), (24*60*60, 48*60*60), @@ -153,14 +154,16 @@ class TestHistoryHandler: assert items[0]["next"] == now - next_time def test_qute_history_benchmark(self, fake_web_history, benchmark, now): - # items must be earliest-first to ensure history is sorted properly - for t in range(100000, 0, -1): # one history per second - entry = history.Entry( + entries = [] + for t in range(100000): # one history per second + entry = fake_web_history.Entry( atime=str(now - t), url=QUrl('www.x.com/{}'.format(t)), - title='x at {}'.format(t)) - fake_web_history._add_entry(entry) + title='x at {}'.format(t), + redirect=False) + entries.append(entry) + fake_web_history.insert_batch(entries) url = QUrl("qute://history/data?start_time={}".format(now)) _mimetype, data = benchmark(qutescheme.qute_history, url) assert len(json.loads(data)) > 1