Skip to content

Commit f4ff60c

Browse files
committed
Implement QtWebEngine HTML bookmark import
1 parent f71ad91 commit f4ff60c

File tree

5 files changed

+301
-1
lines changed

5 files changed

+301
-1
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ add_executable(otter-browser WIN32 MACOSX_BUNDLE
529529
)
530530

531531
if (Qt5WebEngineWidgets_FOUND AND ENABLE_QTWEBENGINE)
532-
target_link_libraries(otter-browser Qt5::WebEngineCore Qt5::WebEngineWidgets)
532+
target_link_libraries(otter-browser Qt5::WebEngineCore Qt5::WebEngineWidgets Qt5::WebChannel)
533533
endif ()
534534

535535
if (Qt5WebKitWidgets_FOUND AND ENABLE_QTWEBKIT)

src/modules/backends/web/qtwebengine/QtWebEngineResources.qrc

+1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
<file>resources/hideBlockedRequests.js</file>
99
<file>resources/hitTest.js</file>
1010
<file>resources/imageViewer.js</file>
11+
<file>resources/importBookmarks.js</file>
1112
</qresource>
1213
</RCC>

src/modules/backends/web/qtwebengine/QtWebEngineWebBackend.cpp

+206
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "QtWebEngineTransfer.h"
2424
#include "QtWebEngineUrlRequestInterceptor.h"
2525
#include "QtWebEngineWebWidget.h"
26+
#include "../../../../core/BookmarksManager.h"
2627
#include "../../../../core/ContentFiltersManager.h"
2728
#include "../../../../core/HandlersManager.h"
2829
#include "../../../../core/NetworkManagerFactory.h"
@@ -37,6 +38,7 @@
3738
#include <QtCore/QCoreApplication>
3839
#include <QtCore/QDir>
3940
#include <QtCore/QRegularExpression>
41+
#include <QtWebChannel/QtWebChannel>
4042
#include <QtWebEngineWidgets/QWebEngineProfile>
4143
#include <QtWebEngineWidgets/QWebEngineSettings>
4244

@@ -276,6 +278,11 @@ WebWidget* QtWebEngineWebBackend::createWidget(const QVariantMap &parameters, Co
276278
return new QtWebEngineWebWidget(parameters, this, parent);
277279
}
278280

281+
BookmarksImportJob *QtWebEngineWebBackend::createBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed)
282+
{
283+
return new QtWebEngineBookmarksImportJob(folder, path, areDuplicatesAllowed, this);
284+
}
285+
279286
QString QtWebEngineWebBackend::getName() const
280287
{
281288
return QLatin1String("qtwebengine");
@@ -344,6 +351,205 @@ WebBackend::BackendCapabilities QtWebEngineWebBackend::getCapabilities() const
344351
return (UserScriptsCapability | GlobalCookiesPolicyCapability | GlobalContentFilteringCapability | GlobalDoNotTrackCapability | GlobalProxyCapability | GlobalReferrerCapability | GlobalUserAgentCapability);
345352
}
346353

354+
QtWebEngineBookmarksImportJob::QtWebEngineBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed, QObject *parent) : BookmarksImportJob(folder, areDuplicatesAllowed, parent),
355+
m_path(path),
356+
m_currentAmount(0),
357+
m_totalAmount(-1),
358+
m_isRunning(false)
359+
{
360+
}
361+
362+
void QtWebEngineBookmarksImportJob::start()
363+
{
364+
QFile file(m_path);
365+
366+
if (!file.open(QIODevice::ReadOnly))
367+
{
368+
endImport();
369+
return;
370+
}
371+
372+
m_isRunning = true;
373+
374+
QWebEnginePage *page = new QWebEnginePage(this);
375+
QWebChannel *webChannel = new QWebChannel(this);
376+
377+
page->settings()->setAttribute(QWebEngineSettings::AutoLoadImages, false);
378+
page->setWebChannel(webChannel);
379+
380+
webChannel->registerObject("bookmarkImporter", this);
381+
382+
connect(page, &QWebEnginePage::loadFinished,
383+
[this, page]() {
384+
QFile qWebChannelFile(":/qtwebchannel/qwebchannel.js");
385+
386+
if (!qWebChannelFile.open(QIODevice::ReadOnly))
387+
{
388+
endImport();
389+
return;
390+
}
391+
392+
page->runJavaScript(QString::fromLatin1(qWebChannelFile.readAll()));
393+
394+
QFile importBookmarksScriptFile(QLatin1String(":/modules/backends/web/qtwebengine/resources/importBookmarks.js"));
395+
396+
if (!importBookmarksScriptFile.open(QIODevice::ReadOnly))
397+
{
398+
endImport();
399+
return;
400+
}
401+
402+
page->runJavaScript(QString::fromLatin1(importBookmarksScriptFile.readAll()));
403+
});
404+
405+
page->setHtml(QString::fromLatin1(file.readAll()));
406+
407+
file.close();
408+
}
409+
410+
void QtWebEngineBookmarksImportJob::cancel()
411+
{
412+
}
413+
414+
QDateTime QtWebEngineBookmarksImportJob::getDateTime(const QVariant &attribute)
415+
{
416+
#if QT_VERSION < 0x050800
417+
const uint seconds(attribute.toUInt());
418+
419+
return ((seconds > 0) ? QDateTime::fromTime_t(seconds) : QDateTime());
420+
#else
421+
const qint64 seconds(attribute.toLongLong());
422+
423+
return ((seconds != 0) ? QDateTime::fromSecsSinceEpoch(seconds) : QDateTime());
424+
#endif
425+
}
426+
427+
bool QtWebEngineBookmarksImportJob::isRunning() const
428+
{
429+
return m_isRunning;
430+
}
431+
432+
void QtWebEngineBookmarksImportJob::beginImport(const int totalAmount, const int estimatedUrlsCount, const int estimatedKeywordsCount)
433+
{
434+
m_totalAmount = totalAmount;
435+
436+
emit importStarted(Importer::BookmarksImport, m_totalAmount);
437+
438+
if (m_totalAmount == 0)
439+
{
440+
endImport();
441+
return;
442+
}
443+
444+
BookmarksManager::getModel()->beginImport(getImportFolder(), estimatedUrlsCount, estimatedKeywordsCount);
445+
}
446+
447+
void QtWebEngineBookmarksImportJob::endImport()
448+
{
449+
m_isRunning = false;
450+
451+
BookmarksManager::getModel()->endImport();
452+
453+
if (m_totalAmount < 0 || m_currentAmount < m_totalAmount)
454+
{
455+
emit importFinished(Importer::BookmarksImport, Importer::FailedImport, m_totalAmount);
456+
emit jobFinished(false);
457+
}
458+
else
459+
{
460+
emit importFinished(Importer::BookmarksImport, Importer::SuccessfullImport, m_totalAmount);
461+
emit jobFinished(true);
462+
}
463+
464+
deleteLater();
465+
}
466+
467+
void QtWebEngineBookmarksImportJob::beginFolder(QVariant itemVariant)
468+
{
469+
++m_currentAmount;
470+
emit importProgress(Importer::BookmarksImport, m_totalAmount, m_currentAmount);
471+
472+
const QVariantMap itemMap = itemVariant.toMap();
473+
const QString title = itemMap["title"].toString();
474+
475+
QMap<int, QVariant> metaData({{BookmarksModel::TitleRole, title}});
476+
477+
const QDateTime dateAdded(getDateTime(itemMap["dateAdded"]));
478+
const QDateTime dateModified(getDateTime(itemMap["dateModified"]));
479+
480+
if (dateAdded.isValid())
481+
{
482+
metaData[BookmarksModel::TimeAddedRole] = dateAdded;
483+
}
484+
485+
if (dateModified.isValid())
486+
{
487+
metaData[BookmarksModel::TimeAddedRole] = dateModified;
488+
}
489+
490+
setCurrentFolder(BookmarksManager::addBookmark(BookmarksModel::FolderBookmark, metaData, getCurrentFolder()));
491+
}
492+
493+
void QtWebEngineBookmarksImportJob::endFolder()
494+
{
495+
goToParent();
496+
}
497+
498+
void QtWebEngineBookmarksImportJob::addBookmark(QVariant itemVariant)
499+
{
500+
++m_currentAmount;
501+
emit importProgress(Importer::BookmarksImport, m_totalAmount, m_currentAmount);
502+
503+
const QVariantMap itemMap = itemVariant.toMap();
504+
const QString typeString = itemMap["type"].toString();
505+
const QString title = itemMap["title"].toString();
506+
507+
BookmarksModel::BookmarkType type(BookmarksModel::UnknownBookmark);
508+
QMap<int, QVariant> metaData({{BookmarksModel::TitleRole, title}});
509+
510+
if (typeString == "anchor" || typeString == "feed")
511+
{
512+
const QDateTime dateAdded(getDateTime(itemMap["dateAdded"]));
513+
const QDateTime dateModified(getDateTime(itemMap["dateModified"]));
514+
515+
if (dateAdded.isValid())
516+
{
517+
metaData[BookmarksModel::TimeAddedRole] = dateAdded;
518+
}
519+
520+
if (dateModified.isValid())
521+
{
522+
metaData[BookmarksModel::TimeAddedRole] = dateModified;
523+
}
524+
525+
type = (typeString == "anchor" ? BookmarksModel::UrlBookmark : BookmarksModel::FeedBookmark);
526+
527+
const QString url = itemMap["url"].toString();
528+
529+
if (!areDuplicatesAllowed() && BookmarksManager::hasBookmark(url))
530+
{
531+
return;
532+
}
533+
534+
const QVariant shortcutUrl = itemMap["keyword"];
535+
if (!shortcutUrl.isNull())
536+
{
537+
metaData[BookmarksModel::KeywordRole] = shortcutUrl.toString();
538+
}
539+
540+
metaData[BookmarksModel::UrlRole] = url;
541+
}
542+
else if (typeString == "separator")
543+
{
544+
type = BookmarksModel::SeparatorBookmark;
545+
}
546+
547+
if (type != BookmarksModel::UnknownBookmark)
548+
{
549+
BookmarksManager::addBookmark(type, metaData, getCurrentFolder());
550+
}
551+
}
552+
347553
bool QtWebEngineWebBackend::hasSslSupport() const
348554
{
349555
return true;

src/modules/backends/web/qtwebengine/QtWebEngineWebBackend.h

+29
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class QtWebEngineWebBackend final : public WebBackend
4545
explicit QtWebEngineWebBackend(QObject *parent = nullptr);
4646

4747
WebWidget* createWidget(const QVariantMap &parameters, ContentsWidget *parent = nullptr) override;
48+
BookmarksImportJob* createBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed) override;
4849
QString getName() const override;
4950
QString getTitle() const override;
5051
QString getDescription() const override;
@@ -81,6 +82,34 @@ protected slots:
8182
friend class QtWebEnginePage;
8283
};
8384

85+
class QtWebEngineBookmarksImportJob final : public BookmarksImportJob
86+
{
87+
Q_OBJECT
88+
89+
public:
90+
explicit QtWebEngineBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed, QObject *parent = nullptr);
91+
bool isRunning() const override;
92+
93+
Q_INVOKABLE void beginImport(const int totalAmount, const int estimatedUrlsCount, const int estimatedKeywordsCount);
94+
Q_INVOKABLE void endImport();
95+
Q_INVOKABLE void beginFolder(QVariant itemVariant);
96+
Q_INVOKABLE void endFolder();
97+
Q_INVOKABLE void addBookmark(QVariant itemVariant);
98+
99+
public slots:
100+
void start() override;
101+
void cancel() override;
102+
103+
protected:
104+
static QDateTime getDateTime(const QVariant &attribute);
105+
106+
private:
107+
QString m_path;
108+
int m_currentAmount;
109+
int m_totalAmount;
110+
bool m_isRunning;
111+
};
112+
84113
}
85114

86115
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
var importBookmarksObj;
2+
3+
function processBookmarkItems(items) {
4+
var currentIndex = 0;
5+
6+
while (currentIndex < items.length) {
7+
var node = items[currentIndex];
8+
currentIndex += 1;
9+
10+
if (node.tagName.toUpperCase() === 'A')
11+
{
12+
if (node.innerHTML === '---' && node.getAttribute('href') === 'http://bookmark.placeholder.url/')
13+
{
14+
// vivaldi markup for separators
15+
importBookmarksObj.addBookmark({'type':'separator'});
16+
}
17+
else
18+
{
19+
importBookmarksObj.addBookmark({
20+
'type': (node.hasAttribute('FEEDURL') ? 'feed' : 'anchor'),
21+
'title': node.innerHTML,
22+
'url': node.getAttribute('href'),
23+
'dateAdded': node.getAttribute('add_date'),
24+
'dateModified': node.getAttribute('last_modified'),
25+
'keyword': node.getAttribute('shortcuturl')
26+
});
27+
}
28+
}
29+
else if (node.tagName.toUpperCase() === 'HR')
30+
{
31+
importBookmarksObj.addBookmark({'type':'separator'});
32+
}
33+
else if (node.tagName.toUpperCase() === 'H3')
34+
{
35+
directChildren = node.parentNode.querySelectorAll(':scope > DL > DT > *, :scope > DL > HR');
36+
37+
importBookmarksObj.beginFolder({
38+
type: 'folder',
39+
title: node.innerHTML,
40+
dateAdded: node.getAttribute('add_date'),
41+
dateModified: node.getAttribute('last_modified')
42+
});
43+
processBookmarkItems(directChildren);
44+
importBookmarksObj.endFolder();
45+
}
46+
}
47+
}
48+
49+
new QWebChannel(qt.webChannelTransport, function(webChannel) {
50+
importBookmarksObj = webChannel.objects.bookmarkImporter;
51+
52+
var totalAmount = document.querySelectorAll('DT, HR').length;
53+
54+
var estimatedUrls = document.querySelectorAll('A[href]').length;
55+
var estimatedKeywords = document.querySelectorAll('A[shortcuturl]').length;
56+
57+
importBookmarksObj.beginImport(totalAmount, estimatedUrls, estimatedKeywords);
58+
59+
var rootNode = document.querySelector('dl');
60+
var directChildren = rootNode.querySelectorAll(':scope > DT > *, :scope > HR');
61+
62+
processBookmarkItems(directChildren);
63+
importBookmarksObj.endImport();
64+
});

0 commit comments

Comments
 (0)