Merge lp:~osomon/webbrowser-app/domain-names-chronological into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Günter Schwann
Approved revision: 241
Merged at revision: 237
Proposed branch: lp:~osomon/webbrowser-app/domain-names-chronological
Merge into: lp:webbrowser-app
Diff against target: 658 lines (+362/-112)
11 files modified
src/Ubuntu/Components/Extras/Browser/CMakeLists.txt (+1/-0)
src/Ubuntu/Components/Extras/Browser/TimelineView.qml (+44/-42)
src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.cpp (+50/-0)
src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.h (+43/-0)
src/Ubuntu/Components/Extras/Browser/history-domainlist-model.cpp (+103/-65)
src/Ubuntu/Components/Extras/Browser/history-domainlist-model.h (+9/-3)
src/Ubuntu/Components/Extras/Browser/plugin.cpp (+2/-0)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/history-domainlist-chronological-model/CMakeLists.txt (+15/-0)
tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp (+93/-0)
tests/unittests/history-domainlist-model/tst_HistoryDomainListModelTests.cpp (+1/-2)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/domain-names-chronological
Reviewer Review Type Date Requested Status
Günter Schwann (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+176900@code.launchpad.net

Commit message

Keep the list of domain names sorted in reverse chronological order
(i.e. the domain with the latest entry visited first).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Günter Schwann (schwann) wrote :
Download full text (8.7 KiB)

Code looks ok.
But when adding a domain, that is already in the "last month", it's not added to the "today" section.

And after launching the webbrowser again, I always get a crash:
schwann@carbon:~/Projects/manhattan/domain-names-chronological$ gdb src/webbrowser-app
GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/schwann/Projects/manhattan/domain-names-chronological/src/webbrowser-app...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/schwann/Projects/manhattan/domain-names-chronological/src/webbrowser-app
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffed124700 (LWP 674)]
[New Thread 0x7fffdf5c7700 (LWP 675)]
Fontconfig warning: "/etc/fonts/conf.d/50-user.conf", line 14: reading configurations from ~/.fonts.conf is deprecated.
[New Thread 0x7fffde2fc700 (LWP 676)]
[New Thread 0x7fffec041700 (LWP 677)]
[New Thread 0x7fffccccf700 (LWP 678)]

WARNING: This project is using the experimental QML API extensions for QtWebKit and is therefore tied to a specific QtWebKit release.
WARNING: The experimental API will change from version to version, or even be removed. You have been warned!

Program received signal SIGSEGV, Segmentation fault.
__memmove_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:97
97 ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S: No such file or directory.
(gdb) bt
#0 __memmove_ssse3_back () at ../sysdeps/x86_64/multiarch/memcpy-ssse3-back.S:97
#1 0x00007ffff79aa0bf in memmove (__len=<optimized out>, __src=<optimized out>, __dest=<optimized out>)
    at /usr/include/x86_64-linux-gnu/bits/string3.h:57
#2 QVector<int>::erase (this=this@entry=0x72fa10, abegin=<optimized out>, aend=<optimized out>)
    at ../../include/QtCore/../../src/corelib/tools/qvector.h:625
#3 0x00007ffff79a0666 in remove (n=<optimized out>, i=150994944, this=0x72fa10)
    at ../../include/QtCore/../../src/corelib/tools/qvector.h:369
#4 QSortFilterProxyModelPrivate::remove_proxy_interval (this=this@entry=0x723f70, source_to_proxy=..., proxy_to_source=...,
    proxy_start=150994944, proxy_end=<optimized out>, proxy_parent=..., orient=orient@entry=Qt::Vertical,
    emit_signal=emit_signal@entry=true) at itemmodels/qsortfilterproxymodel.cpp:577
#5 0x00007ffff79a2ca2 in QSortFilterProxyModelPrivate::remove_source_items (this=this@entry=0x723f70, source_to_proxy=...,
    proxy_to_source=..., source_items=..., source_parent=..., orient=orient@entry=Qt::Vertical, emit_signal=emit_signal@entry=true)
    at itemmodels/qsortfilterproxymodel.cpp:553
#6 0x00007ffff79a2f6f in QSortFilterProxyModelPrivate::source_items_about_to_be_removed (this=0x723f70, source_parent=..., start=...

Read more...

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

I’m not seeing the crash, however I can reproduce the issue that you’re pointing out with a domain already visited last month (or this year, or whenever in the past) not moving to today when visited again.

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

> I’m not seeing the crash, however I can reproduce the issue that you’re
> pointing out with a domain already visited last month (or this year, or
> whenever in the past) not moving to today when visited again.

That said, this issue is already present in the trunk, so this is not a new regression.
If you don’t mind, I’ll file a bug report and tackle it separately.

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

> > I’m not seeing the crash, however I can reproduce the issue that you’re
> > pointing out with a domain already visited last month (or this year, or
> > whenever in the past) not moving to today when visited again.
>
> That said, this issue is already present in the trunk, so this is not a new
> regression.
> If you don’t mind, I’ll file a bug report and tackle it separately.

I filed bug #1204875 to track this issue.

Revision history for this message
Günter Schwann (schwann) wrote :

On the Nexus 10 I get the following (not so useful backtrace):
phablet@ubuntu-phablet:~$ gdb webbrowser-app
GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/webbrowser-app...(no debugging symbols found)...done.
(gdb) run --desktop_file_hint=/usr/share/applications/webbrowser-app.desktop
Starting program: /usr/bin/webbrowser-app --desktop_file_hint=/usr/share/applications/webbrowser-app.desktop
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
__pthread_gettid -2
[New Thread 0xb3363460 (LWP 5458)]
[New Thread 0xb2b63460 (LWP 5459)]
[New Thread 0xb2363460 (LWP 5460)]
[New Thread 0xb1b63460 (LWP 5461)]
[New Thread 0xb1363460 (LWP 5462)]
[New Thread 0xb0b63460 (LWP 5463)]
loaded the dummy plugin
loaded the Linux plugin
Registered the AalSensorPlugin types

[New Thread 0xb0225460 (LWP 5464)]
[New Thread 0xaf7ff460 (LWP 5465)]
[New Thread 0xaf958460 (LWP 5466)]
[New Thread 0xad454460 (LWP 5467)]

WARNING: This project is using the experimental QML API extensions for QtWebKit and is therefore tied to a specific QtWebKit release.
WARNING: The experimental API will change from version to version, or even be removed. You have been warned!

[New Thread 0xac9ff460 (LWP 5468)]
[New Thread 0xac08d460 (LWP 5469)]
[New Thread 0xab88d460 (LWP 5470)]
[New Thread 0xaaf6d460 (LWP 5471)]
[New Thread 0xaa76d460 (LWP 5472)]
[New Thread 0xa9f6d460 (LWP 5473)]
callbacks 0xb456b499 0xb456b49d
[New Thread 0xa976d460 (LWP 5475)]
[New Thread 0xa8f6d460 (LWP 5476)]
[New Thread 0xa876d460 (LWP 5477)]
creating surface at (0, 64) with size (2560, 1536)[New Thread 0xa7067460 (LWP 5478)]
[New Thread 0xa6827460 (LWP 5479)]
__pthread_gettid -2
loaded the dummy plugin
loaded the Linux plugin
Registered the AalSensorPlugin types

QSortFilterProxyModel: inconsistent changes reported by source model
QSortFilterProxyModel: inconsistent changes reported by source model
*** Error in `/usr/bin/webbrowser-app': double free or corruption (!prev): 0x0036db80 ***

Program received signal SIGABRT, Aborted.
0xb6142706 in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
(gdb) bt
#0 0xb6142706 in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
#1 0xb61505fe in raise () from /lib/arm-linux-gnueabihf/libc.so.6
#2 0xb6152e1a in abort () from /lib/arm-linux-gnueabihf/libc.so.6
#3 0xb61769ec in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
#4 0xb61769ec in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

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

I am now able to reproduce the crash on my desktop machine with the history.sqlite database provided by Günter, and when running in a saucy chroot (can’t reproduce the crash on raring, which is what my desktop is running at the moment).

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

Looks like the URL that triggers the crash is the following one:

    http://derstandard.at/1369363282846/Tuerkische-Spezialpolizei-stuermt-Protestcamp-auf-dem-Istanbuler-Taksim-Platz

The title of the page is:

    Proteste in der Türkei: Erdogan dankt Polizei für hartes Vorgehen gegen Demonstranten - Istanbuler Taksim-Platz gestürmt - Türkei - derStandard.at › International

I’m suspecting some sort of unicode issue, although I haven’t observed the issue before with accentuated characters…

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

Looks like the title is not the culprit, I’ve removed it from the entry (set it to ""), and I’m still getting the crash.

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

So the URL seems to be the culprit, and shortening it makes the crash go away, but I don’t know why (yet).

With this URL, I always observe the crash:

    http://derstandard.at/1369363282846/Tuerkische-Spezialpolizei-stuermt-Protestcamp-auf-dem

Whereas with this one, it doesn’t crash:

    http://derstandard.at/1369363282846/Tuerkische-Spezialpolizei-stuermt-Protestcamp

Note that I can reliably reproduce with the trunk version of the browser, so this isn’t linked to this branch.

Revision history for this message
Günter Schwann (schwann) wrote :

as the issues are not related to that MR (as it seems), I approve it

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Ubuntu/Components/Extras/Browser/CMakeLists.txt'
2--- src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2013-07-12 13:49:42 +0000
3+++ src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2013-07-25 10:05:47 +0000
4@@ -9,6 +9,7 @@
5 history-timeframe-model.cpp
6 history-domain-model.cpp
7 history-domainlist-model.cpp
8+ history-domainlist-chronological-model.cpp
9 tabs-model.cpp
10 webthumbnail-utils.cpp
11 webthumbnail-provider.cpp
12
13=== modified file 'src/Ubuntu/Components/Extras/Browser/TimelineView.qml'
14--- src/Ubuntu/Components/Extras/Browser/TimelineView.qml 2013-07-12 13:49:42 +0000
15+++ src/Ubuntu/Components/Extras/Browser/TimelineView.qml 2013-07-25 10:05:47 +0000
16@@ -112,48 +112,50 @@
17 spacing: units.gu(2)
18 orientation: ListView.Horizontal
19
20- model: HistoryDomainListModel {
21- sourceModel: HistoryTimeframeModel {
22- sourceModel: historyModel
23- start: {
24- var date = new Date()
25- if (model.timeframe == "yesterday") {
26- date.setDate(date.getDate() - 1)
27- } else if (model.timeframe == "last7days") {
28- date.setDate(date.getDate() - 7)
29- } else if (model.timeframe == "thismonth") {
30- date.setDate(1)
31- } else if (model.timeframe == "thisyear") {
32- date.setMonth(0)
33- date.setDate(1)
34- } else if (model.timeframe == "older") {
35- date.setFullYear(0, 0, 1)
36- }
37- date.setHours(0)
38- date.setMinutes(0)
39- date.setSeconds(0)
40- date.setMilliseconds(0)
41- return date
42- }
43- end: {
44- var date = new Date()
45- if (model.timeframe == "yesterday") {
46- date.setDate(date.getDate() - 1)
47- } else if (model.timeframe == "last7days") {
48- date.setDate(date.getDate() - 2)
49- } else if (model.timeframe == "thismonth") {
50- date.setDate(date.getDate() - 8)
51- } else if (model.timeframe == "thisyear") {
52- date.setDate(0)
53- } else if (model.timeframe == "older") {
54- date.setMonth(0)
55- date.setDate(0)
56- }
57- date.setHours(23)
58- date.setMinutes(59)
59- date.setSeconds(59)
60- date.setMilliseconds(999)
61- return date
62+ model: HistoryDomainListChronologicalModel {
63+ sourceModel: HistoryDomainListModel {
64+ sourceModel: HistoryTimeframeModel {
65+ sourceModel: historyModel
66+ start: {
67+ var date = new Date()
68+ if (model.timeframe == "yesterday") {
69+ date.setDate(date.getDate() - 1)
70+ } else if (model.timeframe == "last7days") {
71+ date.setDate(date.getDate() - 7)
72+ } else if (model.timeframe == "thismonth") {
73+ date.setDate(1)
74+ } else if (model.timeframe == "thisyear") {
75+ date.setMonth(0)
76+ date.setDate(1)
77+ } else if (model.timeframe == "older") {
78+ date.setFullYear(0, 0, 1)
79+ }
80+ date.setHours(0)
81+ date.setMinutes(0)
82+ date.setSeconds(0)
83+ date.setMilliseconds(0)
84+ return date
85+ }
86+ end: {
87+ var date = new Date()
88+ if (model.timeframe == "yesterday") {
89+ date.setDate(date.getDate() - 1)
90+ } else if (model.timeframe == "last7days") {
91+ date.setDate(date.getDate() - 2)
92+ } else if (model.timeframe == "thismonth") {
93+ date.setDate(date.getDate() - 8)
94+ } else if (model.timeframe == "thisyear") {
95+ date.setDate(0)
96+ } else if (model.timeframe == "older") {
97+ date.setMonth(0)
98+ date.setDate(0)
99+ }
100+ date.setHours(23)
101+ date.setMinutes(59)
102+ date.setSeconds(59)
103+ date.setMilliseconds(999)
104+ return date
105+ }
106 }
107 }
108 }
109
110=== added file 'src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.cpp'
111--- src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.cpp 1970-01-01 00:00:00 +0000
112+++ src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.cpp 2013-07-25 10:05:47 +0000
113@@ -0,0 +1,50 @@
114+/*
115+ * Copyright 2013 Canonical Ltd.
116+ *
117+ * This file is part of webbrowser-app.
118+ *
119+ * webbrowser-app is free software; you can redistribute it and/or modify
120+ * it under the terms of the GNU General Public License as published by
121+ * the Free Software Foundation; version 3.
122+ *
123+ * webbrowser-app is distributed in the hope that it will be useful,
124+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
125+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
126+ * GNU General Public License for more details.
127+ *
128+ * You should have received a copy of the GNU General Public License
129+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
130+ */
131+
132+#include "history-domainlist-chronological-model.h"
133+#include "history-domainlist-model.h"
134+
135+/*!
136+ \class HistoryDomainListChronologicalModel
137+ \brief Proxy model that sorts a domain list model in reverse chronological
138+ order
139+
140+ HistoryDomainListChronologicalModel is a proxy model that sorts a
141+ HistoryDomainListModel in reverse chronological order
142+ (i.e. the domain with the latest entry visited first).
143+*/
144+HistoryDomainListChronologicalModel::HistoryDomainListChronologicalModel(QObject* parent)
145+ : QSortFilterProxyModel(parent)
146+{
147+ setDynamicSortFilter(true);
148+ setSortRole(HistoryDomainListModel::LastVisit);
149+ sort(0, Qt::DescendingOrder);
150+}
151+
152+HistoryDomainListModel* HistoryDomainListChronologicalModel::sourceModel() const
153+{
154+ return qobject_cast<HistoryDomainListModel*>(QSortFilterProxyModel::sourceModel());
155+}
156+
157+void HistoryDomainListChronologicalModel::setSourceModel(HistoryDomainListModel* sourceModel)
158+{
159+ if (sourceModel != this->sourceModel()) {
160+ QSortFilterProxyModel::setSourceModel(sourceModel);
161+ Q_EMIT sourceModelChanged();
162+ }
163+}
164
165=== added file 'src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.h'
166--- src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.h 1970-01-01 00:00:00 +0000
167+++ src/Ubuntu/Components/Extras/Browser/history-domainlist-chronological-model.h 2013-07-25 10:05:47 +0000
168@@ -0,0 +1,43 @@
169+/*
170+ * Copyright 2013 Canonical Ltd.
171+ *
172+ * This file is part of webbrowser-app.
173+ *
174+ * webbrowser-app is free software; you can redistribute it and/or modify
175+ * it under the terms of the GNU General Public License as published by
176+ * the Free Software Foundation; version 3.
177+ *
178+ * webbrowser-app is distributed in the hope that it will be useful,
179+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
180+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
181+ * GNU General Public License for more details.
182+ *
183+ * You should have received a copy of the GNU General Public License
184+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
185+ */
186+
187+#ifndef __HISTORY_DOMAINLIST_CHRONOLOGICAL_MODEL_H__
188+#define __HISTORY_DOMAINLIST_CHRONOLOGICAL_MODEL_H__
189+
190+// Qt
191+#include <QtCore/QSortFilterProxyModel>
192+
193+class HistoryDomainListModel;
194+
195+class HistoryDomainListChronologicalModel : public QSortFilterProxyModel
196+{
197+ Q_OBJECT
198+
199+ Q_PROPERTY(HistoryDomainListModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
200+
201+public:
202+ HistoryDomainListChronologicalModel(QObject* parent=0);
203+
204+ HistoryDomainListModel* sourceModel() const;
205+ void setSourceModel(HistoryDomainListModel* sourceModel);
206+
207+Q_SIGNALS:
208+ void sourceModelChanged() const;
209+};
210+
211+#endif // __HISTORY_DOMAINLIST_CHRONOLOGICAL_MODEL_H__
212
213=== modified file 'src/Ubuntu/Components/Extras/Browser/history-domainlist-model.cpp'
214--- src/Ubuntu/Components/Extras/Browser/history-domainlist-model.cpp 2013-07-17 21:54:25 +0000
215+++ src/Ubuntu/Components/Extras/Browser/history-domainlist-model.cpp 2013-07-25 10:05:47 +0000
216@@ -53,6 +53,7 @@
217 static QHash<int, QByteArray> roles;
218 if (roles.isEmpty()) {
219 roles[Domain] = "domain";
220+ roles[LastVisit] = "lastVisit";
221 roles[Thumbnail] = "thumbnail";
222 roles[Entries] = "entries";
223 }
224@@ -65,6 +66,13 @@
225 return m_domains.count();
226 }
227
228+static void ensureEntriesUpToDate(HistoryDomainModel* entries)
229+{
230+ entries->blockSignals(true);
231+ entries->invalidate();
232+ entries->blockSignals(false);
233+}
234+
235 QVariant HistoryDomainListModel::data(const QModelIndex& index, int role) const
236 {
237 if (!index.isValid()) {
238@@ -75,13 +83,20 @@
239 return QVariant();
240 }
241 const QString domain = m_domains.keys().at(row);
242+ HistoryDomainModel* entries = m_domains.value(domain);
243+
244 switch (role) {
245 case Domain:
246 return domain;
247+ case LastVisit:
248+ {
249+ ensureEntriesUpToDate(entries);
250+ return entries->data(entries->index(0, 0), HistoryModel::LastVisit).toDateTime();
251+ }
252 case Thumbnail:
253 {
254 // Iterate over all the entries, and return the first valid thumbnail.
255- HistoryDomainModel* entries = m_domains.value(domain);
256+ ensureEntriesUpToDate(entries);
257 int count = entries->rowCount();
258 for (int i = 0; i < count; ++i) {
259 QUrl url = entries->data(entries->index(i, 0), HistoryModel::Url).toUrl();
260@@ -93,7 +108,7 @@
261 return QUrl();
262 }
263 case Entries:
264- return QVariant::fromValue(m_domains.value(domain));
265+ return QVariant::fromValue(entries);
266 default:
267 return QVariant();
268 }
269@@ -117,12 +132,6 @@
270 if (m_sourceModel != 0) {
271 connect(m_sourceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
272 SLOT(onRowsInserted(const QModelIndex&, int, int)));
273- connect(m_sourceModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
274- SLOT(onRowsRemoved(const QModelIndex&, int, int)));
275- connect(m_sourceModel, SIGNAL(layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint)),
276- SLOT(onLayoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint)));
277- connect(m_sourceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)),
278- SLOT(onDataChanged(QModelIndex,QModelIndex,QVector<int>)));
279 connect(m_sourceModel, SIGNAL(modelReset()), SLOT(onModelReset()));
280 }
281 endResetModel();
282@@ -152,7 +161,6 @@
283
284 void HistoryDomainListModel::onRowsInserted(const QModelIndex& parent, int start, int end)
285 {
286- QStringList updated;
287 for (int i = start; i <= end; ++i) {
288 QString domain = getDomainFromSourceModel(m_sourceModel->index(i, 0, parent));
289 if (!m_domains.contains(domain)) {
290@@ -167,64 +175,8 @@
291 beginInsertRows(QModelIndex(), insertAt, insertAt);
292 insertNewDomain(domain);
293 endInsertRows();
294- } else {
295- updated.append(domain);
296 }
297 }
298- QVector<int> updatedRoles = QVector<int>() << Thumbnail << Entries;
299- QStringList domains = m_domains.keys();
300- Q_FOREACH(const QString& domain, updated) {
301- QModelIndex index = this->index(domains.indexOf(domain), 0);
302- Q_EMIT dataChanged(index, index, updatedRoles);
303- }
304-}
305-
306-void HistoryDomainListModel::onRowsRemoved(const QModelIndex& parent, int start, int end)
307-{
308- Q_UNUSED(parent);
309- Q_UNUSED(start);
310- Q_UNUSED(end);
311- QSet<QString> newDomains;
312- int count = m_sourceModel->rowCount();
313- for (int i = 0; i < count; ++i) {
314- newDomains.insert(getDomainFromSourceModel(m_sourceModel->index(i, 0)));
315- }
316- QSet<QString> removed = QSet<QString>::fromList(m_domains.keys());
317- removed.subtract(newDomains);
318- Q_FOREACH(const QString& domain, removed) {
319- int removeAt = m_domains.keys().indexOf(domain);
320- beginRemoveRows(QModelIndex(), removeAt, removeAt);
321- delete m_domains.take(domain);
322- endRemoveRows();
323- }
324- // XXX: unfortunately there is no way to get a list of domains that had some
325- // (but not all) entries removed. To ensure the views are correctly updated,
326- // let’s emit the signal for all entries, even those that haven’t changed.
327- Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0),
328- QVector<int>() << Thumbnail << Entries);
329-}
330-
331-void HistoryDomainListModel::onLayoutChanged(const QList<QPersistentModelIndex>& parents, QAbstractItemModel::LayoutChangeHint hint)
332-{
333- Q_UNUSED(parents);
334- Q_UNUSED(hint);
335- Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0),
336- QVector<int>() << Thumbnail << Entries);
337-}
338-
339-void HistoryDomainListModel::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
340-{
341- Q_UNUSED(roles);
342- int start = topLeft.row();
343- int end = bottomRight.row();
344- QSet<QString> changed;
345- for (int i = start; i <= end; ++i) {
346- changed.insert(getDomainFromSourceModel(m_sourceModel->index(i, 0)));
347- }
348- Q_FOREACH(const QString& domain, changed) {
349- QModelIndex index = this->index(m_domains.keys().indexOf(domain), 0);
350- Q_EMIT dataChanged(index, index, QVector<int>() << Thumbnail << Entries);
351- }
352 }
353
354 void HistoryDomainListModel::onModelReset()
355@@ -240,6 +192,17 @@
356 HistoryDomainModel* model = new HistoryDomainModel(this);
357 model->setSourceModel(m_sourceModel);
358 model->setDomain(domain);
359+ connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)),
360+ SLOT(onDomainRowsInserted(QModelIndex, int, int)));
361+ connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
362+ SLOT(onDomainRowsRemoved(QModelIndex, int, int)));
363+ connect(model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
364+ SLOT(onDomainRowsMoved(QModelIndex, int, int, QModelIndex, int)));
365+ connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)),
366+ SLOT(onDomainLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)));
367+ connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
368+ SLOT(onDomainDataChanged(QModelIndex, QModelIndex)));
369+ connect(model, SIGNAL(modelReset()), SLOT(onDomainModelReset()));
370 m_domains.insert(domain, model);
371 }
372
373@@ -248,3 +211,78 @@
374 QUrl url = m_sourceModel->data(index, HistoryModel::Url).toUrl();
375 return DomainUtils::extractTopLevelDomainName(url).toLower();
376 }
377+
378+void HistoryDomainListModel::onDomainRowsInserted(const QModelIndex& parent, int start, int end)
379+{
380+ Q_UNUSED(parent);
381+ Q_UNUSED(start);
382+ Q_UNUSED(end);
383+ HistoryDomainModel* model = qobject_cast<HistoryDomainModel*>(sender());
384+ if (model != 0) {
385+ emitDataChanged(model->domain());
386+ }
387+}
388+
389+void HistoryDomainListModel::onDomainRowsRemoved(const QModelIndex& parent, int start, int end)
390+{
391+ Q_UNUSED(parent);
392+ Q_UNUSED(start);
393+ Q_UNUSED(end);
394+ HistoryDomainModel* model = qobject_cast<HistoryDomainModel*>(sender());
395+ if (model != 0) {
396+ const QString& domain = model->domain();
397+ if (model->rowCount() == 0) {
398+ int removeAt = m_domains.keys().indexOf(domain);
399+ beginRemoveRows(QModelIndex(), removeAt, removeAt);
400+ delete m_domains.take(domain);
401+ endRemoveRows();
402+ } else {
403+ emitDataChanged(domain);
404+ }
405+ }
406+}
407+
408+void HistoryDomainListModel::onDomainRowsMoved(const QModelIndex& sourceParent, int sourceStart, int sourceEnd, const QModelIndex& destinationParent, int destinationRow)
409+{
410+ HistoryDomainModel* model = qobject_cast<HistoryDomainModel*>(sender());
411+ if (model != 0) {
412+ emitDataChanged(model->domain());
413+ }
414+}
415+
416+void HistoryDomainListModel::onDomainLayoutChanged(const QList<QPersistentModelIndex>& parents, QAbstractItemModel::LayoutChangeHint hint)
417+{
418+ Q_UNUSED(parents);
419+ Q_UNUSED(hint);
420+ HistoryDomainModel* model = qobject_cast<HistoryDomainModel*>(sender());
421+ if (model != 0) {
422+ emitDataChanged(model->domain());
423+ }
424+}
425+
426+void HistoryDomainListModel::onDomainDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
427+{
428+ Q_UNUSED(topLeft);
429+ Q_UNUSED(bottomRight);
430+ HistoryDomainModel* model = qobject_cast<HistoryDomainModel*>(sender());
431+ if (model != 0) {
432+ emitDataChanged(model->domain());
433+ }
434+}
435+
436+void HistoryDomainListModel::onDomainModelReset()
437+{
438+ HistoryDomainModel* model = qobject_cast<HistoryDomainModel*>(sender());
439+ if (model != 0) {
440+ emitDataChanged(model->domain());
441+ }
442+}
443+
444+void HistoryDomainListModel::emitDataChanged(const QString& domain)
445+{
446+ int i = m_domains.keys().indexOf(domain);
447+ if (i != -1) {
448+ QModelIndex index = this->index(i, 0);
449+ Q_EMIT dataChanged(index, index, QVector<int>() << LastVisit << Thumbnail << Entries);
450+ }
451+}
452
453=== modified file 'src/Ubuntu/Components/Extras/Browser/history-domainlist-model.h'
454--- src/Ubuntu/Components/Extras/Browser/history-domainlist-model.h 2013-07-17 07:55:51 +0000
455+++ src/Ubuntu/Components/Extras/Browser/history-domainlist-model.h 2013-07-25 10:05:47 +0000
456@@ -41,6 +41,7 @@
457
458 enum Roles {
459 Domain = Qt::UserRole + 1,
460+ LastVisit,
461 Thumbnail,
462 Entries
463 };
464@@ -58,11 +59,15 @@
465
466 private Q_SLOTS:
467 void onRowsInserted(const QModelIndex& parent, int start, int end);
468- void onRowsRemoved(const QModelIndex& parent, int start, int end);
469- void onLayoutChanged(const QList<QPersistentModelIndex>& parents, QAbstractItemModel::LayoutChangeHint hint);
470- void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles);
471 void onModelReset();
472
473+ void onDomainRowsInserted(const QModelIndex& parent, int start, int end);
474+ void onDomainRowsRemoved(const QModelIndex& parent, int start, int end);
475+ void onDomainRowsMoved(const QModelIndex& sourceParent, int sourceStart, int sourceEnd, const QModelIndex& destinationParent, int destinationRow);
476+ void onDomainLayoutChanged(const QList<QPersistentModelIndex>& parents, QAbstractItemModel::LayoutChangeHint hint);
477+ void onDomainDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
478+ void onDomainModelReset();
479+
480 private:
481 HistoryTimeframeModel* m_sourceModel;
482 QMap<QString, HistoryDomainModel*> m_domains;
483@@ -71,6 +76,7 @@
484 void populateModel();
485 void insertNewDomain(const QString& domain);
486 QString getDomainFromSourceModel(const QModelIndex& index) const;
487+ void emitDataChanged(const QString& domain);
488 };
489
490 #endif // __HISTORY_DOMAINLIST_MODEL_H__
491
492=== modified file 'src/Ubuntu/Components/Extras/Browser/plugin.cpp'
493--- src/Ubuntu/Components/Extras/Browser/plugin.cpp 2013-07-12 13:49:42 +0000
494+++ src/Ubuntu/Components/Extras/Browser/plugin.cpp 2013-07-25 10:05:47 +0000
495@@ -22,6 +22,7 @@
496 #include "history-timeframe-model.h"
497 #include "history-domain-model.h"
498 #include "history-domainlist-model.h"
499+#include "history-domainlist-chronological-model.h"
500 #include "tabs-model.h"
501 #include "webthumbnail-provider.h"
502 #include "webview-thumbnailer.h"
503@@ -54,6 +55,7 @@
504 qmlRegisterType<HistoryTimeframeModel>(uri, 0, 1, "HistoryTimeframeModel");
505 qmlRegisterType<HistoryDomainModel>(uri, 0, 1, "HistoryDomainModel");
506 qmlRegisterType<HistoryDomainListModel>(uri, 0, 1, "HistoryDomainListModel");
507+ qmlRegisterType<HistoryDomainListChronologicalModel>(uri, 0, 1, "HistoryDomainListChronologicalModel");
508 qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel");
509 qmlRegisterType<WebviewThumbnailer>(uri, 0, 1, "WebviewThumbnailer");
510 }
511
512=== modified file 'tests/unittests/CMakeLists.txt'
513--- tests/unittests/CMakeLists.txt 2013-07-12 13:49:42 +0000
514+++ tests/unittests/CMakeLists.txt 2013-07-25 10:05:47 +0000
515@@ -6,4 +6,5 @@
516 add_subdirectory(history-timeframe-model)
517 add_subdirectory(history-domain-model)
518 add_subdirectory(history-domainlist-model)
519+add_subdirectory(history-domainlist-chronological-model)
520 add_subdirectory(tabs-model)
521
522=== added directory 'tests/unittests/history-domainlist-chronological-model'
523=== added file 'tests/unittests/history-domainlist-chronological-model/CMakeLists.txt'
524--- tests/unittests/history-domainlist-chronological-model/CMakeLists.txt 1970-01-01 00:00:00 +0000
525+++ tests/unittests/history-domainlist-chronological-model/CMakeLists.txt 2013-07-25 10:05:47 +0000
526@@ -0,0 +1,15 @@
527+set(TEST tst_HistoryDomainListChronologicalModelTests)
528+set(SOURCES
529+ ${webbrowser-plugin_SOURCE_DIR}/domain-utils.cpp
530+ ${webbrowser-plugin_SOURCE_DIR}/history-domain-model.cpp
531+ ${webbrowser-plugin_SOURCE_DIR}/history-domainlist-model.cpp
532+ ${webbrowser-plugin_SOURCE_DIR}/history-domainlist-chronological-model.cpp
533+ ${webbrowser-plugin_SOURCE_DIR}/history-model.cpp
534+ ${webbrowser-plugin_SOURCE_DIR}/history-timeframe-model.cpp
535+ ${webbrowser-plugin_SOURCE_DIR}/webthumbnail-utils.cpp
536+ tst_HistoryDomainListChronologicalModelTests.cpp
537+)
538+add_executable(${TEST} ${SOURCES})
539+include_directories(${webbrowser-plugin_SOURCE_DIR})
540+qt5_use_modules(${TEST} Core Sql Test)
541+add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST})
542
543=== added file 'tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp'
544--- tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp 1970-01-01 00:00:00 +0000
545+++ tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp 2013-07-25 10:05:47 +0000
546@@ -0,0 +1,93 @@
547+/*
548+ * Copyright 2013 Canonical Ltd.
549+ *
550+ * This file is part of webbrowser-app.
551+ *
552+ * webbrowser-app is free software; you can redistribute it and/or modify
553+ * it under the terms of the GNU General Public License as published by
554+ * the Free Software Foundation; version 3.
555+ *
556+ * webbrowser-app is distributed in the hope that it will be useful,
557+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
558+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
559+ * GNU General Public License for more details.
560+ *
561+ * You should have received a copy of the GNU General Public License
562+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
563+ */
564+
565+// Qt
566+#include <QtTest/QSignalSpy>
567+#include <QtTest/QtTest>
568+
569+// local
570+#include "domain-utils.h"
571+#include "history-model.h"
572+#include "history-domain-model.h"
573+#include "history-domainlist-model.h"
574+#include "history-domainlist-chronological-model.h"
575+#include "history-timeframe-model.h"
576+
577+class HistoryDomainListChronologicalModelTests : public QObject
578+{
579+ Q_OBJECT
580+
581+private:
582+ HistoryModel* history;
583+ HistoryTimeframeModel* timeframe;
584+ HistoryDomainListModel* domainlist;
585+ HistoryDomainListChronologicalModel* model;
586+
587+private Q_SLOTS:
588+ void init()
589+ {
590+ history = new HistoryModel;
591+ history->setDatabasePath(":memory:");
592+ timeframe = new HistoryTimeframeModel;
593+ timeframe->setSourceModel(history);
594+ domainlist = new HistoryDomainListModel;
595+ domainlist->setSourceModel(timeframe);
596+ model = new HistoryDomainListChronologicalModel;
597+ model->setSourceModel(domainlist);
598+ }
599+
600+ void cleanup()
601+ {
602+ delete model;
603+ delete domainlist;
604+ delete timeframe;
605+ delete history;
606+ }
607+
608+ void shouldBeInitiallyEmpty()
609+ {
610+ QCOMPARE(model->rowCount(), 0);
611+ }
612+
613+ void shouldNotifyWhenChangingSourceModel()
614+ {
615+ QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
616+ model->setSourceModel(domainlist);
617+ QVERIFY(spy.isEmpty());
618+ HistoryDomainListModel* domainlist2 = new HistoryDomainListModel;
619+ model->setSourceModel(domainlist2);
620+ QCOMPARE(spy.count(), 1);
621+ QCOMPARE(model->sourceModel(), domainlist2);
622+ model->setSourceModel(0);
623+ QCOMPARE(spy.count(), 2);
624+ QCOMPARE(model->sourceModel(), (HistoryDomainListModel*) 0);
625+ delete domainlist2;
626+ }
627+
628+ void shouldRemainSorted()
629+ {
630+ history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
631+ QTest::qWait(1001);
632+ history->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl());
633+ QCOMPARE(model->data(model->index(0, 0), HistoryDomainListModel::Domain).toString(), QString("ubuntu.com"));
634+ QCOMPARE(model->data(model->index(1, 0), HistoryDomainListModel::Domain).toString(), QString("example.org"));
635+ }
636+};
637+
638+QTEST_MAIN(HistoryDomainListChronologicalModelTests)
639+#include "tst_HistoryDomainListChronologicalModelTests.moc"
640
641=== modified file 'tests/unittests/history-domainlist-model/tst_HistoryDomainListModelTests.cpp'
642--- tests/unittests/history-domainlist-model/tst_HistoryDomainListModelTests.cpp 2013-07-17 07:55:51 +0000
643+++ tests/unittests/history-domainlist-model/tst_HistoryDomainListModelTests.cpp 2013-07-25 10:05:47 +0000
644@@ -156,13 +156,12 @@
645 {
646 history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
647 history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
648- QTest::qWait(100);
649
650 QSignalSpy spyRowsMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
651 qRegisterMetaType<QVector<int> >();
652 QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
653
654- history->add(QUrl("http://example.org/"), "New Example Domain", QUrl());
655+ history->add(QUrl("http://example.org/foobar"), "Example Domain", QUrl());
656 QVERIFY(spyRowsMoved.isEmpty());
657 QVERIFY(!spyDataChanged.isEmpty());
658 verifyDataChanged(spyDataChanged, 1);

Subscribers

People subscribed via source and target branches

to status/vote changes: