Skip to content

Commit

Permalink
Implement QtWebEngine HTML bookmark import
Browse files Browse the repository at this point in the history
  • Loading branch information
fnkkio committed Dec 21, 2019
1 parent f71ad91 commit f4ff60c
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ add_executable(otter-browser WIN32 MACOSX_BUNDLE
)

if (Qt5WebEngineWidgets_FOUND AND ENABLE_QTWEBENGINE)
target_link_libraries(otter-browser Qt5::WebEngineCore Qt5::WebEngineWidgets)
target_link_libraries(otter-browser Qt5::WebEngineCore Qt5::WebEngineWidgets Qt5::WebChannel)
endif ()

if (Qt5WebKitWidgets_FOUND AND ENABLE_QTWEBKIT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
<file>resources/hideBlockedRequests.js</file>
<file>resources/hitTest.js</file>
<file>resources/imageViewer.js</file>
<file>resources/importBookmarks.js</file>
</qresource>
</RCC>
206 changes: 206 additions & 0 deletions src/modules/backends/web/qtwebengine/QtWebEngineWebBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "QtWebEngineTransfer.h"
#include "QtWebEngineUrlRequestInterceptor.h"
#include "QtWebEngineWebWidget.h"
#include "../../../../core/BookmarksManager.h"
#include "../../../../core/ContentFiltersManager.h"
#include "../../../../core/HandlersManager.h"
#include "../../../../core/NetworkManagerFactory.h"
Expand All @@ -37,6 +38,7 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QRegularExpression>
#include <QtWebChannel/QtWebChannel>
#include <QtWebEngineWidgets/QWebEngineProfile>
#include <QtWebEngineWidgets/QWebEngineSettings>

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

BookmarksImportJob *QtWebEngineWebBackend::createBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed)
{
return new QtWebEngineBookmarksImportJob(folder, path, areDuplicatesAllowed, this);
}

QString QtWebEngineWebBackend::getName() const
{
return QLatin1String("qtwebengine");
Expand Down Expand Up @@ -344,6 +351,205 @@ WebBackend::BackendCapabilities QtWebEngineWebBackend::getCapabilities() const
return (UserScriptsCapability | GlobalCookiesPolicyCapability | GlobalContentFilteringCapability | GlobalDoNotTrackCapability | GlobalProxyCapability | GlobalReferrerCapability | GlobalUserAgentCapability);
}

QtWebEngineBookmarksImportJob::QtWebEngineBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed, QObject *parent) : BookmarksImportJob(folder, areDuplicatesAllowed, parent),
m_path(path),
m_currentAmount(0),
m_totalAmount(-1),
m_isRunning(false)
{
}

void QtWebEngineBookmarksImportJob::start()
{
QFile file(m_path);

if (!file.open(QIODevice::ReadOnly))
{
endImport();
return;
}

m_isRunning = true;

QWebEnginePage *page = new QWebEnginePage(this);
QWebChannel *webChannel = new QWebChannel(this);

page->settings()->setAttribute(QWebEngineSettings::AutoLoadImages, false);
page->setWebChannel(webChannel);

webChannel->registerObject("bookmarkImporter", this);

connect(page, &QWebEnginePage::loadFinished,
[this, page]() {
QFile qWebChannelFile(":/qtwebchannel/qwebchannel.js");

if (!qWebChannelFile.open(QIODevice::ReadOnly))
{
endImport();
return;
}

page->runJavaScript(QString::fromLatin1(qWebChannelFile.readAll()));

QFile importBookmarksScriptFile(QLatin1String(":/modules/backends/web/qtwebengine/resources/importBookmarks.js"));

if (!importBookmarksScriptFile.open(QIODevice::ReadOnly))
{
endImport();
return;
}

page->runJavaScript(QString::fromLatin1(importBookmarksScriptFile.readAll()));
});

page->setHtml(QString::fromLatin1(file.readAll()));

file.close();
}

void QtWebEngineBookmarksImportJob::cancel()
{
}

QDateTime QtWebEngineBookmarksImportJob::getDateTime(const QVariant &attribute)
{
#if QT_VERSION < 0x050800
const uint seconds(attribute.toUInt());

return ((seconds > 0) ? QDateTime::fromTime_t(seconds) : QDateTime());
#else
const qint64 seconds(attribute.toLongLong());

return ((seconds != 0) ? QDateTime::fromSecsSinceEpoch(seconds) : QDateTime());
#endif
}

bool QtWebEngineBookmarksImportJob::isRunning() const
{
return m_isRunning;
}

void QtWebEngineBookmarksImportJob::beginImport(const int totalAmount, const int estimatedUrlsCount, const int estimatedKeywordsCount)
{
m_totalAmount = totalAmount;

emit importStarted(Importer::BookmarksImport, m_totalAmount);

if (m_totalAmount == 0)
{
endImport();
return;
}

BookmarksManager::getModel()->beginImport(getImportFolder(), estimatedUrlsCount, estimatedKeywordsCount);
}

void QtWebEngineBookmarksImportJob::endImport()
{
m_isRunning = false;

BookmarksManager::getModel()->endImport();

if (m_totalAmount < 0 || m_currentAmount < m_totalAmount)
{
emit importFinished(Importer::BookmarksImport, Importer::FailedImport, m_totalAmount);
emit jobFinished(false);
}
else
{
emit importFinished(Importer::BookmarksImport, Importer::SuccessfullImport, m_totalAmount);
emit jobFinished(true);
}

deleteLater();
}

void QtWebEngineBookmarksImportJob::beginFolder(QVariant itemVariant)
{
++m_currentAmount;
emit importProgress(Importer::BookmarksImport, m_totalAmount, m_currentAmount);

const QVariantMap itemMap = itemVariant.toMap();
const QString title = itemMap["title"].toString();

QMap<int, QVariant> metaData({{BookmarksModel::TitleRole, title}});

const QDateTime dateAdded(getDateTime(itemMap["dateAdded"]));
const QDateTime dateModified(getDateTime(itemMap["dateModified"]));

if (dateAdded.isValid())
{
metaData[BookmarksModel::TimeAddedRole] = dateAdded;
}

if (dateModified.isValid())
{
metaData[BookmarksModel::TimeAddedRole] = dateModified;
}

setCurrentFolder(BookmarksManager::addBookmark(BookmarksModel::FolderBookmark, metaData, getCurrentFolder()));
}

void QtWebEngineBookmarksImportJob::endFolder()
{
goToParent();
}

void QtWebEngineBookmarksImportJob::addBookmark(QVariant itemVariant)
{
++m_currentAmount;
emit importProgress(Importer::BookmarksImport, m_totalAmount, m_currentAmount);

const QVariantMap itemMap = itemVariant.toMap();
const QString typeString = itemMap["type"].toString();
const QString title = itemMap["title"].toString();

BookmarksModel::BookmarkType type(BookmarksModel::UnknownBookmark);
QMap<int, QVariant> metaData({{BookmarksModel::TitleRole, title}});

if (typeString == "anchor" || typeString == "feed")
{
const QDateTime dateAdded(getDateTime(itemMap["dateAdded"]));
const QDateTime dateModified(getDateTime(itemMap["dateModified"]));

if (dateAdded.isValid())
{
metaData[BookmarksModel::TimeAddedRole] = dateAdded;
}

if (dateModified.isValid())
{
metaData[BookmarksModel::TimeAddedRole] = dateModified;
}

type = (typeString == "anchor" ? BookmarksModel::UrlBookmark : BookmarksModel::FeedBookmark);

const QString url = itemMap["url"].toString();

if (!areDuplicatesAllowed() && BookmarksManager::hasBookmark(url))
{
return;
}

const QVariant shortcutUrl = itemMap["keyword"];
if (!shortcutUrl.isNull())
{
metaData[BookmarksModel::KeywordRole] = shortcutUrl.toString();
}

metaData[BookmarksModel::UrlRole] = url;
}
else if (typeString == "separator")
{
type = BookmarksModel::SeparatorBookmark;
}

if (type != BookmarksModel::UnknownBookmark)
{
BookmarksManager::addBookmark(type, metaData, getCurrentFolder());
}
}

bool QtWebEngineWebBackend::hasSslSupport() const
{
return true;
Expand Down
29 changes: 29 additions & 0 deletions src/modules/backends/web/qtwebengine/QtWebEngineWebBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class QtWebEngineWebBackend final : public WebBackend
explicit QtWebEngineWebBackend(QObject *parent = nullptr);

WebWidget* createWidget(const QVariantMap &parameters, ContentsWidget *parent = nullptr) override;
BookmarksImportJob* createBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed) override;
QString getName() const override;
QString getTitle() const override;
QString getDescription() const override;
Expand Down Expand Up @@ -81,6 +82,34 @@ protected slots:
friend class QtWebEnginePage;
};

class QtWebEngineBookmarksImportJob final : public BookmarksImportJob
{
Q_OBJECT

public:
explicit QtWebEngineBookmarksImportJob(BookmarksModel::Bookmark *folder, const QString &path, bool areDuplicatesAllowed, QObject *parent = nullptr);
bool isRunning() const override;

Q_INVOKABLE void beginImport(const int totalAmount, const int estimatedUrlsCount, const int estimatedKeywordsCount);
Q_INVOKABLE void endImport();
Q_INVOKABLE void beginFolder(QVariant itemVariant);
Q_INVOKABLE void endFolder();
Q_INVOKABLE void addBookmark(QVariant itemVariant);

public slots:
void start() override;
void cancel() override;

protected:
static QDateTime getDateTime(const QVariant &attribute);

private:
QString m_path;
int m_currentAmount;
int m_totalAmount;
bool m_isRunning;
};

}

#endif
64 changes: 64 additions & 0 deletions src/modules/backends/web/qtwebengine/resources/importBookmarks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
var importBookmarksObj;

function processBookmarkItems(items) {
var currentIndex = 0;

while (currentIndex < items.length) {
var node = items[currentIndex];
currentIndex += 1;

if (node.tagName.toUpperCase() === 'A')
{
if (node.innerHTML === '---' && node.getAttribute('href') === 'http://bookmark.placeholder.url/')
{
// vivaldi markup for separators
importBookmarksObj.addBookmark({'type':'separator'});
}
else
{
importBookmarksObj.addBookmark({
'type': (node.hasAttribute('FEEDURL') ? 'feed' : 'anchor'),
'title': node.innerHTML,
'url': node.getAttribute('href'),
'dateAdded': node.getAttribute('add_date'),
'dateModified': node.getAttribute('last_modified'),
'keyword': node.getAttribute('shortcuturl')
});
}
}
else if (node.tagName.toUpperCase() === 'HR')
{
importBookmarksObj.addBookmark({'type':'separator'});
}
else if (node.tagName.toUpperCase() === 'H3')
{
directChildren = node.parentNode.querySelectorAll(':scope > DL > DT > *, :scope > DL > HR');

importBookmarksObj.beginFolder({
type: 'folder',
title: node.innerHTML,
dateAdded: node.getAttribute('add_date'),
dateModified: node.getAttribute('last_modified')
});
processBookmarkItems(directChildren);
importBookmarksObj.endFolder();
}
}
}

new QWebChannel(qt.webChannelTransport, function(webChannel) {
importBookmarksObj = webChannel.objects.bookmarkImporter;

var totalAmount = document.querySelectorAll('DT, HR').length;

var estimatedUrls = document.querySelectorAll('A[href]').length;
var estimatedKeywords = document.querySelectorAll('A[shortcuturl]').length;

importBookmarksObj.beginImport(totalAmount, estimatedUrls, estimatedKeywords);

var rootNode = document.querySelector('dl');
var directChildren = rootNode.querySelectorAll(':scope > DT > *, :scope > HR');

processBookmarkItems(directChildren);
importBookmarksObj.endImport();
});

0 comments on commit f4ff60c

Please sign in to comment.