From 42504596248a10eb31a5b719e0676b71f55871e4 Mon Sep 17 00:00:00 2001
From: Michal Klocek <michal.klocek@theqtcompany.com>
Date: Wed, 11 May 2016 13:43:45 +0200
Subject: [PATCH] Add back spellchecking support

This reverts commit:
 * d364c05de52f9ab39034e56cac4e0a7981dc541d
 * e507f140b70f464fb970d2f94357ac588dcc4f03

Moreover it fixes shut down of keyed services,
updates versioning of and fixes spellcheck unit
test context menu request, which otherwise
fails on windows.

Change-Id: I9bfc589544cb969abd6d2d7af69531b4c5c907b7
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
---
 src/core/browser_context_adapter.cpp          | 10 +++
 .../api/qquickwebenginecontextmenudata.cpp    | 34 ++++++++
 .../api/qquickwebenginecontextmenudata_p.h    |  7 ++
 src/webengine/api/qquickwebengineprofile.cpp  | 79 +++++++++++++++++++
 src/webengine/api/qquickwebengineprofile.h    |  9 +++
 src/webengine/api/qquickwebengineview.cpp     | 17 +++-
 src/webengine/api/qquickwebengineview_p.h     |  4 +-
 src/webengine/plugin/experimental/plugin.cpp  |  5 +-
 src/webengine/plugin/plugin.cpp               |  2 +
 .../api/qwebenginecontextmenudata.cpp         | 24 ++++++
 .../api/qwebenginecontextmenudata.h           |  2 +
 src/webenginewidgets/api/qwebenginepage.cpp   | 28 +++++++
 src/webenginewidgets/api/qwebenginepage.h     |  2 +
 .../api/qwebengineprofile.cpp                 | 61 ++++++++++++++
 src/webenginewidgets/api/qwebengineprofile.h  |  5 ++
 .../tst_qwebenginespellcheck.cpp              | 10 ++-
 tools/qmake/mkspecs/features/configure.prf    |  4 -
 17 files changed, 291 insertions(+), 12 deletions(-)

diff --git a/src/core/browser_context_adapter.cpp b/src/core/browser_context_adapter.cpp
index 6563c017f..57afb7c33 100644
--- a/src/core/browser_context_adapter.cpp
+++ b/src/core/browser_context_adapter.cpp
@@ -53,6 +53,10 @@
 
 #include "net/proxy/proxy_service.h"
 
+#if defined(ENABLE_SPELLCHECK)
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#endif
+
 #include <QCoreApplication>
 #include <QDir>
 #include <QString>
@@ -100,6 +104,12 @@ BrowserContextAdapter::~BrowserContextAdapter()
 {
     if (m_downloadManagerDelegate)
         content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE, m_downloadManagerDelegate.take());
+#if defined(ENABLE_SPELLCHECK)
+    // Please note spellchecker is the only keyed service we use so far, therefore
+    // remove this check if there are other serivces also used.
+    BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
+        m_browserContext.data());
+#endif
 }
 
 void BrowserContextAdapter::setStorageName(const QString &storageName)
diff --git a/src/webengine/api/qquickwebenginecontextmenudata.cpp b/src/webengine/api/qquickwebenginecontextmenudata.cpp
index 684903ec0..16cb4ff69 100644
--- a/src/webengine/api/qquickwebenginecontextmenudata.cpp
+++ b/src/webengine/api/qquickwebenginecontextmenudata.cpp
@@ -170,6 +170,34 @@ bool QQuickWebEngineContextMenuData::isContentEditable() const
     return d ? d->isEditable : false;
 }
 
+/*!
+    \qmlproperty QString WebEngineContextMenuData::misspelledWord
+
+    If the context is a word considered misspelled by the spell-checker, returns the misspelled word.
+
+    \since QtWebEngine 1.4
+*/
+QString QQuickWebEngineContextMenuData::misspelledWord() const
+{
+    if (d)
+        return d->misspelledWord;
+    return QString();
+}
+
+/*!
+    \qmlproperty QStringList WebEngineContextMenuData::spellCheckerSuggestions
+
+    If the context is a word considered misspelled by the spell-checker, returns a list of suggested replacements.
+
+    \since QtWebEngine 1.4
+*/
+QStringList QQuickWebEngineContextMenuData::spellCheckerSuggestions() const
+{
+    if (d)
+        return d->spellCheckerSuggestions;
+    return QStringList();
+}
+
 void QQuickWebEngineContextMenuData::update(const QtWebEngineCore::WebEngineContextMenuData &update)
 {
     const QQuickWebEngineContextMenuData old(d);
@@ -198,6 +226,12 @@ void QQuickWebEngineContextMenuData::update(const QtWebEngineCore::WebEngineCont
 
     if (isContentEditable() != old.isContentEditable())
         Q_EMIT isContentEditableChanged();
+
+    if (misspelledWord() != old.misspelledWord())
+        Q_EMIT misspelledWordChanged();
+
+    if (spellCheckerSuggestions() != old.spellCheckerSuggestions())
+        Q_EMIT spellCheckerSuggestionsChanged();
 }
 
 QQuickWebEngineContextMenuData::QQuickWebEngineContextMenuData(const QQuickWebEngineContextMenuDataPrivate *p, QObject *parent)
diff --git a/src/webengine/api/qquickwebenginecontextmenudata_p.h b/src/webengine/api/qquickwebenginecontextmenudata_p.h
index aa081cbe6..7175838db 100644
--- a/src/webengine/api/qquickwebenginecontextmenudata_p.h
+++ b/src/webengine/api/qquickwebenginecontextmenudata_p.h
@@ -92,6 +92,8 @@ public:
     Q_PROPERTY(QUrl mediaUrl READ mediaUrl NOTIFY mediaUrlChanged)
     Q_PROPERTY(MediaType mediaType READ mediaType NOTIFY mediaTypeChanged)
     Q_PROPERTY(bool isContentEditable READ isContentEditable NOTIFY isContentEditableChanged)
+    Q_PROPERTY(QString misspelledWord READ misspelledWord NOTIFY misspelledWordChanged FINAL REVISION 1)
+    Q_PROPERTY(QStringList spellCheckerSuggestions READ spellCheckerSuggestions NOTIFY spellCheckerSuggestionsChanged FINAL REVISION 1)
 
     bool isValid() const;
 
@@ -103,6 +105,9 @@ public:
     MediaType mediaType() const;
     bool isContentEditable() const;
 
+    QString misspelledWord() const;
+    QStringList spellCheckerSuggestions() const;
+
 Q_SIGNALS:
     void isValidChanged();
     void positionChanged();
@@ -112,6 +117,8 @@ Q_SIGNALS:
     void mediaUrlChanged();
     void mediaTypeChanged();
     void isContentEditableChanged();
+    Q_REVISION(1) void misspelledWordChanged();
+    Q_REVISION(1) void spellCheckerSuggestionsChanged();
 
 private:
     void update(const QtWebEngineCore::WebEngineContextMenuData &update);
diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp
index b39fb02fd..e4141c575 100644
--- a/src/webengine/api/qquickwebengineprofile.cpp
+++ b/src/webengine/api/qquickwebengineprofile.cpp
@@ -614,6 +614,85 @@ QQuickWebEngineProfile *QQuickWebEngineProfile::defaultProfile()
 }
 
 /*!
+    \property QQuickWebEngineProfile::spellCheckLanguage
+    \brief the language used by the spell checker.
+
+    \since QtWebEngine 1.4
+*/
+
+/*!
+    \qmlproperty QString WebEngineProfile::spellCheckLanguage
+
+    This property holds the language used by the spell checker.
+    The language should match the name of the \c .bdic dictionary.
+    For example, the \a language \c en-US will load the \c en-US.bdic
+    dictionary file.
+
+    The web engine checks for the \c qtwebengine_dictionaries subdirectory
+    first in the local directory and if it is not found in the Qt
+    installation directory:
+
+    \list
+        \li QCoreApplication::applicationDirPath()/qtwebengine_dictionaries
+        \li [QLibraryInfo::DataPath]/qtwebengine_dictionaries
+    \endlist
+
+    For more information about how to compile \c .bdic dictionaries, see the
+    \l{WebEngine Widgets Spellchecker Example}{Spellchecker Example}.
+
+    \since QtWebEngine 1.4
+*/
+void QQuickWebEngineProfile::setSpellCheckLanguage(const QString &language)
+{
+    Q_D(QQuickWebEngineProfile);
+    if (language != d->browserContext()->spellCheckLanguage()) {
+        d->browserContext()->setSpellCheckLanguage(language);
+        emit spellCheckLanguageChanged();
+    }
+}
+
+/*!
+    \since 5.8
+
+    Returns the language used by the spell checker.
+*/
+QString QQuickWebEngineProfile::spellCheckLanguage() const
+{
+    const Q_D(QQuickWebEngineProfile);
+    return d->browserContext()->spellCheckLanguage();
+}
+
+/*!
+    \property QQuickWebEngineProfile::spellCheckEnabled
+    \brief whether the web engine spell checker is enabled.
+
+    \since QtWebEngine 1.4
+*/
+
+/*!
+    \qmlproperty QString WebEngineProfile::spellCheckEnabled
+
+    This property holds whether the web engine spell checker is enabled.
+
+    \since QtWebEngine 1.4
+*/
+void QQuickWebEngineProfile::setSpellCheckEnabled(bool enable)
+{
+     Q_D(QQuickWebEngineProfile);
+    if (enable != isSpellCheckEnabled()) {
+        d->browserContext()->setSpellCheckEnabled(enable);
+        emit spellCheckEnabledChanged();
+    }
+}
+
+bool QQuickWebEngineProfile::isSpellCheckEnabled() const
+{
+     const Q_D(QQuickWebEngineProfile);
+     return d->browserContext()->isSpellCheckEnabled();
+}
+
+/*!
+
     Returns the cookie store for this profile.
 */
 QWebEngineCookieStore *QQuickWebEngineProfile::cookieStore() const
diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h
index dc5aa7df8..2b0dbf9d4 100644
--- a/src/webengine/api/qquickwebengineprofile.h
+++ b/src/webengine/api/qquickwebengineprofile.h
@@ -71,6 +71,8 @@ class Q_WEBENGINE_EXPORT QQuickWebEngineProfile : public QObject {
     Q_PROPERTY(QString httpAcceptLanguage READ httpAcceptLanguage WRITE setHttpAcceptLanguage NOTIFY httpAcceptLanguageChanged FINAL REVISION 1)
     Q_PROPERTY(PersistentCookiesPolicy persistentCookiesPolicy READ persistentCookiesPolicy WRITE setPersistentCookiesPolicy NOTIFY persistentCookiesPolicyChanged FINAL)
     Q_PROPERTY(int httpCacheMaximumSize READ httpCacheMaximumSize WRITE setHttpCacheMaximumSize NOTIFY httpCacheMaximumSizeChanged FINAL)
+    Q_PROPERTY(QString spellCheckLanguage READ spellCheckLanguage WRITE setSpellCheckLanguage NOTIFY spellCheckLanguageChanged FINAL REVISION 3)
+    Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY spellCheckEnabledChanged FINAL REVISION 3)
 
 public:
     QQuickWebEngineProfile(QObject *parent = Q_NULLPTR);
@@ -129,6 +131,11 @@ public:
 
     Q_REVISION(2) Q_INVOKABLE void clearHttpCache();
 
+    Q_REVISION(3) void setSpellCheckLanguage(const QString &language);
+    Q_REVISION(3) QString spellCheckLanguage() const;
+    Q_REVISION(3) void setSpellCheckEnabled(bool enabled);
+    Q_REVISION(3) bool isSpellCheckEnabled() const;
+
     static QQuickWebEngineProfile *defaultProfile();
 
 Q_SIGNALS:
@@ -141,6 +148,8 @@ Q_SIGNALS:
     void persistentCookiesPolicyChanged();
     void httpCacheMaximumSizeChanged();
     Q_REVISION(1) void httpAcceptLanguageChanged();
+    Q_REVISION(3) void spellCheckLanguageChanged();
+    Q_REVISION(3) void spellCheckEnabledChanged();
 
     void downloadRequested(QQuickWebEngineDownloadItem *download);
     void downloadFinished(QQuickWebEngineDownloadItem *download);
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index ac25cd4d2..b660b06be 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -212,7 +212,16 @@ bool QQuickWebEngineViewPrivate::contextMenuRequested(const WebEngineContextMenu
 
     // Populate our menu
     MenuItemHandler *item = 0;
-
+    if (contextMenuData.isContentEditable() && !contextMenuData.spellCheckerSuggestions().isEmpty()) {
+        const QPointer<QQuickWebEngineView> qRef(q);
+        for (int i=0; i < contextMenuData.spellCheckerSuggestions().count() && i < 4; i++) {
+            item = new MenuItemHandler(menu);
+            QString replacement = contextMenuData.spellCheckerSuggestions().at(i);
+            QObject::connect(item, &MenuItemHandler::triggered, [qRef, replacement] { qRef->replaceMisspelledWord(replacement); });
+            ui()->addMenuItem(item, replacement);
+        }
+        ui()->addMenuSeparator(menu);
+    }
     if (!data.linkText.isEmpty() && data.linkUrl.isValid()) {
         item = new MenuItemHandler(menu);
         QObject::connect(item, &MenuItemHandler::triggered, [q] { q->triggerWebAction(QQuickWebEngineView::OpenLinkInThisWindow); });
@@ -1258,6 +1267,12 @@ void QQuickWebEngineView::printToPdf(const QJSValue &callback, PrintedPageSizeId
     d->m_callbacks.insert(requestId, callback);
 }
 
+void QQuickWebEngineView::replaceMisspelledWord(const QString &replacement)
+{
+    Q_D(QQuickWebEngineView);
+    d->adapter->replaceMisspelling(replacement);
+}
+
 bool QQuickWebEngineView::isFullScreen() const
 {
     Q_D(const QQuickWebEngineView);
diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h
index b9555d2d9..367497da5 100644
--- a/src/webengine/api/qquickwebengineview_p.h
+++ b/src/webengine/api/qquickwebengineview_p.h
@@ -94,7 +94,7 @@ private:
     const bool m_toggleOn;
 };
 
-#define LATEST_WEBENGINEVIEW_REVISION 3
+#define LATEST_WEBENGINEVIEW_REVISION 4
 
 class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem {
     Q_OBJECT
@@ -472,6 +472,8 @@ public Q_SLOTS:
     Q_REVISION(2) void triggerWebAction(WebAction action);
     Q_REVISION(3) void printToPdf(const QString &filePath, PrintedPageSizeId pageSizeId = PrintedPageSizeId::A4, PrintedPageOrientation orientation = PrintedPageOrientation::Portrait);
     Q_REVISION(3) void printToPdf(const QJSValue &callback, PrintedPageSizeId pageSizeId = PrintedPageSizeId::A4, PrintedPageOrientation orientation = PrintedPageOrientation::Portrait);
+    Q_REVISION(4) void replaceMisspelledWord(const QString &replacement);
+
 private Q_SLOTS:
     void lazyInitialize();
 
diff --git a/src/webengine/plugin/experimental/plugin.cpp b/src/webengine/plugin/experimental/plugin.cpp
index c45bcee43..d4f68d142 100644
--- a/src/webengine/plugin/experimental/plugin.cpp
+++ b/src/webengine/plugin/experimental/plugin.cpp
@@ -72,9 +72,10 @@ public:
             tr("Cannot create a separate instance of WebEngineViewport"));
         qmlRegisterUncreatableType<const QQuickWebEngineContextMenuData>(uri, 1, 0, "WebEngineContextMenuData",
             tr("Cannot create a separate instance of WebEngineContextMenuData"));
-
+        qmlRegisterUncreatableType<const QQuickWebEngineContextMenuData, 1>(uri, 1, 1, "WebEngineContextMenuData",
+            tr("Cannot create a separate instance of WebEngineContextMenuData"));
         // Use the latest revision of QQuickWebEngineView when importing QtWebEngine.experimental 1.0
-        qmlRegisterRevision<QQuickWebEngineView, LATEST_WEBENGINEVIEW_REVISION>(uri, 1, 0);
+        qmlRegisterRevision<QQuickWebEngineView, LATEST_WEBENGINEVIEW_REVISION>(uri, 1, 1);
     }
 };
 
diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp
index 6fae500f3..2f7d2c2c4 100644
--- a/src/webengine/plugin/plugin.cpp
+++ b/src/webengine/plugin/plugin.cpp
@@ -81,9 +81,11 @@ public:
         qmlRegisterType<QQuickWebEngineView, 1>(uri, 1, 1, "WebEngineView");
         qmlRegisterType<QQuickWebEngineView, 2>(uri, 1, 2, "WebEngineView");
         qmlRegisterType<QQuickWebEngineView, 3>(uri, 1, 3, "WebEngineView");
+        qmlRegisterType<QQuickWebEngineView, 4>(uri, 1, 4, "WebEngineView");
         qmlRegisterType<QQuickWebEngineProfile>(uri, 1, 1, "WebEngineProfile");
         qmlRegisterType<QQuickWebEngineProfile, 1>(uri, 1, 2, "WebEngineProfile");
         qmlRegisterType<QQuickWebEngineProfile, 2>(uri, 1, 3, "WebEngineProfile");
+        qmlRegisterType<QQuickWebEngineProfile, 3>(uri, 1, 4, "WebEngineProfile");
         qmlRegisterType<QQuickWebEngineScript>(uri, 1, 1, "WebEngineScript");
         qmlRegisterUncreatableType<QQuickWebEngineCertificateError>(uri, 1, 1, "WebEngineCertificateError", tr("Cannot create separate instance of WebEngineCertificateError"));
         qmlRegisterUncreatableType<QQuickWebEngineDownloadItem>(uri, 1, 1, "WebEngineDownloadItem",
diff --git a/src/webenginewidgets/api/qwebenginecontextmenudata.cpp b/src/webenginewidgets/api/qwebenginecontextmenudata.cpp
index c7019977b..808c6f8b0 100644
--- a/src/webenginewidgets/api/qwebenginecontextmenudata.cpp
+++ b/src/webenginewidgets/api/qwebenginecontextmenudata.cpp
@@ -186,6 +186,30 @@ bool QWebEngineContextMenuData::isContentEditable() const
     return d ? d->isEditable : false;
 }
 
+/*!
+    If the context is a word considered misspelled by the spell-checker, returns the misspelled word.
+
+    \since 5.8
+*/
+QString QWebEngineContextMenuData::misspelledWord() const
+{
+    if (d)
+        return d->misspelledWord;
+    return QString();
+}
+
+/*!
+    If the context is a word considered misspelled by the spell-checker, returns a list of suggested replacements.
+
+    \since 5.8
+*/
+QStringList QWebEngineContextMenuData::spellCheckerSuggestions() const
+{
+    if (d)
+        return d->spellCheckerSuggestions;
+    return QStringList();
+}
+
 /*!
     \internal
 */
diff --git a/src/webenginewidgets/api/qwebenginecontextmenudata.h b/src/webenginewidgets/api/qwebenginecontextmenudata.h
index d04b747c8..1a2ff8de4 100644
--- a/src/webenginewidgets/api/qwebenginecontextmenudata.h
+++ b/src/webenginewidgets/api/qwebenginecontextmenudata.h
@@ -76,6 +76,8 @@ public:
     QUrl mediaUrl() const;
     MediaType mediaType() const;
     bool isContentEditable() const;
+    QString misspelledWord() const;
+    QStringList spellCheckerSuggestions() const;
 
 private:
     void reset();
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 63fb6eb44..c4dd4604e 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -1127,6 +1127,22 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
     }
 }
 
+/*!
+ * \since 5.8
+ * Replace the current misspelled word with \a replacement.
+ *
+ * The current misspelled word can be found in QWebEngineContextMenuData::misspelledWord(),
+ * and suggested replacements in QWebEngineContextMenuData::spellCheckerSuggestions().
+ *
+ * \sa contextMenuData(),
+ */
+
+void QWebEnginePage::replaceMisspelledWord(const QString &replacement)
+{
+    Q_D(QWebEnginePage);
+    d->adapter->replaceMisspelling(replacement);
+}
+
 void QWebEnginePage::findText(const QString &subString, FindFlags options, const QWebEngineCallback<bool> &resultCallback)
 {
     Q_D(QWebEnginePage);
@@ -1318,6 +1334,18 @@ QMenu *QWebEnginePage::createStandardContextMenu()
     QAction *action = 0;
     const WebEngineContextMenuData &contextMenuData = *d->contextData.d;
 
+    if (contextMenuData.isEditable && !contextMenuData.spellCheckerSuggestions.isEmpty()) {
+        QPointer<QWebEnginePage> thisRef(this);
+        for (int i=0; i < contextMenuData.spellCheckerSuggestions.count() && i < 4; i++) {
+            QAction *action = new QAction(menu);
+            QString replacement = contextMenuData.spellCheckerSuggestions.at(i);
+            QObject::connect(action, &QAction::triggered, [thisRef, replacement] { if (thisRef) thisRef->replaceMisspelledWord(replacement); });
+            action->setText(replacement);
+            menu->addAction(action);
+        }
+        menu->addSeparator();
+    }
+
     if (!contextMenuData.linkText.isEmpty() && contextMenuData.linkUrl.isValid()) {
         action = QWebEnginePage::action(OpenLinkInThisWindow);
         action->setText(tr("Follow Link"));
diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h
index 40f5b1a23..ad32f169d 100644
--- a/src/webenginewidgets/api/qwebenginepage.h
+++ b/src/webenginewidgets/api/qwebenginepage.h
@@ -207,6 +207,8 @@ public:
 #endif
     virtual void triggerAction(WebAction action, bool checked = false);
 
+    void replaceMisspelledWord(const QString &replacement);
+
     virtual bool event(QEvent*);
 #ifdef Q_QDOC
     void findText(const QString &subString, FindFlags options = FindFlags());
diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp
index 83b6f9714..b35fb0cc1 100644
--- a/src/webenginewidgets/api/qwebengineprofile.cpp
+++ b/src/webenginewidgets/api/qwebengineprofile.cpp
@@ -557,6 +557,67 @@ QWebEngineProfile *QWebEngineProfile::defaultProfile()
     return profile;
 }
 
+/*!
+    \since 5.8
+
+    Sets the current \a language for the spell checker.
+    The language should match the name of the \c .bdic dictionary.
+    For example, the \a language \c en-US will load the \c en-US.bdic
+    dictionary file.
+
+    The web engine checks for the \c qtwebengine_dictionaries subdirectory
+    first in the local directory and if it is not found in the Qt
+    installation directory:
+
+    \list
+        \li QCoreApplication::applicationDirPath()/qtwebengine_dictionaries
+        \li [QLibraryInfo::DataPath]/qtwebengine_dictionaries
+    \endlist
+
+    For more information about how to compile \c .bdic dictionaries, see the
+    \l{WebEngine Widgets Spellchecker Example}{Spellchecker Example}.
+
+*/
+void QWebEngineProfile::setSpellCheckLanguage(const QString &language)
+{
+    Q_D(QWebEngineProfile);
+    d->browserContext()->setSpellCheckLanguage(language);
+}
+
+/*!
+    \since 5.8
+
+    Returns the language used by the spell checker.
+*/
+QString QWebEngineProfile::spellCheckLanguage() const
+{
+    const Q_D(QWebEngineProfile);
+    return d->browserContext()->spellCheckLanguage();
+}
+
+/*!
+    \since 5.8
+
+    Enables spell checker if \a enable is \c true, otherwise disables it.
+    \sa isSpellCheckEnabled()
+ */
+void QWebEngineProfile::setSpellCheckEnabled(bool enable)
+{
+     Q_D(QWebEngineProfile);
+     d->browserContext()->setSpellCheckEnabled(enable);
+}
+/*!
+    \since 5.8
+
+    Returns \c true if the spell checker is enabled; otherwise returns \c false.
+    \sa setSpellCheckEnabled()
+ */
+bool QWebEngineProfile::isSpellCheckEnabled() const
+{
+     const Q_D(QWebEngineProfile);
+     return d->browserContext()->isSpellCheckEnabled();
+}
+
 /*!
     Returns the default settings for all pages in this profile.
 */
diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h
index d981fa5bb..704414bcf 100644
--- a/src/webenginewidgets/api/qwebengineprofile.h
+++ b/src/webenginewidgets/api/qwebengineprofile.h
@@ -121,6 +121,11 @@ public:
 
     void clearHttpCache();
 
+    void setSpellCheckLanguage(const QString &language);
+    QString spellCheckLanguage() const;
+    void setSpellCheckEnabled(bool enabled);
+    bool isSpellCheckEnabled() const;
+
     static QWebEngineProfile *defaultProfile();
 
 Q_SIGNALS:
diff --git a/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp
index 2dfe3305d..7714f4161 100644
--- a/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp
+++ b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp
@@ -37,12 +37,13 @@ class WebView : public QWebEngineView
 {
     Q_OBJECT
 public:
-    void activateMenu(const QPoint &position)
+    void activateMenu(QWidget *widget, const QPoint &position)
     {
-        QTest::mouseMove(focusWidget(), position);
-        QTest::mousePress(focusWidget(), Qt::RightButton, 0, position);
+        QTest::mouseMove(widget, position);
+        QTest::mousePress(widget, Qt::RightButton, 0, position);
         QContextMenuEvent evcont(QContextMenuEvent::Mouse, position, mapToGlobal(position));
         event(&evcont);
+        QTest::mouseRelease(widget, Qt::RightButton, 0, position);
     }
 
     const QWebEngineContextMenuData& data()
@@ -142,6 +143,7 @@ void tst_QWebEngineSpellcheck::spellcheck()
     //type text, spellchecker needs time
     QTest::mouseMove(m_view->focusWidget(), QPoint(20,20));
     QTest::mousePress(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20));
+    QTest::mouseRelease(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20));
     QString text("I lovee Qt ....");
     for (int i = 0; i < text.length(); i++) {
         QTest::keyClicks(m_view->focusWidget(), text.at(i));
@@ -153,7 +155,7 @@ void tst_QWebEngineSpellcheck::spellcheck()
     QVERIFY(result == text);
 
     // open menu on misspelled word
-    m_view->activateMenu(rect.center());
+    m_view->activateMenu(m_view->focusWidget(), rect.center());
     waitForSignal(m_view, SIGNAL(menuReady()));
 
     // check if menu is valid
diff --git a/tools/qmake/mkspecs/features/configure.prf b/tools/qmake/mkspecs/features/configure.prf
index 7ef4b8545..5b2a85330 100644
--- a/tools/qmake/mkspecs/features/configure.prf
+++ b/tools/qmake/mkspecs/features/configure.prf
@@ -64,9 +64,6 @@ defineTest(runConfigure) {
         }
     }
 
-    # Spellcheck support is moved to dev
-    WEBENGINE_CONFIG += no_spellcheck
-
     isEmpty(skipBuildReason): {
         cache(CONFIG, add, $$list(webengine_successfully_configured))
         !isEmpty(WEBENGINE_CONFIG) {
@@ -74,7 +71,6 @@ defineTest(runConfigure) {
             export(WEBENGINE_CONFIG)
         }
     }
-
 }
 
 # This is called from default_post, at which point we've also parsed
-- 
GitLab