Merge lp:~osomon/webbrowser-app/thumbnails into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 207
Merged at revision: 220
Proposed branch: lp:~osomon/webbrowser-app/thumbnails
Merge into: lp:webbrowser-app
Diff against target: 988 lines (+568/-55)
21 files modified
CMakeLists.txt (+0/-2)
debian/control (+5/-1)
src/Ubuntu/Components/Extras/Browser/CMakeLists.txt (+13/-1)
src/Ubuntu/Components/Extras/Browser/PageDelegate.qml (+23/-6)
src/Ubuntu/Components/Extras/Browser/TabsList.qml (+8/-6)
src/Ubuntu/Components/Extras/Browser/TimelineView.qml (+13/-10)
src/Ubuntu/Components/Extras/Browser/UbuntuWebView.qml (+21/-0)
src/Ubuntu/Components/Extras/Browser/history-hostlist-model.cpp (+33/-2)
src/Ubuntu/Components/Extras/Browser/history-hostlist-model.h (+1/-0)
src/Ubuntu/Components/Extras/Browser/plugin.cpp (+7/-0)
src/Ubuntu/Components/Extras/Browser/tabs-model.cpp (+1/-5)
src/Ubuntu/Components/Extras/Browser/tabs-model.h (+0/-1)
src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.cpp (+53/-0)
src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.h (+39/-0)
src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp (+42/-0)
src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h (+35/-0)
src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp (+162/-0)
src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.h (+67/-0)
tests/autopilot/webbrowser_app/emulators/main_window.py (+1/-1)
tests/unittests/history-hostlist-model/CMakeLists.txt (+1/-0)
tests/unittests/history-hostlist-model/tst_HistoryHostListModelTests.cpp (+43/-20)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/thumbnails
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Bill Filler (community) Approve
Review via email: mp+173978@code.launchpad.net

Commit message

Display thumbnails of the web pages in the activity view.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~osomon/webbrowser-app/thumbnails updated
205. By Olivier Tilloy

Fix FTBFS in a clean environment.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~osomon/webbrowser-app/thumbnails updated
206. By Olivier Tilloy

Set the original size of the image.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

Looks like there is a jenkins issue causing a failure, but I tested the functionality and it works well and looks good. Before we land this I'd like to understand the impact on memory this has to make sure it is acceptable. And I have a few other questions..

Specifically:
- roughly what is the memory usage associated with generating and loading each thumbnail
- how can we limit the thumbnail cache as this seems it will grow limitlessly as history grows. We probably need some sort of hard limit on disk size or number stored so we don't chew up the users disk space. And a way to recreate if one from the cache doesn't exist.
- Are all thumbnails for the history items loaded in memory when the browser is started?
- Or do we only load thumbnails for images that are visible on the screen? I hope so :)

We should ensure that if you have hundreds/thousands of history items (which is likely) that we're not creating and loading thumbnails for all of these up front but only on demand.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> Looks like there is a jenkins issue causing a failure, but I tested the
> functionality and it works well and looks good. Before we land this I'd like
> to understand the impact on memory this has to make sure it is acceptable. And
> I have a few other questions..
>
> Specifically:
> - roughly what is the memory usage associated with generating and loading each
> thumbnail
> - how can we limit the thumbnail cache as this seems it will grow limitlessly
> as history grows. We probably need some sort of hard limit on disk size or
> number stored so we don't chew up the users disk space. And a way to recreate
> if one from the cache doesn't exist.
> - Are all thumbnails for the history items loaded in memory when the browser
> is started?
> - Or do we only load thumbnails for images that are visible on the screen? I
> hope so :)
>
> We should ensure that if you have hundreds/thousands of history items (which
> is likely) that we're not creating and loading thumbnails for all of these up
> front but only on demand.

As the timeline view is using nested list views, delegates are created on demand only when needed, and so are images loaded. I haven’t done any specific memory measurements, but I expect the memory consumption related to thumbnails to remain very reasonable (the thumbnails are square PNG images 12×12 grid units), and especially on a small screen such as a phone, where very few delegates are visible at any given time.
We’re not even loading any thumbnail when the application starts, as the timeline view isn’t visible yet, so it doesn’t instantiate any delegate.

Limiting the size of the cache is indeed a problem which this branch doesn’t address at all. I guess we could define a size limit, and when that limit is reached do some cleanup by removing the oldest thumbnails.

There’s also one issue that this branch doesn’t address: if a thumbnail already exists for a page, it’s never going to be re-generated, even if the contents of the page change. An option would be to force re-generating the thumbnail if it’s older than a given interval, e.g. one week.

I believe those two issues can (should) be addressed in other branches.

Revision history for this message
Bill Filler (bfiller) wrote :

Sounds reasonable. Can you file bugs for those two issues please so we can track? I'll approve this but you need to fix the jenks failure before it can land.

review: Approve
Revision history for this message
Olivier Tilloy (osomon) wrote :

The jenkins failures are due to bug #1199662, a regression in the UITK, which has been fixed yesterday, I’ll trigger a re-build now.
As soon as this is merged I’ll file bugs to track those two issues. Thanks for the review!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~osomon/webbrowser-app/thumbnails updated
207. By Olivier Tilloy

Fix autopilot tests.

Revision history for this message
Olivier Tilloy (osomon) wrote :

Some of the failures were actually a bug in the tests, this should now be fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Olivier Tilloy (osomon) wrote :

> As soon as this is merged I’ll file bugs to track those two issues.

Filed bug #1200525 (Thumbnails are never updated) and bug #1200526 (The thumbnail cache grows forever).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-06-05 07:55:27 +0000
+++ CMakeLists.txt 2013-07-12 06:47:24 +0000
@@ -13,8 +13,6 @@
13find_package(Qt5Widgets REQUIRED)13find_package(Qt5Widgets REQUIRED)
14find_package(Qt5Quick REQUIRED)14find_package(Qt5Quick REQUIRED)
1515
16add_definitions(-DQT_NO_KEYWORDS)
17
18set(CMAKE_INCLUDE_CURRENT_DIR ON)16set(CMAKE_INCLUDE_CURRENT_DIR ON)
19set(CMAKE_AUTOMOC ON)17set(CMAKE_AUTOMOC ON)
2018
2119
=== modified file 'debian/control'
--- debian/control 2013-07-09 09:55:10 +0000
+++ debian/control 2013-07-12 06:47:24 +0000
@@ -6,11 +6,15 @@
6 debhelper (>= 9),6 debhelper (>= 9),
7 dh-translations,7 dh-translations,
8 python,8 python,
9 libqt5sql5-sqlite,
10 libqt5v8-5-private-dev,
11 libqt5webkit5-dev,
9 qt5-default,12 qt5-default,
10 qt5-qmake,13 qt5-qmake,
11 qtbase5-dev,14 qtbase5-dev,
15 qtbase5-private-dev,
12 qtdeclarative5-dev,16 qtdeclarative5-dev,
13 libqt5sql5-sqlite,17 qtdeclarative5-private-dev,
14 qtdeclarative5-qtquick2-plugin,18 qtdeclarative5-qtquick2-plugin,
15 qtdeclarative5-test-plugin,19 qtdeclarative5-test-plugin,
16 qtdeclarative5-ubuntu-ui-toolkit-plugin,20 qtdeclarative5-ubuntu-ui-toolkit-plugin,
1721
=== modified file 'src/Ubuntu/Components/Extras/Browser/CMakeLists.txt'
--- src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2013-07-03 12:00:08 +0000
+++ src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2013-07-12 06:47:24 +0000
@@ -9,12 +9,24 @@
9 history-host-model.cpp9 history-host-model.cpp
10 history-hostlist-model.cpp10 history-hostlist-model.cpp
11 tabs-model.cpp11 tabs-model.cpp
12 webthumbnail-utils.cpp
13 webthumbnail-provider.cpp
14 webview-thumbnailer.cpp
12 plugin.cpp15 plugin.cpp
13)16)
1417
15add_library(${PLUGIN} MODULE ${PLUGIN_SRC})18add_library(${PLUGIN} MODULE ${PLUGIN_SRC})
1619
17qt5_use_modules(${PLUGIN} Core Qml Sql)20qt5_use_modules(${PLUGIN} Core Qml Quick Sql WebKit)
21
22# work around the lack of a public cmake module for Qt5V8
23set(Qt5V8_PRIVATE_INCLUDE_DIRS
24 "/usr/include/qt5/QtV8/${Qt5Qml_VERSION_STRING}"
25 "/usr/include/qt5/QtV8/${Qt5Qml_VERSION_STRING}/QtV8")
26include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}
27 ${Qt5V8_PRIVATE_INCLUDE_DIRS}
28 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
29 ${Qt5WebKit_PRIVATE_INCLUDE_DIRS})
1830
19file(GLOB QML_FILES *.qml qmldir *.js)31file(GLOB QML_FILES *.qml qmldir *.js)
20install(TARGETS ${PLUGIN} DESTINATION ${WEBBROWSER_IMPORTS_DIR})32install(TARGETS ${PLUGIN} DESTINATION ${WEBBROWSER_IMPORTS_DIR})
2133
=== modified file 'src/Ubuntu/Components/Extras/Browser/PageDelegate.qml'
--- src/Ubuntu/Components/Extras/Browser/PageDelegate.qml 2013-06-05 11:06:28 +0000
+++ src/Ubuntu/Components/Extras/Browser/PageDelegate.qml 2013-07-12 06:47:24 +0000
@@ -19,17 +19,34 @@
19import QtQuick 2.019import QtQuick 2.0
20import Ubuntu.Components 0.120import Ubuntu.Components 0.1
2121
22UbuntuShape {22Item {
23 property alias title: title.text23 property alias thumbnail: thumbnail.source
24 property alias label: label.text
25
26 UbuntuShape {
27 id: shape
28 anchors {
29 top: parent.top
30 left: parent.left
31 right: parent.right
32 }
33 height: width
34
35 image: Image {
36 id: thumbnail
37 }
38 }
2439
25 Label {40 Label {
26 id: title41 id: label
27 anchors {42 anchors {
28 fill: parent43 top: shape.bottom
29 margins: units.gu(0.5)44 topMargin: units.gu(1)
45 left: parent.left
46 right: parent.right
30 }47 }
48 height: units.gu(1)
31 fontSize: "small"49 fontSize: "small"
32 wrapMode: Text.Wrap
33 elide: Text.ElideRight50 elide: Text.ElideRight
34 }51 }
35}52}
3653
=== modified file 'src/Ubuntu/Components/Extras/Browser/TabsList.qml'
--- src/Ubuntu/Components/Extras/Browser/TabsList.qml 2013-07-09 05:55:49 +0000
+++ src/Ubuntu/Components/Extras/Browser/TabsList.qml 2013-07-12 06:47:24 +0000
@@ -1,4 +1,4 @@
1/*1/*
2 * Copyright 2013 Canonical Ltd.2 * Copyright 2013 Canonical Ltd.
3 *3 *
4 * This file is part of webbrowser-app.4 * This file is part of webbrowser-app.
@@ -41,7 +41,7 @@
41 right: parent.right41 right: parent.right
42 margins: units.gu(2)42 margins: units.gu(2)
43 }43 }
44 height: units.gu(14)44 height: units.gu(16)
45 spacing: units.gu(2)45 spacing: units.gu(2)
46 orientation: ListView.Horizontal46 orientation: ListView.Horizontal
47 currentIndex: model.currentIndex47 currentIndex: model.currentIndex
@@ -50,7 +50,7 @@
50 width: units.gu(14)50 width: units.gu(14)
51 height: parent.height51 height: parent.height
5252
53 PageDelegate {53 UbuntuShape {
54 objectName: "newTabDelegate"54 objectName: "newTabDelegate"
55 width: units.gu(12)55 width: units.gu(12)
56 height: units.gu(12)56 height: units.gu(12)
@@ -69,7 +69,7 @@
6969
70 delegate: ListItem.Empty {70 delegate: ListItem.Empty {
71 width: units.gu(12)71 width: units.gu(12)
72 height: units.gu(12)72 height: units.gu(14)
73 showDivider: false73 showDivider: false
7474
75 // FIXME: http://pad.lv/1187476 makes it impossible to swipe a75 // FIXME: http://pad.lv/1187476 makes it impossible to swipe a
@@ -78,10 +78,12 @@
78 onItemRemoved: tabRemoved(index)78 onItemRemoved: tabRemoved(index)
7979
80 PageDelegate {80 PageDelegate {
81 id: openTabDelegate
81 objectName: "openTabDelegate"82 objectName: "openTabDelegate"
82 anchors.fill: parent83 anchors.fill: parent
83 color: (index == currentIndex) ? UbuntuColors.darkAubergine : "white"84
84 title: model.title85 label: model.url
86 thumbnail: model.webview.thumbnail
85 }87 }
8688
87 onClicked: switchToTabClicked(index)89 onClicked: switchToTabClicked(index)
8890
=== modified file 'src/Ubuntu/Components/Extras/Browser/TimelineView.qml'
--- src/Ubuntu/Components/Extras/Browser/TimelineView.qml 2013-07-05 14:37:29 +0000
+++ src/Ubuntu/Components/Extras/Browser/TimelineView.qml 2013-07-12 06:47:24 +0000
@@ -57,7 +57,7 @@
5757
58 header: TabsList {58 header: TabsList {
59 width: parent.width59 width: parent.width
60 height: units.gu(20)60 height: units.gu(23)
6161
62 model: tabsModel62 model: tabsModel
6363
@@ -107,7 +107,7 @@
107 right: parent.right107 right: parent.right
108 margins: units.gu(2)108 margins: units.gu(2)
109 }109 }
110 height: units.gu(12)110 height: units.gu(14)
111111
112 spacing: units.gu(2)112 spacing: units.gu(2)
113 orientation: ListView.Horizontal113 orientation: ListView.Horizontal
@@ -160,10 +160,10 @@
160160
161 delegate: PageDelegate {161 delegate: PageDelegate {
162 width: units.gu(12)162 width: units.gu(12)
163 height: units.gu(12)163 height: units.gu(14)
164 color: "white"
165164
166 title: model.host ? model.host : i18n.tr("(local files)")165 label: model.host ? model.host : i18n.tr("(local files)")
166 thumbnail: model.thumbnail
167167
168 MouseArea {168 MouseArea {
169 anchors.fill: parent169 anchors.fill: parent
@@ -196,10 +196,12 @@
196196
197 delegate: PageDelegate {197 delegate: PageDelegate {
198 width: units.gu(12)198 width: units.gu(12)
199 height: units.gu(12)199 height: units.gu(14)
200 color: "white"200
201201 label: model.title ? model.title : model.url
202 title: model.title202
203 property url thumbnailSource: "image://webthumbnail/" + model.url
204 thumbnail: WebThumbnailer.thumbnailExists(model.url) ? thumbnailSource : ""
203205
204 MouseArea {206 MouseArea {
205 anchors.fill: parent207 anchors.fill: parent
@@ -213,7 +215,8 @@
213 when: timelineIndex == timeline.currentIndex215 when: timelineIndex == timeline.currentIndex
214 PropertyChanges {216 PropertyChanges {
215 target: entriesView217 target: entriesView
216 height: units.gu(12)218 height: units.gu(14)
219 clip: false
217 }220 }
218 }221 }
219 ]222 ]
220223
=== modified file 'src/Ubuntu/Components/Extras/Browser/UbuntuWebView.qml'
--- src/Ubuntu/Components/Extras/Browser/UbuntuWebView.qml 2013-06-13 08:36:55 +0000
+++ src/Ubuntu/Components/Extras/Browser/UbuntuWebView.qml 2013-07-12 06:47:24 +0000
@@ -21,6 +21,7 @@
21import QtWebKit 3.021import QtWebKit 3.0
22import QtWebKit.experimental 1.022import QtWebKit.experimental 1.0
23import Ubuntu.Components 0.123import Ubuntu.Components 0.1
24import Ubuntu.Components.Extras.Browser 0.1
24import Ubuntu.Components.Popups 0.125import Ubuntu.Components.Popups 0.1
2526
26WebView {27WebView {
@@ -187,4 +188,24 @@
187 flickableItem: _webview188 flickableItem: _webview
188 align: Qt.AlignBottom189 align: Qt.AlignBottom
189 }190 }
191
192 WebviewThumbnailer {
193 id: thumbnailer
194 webview: _webview
195 targetSize: Qt.size(units.gu(12), units.gu(12))
196 property url thumbnailSource: "image://webthumbnail/" + _webview.url
197 onThumbnailRendered: {
198 if (url == _webview.url) {
199 _webview.thumbnail = thumbnailer.thumbnailSource
200 }
201 }
202 }
203 property url thumbnail: (url && thumbnailer.thumbnailExists()) ? thumbnailer.thumbnailSource : ""
204 onLoadingChanged: {
205 if (loadRequest.status === WebView.LoadSucceededStatus) {
206 if (!thumbnailer.thumbnailExists()) {
207 thumbnailer.renderThumbnail()
208 }
209 }
210 }
190}211}
191212
=== modified file 'src/Ubuntu/Components/Extras/Browser/history-hostlist-model.cpp'
--- src/Ubuntu/Components/Extras/Browser/history-hostlist-model.cpp 2013-06-19 14:45:49 +0000
+++ src/Ubuntu/Components/Extras/Browser/history-hostlist-model.cpp 2013-07-12 06:47:24 +0000
@@ -20,6 +20,7 @@
20#include "history-model.h"20#include "history-model.h"
21#include "history-host-model.h"21#include "history-host-model.h"
22#include "history-timeframe-model.h"22#include "history-timeframe-model.h"
23#include "webthumbnail-utils.h"
2324
24// Qt25// Qt
25#include <QtCore/QSet>26#include <QtCore/QSet>
@@ -30,8 +31,9 @@
30 \brief List model that exposes history entries grouped by host31 \brief List model that exposes history entries grouped by host
3132
32 HistoryHostListModel is a list model that exposes history entries from a33 HistoryHostListModel is a list model that exposes history entries from a
33 HistoryTimeframeModel grouped by host. Each item in the list has two roles:34 HistoryTimeframeModel grouped by host. Each item in the list has three
34 'host' for the host name, and 'entries' for the corresponding35 roles: 'host' for the host name, 'thumbnail' for a thumbnail picture of a
36 page corresponding to this host, and 'entries' for the corresponding
35 HistoryHostModel that contains all entries in this group.37 HistoryHostModel that contains all entries in this group.
36*/38*/
37HistoryHostListModel::HistoryHostListModel(QObject* parent)39HistoryHostListModel::HistoryHostListModel(QObject* parent)
@@ -50,6 +52,7 @@
50 static QHash<int, QByteArray> roles;52 static QHash<int, QByteArray> roles;
51 if (roles.isEmpty()) {53 if (roles.isEmpty()) {
52 roles[Host] = "host";54 roles[Host] = "host";
55 roles[Thumbnail] = "thumbnail";
53 roles[Entries] = "entries";56 roles[Entries] = "entries";
54 }57 }
55 return roles;58 return roles;
@@ -74,6 +77,20 @@
74 switch (role) {77 switch (role) {
75 case Host:78 case Host:
76 return host;79 return host;
80 case Thumbnail:
81 {
82 // Iterate over all the entries, and return the first valid thumbnail.
83 HistoryHostModel* entries = m_hosts.value(host);
84 int count = entries->rowCount();
85 for (int i = 0; i < count; ++i) {
86 QUrl url = entries->data(entries->index(i, 0), HistoryModel::Url).toUrl();
87 QFileInfo thumbnailFile = WebThumbnailUtils::thumbnailFile(url);
88 if (thumbnailFile.exists()) {
89 return thumbnailFile.absoluteFilePath();
90 }
91 }
92 return QUrl();
93 }
77 case Entries:94 case Entries:
78 return QVariant::fromValue(m_hosts.value(host));95 return QVariant::fromValue(m_hosts.value(host));
79 default:96 default:
@@ -130,6 +147,7 @@
130147
131void HistoryHostListModel::onRowsInserted(const QModelIndex& parent, int start, int end)148void HistoryHostListModel::onRowsInserted(const QModelIndex& parent, int start, int end)
132{149{
150 QStringList updated;
133 for (int i = start; i <= end; ++i) {151 for (int i = start; i <= end; ++i) {
134 QString host = getHostFromSourceModel(m_sourceModel->index(i, 0, parent));152 QString host = getHostFromSourceModel(m_sourceModel->index(i, 0, parent));
135 if (!m_hosts.contains(host)) {153 if (!m_hosts.contains(host)) {
@@ -144,8 +162,16 @@
144 beginInsertRows(QModelIndex(), insertAt, insertAt);162 beginInsertRows(QModelIndex(), insertAt, insertAt);
145 insertNewHost(host);163 insertNewHost(host);
146 endInsertRows();164 endInsertRows();
165 } else {
166 updated.append(host);
147 }167 }
148 }168 }
169 QVector<int> updatedRoles = QVector<int>() << Thumbnail << Entries;
170 QStringList hosts = m_hosts.keys();
171 Q_FOREACH(const QString& host, updated) {
172 QModelIndex index = this->index(hosts.indexOf(host), 0);
173 Q_EMIT dataChanged(index, index, updatedRoles);
174 }
149}175}
150176
151void HistoryHostListModel::onRowsRemoved(const QModelIndex& parent, int start, int end)177void HistoryHostListModel::onRowsRemoved(const QModelIndex& parent, int start, int end)
@@ -166,6 +192,11 @@
166 delete m_hosts.take(host);192 delete m_hosts.take(host);
167 endRemoveRows();193 endRemoveRows();
168 }194 }
195 // XXX: unfortunately there is no way to get a list of hosts that had some
196 // (but not all) entries removed. To ensure the views are correctly updated,
197 // let’s emit the signal for all entries, even those that haven’t changed.
198 Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0),
199 QVector<int>() << Thumbnail << Entries);
169}200}
170201
171void HistoryHostListModel::onModelReset()202void HistoryHostListModel::onModelReset()
172203
=== modified file 'src/Ubuntu/Components/Extras/Browser/history-hostlist-model.h'
--- src/Ubuntu/Components/Extras/Browser/history-hostlist-model.h 2013-06-19 14:45:49 +0000
+++ src/Ubuntu/Components/Extras/Browser/history-hostlist-model.h 2013-07-12 06:47:24 +0000
@@ -41,6 +41,7 @@
4141
42 enum Roles {42 enum Roles {
43 Host = Qt::UserRole + 1,43 Host = Qt::UserRole + 1,
44 Thumbnail,
44 Entries45 Entries
45 };46 };
4647
4748
=== modified file 'src/Ubuntu/Components/Extras/Browser/plugin.cpp'
--- src/Ubuntu/Components/Extras/Browser/plugin.cpp 2013-07-03 12:00:08 +0000
+++ src/Ubuntu/Components/Extras/Browser/plugin.cpp 2013-07-12 06:47:24 +0000
@@ -23,6 +23,8 @@
23#include "history-host-model.h"23#include "history-host-model.h"
24#include "history-hostlist-model.h"24#include "history-hostlist-model.h"
25#include "tabs-model.h"25#include "tabs-model.h"
26#include "webthumbnail-provider.h"
27#include "webview-thumbnailer.h"
2628
27// Qt29// Qt
28#include <QtCore/QDir>30#include <QtCore/QDir>
@@ -38,6 +40,10 @@
38 }40 }
39 QQmlContext* context = engine->rootContext();41 QQmlContext* context = engine->rootContext();
40 context->setContextProperty("dataLocation", dataLocation.absolutePath());42 context->setContextProperty("dataLocation", dataLocation.absolutePath());
43
44 WebThumbnailProvider* thumbnailer = new WebThumbnailProvider;
45 engine->addImageProvider(QLatin1String("webthumbnail"), thumbnailer);
46 context->setContextProperty("WebThumbnailer", thumbnailer);
41}47}
4248
43void UbuntuBrowserPlugin::registerTypes(const char* uri)49void UbuntuBrowserPlugin::registerTypes(const char* uri)
@@ -49,4 +55,5 @@
49 qmlRegisterType<HistoryHostModel>(uri, 0, 1, "HistoryHostModel");55 qmlRegisterType<HistoryHostModel>(uri, 0, 1, "HistoryHostModel");
50 qmlRegisterType<HistoryHostListModel>(uri, 0, 1, "HistoryHostListModel");56 qmlRegisterType<HistoryHostListModel>(uri, 0, 1, "HistoryHostListModel");
51 qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel");57 qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel");
58 qmlRegisterType<WebviewThumbnailer>(uri, 0, 1, "WebviewThumbnailer");
52}59}
5360
=== modified file 'src/Ubuntu/Components/Extras/Browser/tabs-model.cpp'
--- src/Ubuntu/Components/Extras/Browser/tabs-model.cpp 2013-06-06 07:40:22 +0000
+++ src/Ubuntu/Components/Extras/Browser/tabs-model.cpp 2013-07-12 06:47:24 +0000
@@ -28,7 +28,7 @@
2828
29 TabsModel is a list model that stores the list of currently open tabs.29 TabsModel is a list model that stores the list of currently open tabs.
30 Each tab holds a pointer to a WebView and associated metadata (URL, title,30 Each tab holds a pointer to a WebView and associated metadata (URL, title,
31 icon, thumbnail).31 icon).
3232
33 The model doesn’t own the WebView, so it is the responsibility of whoever33 The model doesn’t own the WebView, so it is the responsibility of whoever
34 adds a tab to instantiate the corresponding WebView, and to destroy it after34 adds a tab to instantiate the corresponding WebView, and to destroy it after
@@ -51,7 +51,6 @@
51 roles[Url] = "url";51 roles[Url] = "url";
52 roles[Title] = "title";52 roles[Title] = "title";
53 roles[Icon] = "icon";53 roles[Icon] = "icon";
54 roles[Thumbnail] = "thumbnail";
55 roles[WebView] = "webview";54 roles[WebView] = "webview";
56 }55 }
57 return roles;56 return roles;
@@ -80,9 +79,6 @@
80 return webview->property("title");79 return webview->property("title");
81 case Icon:80 case Icon:
82 return webview->property("icon");81 return webview->property("icon");
83 case Thumbnail:
84 // XXX: not implemented yet
85 return QVariant();
86 case WebView:82 case WebView:
87 return QVariant::fromValue(webview);83 return QVariant::fromValue(webview);
88 default:84 default:
8985
=== modified file 'src/Ubuntu/Components/Extras/Browser/tabs-model.h'
--- src/Ubuntu/Components/Extras/Browser/tabs-model.h 2013-06-06 07:40:22 +0000
+++ src/Ubuntu/Components/Extras/Browser/tabs-model.h 2013-07-12 06:47:24 +0000
@@ -43,7 +43,6 @@
43 Url = Qt::UserRole + 1,43 Url = Qt::UserRole + 1,
44 Title,44 Title,
45 Icon,45 Icon,
46 Thumbnail,
47 WebView46 WebView
48 };47 };
4948
5049
=== added file 'src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.cpp'
--- src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.cpp 1970-01-01 00:00:00 +0000
+++ src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.cpp 2013-07-12 06:47:24 +0000
@@ -0,0 +1,53 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "webthumbnail-provider.h"
20#include "webthumbnail-utils.h"
21
22// Qt
23#include <QtCore/QDebug>
24#include <QtGui/QImageReader>
25
26WebThumbnailProvider::WebThumbnailProvider(QObject* parent)
27 : QObject(parent)
28 , QQuickImageProvider(QQuickImageProvider::Image)
29{
30}
31
32QImage WebThumbnailProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize)
33{
34 QImage image;
35 QFileInfo cached = WebThumbnailUtils::thumbnailFile(QUrl(id));
36 if (cached.exists()) {
37 QImageReader reader(cached.absoluteFilePath(), "PNG");
38 if (requestedSize.isValid()) {
39 reader.setScaledSize(requestedSize);
40 }
41 *size = reader.size();
42 reader.read(&image);
43 if (image.isNull()) {
44 qWarning() << "Failed to load cached thumbnail:" << reader.errorString();
45 }
46 }
47 return image;
48}
49
50bool WebThumbnailProvider::thumbnailExists(const QUrl& url) const
51{
52 return WebThumbnailUtils::thumbnailFile(url).exists();
53}
054
=== added file 'src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.h'
--- src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.h 1970-01-01 00:00:00 +0000
+++ src/Ubuntu/Components/Extras/Browser/webthumbnail-provider.h 2013-07-12 06:47:24 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __WEBTHUMBNAIL_PROVIDER_H__
20#define __WEBTHUMBNAIL_PROVIDER_H__
21
22// Qt
23#include <QtCore/QObject>
24#include <QtCore/QUrl>
25#include <QtQuick/QQuickImageProvider>
26
27class WebThumbnailProvider : public QObject, public QQuickImageProvider
28{
29 Q_OBJECT
30
31public:
32 WebThumbnailProvider(QObject* parent=0);
33
34 virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize);
35
36 Q_INVOKABLE bool thumbnailExists(const QUrl& url) const;
37};
38
39#endif // __WEBTHUMBNAIL_PROVIDER_H__
040
=== added file 'src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp'
--- src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp 1970-01-01 00:00:00 +0000
+++ src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp 2013-07-12 06:47:24 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "webthumbnail-utils.h"
20
21// Qt
22#include <QtCore/QCryptographicHash>
23#include <QtCore/QStandardPaths>
24
25QDir WebThumbnailUtils::cacheLocation()
26{
27 return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/thumbnails";
28}
29
30void WebThumbnailUtils::ensureCacheLocation()
31{
32 QDir cache = cacheLocation();
33 if (!cache.exists()) {
34 QDir::root().mkpath(cache.absolutePath());
35 }
36}
37
38QFileInfo WebThumbnailUtils::thumbnailFile(const QUrl& url)
39{
40 QString hash(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Md5).toHex());
41 return cacheLocation().absoluteFilePath(hash + ".png");
42}
043
=== added file 'src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h'
--- src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h 1970-01-01 00:00:00 +0000
+++ src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h 2013-07-12 06:47:24 +0000
@@ -0,0 +1,35 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __WEBTHUMBNAIL_UTILS_H__
20#define __WEBTHUMBNAIL_UTILS_H__
21
22// Qt
23#include <QtCore/QDir>
24#include <QtCore/QFileInfo>
25#include <QtCore/QUrl>
26
27class WebThumbnailUtils
28{
29public:
30 static QDir cacheLocation();
31 static void ensureCacheLocation();
32 static QFileInfo thumbnailFile(const QUrl& url);
33};
34
35#endif // __WEBTHUMBNAIL_UTILS_H__
036
=== added file 'src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp'
--- src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp 1970-01-01 00:00:00 +0000
+++ src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp 2013-07-12 06:47:24 +0000
@@ -0,0 +1,162 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "webview-thumbnailer.h"
20#include "webthumbnail-utils.h"
21
22// Qt
23#include <QtCore/QTimer>
24#include <QtQuick/private/qsgrenderer_p.h>
25#include <QtWebKit/private/qquickwebpage_p.h>
26#include <QtWebKit/private/qquickwebview_p.h>
27
28class BindableFbo : public QSGBindable
29{
30public:
31 BindableFbo(QOpenGLFramebufferObject* fbo) : m_fbo(fbo) {}
32 virtual void bind() const { m_fbo->bind(); }
33
34private:
35 QOpenGLFramebufferObject *m_fbo;
36};
37
38WebviewThumbnailer::WebviewThumbnailer(QQuickItem* parent)
39 : QQuickItem(parent)
40 , m_webview(0)
41 , m_renderer(0)
42{
43}
44
45WebviewThumbnailer::~WebviewThumbnailer()
46{
47 delete m_renderer;
48}
49
50QQuickWebView* WebviewThumbnailer::webview() const
51{
52 return m_webview;
53}
54
55void WebviewThumbnailer::setWebview(QQuickWebView* webview)
56{
57 if (webview != m_webview) {
58 m_webview = webview;
59 setFlag(QQuickItem::ItemHasContents, false);
60 Q_EMIT webviewChanged();
61 }
62}
63
64const QSize& WebviewThumbnailer::targetSize() const
65{
66 return m_targetSize;
67}
68
69void WebviewThumbnailer::setTargetSize(const QSize& targetSize)
70{
71 if (targetSize != m_targetSize) {
72 m_targetSize = targetSize;
73 Q_EMIT targetSizeChanged();
74 }
75}
76
77bool WebviewThumbnailer::thumbnailExists() const
78{
79 if (m_webview) {
80 QUrl url = m_webview->url();
81 if (url.isValid()) {
82 return WebThumbnailUtils::thumbnailFile(url).exists();
83 }
84 }
85 return false;
86}
87
88void WebviewThumbnailer::renderThumbnail()
89{
90 // Delay the actual rendering to give all elements on the page
91 // a chance to be fully rendered.
92 QTimer::singleShot(1000, this, SLOT(doRenderThumbnail()));
93}
94
95void WebviewThumbnailer::doRenderThumbnail()
96{
97 if (m_webview) {
98 setFlag(QQuickItem::ItemHasContents);
99 update();
100 }
101}
102
103QSGNode* WebviewThumbnailer::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData)
104{
105 Q_UNUSED(updatePaintNodeData);
106
107 if (!(m_webview && (flags() & QQuickItem::ItemHasContents))) {
108 return oldNode;
109 }
110 setFlag(QQuickItem::ItemHasContents, false);
111
112 QQuickWebPage* page = m_webview->page();
113 qreal min = qMin(page->width(), page->height());
114 QSize size(min, min);
115
116 QSGNode* node = QQuickItemPrivate::get(page)->itemNode();
117 QSGNode* parent = node->QSGNode::parent();
118 QSGNode* previousSibling = node->previousSibling();
119 if (parent) {
120 parent->removeChildNode(node);
121 }
122 QSGRootNode root;
123 root.appendChildNode(node);
124
125 if (m_renderer == 0) {
126 m_renderer = QQuickItemPrivate::get(this)->sceneGraphContext()->createRenderer();
127 }
128 m_renderer->setRootNode(static_cast<QSGRootNode*>(&root));
129
130 QOpenGLFramebufferObject fbo(size);
131
132 m_renderer->setDeviceRect(size);
133 m_renderer->setViewportRect(size);
134 m_renderer->setProjectionMatrixToRect(QRectF(QPointF(), size));
135 m_renderer->setClearColor(Qt::transparent);
136
137 m_renderer->renderScene(BindableFbo(&fbo));
138
139 fbo.release();
140
141 QImage image = fbo.toImage().scaled(m_targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
142
143 WebThumbnailUtils::ensureCacheLocation();
144 QUrl url = m_webview->url();
145 bool saved = image.save(WebThumbnailUtils::thumbnailFile(url).absoluteFilePath());
146
147 root.removeChildNode(node);
148
149 if (parent) {
150 if (previousSibling) {
151 parent->insertChildNodeAfter(node, previousSibling);
152 } else {
153 parent->prependChildNode(node);
154 }
155 }
156
157 if (saved) {
158 Q_EMIT thumbnailRendered(url);
159 }
160
161 return oldNode;
162}
0163
=== added file 'src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.h'
--- src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.h 1970-01-01 00:00:00 +0000
+++ src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.h 2013-07-12 06:47:24 +0000
@@ -0,0 +1,67 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __WEBVIEW_THUMBNAILER_H__
20#define __WEBVIEW_THUMBNAILER_H__
21
22// Qt
23#include <QtCore/QSize>
24#include <QtCore/QUrl>
25#include <QtQuick/private/qquickitem_p.h>
26
27class QQuickWebView;
28class QSGRenderer;
29
30class WebviewThumbnailer : public QQuickItem
31{
32 Q_OBJECT
33
34 Q_PROPERTY(QQuickWebView* webview READ webview WRITE setWebview NOTIFY webviewChanged)
35 Q_PROPERTY(QSize targetSize READ targetSize WRITE setTargetSize NOTIFY targetSizeChanged)
36
37public:
38 WebviewThumbnailer(QQuickItem* parent=0);
39 ~WebviewThumbnailer();
40
41 QQuickWebView* webview() const;
42 void setWebview(QQuickWebView* webview);
43
44 const QSize& targetSize() const;
45 void setTargetSize(const QSize& targetSize);
46
47 Q_INVOKABLE bool thumbnailExists() const;
48 Q_INVOKABLE void renderThumbnail();
49
50Q_SIGNALS:
51 void webviewChanged() const;
52 void targetSizeChanged() const;
53 void thumbnailRendered(const QUrl& url) const;
54
55protected:
56 virtual QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData);
57
58private Q_SLOTS:
59 void doRenderThumbnail();
60
61private:
62 QQuickWebView* m_webview;
63 QSize m_targetSize;
64 QSGRenderer* m_renderer;
65};
66
67#endif // __WEBVIEW_THUMBNAILER_H__
068
=== modified file 'tests/autopilot/webbrowser_app/emulators/main_window.py'
--- tests/autopilot/webbrowser_app/emulators/main_window.py 2013-07-05 11:21:52 +0000
+++ tests/autopilot/webbrowser_app/emulators/main_window.py 2013-07-12 06:47:24 +0000
@@ -79,7 +79,7 @@
79 return self.get_activity_view().select_single("TabsList")79 return self.get_activity_view().select_single("TabsList")
8080
81 def get_tabslist_newtab_delegate(self):81 def get_tabslist_newtab_delegate(self):
82 return self.get_tabslist().select_single("PageDelegate",82 return self.get_tabslist().select_single("UbuntuShape",
83 objectName="newTabDelegate")83 objectName="newTabDelegate")
8484
85 def get_tabslist_view(self):85 def get_tabslist_view(self):
8686
=== modified file 'tests/unittests/history-hostlist-model/CMakeLists.txt'
--- tests/unittests/history-hostlist-model/CMakeLists.txt 2013-07-02 11:29:03 +0000
+++ tests/unittests/history-hostlist-model/CMakeLists.txt 2013-07-12 06:47:24 +0000
@@ -4,6 +4,7 @@
4 ${webbrowser-plugin_SOURCE_DIR}/history-hostlist-model.cpp4 ${webbrowser-plugin_SOURCE_DIR}/history-hostlist-model.cpp
5 ${webbrowser-plugin_SOURCE_DIR}/history-model.cpp5 ${webbrowser-plugin_SOURCE_DIR}/history-model.cpp
6 ${webbrowser-plugin_SOURCE_DIR}/history-timeframe-model.cpp6 ${webbrowser-plugin_SOURCE_DIR}/history-timeframe-model.cpp
7 ${webbrowser-plugin_SOURCE_DIR}/webthumbnail-utils.cpp
7 tst_HistoryHostListModelTests.cpp8 tst_HistoryHostListModelTests.cpp
8)9)
9add_executable(${TEST} ${SOURCES})10add_executable(${TEST} ${SOURCES})
1011
=== modified file 'tests/unittests/history-hostlist-model/tst_HistoryHostListModelTests.cpp'
--- tests/unittests/history-hostlist-model/tst_HistoryHostListModelTests.cpp 2013-07-02 11:29:03 +0000
+++ tests/unittests/history-hostlist-model/tst_HistoryHostListModelTests.cpp 2013-07-12 06:47:24 +0000
@@ -60,52 +60,75 @@
6060
61 void shouldUpdateHostListWhenInsertingEntries()61 void shouldUpdateHostListWhenInsertingEntries()
62 {62 {
63 QSignalSpy spy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));63 QSignalSpy spyRowsInserted(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
64 qRegisterMetaType<QVector<int> >();
65 QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
6466
65 history->add(QUrl("http://example.org/"), "Example Domain", QUrl());67 history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
66 QCOMPARE(spy.count(), 1);68 QVERIFY(spyDataChanged.isEmpty());
67 QList<QVariant> args = spy.takeFirst();69 QCOMPARE(spyRowsInserted.count(), 1);
70 QList<QVariant> args = spyRowsInserted.takeFirst();
68 QCOMPARE(args.at(1).toInt(), 0);71 QCOMPARE(args.at(1).toInt(), 0);
69 QCOMPARE(args.at(2).toInt(), 0);72 QCOMPARE(args.at(2).toInt(), 0);
70 QCOMPARE(model->rowCount(), 1);73 QCOMPARE(model->rowCount(), 1);
71 QCOMPARE(model->data(model->index(0, 0), HistoryHostListModel::Host).toString(), QString("example.org"));74 QCOMPARE(model->data(model->index(0, 0), HistoryHostListModel::Host).toString(), QString("example.org"));
7275
73 history->add(QUrl("http://example.com/"), "Example Domain", QUrl());76 history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
74 QCOMPARE(spy.count(), 1);77 QVERIFY(spyDataChanged.isEmpty());
75 args = spy.takeFirst();78 QCOMPARE(spyRowsInserted.count(), 1);
79 args = spyRowsInserted.takeFirst();
76 QCOMPARE(args.at(1).toInt(), 0);80 QCOMPARE(args.at(1).toInt(), 0);
77 QCOMPARE(args.at(2).toInt(), 0);81 QCOMPARE(args.at(2).toInt(), 0);
78 QCOMPARE(model->rowCount(), 2);82 QCOMPARE(model->rowCount(), 2);
79 QCOMPARE(model->data(model->index(0, 0), HistoryHostListModel::Host).toString(), QString("example.com"));83 QCOMPARE(model->data(model->index(0, 0), HistoryHostListModel::Host).toString(), QString("example.com"));
8084
81 history->add(QUrl("http://example.org/test.html"), "Test page", QUrl());85 history->add(QUrl("http://example.org/test.html"), "Test page", QUrl());
82 QVERIFY(spy.isEmpty());86 QVERIFY(spyRowsInserted.isEmpty());
87 QCOMPARE(spyDataChanged.count(), 1);
88 args = spyDataChanged.takeFirst();
89 QCOMPARE(args.at(0).toModelIndex().row(), 1);
90 QCOMPARE(args.at(1).toModelIndex().row(), 1);
83 QCOMPARE(model->rowCount(), 2);91 QCOMPARE(model->rowCount(), 2);
84 }92 }
8593
86 void shouldUpdateHostListWhenRemovingEntries()94 void shouldUpdateHostListWhenRemovingEntries()
87 {95 {
88 QSignalSpy spy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
89 history->add(QUrl("http://example.org/"), "Example Domain", QUrl());96 history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
90 QTest::qWait(100);97 QTest::qWait(100);
91 QDateTime t0 = QDateTime::currentDateTimeUtc();98 QDateTime t0 = QDateTime::currentDateTimeUtc();
92 QTest::qWait(100);99 QTest::qWait(100);
93 history->add(QUrl("http://example.com/"), "Example Domain", QUrl());100 history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
94 QCOMPARE(model->rowCount(), 2);101 QTest::qWait(100);
95102 QDateTime t1 = QDateTime::currentDateTimeUtc();
96 timeframe->setEnd(t0);103 QTest::qWait(100);
97 QCOMPARE(spy.count(), 1);104 history->add(QUrl("http://example.org/test"), "Example Domain", QUrl());
98 QList<QVariant> args = spy.takeFirst();105 QCOMPARE(model->rowCount(), 2);
99 QCOMPARE(args.at(1).toInt(), 0);106
100 QCOMPARE(args.at(2).toInt(), 0);107 QSignalSpy spyRowsRemoved(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
101 QCOMPARE(model->rowCount(), 1);108 qRegisterMetaType<QVector<int> >();
109 QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
110
111 timeframe->setEnd(t1);
112 QVERIFY(spyRowsRemoved.isEmpty());
113 QVERIFY(!spyDataChanged.isEmpty());
114 QList<QVariant> args;
115 bool changed = false;
116 int expectedIndex = 1;
117 while(!changed && !spyDataChanged.isEmpty()) {
118 args = spyDataChanged.takeFirst();
119 int start = args.at(0).toModelIndex().row();
120 int end = args.at(1).toModelIndex().row();
121 changed = (start <= expectedIndex) && (expectedIndex <= end);
122 }
123 QVERIFY(changed);
124 QCOMPARE(model->rowCount(), 2);
102125
103 timeframe->setStart(t0);126 timeframe->setStart(t0);
104 QCOMPARE(spy.count(), 1);127 QCOMPARE(spyRowsRemoved.count(), 1);
105 args = spy.takeFirst();128 args = spyRowsRemoved.takeFirst();
106 QCOMPARE(args.at(1).toInt(), 0);129 QCOMPARE(args.at(1).toInt(), 1);
107 QCOMPARE(args.at(2).toInt(), 0);130 QCOMPARE(args.at(2).toInt(), 1);
108 QCOMPARE(model->rowCount(), 0);131 QCOMPARE(model->rowCount(), 1);
109 }132 }
110133
111 void shouldUpdateWhenChangingSourceModel()134 void shouldUpdateWhenChangingSourceModel()

Subscribers

People subscribed via source and target branches

to status/vote changes: