summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------src/3rdparty0
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/api/qwebenginepage.cpp14
-rw-r--r--src/core/api/qwebenginepage_p.h7
-rw-r--r--src/core/autofill_client_qt.cpp162
-rw-r--r--src/core/autofill_client_qt.h108
-rw-r--r--src/core/autofill_popup_controller.cpp150
-rw-r--r--src/core/autofill_popup_controller.h98
-rw-r--r--src/core/autofill_popup_controller_p.h76
-rw-r--r--src/core/configure/BUILD.root.gn.in3
-rw-r--r--src/core/content_browser_client_qt.cpp11
-rw-r--r--src/core/pref_service_adapter.cpp9
-rw-r--r--src/core/renderer/content_renderer_client_qt.cpp19
-rw-r--r--src/core/web_contents_adapter.cpp8
-rw-r--r--src/core/web_contents_adapter_client.h4
-rw-r--r--src/webenginequick/api/qquickwebengineview.cpp14
-rw-r--r--src/webenginequick/api/qquickwebengineview_p_p.h4
-rw-r--r--src/webenginequick/ui/AutofillPopup.qml77
-rw-r--r--src/webenginequick/ui/CMakeLists.txt1
-rw-r--r--src/webenginequick/ui_delegates_manager.cpp158
-rw-r--r--src/webenginequick/ui_delegates_manager.h53
-rw-r--r--src/webenginewidgets/CMakeLists.txt1
-rw-r--r--src/webenginewidgets/api/qwebengineview.cpp31
-rw-r--r--src/webenginewidgets/api/qwebengineview.h5
-rw-r--r--src/webenginewidgets/api/qwebengineview_p.h9
-rw-r--r--src/webenginewidgets/ui/autofillpopupwidget.cpp237
-rw-r--r--src/webenginewidgets/ui/autofillpopupwidget_p.h94
-rw-r--r--tests/auto/quick/qmltests/CMakeLists.txt1
-rw-r--r--tests/auto/quick/qmltests/data/TestWebEngineView.qml15
-rw-r--r--tests/auto/quick/qmltests/data/tst_datalist.qml205
-rw-r--r--tests/auto/quick/qmltests/data/tst_keyboardEvents.qml14
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp152
32 files changed, 1712 insertions, 30 deletions
diff --git a/src/3rdparty b/src/3rdparty
-Subproject 88398c89a7b34606120ff919f873cb59ce3bcf2
+Subproject 185dfeb21f478a8503f424570eff01177a46721
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 18078ba5a..904baf7d2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -76,6 +76,8 @@ foreach(arch ${archs})
accessibility_activation_observer.cpp accessibility_activation_observer.h
accessibility_tree_formatter_qt.cpp
authentication_dialog_controller.cpp authentication_dialog_controller.h authentication_dialog_controller_p.h
+ autofill_client_qt.cpp autofill_client_qt.h
+ autofill_popup_controller.cpp autofill_popup_controller.h autofill_popup_controller_p.h
browser_accessibility_manager_qt.cpp browser_accessibility_manager_qt.h
browser_accessibility_qt.cpp browser_accessibility_qt.h
browser_main_parts_qt.cpp browser_main_parts_qt.h
diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp
index 8a49e6577..332a612fc 100644
--- a/src/core/api/qwebenginepage.cpp
+++ b/src/core/api/qwebenginepage.cpp
@@ -59,6 +59,7 @@
#include "qwebenginesettings.h"
#include "authentication_dialog_controller.h"
+#include "autofill_popup_controller.h"
#include "color_chooser_controller.h"
#include "find_text_helper.h"
#include "file_picker_controller.h"
@@ -692,6 +693,19 @@ void QWebEnginePagePrivate::findTextFinished(const QWebEngineFindTextResult &res
Q_EMIT q->findTextFinished(result);
}
+void QWebEnginePagePrivate::showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ const QRect &bounds, bool autoselectFirstSuggestion)
+{
+ if (view)
+ view->showAutofillPopup(controller, bounds, autoselectFirstSuggestion);
+}
+
+void QWebEnginePagePrivate::hideAutofillPopup()
+{
+ if (view)
+ view->hideAutofillPopup();
+}
+
void QWebEnginePagePrivate::ensureInitialized() const
{
if (!adapter->isInitialized())
diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h
index 2b76795b5..68203ba76 100644
--- a/src/core/api/qwebenginepage_p.h
+++ b/src/core/api/qwebenginepage_p.h
@@ -61,6 +61,7 @@
#include <QtCore/QTimer>
namespace QtWebEngineCore {
+class AutofillPopupController;
class RenderWidgetHostViewQtDelegate;
class RenderWidgetHostViewQtDelegateClient;
class RenderWidgetHostViewQtDelegateItem;
@@ -109,6 +110,9 @@ public:
virtual void didPrintPage(QPrinter *&printer, QSharedPointer<QByteArray> result) = 0;
virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0;
virtual void printRequested() = 0;
+ virtual void showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ const QRect &bounds, bool autoselectFirstSuggestion) = 0;
+ virtual void hideAutofillPopup() = 0;
};
class Q_WEBENGINECORE_PRIVATE_EXPORT QWebEnginePagePrivate : public QtWebEngineCore::WebContentsAdapterClient
@@ -195,6 +199,9 @@ public:
const QObject *holdingQObject() const override;
ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; }
void findTextFinished(const QWebEngineFindTextResult &result) override;
+ void showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ const QRect &bounds, bool autoselectFirstSuggestion) override;
+ void hideAutofillPopup() override;
QtWebEngineCore::ProfileAdapter *profileAdapter() override;
QtWebEngineCore::WebContentsAdapter *webContentsAdapter() override;
diff --git a/src/core/autofill_client_qt.cpp b/src/core/autofill_client_qt.cpp
new file mode 100644
index 000000000..b599560d1
--- /dev/null
+++ b/src/core/autofill_client_qt.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "autofill_client_qt.h"
+
+#include "autofill_popup_controller.h"
+#include "autofill_popup_controller_p.h"
+#include "render_widget_host_view_qt.h"
+#include "type_conversion.h"
+#include "web_contents_adapter_client.h"
+#include "web_contents_view_qt.h"
+
+#include "base/task/thread_pool.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/autofill/core/common/autofill_prefs.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+
+namespace QtWebEngineCore {
+
+AutofillClientQt::AutofillClientQt(content::WebContents *webContents)
+ : content::WebContentsObserver(webContents)
+ , m_popupController(new AutofillPopupController(new AutofillPopupControllerPrivate))
+{
+}
+
+AutofillClientQt::~AutofillClientQt() { }
+
+autofill::PersonalDataManager *AutofillClientQt::GetPersonalDataManager()
+{
+ return nullptr;
+}
+
+autofill::AutocompleteHistoryManager *AutofillClientQt::GetAutocompleteHistoryManager()
+{
+ return nullptr;
+}
+
+PrefService *AutofillClientQt::GetPrefs()
+{
+ return const_cast<PrefService *>(base::as_const(*this).GetPrefs());
+}
+
+const PrefService *AutofillClientQt::GetPrefs() const
+{
+ Profile *profile = Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+ return profile->GetPrefs();
+}
+
+void AutofillClientQt::ShowAutofillPopup(const autofill::AutofillClient::PopupOpenArgs &open_args,
+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate)
+{
+ // Specific popups (personal, address, credit card, password) are not supported.
+ DCHECK(open_args.popup_type == autofill::PopupType::kUnspecified);
+
+ m_popupController->d->delegate = delegate;
+ m_popupController->d->suggestions = open_args.suggestions;
+ m_popupController->updateModel();
+
+ adapterClient()->showAutofillPopup(m_popupController.data(),
+ QRect(toQt(gfx::ToEnclosingRect(open_args.element_bounds))),
+ open_args.autoselect_first_suggestion.value());
+}
+
+void AutofillClientQt::UpdateAutofillPopupDataListValues(const std::vector<std::u16string> &values,
+ const std::vector<std::u16string> &labels)
+{
+ Q_UNUSED(labels);
+
+ if (values.empty())
+ HideAutofillPopup(autofill::PopupHidingReason::kNoSuggestions);
+}
+
+void AutofillClientQt::PinPopupView()
+{
+ // Called by password_manager component only.
+ NOTIMPLEMENTED();
+}
+
+autofill::AutofillClient::PopupOpenArgs AutofillClientQt::GetReopenPopupArgs() const
+{
+ // Called by password_manager component only.
+ NOTIMPLEMENTED();
+ return autofill::AutofillClient::PopupOpenArgs();
+}
+
+base::span<const autofill::Suggestion> AutofillClientQt::GetPopupSuggestions() const
+{
+ // Called by password_manager component only.
+ NOTIMPLEMENTED();
+ return base::span<const autofill::Suggestion>();
+}
+
+void AutofillClientQt::UpdatePopup(const std::vector<autofill::Suggestion> &, autofill::PopupType)
+{
+ // Called by password_manager component only.
+ NOTIMPLEMENTED();
+}
+
+void AutofillClientQt::HideAutofillPopup(autofill::PopupHidingReason)
+{
+ adapterClient()->hideAutofillPopup();
+}
+
+bool AutofillClientQt::IsAutocompleteEnabled()
+{
+ return autofill::prefs::IsAutocompleteEnabled(GetPrefs());
+}
+
+void AutofillClientQt::PropagateAutofillPredictions(content::RenderFrameHost *,
+ const std::vector<autofill::FormStructure *> &)
+{
+ // For testing purposes only.
+ NOTIMPLEMENTED();
+}
+
+WebContentsAdapterClient *AutofillClientQt::adapterClient()
+{
+ return WebContentsViewQt::from(
+ static_cast<content::WebContentsImpl *>(web_contents())->GetView())
+ ->client();
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AutofillClientQt);
+
+} // namespace QtWebEngineCore
diff --git a/src/core/autofill_client_qt.h b/src/core/autofill_client_qt.h
new file mode 100644
index 000000000..762ff1109
--- /dev/null
+++ b/src/core/autofill_client_qt.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef AUTOFILL_CLIENT_QT_H
+#define AUTOFILL_CLIENT_QT_H
+
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+#include <QScopedPointer>
+
+namespace QtWebEngineCore {
+
+class AutofillPopupController;
+class WebContentsAdapterClient;
+
+class AutofillClientQt : public autofill::AutofillClient,
+ public content::WebContentsUserData<AutofillClientQt>,
+ public content::WebContentsObserver
+{
+public:
+ ~AutofillClientQt() override;
+
+ // autofill::AutofillClient overrides:
+ autofill::PersonalDataManager *GetPersonalDataManager() override;
+ autofill::AutocompleteHistoryManager *GetAutocompleteHistoryManager() override;
+ PrefService *GetPrefs() override;
+ const PrefService *GetPrefs() const override;
+
+ void ShowAutofillPopup(const autofill::AutofillClient::PopupOpenArgs &open_args,
+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate) override;
+ void UpdateAutofillPopupDataListValues(const std::vector<std::u16string> &values,
+ const std::vector<std::u16string> &labels) override;
+ void PinPopupView() override;
+ autofill::AutofillClient::PopupOpenArgs GetReopenPopupArgs() const override;
+ base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
+ void UpdatePopup(const std::vector<autofill::Suggestion> &, autofill::PopupType) override;
+ void HideAutofillPopup(autofill::PopupHidingReason reason) override;
+ bool IsAutocompleteEnabled() override;
+ void PropagateAutofillPredictions(content::RenderFrameHost *,
+ const std::vector<autofill::FormStructure *> &) override;
+
+private:
+ explicit AutofillClientQt(content::WebContents *webContents);
+
+ WebContentsAdapterClient *adapterClient();
+
+ QScopedPointer<AutofillPopupController> m_popupController;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+ friend class content::WebContentsUserData<AutofillClientQt>;
+};
+
+} // namespace QtWebEngineCore
+
+#endif // AUTOFILL_CLIENT_QT_H
diff --git a/src/core/autofill_popup_controller.cpp b/src/core/autofill_popup_controller.cpp
new file mode 100644
index 000000000..e42f5a4f6
--- /dev/null
+++ b/src/core/autofill_popup_controller.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "autofill_popup_controller.h"
+#include "autofill_popup_controller_p.h"
+
+#include "components/autofill/core/browser/ui/autofill_popup_delegate.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
+
+namespace QtWebEngineCore {
+
+AutofillPopupController::AutofillPopupController(AutofillPopupControllerPrivate *dd)
+{
+ Q_ASSERT(dd);
+ d.reset(dd);
+}
+
+AutofillPopupController::~AutofillPopupController() { }
+
+void AutofillPopupController::setCurrentIndex(const QModelIndex &index)
+{
+ if (m_currentIndex == index)
+ return;
+
+ m_currentIndex = index;
+
+ if (m_currentIndex.isValid()) {
+ const autofill::Suggestion &suggestion = d->suggestions[m_currentIndex.row()];
+ d->delegate->DidSelectSuggestion(suggestion.value, suggestion.frontend_id);
+ }
+
+ Q_EMIT currentIndexChanged(index);
+}
+
+void AutofillPopupController::selectPreviousSuggestion()
+{
+ if (!m_currentIndex.isValid()) {
+ setCurrentIndex(m_model.index(m_model.rowCount() - 1, 0));
+ return;
+ }
+
+ if (m_currentIndex.row() == 0) {
+ selectLastSuggestion();
+ return;
+ }
+
+ setCurrentIndex(m_model.index(m_currentIndex.row() - 1, 0));
+}
+
+void AutofillPopupController::selectNextSuggestion()
+{
+ if (!m_currentIndex.isValid()) {
+ setCurrentIndex(m_model.index(0, 0));
+ return;
+ }
+
+ if (m_currentIndex.row() == m_model.rowCount() - 1) {
+ selectFirstSuggestion();
+ return;
+ }
+
+ setCurrentIndex(m_model.index(m_currentIndex.row() + 1, 0));
+}
+
+void AutofillPopupController::selectFirstSuggestion()
+{
+ setCurrentIndex(m_model.index(0, 0));
+}
+
+void AutofillPopupController::selectLastSuggestion()
+{
+ setCurrentIndex(m_model.index(m_model.rowCount() - 1, 0));
+}
+
+void AutofillPopupController::acceptSuggestion()
+{
+ if (!m_currentIndex.isValid())
+ return;
+
+ const int index = m_currentIndex.row();
+ const autofill::Suggestion &suggestion = d->suggestions[index];
+ d->delegate->DidAcceptSuggestion(suggestion.value, suggestion.frontend_id,
+ suggestion.backend_id, index);
+}
+
+void AutofillPopupController::notifyPopupShown()
+{
+ d->delegate->OnPopupShown();
+}
+
+void AutofillPopupController::notifyPopupHidden()
+{
+ d->delegate->OnPopupHidden();
+}
+
+void AutofillPopupController::selectSuggestion(int index)
+{
+ if (index < 0)
+ setCurrentIndex(QModelIndex());
+ else
+ setCurrentIndex(m_model.index(index, 0));
+}
+
+void AutofillPopupController::updateModel()
+{
+ QStringList values;
+ for (size_t i = 0; i < d->suggestions.size(); ++i) {
+ values.append(QString::fromStdU16String(d->suggestions[i].value));
+ }
+ m_model.setStringList(values);
+ setCurrentIndex(QModelIndex());
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/autofill_popup_controller.h b/src/core/autofill_popup_controller.h
new file mode 100644
index 000000000..5432123f1
--- /dev/null
+++ b/src/core/autofill_popup_controller.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AUTOFILL_POPUP_CONTROLLER_H
+#define AUTOFILL_POPUP_CONTROLLER_H
+
+#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
+
+#include <QModelIndex>
+#include <QObject>
+#include <QScopedPointer>
+#include <QStringListModel>
+
+namespace QtWebEngineCore {
+
+class AutofillPopupControllerPrivate;
+
+class Q_WEBENGINECORE_PRIVATE_EXPORT AutofillPopupController : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QStringListModel *model READ model CONSTANT FINAL)
+
+public:
+ ~AutofillPopupController();
+
+ QStringListModel *model() { return &m_model; }
+
+ void selectPreviousSuggestion();
+ void selectNextSuggestion();
+ void selectFirstSuggestion();
+ void selectLastSuggestion();
+
+ void notifyPopupShown();
+ void notifyPopupHidden();
+
+public Q_SLOTS:
+ void selectSuggestion(int index);
+ void acceptSuggestion();
+
+Q_SIGNALS:
+ void currentIndexChanged(const QModelIndex &index);
+
+private:
+ AutofillPopupController(AutofillPopupControllerPrivate *);
+ QScopedPointer<AutofillPopupControllerPrivate> d;
+
+ void setCurrentIndex(const QModelIndex &index);
+
+ // Only called by AutofillClientQt:
+ void updateModel();
+
+ QStringListModel m_model;
+ QModelIndex m_currentIndex;
+
+ friend class AutofillClientQt;
+};
+
+} // namespace QtWebEngineCore
+
+Q_DECLARE_METATYPE(QtWebEngineCore::AutofillPopupController *)
+
+#endif // AUTOFILL_POPUP_CONTROLLER_H
diff --git a/src/core/autofill_popup_controller_p.h b/src/core/autofill_popup_controller_p.h
new file mode 100644
index 000000000..880eec86e
--- /dev/null
+++ b/src/core/autofill_popup_controller_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AUTOFILL_POPUP_CONTROLLER_P_H
+#define AUTOFILL_POPUP_CONTROLLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
+
+namespace autofill {
+class AutofillPopupDelegate;
+}
+
+namespace QtWebEngineCore {
+
+class AutofillPopupControllerPrivate
+{
+public:
+ AutofillPopupControllerPrivate() = default;
+
+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate = nullptr;
+ std::vector<autofill::Suggestion> suggestions;
+};
+
+} // namespace QtWebEngineCore
+
+#endif // AUTOFILL_POPUP_CONTROLLER_P_H
diff --git a/src/core/configure/BUILD.root.gn.in b/src/core/configure/BUILD.root.gn.in
index 35fe32476..f1fa1eaa3 100644
--- a/src/core/configure/BUILD.root.gn.in
+++ b/src/core/configure/BUILD.root.gn.in
@@ -104,6 +104,9 @@ shared_library("QtWebEngineCore") {
defines = [ "CHROMIUM_VERSION=\"" + chromium_version[0] + "\"" ]
deps = [
"//base",
+ "//components/autofill/content/browser",
+ "//components/autofill/content/renderer",
+ "//components/autofill/core/browser",
"//components/cdm/renderer",
"//components/error_page/common",
"//components/favicon/content",
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 1bbac4439..0fdf1f397 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -42,6 +42,7 @@
#include "base/files/file_util.h"
#include "base/task/post_task.h"
#include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/error_page/common/error.h"
#include "components/error_page/common/localized_error.h"
@@ -502,6 +503,8 @@ void ContentBrowserClientQt::ExposeInterfacesToRenderer(service_manager::BinderR
#endif
}
+// TODO: BindAssociatedReceiverForFrame -> RegisterAssociatedInterfaceBindersForRenderFrameHost
+// https://chromium-review.googlesource.com/c/chromium/src/+/3281481
bool ContentBrowserClientQt::BindAssociatedReceiverFromFrame(content::RenderFrameHost *rfh,
const std::string &interface_name,
mojo::ScopedInterfaceEndpointHandle *handle)
@@ -529,6 +532,14 @@ bool ContentBrowserClientQt::BindAssociatedReceiverFromFrame(content::RenderFram
return true;
}
#endif
+
+ if (interface_name == autofill::mojom::AutofillDriver::Name_) {
+ mojo::PendingAssociatedReceiver<autofill::mojom::AutofillDriver> receiver(
+ std::move(*handle));
+ autofill::ContentAutofillDriverFactory::BindAutofillDriver(std::move(receiver), rfh);
+ return true;
+ }
+
DCHECK(!ContentBrowserClient::BindAssociatedReceiverFromFrame(rfh, interface_name, handle));
return false;
}
diff --git a/src/core/pref_service_adapter.cpp b/src/core/pref_service_adapter.cpp
index d7795c7e9..f3a99c3e2 100644
--- a/src/core/pref_service_adapter.cpp
+++ b/src/core/pref_service_adapter.cpp
@@ -46,6 +46,7 @@
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/prefs/chrome_command_line_pref_store.h"
#include "content/public/browser/browser_thread.h"
+#include "components/autofill/core/common/autofill_prefs.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/in_memory_pref_store.h"
@@ -131,6 +132,14 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter)
// default value will be different. We'll need to initialize it later.
registry->RegisterStringPref(kPrefMediaDeviceIDSalt, std::string());
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillEnabledDeprecated, false);
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillProfileEnabled, false);
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillCreditCardEnabled, false);
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillCreditCardFidoAuthEnabled, false);
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillWalletImportEnabled, false);
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillJapanCityFieldMigratedDeprecated,
+ false);
+
{
base::ScopedAllowBlocking allowBlock;
m_prefService = factory.Create(registry);
diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp
index b410ad377..ffb5178e7 100644
--- a/src/core/renderer/content_renderer_client_qt.cpp
+++ b/src/core/renderer/content_renderer_client_qt.cpp
@@ -47,6 +47,10 @@
#include "components/spellcheck/renderer/spellcheck.h"
#include "components/spellcheck/renderer/spellcheck_provider.h"
#endif
+#include "components/autofill/content/renderer/autofill_agent.h"
+#include "components/autofill/content/renderer/autofill_assistant_agent.h"
+#include "components/autofill/content/renderer/password_autofill_agent.h"
+#include "components/autofill/content/renderer/password_generation_agent.h"
#include "components/cdm/renderer/external_clear_key_key_system_properties.h"
#include "components/cdm/renderer/widevine_key_system_properties.h"
#include "components/error_page/common/error.h"
@@ -210,8 +214,10 @@ void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame *render_fr
#if QT_CONFIG(webengine_printing_and_pdf)
new printing::PrintRenderFrameHelper(render_frame, base::WrapUnique(new PrintWebViewHelperDelegateQt()));
#endif // QT_CONFIG(webengine_printing_and_pdf)
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+
blink::AssociatedInterfaceRegistry *associated_interfaces = render_frame_observer->associatedInterfaces();
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
associated_interfaces->AddInterface(base::BindRepeating(
&extensions::MimeHandlerViewContainerManager::BindReceiver,
render_frame->GetRoutingID()));
@@ -219,6 +225,17 @@ void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame *render_fr
auto registry = std::make_unique<service_manager::BinderRegistry>();
ExtensionsRendererClientQt::GetInstance()->RenderFrameCreated(render_frame, render_frame_observer->registry());
#endif
+
+ autofill::AutofillAssistantAgent *autofill_assistant_agent =
+ new autofill::AutofillAssistantAgent(render_frame);
+ autofill::PasswordAutofillAgent *password_autofill_agent =
+ new autofill::PasswordAutofillAgent(render_frame, associated_interfaces);
+ autofill::PasswordGenerationAgent *password_generation_agent =
+ new autofill::PasswordGenerationAgent(render_frame, password_autofill_agent,
+ associated_interfaces);
+
+ new autofill::AutofillAgent(render_frame, password_autofill_agent, password_generation_agent,
+ autofill_assistant_agent, associated_interfaces);
}
void ContentRendererClientQt::RunScriptsAtDocumentStart(content::RenderFrame *render_frame)
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 45dfd1017..525ee728d 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -43,6 +43,7 @@
#include "web_contents_adapter.h"
+#include "autofill_client_qt.h"
#include "devtools_frontend_qt.h"
#include "download_manager_delegate_qt.h"
#include "favicon_driver_qt.h"
@@ -66,6 +67,8 @@
#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
#include "base/values.h"
#include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/favicon/core/favicon_service.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/text_input_manager.h"
@@ -517,6 +520,11 @@ void WebContentsAdapter::initialize(content::SiteInstance *site)
FaviconDriverQt::CreateForWebContents(
webContents(), FaviconServiceFactoryQt::GetForBrowserContext(context), m_adapterClient);
+ AutofillClientQt::CreateForWebContents(webContents());
+ autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
+ webContents(), AutofillClientQt::FromWebContents(webContents()),
+ /* app_locale = */ "", autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+
// Create an instance of WebEngineVisitedLinksManager to catch the first
// content::NOTIFICATION_RENDERER_PROCESS_CREATED event. This event will
// force to initialize visited links in VisitedLinkSlave.
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 2729e7beb..0ad91e92e 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -80,6 +80,7 @@ struct DropData;
namespace QtWebEngineCore {
+class AutofillPopupController;
class CertificateErrorController;
class ClientCertSelectController;
class AuthenticationDialogController;
@@ -245,6 +246,9 @@ public:
virtual void showTouchSelectionMenu(TouchSelectionMenuController *menuController, const QRect &bounds, const QSize &handleSize) = 0;
virtual void hideTouchSelectionMenu() = 0;
virtual void findTextFinished(const QWebEngineFindTextResult &result) = 0;
+ virtual void showAutofillPopup(AutofillPopupController *controller, const QRect &bounds,
+ bool autoselectFirstSuggestion) = 0;
+ virtual void hideAutofillPopup() = 0;
virtual ProfileAdapter *profileAdapter() = 0;
virtual WebContentsAdapter* webContentsAdapter() = 0;
diff --git a/src/webenginequick/api/qquickwebengineview.cpp b/src/webenginequick/api/qquickwebengineview.cpp
index babd0ade7..015f916b3 100644
--- a/src/webenginequick/api/qquickwebengineview.cpp
+++ b/src/webenginequick/api/qquickwebengineview.cpp
@@ -55,6 +55,7 @@
#include "qquickwebengineview_p_p.h"
#include "authentication_dialog_controller.h"
+#include "autofill_popup_controller.h"
#include "profile_adapter.h"
#include "file_picker_controller.h"
#include "find_text_helper.h"
@@ -737,6 +738,19 @@ void QQuickWebEngineViewPrivate::findTextFinished(const QWebEngineFindTextResult
Q_EMIT q->findTextFinished(result);
}
+void QQuickWebEngineViewPrivate::showAutofillPopup(
+ QtWebEngineCore::AutofillPopupController *controller, const QRect &bounds,
+ bool autoselectFirstSuggestion)
+{
+ ui()->showAutofillPopup(controller, bounds.bottomLeft(), bounds.width() + 2,
+ autoselectFirstSuggestion);
+}
+
+void QQuickWebEngineViewPrivate::hideAutofillPopup()
+{
+ ui()->hideAutofillPopup();
+}
+
QWebEngineSettings *QQuickWebEngineViewPrivate::webEngineSettings() const
{
return m_settings->d_ptr.data();
diff --git a/src/webenginequick/api/qquickwebengineview_p_p.h b/src/webenginequick/api/qquickwebengineview_p_p.h
index fe9f78322..9f066af90 100644
--- a/src/webenginequick/api/qquickwebengineview_p_p.h
+++ b/src/webenginequick/api/qquickwebengineview_p_p.h
@@ -167,6 +167,10 @@ public:
QtWebEngineCore::WebContentsAdapter *webContentsAdapter() override;
void printRequested() override;
void findTextFinished(const QWebEngineFindTextResult &result) override;
+ void showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ const QRect &bounds, bool autoselectFirstSuggestion) override;
+ void hideAutofillPopup() override;
+
void updateAction(QQuickWebEngineView::WebAction) const;
bool adoptWebContents(QtWebEngineCore::WebContentsAdapter *webContents);
void setProfile(QQuickWebEngineProfile *profile);
diff --git a/src/webenginequick/ui/AutofillPopup.qml b/src/webenginequick/ui/AutofillPopup.qml
new file mode 100644
index 000000000..28b274bb6
--- /dev/null
+++ b/src/webenginequick/ui/AutofillPopup.qml
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+
+Popup {
+ id: root
+ // Let Chromium close the popup.
+ closePolicy: Popup.NoAutoClose
+
+ property variant controller: null
+ property int itemHeight: 0
+
+ signal selected(int index)
+ signal accepted()
+
+ function setCurrentIndex(index)
+ {
+ listView.currentIndex = index;
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ clip: true
+
+ model: controller.model
+ currentIndex: -1
+
+ delegate: ItemDelegate {
+ width: listView.width
+ height: root.itemHeight
+ text: model.display
+ highlighted: ListView.isCurrentItem
+
+ onHoveredChanged: if (hovered) selected(index);
+ onClicked: accepted();
+ }
+ }
+}
diff --git a/src/webenginequick/ui/CMakeLists.txt b/src/webenginequick/ui/CMakeLists.txt
index aa5832ba6..c24d8da8d 100644
--- a/src/webenginequick/ui/CMakeLists.txt
+++ b/src/webenginequick/ui/CMakeLists.txt
@@ -1,6 +1,7 @@
set(qml_files
"AlertDialog.qml"
"AuthenticationDialog.qml"
+ "AutofillPopup.qml"
"ColorDialog.qml"
"ConfirmDialog.qml"
"FilePicker.qml"
diff --git a/src/webenginequick/ui_delegates_manager.cpp b/src/webenginequick/ui_delegates_manager.cpp
index 5a01ea2f0..06b72348c 100644
--- a/src/webenginequick/ui_delegates_manager.cpp
+++ b/src/webenginequick/ui_delegates_manager.cpp
@@ -43,6 +43,7 @@
#include "api/qquickwebengineview_p_p.h"
#include <authentication_dialog_controller.h>
+#include <autofill_popup_controller.h>
#include <color_chooser_controller.h>
#include <file_picker_controller.h>
#include <javascript_dialog_controller.h>
@@ -58,6 +59,9 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlproperty.h>
+#include <QtQuick/qquickwindow.h>
+
+#include <algorithm>
// Uncomment for QML debugging
//#define UI_DELEGATES_DEBUG
@@ -129,7 +133,7 @@ UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view)
: m_view(view)
, m_toolTip(nullptr)
, m_touchSelectionMenu(nullptr)
- FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_INIT, NO_SEPARATOR)
+ , m_autofillPopup(nullptr) FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_INIT, NO_SEPARATOR)
{
}
@@ -567,6 +571,158 @@ void UIDelegatesManager::hideTouchSelectionMenu()
QTimer::singleShot(0, m_view, [this] { m_touchSelectionMenu.reset(); });
}
+bool AutofillPopupEventFilter::eventFilter(QObject *object, QEvent *event)
+{
+ if (event->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ if (keyEvent->key() == Qt::Key_Escape) {
+ m_manager->hideAutofillPopup();
+ return true;
+ }
+
+ // Ignore shortcuts while the popup is open. It may result unwanted
+ // edit commands sent to Chromium that blocks the key press.
+ event->ignore();
+ return true;
+ }
+
+ // AutofillPopupControllerImpl::HandleKeyPressEvent()
+ // chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ switch (keyEvent->key()) {
+ case Qt::Key_Up:
+ m_controller->selectPreviousSuggestion();
+ return true;
+ case Qt::Key_Down:
+ m_controller->selectNextSuggestion();
+ return true;
+ case Qt::Key_PageUp:
+ m_controller->selectFirstSuggestion();
+ return true;
+ case Qt::Key_PageDown:
+ m_controller->selectLastSuggestion();
+ return true;
+ case Qt::Key_Escape:
+ m_manager->hideAutofillPopup();
+ return true;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ m_controller->acceptSuggestion();
+ return true;
+ case Qt::Key_Delete:
+ // Remove suggestion is not supported for datalist.
+ // Forward delete to view to be able to remove selected text.
+ break;
+ case Qt::Key_Tab:
+ m_controller->acceptSuggestion();
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Do not forward release events of the overridden key presses.
+ if (event->type() == QEvent::KeyRelease) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ switch (keyEvent->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Escape:
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return QObject::eventFilter(object, event);
+}
+
+void UIDelegatesManager::showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ QPointF pos, int width, bool autoselectFirstSuggestion)
+{
+ static const int padding = 1;
+ static const int itemHeight = 20;
+ const int proposedHeight = itemHeight * (controller->model()->rowCount()) + padding * 2;
+
+ bool popupWasNull = false;
+ if (m_autofillPopup.isNull()) {
+ popupWasNull = true;
+ if (!ensureComponentLoaded(AutofillPopup))
+ return;
+
+ QQmlContext *context = qmlContext(m_view);
+ m_autofillPopup.reset(autofillPopupComponent->beginCreate(context));
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(m_autofillPopup.data()))
+ item->setParentItem(m_view);
+ m_autofillPopup->setParent(m_view);
+ }
+
+ m_autofillPopup->setProperty("controller", QVariant::fromValue(controller));
+ m_autofillPopup->setProperty("x", pos.x());
+ m_autofillPopup->setProperty("y", pos.y());
+ m_autofillPopup->setProperty("width", width);
+ m_autofillPopup->setProperty("height",
+ std::min(proposedHeight, qRound(m_view->height() - pos.y())));
+ m_autofillPopup->setProperty("padding", padding);
+ m_autofillPopup->setProperty("itemHeight", itemHeight);
+
+ if (popupWasNull) {
+ QQmlProperty selectedSignal(m_autofillPopup.data(), QStringLiteral("onSelected"));
+ CHECK_QML_SIGNAL_PROPERTY(selectedSignal, autofillPopupComponent->url());
+ static int selectSuggestionIndex =
+ controller->metaObject()->indexOfSlot("selectSuggestion(int)");
+ QObject::connect(m_autofillPopup.data(), selectedSignal.method(), controller,
+ controller->metaObject()->method(selectSuggestionIndex));
+
+ QQmlProperty acceptedSignal(m_autofillPopup.data(), QStringLiteral("onAccepted"));
+ CHECK_QML_SIGNAL_PROPERTY(acceptedSignal, autofillPopupComponent->url());
+ static int acceptSuggestionIndex =
+ controller->metaObject()->indexOfSlot("acceptSuggestion()");
+ QObject::connect(m_autofillPopup.data(), acceptedSignal.method(), controller,
+ controller->metaObject()->method(acceptSuggestionIndex));
+
+ QObject::connect(controller, &QtWebEngineCore::AutofillPopupController::currentIndexChanged,
+ [this](const QModelIndex &index) {
+ QMetaObject::invokeMethod(m_autofillPopup.data(), "setCurrentIndex",
+ Qt::DirectConnection,
+ Q_ARG(QVariant, index.row()));
+ });
+
+ autofillPopupComponent->completeCreate();
+
+ m_view->window()->installEventFilter(
+ new AutofillPopupEventFilter(controller, this, m_autofillPopup.data()));
+
+ QMetaObject::invokeMethod(m_autofillPopup.data(), "open");
+ controller->notifyPopupShown();
+ }
+
+ if (autoselectFirstSuggestion)
+ controller->selectFirstSuggestion();
+}
+
+void UIDelegatesManager::hideAutofillPopup()
+{
+ if (!m_autofillPopup)
+ return;
+
+ QTimer::singleShot(0, m_view, [this] {
+ if (m_autofillPopup) {
+ QtWebEngineCore::AutofillPopupController *controller =
+ m_autofillPopup->property("controller")
+ .value<QtWebEngineCore::AutofillPopupController *>();
+ m_autofillPopup.reset();
+ controller->notifyPopupHidden();
+ }
+ });
+}
+
bool UIDelegatesManager::initializeImportDirs(QStringList &dirs, QQmlEngine *engine)
{
const QStringList paths = engine->importPathList();
diff --git a/src/webenginequick/ui_delegates_manager.h b/src/webenginequick/ui_delegates_manager.h
index 0f515d4be..218d403d4 100644
--- a/src/webenginequick/ui_delegates_manager.h
+++ b/src/webenginequick/ui_delegates_manager.h
@@ -47,19 +47,20 @@
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
-#define FOR_EACH_COMPONENT_TYPE(F, SEPARATOR) \
- F(Menu, menu) SEPARATOR \
- F(MenuItem, menuItem) SEPARATOR \
- F(MenuSeparator, menuSeparator) SEPARATOR \
- F(AlertDialog, alertDialog) SEPARATOR \
- F(ColorDialog, colorDialog) SEPARATOR \
- F(ConfirmDialog, confirmDialog) SEPARATOR \
- F(PromptDialog, promptDialog) SEPARATOR \
- F(FilePicker, filePicker) SEPARATOR \
- F(AuthenticationDialog, authenticationDialog) SEPARATOR \
- F(ToolTip, toolTip) SEPARATOR \
- F(TouchHandle, touchHandle) SEPARATOR \
- F(TouchSelectionMenu, touchSelectionMenu) SEPARATOR \
+#define FOR_EACH_COMPONENT_TYPE(F, SEPARATOR) \
+ F(Menu, menu) SEPARATOR F(MenuItem, menuItem) \
+ SEPARATOR \
+ F(MenuSeparator, menuSeparator) SEPARATOR F(AlertDialog, alertDialog) \
+ SEPARATOR \
+ F(ColorDialog, colorDialog) SEPARATOR F(ConfirmDialog, confirmDialog) \
+ SEPARATOR \
+ F(PromptDialog, promptDialog) SEPARATOR F(FilePicker, filePicker) \
+ SEPARATOR \
+ F(AuthenticationDialog, authenticationDialog) SEPARATOR F(ToolTip, toolTip) \
+ SEPARATOR \
+ F(TouchHandle, touchHandle) SEPARATOR F(TouchSelectionMenu, touchSelectionMenu) \
+ SEPARATOR \
+ F(AutofillPopup, autofillPopup) SEPARATOR
#define COMMA_SEPARATOR ,
#define SEMICOLON_SEPARATOR ;
@@ -79,6 +80,7 @@ QT_END_NAMESPACE
namespace QtWebEngineCore {
class AuthenticationDialogController;
+class AutofillPopupController;
class ColorChooserController;
class FilePickerController;
class JavaScriptDialogController;
@@ -115,14 +117,18 @@ public:
QQuickItem *createTouchHandle();
void showTouchSelectionMenu(TouchSelectionMenuController *, const QRect &, const int spacing);
void hideTouchSelectionMenu();
+ void showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller, QPointF pos,
+ int width, bool autoselectFirstSuggestion);
+ void hideAutofillPopup();
private:
bool ensureComponentLoaded(ComponentType);
QQuickWebEngineView *m_view;
- QScopedPointer<QObject> m_toolTip;
QStringList m_importDirs;
+ QScopedPointer<QObject> m_toolTip;
QScopedPointer<QObject> m_touchSelectionMenu;
+ QScopedPointer<QObject> m_autofillPopup;
FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR)
@@ -130,6 +136,25 @@ private:
};
+class AutofillPopupEventFilter : public QObject
+{
+ Q_OBJECT
+
+public:
+ AutofillPopupEventFilter(QtWebEngineCore::AutofillPopupController *controller,
+ UIDelegatesManager *manager, QObject *parent)
+ : QObject(parent), m_controller(controller), m_manager(manager)
+ {
+ }
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event) override;
+
+private:
+ QtWebEngineCore::AutofillPopupController *m_controller;
+ UIDelegatesManager *m_manager;
+};
+
} // namespace QtWebEngineCore
#endif // UI_DELEGATES_MANAGER_H
diff --git a/src/webenginewidgets/CMakeLists.txt b/src/webenginewidgets/CMakeLists.txt
index 1aa4ae1e2..97bde6b56 100644
--- a/src/webenginewidgets/CMakeLists.txt
+++ b/src/webenginewidgets/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_internal_add_module(WebEngineWidgets
api/qwebenginenotificationpresenter.cpp api/qwebenginenotificationpresenter_p.h
api/qwebengineview.cpp api/qwebengineview.h api/qwebengineview_p.h
qwebengine_accessible.cpp qwebengine_accessible.h
+ ui/autofillpopupwidget.cpp ui/autofillpopupwidget_p.h
DEFINES
QT_BUILD_WEBENGINEWIDGETS_LIB
INCLUDE_DIRECTORIES
diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp
index 9fa5f11fc..5fdd995b8 100644
--- a/src/webenginewidgets/api/qwebengineview.cpp
+++ b/src/webenginewidgets/api/qwebengineview.cpp
@@ -43,12 +43,15 @@
#include "render_widget_host_view_qt_delegate_client.h"
#include "render_widget_host_view_qt_delegate_item.h"
#include "qwebengine_accessible.h"
+#include "ui/autofillpopupwidget_p.h"
#include <QtWebEngineCore/private/qwebenginepage_p.h>
#include <QtWebEngineCore/qwebenginecontextmenurequest.h>
#include <QtWebEngineCore/qwebenginehistory.h>
#include <QtWebEngineCore/qwebenginehttprequest.h>
#include <QtWebEngineCore/qwebengineprofile.h>
+
+#include "autofill_popup_controller.h"
#include "color_chooser_controller.h"
#include "web_contents_adapter.h"
@@ -911,6 +914,34 @@ QWebEngineContextMenuRequest *QWebEngineViewPrivate::lastContextMenuRequest() co
{
return m_contextRequest;
}
+
+void QWebEngineViewPrivate::showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ const QRect &bounds, bool autoselectFirstSuggestion)
+{
+ Q_Q(QWebEngineView);
+ if (!m_autofillPopupWidget)
+ m_autofillPopupWidget.reset(new QtWebEngineWidgetUI::AutofillPopupWidget(controller, q));
+ m_autofillPopupWidget->showPopup(q->mapToGlobal(bounds.bottomLeft()), bounds.width() + 2,
+ autoselectFirstSuggestion);
+ controller->notifyPopupShown();
+}
+
+void QWebEngineViewPrivate::hideAutofillPopup()
+{
+ if (!m_autofillPopupWidget)
+ return;
+
+ Q_Q(QWebEngineView);
+ QTimer::singleShot(0, q, [this] {
+ if (m_autofillPopupWidget) {
+ QtWebEngineCore::AutofillPopupController *controller =
+ m_autofillPopupWidget->m_controller;
+ m_autofillPopupWidget.reset();
+ controller->notifyPopupHidden();
+ }
+ });
+}
+
/*!
\fn QWebEngineView::renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode)
\since 5.6
diff --git a/src/webenginewidgets/api/qwebengineview.h b/src/webenginewidgets/api/qwebengineview.h
index cc1495d24..b3dd66f85 100644
--- a/src/webenginewidgets/api/qwebengineview.h
+++ b/src/webenginewidgets/api/qwebengineview.h
@@ -47,6 +47,10 @@
#include <QtWebEngineWidgets/qtwebenginewidgetsglobal.h>
#include <QtWebEngineCore/qwebenginepage.h>
+namespace QtWebEngineWidgetUI {
+class AutofillPopupWidget;
+}
+
QT_BEGIN_NAMESPACE
class QContextMenuEvent;
@@ -169,6 +173,7 @@ private:
Q_DECLARE_PRIVATE(QWebEngineView)
QScopedPointer<QWebEngineViewPrivate> d_ptr;
+ friend class QtWebEngineWidgetUI::AutofillPopupWidget;
friend class QWebEnginePage;
friend class QWebEnginePagePrivate;
#if QT_CONFIG(accessibility)
diff --git a/src/webenginewidgets/api/qwebengineview_p.h b/src/webenginewidgets/api/qwebengineview_p.h
index e3c9468ce..44eb32203 100644
--- a/src/webenginewidgets/api/qwebengineview_p.h
+++ b/src/webenginewidgets/api/qwebengineview_p.h
@@ -56,12 +56,17 @@
#include "render_view_context_menu_qt.h"
namespace QtWebEngineCore {
+class AutofillPopupController;
class QWebEngineContextMenuRequest;
class WebEngineQuickWidget;
class RenderWidgetHostViewQtDelegate;
class RenderWidgetHostViewQtDelegateClient;
}
+namespace QtWebEngineWidgetUI {
+class AutofillPopupWidget;
+}
+
QT_BEGIN_NAMESPACE
class QMenu;
@@ -99,6 +104,9 @@ public:
void didPrintPage(QPrinter *&printer, QSharedPointer<QByteArray> result) override;
void didPrintPageToPdf(const QString &filePath, bool success) override;
void printRequested() override;
+ void showAutofillPopup(QtWebEngineCore::AutofillPopupController *controller,
+ const QRect &bounds, bool autoselectFirstSuggestion) override;
+ void hideAutofillPopup() override;
QWebEngineViewPrivate();
virtual ~QWebEngineViewPrivate();
@@ -117,6 +125,7 @@ public:
bool m_dragEntered;
mutable bool m_ownsPage;
QWebEngineContextMenuRequest *m_contextRequest;
+ QScopedPointer<QtWebEngineWidgetUI::AutofillPopupWidget> m_autofillPopupWidget;
};
class QContextMenuBuilder : public QtWebEngineCore::RenderViewContextMenuQt
diff --git a/src/webenginewidgets/ui/autofillpopupwidget.cpp b/src/webenginewidgets/ui/autofillpopupwidget.cpp
new file mode 100644
index 000000000..579869d9e
--- /dev/null
+++ b/src/webenginewidgets/ui/autofillpopupwidget.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "autofillpopupwidget_p.h"
+#include "qwebengineview.h"
+#include "qwebengineview_p.h"
+
+#include "autofill_popup_controller.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+#include <QEvent>
+#include <QKeyEvent>
+#include <QListView>
+#include <QMouseEvent>
+
+namespace QtWebEngineWidgetUI {
+
+AutofillPopupWidget::AutofillPopupWidget(QtWebEngineCore::AutofillPopupController *controller,
+ QWebEngineView *parent)
+ : QFrame(parent, Qt::Popup), m_controller(controller), m_webEngineView(parent)
+{
+ setAttribute(Qt::WA_WindowPropagation);
+ setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
+
+ // we need a vertical layout
+ QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+ layout->setSpacing(0);
+ layout->setContentsMargins(QMargins());
+
+ m_listView = new QListView(m_webEngineView);
+ m_listView->setModel(m_controller->model());
+ m_listView->setTextElideMode(Qt::ElideMiddle);
+
+ // Based on QComboBoxPrivateContainer::setItemView
+ m_listView->setParent(this);
+ m_listView->setAttribute(Qt::WA_MacShowFocusRect, false);
+ layout->insertWidget(0, m_listView);
+ m_listView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+
+ m_listView->installEventFilter(this);
+ // Necessary for filtering QEvent::MouseMove:
+ m_listView->viewport()->installEventFilter(this);
+
+ m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ // TODO: Implement vertical scrollbar. Chromium also has it.
+ m_listView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_listView->setMouseTracking(true);
+
+ m_listView->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ // Some styles (Mac) have a margin at the top and bottom of the popup.
+ layout->insertSpacing(0, 0);
+ layout->addSpacing(0);
+
+ connect(m_controller, &QtWebEngineCore::AutofillPopupController::currentIndexChanged,
+ m_listView, &QListView::setCurrentIndex);
+}
+
+AutofillPopupWidget::~AutofillPopupWidget() { }
+
+// Based on QComboBox::showPopup()
+void AutofillPopupWidget::showPopup(QPoint pos, int width, bool autoselectFirstSuggestion)
+{
+ QStyle *const style = m_webEngineView->style();
+ QStyleOptionComboBox opt;
+ opt.initFrom(m_webEngineView);
+
+ if (autoselectFirstSuggestion)
+ m_controller->selectFirstSuggestion();
+
+ QRect listRect(pos, QSize(width, 0));
+
+ // Calculate height
+ {
+ int listHeight = 0;
+ int rowCount = m_controller->model()->rowCount();
+ for (int i = 0; i < rowCount; ++i) {
+ QModelIndex idx = m_controller->model()->index(i, 0);
+ listHeight += m_listView->visualRect(idx).height();
+ }
+ if (rowCount > 1)
+ listHeight += (rowCount - 1) * m_listView->spacing() * 2;
+
+ listRect.setHeight(listRect.height() + listHeight);
+ }
+
+ // Calculate height margin
+ {
+ int heightMargin = m_listView->spacing() * 2;
+
+ // Add the frame of the popup
+ const QMargins pm = contentsMargins();
+ heightMargin += pm.top() + pm.bottom();
+
+ // Add the frame of the list view
+ const QMargins vm = m_listView->contentsMargins();
+ heightMargin += vm.top() + vm.bottom();
+ listRect.setHeight(listRect.height() + heightMargin);
+ }
+
+ // Add space for margin at top and bottom if the style wants it
+ int styleMargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) * 2;
+ listRect.setHeight(listRect.height() + styleMargin);
+
+ // Takes account of the mimium/maximum size of the popup
+ layout()->activate();
+ listRect.setSize(listRect.size().expandedTo(minimumSize()).boundedTo(maximumSize()));
+
+ setGeometry(listRect);
+ QFrame::show();
+}
+
+bool AutofillPopupWidget::eventFilter(QObject *object, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::MouseMove:
+ if (isVisible()) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+ QModelIndex indexUnderMouse = m_listView->indexAt(mouseEvent->position().toPoint());
+ if (indexUnderMouse.isValid()
+ && indexUnderMouse.data(Qt::AccessibleDescriptionRole).toString()
+ != QLatin1String("separator")) {
+ m_controller->selectSuggestion(indexUnderMouse.row());
+ }
+ }
+ return true;
+ case QEvent::MouseButtonRelease: {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+ if (mouseEvent->button() == Qt::LeftButton) {
+ m_controller->acceptSuggestion();
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return QFrame::eventFilter(object, event);
+}
+
+void AutofillPopupWidget::keyPressEvent(QKeyEvent *event)
+{
+ // AutofillPopupControllerImpl::HandleKeyPressEvent()
+ // chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+ switch (event->key()) {
+ case Qt::Key_Up:
+ m_controller->selectPreviousSuggestion();
+ return;
+ case Qt::Key_Down:
+ m_controller->selectNextSuggestion();
+ return;
+ case Qt::Key_PageUp:
+ m_controller->selectFirstSuggestion();
+ return;
+ case Qt::Key_PageDown:
+ m_controller->selectLastSuggestion();
+ return;
+ case Qt::Key_Escape:
+ m_webEngineView->d_ptr->hideAutofillPopup();
+ return;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ m_controller->acceptSuggestion();
+ return;
+ case Qt::Key_Delete:
+ // Remove suggestion is not supported for datalist.
+ // Forward delete to view to be able to remove selected text.
+ break;
+ case Qt::Key_Tab:
+ m_controller->acceptSuggestion();
+ break;
+ default:
+ break;
+ }
+
+ QCoreApplication::sendEvent(m_webEngineView->focusWidget(), event);
+}
+
+void AutofillPopupWidget::keyReleaseEvent(QKeyEvent *event)
+{
+ // Do not forward release events of the overridden key presses.
+ switch (event->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Escape:
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ return;
+ default:
+ break;
+ }
+
+ QCoreApplication::sendEvent(m_webEngineView->focusWidget(), event);
+}
+
+} // namespace QtWebEngineWidgetUI
diff --git a/src/webenginewidgets/ui/autofillpopupwidget_p.h b/src/webenginewidgets/ui/autofillpopupwidget_p.h
new file mode 100644
index 000000000..064c4f824
--- /dev/null
+++ b/src/webenginewidgets/ui/autofillpopupwidget_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AUTOFILLPOPUPWIDGET_P_H
+#define AUTOFILLPOPUPWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QFrame>
+
+namespace QtWebEngineCore {
+class AutofillPopupController;
+}
+
+QT_BEGIN_NAMESPACE
+class QListView;
+class QWebEngineView;
+class QWebEngineViewPrivate;
+QT_END_NAMESPACE
+
+namespace QtWebEngineWidgetUI {
+
+// Based on QComboBoxPrivateContainer
+class AutofillPopupWidget : public QFrame
+{
+ Q_OBJECT
+public:
+ AutofillPopupWidget(QtWebEngineCore::AutofillPopupController *controller,
+ QWebEngineView *parent);
+ ~AutofillPopupWidget();
+
+ void showPopup(QPoint pos, int width, bool autoselectFirstSuggestion);
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+
+private:
+ QtWebEngineCore::AutofillPopupController *m_controller;
+ QWebEngineView *m_webEngineView;
+ QListView *m_listView;
+
+ friend class QT_PREPEND_NAMESPACE(QWebEngineViewPrivate);
+};
+
+} // namespace QtWebEngineWidgetUI
+
+#endif // AUTOFILLPOPUPWIDGET_P_H
diff --git a/tests/auto/quick/qmltests/CMakeLists.txt b/tests/auto/quick/qmltests/CMakeLists.txt
index d878bdfcf..53c8ed231 100644
--- a/tests/auto/quick/qmltests/CMakeLists.txt
+++ b/tests/auto/quick/qmltests/CMakeLists.txt
@@ -19,6 +19,7 @@ set(testList
tst_audioMuted.qml
tst_contextMenu.qml
tst_basicProfiles.qml
+ tst_datalist.qml
tst_desktopBehaviorLoadHtml.qml
tst_download.qml
tst_favicon.qml
diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml
index 68417c6c5..869754c90 100644
--- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml
+++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml
@@ -113,6 +113,21 @@ WebEngineView {
return textSelection;
}
+ function getElementValue(element) {
+ var elementValue;
+ runJavaScript("document.getElementById('" + element + "').value", function(result) {
+ elementValue = result;
+ });
+ testCase.tryVerify(function() { return elementValue != undefined; });
+ return elementValue;
+ }
+
+ function compareElementValue(element, expected) {
+ testCase.tryVerify(function() { return expected == getElementValue(element); }, 5000,
+ "Value of element \"" + element + "\" is \"" + expected + "\"");
+ }
+
+
TestResult { id: testResult }
onLoadingChanged: function(load) {
diff --git a/tests/auto/quick/qmltests/data/tst_datalist.qml b/tests/auto/quick/qmltests/data/tst_datalist.qml
new file mode 100644
index 000000000..66cc49b0c
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/tst_datalist.qml
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtTest
+import QtWebEngine
+
+TestWebEngineView {
+ id: webEngineView
+ width: 200
+ height: 400
+
+ property string html: "<html><body>" +
+ "<input id='browserInput' list='browserDatalist'>" +
+ "<datalist id='browserDatalist'>" +
+ " <option value='Internet Explorer'>" +
+ " <option value='Firefox'>" +
+ " <option value='Chrome'>" +
+ " <option value='Opera'>" +
+ " <option value='Safari'>" +
+ "</datalist>" +
+ "</body></html>"
+
+ function listView() {
+ if (webEngineView.parent.visibleChildren.length == 1) {
+ // No popup case.
+ return null;
+ }
+
+ let overlay = null;
+ for (let i = 0; i < webEngineView.parent.visibleChildren.length; ++i) {
+ let child = webEngineView.parent.visibleChildren[i];
+ if (child instanceof Overlay) {
+ overlay = child;
+ break;
+ }
+ }
+
+ if (!overlay)
+ return null;
+
+ let popupItem = null;
+ for (let i = 0; i < overlay.visibleChildren[0].visibleChildren.length; ++i) {
+ let child = overlay.visibleChildren[0].visibleChildren[i];
+ if (child.objectName == "QQuickPopupItem") {
+ popupItem = child;
+ }
+ }
+
+ if (!popupItem)
+ return null;
+
+ for (let i = 0; i < popupItem.visibleChildren.length; ++i) {
+ let child = popupItem.visibleChildren[i];
+ if (child instanceof ListView)
+ return child;
+ }
+
+ return null;
+ }
+
+ TestCase {
+ id: testCase
+ name: "WebEngineDatalist"
+ when: windowShown
+
+ function test_showAndHide() {
+ webEngineView.loadHtml(webEngineView.html);
+ verify(webEngineView.waitForLoadSucceeded());
+
+ var values = "";
+ webEngineView.runJavaScript(
+ "(function() {" +
+ " var browserDatalist = document.getElementById('browserDatalist');" +
+ " var options = browserDatalist.options;" +
+ " var result = [];" +
+ " for (let i = 0; i < options.length; ++i) {" +
+ " result.push(options[i].value);" +
+ " }" +
+ " return result;" +
+ "})();", function(result) { values = result; });
+ tryVerify(function() { return values.length != 0; });
+ compare(values, ["Internet Explorer", "Firefox", "Chrome", "Opera", "Safari"]);
+ compareElementValue("browserInput", "");
+
+ // Make sure there is no open popup yet.
+ verify(!listView());
+ // Click in the input field.
+ var browserInputCenter = getElementCenter("browserInput");
+ mouseClick(webEngineView, browserInputCenter.x, browserInputCenter.y, Qt.LeftButton);
+ // Wait for the popup.
+ tryVerify(function() { return listView() != null; });
+
+ // No suggestion is selected.
+ verify(!listView().currentItem);
+ compare(listView().count, 5);
+
+ // Accepting suggestion does nothing.
+ keyClick(Qt.Key_Enter);
+ tryVerify(function() { return listView() != null; });
+ verify(!listView().currentItem);
+
+ // Escape should close popup.
+ keyClick(Qt.Key_Escape);
+ tryVerify(function() { return listView() == null; });
+
+ // Key Down should open the popup and select the first suggestion.
+ keyClick(Qt.Key_Down);
+ tryVerify(function() { return listView() != null; });
+ compare(listView().currentIndex, 0);
+ verify(listView().currentItem);
+ }
+
+ function test_keyboardNavigationAndAccept() {
+ webEngineView.loadHtml(html);
+ verify(webEngineView.waitForLoadSucceeded());
+ setFocusToElement("browserInput");
+
+ // Make sure there is no open popup yet.
+ verify(!listView());
+
+ // Key Down should open the popup and select the first suggestion.
+ keyClick(Qt.Key_Down);
+ tryVerify(function() { return listView() != null; });
+ compare(listView().currentIndex, 0);
+
+ // Test keyboard navigation in list.
+ keyClick(Qt.Key_Up);
+ compare(listView().currentIndex, 4);
+ keyClick(Qt.Key_Up);
+ compare(listView().currentIndex, 3);
+ keyClick(Qt.Key_PageDown);
+ compare(listView().currentIndex, 4);
+ keyClick(Qt.Key_PageUp);
+ compare(listView().currentIndex, 0);
+ keyClick(Qt.Key_Down);
+ compare(listView().currentIndex, 1);
+ keyClick(Qt.Key_Down);
+ compare(listView().currentIndex, 2);
+
+ // Test accepting suggestion.
+ compare(listView().currentItem.text, "Chrome");
+ keyClick(Qt.Key_Enter);
+ compareElementValue("browserInput", "Chrome");
+ // Accept closes popup.
+ tryVerify(function() { return listView() == null; });
+
+ // Clear input field, should not trigger popup.
+ webEngineView.runJavaScript("document.getElementById('browserInput').value = ''");
+ compareElementValue("browserInput", "");
+ verify(listView() == null);
+ }
+
+ function test_filterSuggestion() {
+ webEngineView.loadHtml(html);
+ verify(webEngineView.waitForLoadSucceeded());
+ setFocusToElement("browserInput");
+
+ // Make sure there is no open popup yet.
+ verify(!listView());
+
+ // Filter suggestions.
+ keyClick(Qt.Key_F);
+ tryVerify(function() { return listView() != null; });
+ compare(listView().count, 2);
+ verify(!listView().currentItem);
+ compare(listView().itemAtIndex(0).text, "Firefox");
+ compare(listView().itemAtIndex(1).text, "Safari");
+ keyClick(Qt.Key_I);
+ tryVerify(function() { return listView().count == 1; });
+ verify(!listView().currentItem);
+ compare(listView().itemAtIndex(0).text, "Firefox");
+ keyClick(Qt.Key_L);
+ // Mismatch should close popup.
+ tryVerify(function() { return listView() == null; });
+ compareElementValue("browserInput", "fil");
+ }
+ }
+}
diff --git a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml
index 7c594d921..170dde5e7 100644
--- a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml
+++ b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml
@@ -54,20 +54,6 @@ TestWebEngineView {
"Element \"" + element + "\" is " + (expected ? "" : "not") + " checked");
}
- function getElementValue(element) {
- var elementValue;
- runJavaScript("document.getElementById('" + element + "').value", function(result) {
- elementValue = result;
- });
- tryVerify(function() { return elementValue != undefined; });
- return elementValue;
- }
-
- function compareElementValue(element, expected) {
- tryVerify(function() { return expected == getElementValue(element); }, 5000,
- "Value of element \"" + element + "\" is \"" + expected + "\"");
- }
-
function test_keyboardEvents() {
webEngineView.url = Qt.resolvedUrl("keyboardEvents.html");
verify(webEngineView.waitForLoadSucceeded());
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index 80da4664c..f54fdbbfc 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -37,6 +37,7 @@
#include <QDropEvent>
#include <QLabel>
#include <QLineEdit>
+#include <QListView>
#include <QHBoxLayout>
#include <QMenu>
#include <QMimeData>
@@ -44,6 +45,7 @@
#include <QQuickWidget>
#include <QtWebEngineCore/qwebenginehttprequest.h>
#include <QScopeGuard>
+#include <QStringListModel>
#include <QTcpServer>
#include <QTcpSocket>
#include <QStyle>
@@ -179,6 +181,7 @@ private Q_SLOTS:
void inspectElement();
void navigateOnDrop_data();
void navigateOnDrop();
+ void datalist();
};
// This will be called before the first test function is executed.
@@ -3597,5 +3600,154 @@ void tst_QWebEngineView::navigateOnDrop()
}
}
+void tst_QWebEngineView::datalist()
+{
+ QString html("<html><body>"
+ "<input id='browserInput' list='browserDatalist'>"
+ "<datalist id='browserDatalist'>"
+ " <option value='Internet Explorer'>"
+ " <option value='Firefox'>"
+ " <option value='Chrome'>"
+ " <option value='Opera'>"
+ " <option value='Safari'>"
+ "</datalist>"
+ "</body></html>");
+
+ QWebEngineView view;
+ view.resize(200, 400);
+ view.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished);
+ view.setHtml(html);
+ QTRY_COMPARE(loadSpy.count(), 1);
+
+ QString listValuesJS("(function() {"
+ " var browserDatalist = document.getElementById('browserDatalist');"
+ " var options = browserDatalist.options;"
+ " var result = [];"
+ " for (let i = 0; i < options.length; ++i) {"
+ " result.push(options[i].value);"
+ " }"
+ " return result;"
+ "})();");
+ QStringList values = evaluateJavaScriptSync(view.page(), listValuesJS).toStringList();
+ QCOMPARE(values, QStringList({ "Internet Explorer", "Firefox", "Chrome", "Opera", "Safari" }));
+ QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value;")
+ .toString(),
+ QStringLiteral(""));
+
+ auto listView = [&view]() -> QListView * {
+ if (QApplication::topLevelWidgets().size() == 1) {
+ // No popup case.
+ return nullptr;
+ }
+
+ QWidget *autofillPopupWidget = nullptr;
+ for (QWidget *w : QApplication::topLevelWidgets()) {
+ if (w != &view) {
+ autofillPopupWidget = w;
+ break;
+ }
+ }
+
+ if (!autofillPopupWidget)
+ return nullptr;
+
+ for (QObject *o : autofillPopupWidget->children()) {
+ if (QListView *listView = qobject_cast<QListView *>(o))
+ return listView;
+ }
+
+ return nullptr;
+ };
+
+ // Make sure there is no open popup yet.
+ QVERIFY(!listView());
+ // Click in the input field.
+ QPoint browserInputCenter = elementCenter(view.page(), "browserInput");
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, browserInputCenter);
+ // Wait for the popup.
+ QTRY_VERIFY(listView());
+
+ // No suggestion is selected.
+ QCOMPARE(listView()->currentIndex(), QModelIndex());
+ QCOMPARE(listView()->model()->rowCount(), 5);
+
+ // Accepting suggestion does nothing.
+ QTest::keyClick(view.windowHandle(), Qt::Key_Enter);
+ QVERIFY(listView());
+ QCOMPARE(listView()->currentIndex(), QModelIndex());
+
+ // Escape should close popup.
+ QTest::keyClick(view.windowHandle(), Qt::Key_Escape);
+ QTRY_VERIFY(!listView());
+
+ // Key Down should open the popup and select the first suggestion.
+ QTest::keyClick(view.windowHandle(), Qt::Key_Down);
+ QTRY_VERIFY(listView());
+ QCOMPARE(listView()->currentIndex().row(), 0);
+
+ // Test keyboard navigation in list.
+ QTest::keyClick(view.windowHandle(), Qt::Key_Up);
+ QCOMPARE(listView()->currentIndex().row(), 4);
+ QTest::keyClick(view.windowHandle(), Qt::Key_Up);
+ QCOMPARE(listView()->currentIndex().row(), 3);
+ QTest::keyClick(view.windowHandle(), Qt::Key_PageDown);
+ QCOMPARE(listView()->currentIndex().row(), 4);
+ QTest::keyClick(view.windowHandle(), Qt::Key_PageUp);
+ QCOMPARE(listView()->currentIndex().row(), 0);
+ QTest::keyClick(view.windowHandle(), Qt::Key_Down);
+ QCOMPARE(listView()->currentIndex().row(), 1);
+ QTest::keyClick(view.windowHandle(), Qt::Key_Down);
+ QCOMPARE(listView()->currentIndex().row(), 2);
+
+ // Test accepting suggestion.
+ QCOMPARE(static_cast<QStringListModel *>(listView()->model())
+ ->data(listView()->currentIndex())
+ .toString(),
+ QStringLiteral("Chrome"));
+ QTest::keyClick(view.windowHandle(), Qt::Key_Enter);
+ QTRY_COMPARE(
+ evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value")
+ .toString(),
+ QStringLiteral("Chrome"));
+ // Accept closes popup.
+ QTRY_VERIFY(!listView());
+
+ // Clear input field, should not trigger popup.
+ evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value = ''");
+ QVERIFY(!listView());
+
+ // Filter suggestions.
+ QTest::keyClick(view.windowHandle(), Qt::Key_F);
+ QTRY_VERIFY(listView());
+ QCOMPARE(listView()->model()->rowCount(), 2);
+ QCOMPARE(listView()->currentIndex(), QModelIndex());
+ QCOMPARE(static_cast<QStringListModel *>(listView()->model())
+ ->data(listView()->model()->index(0, 0))
+ .toString(),
+ QStringLiteral("Firefox"));
+ QCOMPARE(static_cast<QStringListModel *>(listView()->model())
+ ->data(listView()->model()->index(1, 0))
+ .toString(),
+ QStringLiteral("Safari"));
+ QTest::keyClick(view.windowHandle(), Qt::Key_I);
+ QTRY_COMPARE(listView()->model()->rowCount(), 1);
+ QCOMPARE(listView()->currentIndex(), QModelIndex());
+ QCOMPARE(static_cast<QStringListModel *>(listView()->model())
+ ->data(listView()->model()->index(0, 0))
+ .toString(),
+ QStringLiteral("Firefox"));
+ QTest::keyClick(view.windowHandle(), Qt::Key_L);
+ // Mismatch should close popup.
+ QTRY_VERIFY(!listView());
+ QTRY_COMPARE(
+ evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value")
+ .toString(),
+ QStringLiteral("fil"));
+}
+
QTEST_MAIN(tst_QWebEngineView)
#include "tst_qwebengineview.moc"