1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qtextbrowser.h>
32#include <qapplication.h>
33#include <qscrollbar.h>
34
35#include <qtextbrowser.h>
36#include <qtextobject.h>
37
38class TestBrowser : public QTextBrowser
39{
40public:
41 inline TestBrowser() {
42 show();
43 QApplication::setActiveWindow(this);
44 activateWindow();
45 setFocus();
46 QVERIFY(QTest::qWaitForWindowActive(this));
47 QVERIFY(hasFocus());
48 }
49
50 QVariant loadResource(int type, const QUrl &name) override;
51
52 int htmlLoadAttempts = 0;
53 QUrl lastResource;
54 QUrl sourceInsideLoadResource;
55 QUrl baseInsideLoadResource;
56};
57
58QVariant TestBrowser::loadResource(int type, const QUrl &name)
59{
60 if (type == QTextDocument::HtmlResource)
61 htmlLoadAttempts++;
62 lastResource = name;
63 sourceInsideLoadResource = source();
64 baseInsideLoadResource = document()->baseUrl();
65 return QTextBrowser::loadResource(type, name);
66}
67
68class tst_QTextBrowser : public QObject
69{
70 Q_OBJECT
71
72private slots:
73 void initTestCase();
74 void init();
75 void cleanup();
76
77 void noReloadOnAnchorJump();
78 void bgColorOnSourceChange();
79 void forwardButton();
80 void viewportPositionInHistory();
81 void relativeLinks();
82 void anchors();
83 void resourceAutoDetection();
84 void forwardBackwardAvailable();
85 void clearHistory();
86 void sourceInsideLoadResource();
87 void textInteractionFlags_vs_readOnly();
88 void inputMethodAttribute_vs_readOnly();
89 void anchorsWithSelfBuiltHtml();
90 void relativeNonLocalUrls();
91 void adjacentAnchors();
92 void loadResourceOnRelativeLocalFiles();
93 void focusIndicator();
94 void focusHistory();
95 void urlEncoding();
96 void sourceType_data();
97 void sourceType();
98 void unicode_data();
99 void unicode();
100
101private:
102 TestBrowser *browser;
103};
104
105void tst_QTextBrowser::initTestCase()
106{
107 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
108 QSKIP("Wayland: This fails. Figure out why.");
109}
110
111void tst_QTextBrowser::init()
112{
113 QString prefix = QFileInfo(QFINDTESTDATA("subdir")).absolutePath();
114 QVERIFY2(!prefix.isEmpty(), "Test data directory not found");
115 QDir::setCurrent(prefix);
116
117 browser = new TestBrowser;
118 browser->show();
119}
120
121void tst_QTextBrowser::cleanup()
122{
123 delete browser;
124 browser = nullptr;
125}
126
127void tst_QTextBrowser::noReloadOnAnchorJump()
128{
129 QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("anchor.html"));
130
131 browser->htmlLoadAttempts = 0;
132 browser->setSource(url);
133 QCOMPARE(browser->htmlLoadAttempts, 1);
134 QVERIFY(!browser->toPlainText().isEmpty());
135
136 url.setFragment(fragment: "jumphere"); // anchor.html#jumphere
137 browser->setSource(url);
138 QCOMPARE(browser->htmlLoadAttempts, 1);
139 QVERIFY(!browser->toPlainText().isEmpty());
140 QCOMPARE(browser->source(), url);
141}
142
143void tst_QTextBrowser::bgColorOnSourceChange()
144{
145 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html")));
146 QVERIFY(browser->document()->rootFrame()->frameFormat().hasProperty(QTextFormat::BackgroundBrush));
147 QCOMPARE(browser->document()->rootFrame()->frameFormat().background().color(), QColor(Qt::blue));
148
149 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithoutbg.html")));
150 QVERIFY(!browser->document()->rootFrame()->frameFormat().hasProperty(QTextFormat::BackgroundBrush));
151}
152
153void tst_QTextBrowser::forwardButton()
154{
155 QSignalSpy forwardEmissions(browser, SIGNAL(forwardAvailable(bool)));
156 QSignalSpy backwardEmissions(browser, SIGNAL(backwardAvailable(bool)));
157
158 QVERIFY(browser->historyTitle(-1).isEmpty());
159 QVERIFY(browser->historyTitle(0).isEmpty());
160 QVERIFY(browser->historyTitle(1).isEmpty());
161
162 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html")));
163
164 QVERIFY(!forwardEmissions.isEmpty());
165 QVariant val = forwardEmissions.takeLast()[0];
166 QCOMPARE(val.type(), QVariant::Bool);
167 QVERIFY(!val.toBool());
168
169 QVERIFY(!backwardEmissions.isEmpty());
170 val = backwardEmissions.takeLast()[0];
171 QCOMPARE(val.type(), QVariant::Bool);
172 QVERIFY(!val.toBool());
173
174 QVERIFY(browser->historyTitle(-1).isEmpty());
175 QCOMPARE(browser->historyUrl(0), QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html")));
176 QCOMPARE(browser->documentTitle(), QString("Page With BG"));
177 QCOMPARE(browser->historyTitle(0), QString("Page With BG"));
178 QVERIFY(browser->historyTitle(1).isEmpty());
179
180 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("anchor.html")));
181
182 QVERIFY(!forwardEmissions.isEmpty());
183 val = forwardEmissions.takeLast()[0];
184 QCOMPARE(val.type(), QVariant::Bool);
185 QVERIFY(!val.toBool());
186
187 QVERIFY(!backwardEmissions.isEmpty());
188 val = backwardEmissions.takeLast()[0];
189 QCOMPARE(val.type(), QVariant::Bool);
190 QVERIFY(val.toBool());
191
192 QCOMPARE(browser->historyTitle(-1), QString("Page With BG"));
193 QCOMPARE(browser->historyTitle(0), QString("Sample Anchor"));
194 QVERIFY(browser->historyTitle(1).isEmpty());
195
196 browser->backward();
197
198 QVERIFY(!forwardEmissions.isEmpty());
199 val = forwardEmissions.takeLast()[0];
200 QCOMPARE(val.type(), QVariant::Bool);
201 QVERIFY(val.toBool());
202
203 QVERIFY(!backwardEmissions.isEmpty());
204 val = backwardEmissions.takeLast()[0];
205 QCOMPARE(val.type(), QVariant::Bool);
206 QVERIFY(!val.toBool());
207
208 QVERIFY(browser->historyTitle(-1).isEmpty());
209 QCOMPARE(browser->historyTitle(0), QString("Page With BG"));
210 QCOMPARE(browser->historyTitle(1), QString("Sample Anchor"));
211
212 browser->setSource(QUrl(QFINDTESTDATA("pagewithoutbg.html")));
213
214#ifdef Q_OS_WINRT
215 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
216#endif
217 QVERIFY(!forwardEmissions.isEmpty());
218 val = forwardEmissions.takeLast()[0];
219 QCOMPARE(val.type(), QVariant::Bool);
220 QVERIFY(!val.toBool());
221
222 QVERIFY(!backwardEmissions.isEmpty());
223 val = backwardEmissions.takeLast()[0];
224 QCOMPARE(val.type(), QVariant::Bool);
225 QVERIFY(val.toBool());
226}
227
228void tst_QTextBrowser::viewportPositionInHistory()
229{
230 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("bigpage.html")));
231 browser->scrollToAnchor(name: "bottom");
232 QVERIFY(browser->verticalScrollBar()->value() > 0);
233
234 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html")));
235 QCOMPARE(browser->verticalScrollBar()->value(), 0);
236
237 browser->backward();
238 QVERIFY(browser->verticalScrollBar()->value() > 0);
239}
240
241void tst_QTextBrowser::relativeLinks()
242{
243#ifdef BUILTIN_TESTDATA
244 QSKIP("Relative links cannot be checked when resources are used to package tests.");
245#endif
246 QSignalSpy sourceChangedSpy(browser, SIGNAL(sourceChanged(QUrl)));
247 browser->setSource(QUrl("subdir/../qtextbrowser.html"));
248 QVERIFY(!browser->document()->isEmpty());
249 QCOMPARE(sourceChangedSpy.count(), 1);
250 QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/../qtextbrowser.html"));
251 browser->setSource(QUrl("subdir/index.html"));
252 QVERIFY(!browser->document()->isEmpty());
253 QCOMPARE(sourceChangedSpy.count(), 1);
254 QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/index.html"));
255 browser->setSource(QUrl("anchor.html"));
256 QVERIFY(!browser->document()->isEmpty());
257 QCOMPARE(sourceChangedSpy.count(), 1);
258 QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("anchor.html"));
259 browser->setSource(QUrl("subdir/index.html"));
260 QVERIFY(!browser->document()->isEmpty());
261 QCOMPARE(sourceChangedSpy.count(), 1);
262 QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/index.html"));
263
264 // using QUrl::fromLocalFile()
265 browser->setSource(QUrl::fromLocalFile(localfile: "anchor.html"));
266 QVERIFY(!browser->document()->isEmpty());
267 QCOMPARE(sourceChangedSpy.count(), 1);
268 QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("file:anchor.html"));
269 browser->setSource(QUrl("subdir/../qtextbrowser.html"));
270 QVERIFY(!browser->document()->isEmpty());
271 QCOMPARE(sourceChangedSpy.count(), 1);
272 QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/../qtextbrowser.html"));
273}
274
275void tst_QTextBrowser::anchors()
276{
277 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("bigpage.html")));
278 browser->setSource(QUrl("#bottom"));
279 QVERIFY(browser->verticalScrollBar()->value() > 0);
280
281 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("bigpage.html")));
282 browser->setSource(QUrl("#id-anchor"));
283 QVERIFY(browser->verticalScrollBar()->value() > 0);
284}
285
286void tst_QTextBrowser::resourceAutoDetection()
287{
288 browser->setHtml("<img src=\":/some/resource\"/>");
289 QCOMPARE(browser->lastResource.toString(), QString("qrc:/some/resource"));
290}
291
292void tst_QTextBrowser::forwardBackwardAvailable()
293{
294 QSignalSpy backwardSpy(browser, SIGNAL(backwardAvailable(bool)));
295 QSignalSpy forwardSpy(browser, SIGNAL(forwardAvailable(bool)));
296
297 QVERIFY(!browser->isBackwardAvailable());
298 QVERIFY(!browser->isForwardAvailable());
299
300 browser->setSource(QUrl::fromLocalFile(localfile: "anchor.html"));
301 QVERIFY(!browser->isBackwardAvailable());
302 QVERIFY(!browser->isForwardAvailable());
303 QCOMPARE(backwardSpy.count(), 1);
304 QVERIFY(!backwardSpy.at(0).at(0).toBool());
305 QCOMPARE(forwardSpy.count(), 1);
306 QVERIFY(!forwardSpy.at(0).at(0).toBool());
307
308 backwardSpy.clear();
309 forwardSpy.clear();
310
311 browser->setSource(QUrl::fromLocalFile(localfile: "bigpage.html"));
312 QVERIFY(browser->isBackwardAvailable());
313 QVERIFY(!browser->isForwardAvailable());
314 QCOMPARE(backwardSpy.count(), 1);
315 QVERIFY(backwardSpy.at(0).at(0).toBool());
316 QCOMPARE(forwardSpy.count(), 1);
317 QVERIFY(!forwardSpy.at(0).at(0).toBool());
318
319 backwardSpy.clear();
320 forwardSpy.clear();
321
322 browser->setSource(QUrl::fromLocalFile(localfile: "pagewithbg.html"));
323 QVERIFY(browser->isBackwardAvailable());
324 QVERIFY(!browser->isForwardAvailable());
325 QCOMPARE(backwardSpy.count(), 1);
326 QVERIFY(backwardSpy.at(0).at(0).toBool());
327 QCOMPARE(forwardSpy.count(), 1);
328 QVERIFY(!forwardSpy.at(0).at(0).toBool());
329
330 backwardSpy.clear();
331 forwardSpy.clear();
332
333 browser->backward();
334 QVERIFY(browser->isBackwardAvailable());
335 QVERIFY(browser->isForwardAvailable());
336 QCOMPARE(backwardSpy.count(), 1);
337 QVERIFY(backwardSpy.at(0).at(0).toBool());
338 QCOMPARE(forwardSpy.count(), 1);
339 QVERIFY(forwardSpy.at(0).at(0).toBool());
340
341 backwardSpy.clear();
342 forwardSpy.clear();
343
344 browser->backward();
345 QVERIFY(!browser->isBackwardAvailable());
346 QVERIFY(browser->isForwardAvailable());
347 QCOMPARE(backwardSpy.count(), 1);
348 QVERIFY(!backwardSpy.at(0).at(0).toBool());
349 QCOMPARE(forwardSpy.count(), 1);
350 QVERIFY(forwardSpy.at(0).at(0).toBool());
351
352 backwardSpy.clear();
353 forwardSpy.clear();
354
355 browser->forward();
356 QVERIFY(browser->isBackwardAvailable());
357 QVERIFY(browser->isForwardAvailable());
358 QCOMPARE(backwardSpy.count(), 1);
359 QVERIFY(backwardSpy.at(0).at(0).toBool());
360 QCOMPARE(forwardSpy.count(), 1);
361 QVERIFY(forwardSpy.at(0).at(0).toBool());
362
363 backwardSpy.clear();
364 forwardSpy.clear();
365
366 browser->forward();
367 QVERIFY(browser->isBackwardAvailable());
368 QVERIFY(!browser->isForwardAvailable());
369 QCOMPARE(backwardSpy.count(), 1);
370 QVERIFY(backwardSpy.at(0).at(0).toBool());
371 QCOMPARE(forwardSpy.count(), 1);
372 QVERIFY(!forwardSpy.at(0).at(0).toBool());
373
374 backwardSpy.clear();
375 forwardSpy.clear();
376}
377
378void tst_QTextBrowser::clearHistory()
379{
380 QSignalSpy backwardSpy(browser, SIGNAL(backwardAvailable(bool)));
381 QSignalSpy forwardSpy(browser, SIGNAL(forwardAvailable(bool)));
382
383 QVERIFY(!browser->isBackwardAvailable());
384 QVERIFY(!browser->isForwardAvailable());
385
386 browser->clearHistory();
387 QVERIFY(!browser->isBackwardAvailable());
388 QVERIFY(!browser->isForwardAvailable());
389 QCOMPARE(backwardSpy.count(), 1);
390 QVERIFY(!backwardSpy.at(0).at(0).toBool());
391 QCOMPARE(forwardSpy.count(), 1);
392 QVERIFY(!forwardSpy.at(0).at(0).toBool());
393 QVERIFY(browser->historyTitle(-1).isEmpty());
394 QVERIFY(browser->historyTitle(0).isEmpty());
395 QVERIFY(browser->historyTitle(1).isEmpty());
396
397 backwardSpy.clear();
398 forwardSpy.clear();
399
400 browser->setSource(QUrl::fromLocalFile(localfile: "anchor.html"));
401 QVERIFY(!browser->isBackwardAvailable());
402 QVERIFY(!browser->isForwardAvailable());
403 QCOMPARE(backwardSpy.count(), 1);
404 QVERIFY(!backwardSpy.at(0).at(0).toBool());
405 QCOMPARE(forwardSpy.count(), 1);
406 QVERIFY(!forwardSpy.at(0).at(0).toBool());
407
408 backwardSpy.clear();
409 forwardSpy.clear();
410
411 browser->setSource(QUrl::fromLocalFile(localfile: "bigpage.html"));
412 QVERIFY(browser->isBackwardAvailable());
413 QVERIFY(!browser->isForwardAvailable());
414 QCOMPARE(backwardSpy.count(), 1);
415 QVERIFY(backwardSpy.at(0).at(0).toBool());
416 QCOMPARE(forwardSpy.count(), 1);
417 QVERIFY(!forwardSpy.at(0).at(0).toBool());
418
419 backwardSpy.clear();
420 forwardSpy.clear();
421
422 browser->clearHistory();
423 QVERIFY(!browser->isBackwardAvailable());
424 QVERIFY(!browser->isForwardAvailable());
425 QCOMPARE(backwardSpy.count(), 1);
426 QVERIFY(!backwardSpy.at(0).at(0).toBool());
427 QCOMPARE(forwardSpy.count(), 1);
428 QVERIFY(!forwardSpy.at(0).at(0).toBool());
429 QVERIFY(browser->historyTitle(-1).isEmpty());
430 QVERIFY(browser->historyTitle(1).isEmpty());
431
432 QCOMPARE(browser->source(), QUrl::fromLocalFile("bigpage.html"));
433 browser->backward();
434 QCOMPARE(browser->source(), QUrl::fromLocalFile("bigpage.html"));
435 browser->home();
436 QCOMPARE(browser->source(), QUrl::fromLocalFile("bigpage.html"));
437}
438
439void tst_QTextBrowser::sourceInsideLoadResource()
440{
441#ifdef Q_OS_WINRT
442 QSKIP("Paths cannot be compared if applications are sandboxed.");
443#endif
444 QUrl url = QUrl::fromLocalFile(localfile: "pagewithimage.html"); // "file://pagewithimage.html"
445 browser->setSource(url);
446 QCOMPARE(browser->lastResource, QUrl::fromLocalFile(QDir::current().filePath("foobar.png")));
447 // baseUrl was not set because the source URL was a relative one
448 QCOMPARE(browser->baseInsideLoadResource, QUrl());
449 QEXPECT_FAIL("", "This is currently not supported", Continue);
450 QCOMPARE(browser->sourceInsideLoadResource.toString(), url.toString());
451 url = QUrl::fromLocalFile(localfile: QDir::current().filePath(fileName: "pagewithimage.html")); // "file:///home/user/path/to/pagewithimage.html"
452 browser->setSource(url);
453 QCOMPARE(browser->lastResource, QUrl::fromLocalFile(QDir::current().filePath("foobar.png")));
454 // baseUrl has the full path, and that's where relative-path resources come from
455 QCOMPARE(browser->baseInsideLoadResource, QUrl::fromLocalFile(QDir::currentPath() + QLatin1Char('/')));
456}
457
458void tst_QTextBrowser::textInteractionFlags_vs_readOnly()
459{
460 QVERIFY(browser->isReadOnly());
461 QCOMPARE(browser->textInteractionFlags(), Qt::TextBrowserInteraction);
462 browser->setReadOnly(true);
463 QCOMPARE(browser->textInteractionFlags(), Qt::TextBrowserInteraction);
464 browser->setReadOnly(false);
465 QCOMPARE(browser->textInteractionFlags(), Qt::TextEditorInteraction);
466 browser->setReadOnly(true);
467 QCOMPARE(browser->textInteractionFlags(), Qt::TextBrowserInteraction);
468}
469
470void tst_QTextBrowser::inputMethodAttribute_vs_readOnly()
471{
472 QVERIFY(browser->isReadOnly());
473 QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled));
474 browser->setReadOnly(false);
475 QVERIFY(browser->testAttribute(Qt::WA_InputMethodEnabled));
476 browser->setReadOnly(true);
477 QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled));
478}
479
480void tst_QTextBrowser::anchorsWithSelfBuiltHtml()
481{
482 browser->setHtml("<p>Hello <a href=\"#anchor\">Link</a>"
483 "<p><a name=\"anchor\"/>Blah</p>");
484 QVERIFY(browser->document()->blockCount() > 1);
485 browser->setSource(QUrl("#anchor"));
486 QVERIFY(browser->document()->blockCount() > 1);
487}
488
489class HelpBrowser : public QTextBrowser
490{
491public:
492 virtual QVariant loadResource(int /*type*/, const QUrl &name) {
493 QString url = name.toString();
494 if(url == "qhelp://docs/index.html") {
495 return "index";
496 } else if (url == "qhelp://docs/classes.html") {
497 return "classes";
498 } else if (url == "qhelp://docs/someclass.html") {
499 return "someclass";
500 }
501 return QVariant();
502 }
503};
504
505void tst_QTextBrowser::relativeNonLocalUrls()
506{
507 HelpBrowser browser;
508 browser.setSource(QUrl("qhelp://docs/index.html"));
509 QCOMPARE(browser.toPlainText(), QString("index"));
510 browser.setSource(QUrl("classes.html"));
511 QCOMPARE(browser.toPlainText(), QString("classes"));
512 browser.setSource(QUrl("someclass.html"));
513 QCOMPARE(browser.toPlainText(), QString("someclass"));
514}
515
516class HackBrowser : public TestBrowser
517{
518public:
519 inline bool focusTheNextChild() { return QTextBrowser::focusNextChild(); }
520 inline bool focusThePreviousChild() { return QTextBrowser::focusPreviousChild(); }
521};
522
523void tst_QTextBrowser::adjacentAnchors()
524{
525 HackBrowser *browser = new HackBrowser;
526 browser->setHtml("<a href=\"#foo\">foo</a><a href=\"#bar\">bar</a>");
527 QVERIFY(browser->focusTheNextChild());
528 QCOMPARE(browser->textCursor().selectedText(), QString("foo"));
529
530 QVERIFY(browser->focusTheNextChild());
531 QCOMPARE(browser->textCursor().selectedText(), QString("bar"));
532
533 QVERIFY(!browser->focusTheNextChild());
534
535 browser->moveCursor(operation: QTextCursor::End);
536 QVERIFY(browser->focusThePreviousChild());
537 QCOMPARE(browser->textCursor().selectedText(), QString("bar"));
538 QVERIFY(browser->focusThePreviousChild());
539 QCOMPARE(browser->textCursor().selectedText(), QString("foo"));
540
541 delete browser;
542}
543
544void tst_QTextBrowser::loadResourceOnRelativeLocalFiles()
545{
546#ifndef BUILTIN_TESTDATA
547 browser->setSource(QUrl::fromLocalFile(localfile: "subdir/index.html"));
548#else
549 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("subdir/index.html")));
550#endif
551 QVERIFY(!browser->toPlainText().isEmpty());
552 QVariant v = browser->loadResource(type: QTextDocument::HtmlResource, name: QUrl("../anchor.html"));
553 QVERIFY(v.isValid());
554 QCOMPARE(v.type(), QVariant::ByteArray);
555 QVERIFY(!v.toByteArray().isEmpty());
556}
557
558void tst_QTextBrowser::focusIndicator()
559{
560 HackBrowser *browser = new HackBrowser;
561 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("firstpage.html")));
562 QVERIFY(!browser->textCursor().hasSelection());
563
564 browser->focusTheNextChild();
565
566 QVERIFY(browser->textCursor().hasSelection());
567 QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page"));
568
569#ifdef QT_KEYPAD_NAVIGATION
570 browser->setEditFocus(true);
571#endif
572 QTest::keyClick(widget: browser, key: Qt::Key_Enter);
573 QVERIFY(!browser->textCursor().hasSelection());
574
575 browser->focusTheNextChild();
576
577 QVERIFY(browser->textCursor().hasSelection());
578 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page"));
579
580 QTest::keyClick(widget: browser, key: Qt::Key_Enter);
581 QVERIFY(!browser->textCursor().hasSelection());
582
583 browser->backward();
584
585 QVERIFY(browser->textCursor().hasSelection());
586 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page"));
587
588 browser->backward();
589
590 QVERIFY(browser->textCursor().hasSelection());
591 QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page"));
592
593 browser->forward();
594
595 QVERIFY(browser->textCursor().hasSelection());
596 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page"));
597
598 browser->backward();
599 browser->backward();
600
601 QVERIFY(browser->textCursor().hasSelection());
602 QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page"));
603
604 QTest::keyClick(widget: browser, key: Qt::Key_Enter);
605 QVERIFY(!browser->textCursor().hasSelection());
606
607 delete browser;
608}
609
610void tst_QTextBrowser::focusHistory()
611{
612 HackBrowser *browser = new HackBrowser;
613 browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("firstpage.html")));
614 QVERIFY(!browser->textCursor().hasSelection());
615
616 browser->focusTheNextChild();
617
618 QVERIFY(browser->textCursor().hasSelection());
619 QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page"));
620
621#ifdef QT_KEYPAD_NAVIGATION
622 browser->setEditFocus(true);
623#endif
624 QTest::keyClick(widget: browser, key: Qt::Key_Enter);
625 QVERIFY(!browser->textCursor().hasSelection());
626
627 browser->focusTheNextChild();
628
629 QVERIFY(browser->textCursor().hasSelection());
630 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page"));
631
632 browser->backward();
633
634 QVERIFY(browser->textCursor().hasSelection());
635 QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page"));
636
637 browser->focusTheNextChild();
638
639 QVERIFY(browser->textCursor().hasSelection());
640 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page"));
641
642 // Despite the third page link being highlighted, going forward should go to second,
643 // and going back after that should still highlight the third link
644 browser->forward();
645
646 QVERIFY(browser->textCursor().hasSelection());
647 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page"));
648
649 browser->backward();
650
651 QVERIFY(browser->textCursor().hasSelection());
652 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page"));
653
654 browser->forward();
655
656 QVERIFY(browser->textCursor().hasSelection());
657 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page"));
658
659 QTest::keyClick(widget: browser, key: Qt::Key_Enter);
660 QVERIFY(!browser->textCursor().hasSelection());
661
662 browser->backward();
663 browser->backward();
664
665 QVERIFY(browser->textCursor().hasSelection());
666 QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page"));
667
668 delete browser;
669}
670
671void tst_QTextBrowser::urlEncoding()
672{
673 HackBrowser *browser = new HackBrowser;
674 browser->setOpenLinks(false);
675 browser->setHtml("<a href=\"http://www.google.com/q=%22\">link</a>");
676 browser->focusTheNextChild();
677
678 QSignalSpy spy(browser, SIGNAL(anchorClicked(QUrl)));
679
680#ifdef QT_KEYPAD_NAVIGATION
681 browser->setEditFocus(true);
682#endif
683 QTest::keyClick(widget: browser, key: Qt::Key_Enter);
684 QCOMPARE(spy.count(), 1);
685
686 QUrl url = spy.at(i: 0).at(i: 0).toUrl();
687 QCOMPARE(url.toEncoded(), QByteArray("http://www.google.com/q=%22"));
688
689 delete browser;
690}
691
692void tst_QTextBrowser::sourceType_data()
693{
694 QTest::addColumn<QString>(name: "sourceFile");
695 QTest::addColumn<QTextDocument::ResourceType>(name: "sourceType");
696 QTest::addColumn<int>(name: "expectedMaxHeadingLevel");
697 QTest::addColumn<QTextDocument::ResourceType>(name: "expectedSourceType");
698
699#if QT_CONFIG(textmarkdownreader)
700 const int maxMdHeadingLevel = 3;
701 const QTextDocument::ResourceType mdExpectedType = QTextDocument::MarkdownResource;
702#else
703 // If Qt doesn't support markdown, and we read a MD document anyway, it won't have any H3's.
704 const int maxMdHeadingLevel = 0;
705 const QTextDocument::ResourceType mdExpectedType = QTextDocument::HtmlResource;
706#endif
707 QTest::newRow(dataTag: "markdown detected") << "markdown.md" << QTextDocument::UnknownResource << maxMdHeadingLevel << mdExpectedType;
708 QTest::newRow(dataTag: "markdown specified") << "markdown.really" << QTextDocument::MarkdownResource << maxMdHeadingLevel << mdExpectedType;
709 QTest::newRow(dataTag: "markdown not identified") << "markdown.really" << QTextDocument::UnknownResource << 0 << QTextDocument::HtmlResource;
710 QTest::newRow(dataTag: "html detected") << "heading.html" << QTextDocument::UnknownResource << 3 << QTextDocument::HtmlResource;
711 QTest::newRow(dataTag: "html specified") << "heading.html" << QTextDocument::HtmlResource << 3 << QTextDocument::HtmlResource;
712}
713
714void tst_QTextBrowser::sourceType()
715{
716 QFETCH(QString, sourceFile);
717 QFETCH(QTextDocument::ResourceType, sourceType);
718 QFETCH(int, expectedMaxHeadingLevel);
719 QFETCH(QTextDocument::ResourceType, expectedSourceType);
720 if (sourceType == QTextDocument::UnknownResource)
721 // verify that the property setter works, with its default parameter for sourceType
722 browser->setProperty(name: "source", value: QUrl::fromLocalFile(QFINDTESTDATA(sourceFile)));
723 else
724 browser->setSource(name: QUrl::fromLocalFile(QFINDTESTDATA(sourceFile)), type: sourceType);
725 QCOMPARE(browser->sourceType(), expectedSourceType);
726 QTextFrame::iterator iterator = browser->document()->rootFrame()->begin();
727 int maxHeadingLevel = -1;
728 while (!iterator.atEnd())
729 maxHeadingLevel = qMax(a: iterator++.currentBlock().blockFormat().intProperty(propertyId: QTextFormat::HeadingLevel), b: maxHeadingLevel);
730 QCOMPARE(maxHeadingLevel, expectedMaxHeadingLevel);
731}
732
733void tst_QTextBrowser::unicode_data()
734{
735 QTest::addColumn<QString>(name: "sourceFile");
736 QTest::addColumn<QTextDocument::ResourceType>(name: "sourceType");
737 QTest::addColumn<QString>(name: "expectedText");
738
739#if QT_CONFIG(textmarkdownreader)
740 QTest::newRow(dataTag: "markdown with quotes and fractions") << "quotesAndFractions.md" << QTextDocument::MarkdownResource <<
741 "you\u2019ll hope to see \u275Dquotes\u275E \uFE601\u00BD \u2154 \u00BC \u2157 \u215A \u215D some \u201Cvulgar\u201D fractions (pardon my \u00ABFrench\u00BB)";
742#endif
743}
744
745void tst_QTextBrowser::unicode()
746{
747 QFETCH(QString, sourceFile);
748 QFETCH(QTextDocument::ResourceType, sourceType);
749 QFETCH(QString, expectedText);
750 browser->setSource(name: QUrl::fromLocalFile(QFINDTESTDATA(sourceFile)), type: sourceType);
751 QTextFrame::iterator iterator = browser->document()->rootFrame()->begin();
752 while (!iterator.atEnd()) {
753 QString blockText = iterator++.currentBlock().text();
754 if (!blockText.isEmpty())
755 QCOMPARE(blockText, expectedText);
756 }
757}
758
759QTEST_MAIN(tst_QTextBrowser)
760#include "tst_qtextbrowser.moc"
761

source code of qtbase/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp