Merge lp:~osomon/webbrowser-app/domain-names-chronological into lp:webbrowser-app
- domain-names-chronological
- Merge into trunk
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 |
Related bugs: |
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).
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
Günter Schwann (schwann) wrote : | # |
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@
GNU gdb (GDB) 7.5.91.
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://
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://
Reading symbols from /home/schwann/
(gdb) run
Starting program: /home/schwann/
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_
[New Thread 0x7fffed124700 (LWP 674)]
[New Thread 0x7fffdf5c7700 (LWP 675)]
Fontconfig warning: "/etc/fonts/
[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_
97 ../sysdeps/
(gdb) bt
#0 __memmove_
#1 0x00007ffff79aa0bf in memmove (__len=<optimized out>, __src=<optimized out>, __dest=<optimized out>)
at /usr/include/
#2 QVector<int>::erase (this=this@
at ../../include/
#3 0x00007ffff79a0666 in remove (n=<optimized out>, i=150994944, this=0x72fa10)
at ../../include/
#4 QSortFilterProx
proxy_
emit_
#5 0x00007ffff79a2ca2 in QSortFilterProx
proxy_
at itemmodels/
#6 0x00007ffff79a2f6f in QSortFilterProx
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.
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.
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.
Günter Schwann (schwann) wrote : | # |
On the Nexus 10 I get the following (not so useful backtrace):
phablet@
GNU gdb (GDB) 7.5.91.
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://
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-
For bug reporting instructions, please see:
<http://
Reading symbols from /usr/bin/
(gdb) run --desktop_
Starting program: /usr/bin/
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-
__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
QSortFilterProx
QSortFilterProx
*** Error in `/usr/bin/
Program received signal SIGABRT, Aborted.
0xb6142706 in ?? () from /lib/arm-
(gdb) bt
#0 0xb6142706 in ?? () from /lib/arm-
#1 0xb61505fe in raise () from /lib/arm-
#2 0xb6152e1a in abort () from /lib/arm-
#3 0xb61769ec in ?? () from /lib/arm-
#4 0xb61769ec in ?? () from /lib/arm-
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
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).
Olivier Tilloy (osomon) wrote : | # |
Looks like the URL that triggers the crash is the following one:
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…
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.
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://
Whereas with this one, it doesn’t crash:
http://
Note that I can reliably reproduce with the trunk version of the browser, so this isn’t linked to this branch.
Günter Schwann (schwann) wrote : | # |
as the issues are not related to that MR (as it seems), I approve it
Preview Diff
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); |
PASSED: Continuous integration, rev:241 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 219/ jenkins. qa.ubuntu. com/job/ generic- mediumtests- saucy/1529 jenkins. qa.ubuntu. com/job/ webbrowser- app-saucy- amd64-ci/ 102 jenkins. qa.ubuntu. com/job/ webbrowser- app-saucy- armhf-ci/ 102 jenkins. qa.ubuntu. com/job/ webbrowser- app-saucy- armhf-ci/ 102/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-saucy- i386-ci/ 102 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- saucy/1533 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- saucy/1533/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- runner- saucy/1294
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ webbrowser- app-ci/ 219/rebuild
http://