summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/changes-5.12.02
m---------src/3rdparty0
-rw-r--r--src/core/api/qwebengineurlscheme.cpp6
-rw-r--r--src/core/api/qwebengineurlschemehandler.cpp43
-rw-r--r--src/core/chromium_overrides.cpp5
-rw-r--r--src/core/compositor/delegated_frame_node.cpp19
-rw-r--r--src/core/config/linux.pri10
-rw-r--r--src/core/ozone/gl_context_qt.cpp5
-rw-r--r--src/core/profile_adapter.cpp5
-rw-r--r--src/core/profile_io_data_qt.cpp19
-rw-r--r--src/core/profile_io_data_qt.h3
-rw-r--r--src/core/render_widget_host_view_qt.cpp33
-rw-r--r--src/core/web_event_factory.cpp2
-rw-r--r--src/webengine/api/qquickwebengineaction.cpp5
-rw-r--r--src/webengine/api/qquickwebengineaction_p_p.h6
-rw-r--r--src/webengine/api/qquickwebengineprofile.cpp4
-rw-r--r--src/webengine/api/qquickwebengineview.cpp3
-rw-r--r--src/webengine/doc/src/qtwebengine-platform-notes.qdoc2
-rw-r--r--src/webengine/doc/src/webengineview_lgpl.qdoc13
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.cpp7
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.h1
-rw-r--r--src/webenginewidgets/api/qwebengineprofile.cpp3
-rw-r--r--src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc32
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp9
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h1
-rw-r--r--tests/auto/quick/qquickwebenginedefaultsurfaceformat/BLACKLIST4
-rw-r--r--tests/auto/quick/qquickwebengineview/BLACKLIST5
-rw-r--r--tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp186
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp191
29 files changed, 550 insertions, 74 deletions
diff --git a/dist/changes-5.12.0 b/dist/changes-5.12.0
index 557bdf0a9..affb7567e 100644
--- a/dist/changes-5.12.0
+++ b/dist/changes-5.12.0
@@ -78,7 +78,7 @@ Qt WebEngine (QML)
Chromium's behavior.
- QWebEngineDownloadItem::page() accessor added to tell were the download was triggered.
- [QTBUG-53745, QTBUG-69237] QWebEnginePage::printRequest added for window.print() support.
-- [QTBUG-54877] Introduced support for client certificates on macOS and Windows.
+- [QTBUG-54877] Introduced support for client certificates.
- [QTBUG-64501] Fixed a way to trigger an infinite loop.
- [QTBUG-69222] Fixed call order of print callback.
diff --git a/src/3rdparty b/src/3rdparty
-Subproject 91432e048e9bef479cc61f97d6ab4599121a199
+Subproject 323b45aa242e0b4e75689d67418ec124ba1ca81
diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp
index f36f3335b..d63599163 100644
--- a/src/core/api/qwebengineurlscheme.cpp
+++ b/src/core/api/qwebengineurlscheme.cpp
@@ -84,8 +84,10 @@ public:
URLs.
Custom URL schemes must be configured early at application startup, before
- creating any Qt WebEngine classes. The configuration applies globally to all
- profiles.
+ creating any Qt WebEngine classes. In general this means the schemes need to be configured before
+ a QGuiApplication or QApplication instance is created.
+
+ Every registered scheme configuration applies globally to all profiles.
\code
int main(int argc, char **argv)
diff --git a/src/core/api/qwebengineurlschemehandler.cpp b/src/core/api/qwebengineurlschemehandler.cpp
index 6ec5c25ec..2e93f4b73 100644
--- a/src/core/api/qwebengineurlschemehandler.cpp
+++ b/src/core/api/qwebengineurlschemehandler.cpp
@@ -48,12 +48,51 @@ QT_BEGIN_NAMESPACE
\brief The QWebEngineUrlSchemeHandler is a base class for handling custom URL schemes.
\since 5.6
- To implement a custom URL scheme for QtWebEngine, you must write a class derived from this class,
- and reimplement requestStarted(). Then install it via QWebEngineProfile::installUrlSchemeHandler()
+ To implement a custom URL scheme for QtWebEngine, you first have to create an instance of
+ QWebEngineUrlScheme and register it using QWebEngineUrlScheme::registerScheme().
+
+ \note Make sure that you create and register the scheme object \e before the QGuiApplication
+ or QApplication object is instantiated.
+
+ Then you must create a class derived from QWebEngineUrlSchemeHandler,
+ and reimplement the requestStarted() method.
+
+ Finally, install the scheme handler object via QWebEngineProfile::installUrlSchemeHandler()
or QQuickWebEngineProfile::installUrlSchemeHandler().
+ \code
+
+ class MySchemeHandler : public QWebEngineUrlSchemeHandler
+ {
+ public:
+ MySchemeHandler(QObject *parent = nullptr);
+ void requestStarted(QWebEngineUrlRequestJob *request)
+ {
+ // ....
+ }
+ };
+
+ int main(int argc, char **argv)
+ {
+ QWebEngineUrlScheme scheme("myscheme");
+ scheme.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort);
+ scheme.setDefaultPort(2345);
+ scheme.setFlags(QWebEngineUrlScheme::SecureScheme);
+ QWebEngineUrlScheme::registerScheme(scheme);
+
+ // ...
+ QApplication app(argc, argv);
+ // ...
+
+ // installUrlSchemeHandler does not take ownership of the handler.
+ MySchemeHandler *handler = new MySchemeHandler(parent);
+ QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("myscheme", handler);
+ }
+ \endcode
+
\inmodule QtWebEngineCore
+ \sa {QWebEngineUrlScheme}, {WebEngine Widgets WebUI Example}
*/
/*!
diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp
index a497ddc46..841dcf4c9 100644
--- a/src/core/chromium_overrides.cpp
+++ b/src/core/chromium_overrides.cpp
@@ -92,6 +92,11 @@ void GetScreenInfoFromNativeWindow(QWindow* window, content::ScreenInfo* results
} // namespace QtWebEngineCore
+void *GetQtXDisplay()
+{
+ return GLContextHelper::getXDisplay();
+}
+
namespace content {
class WebContentsImpl;
class WebContentsView;
diff --git a/src/core/compositor/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp
index c4a6d8078..e91a8728f 100644
--- a/src/core/compositor/delegated_frame_node.cpp
+++ b/src/core/compositor/delegated_frame_node.cpp
@@ -92,6 +92,10 @@
#define GL_TEXTURE_RECTANGLE 0x84F5
#endif
+#ifndef GL_NEAREST
+#define GL_NEAREST 0x2600
+#endif
+
#ifndef GL_LINEAR
#define GL_LINEAR 0x2601
#endif
@@ -986,7 +990,20 @@ inline auto &findTexture(Container &map, Container &previousMap, const Key &key)
QSGTexture *DelegatedFrameNode::initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate, int target)
{
- QSGTexture::Filtering filtering = resource->filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest;
+ QSGTexture::Filtering filtering;
+
+ if (resource->filter == GL_NEAREST)
+ filtering = QSGTexture::Nearest;
+ else if (resource->filter == GL_LINEAR)
+ filtering = QSGTexture::Linear;
+ else {
+ // Depends on qtdeclarative fix, see QTBUG-71322
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1)
+ filtering = QSGTexture::Linear;
+#else
+ filtering = QSGTexture::Nearest;
+#endif
+ }
if (resource->is_software) {
QSharedPointer<QSGTexture> &texture =
diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri
index 752d2281f..85b948db2 100644
--- a/src/core/config/linux.pri
+++ b/src/core/config/linux.pri
@@ -24,10 +24,6 @@ qtConfig(webengine-embedded-build) {
!use_gold_linker: gn_args += use_gold=false
}
-qtConfig(webengine-system-x11): hasX11Dependencies() {
- gn_args += ozone_platform_x11=true
-}
-
clang {
clang_full_path = $$which($${QMAKE_CXX})
# Remove the "/bin/clang++" part.
@@ -171,7 +167,11 @@ host_build {
gn_args += use_alsa=false
}
!packagesExist(libpci): gn_args += use_libpci=false
- !packagesExist(xscrnsaver): gn_args += use_xscrnsaver=false
+
+ qtConfig(webengine-system-x11): hasX11Dependencies() {
+ gn_args += ozone_platform_x11=true
+ packagesExist(xscrnsaver): gn_args += use_xscrnsaver=true
+ }
qtConfig(webengine-system-libevent): gn_args += use_system_libevent=true
qtConfig(webengine-system-libwebp): gn_args += use_system_libwebp=true
diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp
index 18181d310..7e913817e 100644
--- a/src/core/ozone/gl_context_qt.cpp
+++ b/src/core/ozone/gl_context_qt.cpp
@@ -130,8 +130,9 @@ void* GLContextHelper::getEGLDisplay()
void* GLContextHelper::getXDisplay()
{
- return qApp->platformNativeInterface()->nativeResourceForScreen(
- QByteArrayLiteral("display"), qApp->primaryScreen());
+ if (QGuiApplication::platformName() != QLatin1String("xcb"))
+ return nullptr;
+ return qApp->platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("display"), qApp->primaryScreen());
}
void* GLContextHelper::getNativeDisplay()
diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp
index 437423a4a..b080a4ced 100644
--- a/src/core/profile_adapter.cpp
+++ b/src/core/profile_adapter.cpp
@@ -44,6 +44,7 @@
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/download_manager.h"
+#include "api/qwebengineurlscheme.h"
#include "content_client_qt.h"
#include "download_manager_delegate_qt.h"
#include "net/url_request_context_getter_qt.h"
@@ -489,6 +490,10 @@ void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngin
qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData());
return;
}
+ if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme())
+ qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() "
+ "before installing the custom scheme handler.", scheme.constData());
+
m_customUrlSchemeHandlers.insert(canonicalScheme, handler);
updateCustomUrlSchemeHandlers();
}
diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp
index 4519bd8fb..a95607d09 100644
--- a/src/core/profile_io_data_qt.cpp
+++ b/src/core/profile_io_data_qt.cpp
@@ -59,6 +59,7 @@
#include "net/http/http_cache.h"
#include "net/http/http_server_properties_impl.h"
#include "net/http/http_network_session.h"
+#include "net/http/transport_security_persister.h"
#include "net/proxy_resolution/dhcp_pac_file_fetcher_factory.h"
#include "net/proxy_resolution/pac_file_fetcher_impl.h"
#include "net/proxy_resolution/proxy_config_service.h"
@@ -306,6 +307,7 @@ void ProfileIODataQt::generateStorage()
// we need to get rid of dangling pointer due to coming storage deletion
m_urlRequestContext->set_http_transaction_factory(0);
m_httpNetworkSession.reset();
+ m_transportSecurityPersister.reset();
}
m_storage.reset(new net::URLRequestContextStorage(m_urlRequestContext.get()));
@@ -338,8 +340,20 @@ void ProfileIODataQt::generateStorage()
m_networkDelegate.get()));
m_storage->set_ssl_config_service(std::make_unique<SSLConfigServiceQt>());
- m_storage->set_transport_security_state(std::unique_ptr<net::TransportSecurityState>(
- new net::TransportSecurityState()));
+ m_storage->set_transport_security_state(std::make_unique<net::TransportSecurityState>());
+
+ if (!m_dataPath.isEmpty()) {
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner(
+ base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(),
+ base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::BLOCK_SHUTDOWN}));
+ m_transportSecurityPersister =
+ std::make_unique<net::TransportSecurityPersister>(
+ m_urlRequestContext->transport_security_state(),
+ toFilePath(m_dataPath),
+ background_task_runner);
+ };
if (!m_httpAuthPreferences)
m_httpAuthPreferences.reset(new net::HttpAuthPreferences());
@@ -618,6 +632,7 @@ void ProfileIODataQt::setFullConfiguration()
m_httpCacheMaxSize = m_profileAdapter->httpCacheMaxSize();
m_customUrlSchemes = m_profileAdapter->customUrlSchemes();
m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification();
+ m_dataPath = m_profileAdapter->dataPath();
}
void ProfileIODataQt::updateStorageSettings()
diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h
index b7706f190..9291f7313 100644
--- a/src/core/profile_io_data_qt.h
+++ b/src/core/profile_io_data_qt.h
@@ -58,6 +58,7 @@ class ProxyConfigService;
class URLRequestContext;
class URLRequestContextStorage;
class URLRequestJobFactoryImpl;
+class TransportSecurityPersister;
}
namespace QtWebEngineCore {
@@ -116,6 +117,7 @@ private:
std::unique_ptr<net::DhcpPacFileFetcherFactory> m_dhcpPacFileFetcherFactory;
std::unique_ptr<net::HttpAuthPreferences> m_httpAuthPreferences;
std::unique_ptr<net::URLRequestJobFactory> m_jobFactory;
+ std::unique_ptr<net::TransportSecurityPersister> m_transportSecurityPersister;
base::WeakPtr<ProfileIODataQt> m_weakPtr;
scoped_refptr<CookieMonsterDelegateQt> m_cookieDelegate;
content::URLRequestInterceptorScopedVector m_requestInterceptors;
@@ -146,6 +148,7 @@ private:
bool m_useForGlobalCertificateVerification = false;
bool m_hasPageInterceptors = false;
base::WeakPtrFactory<ProfileIODataQt> m_weakPtrFactory; // this should be always the last member
+ QString m_dataPath;
DISALLOW_COPY_AND_ASSIGN(ProfileIODataQt);
};
} // namespace QtWebEngineCore
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index fc42d5bde..4bb41a303 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -747,25 +747,29 @@ void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputMana
Q_UNUSED(updated_view);
Q_UNUSED(did_update_state);
- ui::TextInputType type = getTextInputType();
- m_delegate->inputMethodStateChanged(type != ui::TEXT_INPUT_TYPE_NONE, type == ui::TEXT_INPUT_TYPE_PASSWORD);
- m_delegate->setInputMethodHints(toQtInputMethodHints(type));
-
const content::TextInputState *state = text_input_manager_->GetTextInputState();
- if (!state)
+ if (!state) {
+ m_delegate->inputMethodStateChanged(false /*editorVisible*/, false /*passwordInput*/);
+ m_delegate->setInputMethodHints(Qt::ImhNone);
return;
+ }
- // At this point it is unknown whether the text input state has been updated due to a text selection.
- // Keep the cursor position updated for cursor movements too.
- if (GetSelectedText().empty())
- m_cursorPosition = state->selection_start;
+ ui::TextInputType type = getTextInputType();
+ m_delegate->setInputMethodHints(toQtInputMethodHints(getTextInputType()) | Qt::ImhNoPredictiveText | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu);
m_surroundingText = QString::fromStdString(state->value);
-
// Remove IME composition text from the surrounding text
if (state->composition_start != -1 && state->composition_end != -1)
m_surroundingText.remove(state->composition_start, state->composition_end - state->composition_start);
+ // In case of text selection, the update is expected in RenderWidgetHostViewQt::selectionChanged().
+ if (GetSelectedText().empty()) {
+ // At this point it is unknown whether the text input state has been updated due to a text selection.
+ // Keep the cursor position updated for cursor movements too.
+ m_cursorPosition = state->selection_start;
+ m_delegate->inputMethodStateChanged(type != ui::TEXT_INPUT_TYPE_NONE, type == ui::TEXT_INPUT_TYPE_PASSWORD);
+ }
+
if (m_imState & ImStateFlags::TextInputStateUpdated) {
m_imState = ImStateFlags::TextInputStateUpdated;
return;
@@ -822,9 +826,10 @@ void RenderWidgetHostViewQt::selectionChanged()
{
// Reset input manager state
m_imState = 0;
+ ui::TextInputType type = getTextInputType();
// Handle text selection out of an input field
- if (getTextInputType() == ui::TEXT_INPUT_TYPE_NONE) {
+ if (type == ui::TEXT_INPUT_TYPE_NONE) {
if (GetSelectedText().empty() && m_emptyPreviousSelection)
return;
@@ -844,12 +849,13 @@ void RenderWidgetHostViewQt::selectionChanged()
// if the selection is cleared because TextInputState changes before the TextSelection change.
Q_ASSERT(text_input_manager_->GetTextInputState());
m_cursorPosition = text_input_manager_->GetTextInputState()->selection_start;
+ m_delegate->inputMethodStateChanged(true /*editorVisible*/, type == ui::TEXT_INPUT_TYPE_PASSWORD);
m_anchorPositionWithinSelection = m_cursorPosition;
m_cursorPositionWithinSelection = m_cursorPosition;
if (!m_emptyPreviousSelection) {
- m_emptyPreviousSelection = GetSelectedText().empty();
+ m_emptyPreviousSelection = true;
m_adapterClient->selectionChanged();
}
@@ -884,6 +890,7 @@ void RenderWidgetHostViewQt::selectionChanged()
m_cursorPosition = newCursorPositionWithinSelection;
m_emptyPreviousSelection = selection->selected_text().empty();
+ m_delegate->inputMethodStateChanged(true /*editorVisible*/, type == ui::TEXT_INPUT_TYPE_PASSWORD);
m_adapterClient->selectionChanged();
}
@@ -1106,7 +1113,7 @@ QVariant RenderWidgetHostViewQt::inputMethodQuery(Qt::InputMethodQuery query)
// TODO: Implement this
return QVariant(); // No limit.
case Qt::ImHints:
- return int(toQtInputMethodHints(getTextInputType()));
+ return int(toQtInputMethodHints(getTextInputType()) | Qt::ImhNoPredictiveText | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu);
default:
return QVariant();
}
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp
index a45f7048b..24c3a3e64 100644
--- a/src/core/web_event_factory.cpp
+++ b/src/core/web_event_factory.cpp
@@ -101,7 +101,7 @@ static KeyboardDriver keyboardDriverImpl()
if (platformName == QLatin1Literal("xcb") || platformName == QLatin1Literal("wayland"))
return KeyboardDriver::Xkb;
-#if QT_CONFIG(libinput) && QT_CONFIG(xkbcommon_evdev)
+#if QT_CONFIG(libinput)
// Based on QEglFSIntegration::createInputHandlers and QLibInputKeyboard::processKey.
if (platformName == QLatin1Literal("eglfs") && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT"))
return KeyboardDriver::Xkb;
diff --git a/src/webengine/api/qquickwebengineaction.cpp b/src/webengine/api/qquickwebengineaction.cpp
index 16eef04d3..69a05f29b 100644
--- a/src/webengine/api/qquickwebengineaction.cpp
+++ b/src/webengine/api/qquickwebengineaction.cpp
@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
\inqmlmodule QtWebEngine
\since QtWebEngine 1.8
- \brief An action that represents a \l WebEngineView::WebAction
+ \brief An action that represents a \l WebEngineView::WebAction.
A WebEngineAction is returned by the \l WebEngineView::action()
method. It provides information about the action, such as
@@ -146,7 +146,8 @@ QString QQuickWebEngineAction::iconName() const
/*!
\qmlproperty bool WebEngineAction::enabled
- This property holds whether the action is enabled.
+ This property holds whether the action is enabled. Context-dependent
+ actions are always enabled.
*/
bool QQuickWebEngineAction::isEnabled() const
{
diff --git a/src/webengine/api/qquickwebengineaction_p_p.h b/src/webengine/api/qquickwebengineaction_p_p.h
index 4320f73e4..d2ead30e9 100644
--- a/src/webengine/api/qquickwebengineaction_p_p.h
+++ b/src/webengine/api/qquickwebengineaction_p_p.h
@@ -72,13 +72,13 @@ public:
void trigger();
-private:
- QQuickWebEngineAction *q_ptr;
-
QVariant m_data;
QString m_text;
QString m_iconName;
bool m_enabled;
+
+private:
+ QQuickWebEngineAction *q_ptr;
};
QT_END_NAMESPACE
diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp
index 508106583..22202cc97 100644
--- a/src/webengine/api/qquickwebengineprofile.cpp
+++ b/src/webengine/api/qquickwebengineprofile.cpp
@@ -53,6 +53,8 @@
#include "renderer_host/user_resource_controller_host.h"
#include "web_engine_settings.h"
+#include <QtWebEngineCore/qwebengineurlscheme.h>
+
using QtWebEngineCore::ProfileAdapter;
QT_BEGIN_NAMESPACE
@@ -904,7 +906,7 @@ const QWebEngineUrlSchemeHandler *QQuickWebEngineProfile::urlSchemeHandler(const
/*!
Registers a handler \a handler for custom URL scheme \a scheme in the profile.
- It is recommended to first register the scheme with \l
+ It is necessary to first register the scheme with \l
QWebEngineUrlScheme::registerScheme at application startup.
*/
void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index e2a8b562d..3659f35fa 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -2225,7 +2225,8 @@ void QQuickContextMenuBuilder::addMenuItem(ContextMenuItem menuItem)
m_view->d_ptr->ui()->addMenuSeparator(m_menu);
return;
}
- action->d_ptr->setEnabled(isMenuItemEnabled(menuItem));
+ // Set enabled property directly with avoiding binding loops caused by its notifier signal.
+ action->d_ptr->m_enabled = isMenuItemEnabled(menuItem);
m_view->d_ptr->ui()->addMenuItem(action, m_menu);
}
diff --git a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc
index 59429ec5a..0b2aebfce 100644
--- a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc
+++ b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc
@@ -41,7 +41,7 @@
\list
\li \l{Qt for Windows - Requirements}
\li \l{Qt for X11 Requirements}
- \li \l{Qt for macOS - Requirements}
+ \li \l{Qt for macOS - Building from Source}
\endlist
In addition, the following tools are required for building the \l {Qt WebEngine} module:
diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc
index 87094c266..0e25e16f3 100644
--- a/src/webengine/doc/src/webengineview_lgpl.qdoc
+++ b/src/webengine/doc/src/webengineview_lgpl.qdoc
@@ -754,9 +754,11 @@
\value WebEngineView.OpenLinkInThisWindow
Open the current link in the current window. (Added in Qt 5.6)
\value WebEngineView.OpenLinkInNewWindow
- Open the current link in a new window. (Added in Qt 5.6)
+ Open the current link in a new window. Requires a handler for the
+ \l newViewRequested() signal. (Added in Qt 5.6)
\value WebEngineView.OpenLinkInNewTab
- Open the current link in a new tab. (Added in Qt 5.6)
+ Open the current link in a new tab. Requires a handler for the
+ \l newViewRequested() signal. (Added in Qt 5.6)
\value WebEngineView.CopyLinkToClipboard
Copy the current link to the clipboard. (Added in Qt 5.6)
\value WebEngineView.CopyImageToClipboard
@@ -777,8 +779,8 @@
Mute or unmute the hovered audio or video element. (Added in Qt 5.6)
\value WebEngineView.DownloadLinkToDisk
Download the current link to the disk. To implement download
- actions, connect to the QWebEngineProfile::downloadRequested signal.
- (Added in Qt 5.6)
+ actions, connect to the \l {QQuickWebEngineProfile::downloadRequested}
+ {WebEngineProfile.downloadRequested} signal. (Added in Qt 5.6)
\value WebEngineView.DownloadImageToDisk
Download the highlighted image to the disk. (Added in Qt 5.6)
\value WebEngineView.DownloadMediaToDisk
@@ -791,7 +793,8 @@
\value WebEngineView.SavePage
Save the current web page to disk. (Added in Qt 5.7)
\value WebEngineView.ViewSource
- Show the source of the current page in a new tab. (Added in Qt 5.8)
+ Show the source of the current page in a new tab. Requires a handler for the
+ \l newViewRequested() signal. (Added in Qt 5.8)
\value WebEngineView.ToggleBold
Toggles boldness for the selection or at the cursor position.
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
index 1176573fd..2f65db97a 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
@@ -54,7 +54,6 @@ namespace QtWebEngineCore {
RenderWidgetHostViewQtDelegateQuick::RenderWidgetHostViewQtDelegateQuick(RenderWidgetHostViewQtDelegateClient *client, bool isPopup)
: m_client(client)
, m_isPopup(isPopup)
- , m_isPasswordInput(false)
{
setFlag(ItemHasContents);
setAcceptedMouseButtons(Qt::AllButtons);
@@ -206,11 +205,9 @@ void RenderWidgetHostViewQtDelegateQuick::inputMethodStateChanged(bool editorVis
if (parentItem())
parentItem()->setFlag(QQuickItem::ItemAcceptsInputMethod, editorVisible && !passwordInput);
- if (qApp->inputMethod()->isVisible() != editorVisible || m_isPasswordInput != passwordInput) {
- qApp->inputMethod()->update(Qt::ImQueryInput | Qt::ImEnabled | Qt::ImHints);
+ qApp->inputMethod()->update(Qt::ImQueryInput | Qt::ImEnabled | Qt::ImHints);
+ if (qApp->inputMethod()->isVisible() != editorVisible)
qApp->inputMethod()->setVisible(editorVisible);
- m_isPasswordInput = passwordInput;
- }
}
bool RenderWidgetHostViewQtDelegateQuick::event(QEvent *event)
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h
index 6f6cd1509..d4d64804a 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.h
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h
@@ -113,7 +113,6 @@ private:
RenderWidgetHostViewQtDelegateClient *m_client;
QList<QMetaObject::Connection> m_windowConnections;
bool m_isPopup;
- bool m_isPasswordInput;
QPointF m_lastGlobalPos;
QQuickWebEngineView *m_view = nullptr;
};
diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp
index d80ed997c..7b2f398ba 100644
--- a/src/webenginewidgets/api/qwebengineprofile.cpp
+++ b/src/webenginewidgets/api/qwebengineprofile.cpp
@@ -52,6 +52,7 @@
#include "visited_links_manager_qt.h"
#include "web_engine_settings.h"
+#include <QtWebEngineCore/qwebengineurlscheme.h>
QT_BEGIN_NAMESPACE
@@ -675,7 +676,7 @@ const QWebEngineUrlSchemeHandler *QWebEngineProfile::urlSchemeHandler(const QByt
Registers a handler \a handler for custom URL scheme \a scheme in the profile.
- It is recommended to first register the scheme with \l
+ It is necessary to first register the scheme with \l
QWebEngineUrlScheme::registerScheme at application startup.
*/
void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
index f91b71ea1..d012c678c 100644
--- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
+++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
@@ -109,9 +109,11 @@
This enum describes the types of action which can be performed on the web page.
- Actions only have an effect when they are applicable. The availability of
- actions can be be determined by checking \l{QAction::}{isEnabled()} on the
- action returned by action().
+ Actions only have an effect when they are applicable.
+
+ The availability of actions can be be determined by checking
+ \l{QAction::}{isEnabled()} on the action returned by action(). Context-dependent
+ actions are always enabled.
\value NoWebAction No action is triggered.
\value Back Navigate back in the history of navigated links.
@@ -128,9 +130,12 @@
\value PasteAndMatchStyle Paste content from the clipboard with current style.
\value OpenLinkInThisWindow Open the current link in the current window. (Added in Qt 5.6)
- \value OpenLinkInNewWindow Open the current link in a new window. (Added in Qt 5.6)
- \value OpenLinkInNewTab Open the current link in a new tab. (Added in Qt 5.6)
- \value OpenLinkInNewBackgroundTab Open the current link in a new background tab. (Added in Qt 5.7)
+ \value OpenLinkInNewWindow Open the current link in a new window. Requires implementation of
+ \l createWindow(). (Added in Qt 5.6)
+ \value OpenLinkInNewTab Open the current link in a new tab. Requires implementation of
+ \l createWindow(). (Added in Qt 5.6)
+ \value OpenLinkInNewBackgroundTab Open the current link in a new background tab. Requires
+ implementation of \l createWindow(). (Added in Qt 5.7)
\value CopyLinkToClipboard Copy the current link to the clipboard. (Added in Qt 5.6)
\value CopyImageToClipboard Copy the clicked image to the clipboard. (Added in Qt 5.6)
@@ -143,9 +148,12 @@
\value ToggleMediaPlayPause Toggle the play/pause state of the hovered audio or video element.
(Added in Qt 5.6)
\value ToggleMediaMute Mute or unmute the hovered audio or video element. (Added in Qt 5.6)
- \value DownloadLinkToDisk Download the current link to the disk. (Added in Qt 5.6)
- \value DownloadImageToDisk Download the highlighted image to the disk. (Added in Qt 5.6)
- \value DownloadMediaToDisk Download the hovered audio or video to the disk. (Added in Qt 5.6)
+ \value DownloadLinkToDisk Download the current link to the disk. Requires a slot for
+ \l{QWebEngineProfile::}{downloadRequested()}. (Added in Qt 5.6)
+ \value DownloadImageToDisk Download the highlighted image to the disk. Requires a slot for
+ \l{QWebEngineProfile::}{downloadRequested()}. (Added in Qt 5.6)
+ \value DownloadMediaToDisk Download the hovered audio or video to the disk. Requires a slot for
+ \l{QWebEngineProfile::}{downloadRequested()}. (Added in Qt 5.6)
\value InspectElement Trigger any attached Web Inspector to inspect the highlighed element.
(Added in Qt 5.6)
@@ -155,8 +163,10 @@
request is confirmed, \c windowCloseRequested is emitted. (Added in Qt 5.6)
\value Unselect Clear the current selection. (Added in Qt 5.7)
\value SavePage Save the current page to disk. MHTML is the default format that is used to store
- the web page on disk. (Added in Qt 5.7)
- \value ViewSource Show the source of the current page in a new tab. (Added in Qt 5.8)
+ the web page on disk. Requires a slot for \l{QWebEngineProfile::}{downloadRequested()}.
+ (Added in Qt 5.7)
+ \value ViewSource Show the source of the current page in a new tab. Requires implementation of
+ \l createWindow(). (Added in Qt 5.8)
\value ToggleBold
Toggles boldness for the selection or at the cursor position.
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
index 45e87477f..5b464a461 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
@@ -107,7 +107,6 @@ RenderWidgetHostViewQtDelegateWidget::RenderWidgetHostViewQtDelegateWidget(Rende
, m_client(client)
, m_rootItem(new RenderWidgetHostViewQuickItem(client))
, m_isPopup(false)
- , m_isPasswordInput(false)
{
setFocusPolicy(Qt::StrongFocus);
@@ -347,14 +346,10 @@ void RenderWidgetHostViewQtDelegateWidget::move(const QPoint &screenPos)
void RenderWidgetHostViewQtDelegateWidget::inputMethodStateChanged(bool editorVisible, bool passwordInput)
{
- if (qApp->inputMethod()->isVisible() == editorVisible && m_isPasswordInput == passwordInput)
- return;
-
QQuickWidget::setAttribute(Qt::WA_InputMethodEnabled, editorVisible && !passwordInput);
- m_isPasswordInput = passwordInput;
-
qApp->inputMethod()->update(Qt::ImQueryInput | Qt::ImEnabled | Qt::ImHints);
- qApp->inputMethod()->setVisible(editorVisible);
+ if (qApp->inputMethod()->isVisible() != editorVisible)
+ qApp->inputMethod()->setVisible(editorVisible);
}
void RenderWidgetHostViewQtDelegateWidget::setInputMethodHints(Qt::InputMethodHints hints)
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
index e23f13d86..c1cd90093 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
@@ -110,7 +110,6 @@ private:
RenderWidgetHostViewQtDelegateClient *m_client;
QScopedPointer<QQuickItem> m_rootItem;
bool m_isPopup;
- bool m_isPasswordInput;
QColor m_clearColor;
QPoint m_lastGlobalPos;
QList<QMetaObject::Connection> m_windowConnections;
diff --git a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/BLACKLIST b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/BLACKLIST
deleted file mode 100644
index a48edeeab..000000000
--- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/BLACKLIST
+++ /dev/null
@@ -1,4 +0,0 @@
-[javascriptClipboard:default]
-opensuse-leap
-[javascriptClipboard:canPaste]
-opensuse-leap
diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST
index 831039266..76cb18c1e 100644
--- a/tests/auto/quick/qquickwebengineview/BLACKLIST
+++ b/tests/auto/quick/qquickwebengineview/BLACKLIST
@@ -9,3 +9,8 @@ windows
[basicRenderingSanity]
*
+[javascriptClipboard:default]
+opensuse-leap
+[javascriptClipboard:canPaste]
+opensuse-leap
+
diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
index 5cb904fb6..29df3444c 100644
--- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
+++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
@@ -75,6 +75,7 @@ private Q_SLOTS:
void inputMethod();
void inputMethodHints();
+ void inputContextQueryInput();
void interruptImeTextComposition_data();
void interruptImeTextComposition();
void basicRenderingSanity();
@@ -471,6 +472,24 @@ void tst_QQuickWebEngineView::inputMethod()
QVERIFY(!view->flags().testFlag(QQuickItem::ItemAcceptsInputMethod));
}
+struct InputMethodInfo
+{
+ InputMethodInfo(const int cursorPosition,
+ const int anchorPosition,
+ QString surroundingText,
+ QString selectedText)
+ : cursorPosition(cursorPosition)
+ , anchorPosition(anchorPosition)
+ , surroundingText(surroundingText)
+ , selectedText(selectedText)
+ {}
+
+ const int cursorPosition;
+ const int anchorPosition;
+ QString surroundingText;
+ QString selectedText;
+};
+
class TestInputContext : public QPlatformInputContext
{
public:
@@ -496,8 +515,28 @@ public:
resetCallCount++;
}
+ virtual void update(Qt::InputMethodQueries queries)
+ {
+ if (!qApp->focusObject())
+ return;
+
+ if (!(queries & Qt::ImQueryInput))
+ return;
+
+ QInputMethodQueryEvent imQueryEvent(Qt::ImQueryInput);
+ QGuiApplication::sendEvent(qApp->focusObject(), &imQueryEvent);
+
+ const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt();
+ const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt();
+ QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString();
+ QString selectedText = imQueryEvent.value(Qt::ImCurrentSelection).toString();
+
+ infos.append(InputMethodInfo(cursorPosition, anchorPosition, surroundingText, selectedText));
+ }
+
int commitCallCount;
int resetCallCount;
+ QList<InputMethodInfo> infos;
};
void tst_QQuickWebEngineView::interruptImeTextComposition_data()
@@ -561,6 +600,151 @@ void tst_QQuickWebEngineView::interruptImeTextComposition()
QTRY_COMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QStringLiteral("x"));
}
+void tst_QQuickWebEngineView::inputContextQueryInput()
+{
+ m_window->show();
+ QTRY_VERIFY(qApp->focusObject());
+ TestInputContext testContext;
+
+ QQuickWebEngineView *view = webEngineView();
+ view->settings()->setFocusOnNavigationEnabled(true);
+ view->loadHtml("<html><body>"
+ " <input type='text' id='input1' />"
+ "</body></html>");
+ QVERIFY(waitForLoadSucceeded(view));
+ QCOMPARE(testContext.infos.count(), 0);
+
+ // Set focus on an input field.
+ QPoint textInputCenter = elementCenter(view, "input1");
+ QTest::mouseClick(view->window(), Qt::LeftButton, 0, textInputCenter);
+ QTRY_COMPARE(testContext.infos.count(), 2);
+ QCOMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input1"));
+ foreach (const InputMethodInfo &info, testContext.infos) {
+ QCOMPARE(info.cursorPosition, 0);
+ QCOMPARE(info.anchorPosition, 0);
+ QCOMPARE(info.surroundingText, QStringLiteral(""));
+ QCOMPARE(info.selectedText, QStringLiteral(""));
+ }
+ testContext.infos.clear();
+
+ // Change content of an input field from JavaScript.
+ evaluateJavaScriptSync(view, "document.getElementById('input1').value='QtWebEngine';");
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 11);
+ QCOMPARE(testContext.infos[0].anchorPosition, 11);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Change content of an input field by key press.
+ QTest::keyClick(view->window(), Qt::Key_Exclam);
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 12);
+ QCOMPARE(testContext.infos[0].anchorPosition, 12);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Change cursor position.
+ QTest::keyClick(view->window(), Qt::Key_Left);
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 11);
+ QCOMPARE(testContext.infos[0].anchorPosition, 11);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Selection by IME.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 2, 12, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("", attributes);
+ QGuiApplication::sendEvent(qApp->focusObject(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 2);
+
+ // As a first step, Chromium moves the cursor to the start of the selection.
+ // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection.
+ QCOMPARE(testContext.infos[0].cursorPosition, 2);
+ QCOMPARE(testContext.infos[0].anchorPosition, 2);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+
+ // The update of the selection.
+ QCOMPARE(testContext.infos[1].cursorPosition, 12);
+ QCOMPARE(testContext.infos[1].anchorPosition, 2);
+ QCOMPARE(testContext.infos[1].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[1].selectedText, QStringLiteral("WebEngine!"));
+ testContext.infos.clear();
+
+ // Clear selection by IME.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("", attributes);
+ QGuiApplication::sendEvent(qApp->focusObject(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 0);
+ QCOMPARE(testContext.infos[0].anchorPosition, 0);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Compose text.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("123", attributes);
+ QGuiApplication::sendEvent(qApp->focusObject(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 3);
+ QCOMPARE(testContext.infos[0].anchorPosition, 3);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!"));
+ testContext.infos.clear();
+
+ // Cancel composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ QGuiApplication::sendEvent(qApp->focusObject(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 2);
+ foreach (const InputMethodInfo &info, testContext.infos) {
+ QCOMPARE(info.cursorPosition, 0);
+ QCOMPARE(info.anchorPosition, 0);
+ QCOMPARE(info.surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(info.selectedText, QStringLiteral(""));
+ }
+ QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('input1').value").toString(), QStringLiteral("QtWebEngine!"));
+ testContext.infos.clear();
+
+ // Commit text.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString(QStringLiteral("123"), 0, 0);
+ QGuiApplication::sendEvent(qApp->focusObject(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 3);
+ QCOMPARE(testContext.infos[0].anchorPosition, 3);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("123QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!"));
+ testContext.infos.clear();
+
+ // Focus out.
+ QTest::keyPress(view->window(), Qt::Key_Tab);
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QTRY_COMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral(""));
+ testContext.infos.clear();
+}
+
void tst_QQuickWebEngineView::inputMethodHints()
{
m_window->show();
@@ -599,7 +783,7 @@ void tst_QQuickWebEngineView::inputMethodHints()
QVERIFY(view->flags().testFlag(QQuickItem::ItemAcceptsInputMethod));
query = QInputMethodQueryEvent(Qt::ImHints);
QGuiApplication::sendEvent(input, &query);
- QTRY_COMPARE(Qt::InputMethodHints(query.value(Qt::ImHints).toUInt()), Qt::ImhPreferLowercase | Qt::ImhMultiLine);
+ QTRY_COMPARE(Qt::InputMethodHints(query.value(Qt::ImHints).toUInt()), Qt::ImhPreferLowercase | Qt::ImhNoPredictiveText | Qt::ImhMultiLine | Qt::ImhNoEditMenu | Qt::ImhNoTextHandles);
}
void tst_QQuickWebEngineView::setZoomFactor()
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index a6487d19a..c7d3ccca7 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -48,7 +48,7 @@
#include <QtWidgets/qaction.h>
#define VERIFY_INPUTMETHOD_HINTS(actual, expect) \
- QVERIFY(actual == expect);
+ QVERIFY(actual == (expect | Qt::ImhNoPredictiveText | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu));
#define QTRY_COMPARE_WITH_TIMEOUT_FAIL_BLOCK(__expr, __expected, __timeout, __fail_block) \
do { \
@@ -167,6 +167,7 @@ private Q_SLOTS:
void inputFieldOverridesShortcuts();
void softwareInputPanel();
+ void inputContextQueryInput();
void inputMethods();
void textSelectionInInputField();
void textSelectionOutOfInputField();
@@ -1689,6 +1690,24 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts()
QTRY_VERIFY(actionTriggered);
}
+struct InputMethodInfo
+{
+ InputMethodInfo(const int cursorPosition,
+ const int anchorPosition,
+ QString surroundingText,
+ QString selectedText)
+ : cursorPosition(cursorPosition)
+ , anchorPosition(anchorPosition)
+ , surroundingText(surroundingText)
+ , selectedText(selectedText)
+ {}
+
+ const int cursorPosition;
+ const int anchorPosition;
+ QString surroundingText;
+ QString selectedText;
+};
+
class TestInputContext : public QPlatformInputContext
{
public:
@@ -1718,7 +1737,27 @@ public:
return m_visible;
}
+ virtual void update(Qt::InputMethodQueries queries)
+ {
+ if (!qApp->focusObject())
+ return;
+
+ if (!(queries & Qt::ImQueryInput))
+ return;
+
+ QInputMethodQueryEvent imQueryEvent(Qt::ImQueryInput);
+ QApplication::sendEvent(qApp->focusObject(), &imQueryEvent);
+
+ const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt();
+ const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt();
+ QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString();
+ QString selectedText = imQueryEvent.value(Qt::ImCurrentSelection).toString();
+
+ infos.append(InputMethodInfo(cursorPosition, anchorPosition, surroundingText, selectedText));
+ }
+
bool m_visible;
+ QList<InputMethodInfo> infos;
};
void tst_QWebEngineView::softwareInputPanel()
@@ -1779,6 +1818,156 @@ void tst_QWebEngineView::softwareInputPanel()
QVERIFY(!testContext.isInputPanelVisible());
}
+void tst_QWebEngineView::inputContextQueryInput()
+{
+ TestInputContext testContext;
+ QWebEngineView view;
+ view.resize(640, 480);
+ view.show();
+
+ QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged()));
+ QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
+ view.setHtml("<html><body>"
+ " <input type='text' id='input1' value='' size='50'/>"
+ "</body></html>");
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QCOMPARE(testContext.infos.count(), 0);
+
+ // Set focus on an input field.
+ QPoint textInputCenter = elementCenter(view.page(), "input1");
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTRY_COMPARE(testContext.infos.count(), 2);
+ QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
+ foreach (const InputMethodInfo &info, testContext.infos) {
+ QCOMPARE(info.cursorPosition, 0);
+ QCOMPARE(info.anchorPosition, 0);
+ QCOMPARE(info.surroundingText, QStringLiteral(""));
+ QCOMPARE(info.selectedText, QStringLiteral(""));
+ }
+ testContext.infos.clear();
+
+ // Change content of an input field from JavaScript.
+ evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value='QtWebEngine';");
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 11);
+ QCOMPARE(testContext.infos[0].anchorPosition, 11);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Change content of an input field by key press.
+ QTest::keyClick(view.focusProxy(), Qt::Key_Exclam);
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 12);
+ QCOMPARE(testContext.infos[0].anchorPosition, 12);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Change cursor position.
+ QTest::keyClick(view.focusProxy(), Qt::Key_Left);
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 11);
+ QCOMPARE(testContext.infos[0].anchorPosition, 11);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+
+ // Selection by IME.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 2, 12, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 2);
+ QCOMPARE(selectionChangedSpy.count(), 1);
+
+ // As a first step, Chromium moves the cursor to the start of the selection.
+ // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection.
+ QCOMPARE(testContext.infos[0].cursorPosition, 2);
+ QCOMPARE(testContext.infos[0].anchorPosition, 2);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+
+ // The update of the selection.
+ QCOMPARE(testContext.infos[1].cursorPosition, 12);
+ QCOMPARE(testContext.infos[1].anchorPosition, 2);
+ QCOMPARE(testContext.infos[1].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[1].selectedText, QStringLiteral("WebEngine!"));
+ testContext.infos.clear();
+ selectionChangedSpy.clear();
+
+ // Clear selection by IME.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(selectionChangedSpy.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 0);
+ QCOMPARE(testContext.infos[0].anchorPosition, 0);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ testContext.infos.clear();
+ selectionChangedSpy.clear();
+
+ // Compose text.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("123", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 3);
+ QCOMPARE(testContext.infos[0].anchorPosition, 3);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!"));
+ testContext.infos.clear();
+
+ // Cancel composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 2);
+ foreach (const InputMethodInfo &info, testContext.infos) {
+ QCOMPARE(info.cursorPosition, 0);
+ QCOMPARE(info.anchorPosition, 0);
+ QCOMPARE(info.surroundingText, QStringLiteral("QtWebEngine!"));
+ QCOMPARE(info.selectedText, QStringLiteral(""));
+ }
+ QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("QtWebEngine!"));
+ testContext.infos.clear();
+
+ // Commit text.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString(QStringLiteral("123"), 0, 0);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QCOMPARE(testContext.infos[0].cursorPosition, 3);
+ QCOMPARE(testContext.infos[0].anchorPosition, 3);
+ QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("123QtWebEngine!"));
+ QCOMPARE(testContext.infos[0].selectedText, QStringLiteral(""));
+ QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!"));
+ testContext.infos.clear();
+
+ // Focus out.
+ QTest::keyPress(view.focusProxy(), Qt::Key_Tab);
+ QTRY_COMPARE(testContext.infos.count(), 1);
+ QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral(""));
+ testContext.infos.clear();
+}
+
void tst_QWebEngineView::inputMethods()
{
QWebEngineView view;