summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/render_widget_host_view_qt.cpp237
-rw-r--r--src/core/render_widget_host_view_qt.h22
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp565
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp385
4 files changed, 590 insertions, 619 deletions
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index f96329139..619577d93 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -54,9 +54,10 @@
#include "base/command_line.h"
#include "cc/output/direct_renderer.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
+#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/text_input_manager.h"
#include "content/common/cursors/webcursor.h"
+#include "content/common/input_messages.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
@@ -92,6 +93,13 @@
namespace QtWebEngineCore {
+enum ImStateFlags {
+ TextInputStateUpdated = 1 << 0,
+ TextSelectionUpdated = 1 << 1,
+ TextSelectionBoundsUpdated = 1 << 2,
+ AllFlags = TextInputStateUpdated | TextSelectionUpdated | TextSelectionBoundsUpdated
+};
+
static inline ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& event) {
ui::LatencyInfo latency_info;
// The latency number should only be added if the timestamp is valid.
@@ -238,13 +246,17 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
, m_needsDelegatedFrameAck(false)
, m_loadVisuallyCommittedState(NotCommitted)
, m_adapterClient(0)
- , m_currentInputType(ui::TEXT_INPUT_TYPE_NONE)
, m_imeInProgress(false)
, m_receivedEmptyImeText(false)
, m_initPending(false)
, m_beginFrameSource(nullptr)
, m_needsBeginFrames(false)
, m_addedFrameObserver(false)
+ , m_imState(0)
+ , m_anchorPositionWithinSelection(0)
+ , m_cursorPositionWithinSelection(0)
+ , m_cursorPosition(0)
+ , m_emptyPreviousSelection(true)
{
m_host->SetView(this);
#ifndef QT_NO_ACCESSIBILITY
@@ -255,6 +267,9 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
auto* task_runner = base::ThreadTaskRunnerHandle::Get().get();
m_beginFrameSource.reset(new cc::DelayBasedBeginFrameSource(
base::MakeUnique<cc::DelayBasedTimeSource>(task_runner)));
+
+ if (GetTextInputManager())
+ GetTextInputManager()->AddObserver(this);
}
RenderWidgetHostViewQt::~RenderWidgetHostViewQt()
@@ -263,6 +278,9 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt()
#ifndef QT_NO_ACCESSIBILITY
QAccessible::removeActivationObserver(this);
#endif // QT_NO_ACCESSIBILITY
+
+ if (text_input_manager_)
+ text_input_manager_->RemoveObserver(this);
}
void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate)
@@ -576,15 +594,6 @@ void RenderWidgetHostViewQt::SetIsLoading(bool)
// We use WebContentsDelegateQt::LoadingStateChanged to notify about loading state.
}
-void RenderWidgetHostViewQt::TextInputStateChanged(const content::TextInputState &params)
-{
- m_currentInputType = params.type;
- m_delegate->inputMethodStateChanged(params.type != ui::TEXT_INPUT_TYPE_NONE);
- m_delegate->setInputMethodHints(toQtInputMethodHints(params.type));
-
- m_surroundingText = QString::fromStdString(params.value);
-}
-
void RenderWidgetHostViewQt::ImeCancelComposition()
{
qApp->inputMethod()->reset();
@@ -707,16 +716,114 @@ void RenderWidgetHostViewQt::ClearCompositorFrame()
{
}
-void RenderWidgetHostViewQt::SelectionChanged(const base::string16 &text, size_t offset, const gfx::Range &range)
+void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state)
{
- content::RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
- m_adapterClient->selectionChanged();
+ Q_UNUSED(text_input_manager);
+ Q_UNUSED(updated_view);
+ Q_UNUSED(did_update_state);
+
+ ui::TextInputType type = getTextInputType();
+ m_delegate->inputMethodStateChanged(type != ui::TEXT_INPUT_TYPE_NONE);
+ m_delegate->setInputMethodHints(toQtInputMethodHints(type));
+
+ const content::TextInputState *state = text_input_manager_->GetTextInputState();
+ if (!state)
+ return;
+
+ if (GetSelectedText().empty())
+ m_cursorPosition = state->selection_start;
+
+ 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);
+
+ if (m_imState & ImStateFlags::TextInputStateUpdated) {
+ m_imState = ImStateFlags::TextInputStateUpdated;
+ return;
+ }
+
+ // Ignore selection change triggered by ime composition unless it clears an actual text selection
+ if (state->composition_start != -1 && m_emptyPreviousSelection) {
+ m_imState = 0;
+ return;
+ }
+
+ m_imState |= ImStateFlags::TextInputStateUpdated;
+ if (m_imState == ImStateFlags::AllFlags)
+ selectionChanged();
+}
+
+void RenderWidgetHostViewQt::OnSelectionBoundsChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view)
+{
+ Q_UNUSED(text_input_manager);
+ Q_UNUSED(updated_view);
+
+ m_imState |= ImStateFlags::TextSelectionBoundsUpdated;
+ if (m_imState == ImStateFlags::AllFlags)
+ selectionChanged();
+}
+
+void RenderWidgetHostViewQt::OnTextSelectionChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view)
+{
+ Q_UNUSED(text_input_manager);
+ Q_UNUSED(updated_view);
#if defined(USE_X11)
- // Set the CLIPBOARD_TYPE_SELECTION to the ui::Clipboard.
- ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_SELECTION);
- clipboard_writer.WriteText(text);
-#endif
+ if (!GetSelectedText().empty()) {
+ // Set the CLIPBOARD_TYPE_SELECTION to the ui::Clipboard.
+ ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_SELECTION);
+ clipboard_writer.WriteText(GetSelectedText());
+ }
+#endif // defined(USE_X11)
+
+ m_imState |= ImStateFlags::TextSelectionUpdated;
+ if (m_imState == ImStateFlags::AllFlags)
+ selectionChanged();
+}
+
+void RenderWidgetHostViewQt::selectionChanged()
+{
+ // Reset input manager state
+ m_imState = 0;
+
+ const content::TextInputManager::TextSelection *selection = text_input_manager_->GetTextSelection();
+ if (!selection)
+ return;
+
+ if (!selection->range.IsValid())
+ return;
+
+ // Avoid duplicate empty selectionChanged() signals
+ if (GetSelectedText().empty() && m_emptyPreviousSelection) {
+ m_anchorPositionWithinSelection = m_cursorPosition;
+ m_cursorPositionWithinSelection = m_cursorPosition;
+ return;
+ }
+
+ uint newAnchorPositionWithinSelection = 0;
+ uint newCursorPositionWithinSelection = 0;
+
+ if (text_input_manager_->GetSelectionRegion()->anchor.type() == gfx::SelectionBound::RIGHT) {
+ newAnchorPositionWithinSelection = selection->range.GetMax() - selection->offset;
+ newCursorPositionWithinSelection = selection->range.GetMin() - selection->offset;
+ } else {
+ newAnchorPositionWithinSelection = selection->range.GetMin() - selection->offset;
+ newCursorPositionWithinSelection = selection->range.GetMax() - selection->offset;
+ }
+
+ if (m_anchorPositionWithinSelection == newAnchorPositionWithinSelection && m_cursorPositionWithinSelection == newCursorPositionWithinSelection)
+ return;
+
+ m_anchorPositionWithinSelection = newAnchorPositionWithinSelection;
+ m_cursorPositionWithinSelection = newCursorPositionWithinSelection;
+
+ if (!GetSelectedText().empty())
+ m_cursorPosition = newCursorPositionWithinSelection;
+
+ m_emptyPreviousSelection = GetSelectedText().empty();
+ m_adapterClient->selectionChanged();
}
void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture)
@@ -820,34 +927,27 @@ QVariant RenderWidgetHostViewQt::inputMethodQuery(Qt::InputMethodQuery query)
{
switch (query) {
case Qt::ImEnabled:
- return QVariant(m_currentInputType != ui::TEXT_INPUT_TYPE_NONE);
+ return QVariant(getTextInputType() != ui::TEXT_INPUT_TYPE_NONE);
case Qt::ImFont:
+ // TODO: Implement this
return QVariant();
case Qt::ImCursorRectangle:
- // QIBusPlatformInputContext might query ImCursorRectangle before the
- // RenderWidgetHostView is created. Without an available view GetSelectionRange()
- // returns nullptr.
- if (!GetTextInputManager()->GetSelectionRegion())
+ if (!text_input_manager_ || !text_input_manager_->GetActiveWidget())
return QVariant();
- return toQt(GetTextInputManager()->GetSelectionRegion()->caret_rect);
+ return toQt(text_input_manager_->GetSelectionRegion()->caret_rect);
case Qt::ImCursorPosition:
- Q_ASSERT(GetTextInputManager()->GetSelectionRegion());
- return toQt(GetTextInputManager()->GetSelectionRegion()->focus.edge_top_rounded().x());
+ return m_cursorPosition;
case Qt::ImAnchorPosition:
- Q_ASSERT(GetTextInputManager()->GetSelectionRegion());
- return toQt(GetTextInputManager()->GetSelectionRegion()->anchor.edge_top_rounded().x());
+ return GetSelectedText().empty() ? m_cursorPosition : m_anchorPositionWithinSelection;
case Qt::ImSurroundingText:
return m_surroundingText;
- case Qt::ImCurrentSelection: {
- Q_ASSERT(GetTextInputManager()->GetTextSelection());
- base::string16 text;
- GetTextInputManager()->GetTextSelection()->GetSelectedText(&text);
- return toQt(text);
- }
+ case Qt::ImCurrentSelection:
+ return toQt(GetSelectedText());
case Qt::ImMaximumTextLength:
+ // TODO: Implement this
return QVariant(); // No limit.
case Qt::ImHints:
- return int(toQtInputMethodHints(m_currentInputType));
+ return int(toQtInputMethodHints(getTextInputType()));
default:
return QVariant();
}
@@ -1005,6 +1105,9 @@ void RenderWidgetHostViewQt::handleKeyEvent(QKeyEvent *ev)
void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
{
+ // Reset input manager state
+ m_imState = 0;
+
if (!m_host)
return;
@@ -1016,19 +1119,9 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
const QList<QInputMethodEvent::Attribute> &attributes = ev->attributes();
std::vector<blink::WebCompositionUnderline> underlines;
- auto ensureValidSelectionRange = [&]() {
- if (!selectionRange.IsValid()) {
- // We did not receive a valid selection range, hence the range is going to mark the
- // cursor position.
- int newCursorPosition =
- (cursorPositionInPreeditString < 0) ? preeditString.length()
- : cursorPositionInPreeditString;
- selectionRange.set_start(newCursorPosition);
- selectionRange.set_end(newCursorPosition);
- }
- };
+ bool hasSelection = false;
- Q_FOREACH (const QInputMethodEvent::Attribute &attribute, attributes) {
+ for (const auto &attribute : attributes) {
switch (attribute.type) {
case QInputMethodEvent::TextFormat: {
if (preeditString.isEmpty())
@@ -1065,6 +1158,15 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
cursorPositionInPreeditString = attribute.start;
break;
case QInputMethodEvent::Selection:
+ hasSelection = true;
+
+ // Cancel IME composition
+ if (preeditString.isEmpty() && attribute.start + attribute.length == 0) {
+ selectionRange.set_start(0);
+ selectionRange.set_end(0);
+ break;
+ }
+
selectionRange.set_start(qMin(attribute.start, (attribute.start + attribute.length)));
selectionRange.set_end(qMax(attribute.start, (attribute.start + attribute.length)));
break;
@@ -1073,6 +1175,22 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
}
}
+ if (!selectionRange.IsValid()) {
+ // We did not receive a valid selection range, hence the range is going to mark the
+ // cursor position.
+ int newCursorPosition =
+ (cursorPositionInPreeditString < 0) ? preeditString.length()
+ : cursorPositionInPreeditString;
+ selectionRange.set_start(newCursorPosition);
+ selectionRange.set_end(newCursorPosition);
+ }
+
+ if (hasSelection) {
+ content::RenderFrameHost *frameHost = getFocusedFrameHost();
+ if (frameHost)
+ frameHost->Send(new InputMsg_SetEditableSelectionOffsets(frameHost->GetRoutingID(), selectionRange.start(), selectionRange.end()));
+ }
+
int replacementLength = ev->replacementLength();
gfx::Range replacementRange = gfx::Range::InvalidRange();
@@ -1089,7 +1207,6 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
}
auto setCompositionString = [&](const QString &compositionString){
- ensureValidSelectionRange();
m_host->ImeSetComposition(toString16(compositionString),
underlines,
replacementRange,
@@ -1126,7 +1243,7 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
// flickering in the textarea (or any other element).
// Instead we postpone the processing of the empty QInputMethodEvent by posting it
// to the same focused object, and cancelling the composition on the next event loop tick.
- if (!m_receivedEmptyImeText && m_imeInProgress) {
+ if (!m_receivedEmptyImeText && m_imeInProgress && !hasSelection) {
m_receivedEmptyImeText = true;
m_imeInProgress = false;
QInputMethodEvent *eventCopy = new QInputMethodEvent(*ev);
@@ -1304,4 +1421,26 @@ void RenderWidgetHostViewQt::OnBeginFrameSourcePausedChanged(bool paused)
// begin frames.
}
+content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost()
+{
+ content::RenderViewHostImpl *viewHost = content::RenderViewHostImpl::From(m_host);
+ if (!viewHost)
+ return nullptr;
+
+ content::FrameTreeNode *focusedFrame = viewHost->GetDelegate()->GetFrameTree()->GetFocusedFrame();
+ if (!focusedFrame)
+ return nullptr;
+
+ return focusedFrame->current_frame_host();
+}
+
+ui::TextInputType RenderWidgetHostViewQt::getTextInputType() const
+{
+ if (text_input_manager_ && text_input_manager_->GetTextInputState())
+ return text_input_manager_->GetTextInputState()->type;
+
+ return ui::TEXT_INPUT_TYPE_NONE;
+}
+
+
} // namespace QtWebEngineCore
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 1dae96a53..78b946a60 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -47,6 +47,7 @@
#include "cc/resources/transferable_resource.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/renderer_host/text_input_manager.h"
#include "content/common/view_messages.h"
#include "gpu/ipc/common/gpu_messages.h"
#include "ui/events/gesture_detection/filtered_gesture_provider.h"
@@ -74,6 +75,7 @@ QT_END_NAMESPACE
class WebContentsAdapterClient;
namespace content {
+class RenderFrameHost;
class RenderWidgetHostImpl;
}
@@ -104,6 +106,7 @@ class RenderWidgetHostViewQt
#ifndef QT_NO_ACCESSIBILITY
, public QAccessible::ActivationObserver
#endif // QT_NO_ACCESSIBILITY
+ , public content::TextInputManager::Observer
{
public:
enum LoadVisuallyCommittedState {
@@ -140,7 +143,6 @@ public:
virtual void UnlockMouse() Q_DECL_OVERRIDE;
virtual void UpdateCursor(const content::WebCursor&) Q_DECL_OVERRIDE;
virtual void SetIsLoading(bool) Q_DECL_OVERRIDE;
- virtual void TextInputStateChanged(const content::TextInputState& params) Q_DECL_OVERRIDE;
virtual void ImeCancelComposition() Q_DECL_OVERRIDE;
virtual void ImeCompositionRangeChanged(const gfx::Range&, const std::vector<gfx::Rect>&) Q_DECL_OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus, int) Q_DECL_OVERRIDE;
@@ -161,9 +163,6 @@ public:
virtual void UnlockCompositingSurface() Q_DECL_OVERRIDE;
virtual void SetNeedsBeginFrames(bool needs_begin_frames) Q_DECL_OVERRIDE;
- // Overridden from RenderWidgetHostViewBase.
- virtual void SelectionChanged(const base::string16 &text, size_t offset, const gfx::Range &range) Q_DECL_OVERRIDE;
-
// Overridden from ui::GestureProviderClient.
virtual void OnGestureEvent(const ui::GestureEventData& gesture) Q_DECL_OVERRIDE;
@@ -177,6 +176,11 @@ public:
virtual bool forwardEvent(QEvent *) Q_DECL_OVERRIDE;
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) Q_DECL_OVERRIDE;
+ // Overridden from content::TextInputManager::Observer
+ virtual void OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state) Q_DECL_OVERRIDE;
+ virtual void OnSelectionBoundsChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view) Q_DECL_OVERRIDE;
+ virtual void OnTextSelectionChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view) Q_DECL_OVERRIDE;
+
// cc::BeginFrameObserverBase implementation.
bool OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) override;
void OnBeginFrameSourcePausedChanged(bool paused) override;
@@ -220,6 +224,10 @@ private:
bool IsPopup() const;
+ void selectionChanged();
+ content::RenderFrameHost *getFocusedFrameHost();
+ ui::TextInputType getTextInputType() const;
+
content::RenderWidgetHostImpl *m_host;
ui::FilteredGestureProvider m_gestureProvider;
base::TimeDelta m_eventsToNowDelta;
@@ -239,7 +247,6 @@ private:
WebContentsAdapterClient *m_adapterClient;
MultipleMouseClickHelper m_clickHelper;
- ui::TextInputType m_currentInputType;
bool m_imeInProgress;
bool m_receivedEmptyImeText;
QPoint m_previousMousePosition;
@@ -253,6 +260,11 @@ private:
gfx::Vector2dF m_lastScrollOffset;
gfx::SizeF m_lastContentsSize;
+ uint m_imState;
+ uint m_anchorPositionWithinSelection;
+ uint m_cursorPositionWithinSelection;
+ uint m_cursorPosition;
+ bool m_emptyPreviousSelection;
QString m_surroundingText;
};
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 4d073e94c..b03418aea 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -120,8 +120,6 @@ private Q_SLOTS:
void testLocalStorageVisibility();
void testEnablePersistentStorage();
void consoleOutput();
- void inputMethods_data();
- void inputMethods();
void errorPageExtension();
void errorPageExtensionLoadFinished();
void userAgentNewlineStripping();
@@ -1631,569 +1629,6 @@ void tst_QWebEnginePage::backActionUpdate()
QVERIFY(action->isEnabled());
}
-void tst_QWebEnginePage::inputMethods_data()
-{
- QTest::addColumn<QString>("viewType");
- QTest::newRow("QWebEngineView") << "QWebEngineView";
- QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
-}
-
-#if defined(QWEBENGINEPAGE_INPUTMETHODQUERY)
-static void clickOnPage(QWebEnginePage* page, const QPoint& position)
-{
- QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
- page->event(&evpres);
- QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
- page->event(&evrel);
-}
-#endif
-
-void tst_QWebEnginePage::inputMethods()
-{
-#if !defined(QWEBENGINEPAGE_INPUTMETHODQUERY)
- QSKIP("QWEBENGINEPAGE_INPUTMETHODQUERY");
-#else
- QFETCH(QString, viewType);
- QWebEnginePage* page = new QWebEnginePage;
- QObject* view = 0;
- QScopedPointer<QObject> container(0);
- if (viewType == "QWebEngineView") {
- QWebEngineView* wv = new QWebEngineView;
- wv->setPage(page);
- view = wv;
- container.reset(view);
- } else if (viewType == "QGraphicsWebView") {
- QGraphicsWebView* wv = new QGraphicsWebView;
- wv->setPage(page);
- view = wv;
-
- QGraphicsView* gv = new QGraphicsView;
- QGraphicsScene* scene = new QGraphicsScene(gv);
- gv->setScene(scene);
- scene->addItem(wv);
- wv->setGeometry(QRect(0, 0, 500, 500));
-
- container.reset(gv);
- } else
- QVERIFY2(false, "Unknown view type");
-
- page->settings()->setFontFamily(QWebEngineSettings::SerifFont, page->settings()->fontFamily(QWebEngineSettings::FixedFont));
- page->setHtml("<html><body>" \
- "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
- "<input type='password'/>" \
- "</body></html>");
- page->mainFrame()->setFocus();
-
- TestInputContext testContext;
-
- QWebEngineElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
- QPoint textInputCenter = inputs.at(0).geometry().center();
-
- clickOnPage(page, textInputCenter);
-
- //ImMicroFocus
- QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
- QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
-
- // We assigned the serif font famility to be the same as the fixef font family.
- // Then test ImFont on a serif styled element, we should get our fixef font family.
- variant = page->inputMethodQuery(Qt::ImFont);
- QFont font = variant.value<QFont>();
- QCOMPARE(page->settings()->fontFamily(QWebEngineSettings::FixedFont), font.family());
-
- QList<QInputMethodEvent::Attribute> inputAttributes;
-
- //Insert text.
- {
- QInputMethodEvent eventText("QtWebEngine", inputAttributes);
- QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
- page->event(&eventText);
- QCOMPARE(signalSpy.count(), 0);
- }
-
- {
- QInputMethodEvent eventText("", inputAttributes);
- eventText.setCommitString(QString("QtWebEngine"), 0, 0);
- page->event(&eventText);
- }
-
- //ImMaximumTextLength
- variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
- QCOMPARE(20, variant.toInt());
-
- //Set selection
- inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
- QInputMethodEvent eventSelection("",inputAttributes);
- page->event(&eventSelection);
-
- //ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- int anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 3);
-
- //ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- int cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 5);
-
- //ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- QString selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString("eb"));
-
- //Set selection with negative length
- inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
- QInputMethodEvent eventSelection3("",inputAttributes);
- page->event(&eventSelection3);
-
- //ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 1);
-
- //ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 6);
-
- //ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString("tWebK"));
-
- //ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- QString value = variant.value<QString>();
- QCOMPARE(value, QString("QtWebEngine"));
-
- {
- QList<QInputMethodEvent::Attribute> attributes;
- // Clear the selection, so the next test does not clear any contents.
- QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
- attributes.append(newSelection);
- QInputMethodEvent event("composition", attributes);
- page->event(&event);
- }
-
- // A ongoing composition should not change the surrounding text before it is committed.
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- value = variant.value<QString>();
- QCOMPARE(value, QString("QtWebEngine"));
-
- // Cancel current composition first
- inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
- QInputMethodEvent eventSelection4("", inputAttributes);
- page->event(&eventSelection4);
-
- // START - Tests for Selection when the Editor is NOT in Composition mode
-
- // LEFT to RIGHT selection
- // Deselect the selection by sending MouseButtonPress events
- // This moves the current cursor to the end of the text
- clickOnPage(page, textInputCenter);
-
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QString(), attributes);
- event.setCommitString("XXX", 0, 0);
- page->event(&event);
- event.setCommitString(QString(), -2, 2); // Erase two characters.
- page->event(&event);
- event.setCommitString(QString(), -1, 1); // Erase one character.
- page->event(&event);
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- value = variant.value<QString>();
- QCOMPARE(value, QString("QtWebEngine"));
- }
-
- //Move to the start of the line
- page->triggerAction(QWebEnginePage::MoveToStartOfLine);
-
- QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
- QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
-
- //Move 2 characters RIGHT
- for (int j = 0; j < 2; ++j) {
- page->event(&keyRightEventPress);
- page->event(&keyRightEventRelease);
- }
-
- //Select to the end of the line
- page->triggerAction(QWebEnginePage::SelectEndOfLine);
-
- //ImAnchorPosition QtWebEngine
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 2);
-
- //ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 8);
-
- //ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString("WebKit"));
-
- //RIGHT to LEFT selection
- //Deselect the selection (this moves the current cursor to the end of the text)
- clickOnPage(page, textInputCenter);
-
- //ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 8);
-
- //ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 8);
-
- //ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
- QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
-
- //Move 2 characters LEFT
- for (int i = 0; i < 2; ++i) {
- page->event(&keyLeftEventPress);
- page->event(&keyLeftEventRelease);
- }
-
- //Select to the start of the line
- page->triggerAction(QWebEnginePage::SelectStartOfLine);
-
- //ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 6);
-
- //ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 0);
-
- //ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString("QtWebK"));
-
- //END - Tests for Selection when the Editor is not in Composition mode
-
- page->setHtml("<html><body>" \
- "<input type='text' id='input4' value='QtWebEngine inputMethod'/>" \
- "</body></html>");
- evaluateJavaScriptSync(page, "var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
-
- // Clear the selection, also cancel the ongoing composition if there is one.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
- attributes.append(newSelection);
- QInputMethodEvent event("", attributes);
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- QString surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod"));
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 0);
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 0);
-
- // 1. Insert a character to the beginning of the line.
- // Send temporary text, which makes the editor has composition 'm'.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("m", attributes);
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 0);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 0);
-
- // Send temporary text, which makes the editor has composition 'n'.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("n", attributes);
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 0);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 0);
-
- // Send commit text, which makes the editor conforms composition.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("", attributes);
- event.setCommitString("o");
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oQtWebEngine inputMethod"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 1);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 1);
-
- // 2. insert a character to the middle of the line.
- // Send temporary text, which makes the editor has composition 'd'.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("d", attributes);
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oQtWebEngine inputMethod"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 1);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 1);
-
- // Send commit text, which makes the editor conforms composition.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("", attributes);
- event.setCommitString("e");
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethod"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 2);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 2);
-
- // 3. Insert a character to the end of the line.
- page->triggerAction(QWebEnginePage::MoveToEndOfLine);
-
- // Send temporary text, which makes the editor has composition 't'.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("t", attributes);
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethod"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 22);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 22);
-
- // Send commit text, which makes the editor conforms composition.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("", attributes);
- event.setCommitString("t");
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethodt"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 23);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 23);
-
- // 4. Replace the selection.
- page->triggerAction(QWebEnginePage::SelectPreviousWord);
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString("inputMethodt"));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethodt"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 11);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 23);
-
- // Send temporary text, which makes the editor has composition 'w'.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("w", attributes);
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oeQtWebEngine "));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 11);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 11);
-
- // Send commit text, which makes the editor conforms composition.
- {
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event("", attributes);
- event.setCommitString("2");
- page->event(&event);
- }
-
- // ImCurrentSelection
- variant = page->inputMethodQuery(Qt::ImCurrentSelection);
- selectionValue = variant.value<QString>();
- QCOMPARE(selectionValue, QString(""));
-
- // ImSurroundingText
- variant = page->inputMethodQuery(Qt::ImSurroundingText);
- surroundingValue = variant.value<QString>();
- QCOMPARE(surroundingValue, QString("oeQtWebEngine 2"));
-
- // ImCursorPosition
- variant = page->inputMethodQuery(Qt::ImCursorPosition);
- cursorPosition = variant.toInt();
- QCOMPARE(cursorPosition, 12);
-
- // ImAnchorPosition
- variant = page->inputMethodQuery(Qt::ImAnchorPosition);
- anchorPosition = variant.toInt();
- QCOMPARE(anchorPosition, 12);
-#endif
-}
-
void tst_QWebEnginePage::protectBindingsRuntimeObjectsFromCollector()
{
#if !defined(QWEBENGINEPAGE_CREATEPLUGIN)
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index a6138c59d..82e50409d 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -97,8 +97,11 @@ private Q_SLOTS:
void postData();
void softwareInputPanel();
+ void inputMethods();
+ void textSelection();
void hiddenText();
void emptyInputMethodEvent();
+ void imeComposition();
void newlineInTextarea();
};
@@ -1341,6 +1344,24 @@ static QPoint elementCenter(QWebEnginePage *page, const QString &id)
return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt());
}
+static QRect elementGeometry(QWebEnginePage *page, const QString &id)
+{
+ const QString jsCode(
+ "(function() {"
+ " var elem = document.getElementById('" + id + "');"
+ " var rect = elem.getBoundingClientRect();"
+ " return [rect.left, rect.top, rect.right, rect.bottom];"
+ "})()");
+ QVariantList coords = evaluateJavaScriptSync(page, jsCode).toList();
+
+ if (coords.count() != 4) {
+ qWarning("elementGeometry faield.");
+ return QRect();
+ }
+
+ return QRect(coords[0].toInt(), coords[1].toInt(), coords[2].toInt(), coords[3].toInt());
+}
+
void tst_QWebEngineView::softwareInputPanel()
{
TestInputContext testContext;
@@ -1398,6 +1419,183 @@ void tst_QWebEngineView::softwareInputPanel()
QVERIFY(!testContext.isInputPanelVisible());
}
+void tst_QWebEngineView::inputMethods()
+{
+ QWebEngineView view;
+ view.show();
+
+ QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged()));
+ QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
+ view.settings()->setFontFamily(QWebEngineSettings::SerifFont, view.settings()->fontFamily(QWebEngineSettings::FixedFont));
+ view.setHtml("<html><body>"
+ " <input type='text' id='input1' style='font-family: serif' value='' maxlength='20' size='50'/>"
+ "</body></html>");
+ QVERIFY(loadFinishedSpy.wait());
+
+ QPoint textInputCenter = elementCenter(view.page(), "input1");
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
+
+ // ImCursorRectangle
+ QVariant variant = view.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle);
+ QVERIFY(elementGeometry(view.page(), "input1").contains(variant.toRect().topLeft()));
+
+ // We assigned the serif font family to be the same as the fixed font family.
+ // Then test ImFont on a serif styled element, we should get our fixed font family.
+ variant = view.focusProxy()->inputMethodQuery(Qt::ImFont);
+ QFont font = variant.value<QFont>();
+ QEXPECT_FAIL("", "UNIMPLEMENTED: RenderWidgetHostViewQt::inputMethodQuery(Qt::ImFont)", Continue);
+ QCOMPARE(view.settings()->fontFamily(QWebEngineSettings::FixedFont), font.family());
+
+ QList<QInputMethodEvent::Attribute> inputAttributes;
+
+ // Insert text
+ {
+ QString text = QStringLiteral("QtWebEngine");
+ QInputMethodEvent eventText(text, inputAttributes);
+ QApplication::sendEvent(view.focusProxy(), &eventText);
+ QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), text);
+ QCOMPARE(selectionChangedSpy.count(), 0);
+ }
+
+ {
+ QString text = QStringLiteral("QtWebEngine");
+ QInputMethodEvent eventText("", inputAttributes);
+ eventText.setCommitString(text, 0, 0);
+ QApplication::sendEvent(view.focusProxy(), &eventText);
+ QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), text);
+ QCOMPARE(selectionChangedSpy.count(), 0);
+ }
+
+ // ImMaximumTextLength
+ QEXPECT_FAIL("", "UNIMPLEMENTED: RenderWidgetHostViewQt::inputMethodQuery(Qt::ImMaximumTextLength)", Continue);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(), 20);
+
+ // Set selection
+ inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
+ QInputMethodEvent eventSelection1("", inputAttributes);
+
+ QApplication::sendEvent(view.focusProxy(), &eventSelection1);
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 1);
+
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 3);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 5);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("eb"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine"));
+
+ // Set selection with negative length
+ inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
+ QInputMethodEvent eventSelection2("", inputAttributes);
+ QApplication::sendEvent(view.focusProxy(), &eventSelection2);
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 2);
+
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 6);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("tWebE"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine"));
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ // Clear the selection, so the next test does not clear any contents.
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent eventComposition("composition", attributes);
+ QApplication::sendEvent(view.focusProxy(), &eventComposition);
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 3);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+
+ // An ongoing composition should not change the surrounding text before it is committed.
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine"));
+
+ // Cancel current composition first
+ inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
+ QInputMethodEvent eventSelection3("", inputAttributes);
+ QApplication::sendEvent(view.focusProxy(), &eventSelection3);
+
+ // Cancelling composition should not clear the surrounding text
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine"));
+}
+
+void tst_QWebEngineView::textSelection()
+{
+ QWebEngineView view;
+ view.show();
+
+ QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged()));
+ QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
+ view.setHtml("<html><body>"
+ " <input type='text' id='input1' value='QtWebEngine' size='50'/>"
+ "</body></html>");
+ QVERIFY(loadFinishedSpy.wait());
+
+ // Tests for Selection when the Editor is NOT in Composition mode
+
+ // LEFT to RIGHT selection
+ // Mouse click event moves the current cursor to the end of the text
+ QPoint textInputCenter = elementCenter(view.page(), "input1");
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11);
+ // There was no selection to be changed by the click
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QString(), attributes);
+ event.setCommitString("XXX", 0, 0);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngineXXX"));
+
+ event.setCommitString(QString(), -2, 2); // Erase two characters.
+ QApplication::sendEvent(view.focusProxy(), &event);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngineX"));
+
+ event.setCommitString(QString(), -1, 1); // Erase one character.
+ QApplication::sendEvent(view.focusProxy(), &event);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine"));
+
+ // Move to the start of the line
+ QTest::keyClick(view.focusProxy(), Qt::Key_Home);
+
+ // Move 2 characters RIGHT
+ for (int j = 0; j < 2; ++j)
+ QTest::keyClick(view.focusProxy(), Qt::Key_Right);
+
+ // Select to the end of the line
+ QTest::keyClick(view.focusProxy(), Qt::Key_End, Qt::ShiftModifier);
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 1);
+
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("WebEngine"));
+
+ // RIGHT to LEFT selection
+ // Deselect the selection (this moves the current cursor to the end of the text)
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 2);
+
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+
+ // Move 2 characters LEFT
+ for (int i = 0; i < 2; ++i)
+ QTest::keyClick(view.focusProxy(), Qt::Key_Left);
+
+ // Select to the start of the line
+ QTest::keyClick(view.focusProxy(), Qt::Key_Home, Qt::ShiftModifier);
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 3);
+
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 9);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("QtWebEngi"));
+}
+
void tst_QWebEngineView::hiddenText()
{
QWebEngineView view;
@@ -1451,6 +1649,193 @@ void tst_QWebEngineView::emptyInputMethodEvent()
QCOMPARE(inputValue, QString("QtWebEngine"));
}
+void tst_QWebEngineView::imeComposition()
+{
+ QWebEngineView view;
+ view.show();
+
+ QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged()));
+ QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
+ view.setHtml("<html><body>"
+ " <input type='text' id='input1' value='QtWebEngine inputMethod'/>"
+ "</body></html>");
+ QVERIFY(loadFinishedSpy.wait());
+
+ evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();");
+ QTRY_VERIFY(!evaluateJavaScriptSync(view.page(), "window.getSelection().toString()").toString().isEmpty());
+
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QVERIFY(selectionChangedSpy.wait(100));
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QCOMPARE(selectionChangedSpy.count(), 1);
+
+ // Clear the selection, also cancel the ongoing composition if there is one.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
+ attributes.append(newSelection);
+ QInputMethodEvent event("", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "window.getSelection().toString()").toString().isEmpty());
+
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QVERIFY(selectionChangedSpy.wait(100));
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QCOMPARE(selectionChangedSpy.count(), 2);
+ }
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine inputMethod"));
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+
+ selectionChangedSpy.clear();
+
+
+ // 1. Insert a character to the beginning of the line.
+ // Send temporary text, which makes the editor has composition 'm'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("m", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine inputMethod"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+ // Send temporary text, which makes the editor has composition 'n'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("n", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine inputMethod"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("o");
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oQtWebEngine inputMethod"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 1);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+
+ // 2. insert a character to the middle of the line.
+ // Send temporary text, which makes the editor has composition 'd'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("d", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oQtWebEngine inputMethod"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 1);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("e");
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine inputMethod"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 2);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+
+ // 3. Insert a character to the end of the line.
+ QTest::keyClick(view.focusProxy(), Qt::Key_End);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 25);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 25);
+
+ // Send temporary text, which makes the editor has composition 't'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("t", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine inputMethod"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 25);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 25);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("t");
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine inputMethodt"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 26);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 26);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+ QCOMPARE(selectionChangedSpy.count(), 0);
+
+
+ // 4. Replace the selection.
+#ifndef Q_OS_MACOS
+ QTest::keyClick(view.focusProxy(), Qt::Key_Left, Qt::ShiftModifier | Qt::ControlModifier);
+#else
+ QTest::keyClick(view.focusProxy(), Qt::Key_Left, Qt::ShiftModifier | Qt::AltModifier);
+#endif
+ QVERIFY(selectionChangedSpy.wait());
+ QCOMPARE(selectionChangedSpy.count(), 1);
+
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine inputMethodt"));
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 14);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 26);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("inputMethodt"));
+
+ // Send temporary text, which makes the editor has composition 'w'.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("w", attributes);
+ QApplication::sendEvent(view.focusProxy(), &event);
+ QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "window.getSelection().toString()").toString().isEmpty());
+ QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("oeQtWebEngine w"));
+
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QVERIFY(selectionChangedSpy.wait(100));
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QCOMPARE(selectionChangedSpy.count(), 2);
+ }
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine "));
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 14);
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 14);
+ QEXPECT_FAIL("", "https://bugreports.qt.io/browse/QTBUG-53134", Continue);
+ QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+
+ // Send commit text, which makes the editor conforms composition.
+ {
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("2");
+ QApplication::sendEvent(view.focusProxy(), &event);
+ }
+ // There is no text selection to be changed at this point thus we can't wait for selectionChanged signal.
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine 2"));
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 15);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 15);
+ QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString(""));
+}
+
void tst_QWebEngineView::newlineInTextarea()
{
QWebEngineView view;