176 lines
5.6 KiB
Python
176 lines
5.6 KiB
Python
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
|
|
|
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
|
#
|
|
# 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/>.
|
|
|
|
"""Utilities related to QWebHistory."""
|
|
|
|
|
|
from PyQt5.QtCore import QByteArray, QDataStream, QIODevice, QUrl
|
|
|
|
from qutebrowser.utils import utils, qtutils
|
|
|
|
|
|
HISTORY_STREAM_VERSION = 2
|
|
BACK_FORWARD_TREE_VERSION = 2
|
|
|
|
|
|
class TabHistoryItem:
|
|
|
|
"""A single item in the tab history.
|
|
|
|
Attributes:
|
|
url: The QUrl of this item.
|
|
title: The title as string of this item.
|
|
active: Whether this item is the item currently navigated to.
|
|
user_data: The user data for this item.
|
|
"""
|
|
|
|
def __init__(self, url, original_url, title, active=False, user_data=None):
|
|
self.url = url
|
|
self.original_url = original_url
|
|
self.title = title
|
|
self.active = active
|
|
self.user_data = user_data
|
|
|
|
def __repr__(self):
|
|
return utils.get_repr(self, constructor=True, url=self.url,
|
|
original_url=self.original_url, title=self.title,
|
|
active=self.active, user_data=self.user_data)
|
|
|
|
|
|
def _encode_url(url):
|
|
"""Encode an QUrl suitable to pass to QWebHistory."""
|
|
data = bytes(QUrl.toPercentEncoding(url.toString(), b':/#?&+=@%*'))
|
|
return data.decode('ascii')
|
|
|
|
|
|
def _serialize_item(i, item, stream):
|
|
"""Serialize a single WebHistoryItem into a QDataStream.
|
|
|
|
Args:
|
|
i: The index of the current item.
|
|
item: The WebHistoryItem to write.
|
|
stream: The QDataStream to write to.
|
|
"""
|
|
### Source/WebCore/history/qt/HistoryItemQt.cpp restoreState
|
|
## urlString
|
|
stream.writeQString(_encode_url(item.url))
|
|
## title
|
|
stream.writeQString(item.title)
|
|
## originalURLString
|
|
stream.writeQString(_encode_url(item.original_url))
|
|
|
|
### Source/WebCore/history/HistoryItem.cpp decodeBackForwardTree
|
|
## backForwardTreeEncodingVersion
|
|
stream.writeUInt32(BACK_FORWARD_TREE_VERSION)
|
|
## size (recursion stack)
|
|
stream.writeUInt64(0)
|
|
## node->m_documentSequenceNumber
|
|
# If two HistoryItems have the same document sequence number, then they
|
|
# refer to the same instance of a document. Traversing history from one
|
|
# such HistoryItem to another preserves the document.
|
|
stream.writeInt64(i + 1)
|
|
## size (node->m_documentState)
|
|
stream.writeUInt64(0)
|
|
## node->m_formContentType
|
|
# info used to repost form data
|
|
stream.writeQString(None)
|
|
## hasFormData
|
|
stream.writeBool(False)
|
|
## node->m_itemSequenceNumber
|
|
# If two HistoryItems have the same item sequence number, then they are
|
|
# clones of one another. Traversing history from one such HistoryItem to
|
|
# another is a no-op. HistoryItem clones are created for parent and
|
|
# sibling frames when only a subframe navigates.
|
|
stream.writeInt64(i + 1)
|
|
## node->m_referrer
|
|
stream.writeQString(None)
|
|
## node->m_scrollPoint (x)
|
|
try:
|
|
stream.writeInt32(item.user_data['scroll-pos'].x())
|
|
except (KeyError, TypeError):
|
|
stream.writeInt32(0)
|
|
## node->m_scrollPoint (y)
|
|
try:
|
|
stream.writeInt32(item.user_data['scroll-pos'].y())
|
|
except (KeyError, TypeError):
|
|
stream.writeInt32(0)
|
|
## node->m_pageScaleFactor
|
|
stream.writeFloat(1)
|
|
## hasStateObject
|
|
# Support for HTML5 History
|
|
stream.writeBool(False)
|
|
## node->m_target
|
|
stream.writeQString(None)
|
|
|
|
### Source/WebCore/history/qt/HistoryItemQt.cpp restoreState
|
|
## validUserData
|
|
# We could restore the user data here, but we prefer to use the
|
|
# QWebHistoryItem API for that.
|
|
stream.writeBool(False)
|
|
|
|
|
|
def serialize(items):
|
|
"""Serialize a list of QWebHistoryItems to a data stream.
|
|
|
|
Args:
|
|
items: An iterable of WebHistoryItems.
|
|
|
|
Return:
|
|
A (stream, data, user_data) tuple.
|
|
stream: The reseted QDataStream.
|
|
data: The QByteArray with the raw data.
|
|
user_data: A list with each item's user data.
|
|
|
|
Warning:
|
|
If 'data' goes out of scope, reading from 'stream' will result in a
|
|
segfault!
|
|
"""
|
|
data = QByteArray()
|
|
stream = QDataStream(data, QIODevice.ReadWrite)
|
|
user_data = []
|
|
|
|
current_idx = None
|
|
|
|
for i, item in enumerate(items):
|
|
if item.active:
|
|
if current_idx is not None:
|
|
raise ValueError("Multiple active items ({} and {}) "
|
|
"found!".format(current_idx, i))
|
|
else:
|
|
current_idx = i
|
|
|
|
if items:
|
|
if current_idx is None:
|
|
raise ValueError("No active item found!")
|
|
else:
|
|
current_idx = 0
|
|
|
|
### Source/WebKit/qt/Api/qwebhistory.cpp operator<<
|
|
stream.writeInt(HISTORY_STREAM_VERSION)
|
|
stream.writeInt(len(items))
|
|
stream.writeInt(current_idx)
|
|
|
|
for i, item in enumerate(items):
|
|
_serialize_item(i, item, stream)
|
|
user_data.append(item.user_data)
|
|
|
|
stream.device().reset()
|
|
qtutils.check_qdatastream(stream)
|
|
return stream, data, user_data
|